From 14d0e347ea2db51144a8800d7c7576db96f69983 Mon Sep 17 00:00:00 2001 From: Zoran Markovic Date: Wed, 26 Jun 2013 16:09:13 -0700 Subject: rtc: Keep system awake until all expired RTC timers are handled Current implementation of RTC interface allows for system suspend to occur in the following cases: (a) if a timer is set in the past and rtc_timer_do_work() is scheduled to handle it, and (b) if rtc_timer_do_work() is called to handle expired timers whose handlers implement a preemption point. A pending suspend request may be honoured in the above cases causing timer handling to be delayed until after the next resume. This is undesirable since timer handlers may have time-critical code to execute. This patch makes sure that the system stays awake until all expired timers are handled. Note that all calls to pm_stay_awake() are eventually paired with the single pm_relax() call in rtc_timer_do_work(), which is launched using schedule_work(). Cc: Alessandro Zummo Cc: John Stultz Cc: Arve Hjonnevag Cc: Todd Poynor Signed-off-by: Zoran Markovic Signed-off-by: John Stultz diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 72c5cdb..544be72 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -72,6 +72,7 @@ int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm) } else err = -EINVAL; + pm_stay_awake(rtc->dev.parent); mutex_unlock(&rtc->ops_lock); /* A timer might have just expired */ schedule_work(&rtc->irqwork); @@ -113,6 +114,7 @@ int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs) err = -EINVAL; } + pm_stay_awake(rtc->dev.parent); mutex_unlock(&rtc->ops_lock); /* A timer might have just expired */ schedule_work(&rtc->irqwork); @@ -771,9 +773,10 @@ static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) alarm.time = rtc_ktime_to_tm(timer->node.expires); alarm.enabled = 1; err = __rtc_set_alarm(rtc, &alarm); - if (err == -ETIME) + if (err == -ETIME) { + pm_stay_awake(rtc->dev.parent); schedule_work(&rtc->irqwork); - else if (err) { + } else if (err) { timerqueue_del(&rtc->timerqueue, &timer->node); timer->enabled = 0; return err; @@ -818,8 +821,10 @@ static void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer) alarm.time = rtc_ktime_to_tm(next->expires); alarm.enabled = 1; err = __rtc_set_alarm(rtc, &alarm); - if (err == -ETIME) + if (err == -ETIME) { + pm_stay_awake(rtc->dev.parent); schedule_work(&rtc->irqwork); + } } } @@ -845,7 +850,6 @@ void rtc_timer_do_work(struct work_struct *work) mutex_lock(&rtc->ops_lock); again: - pm_relax(rtc->dev.parent); __rtc_read_time(rtc, &tm); now = rtc_tm_to_ktime(tm); while ((next = timerqueue_getnext(&rtc->timerqueue))) { @@ -880,6 +884,7 @@ again: } else rtc_alarm_disable(rtc); + pm_relax(rtc->dev.parent); mutex_unlock(&rtc->ops_lock); } -- cgit v0.10.2 From 397bbf6dee50bb1f07cbdb464c41b0f5b7a85493 Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Fri, 22 Feb 2013 15:08:56 -0500 Subject: clocksource: Fix !CONFIG_CLOCKSOURCE_WATCHDOG compile If I explicitly disable the clocksource watchdog in the x86 Kconfig, the x86 kernel will not compile unless this is properly defined. Cc: John Stultz Cc: Thomas Gleixner Cc: x86@kernel.org Signed-off-by: Prarit Bhargava Signed-off-by: John Stultz diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index 50a8736..a2e72b8 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -479,6 +479,7 @@ static inline void clocksource_dequeue_watchdog(struct clocksource *cs) { } static inline void clocksource_resume_watchdog(void) { } static inline int __clocksource_watchdog_kthread(void) { return 0; } static bool clocksource_is_watchdog(struct clocksource *cs) { return false; } +void clocksource_mark_unstable(struct clocksource *cs) { } #endif /* CONFIG_CLOCKSOURCE_WATCHDOG */ -- cgit v0.10.2 From 87d8b9eb7eb6669aad6435a51e9862362141ba76 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 18 Jul 2013 16:21:14 -0700 Subject: clocksource: Extract max nsec calculation into separate function We need to calculate the same number in the clocksource code and the sched_clock code, so extract this code into its own function. We also drop the min_t and just use min() because the two types are the same. Signed-off-by: Stephen Boyd Signed-off-by: John Stultz diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index dbbf8aa..67301a4 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -292,6 +292,8 @@ extern void clocksource_resume(void); extern struct clocksource * __init __weak clocksource_default_clock(void); extern void clocksource_mark_unstable(struct clocksource *cs); +extern u64 +clocks_calc_max_nsecs(u32 mult, u32 shift, u32 maxadj, u64 mask); extern void clocks_calc_mult_shift(u32 *mult, u32 *shift, u32 from, u32 to, u32 minsec); diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index 50a8736..637a14a 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -537,40 +537,55 @@ static u32 clocksource_max_adjustment(struct clocksource *cs) } /** - * clocksource_max_deferment - Returns max time the clocksource can be deferred - * @cs: Pointer to clocksource - * + * clocks_calc_max_nsecs - Returns maximum nanoseconds that can be converted + * @mult: cycle to nanosecond multiplier + * @shift: cycle to nanosecond divisor (power of two) + * @maxadj: maximum adjustment value to mult (~11%) + * @mask: bitmask for two's complement subtraction of non 64 bit counters */ -static u64 clocksource_max_deferment(struct clocksource *cs) +u64 clocks_calc_max_nsecs(u32 mult, u32 shift, u32 maxadj, u64 mask) { u64 max_nsecs, max_cycles; /* * Calculate the maximum number of cycles that we can pass to the * cyc2ns function without overflowing a 64-bit signed result. The - * maximum number of cycles is equal to ULLONG_MAX/(cs->mult+cs->maxadj) + * maximum number of cycles is equal to ULLONG_MAX/(mult+maxadj) * which is equivalent to the below. - * max_cycles < (2^63)/(cs->mult + cs->maxadj) - * max_cycles < 2^(log2((2^63)/(cs->mult + cs->maxadj))) - * max_cycles < 2^(log2(2^63) - log2(cs->mult + cs->maxadj)) - * max_cycles < 2^(63 - log2(cs->mult + cs->maxadj)) - * max_cycles < 1 << (63 - log2(cs->mult + cs->maxadj)) + * max_cycles < (2^63)/(mult + maxadj) + * max_cycles < 2^(log2((2^63)/(mult + maxadj))) + * max_cycles < 2^(log2(2^63) - log2(mult + maxadj)) + * max_cycles < 2^(63 - log2(mult + maxadj)) + * max_cycles < 1 << (63 - log2(mult + maxadj)) * Please note that we add 1 to the result of the log2 to account for * any rounding errors, ensure the above inequality is satisfied and * no overflow will occur. */ - max_cycles = 1ULL << (63 - (ilog2(cs->mult + cs->maxadj) + 1)); + max_cycles = 1ULL << (63 - (ilog2(mult + maxadj) + 1)); /* * The actual maximum number of cycles we can defer the clocksource is - * determined by the minimum of max_cycles and cs->mask. + * determined by the minimum of max_cycles and mask. * Note: Here we subtract the maxadj to make sure we don't sleep for * too long if there's a large negative adjustment. */ - max_cycles = min_t(u64, max_cycles, (u64) cs->mask); - max_nsecs = clocksource_cyc2ns(max_cycles, cs->mult - cs->maxadj, - cs->shift); + max_cycles = min(max_cycles, mask); + max_nsecs = clocksource_cyc2ns(max_cycles, mult - maxadj, shift); + + return max_nsecs; +} + +/** + * clocksource_max_deferment - Returns max time the clocksource can be deferred + * @cs: Pointer to clocksource + * + */ +static u64 clocksource_max_deferment(struct clocksource *cs) +{ + u64 max_nsecs; + max_nsecs = clocks_calc_max_nsecs(cs->mult, cs->shift, cs->maxadj, + cs->mask); /* * To ensure that the clocksource does not wrap whilst we are idle, * limit the time the clocksource can be deferred by 12.5%. Please -- cgit v0.10.2 From 85c3d2dd15be4d577a37ffb8bbbd019fc8e3280a Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 18 Jul 2013 16:21:15 -0700 Subject: sched_clock: Use seqcount instead of rolling our own We're going to increase the cyc value to 64 bits in the near future. Doing that is going to break the custom seqcount implementation in the sched_clock code because 64 bit numbers aren't guaranteed to be atomic. Replace the cyc_copy with a seqcount to avoid this problem. Cc: Russell King Acked-by: Will Deacon Signed-off-by: Stephen Boyd Signed-off-by: John Stultz diff --git a/kernel/time/sched_clock.c b/kernel/time/sched_clock.c index a326f27..396f7b9 100644 --- a/kernel/time/sched_clock.c +++ b/kernel/time/sched_clock.c @@ -14,11 +14,12 @@ #include #include #include +#include struct clock_data { u64 epoch_ns; u32 epoch_cyc; - u32 epoch_cyc_copy; + seqcount_t seq; unsigned long rate; u32 mult; u32 shift; @@ -54,23 +55,16 @@ static unsigned long long notrace sched_clock_32(void) u64 epoch_ns; u32 epoch_cyc; u32 cyc; + unsigned long seq; if (cd.suspended) return cd.epoch_ns; - /* - * Load the epoch_cyc and epoch_ns atomically. We do this by - * ensuring that we always write epoch_cyc, epoch_ns and - * epoch_cyc_copy in strict order, and read them in strict order. - * If epoch_cyc and epoch_cyc_copy are not equal, then we're in - * the middle of an update, and we should repeat the load. - */ do { + seq = read_seqcount_begin(&cd.seq); epoch_cyc = cd.epoch_cyc; - smp_rmb(); epoch_ns = cd.epoch_ns; - smp_rmb(); - } while (epoch_cyc != cd.epoch_cyc_copy); + } while (read_seqcount_retry(&cd.seq, seq)); cyc = read_sched_clock(); cyc = (cyc - epoch_cyc) & sched_clock_mask; @@ -90,16 +84,12 @@ static void notrace update_sched_clock(void) ns = cd.epoch_ns + cyc_to_ns((cyc - cd.epoch_cyc) & sched_clock_mask, cd.mult, cd.shift); - /* - * Write epoch_cyc and epoch_ns in a way that the update is - * detectable in cyc_to_fixed_sched_clock(). - */ + raw_local_irq_save(flags); - cd.epoch_cyc_copy = cyc; - smp_wmb(); + write_seqcount_begin(&cd.seq); cd.epoch_ns = ns; - smp_wmb(); cd.epoch_cyc = cyc; + write_seqcount_end(&cd.seq); raw_local_irq_restore(flags); } @@ -195,7 +185,6 @@ static int sched_clock_suspend(void) static void sched_clock_resume(void) { cd.epoch_cyc = read_sched_clock(); - cd.epoch_cyc_copy = cd.epoch_cyc; cd.suspended = false; } -- cgit v0.10.2 From a08ca5d1089da03724f96fa0870c64968e66765b Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 18 Jul 2013 16:21:16 -0700 Subject: sched_clock: Use an hrtimer instead of timer In the next patch we're going to increase the number of bits that the generic sched_clock can handle to be greater than 32. With more than 32 bits the wraparound time can be larger than what can fit into the units that msecs_to_jiffies takes (unsigned int). Luckily, the wraparound is initially calculated in nanoseconds which we can easily use with hrtimers, so switch to using an hrtimer. Cc: Russell King Signed-off-by: Stephen Boyd [jstultz: Fixup hrtimer intitialization order issue] Signed-off-by: John Stultz diff --git a/kernel/time/sched_clock.c b/kernel/time/sched_clock.c index 396f7b9..c018ffc 100644 --- a/kernel/time/sched_clock.c +++ b/kernel/time/sched_clock.c @@ -8,15 +8,17 @@ #include #include #include +#include #include #include #include #include -#include +#include #include #include struct clock_data { + ktime_t wrap_kt; u64 epoch_ns; u32 epoch_cyc; seqcount_t seq; @@ -26,8 +28,7 @@ struct clock_data { bool suspended; }; -static void sched_clock_poll(unsigned long wrap_ticks); -static DEFINE_TIMER(sched_clock_timer, sched_clock_poll, 0, 0); +static struct hrtimer sched_clock_timer; static int irqtime = -1; core_param(irqtime, irqtime, int, 0400); @@ -93,15 +94,16 @@ static void notrace update_sched_clock(void) raw_local_irq_restore(flags); } -static void sched_clock_poll(unsigned long wrap_ticks) +static enum hrtimer_restart sched_clock_poll(struct hrtimer *hrt) { - mod_timer(&sched_clock_timer, round_jiffies(jiffies + wrap_ticks)); update_sched_clock(); + hrtimer_forward_now(hrt, cd.wrap_kt); + return HRTIMER_RESTART; } void __init setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate) { - unsigned long r, w; + unsigned long r; u64 res, wrap; char r_unit; @@ -129,19 +131,13 @@ void __init setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate) /* calculate how many ns until we wrap */ wrap = cyc_to_ns((1ULL << bits) - 1, cd.mult, cd.shift); - do_div(wrap, NSEC_PER_MSEC); - w = wrap; + cd.wrap_kt = ns_to_ktime(wrap - (wrap >> 3)); /* calculate the ns resolution of this counter */ res = cyc_to_ns(1ULL, cd.mult, cd.shift); - pr_info("sched_clock: %u bits at %lu%cHz, resolution %lluns, wraps every %lums\n", - bits, r, r_unit, res, w); + pr_info("sched_clock: %u bits at %lu%cHz, resolution %lluns, wraps every %lluns\n", + bits, r, r_unit, res, wrap); - /* - * Start the timer to keep sched_clock() properly updated and - * sets the initial epoch. - */ - sched_clock_timer.data = msecs_to_jiffies(w - (w / 10)); update_sched_clock(); /* @@ -172,12 +168,20 @@ void __init sched_clock_postinit(void) if (read_sched_clock == jiffy_sched_clock_read) setup_sched_clock(jiffy_sched_clock_read, 32, HZ); - sched_clock_poll(sched_clock_timer.data); + update_sched_clock(); + + /* + * Start the timer to keep sched_clock() properly updated and + * sets the initial epoch. + */ + hrtimer_init(&sched_clock_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + sched_clock_timer.function = sched_clock_poll; + hrtimer_start(&sched_clock_timer, cd.wrap_kt, HRTIMER_MODE_REL); } static int sched_clock_suspend(void) { - sched_clock_poll(sched_clock_timer.data); + sched_clock_poll(&sched_clock_timer); cd.suspended = true; return 0; } -- cgit v0.10.2 From e7e3ff1bfe9c42ee31172e9afdc0383a9e595e29 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 18 Jul 2013 16:21:17 -0700 Subject: sched_clock: Add support for >32 bit sched_clock The ARM architected system counter has at least 56 usable bits. Add support for counters with more than 32 bits to the generic sched_clock implementation so we can increase the time between wakeups due to dealing with wrap-around on these devices while benefiting from the irqtime accounting and suspend/resume handling that the generic sched_clock code already has. On my system using 56 bits over 32 bits changes the wraparound time from a few minutes to an hour. For faster running counters (GHz range) this is even more important because we may not be able to execute the timer in time to deal with the wraparound if only 32 bits are used. We choose a maxsec value of 3600 seconds because we assume no system will go idle for more than an hour. In the future we may need to increase this value. Note: All users should switch over to the 64-bit read function so we can remove setup_sched_clock() in favor of sched_clock_register(). Cc: Russell King Signed-off-by: Stephen Boyd Signed-off-by: John Stultz diff --git a/include/linux/sched_clock.h b/include/linux/sched_clock.h index fa7922c..eca7abe 100644 --- a/include/linux/sched_clock.h +++ b/include/linux/sched_clock.h @@ -15,6 +15,8 @@ static inline void sched_clock_postinit(void) { } #endif extern void setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate); +extern void sched_clock_register(u64 (*read)(void), int bits, + unsigned long rate); extern unsigned long long (*sched_clock_func)(void); diff --git a/kernel/time/sched_clock.c b/kernel/time/sched_clock.c index c018ffc..f388bae 100644 --- a/kernel/time/sched_clock.c +++ b/kernel/time/sched_clock.c @@ -16,11 +16,12 @@ #include #include #include +#include struct clock_data { ktime_t wrap_kt; u64 epoch_ns; - u32 epoch_cyc; + u64 epoch_cyc; seqcount_t seq; unsigned long rate; u32 mult; @@ -37,14 +38,25 @@ static struct clock_data cd = { .mult = NSEC_PER_SEC / HZ, }; -static u32 __read_mostly sched_clock_mask = 0xffffffff; +static u64 __read_mostly sched_clock_mask; -static u32 notrace jiffy_sched_clock_read(void) +static u64 notrace jiffy_sched_clock_read(void) { - return (u32)(jiffies - INITIAL_JIFFIES); + /* + * We don't need to use get_jiffies_64 on 32-bit arches here + * because we register with BITS_PER_LONG + */ + return (u64)(jiffies - INITIAL_JIFFIES); +} + +static u32 __read_mostly (*read_sched_clock_32)(void); + +static u64 notrace read_sched_clock_32_wrapper(void) +{ + return read_sched_clock_32(); } -static u32 __read_mostly (*read_sched_clock)(void) = jiffy_sched_clock_read; +static u64 __read_mostly (*read_sched_clock)(void) = jiffy_sched_clock_read; static inline u64 notrace cyc_to_ns(u64 cyc, u32 mult, u32 shift) { @@ -54,8 +66,8 @@ static inline u64 notrace cyc_to_ns(u64 cyc, u32 mult, u32 shift) static unsigned long long notrace sched_clock_32(void) { u64 epoch_ns; - u32 epoch_cyc; - u32 cyc; + u64 epoch_cyc; + u64 cyc; unsigned long seq; if (cd.suspended) @@ -78,7 +90,7 @@ static unsigned long long notrace sched_clock_32(void) static void notrace update_sched_clock(void) { unsigned long flags; - u32 cyc; + u64 cyc; u64 ns; cyc = read_sched_clock(); @@ -101,7 +113,8 @@ static enum hrtimer_restart sched_clock_poll(struct hrtimer *hrt) return HRTIMER_RESTART; } -void __init setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate) +void __init sched_clock_register(u64 (*read)(void), int bits, + unsigned long rate) { unsigned long r; u64 res, wrap; @@ -110,14 +123,13 @@ void __init setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate) if (cd.rate > rate) return; - BUG_ON(bits > 32); WARN_ON(!irqs_disabled()); read_sched_clock = read; - sched_clock_mask = (1 << bits) - 1; + sched_clock_mask = CLOCKSOURCE_MASK(bits); cd.rate = rate; /* calculate the mult/shift to convert counter ticks to ns. */ - clocks_calc_mult_shift(&cd.mult, &cd.shift, rate, NSEC_PER_SEC, 0); + clocks_calc_mult_shift(&cd.mult, &cd.shift, rate, NSEC_PER_SEC, 3600); r = rate; if (r >= 4000000) { @@ -130,7 +142,7 @@ void __init setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate) r_unit = ' '; /* calculate how many ns until we wrap */ - wrap = cyc_to_ns((1ULL << bits) - 1, cd.mult, cd.shift); + wrap = clocks_calc_max_nsecs(cd.mult, cd.shift, 0, sched_clock_mask); cd.wrap_kt = ns_to_ktime(wrap - (wrap >> 3)); /* calculate the ns resolution of this counter */ @@ -152,6 +164,12 @@ void __init setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate) pr_debug("Registered %pF as sched_clock source\n", read); } +void __init setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate) +{ + read_sched_clock_32 = read; + sched_clock_register(read_sched_clock_32_wrapper, bits, rate); +} + unsigned long long __read_mostly (*sched_clock_func)(void) = sched_clock_32; unsigned long long notrace sched_clock(void) @@ -166,7 +184,7 @@ void __init sched_clock_postinit(void) * make it the final one one. */ if (read_sched_clock == jiffy_sched_clock_read) - setup_sched_clock(jiffy_sched_clock_read, 32, HZ); + sched_clock_register(jiffy_sched_clock_read, BITS_PER_LONG, HZ); update_sched_clock(); -- cgit v0.10.2 From 18952f20fadef0a5e099f5c4cac34b97644ccc35 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 18 Jul 2013 16:21:20 -0700 Subject: clocksource: bcm2835: Switch to sched_clock_register() The 32 bit sched_clock interface now supports 64 bits. Upgrade to the 64 bit function to allow us to remove the 32 bit registration interface. Cc: Stephen Warren Acked-by: Stephen Warren Signed-off-by: Stephen Boyd Signed-off-by: John Stultz diff --git a/drivers/clocksource/bcm2835_timer.c b/drivers/clocksource/bcm2835_timer.c index 07ea7ce..26ed331 100644 --- a/drivers/clocksource/bcm2835_timer.c +++ b/drivers/clocksource/bcm2835_timer.c @@ -49,7 +49,7 @@ struct bcm2835_timer { static void __iomem *system_clock __read_mostly; -static u32 notrace bcm2835_sched_read(void) +static u64 notrace bcm2835_sched_read(void) { return readl_relaxed(system_clock); } @@ -110,7 +110,7 @@ static void __init bcm2835_timer_init(struct device_node *node) panic("Can't read clock-frequency"); system_clock = base + REG_COUNTER_LO; - setup_sched_clock(bcm2835_sched_read, 32, freq); + sched_clock_register(bcm2835_sched_read, 32, freq); clocksource_mmio_init(base + REG_COUNTER_LO, node->name, freq, 300, 32, clocksource_mmio_readl_up); -- cgit v0.10.2 From 5602d7c808aa99230ab1ef1598e2425cf2acedc5 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 18 Jul 2013 16:21:21 -0700 Subject: clocksource: dbx500-prcmu: Switch to sched_clock_register() The 32 bit sched_clock interface now supports 64 bits. Upgrade to the 64 bit function to allow us to remove the 32 bit registration interface. Cc: Srinidhi Kasagar Cc: Linus Walleij Acked-by: Linus Walleij Signed-off-by: Stephen Boyd Signed-off-by: John Stultz diff --git a/drivers/clocksource/clksrc-dbx500-prcmu.c b/drivers/clocksource/clksrc-dbx500-prcmu.c index a9fd4ad..b375106 100644 --- a/drivers/clocksource/clksrc-dbx500-prcmu.c +++ b/drivers/clocksource/clksrc-dbx500-prcmu.c @@ -53,7 +53,7 @@ static struct clocksource clocksource_dbx500_prcmu = { #ifdef CONFIG_CLKSRC_DBX500_PRCMU_SCHED_CLOCK -static u32 notrace dbx500_prcmu_sched_clock_read(void) +static u64 notrace dbx500_prcmu_sched_clock_read(void) { if (unlikely(!clksrc_dbx500_timer_base)) return 0; @@ -81,8 +81,7 @@ void __init clksrc_dbx500_prcmu_init(void __iomem *base) clksrc_dbx500_timer_base + PRCMU_TIMER_REF); } #ifdef CONFIG_CLKSRC_DBX500_PRCMU_SCHED_CLOCK - setup_sched_clock(dbx500_prcmu_sched_clock_read, - 32, RATE_32K); + sched_clock_register(dbx500_prcmu_sched_clock_read, 32, RATE_32K); #endif clocksource_register_hz(&clocksource_dbx500_prcmu, RATE_32K); } -- cgit v0.10.2 From fa8296ae62364d80bb82c4c011469ae3e423d509 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 18 Jul 2013 16:21:22 -0700 Subject: clocksource: dw_apb_timer_of: Switch to sched_clock_register() The 32 bit sched_clock interface now supports 64 bits. Upgrade to the 64 bit function to allow us to remove the 32 bit registration interface. Signed-off-by: Stephen Boyd Signed-off-by: John Stultz diff --git a/drivers/clocksource/dw_apb_timer_of.c b/drivers/clocksource/dw_apb_timer_of.c index 4cbae4f..003b230 100644 --- a/drivers/clocksource/dw_apb_timer_of.c +++ b/drivers/clocksource/dw_apb_timer_of.c @@ -106,7 +106,7 @@ static void add_clocksource(struct device_node *source_timer) sched_rate = rate; } -static u32 read_sched_clock(void) +static u64 read_sched_clock(void) { return __raw_readl(sched_io_base); } @@ -128,7 +128,7 @@ static void init_sched_clock(void) of_node_put(sched_timer); } - setup_sched_clock(read_sched_clock, 32, sched_rate); + sched_clock_register(read_sched_clock, 32, sched_rate); } static int num_called; -- cgit v0.10.2 From fcfca6ef6a35690648de6529b607674d4132b10e Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 18 Jul 2013 16:21:23 -0700 Subject: clocksource: mxs_timer: Switch to sched_clock_register() The 32 bit sched_clock interface now supports 64 bits. Upgrade to the 64 bit function to allow us to remove the 32 bit registration interface. Cc: Shawn Guo Acked-by: Shawn Guo Signed-off-by: Stephen Boyd Signed-off-by: John Stultz diff --git a/drivers/clocksource/mxs_timer.c b/drivers/clocksource/mxs_timer.c index 0f5e65f..445b68a 100644 --- a/drivers/clocksource/mxs_timer.c +++ b/drivers/clocksource/mxs_timer.c @@ -222,7 +222,7 @@ static struct clocksource clocksource_mxs = { .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; -static u32 notrace mxs_read_sched_clock_v2(void) +static u64 notrace mxs_read_sched_clock_v2(void) { return ~readl_relaxed(mxs_timrot_base + HW_TIMROT_RUNNING_COUNTn(1)); } @@ -236,7 +236,7 @@ static int __init mxs_clocksource_init(struct clk *timer_clk) else { clocksource_mmio_init(mxs_timrot_base + HW_TIMROT_RUNNING_COUNTn(1), "mxs_timer", c, 200, 32, clocksource_mmio_readl_down); - setup_sched_clock(mxs_read_sched_clock_v2, 32, c); + sched_clock_register(mxs_read_sched_clock_v2, 32, c); } return 0; -- cgit v0.10.2 From e25bc5f5ad192cf8782db64acb83962a0b27c4e0 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 18 Jul 2013 16:21:24 -0700 Subject: clocksource: nomadik: Switch to sched_clock_register() The 32 bit sched_clock interface now supports 64 bits. Upgrade to the 64 bit function to allow us to remove the 32 bit registration interface. Cc: Linus Walleij Acked-by: Linus Walleij Signed-off-by: Stephen Boyd Signed-off-by: John Stultz diff --git a/drivers/clocksource/nomadik-mtu.c b/drivers/clocksource/nomadik-mtu.c index 7d2c2c5..2242cd3 100644 --- a/drivers/clocksource/nomadik-mtu.c +++ b/drivers/clocksource/nomadik-mtu.c @@ -76,7 +76,7 @@ static struct delay_timer mtu_delay_timer; * local implementation which uses the clocksource to get some * better resolution when scheduling the kernel. */ -static u32 notrace nomadik_read_sched_clock(void) +static u64 notrace nomadik_read_sched_clock(void) { if (unlikely(!mtu_base)) return 0; @@ -230,7 +230,7 @@ static void __init __nmdk_timer_init(void __iomem *base, int irq, "mtu_0"); #ifdef CONFIG_CLKSRC_NOMADIK_MTU_SCHED_CLOCK - setup_sched_clock(nomadik_read_sched_clock, 32, rate); + sched_clock_register(nomadik_read_sched_clock, 32, rate); #endif /* Timer 1 is used for events, register irq and clockevents */ -- cgit v0.10.2 From 2902b30e0bd78686e7d891519dbfe2e4e43825fd Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 18 Jul 2013 16:21:25 -0700 Subject: clocksource: samsung_pwm_timer: Switch to sched_clock_register() The 32 bit sched_clock interface now supports 64 bits. Upgrade to the 64 bit function to allow us to remove the 32 bit registration interface. Cc: Tomasz Figa Cc: Kyungmin Park Cc: Kukjin Kim Signed-off-by: Stephen Boyd Signed-off-by: John Stultz diff --git a/drivers/clocksource/samsung_pwm_timer.c b/drivers/clocksource/samsung_pwm_timer.c index 584b547..09e8bc7 100644 --- a/drivers/clocksource/samsung_pwm_timer.c +++ b/drivers/clocksource/samsung_pwm_timer.c @@ -310,7 +310,7 @@ static void __iomem *samsung_timer_reg(void) * this wraps around for now, since it is just a relative time * stamp. (Inspired by U300 implementation.) */ -static u32 notrace samsung_read_sched_clock(void) +static u64 notrace samsung_read_sched_clock(void) { void __iomem *reg = samsung_timer_reg(); @@ -337,7 +337,7 @@ static void __init samsung_clocksource_init(void) samsung_time_setup(pwm.source_id, pwm.tcnt_max); samsung_time_start(pwm.source_id, true); - setup_sched_clock(samsung_read_sched_clock, + sched_clock_register(samsung_read_sched_clock, pwm.variant.bits, clock_rate); ret = clocksource_mmio_init(reg, "samsung_clocksource_timer", -- cgit v0.10.2 From 35702999b1f366ed80fc4bd94bd8ebc8a1c46954 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 18 Jul 2013 16:21:26 -0700 Subject: clocksource: tegra: Switch to sched_clock_register() The 32 bit sched_clock interface now supports 64 bits. Upgrade to the 64 bit function to allow us to remove the 32 bit registration interface. Cc: Stephen Warren Acked-by: Stephen Warren Tested-by: Stephen Warren Signed-off-by: Stephen Boyd Signed-off-by: John Stultz diff --git a/drivers/clocksource/tegra20_timer.c b/drivers/clocksource/tegra20_timer.c index 9396170..5cff616 100644 --- a/drivers/clocksource/tegra20_timer.c +++ b/drivers/clocksource/tegra20_timer.c @@ -98,7 +98,7 @@ static struct clock_event_device tegra_clockevent = { .set_mode = tegra_timer_set_mode, }; -static u32 notrace tegra_read_sched_clock(void) +static u64 notrace tegra_read_sched_clock(void) { return timer_readl(TIMERUS_CNTR_1US); } @@ -200,7 +200,7 @@ static void __init tegra20_init_timer(struct device_node *np) WARN(1, "Unknown clock rate"); } - setup_sched_clock(tegra_read_sched_clock, 32, 1000000); + sched_clock_register(tegra_read_sched_clock, 32, 1000000); if (clocksource_mmio_init(timer_reg_base + TIMERUS_CNTR_1US, "timer_us", 1000000, 300, 32, clocksource_mmio_readl_up)) { -- cgit v0.10.2 From d9dbcbe0ea29dac15a9ca70c274fec4ef100e187 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 18 Jul 2013 16:21:27 -0700 Subject: clocksource: time-armada-370-xp: Switch to sched_clock_register() The 32 bit sched_clock interface now supports 64 bits. Upgrade to the 64 bit function to allow us to remove the 32 bit registration interface. Cc: Gregory CLEMENT Signed-off-by: Stephen Boyd Signed-off-by: John Stultz diff --git a/drivers/clocksource/time-armada-370-xp.c b/drivers/clocksource/time-armada-370-xp.c index efdca32..2bec8dc 100644 --- a/drivers/clocksource/time-armada-370-xp.c +++ b/drivers/clocksource/time-armada-370-xp.c @@ -71,7 +71,7 @@ static u32 ticks_per_jiffy; static struct clock_event_device __percpu **percpu_armada_370_xp_evt; -static u32 notrace armada_370_xp_read_sched_clock(void) +static u64 notrace armada_370_xp_read_sched_clock(void) { return ~readl(timer_base + TIMER0_VAL_OFF); } @@ -258,7 +258,7 @@ void __init armada_370_xp_timer_init(void) /* * Set scale and timer for sched_clock. */ - setup_sched_clock(armada_370_xp_read_sched_clock, 32, timer_clk); + sched_clock_register(armada_370_xp_read_sched_clock, 32, timer_clk); /* * Setup free-running clocksource timer (interrupts -- cgit v0.10.2 From 130e6b25a28ff5b2421d6cae5f2bac1f5afdcfb0 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 18 Jul 2013 16:21:28 -0700 Subject: clocksource: sirf: Switch to sched_clock_register() and use 64 bits The 32 bit sched_clock interface now supports 64 bits. Upgrade to the 64 bit function to allow us to remove the 32 bit registration interface and use all 64 bits of this timer. Cc: Barry Song Signed-off-by: Stephen Boyd Signed-off-by: John Stultz diff --git a/drivers/clocksource/timer-prima2.c b/drivers/clocksource/timer-prima2.c index ef3cfb2..8a492d3 100644 --- a/drivers/clocksource/timer-prima2.c +++ b/drivers/clocksource/timer-prima2.c @@ -165,9 +165,9 @@ static struct irqaction sirfsoc_timer_irq = { }; /* Overwrite weak default sched_clock with more precise one */ -static u32 notrace sirfsoc_read_sched_clock(void) +static u64 notrace sirfsoc_read_sched_clock(void) { - return (u32)(sirfsoc_timer_read(NULL) & 0xffffffff); + return sirfsoc_timer_read(NULL); } static void __init sirfsoc_clockevent_init(void) @@ -206,7 +206,7 @@ static void __init sirfsoc_prima2_timer_init(struct device_node *np) BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, CLOCK_TICK_RATE)); - setup_sched_clock(sirfsoc_read_sched_clock, 32, CLOCK_TICK_RATE); + sched_clock_register(sirfsoc_read_sched_clock, 64, CLOCK_TICK_RATE); BUG_ON(setup_irq(sirfsoc_timer_irq.irq, &sirfsoc_timer_irq)); -- cgit v0.10.2 From f4e6e1ea19737077d958f2bc6c196eb579d97544 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 18 Jul 2013 16:21:29 -0700 Subject: clocksource: vf_pit_timer: Switch to sched_clock_register() The 32 bit sched_clock interface now supports 64 bits. Upgrade to the 64 bit function to allow us to remove the 32 bit registration interface. Cc: Jingchang Lu Cc: Fabio Estevam Signed-off-by: Stephen Boyd Signed-off-by: John Stultz diff --git a/drivers/clocksource/vf_pit_timer.c b/drivers/clocksource/vf_pit_timer.c index 587e020..02821b0 100644 --- a/drivers/clocksource/vf_pit_timer.c +++ b/drivers/clocksource/vf_pit_timer.c @@ -52,7 +52,7 @@ static inline void pit_irq_acknowledge(void) __raw_writel(PITTFLG_TIF, clkevt_base + PITTFLG); } -static unsigned int pit_read_sched_clock(void) +static u64 pit_read_sched_clock(void) { return __raw_readl(clksrc_base + PITCVAL); } @@ -64,7 +64,7 @@ static int __init pit_clocksource_init(unsigned long rate) __raw_writel(~0UL, clksrc_base + PITLDVAL); __raw_writel(PITTCTRL_TEN, clksrc_base + PITTCTRL); - setup_sched_clock(pit_read_sched_clock, 32, rate); + sched_clock_register(pit_read_sched_clock, 32, rate); return clocksource_mmio_init(clksrc_base + PITCVAL, "vf-pit", rate, 300, 32, clocksource_mmio_readl_down); } -- cgit v0.10.2 From a97ad0c4b447a132a322cedc3a5f7fa4cab4b304 Mon Sep 17 00:00:00 2001 From: Miroslav Lichvar Date: Thu, 1 Aug 2013 19:31:35 +0200 Subject: ntp: Make periodic RTC update more reliable The current code requires that the scheduled update of the RTC happens in the closest tick to the half of the second. This seems to be difficult to achieve reliably. The scheduled work may be missing the target time by a tick or two and be constantly rescheduled every second. Relax the limit to 10 ticks. As a typical RTC drifts in the 11-minute update interval by several milliseconds, this shouldn't affect the overall accuracy of the RTC much. Signed-off-by: Miroslav Lichvar Signed-off-by: John Stultz diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c index 8f5b3b9..ab1fa7c 100644 --- a/kernel/time/ntp.c +++ b/kernel/time/ntp.c @@ -475,6 +475,7 @@ static void sync_cmos_clock(struct work_struct *work) * called as close as possible to 500 ms before the new second starts. * This code is run on a timer. If the clock is set, that timer * may not expire at the correct time. Thus, we adjust... + * We want the clock to be within a couple of ticks from the target. */ if (!ntp_synced()) { /* @@ -485,7 +486,7 @@ static void sync_cmos_clock(struct work_struct *work) } getnstimeofday(&now); - if (abs(now.tv_nsec - (NSEC_PER_SEC / 2)) <= tick_nsec / 2) { + if (abs(now.tv_nsec - (NSEC_PER_SEC / 2)) <= tick_nsec * 5) { struct timespec adjust = now; fail = -ENODEV; -- cgit v0.10.2 From 7752572f18f98ee796e173334b088a1d621d2da4 Mon Sep 17 00:00:00 2001 From: Yanchuan Nian Date: Wed, 4 Sep 2013 09:25:24 +0800 Subject: x86/irq: Correct comment about i8259 initialization 0x30-0x3f have been used for ISA interrupts on i386 as well since 5 years ago, but old comments about i8259 initialization were still referring to the old i386 usage of this port range. Signed-off-by: Yanchuan Nian Cc: yinghai@kernel.org Cc: pavel@suse.cz Link: http://lkml.kernel.org/r/1378257924-29446-1-git-send-email-ycnian@gmail.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/i8259.c b/arch/x86/kernel/i8259.c index 9a5c460..2e977b5 100644 --- a/arch/x86/kernel/i8259.c +++ b/arch/x86/kernel/i8259.c @@ -312,8 +312,7 @@ static void init_8259A(int auto_eoi) */ outb_pic(0x11, PIC_MASTER_CMD); /* ICW1: select 8259A-1 init */ - /* ICW2: 8259A-1 IR0-7 mapped to 0x30-0x37 on x86-64, - to 0x20-0x27 on i386 */ + /* ICW2: 8259A-1 IR0-7 mapped to 0x30-0x37 */ outb_pic(IRQ0_VECTOR, PIC_MASTER_IMR); /* 8259A-1 (the master) has a slave on IR2 */ -- cgit v0.10.2 From 6f9dd30c22da4e48c4b7b837e9641f072e673161 Mon Sep 17 00:00:00 2001 From: Bojan Prtvar Date: Tue, 3 Sep 2013 08:56:20 +0200 Subject: efivars: Mark local function as static This fixes the following sparse warning drivers/firmware/efi/efivars.c:567:6: warning: symbol 'efivars_sysfs_exit' was not declared. Should it be static? Signed-off-by: Bojan Prtvar Signed-off-by: Matt Fleming diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c index 8a7432a..933eb02 100644 --- a/drivers/firmware/efi/efivars.c +++ b/drivers/firmware/efi/efivars.c @@ -564,7 +564,7 @@ static int efivar_sysfs_destroy(struct efivar_entry *entry, void *data) return 0; } -void efivars_sysfs_exit(void) +static void efivars_sysfs_exit(void) { /* Remove all entries and destroy */ __efivar_entry_iter(efivar_sysfs_destroy, &efivar_sysfs_list, NULL, NULL); -- cgit v0.10.2 From d02d0545f1fc62302fd9973a530b8029f1d9a9f1 Mon Sep 17 00:00:00 2001 From: Leif Lindholm Date: Thu, 5 Sep 2013 11:34:53 +0100 Subject: ia64: add early_memremap() alias for early_ioremap() early_ioremap() on IA64 chooses its mapping type based on the EFI memory map. This patch adds an alias "early_memremap()" to be used where the targeted location is memory rather than an i/o device. Signed-off-by: Leif Lindholm Acked-by: Tony Luck Signed-off-by: Matt Fleming diff --git a/arch/ia64/include/asm/io.h b/arch/ia64/include/asm/io.h index 74a7cc3..0d2bcb3 100644 --- a/arch/ia64/include/asm/io.h +++ b/arch/ia64/include/asm/io.h @@ -424,6 +424,7 @@ extern void __iomem * ioremap(unsigned long offset, unsigned long size); extern void __iomem * ioremap_nocache (unsigned long offset, unsigned long size); extern void iounmap (volatile void __iomem *addr); extern void __iomem * early_ioremap (unsigned long phys_addr, unsigned long size); +#define early_memremap(phys_addr, size) early_ioremap(phys_addr, size) extern void early_iounmap (volatile void __iomem *addr, unsigned long size); static inline void __iomem * ioremap_cache (unsigned long phys_addr, unsigned long size) { -- cgit v0.10.2 From 272686bf46a34f86d270cf192f68769667792026 Mon Sep 17 00:00:00 2001 From: Leif Lindholm Date: Thu, 5 Sep 2013 11:34:54 +0100 Subject: efi: x86: ia64: provide a generic efi_config_init() Common to (U)EFI support on all platforms is the global "efi" data structure, and the code that parses the System Table to locate addresses to populate that structure with. This patch adds both of these to the global EFI driver code and removes the local definition of the global "efi" data structure from the x86 and ia64 code. Squashed into one big patch to avoid breaking bisection. Signed-off-by: Leif Lindholm Acked-by: Tony Luck Signed-off-by: Matt Fleming diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c index 51bce59..da5b462 100644 --- a/arch/ia64/kernel/efi.c +++ b/arch/ia64/kernel/efi.c @@ -44,10 +44,15 @@ #define EFI_DEBUG 0 +static __initdata unsigned long palo_phys; + +static __initdata efi_config_table_type_t arch_tables[] = { + {PROCESSOR_ABSTRACTION_LAYER_OVERWRITE_GUID, "PALO", &palo_phys}, + {NULL_GUID, NULL, 0}, +}; + extern efi_status_t efi_call_phys (void *, ...); -struct efi efi; -EXPORT_SYMBOL(efi); static efi_runtime_services_t *runtime; static u64 mem_limit = ~0UL, max_addr = ~0UL, min_addr = 0UL; @@ -423,9 +428,9 @@ static u8 __init palo_checksum(u8 *buffer, u32 length) * Parse and handle PALO table which is published at: * http://www.dig64.org/home/DIG64_PALO_R1_0.pdf */ -static void __init handle_palo(unsigned long palo_phys) +static void __init handle_palo(unsigned long phys_addr) { - struct palo_table *palo = __va(palo_phys); + struct palo_table *palo = __va(phys_addr); u8 checksum; if (strncmp(palo->signature, PALO_SIG, sizeof(PALO_SIG) - 1)) { @@ -467,12 +472,10 @@ void __init efi_init (void) { void *efi_map_start, *efi_map_end; - efi_config_table_t *config_tables; efi_char16_t *c16; u64 efi_desc_size; char *cp, vendor[100] = "unknown"; int i; - unsigned long palo_phys; /* * It's too early to be able to use the standard kernel command line @@ -514,8 +517,6 @@ efi_init (void) efi.systab->hdr.revision >> 16, efi.systab->hdr.revision & 0xffff); - config_tables = __va(efi.systab->tables); - /* Show what we know for posterity */ c16 = __va(efi.systab->fw_vendor); if (c16) { @@ -528,43 +529,10 @@ efi_init (void) efi.systab->hdr.revision >> 16, efi.systab->hdr.revision & 0xffff, vendor); - efi.mps = EFI_INVALID_TABLE_ADDR; - efi.acpi = EFI_INVALID_TABLE_ADDR; - efi.acpi20 = EFI_INVALID_TABLE_ADDR; - efi.smbios = EFI_INVALID_TABLE_ADDR; - efi.sal_systab = EFI_INVALID_TABLE_ADDR; - efi.boot_info = EFI_INVALID_TABLE_ADDR; - efi.hcdp = EFI_INVALID_TABLE_ADDR; - efi.uga = EFI_INVALID_TABLE_ADDR; - palo_phys = EFI_INVALID_TABLE_ADDR; - for (i = 0; i < (int) efi.systab->nr_tables; i++) { - if (efi_guidcmp(config_tables[i].guid, MPS_TABLE_GUID) == 0) { - efi.mps = config_tables[i].table; - printk(" MPS=0x%lx", config_tables[i].table); - } else if (efi_guidcmp(config_tables[i].guid, ACPI_20_TABLE_GUID) == 0) { - efi.acpi20 = config_tables[i].table; - printk(" ACPI 2.0=0x%lx", config_tables[i].table); - } else if (efi_guidcmp(config_tables[i].guid, ACPI_TABLE_GUID) == 0) { - efi.acpi = config_tables[i].table; - printk(" ACPI=0x%lx", config_tables[i].table); - } else if (efi_guidcmp(config_tables[i].guid, SMBIOS_TABLE_GUID) == 0) { - efi.smbios = config_tables[i].table; - printk(" SMBIOS=0x%lx", config_tables[i].table); - } else if (efi_guidcmp(config_tables[i].guid, SAL_SYSTEM_TABLE_GUID) == 0) { - efi.sal_systab = config_tables[i].table; - printk(" SALsystab=0x%lx", config_tables[i].table); - } else if (efi_guidcmp(config_tables[i].guid, HCDP_TABLE_GUID) == 0) { - efi.hcdp = config_tables[i].table; - printk(" HCDP=0x%lx", config_tables[i].table); - } else if (efi_guidcmp(config_tables[i].guid, - PROCESSOR_ABSTRACTION_LAYER_OVERWRITE_GUID) == 0) { - palo_phys = config_tables[i].table; - printk(" PALO=0x%lx", config_tables[i].table); - } - } - printk("\n"); + if (efi_config_init(arch_tables) != 0) + return; if (palo_phys != EFI_INVALID_TABLE_ADDR) handle_palo(palo_phys); diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 90f6ed1..ed2be58 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -60,19 +60,6 @@ static efi_char16_t efi_dummy_name[6] = { 'D', 'U', 'M', 'M', 'Y', 0 }; -struct efi __read_mostly efi = { - .mps = EFI_INVALID_TABLE_ADDR, - .acpi = EFI_INVALID_TABLE_ADDR, - .acpi20 = EFI_INVALID_TABLE_ADDR, - .smbios = EFI_INVALID_TABLE_ADDR, - .sal_systab = EFI_INVALID_TABLE_ADDR, - .boot_info = EFI_INVALID_TABLE_ADDR, - .hcdp = EFI_INVALID_TABLE_ADDR, - .uga = EFI_INVALID_TABLE_ADDR, - .uv_systab = EFI_INVALID_TABLE_ADDR, -}; -EXPORT_SYMBOL(efi); - struct efi_memory_map memmap; static struct efi efi_phys __initdata; @@ -80,6 +67,13 @@ static efi_system_table_t efi_systab __initdata; unsigned long x86_efi_facility; +static __initdata efi_config_table_type_t arch_tables[] = { +#ifdef CONFIG_X86_UV + {UV_SYSTEM_TABLE_GUID, "UVsystab", &efi.uv_systab}, +#endif + {NULL_GUID, NULL, 0}, +}; + /* * Returns 1 if 'facility' is enabled, 0 otherwise. */ @@ -578,80 +572,6 @@ static int __init efi_systab_init(void *phys) return 0; } -static int __init efi_config_init(u64 tables, int nr_tables) -{ - void *config_tables, *tablep; - int i, sz; - - if (efi_enabled(EFI_64BIT)) - sz = sizeof(efi_config_table_64_t); - else - sz = sizeof(efi_config_table_32_t); - - /* - * Let's see what config tables the firmware passed to us. - */ - config_tables = early_ioremap(tables, nr_tables * sz); - if (config_tables == NULL) { - pr_err("Could not map Configuration table!\n"); - return -ENOMEM; - } - - tablep = config_tables; - pr_info(""); - for (i = 0; i < efi.systab->nr_tables; i++) { - efi_guid_t guid; - unsigned long table; - - if (efi_enabled(EFI_64BIT)) { - u64 table64; - guid = ((efi_config_table_64_t *)tablep)->guid; - table64 = ((efi_config_table_64_t *)tablep)->table; - table = table64; -#ifdef CONFIG_X86_32 - if (table64 >> 32) { - pr_cont("\n"); - pr_err("Table located above 4GB, disabling EFI.\n"); - early_iounmap(config_tables, - efi.systab->nr_tables * sz); - return -EINVAL; - } -#endif - } else { - guid = ((efi_config_table_32_t *)tablep)->guid; - table = ((efi_config_table_32_t *)tablep)->table; - } - if (!efi_guidcmp(guid, MPS_TABLE_GUID)) { - efi.mps = table; - pr_cont(" MPS=0x%lx ", table); - } else if (!efi_guidcmp(guid, ACPI_20_TABLE_GUID)) { - efi.acpi20 = table; - pr_cont(" ACPI 2.0=0x%lx ", table); - } else if (!efi_guidcmp(guid, ACPI_TABLE_GUID)) { - efi.acpi = table; - pr_cont(" ACPI=0x%lx ", table); - } else if (!efi_guidcmp(guid, SMBIOS_TABLE_GUID)) { - efi.smbios = table; - pr_cont(" SMBIOS=0x%lx ", table); -#ifdef CONFIG_X86_UV - } else if (!efi_guidcmp(guid, UV_SYSTEM_TABLE_GUID)) { - efi.uv_systab = table; - pr_cont(" UVsystab=0x%lx ", table); -#endif - } else if (!efi_guidcmp(guid, HCDP_TABLE_GUID)) { - efi.hcdp = table; - pr_cont(" HCDP=0x%lx ", table); - } else if (!efi_guidcmp(guid, UGA_IO_PROTOCOL_GUID)) { - efi.uga = table; - pr_cont(" UGA=0x%lx ", table); - } - tablep += sz; - } - pr_cont("\n"); - early_iounmap(config_tables, efi.systab->nr_tables * sz); - return 0; -} - static int __init efi_runtime_init(void) { efi_runtime_services_t *runtime; @@ -745,7 +665,7 @@ void __init efi_init(void) efi.systab->hdr.revision >> 16, efi.systab->hdr.revision & 0xffff, vendor); - if (efi_config_init(efi.systab->tables, efi.systab->nr_tables)) + if (efi_config_init(arch_tables)) return; set_bit(EFI_CONFIG_TABLES, &x86_efi_facility); diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 5145fa3..e1010d4 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -13,11 +13,27 @@ * This file is released under the GPLv2. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include #include #include +#include + +struct efi __read_mostly efi = { + .mps = EFI_INVALID_TABLE_ADDR, + .acpi = EFI_INVALID_TABLE_ADDR, + .acpi20 = EFI_INVALID_TABLE_ADDR, + .smbios = EFI_INVALID_TABLE_ADDR, + .sal_systab = EFI_INVALID_TABLE_ADDR, + .boot_info = EFI_INVALID_TABLE_ADDR, + .hcdp = EFI_INVALID_TABLE_ADDR, + .uga = EFI_INVALID_TABLE_ADDR, + .uv_systab = EFI_INVALID_TABLE_ADDR, +}; +EXPORT_SYMBOL(efi); static struct kobject *efi_kobj; static struct kobject *efivars_kobj; @@ -132,3 +148,95 @@ err_put: } subsys_initcall(efisubsys_init); + + +static __initdata efi_config_table_type_t common_tables[] = { + {ACPI_20_TABLE_GUID, "ACPI 2.0", &efi.acpi20}, + {ACPI_TABLE_GUID, "ACPI", &efi.acpi}, + {HCDP_TABLE_GUID, "HCDP", &efi.hcdp}, + {MPS_TABLE_GUID, "MPS", &efi.mps}, + {SAL_SYSTEM_TABLE_GUID, "SALsystab", &efi.sal_systab}, + {SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios}, + {UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga}, + {NULL_GUID, NULL, 0}, +}; + +static __init int match_config_table(efi_guid_t *guid, + unsigned long table, + efi_config_table_type_t *table_types) +{ + u8 str[EFI_VARIABLE_GUID_LEN + 1]; + int i; + + if (table_types) { + efi_guid_unparse(guid, str); + + for (i = 0; efi_guidcmp(table_types[i].guid, NULL_GUID); i++) { + efi_guid_unparse(&table_types[i].guid, str); + + if (!efi_guidcmp(*guid, table_types[i].guid)) { + *(table_types[i].ptr) = table; + pr_cont(" %s=0x%lx ", + table_types[i].name, table); + return 1; + } + } + } + + return 0; +} + +int __init efi_config_init(efi_config_table_type_t *arch_tables) +{ + void *config_tables, *tablep; + int i, sz; + + if (efi_enabled(EFI_64BIT)) + sz = sizeof(efi_config_table_64_t); + else + sz = sizeof(efi_config_table_32_t); + + /* + * Let's see what config tables the firmware passed to us. + */ + config_tables = early_memremap(efi.systab->tables, + efi.systab->nr_tables * sz); + if (config_tables == NULL) { + pr_err("Could not map Configuration table!\n"); + return -ENOMEM; + } + + tablep = config_tables; + pr_info(""); + for (i = 0; i < efi.systab->nr_tables; i++) { + efi_guid_t guid; + unsigned long table; + + if (efi_enabled(EFI_64BIT)) { + u64 table64; + guid = ((efi_config_table_64_t *)tablep)->guid; + table64 = ((efi_config_table_64_t *)tablep)->table; + table = table64; +#ifndef CONFIG_64BIT + if (table64 >> 32) { + pr_cont("\n"); + pr_err("Table located above 4GB, disabling EFI.\n"); + early_iounmap(config_tables, + efi.systab->nr_tables * sz); + return -EINVAL; + } +#endif + } else { + guid = ((efi_config_table_32_t *)tablep)->guid; + table = ((efi_config_table_32_t *)tablep)->table; + } + + if (!match_config_table(&guid, table, common_tables)) + match_config_table(&guid, table, arch_tables); + + tablep += sz; + } + pr_cont("\n"); + early_iounmap(config_tables, efi.systab->nr_tables * sz); + return 0; +} diff --git a/include/linux/efi.h b/include/linux/efi.h index 5f8f176..09d9e42 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -404,6 +404,12 @@ typedef struct { unsigned long table; } efi_config_table_t; +typedef struct { + efi_guid_t guid; + const char *name; + unsigned long *ptr; +} efi_config_table_type_t; + #define EFI_SYSTEM_TABLE_SIGNATURE ((u64)0x5453595320494249ULL) #define EFI_2_30_SYSTEM_TABLE_REVISION ((2 << 16) | (30)) @@ -587,6 +593,7 @@ static inline efi_status_t efi_query_variable_store(u32 attributes, unsigned lon } #endif extern void __iomem *efi_lookup_mapped_addr(u64 phys_addr); +extern int efi_config_init(efi_config_table_type_t *arch_tables); extern u64 efi_get_iobase (void); extern u32 efi_mem_type (unsigned long phys_addr); extern u64 efi_mem_attributes (unsigned long phys_addr); -- cgit v0.10.2 From 258f6fd738221766b512cd8c7120563b78d62829 Mon Sep 17 00:00:00 2001 From: Leif Lindholm Date: Thu, 5 Sep 2013 11:34:55 +0100 Subject: efi: x86: make efi_lookup_mapped_addr() a common function efi_lookup_mapped_addr() is a handy utility for other platforms than x86. Move it from arch/x86 to drivers/firmware. Add memmap pointer to global efi structure, and initialise it on x86. Signed-off-by: Leif Lindholm Signed-off-by: Matt Fleming diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index ed2be58..fbc1d70 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -393,6 +393,8 @@ int __init efi_memblock_x86_reserve_range(void) memblock_reserve(pmap, memmap.nr_map * memmap.desc_size); + efi.memmap = &memmap; + return 0; } @@ -736,34 +738,6 @@ static void __init runtime_code_page_mkexec(void) } } -/* - * We can't ioremap data in EFI boot services RAM, because we've already mapped - * it as RAM. So, look it up in the existing EFI memory map instead. Only - * callable after efi_enter_virtual_mode and before efi_free_boot_services. - */ -void __iomem *efi_lookup_mapped_addr(u64 phys_addr) -{ - void *p; - if (WARN_ON(!memmap.map)) - return NULL; - for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { - efi_memory_desc_t *md = p; - u64 size = md->num_pages << EFI_PAGE_SHIFT; - u64 end = md->phys_addr + size; - if (!(md->attribute & EFI_MEMORY_RUNTIME) && - md->type != EFI_BOOT_SERVICES_CODE && - md->type != EFI_BOOT_SERVICES_DATA) - continue; - if (!md->virt_addr) - continue; - if (phys_addr >= md->phys_addr && phys_addr < end) { - phys_addr += md->virt_addr - md->phys_addr; - return (__force void __iomem *)(unsigned long)phys_addr; - } - } - return NULL; -} - void efi_memory_uc(u64 addr, unsigned long size) { unsigned long page_shift = 1UL << EFI_PAGE_SHIFT; diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index e1010d4..2e2fbde 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -150,6 +150,38 @@ err_put: subsys_initcall(efisubsys_init); +/* + * We can't ioremap data in EFI boot services RAM, because we've already mapped + * it as RAM. So, look it up in the existing EFI memory map instead. Only + * callable after efi_enter_virtual_mode and before efi_free_boot_services. + */ +void __iomem *efi_lookup_mapped_addr(u64 phys_addr) +{ + struct efi_memory_map *map; + void *p; + map = efi.memmap; + if (!map) + return NULL; + if (WARN_ON(!map->map)) + return NULL; + for (p = map->map; p < map->map_end; p += map->desc_size) { + efi_memory_desc_t *md = p; + u64 size = md->num_pages << EFI_PAGE_SHIFT; + u64 end = md->phys_addr + size; + if (!(md->attribute & EFI_MEMORY_RUNTIME) && + md->type != EFI_BOOT_SERVICES_CODE && + md->type != EFI_BOOT_SERVICES_DATA) + continue; + if (!md->virt_addr) + continue; + if (phys_addr >= md->phys_addr && phys_addr < end) { + phys_addr += md->virt_addr - md->phys_addr; + return (__force void __iomem *)(unsigned long)phys_addr; + } + } + return NULL; +} + static __initdata efi_config_table_type_t common_tables[] = { {ACPI_20_TABLE_GUID, "ACPI 2.0", &efi.acpi20}, {ACPI_TABLE_GUID, "ACPI", &efi.acpi}, diff --git a/include/linux/efi.h b/include/linux/efi.h index 09d9e42..c084b6d 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -558,6 +558,7 @@ extern struct efi { efi_get_next_high_mono_count_t *get_next_high_mono_count; efi_reset_system_t *reset_system; efi_set_virtual_address_map_t *set_virtual_address_map; + struct efi_memory_map *memmap; } efi; static inline int -- cgit v0.10.2 From 6979287a7df66a92d6f308338e972a406f9ef842 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 6 Sep 2013 19:07:09 -0700 Subject: x86/mm: Add 'step_size' comments to init_mem_mapping() Current code uses macro to shift by 5, but there is no explanation why there's no worry about an overflow there. Signed-off-by: Yinghai Lu Cc: Pekka Enberg Cc: Jacob Shin Link: http://lkml.kernel.org/r/1378519629-10433-1-git-send-email-yinghai@kernel.org Signed-off-by: Ingo Molnar diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 04664cd..ce32017 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -399,8 +399,25 @@ static unsigned long __init init_range_memory_mapping( return mapped_ram_size; } -/* (PUD_SHIFT-PMD_SHIFT)/2 */ -#define STEP_SIZE_SHIFT 5 +static unsigned long __init get_new_step_size(unsigned long step_size) +{ + /* + * Explain why we shift by 5 and why we don't have to worry about + * 'step_size << 5' overflowing: + * + * initial mapped size is PMD_SIZE (2M). + * We can not set step_size to be PUD_SIZE (1G) yet. + * In worse case, when we cross the 1G boundary, and + * PG_LEVEL_2M is not set, we will need 1+1+512 pages (2M + 8k) + * to map 1G range with PTE. Use 5 as shift for now. + * + * Don't need to worry about overflow, on 32bit, when step_size + * is 0, round_down() returns 0 for start, and that turns it + * into 0x100000000ULL. + */ + return step_size << 5; +} + void __init init_mem_mapping(void) { unsigned long end, real_end, start, last_start; @@ -449,7 +466,7 @@ void __init init_mem_mapping(void) min_pfn_mapped = last_start >> PAGE_SHIFT; /* only increase step_size after big range get mapped */ if (new_mapped_ram_size > mapped_ram_size) - step_size <<= STEP_SIZE_SHIFT; + step_size = get_new_step_size(step_size); mapped_ram_size += new_mapped_ram_size; } -- cgit v0.10.2 From 8fec6f74a62c4e3d6897bc91f2eb08dfbadfe6da Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 10 Sep 2013 17:06:26 +0530 Subject: spi: atmel: Fix incorrect error path 'irq' was not released when clk_prepare_enable failed. Fix it. Signed-off-by: Sachin Kamat Acked-by: Nicolas Ferre Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index fd7cc56..d4ac60b 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -1583,7 +1583,7 @@ static int atmel_spi_probe(struct platform_device *pdev) /* Initialize the hardware */ ret = clk_prepare_enable(clk); if (ret) - goto out_unmap_regs; + goto out_free_irq; spi_writel(as, CR, SPI_BIT(SWRST)); spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */ if (as->caps.has_wdrbt) { @@ -1614,6 +1614,7 @@ out_free_dma: spi_writel(as, CR, SPI_BIT(SWRST)); spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */ clk_disable_unprepare(clk); +out_free_irq: free_irq(irq, master); out_unmap_regs: iounmap(as->regs); -- cgit v0.10.2 From 5a523605afa7d3b54b2e7041f8c9e6bc39872a7e Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 10 Sep 2013 15:45:05 +0530 Subject: regulator: core: provide fixed voltage in desc for single voltage rail If given rail has the single voltage (n_voltages = 1) then provide the rail voltage through regulator descriptor so that core can use this value for finding voltage. This will avoid the implementation of the callback for get_voltage() or list_voltage() callback on regulator driver. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index a01b8b3..abc899e 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2489,6 +2489,8 @@ static int _regulator_get_voltage(struct regulator_dev *rdev) ret = rdev->desc->ops->get_voltage(rdev); } else if (rdev->desc->ops->list_voltage) { ret = rdev->desc->ops->list_voltage(rdev, 0); + } else if (rdev->desc->fixed_uV && (rdev->desc->n_voltages == 1)) { + ret = rdev->desc->fixed_uV; } else { return -EINVAL; } @@ -3170,7 +3172,8 @@ static int add_regulator_attributes(struct regulator_dev *rdev) /* some attributes need specific methods to be displayed */ if ((ops->get_voltage && ops->get_voltage(rdev) >= 0) || (ops->get_voltage_sel && ops->get_voltage_sel(rdev) >= 0) || - (ops->list_voltage && ops->list_voltage(rdev, 0) >= 0)) { + (ops->list_voltage && ops->list_voltage(rdev, 0) >= 0) || + (rdev->desc->fixed_uV && (rdev->desc->n_voltages == 1))) { status = device_create_file(dev, &dev_attr_microvolts); if (status < 0) return status; diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 67e13aa..9e8241a 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -207,6 +207,7 @@ enum regulator_type { * @min_uV: Voltage given by the lowest selector (if linear mapping) * @uV_step: Voltage increase with each selector (if linear mapping) * @linear_min_sel: Minimal selector for starting linear mapping + * @fixed_uV: Fixed voltage of rails. * @ramp_delay: Time to settle down after voltage change (unit: uV/us) * @volt_table: Voltage mapping table (if table based mapping) * @@ -239,6 +240,7 @@ struct regulator_desc { unsigned int min_uV; unsigned int uV_step; unsigned int linear_min_sel; + int fixed_uV; unsigned int ramp_delay; const struct regulator_linear_range *linear_ranges; -- cgit v0.10.2 From c368e5fc2a190923b786f2de3e79430ea3566a25 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 10 Sep 2013 15:45:06 +0530 Subject: regulator: fixed: get rid of {get|list}_voltage() Provide the rail supply voltage through descriptor to the core and remove the callbacks which implement the get_voltage and list_voltage. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index 7610920..de811f3 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -34,7 +34,6 @@ struct fixed_voltage_data { struct regulator_desc desc; struct regulator_dev *dev; - int microvolts; }; @@ -108,30 +107,7 @@ of_get_fixed_voltage_config(struct device *dev) return config; } -static int fixed_voltage_get_voltage(struct regulator_dev *dev) -{ - struct fixed_voltage_data *data = rdev_get_drvdata(dev); - - if (data->microvolts) - return data->microvolts; - else - return -EINVAL; -} - -static int fixed_voltage_list_voltage(struct regulator_dev *dev, - unsigned selector) -{ - struct fixed_voltage_data *data = rdev_get_drvdata(dev); - - if (selector != 0) - return -EINVAL; - - return data->microvolts; -} - static struct regulator_ops fixed_voltage_ops = { - .get_voltage = fixed_voltage_get_voltage, - .list_voltage = fixed_voltage_list_voltage, }; static int reg_fixed_voltage_probe(struct platform_device *pdev) @@ -186,7 +162,7 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) if (config->microvolts) drvdata->desc.n_voltages = 1; - drvdata->microvolts = config->microvolts; + drvdata->desc.fixed_uV = config->microvolts; if (config->gpio >= 0) cfg.ena_gpio = config->gpio; @@ -222,7 +198,7 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) platform_set_drvdata(pdev, drvdata); dev_dbg(&pdev->dev, "%s supplying %duV\n", drvdata->desc.name, - drvdata->microvolts); + drvdata->desc.fixed_uV); return 0; -- cgit v0.10.2 From ff47ab4ff3cddfa7bc1b25b990e24abe2ae474ff Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Fri, 16 Aug 2013 14:17:19 -0700 Subject: x86: Add 1/2/4/8 byte optimization to 64bit __copy_{from,to}_user_inatomic The 64bit __copy_{from,to}_user_inatomic always called copy_from_user_generic, but skipped the special optimizations for 1/2/4/8 byte accesses. This especially hurts the futex call, which accesses the 4 byte futex user value with a complicated fast string operation in a function call, instead of a single movl. Use __copy_{from,to}_user for _inatomic instead to get the same optimizations. The only problem was the might_fault() in those functions. So move that to new wrapper and call __copy_{f,t}_user_nocheck() from *_inatomic directly. 32bit already did this correctly by duplicating the code. Signed-off-by: Andi Kleen Link: http://lkml.kernel.org/r/1376687844-19857-2-git-send-email-andi@firstfloor.org Signed-off-by: H. Peter Anvin diff --git a/arch/x86/include/asm/uaccess_64.h b/arch/x86/include/asm/uaccess_64.h index 4f7923d..64476bb 100644 --- a/arch/x86/include/asm/uaccess_64.h +++ b/arch/x86/include/asm/uaccess_64.h @@ -77,11 +77,10 @@ int copy_to_user(void __user *dst, const void *src, unsigned size) } static __always_inline __must_check -int __copy_from_user(void *dst, const void __user *src, unsigned size) +int __copy_from_user_nocheck(void *dst, const void __user *src, unsigned size) { int ret = 0; - might_fault(); if (!__builtin_constant_p(size)) return copy_user_generic(dst, (__force void *)src, size); switch (size) { @@ -121,11 +120,17 @@ int __copy_from_user(void *dst, const void __user *src, unsigned size) } static __always_inline __must_check -int __copy_to_user(void __user *dst, const void *src, unsigned size) +int __copy_from_user(void *dst, const void __user *src, unsigned size) +{ + might_fault(); + return __copy_from_user_nocheck(dst, src, size); +} + +static __always_inline __must_check +int __copy_to_user_nocheck(void __user *dst, const void *src, unsigned size) { int ret = 0; - might_fault(); if (!__builtin_constant_p(size)) return copy_user_generic((__force void *)dst, src, size); switch (size) { @@ -165,6 +170,13 @@ int __copy_to_user(void __user *dst, const void *src, unsigned size) } static __always_inline __must_check +int __copy_to_user(void __user *dst, const void *src, unsigned size) +{ + might_fault(); + return __copy_to_user_nocheck(dst, src, size); +} + +static __always_inline __must_check int __copy_in_user(void __user *dst, const void __user *src, unsigned size) { int ret = 0; @@ -220,13 +232,13 @@ int __copy_in_user(void __user *dst, const void __user *src, unsigned size) static __must_check __always_inline int __copy_from_user_inatomic(void *dst, const void __user *src, unsigned size) { - return copy_user_generic(dst, (__force const void *)src, size); + return __copy_from_user_nocheck(dst, (__force const void *)src, size); } static __must_check __always_inline int __copy_to_user_inatomic(void __user *dst, const void *src, unsigned size) { - return copy_user_generic((__force void *)dst, src, size); + return __copy_to_user_nocheck((__force void *)dst, src, size); } extern long __copy_user_nocache(void *dst, const void __user *src, -- cgit v0.10.2 From 2dbf0116aa8c7bfa900352d3f7b2609748fcc1c5 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 5 Sep 2013 20:37:38 -0700 Subject: perf/x86/intel: Avoid checkpointed counters causing excessive TSX aborts With checkpointed counters there can be a situation where the counter is overflowing, aborts the transaction, is set back to a non overflowing checkpoint, causes interupt. The interrupt doesn't see the overflow because it has been checkpointed. This is then a spurious PMI, typically with a ugly NMI message. It can also lead to excessive aborts. Avoid this problem by: - Using the full counter width for counting counters (earlier patch) - Forbid sampling for checkpointed counters. It's not too useful anyways, checkpointing is mainly for counting. The check is approximate (to still handle KVM), but should catch the majority of cases. - On a PMI always set back checkpointed counters to zero. Signed-off-by: Andi Kleen Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1378438661-24765-2-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 9db76c3..57d64b7 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -1282,6 +1282,11 @@ static void intel_pmu_enable_event(struct perf_event *event) __x86_pmu_enable_event(hwc, ARCH_PERFMON_EVENTSEL_ENABLE); } +static inline bool event_is_checkpointed(struct perf_event *event) +{ + return (event->hw.config & HSW_IN_TX_CHECKPOINTED) != 0; +} + /* * Save and restart an expired event. Called by NMI contexts, * so it has to be careful about preempting normal event ops: @@ -1289,6 +1294,17 @@ static void intel_pmu_enable_event(struct perf_event *event) int intel_pmu_save_and_restart(struct perf_event *event) { x86_perf_event_update(event); + /* + * For a checkpointed counter always reset back to 0. This + * avoids a situation where the counter overflows, aborts the + * transaction and is then set back to shortly before the + * overflow, and overflows and aborts again. + */ + if (unlikely(event_is_checkpointed(event))) { + /* No race with NMIs because the counter should not be armed */ + wrmsrl(event->hw.event_base, 0); + local64_set(&event->hw.prev_count, 0); + } return x86_perf_event_set_period(event); } @@ -1372,6 +1388,13 @@ again: x86_pmu.drain_pebs(regs); } + /* + * To avoid spurious interrupts with perf stat always reset checkpointed + * counters. + */ + if (cpuc->events[2] && event_is_checkpointed(cpuc->events[2])) + status |= (1ULL << 2); + for_each_set_bit(bit, (unsigned long *)&status, X86_PMC_IDX_MAX) { struct perf_event *event = cpuc->events[bit]; @@ -1837,6 +1860,20 @@ static int hsw_hw_config(struct perf_event *event) event->attr.precise_ip > 0)) return -EOPNOTSUPP; + if (event_is_checkpointed(event)) { + /* + * Sampling of checkpointed events can cause situations where + * the CPU constantly aborts because of a overflow, which is + * then checkpointed back and ignored. Forbid checkpointing + * for sampling. + * + * But still allow a long sampling period, so that perf stat + * from KVM works. + */ + if (event->attr.sample_period > 0 && + event->attr.sample_period < 0x7fffffff) + return -EOPNOTSUPP; + } return 0; } -- cgit v0.10.2 From 748e86aa90edfddfa6016f1cf383ff5bc6aada91 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 5 Sep 2013 20:37:39 -0700 Subject: perf/x86: Report TSX transaction abort cost as weight Use the existing weight reporting facility to report the transaction abort cost, that is the number of cycles wasted in aborts. Haswell reports this in the PEBS record. This was in fact the original user for weight. This is a very useful sort key to concentrate on the most costly aborts and a good metric for TSX tuning. Signed-off-by: Andi Kleen Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1378438661-24765-3-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index 63438aa..104cbba 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -182,16 +182,29 @@ struct pebs_record_nhm { * Same as pebs_record_nhm, with two additional fields. */ struct pebs_record_hsw { - struct pebs_record_nhm nhm; - /* - * Real IP of the event. In the Intel documentation this - * is called eventingrip. - */ - u64 real_ip; - /* - * TSX tuning information field: abort cycles and abort flags. - */ - u64 tsx_tuning; + u64 flags, ip; + u64 ax, bx, cx, dx; + u64 si, di, bp, sp; + u64 r8, r9, r10, r11; + u64 r12, r13, r14, r15; + u64 status, dla, dse, lat; + u64 real_ip; /* the actual eventing ip */ + u64 tsx_tuning; /* TSX abort cycles and flags */ +}; + +union hsw_tsx_tuning { + struct { + u32 cycles_last_block : 32, + hle_abort : 1, + rtm_abort : 1, + instruction_abort : 1, + non_instruction_abort : 1, + retry : 1, + data_conflict : 1, + capacity_writes : 1, + capacity_reads : 1; + }; + u64 value; }; void init_debug_store_on_cpu(int cpu) @@ -785,16 +798,26 @@ static int intel_pmu_pebs_fixup_ip(struct pt_regs *regs) return 0; } +static inline u64 intel_hsw_weight(struct pebs_record_hsw *pebs) +{ + if (pebs->tsx_tuning) { + union hsw_tsx_tuning tsx = { .value = pebs->tsx_tuning }; + return tsx.cycles_last_block; + } + return 0; +} + static void __intel_pmu_pebs_event(struct perf_event *event, struct pt_regs *iregs, void *__pebs) { /* * We cast to pebs_record_nhm to get the load latency data * if extra_reg MSR_PEBS_LD_LAT_THRESHOLD used + * We cast to the biggest PEBS record are careful not + * to access out-of-bounds members. */ struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); - struct pebs_record_nhm *pebs = __pebs; - struct pebs_record_hsw *pebs_hsw = __pebs; + struct pebs_record_hsw *pebs = __pebs; struct perf_sample_data data; struct pt_regs regs; u64 sample_type; @@ -853,7 +876,7 @@ static void __intel_pmu_pebs_event(struct perf_event *event, regs.sp = pebs->sp; if (event->attr.precise_ip > 1 && x86_pmu.intel_cap.pebs_format >= 2) { - regs.ip = pebs_hsw->real_ip; + regs.ip = pebs->real_ip; regs.flags |= PERF_EFLAGS_EXACT; } else if (event->attr.precise_ip > 1 && intel_pmu_pebs_fixup_ip(®s)) regs.flags |= PERF_EFLAGS_EXACT; @@ -864,6 +887,12 @@ static void __intel_pmu_pebs_event(struct perf_event *event, x86_pmu.intel_cap.pebs_format >= 1) data.addr = pebs->dla; + /* Only set the TSX weight when no memory weight was requested. */ + if ((event->attr.sample_type & PERF_SAMPLE_WEIGHT) && + !fll && + (x86_pmu.intel_cap.pebs_format >= 2)) + data.weight = intel_hsw_weight(pebs); + if (has_branch_stack(event)) data.br_stack = &cpuc->lbr_stack; -- cgit v0.10.2 From 4b2c4f1f1b71fe18381f089c501ac21cd2167dfa Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 5 Sep 2013 20:37:40 -0700 Subject: perf/x86/intel: Add Haswell TSX event aliases Add TSX event aliases, and export them from the kernel to perf. These are used by perf stat -T and to allow more user friendly access to events. The events are designed to be fairly generic and may also apply to other architectures implementing HTM. They all cover common situations that happens during tuning of transactional code. For Haswell we have to separate the HLE and RTM events, as they are separate in the PMU. This adds the following events: tx-start Count start transaction (used by perf stat -T) tx-commit Count commit of transaction tx-abort Count all aborts tx-conflict Count aborts due to conflict with another CPU. tx-capacity Count capacity aborts (transaction too large) Then matching el-* events for HLE cycles-t Transactional cycles (used by perf stat -T) * also exists on POWER8 cycles-ct Transactional cycles commited (used by perf stat -T) * according to Michael Ellerman POWER8 has a cycles-transactional-committed, * perf stat -T handles both cases Note for useful abort profiling often precise has to be set, as Haswell can only report the point inside the transaction with precise=2. For some classes of aborts, like conflicts, this is not needed, as it makes more sense to look at the complete critical section. This gives a clean set of generalized events to examine transaction success and aborts. Haswell has additional events for TSX, but those are more specialized for very specific situations. Signed-off-by: Andi Kleen Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1378438661-24765-4-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 57d64b7..dd1d4f3 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -2222,7 +2222,34 @@ static __init void intel_nehalem_quirk(void) EVENT_ATTR_STR(mem-loads, mem_ld_hsw, "event=0xcd,umask=0x1,ldlat=3"); EVENT_ATTR_STR(mem-stores, mem_st_hsw, "event=0xd0,umask=0x82") +/* Haswell special events */ +EVENT_ATTR_STR(tx-start, tx_start, "event=0xc9,umask=0x1"); +EVENT_ATTR_STR(tx-commit, tx_commit, "event=0xc9,umask=0x2"); +EVENT_ATTR_STR(tx-abort, tx_abort, "event=0xc9,umask=0x4"); +EVENT_ATTR_STR(tx-capacity, tx_capacity, "event=0x54,umask=0x2"); +EVENT_ATTR_STR(tx-conflict, tx_conflict, "event=0x54,umask=0x1"); +EVENT_ATTR_STR(el-start, el_start, "event=0xc8,umask=0x1"); +EVENT_ATTR_STR(el-commit, el_commit, "event=0xc8,umask=0x2"); +EVENT_ATTR_STR(el-abort, el_abort, "event=0xc8,umask=0x4"); +EVENT_ATTR_STR(el-capacity, el_capacity, "event=0x54,umask=0x2"); +EVENT_ATTR_STR(el-conflict, el_conflict, "event=0x54,umask=0x1"); +EVENT_ATTR_STR(cycles-t, cycles_t, "event=0x3c,in_tx=1"); +EVENT_ATTR_STR(cycles-ct, cycles_ct, + "event=0x3c,in_tx=1,in_tx_cp=1"); + static struct attribute *hsw_events_attrs[] = { + EVENT_PTR(tx_start), + EVENT_PTR(tx_commit), + EVENT_PTR(tx_abort), + EVENT_PTR(tx_capacity), + EVENT_PTR(tx_conflict), + EVENT_PTR(el_start), + EVENT_PTR(el_commit), + EVENT_PTR(el_abort), + EVENT_PTR(el_capacity), + EVENT_PTR(el_conflict), + EVENT_PTR(cycles_t), + EVENT_PTR(cycles_ct), EVENT_PTR(mem_ld_hsw), EVENT_PTR(mem_st_hsw), NULL -- cgit v0.10.2 From 2b9e344df384e595db24ac61ae5f780e9b024878 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 12 Sep 2013 12:53:44 +0200 Subject: perf/x86/intel: Clean up checkpoint-interrupt bits Clean up the weird CP interrupt exception code by keeping a CP mask. Andi suggested this implementation but weirdly didn't actually implement it himself, do so now because it removes the conditional in the interrupt handler and avoids the assumption its only on cnt2. Suggested-by: Andi Kleen Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-dvb4q0rydkfp00kqat4p5bah@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h index cc16faa..ce84ede 100644 --- a/arch/x86/kernel/cpu/perf_event.h +++ b/arch/x86/kernel/cpu/perf_event.h @@ -164,6 +164,11 @@ struct cpu_hw_events { struct perf_guest_switch_msr guest_switch_msrs[X86_PMC_IDX_MAX]; /* + * Intel checkpoint mask + */ + u64 intel_cp_status; + + /* * manage shared (per-core, per-cpu) registers * used on Intel NHM/WSM/SNB */ diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index dd1d4f3..ec70d0c 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -1184,6 +1184,11 @@ static void intel_pmu_disable_fixed(struct hw_perf_event *hwc) wrmsrl(hwc->config_base, ctrl_val); } +static inline bool event_is_checkpointed(struct perf_event *event) +{ + return (event->hw.config & HSW_IN_TX_CHECKPOINTED) != 0; +} + static void intel_pmu_disable_event(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; @@ -1197,6 +1202,7 @@ static void intel_pmu_disable_event(struct perf_event *event) cpuc->intel_ctrl_guest_mask &= ~(1ull << hwc->idx); cpuc->intel_ctrl_host_mask &= ~(1ull << hwc->idx); + cpuc->intel_cp_status &= ~(1ull << hwc->idx); /* * must disable before any actual event @@ -1271,6 +1277,9 @@ static void intel_pmu_enable_event(struct perf_event *event) if (event->attr.exclude_guest) cpuc->intel_ctrl_host_mask |= (1ull << hwc->idx); + if (unlikely(event_is_checkpointed(event))) + cpuc->intel_cp_status |= (1ull << hwc->idx); + if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) { intel_pmu_enable_fixed(hwc); return; @@ -1282,11 +1291,6 @@ static void intel_pmu_enable_event(struct perf_event *event) __x86_pmu_enable_event(hwc, ARCH_PERFMON_EVENTSEL_ENABLE); } -static inline bool event_is_checkpointed(struct perf_event *event) -{ - return (event->hw.config & HSW_IN_TX_CHECKPOINTED) != 0; -} - /* * Save and restart an expired event. Called by NMI contexts, * so it has to be careful about preempting normal event ops: @@ -1389,11 +1393,11 @@ again: } /* - * To avoid spurious interrupts with perf stat always reset checkpointed - * counters. + * Checkpointed counters can lead to 'spurious' PMIs because the + * rollback caused by the PMI will have cleared the overflow status + * bit. Therefore always force probe these counters. */ - if (cpuc->events[2] && event_is_checkpointed(cpuc->events[2])) - status |= (1ULL << 2); + status |= cpuc->intel_cp_status; for_each_set_bit(bit, (unsigned long *)&status, X86_PMC_IDX_MAX) { struct perf_event *event = cpuc->events[bit]; -- cgit v0.10.2 From d2beea4a3419e63804094e9ac4b6d1518bc17a9b Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 12 Sep 2013 13:00:47 +0200 Subject: perf/x86/intel: Clean-up/reduce PEBS code Get rid of some pointless duplication introduced by the Haswell code. Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-8q6y4davda9aawwv5yxe7klp@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index 104cbba..f364c13 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -188,8 +188,7 @@ struct pebs_record_hsw { u64 r8, r9, r10, r11; u64 r12, r13, r14, r15; u64 status, dla, dse, lat; - u64 real_ip; /* the actual eventing ip */ - u64 tsx_tuning; /* TSX abort cycles and flags */ + u64 real_ip, tsx_tuning; }; union hsw_tsx_tuning { @@ -811,10 +810,8 @@ static void __intel_pmu_pebs_event(struct perf_event *event, struct pt_regs *iregs, void *__pebs) { /* - * We cast to pebs_record_nhm to get the load latency data - * if extra_reg MSR_PEBS_LD_LAT_THRESHOLD used - * We cast to the biggest PEBS record are careful not - * to access out-of-bounds members. + * We cast to the biggest pebs_record but are careful not to + * unconditionally access the 'extra' entries. */ struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); struct pebs_record_hsw *pebs = __pebs; @@ -884,12 +881,11 @@ static void __intel_pmu_pebs_event(struct perf_event *event, regs.flags &= ~PERF_EFLAGS_EXACT; if ((event->attr.sample_type & PERF_SAMPLE_ADDR) && - x86_pmu.intel_cap.pebs_format >= 1) + x86_pmu.intel_cap.pebs_format >= 1) data.addr = pebs->dla; /* Only set the TSX weight when no memory weight was requested. */ - if ((event->attr.sample_type & PERF_SAMPLE_WEIGHT) && - !fll && + if ((event->attr.sample_type & PERF_SAMPLE_WEIGHT) && !fll && (x86_pmu.intel_cap.pebs_format >= 2)) data.weight = intel_hsw_weight(pebs); @@ -941,17 +937,34 @@ static void intel_pmu_drain_pebs_core(struct pt_regs *iregs) __intel_pmu_pebs_event(event, iregs, at); } -static void __intel_pmu_drain_pebs_nhm(struct pt_regs *iregs, void *at, - void *top) +static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs) { struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); struct debug_store *ds = cpuc->ds; struct perf_event *event = NULL; + void *at, *top; u64 status = 0; - int bit; + int bit, n; + + if (!x86_pmu.pebs_active) + return; + + at = (struct pebs_record_nhm *)(unsigned long)ds->pebs_buffer_base; + top = (struct pebs_record_nhm *)(unsigned long)ds->pebs_index; ds->pebs_index = ds->pebs_buffer_base; + n = (top - at) / x86_pmu.pebs_record_size; + if (n <= 0) + return; + + /* + * Should not happen, we program the threshold at 1 and do not + * set a reset value. + */ + WARN_ONCE(n > x86_pmu.max_pebs_events, + "Unexpected number of pebs records %d\n", n); + for (; at < top; at += x86_pmu.pebs_record_size) { struct pebs_record_nhm *p = at; @@ -979,61 +992,6 @@ static void __intel_pmu_drain_pebs_nhm(struct pt_regs *iregs, void *at, } } -static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs) -{ - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); - struct debug_store *ds = cpuc->ds; - struct pebs_record_nhm *at, *top; - int n; - - if (!x86_pmu.pebs_active) - return; - - at = (struct pebs_record_nhm *)(unsigned long)ds->pebs_buffer_base; - top = (struct pebs_record_nhm *)(unsigned long)ds->pebs_index; - - ds->pebs_index = ds->pebs_buffer_base; - - n = top - at; - if (n <= 0) - return; - - /* - * Should not happen, we program the threshold at 1 and do not - * set a reset value. - */ - WARN_ONCE(n > x86_pmu.max_pebs_events, - "Unexpected number of pebs records %d\n", n); - - return __intel_pmu_drain_pebs_nhm(iregs, at, top); -} - -static void intel_pmu_drain_pebs_hsw(struct pt_regs *iregs) -{ - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); - struct debug_store *ds = cpuc->ds; - struct pebs_record_hsw *at, *top; - int n; - - if (!x86_pmu.pebs_active) - return; - - at = (struct pebs_record_hsw *)(unsigned long)ds->pebs_buffer_base; - top = (struct pebs_record_hsw *)(unsigned long)ds->pebs_index; - - n = top - at; - if (n <= 0) - return; - /* - * Should not happen, we program the threshold at 1 and do not - * set a reset value. - */ - WARN_ONCE(n > x86_pmu.max_pebs_events, - "Unexpected number of pebs records %d\n", n); - - return __intel_pmu_drain_pebs_nhm(iregs, at, top); -} - /* * BTS, PEBS probe and setup */ @@ -1068,7 +1026,7 @@ void intel_ds_init(void) case 2: pr_cont("PEBS fmt2%c, ", pebs_type); x86_pmu.pebs_record_size = sizeof(struct pebs_record_hsw); - x86_pmu.drain_pebs = intel_pmu_drain_pebs_hsw; + x86_pmu.drain_pebs = intel_pmu_drain_pebs_nhm; break; default: -- cgit v0.10.2 From 6263322c5e8ffdaf5eaaa29e9d02d84a786aa970 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 19 Aug 2013 12:41:09 +0200 Subject: sched/fair: Rewrite group_imb trigger Change the group_imb detection from the old 'load-spike' detector to an actual imbalance detector. We set it from the lower domain balance pass when it fails to create a balance in the presence of task affinities. The advantage is that this should no longer generate the false positive group_imb conditions generated by transient load spikes from the normal balancing/bulk-wakeup etc. behaviour. While I haven't actually observed those they could happen. I'm not entirely happy with this patch; it somehow feels a little fragile. Nor does it solve the biggest issue I have with the group_imb code; it it still a fragile construct in that once we 'fixed' the imbalance we'll not detect the group_imb again and could end up re-creating it. That said, this patch does seem to preserve behaviour for the described degenerate case. In particular on my 2*6*2 wsm-ep: taskset -c 3-11 bash -c 'for ((i=0;i<9;i++)) do while :; do :; done & done' ends up with 9 spinners, each on their own CPU; whereas if you disable the group_imb code that typically doesn't happen (you'll get one pair sharing a CPU most of the time). Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-36fpbgl39dv4u51b6yz2ypz5@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 11cd136..7325ca7 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -3906,7 +3906,8 @@ static unsigned long __read_mostly max_load_balance_interval = HZ/10; #define LBF_ALL_PINNED 0x01 #define LBF_NEED_BREAK 0x02 -#define LBF_SOME_PINNED 0x04 +#define LBF_DST_PINNED 0x04 +#define LBF_SOME_PINNED 0x08 struct lb_env { struct sched_domain *sd; @@ -3997,6 +3998,8 @@ int can_migrate_task(struct task_struct *p, struct lb_env *env) schedstat_inc(p, se.statistics.nr_failed_migrations_affine); + env->flags |= LBF_SOME_PINNED; + /* * Remember if this task can be migrated to any other cpu in * our sched_group. We may want to revisit it if we couldn't @@ -4005,13 +4008,13 @@ int can_migrate_task(struct task_struct *p, struct lb_env *env) * Also avoid computing new_dst_cpu if we have already computed * one in current iteration. */ - if (!env->dst_grpmask || (env->flags & LBF_SOME_PINNED)) + if (!env->dst_grpmask || (env->flags & LBF_DST_PINNED)) return 0; /* Prevent to re-select dst_cpu via env's cpus */ for_each_cpu_and(cpu, env->dst_grpmask, env->cpus) { if (cpumask_test_cpu(cpu, tsk_cpus_allowed(p))) { - env->flags |= LBF_SOME_PINNED; + env->flags |= LBF_DST_PINNED; env->new_dst_cpu = cpu; break; } @@ -4526,13 +4529,12 @@ fix_small_capacity(struct sched_domain *sd, struct sched_group *group) * cpu 3 and leave one of the cpus in the second group unused. * * The current solution to this issue is detecting the skew in the first group - * by noticing it has a cpu that is overloaded while the remaining cpus are - * idle -- or rather, there's a distinct imbalance in the cpus; see - * sg_imbalanced(). + * by noticing the lower domain failed to reach balance and had difficulty + * moving tasks due to affinity constraints. * * When this is so detected; this group becomes a candidate for busiest; see * update_sd_pick_busiest(). And calculcate_imbalance() and - * find_busiest_group() avoid some of the usual balance conditional to allow it + * find_busiest_group() avoid some of the usual balance conditions to allow it * to create an effective group imbalance. * * This is a somewhat tricky proposition since the next run might not find the @@ -4540,49 +4542,9 @@ fix_small_capacity(struct sched_domain *sd, struct sched_group *group) * subtle and fragile situation. */ -struct sg_imb_stats { - unsigned long max_nr_running, min_nr_running; - unsigned long max_cpu_load, min_cpu_load; -}; - -static inline void init_sg_imb_stats(struct sg_imb_stats *sgi) -{ - sgi->max_cpu_load = sgi->max_nr_running = 0UL; - sgi->min_cpu_load = sgi->min_nr_running = ~0UL; -} - -static inline void -update_sg_imb_stats(struct sg_imb_stats *sgi, - unsigned long load, unsigned long nr_running) -{ - if (load > sgi->max_cpu_load) - sgi->max_cpu_load = load; - if (sgi->min_cpu_load > load) - sgi->min_cpu_load = load; - - if (nr_running > sgi->max_nr_running) - sgi->max_nr_running = nr_running; - if (sgi->min_nr_running > nr_running) - sgi->min_nr_running = nr_running; -} - -static inline int -sg_imbalanced(struct sg_lb_stats *sgs, struct sg_imb_stats *sgi) +static inline int sg_imbalanced(struct sched_group *group) { - /* - * Consider the group unbalanced when the imbalance is larger - * than the average weight of a task. - * - * APZ: with cgroup the avg task weight can vary wildly and - * might not be a suitable number - should we keep a - * normalized nr_running number somewhere that negates - * the hierarchy? - */ - if ((sgi->max_cpu_load - sgi->min_cpu_load) >= sgs->load_per_task && - (sgi->max_nr_running - sgi->min_nr_running) > 1) - return 1; - - return 0; + return group->sgp->imbalance; } /** @@ -4597,25 +4559,20 @@ static inline void update_sg_lb_stats(struct lb_env *env, struct sched_group *group, int load_idx, int local_group, struct sg_lb_stats *sgs) { - struct sg_imb_stats sgi; unsigned long nr_running; unsigned long load; int i; - init_sg_imb_stats(&sgi); - for_each_cpu_and(i, sched_group_cpus(group), env->cpus) { struct rq *rq = cpu_rq(i); nr_running = rq->nr_running; /* Bias balancing toward cpus of our domain */ - if (local_group) { + if (local_group) load = target_load(i, load_idx); - } else { + else load = source_load(i, load_idx); - update_sg_imb_stats(&sgi, load, nr_running); - } sgs->group_load += load; sgs->sum_nr_running += nr_running; @@ -4635,7 +4592,7 @@ static inline void update_sg_lb_stats(struct lb_env *env, if (sgs->sum_nr_running) sgs->load_per_task = sgs->sum_weighted_load / sgs->sum_nr_running; - sgs->group_imb = sg_imbalanced(sgs, &sgi); + sgs->group_imb = sg_imbalanced(group); sgs->group_capacity = DIV_ROUND_CLOSEST(sgs->group_power, SCHED_POWER_SCALE); @@ -5163,6 +5120,7 @@ static int load_balance(int this_cpu, struct rq *this_rq, int *continue_balancing) { int ld_moved, cur_ld_moved, active_balance = 0; + struct sched_domain *sd_parent = sd->parent; struct sched_group *group; struct rq *busiest; unsigned long flags; @@ -5267,11 +5225,11 @@ more_balance: * moreover subsequent load balance cycles should correct the * excess load moved. */ - if ((env.flags & LBF_SOME_PINNED) && env.imbalance > 0) { + if ((env.flags & LBF_DST_PINNED) && env.imbalance > 0) { env.dst_rq = cpu_rq(env.new_dst_cpu); env.dst_cpu = env.new_dst_cpu; - env.flags &= ~LBF_SOME_PINNED; + env.flags &= ~LBF_DST_PINNED; env.loop = 0; env.loop_break = sched_nr_migrate_break; @@ -5285,6 +5243,18 @@ more_balance: goto more_balance; } + /* + * We failed to reach balance because of affinity. + */ + if (sd_parent) { + int *group_imbalance = &sd_parent->groups->sgp->imbalance; + + if ((env.flags & LBF_SOME_PINNED) && env.imbalance > 0) { + *group_imbalance = 1; + } else if (*group_imbalance) + *group_imbalance = 0; + } + /* All tasks on this runqueue were pinned by CPU affinity */ if (unlikely(env.flags & LBF_ALL_PINNED)) { cpumask_clear_cpu(cpu_of(busiest), cpus); @@ -5688,7 +5658,7 @@ static void rebalance_domains(int cpu, enum cpu_idle_type idle) if (time_after_eq(jiffies, sd->last_balance + interval)) { if (load_balance(cpu, rq, sd, idle, &continue_balancing)) { /* - * The LBF_SOME_PINNED logic could have changed + * The LBF_DST_PINNED logic could have changed * env->dst_cpu, so we can't know our idle * state even if we migrated tasks. Update it. */ diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index b3c5653..0d7544c 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -605,6 +605,7 @@ struct sched_group_power { */ unsigned int power, power_orig; unsigned long next_update; + int imbalance; /* XXX unrelated to power but shared group state */ /* * Number of busy cpus in this group. */ -- cgit v0.10.2 From b72ff13ce6021b37459afacbccc0bc9b16989013 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 28 Aug 2013 10:32:32 +0200 Subject: sched/fair: Reduce local_group logic Try and reduce the local_group logic by pulling most of it into update_sd_lb_stats. Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-mgezl354xgyhiyrte78fdkpd@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 7325ca7..f9f4385 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -4563,6 +4563,8 @@ static inline void update_sg_lb_stats(struct lb_env *env, unsigned long load; int i; + memset(sgs, 0, sizeof(*sgs)); + for_each_cpu_and(i, sched_group_cpus(group), env->cpus) { struct rq *rq = cpu_rq(i); @@ -4581,10 +4583,6 @@ static inline void update_sg_lb_stats(struct lb_env *env, sgs->idle_cpus++; } - if (local_group && (env->idle != CPU_NEWLY_IDLE || - time_after_eq(jiffies, group->sgp->next_update))) - update_group_power(env->sd, env->dst_cpu); - /* Adjust by relative CPU power of the group */ sgs->group_power = group->sgp->power; sgs->avg_load = (sgs->group_load*SCHED_POWER_SCALE) / sgs->group_power; @@ -4677,11 +4675,17 @@ static inline void update_sd_lb_stats(struct lb_env *env, if (local_group) { sds->local = sg; sgs = &sds->local_stat; + + if (env->idle != CPU_NEWLY_IDLE || + time_after_eq(jiffies, sg->sgp->next_update)) + update_group_power(env->sd, env->dst_cpu); } - memset(sgs, 0, sizeof(*sgs)); update_sg_lb_stats(env, sg, load_idx, local_group, sgs); + if (local_group) + goto next_group; + /* * In case the child domain prefers tasks go to siblings * first, lower the sg capacity to one so that we'll try @@ -4692,19 +4696,20 @@ static inline void update_sd_lb_stats(struct lb_env *env, * heaviest group when it is already under-utilized (possible * with a large weight task outweighs the tasks on the system). */ - if (prefer_sibling && !local_group && - sds->local && sds->local_stat.group_has_capacity) + if (prefer_sibling && sds->local && + sds->local_stat.group_has_capacity) sgs->group_capacity = min(sgs->group_capacity, 1U); - /* Now, start updating sd_lb_stats */ - sds->total_load += sgs->group_load; - sds->total_pwr += sgs->group_power; - - if (!local_group && update_sd_pick_busiest(env, sds, sg, sgs)) { + if (update_sd_pick_busiest(env, sds, sg, sgs)) { sds->busiest = sg; sds->busiest_stat = *sgs; } +next_group: + /* Now, start updating sd_lb_stats */ + sds->total_load += sgs->group_load; + sds->total_pwr += sgs->group_power; + sg = sg->next; } while (sg != env->sd->groups); } -- cgit v0.10.2 From 863bffc80898b8df295ebac111af2335ec05f85d Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 28 Aug 2013 11:44:39 +0200 Subject: sched/fair: Fix group power_orig computation When looking at the code I noticed we don't actually compute sgp->power_orig correctly for groups, fix that. Currently the only consumer of that value is fix_small_capacity() which is only used on POWER7+ and that code excludes this case by being limited to SD_SHARE_CPUPOWER which is only ever set on the SMT domain which must be the lowest domain and this has singleton groups. So nothing should be affected by this change. Cc: Michael Neuling Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-db2pe0vxwunv37plc7onnugj@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index f9f4385..baba313 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -4450,7 +4450,7 @@ void update_group_power(struct sched_domain *sd, int cpu) { struct sched_domain *child = sd->child; struct sched_group *group, *sdg = sd->groups; - unsigned long power; + unsigned long power, power_orig; unsigned long interval; interval = msecs_to_jiffies(sd->balance_interval); @@ -4462,7 +4462,7 @@ void update_group_power(struct sched_domain *sd, int cpu) return; } - power = 0; + power_orig = power = 0; if (child->flags & SD_OVERLAP) { /* @@ -4470,8 +4470,12 @@ void update_group_power(struct sched_domain *sd, int cpu) * span the current group. */ - for_each_cpu(cpu, sched_group_cpus(sdg)) - power += power_of(cpu); + for_each_cpu(cpu, sched_group_cpus(sdg)) { + struct sched_group *sg = cpu_rq(cpu)->sd->groups; + + power_orig += sg->sgp->power_orig; + power += sg->sgp->power; + } } else { /* * !SD_OVERLAP domains can assume that child groups @@ -4480,12 +4484,14 @@ void update_group_power(struct sched_domain *sd, int cpu) group = child->groups; do { + power_orig += group->sgp->power_orig; power += group->sgp->power; group = group->next; } while (group != child->groups); } - sdg->sgp->power_orig = sdg->sgp->power = power; + sdg->sgp->power_orig = power_orig; + sdg->sgp->power = power; } /* -- cgit v0.10.2 From b37d931685b519cd61a67fbdfe5b04707eb76e32 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 28 Aug 2013 11:50:34 +0200 Subject: sched/fair: Rework and comment the group_capacity code Pull out the group_capacity computation so that we can more clearly comment its issues. Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-az1hl1ya55k361nkeh9bj0yw@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index baba313..218f9c5 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -4553,6 +4553,27 @@ static inline int sg_imbalanced(struct sched_group *group) return group->sgp->imbalance; } +/* + * Compute the group capacity. + * + * For now the capacity is simply the number of power units in the group_power. + * A power unit represents a full core. + * + * This has an issue where N*frac(smt_power) >= 1, in that case we'll see extra + * 'cores' that aren't actually there. + */ +static inline int sg_capacity(struct lb_env *env, struct sched_group *group) +{ + + unsigned int power = group->sgp->power; + unsigned int capacity = DIV_ROUND_CLOSEST(power, SCHED_POWER_SCALE); + + if (!capacity) + capacity = fix_small_capacity(env->sd, group); + + return capacity; +} + /** * update_sg_lb_stats - Update sched_group's statistics for load balancing. * @env: The load balancing environment. @@ -4596,16 +4617,11 @@ static inline void update_sg_lb_stats(struct lb_env *env, if (sgs->sum_nr_running) sgs->load_per_task = sgs->sum_weighted_load / sgs->sum_nr_running; - sgs->group_imb = sg_imbalanced(group); - - sgs->group_capacity = - DIV_ROUND_CLOSEST(sgs->group_power, SCHED_POWER_SCALE); - - if (!sgs->group_capacity) - sgs->group_capacity = fix_small_capacity(env->sd, group); - sgs->group_weight = group->group_weight; + sgs->group_imb = sg_imbalanced(group); + sgs->group_capacity = sg_capacity(env, group); + if (sgs->group_capacity > sgs->sum_nr_running) sgs->group_has_capacity = 1; } -- cgit v0.10.2 From c61037e905a5cb74c7d786c35ee2cdbab9ed63af Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 28 Aug 2013 12:40:38 +0200 Subject: sched/fair: Fix the group_capacity computation Do away with 'phantom' cores due to N*frac(smt_power) >= 1 by limiting the capacity to the actual number of cores. The assumption of 1 < smt_power < 2 is an actual requirement because of what SMT is so this should work regardless of the SMT implementation. It can still be defeated by creative use of cpu hotplug, but if you're one of those freaks, you get to live with it. Signed-off-by: Peter Zijlstra Acked-by: Vincent Guittot Link: http://lkml.kernel.org/n/tip-dczmbi8tfgixacg1ji2av1un@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 218f9c5..51c5c3e 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -4556,18 +4556,24 @@ static inline int sg_imbalanced(struct sched_group *group) /* * Compute the group capacity. * - * For now the capacity is simply the number of power units in the group_power. - * A power unit represents a full core. - * - * This has an issue where N*frac(smt_power) >= 1, in that case we'll see extra - * 'cores' that aren't actually there. + * Avoid the issue where N*frac(smt_power) >= 1 creates 'phantom' cores by + * first dividing out the smt factor and computing the actual number of cores + * and limit power unit capacity with that. */ static inline int sg_capacity(struct lb_env *env, struct sched_group *group) { + unsigned int capacity, smt, cpus; + unsigned int power, power_orig; + + power = group->sgp->power; + power_orig = group->sgp->power_orig; + cpus = group->group_weight; - unsigned int power = group->sgp->power; - unsigned int capacity = DIV_ROUND_CLOSEST(power, SCHED_POWER_SCALE); + /* smt := ceil(cpus / power), assumes: 1 < smt_power < 2 */ + smt = DIV_ROUND_UP(SCHED_POWER_SCALE * cpus, power_orig); + capacity = cpus / smt; /* cores */ + capacity = min_t(unsigned, capacity, DIV_ROUND_CLOSEST(power, SCHED_POWER_SCALE)); if (!capacity) capacity = fix_small_capacity(env->sd, group); -- cgit v0.10.2 From 7f2ee91f54fb56071f97bde1ef7ba7ba0d58dfe5 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Thu, 12 Sep 2013 19:17:00 +0200 Subject: perf/x86/intel: Clean up EVENT_ATTR_STR() muck Make the code a bit more readable by removing stray whitespaces et al. Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Link: http://lkml.kernel.org/n/tip-lzEnychz1ylqy8zjenxOmeht@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index ec70d0c..353b7a3 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -190,9 +190,9 @@ static struct extra_reg intel_snbep_extra_regs[] __read_mostly = { EVENT_EXTRA_END }; -EVENT_ATTR_STR(mem-loads, mem_ld_nhm, "event=0x0b,umask=0x10,ldlat=3"); -EVENT_ATTR_STR(mem-loads, mem_ld_snb, "event=0xcd,umask=0x1,ldlat=3"); -EVENT_ATTR_STR(mem-stores, mem_st_snb, "event=0xcd,umask=0x2"); +EVENT_ATTR_STR(mem-loads, mem_ld_nhm, "event=0x0b,umask=0x10,ldlat=3"); +EVENT_ATTR_STR(mem-loads, mem_ld_snb, "event=0xcd,umask=0x1,ldlat=3"); +EVENT_ATTR_STR(mem-stores, mem_st_snb, "event=0xcd,umask=0x2"); struct attribute *nhm_events_attrs[] = { EVENT_PTR(mem_ld_nhm), @@ -2223,23 +2223,22 @@ static __init void intel_nehalem_quirk(void) } } -EVENT_ATTR_STR(mem-loads, mem_ld_hsw, "event=0xcd,umask=0x1,ldlat=3"); -EVENT_ATTR_STR(mem-stores, mem_st_hsw, "event=0xd0,umask=0x82") +EVENT_ATTR_STR(mem-loads, mem_ld_hsw, "event=0xcd,umask=0x1,ldlat=3"); +EVENT_ATTR_STR(mem-stores, mem_st_hsw, "event=0xd0,umask=0x82") /* Haswell special events */ -EVENT_ATTR_STR(tx-start, tx_start, "event=0xc9,umask=0x1"); -EVENT_ATTR_STR(tx-commit, tx_commit, "event=0xc9,umask=0x2"); -EVENT_ATTR_STR(tx-abort, tx_abort, "event=0xc9,umask=0x4"); -EVENT_ATTR_STR(tx-capacity, tx_capacity, "event=0x54,umask=0x2"); -EVENT_ATTR_STR(tx-conflict, tx_conflict, "event=0x54,umask=0x1"); -EVENT_ATTR_STR(el-start, el_start, "event=0xc8,umask=0x1"); -EVENT_ATTR_STR(el-commit, el_commit, "event=0xc8,umask=0x2"); -EVENT_ATTR_STR(el-abort, el_abort, "event=0xc8,umask=0x4"); -EVENT_ATTR_STR(el-capacity, el_capacity, "event=0x54,umask=0x2"); -EVENT_ATTR_STR(el-conflict, el_conflict, "event=0x54,umask=0x1"); -EVENT_ATTR_STR(cycles-t, cycles_t, "event=0x3c,in_tx=1"); -EVENT_ATTR_STR(cycles-ct, cycles_ct, - "event=0x3c,in_tx=1,in_tx_cp=1"); +EVENT_ATTR_STR(tx-start, tx_start, "event=0xc9,umask=0x1"); +EVENT_ATTR_STR(tx-commit, tx_commit, "event=0xc9,umask=0x2"); +EVENT_ATTR_STR(tx-abort, tx_abort, "event=0xc9,umask=0x4"); +EVENT_ATTR_STR(tx-capacity, tx_capacity, "event=0x54,umask=0x2"); +EVENT_ATTR_STR(tx-conflict, tx_conflict, "event=0x54,umask=0x1"); +EVENT_ATTR_STR(el-start, el_start, "event=0xc8,umask=0x1"); +EVENT_ATTR_STR(el-commit, el_commit, "event=0xc8,umask=0x2"); +EVENT_ATTR_STR(el-abort, el_abort, "event=0xc8,umask=0x4"); +EVENT_ATTR_STR(el-capacity, el_capacity, "event=0x54,umask=0x2"); +EVENT_ATTR_STR(el-conflict, el_conflict, "event=0x54,umask=0x1"); +EVENT_ATTR_STR(cycles-t, cycles_t, "event=0x3c,in_tx=1"); +EVENT_ATTR_STR(cycles-ct, cycles_ct, "event=0x3c,in_tx=1,in_tx_cp=1"); static struct attribute *hsw_events_attrs[] = { EVENT_PTR(tx_start), -- cgit v0.10.2 From 0c21fccd97f0ff58e6e9699370a09f6ec8946061 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 13 Sep 2013 10:44:44 +0300 Subject: ALSA: asihpi: a couple array out of bounds issues These ->put() functions are called from snd_ctl_elem_write() with user supplied data. snd_asihpi_tuner_band_put() is missing a limit check and the check in snd_asihpi_clksrc_put() can underflow. Signed-off-by: Dan Carpenter Signed-off-by: Takashi Iwai diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index dc632cd..5f2acd3 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -1913,6 +1913,7 @@ static int snd_asihpi_tuner_band_put(struct snd_kcontrol *kcontrol, struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol); */ u32 h_control = kcontrol->private_value; + unsigned int idx; u16 band; u16 tuner_bands[HPI_TUNER_BAND_LAST]; u32 num_bands = 0; @@ -1920,7 +1921,10 @@ static int snd_asihpi_tuner_band_put(struct snd_kcontrol *kcontrol, num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands, HPI_TUNER_BAND_LAST); - band = tuner_bands[ucontrol->value.enumerated.item[0]]; + idx = ucontrol->value.enumerated.item[0]; + if (idx >= ARRAY_SIZE(tuner_bands)) + idx = ARRAY_SIZE(tuner_bands) - 1; + band = tuner_bands[idx]; hpi_handle_error(hpi_tuner_set_band(h_control, band)); return 1; @@ -2383,7 +2387,8 @@ static int snd_asihpi_clksrc_put(struct snd_kcontrol *kcontrol, struct snd_card_asihpi *asihpi = (struct snd_card_asihpi *)(kcontrol->private_data); struct clk_cache *clkcache = &asihpi->cc; - int change, item; + unsigned int item; + int change; u32 h_control = kcontrol->private_value; change = 1; -- cgit v0.10.2 From b71495ee425943afd80b8188e5c273254b3557a2 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 13 Sep 2013 15:14:21 +0530 Subject: ALSA: ctxfi: Staticize local symbols Local symbols used only in this file are made static. Signed-off-by: Sachin Kamat Signed-off-by: Takashi Iwai diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c index 0c00eb4..84f86bf 100644 --- a/sound/pci/ctxfi/ctdaio.c +++ b/sound/pci/ctxfi/ctdaio.c @@ -33,7 +33,7 @@ struct daio_rsc_idx { unsigned short right; }; -struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = { +static struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = { [LINEO1] = {.left = 0x00, .right = 0x01}, [LINEO2] = {.left = 0x18, .right = 0x19}, [LINEO3] = {.left = 0x08, .right = 0x09}, @@ -44,7 +44,7 @@ struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = { [SPDIFI1] = {.left = 0x95, .right = 0x9d}, }; -struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = { +static struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = { [LINEO1] = {.left = 0x40, .right = 0x41}, [LINEO2] = {.left = 0x60, .right = 0x61}, [LINEO3] = {.left = 0x50, .right = 0x51}, -- cgit v0.10.2 From 399ae7254e13de9239d74574f9fc39fb91a536cf Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 13 Sep 2013 15:14:22 +0530 Subject: ALSA: hda/ca0132: Staticize codec_send_command 'codec_send_command' is used only in this file. Make it static. Signed-off-by: Sachin Kamat Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 6e9876f..54d1479 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -759,7 +759,7 @@ struct ca0132_spec { /* * CA0132 codec access */ -unsigned int codec_send_command(struct hda_codec *codec, hda_nid_t nid, +static unsigned int codec_send_command(struct hda_codec *codec, hda_nid_t nid, unsigned int verb, unsigned int parm, unsigned int *res) { unsigned int response; -- cgit v0.10.2 From 79ae92dd24bb673dd5ddafdbc27cbf4a1e419525 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 13 Sep 2013 16:03:15 +0530 Subject: ALSA: au88x0: Remove redundant break 'break' after a return statement is redundant. Remove it. Signed-off-by: Sachin Kamat Signed-off-by: Takashi Iwai diff --git a/sound/pci/au88x0/au88x0_synth.c b/sound/pci/au88x0/au88x0_synth.c index 8bef473..922a84b 100644 --- a/sound/pci/au88x0/au88x0_synth.c +++ b/sound/pci/au88x0/au88x0_synth.c @@ -219,7 +219,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt, */ hwwrite(vortex->mmio, WT_RUN(wt), val); return 0xc; - break; case 1: /* param 0 */ /* printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", @@ -227,7 +226,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt, */ hwwrite(vortex->mmio, WT_PARM(wt, 0), val); return 0xc; - break; case 2: /* param 1 */ /* printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", @@ -235,7 +233,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt, */ hwwrite(vortex->mmio, WT_PARM(wt, 1), val); return 0xc; - break; case 3: /* param 2 */ /* printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", @@ -243,7 +240,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt, */ hwwrite(vortex->mmio, WT_PARM(wt, 2), val); return 0xc; - break; case 4: /* param 3 */ /* printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", @@ -251,7 +247,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt, */ hwwrite(vortex->mmio, WT_PARM(wt, 3), val); return 0xc; - break; case 6: /* mute */ /* printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", @@ -259,20 +254,17 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt, */ hwwrite(vortex->mmio, WT_MUTE(wt), val); return 0xc; - break; case 0xb: - { /* delay */ - /* - printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", - WT_DELAY(wt,0), (int)val); - */ - hwwrite(vortex->mmio, WT_DELAY(wt, 3), val); - hwwrite(vortex->mmio, WT_DELAY(wt, 2), val); - hwwrite(vortex->mmio, WT_DELAY(wt, 1), val); - hwwrite(vortex->mmio, WT_DELAY(wt, 0), val); - return 0xc; - } - break; + /* delay */ + /* + printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", + WT_DELAY(wt,0), (int)val); + */ + hwwrite(vortex->mmio, WT_DELAY(wt, 3), val); + hwwrite(vortex->mmio, WT_DELAY(wt, 2), val); + hwwrite(vortex->mmio, WT_DELAY(wt, 1), val); + hwwrite(vortex->mmio, WT_DELAY(wt, 0), val); + return 0xc; /* Global WT block parameters */ case 5: /* sramp */ ecx = WT_SRAMP(wt); @@ -291,7 +283,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt, break; default: return 0; - break; } /* printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", ecx, (int)val); -- cgit v0.10.2 From bf69aa906ccddacc9e966794869431374953738a Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 13 Sep 2013 16:18:59 +0530 Subject: ALSA: rme9652: Remove redundant break 'break' after return statement is not necessary. Signed-off-by: Sachin Kamat Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 3cde55b..2907e68 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -3996,7 +3996,6 @@ static int hdspm_tco_sync_check(struct hdspm *hdspm) return 1; } return 0; - break; case AES32: status = hdspm_read(hdspm, HDSPM_statusRegister); if (status & HDSPM_tcoLockAes) { @@ -4006,9 +4005,6 @@ static int hdspm_tco_sync_check(struct hdspm *hdspm) return 1; } return 0; - - break; - case RayDAT: case AIO: status = hdspm_read(hdspm, HDSPM_RD_STATUS_1); @@ -4018,7 +4014,6 @@ static int hdspm_tco_sync_check(struct hdspm *hdspm) if (status & 0x4000000) return 1; /* Lock */ return 0; /* No signal */ - break; default: break; -- cgit v0.10.2 From fdf200290581150f7b69148abf6ca860684cbfbb Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 1 Sep 2013 20:24:50 -0700 Subject: regmap: add regmap_field_update_bits() Current regmap_field is supporting read/write functions. This patch adds new update_bits function for it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7d689a1..285afa7 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1369,6 +1369,26 @@ int regmap_field_write(struct regmap_field *field, unsigned int val) } EXPORT_SYMBOL_GPL(regmap_field_write); +/** + * regmap_field_update_bits(): Perform a read/modify/write cycle + * on the register field + * + * @field: Register field to write to + * @mask: Bitmask to change + * @val: Value to be written + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_field_update_bits(struct regmap_field *field, unsigned int mask, unsigned int val) +{ + mask = (mask << field->shift) & field->mask; + + return regmap_update_bits(field->regmap, field->reg, + mask, val << field->shift); +} +EXPORT_SYMBOL_GPL(regmap_field_update_bits); + /* * regmap_bulk_write(): Write multiple registers to the device * diff --git a/include/linux/regmap.h b/include/linux/regmap.h index a10380b..4c8c20a 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -448,6 +448,8 @@ void devm_regmap_field_free(struct device *dev, struct regmap_field *field); int regmap_field_read(struct regmap_field *field, unsigned int *val); int regmap_field_write(struct regmap_field *field, unsigned int val); +int regmap_field_update_bits(struct regmap_field *field, + unsigned int mask, unsigned int val); /** * Description of an IRQ for the generic regmap irq_chip. -- cgit v0.10.2 From ec60dd37e1d907d0524fa4c5806ecf24b16ea712 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 9 Sep 2013 17:54:12 +0900 Subject: spi: atmel: convert from legacy pm ops to dev_pm_ops Instead of using legacy suspend/resume methods, using newer dev_pm_ops structure allows better control over power management. Also, duplicated 'return' is removed from atmel_spi_resume(). Signed-off-by: Jingoo Han Acked-by: Nicolas Ferre Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index fd7cc56..3daf754 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -1675,29 +1675,30 @@ static int atmel_spi_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM - -static int atmel_spi_suspend(struct platform_device *pdev, pm_message_t mesg) +#ifdef CONFIG_PM_SLEEP +static int atmel_spi_suspend(struct device *dev) { - struct spi_master *master = platform_get_drvdata(pdev); + struct spi_master *master = dev_get_drvdata(dev); struct atmel_spi *as = spi_master_get_devdata(master); clk_disable_unprepare(as->clk); return 0; } -static int atmel_spi_resume(struct platform_device *pdev) +static int atmel_spi_resume(struct device *dev) { - struct spi_master *master = platform_get_drvdata(pdev); + struct spi_master *master = dev_get_drvdata(dev); struct atmel_spi *as = spi_master_get_devdata(master); - return clk_prepare_enable(as->clk); + clk_prepare_enable(as->clk); return 0; } +static SIMPLE_DEV_PM_OPS(atmel_spi_pm_ops, atmel_spi_suspend, atmel_spi_resume); + +#define ATMEL_SPI_PM_OPS (&atmel_spi_pm_ops) #else -#define atmel_spi_suspend NULL -#define atmel_spi_resume NULL +#define ATMEL_SPI_PM_OPS NULL #endif #if defined(CONFIG_OF) @@ -1713,10 +1714,9 @@ static struct platform_driver atmel_spi_driver = { .driver = { .name = "atmel_spi", .owner = THIS_MODULE, + .pm = ATMEL_SPI_PM_OPS, .of_match_table = of_match_ptr(atmel_spi_dt_ids), }, - .suspend = atmel_spi_suspend, - .resume = atmel_spi_resume, .probe = atmel_spi_probe, .remove = atmel_spi_remove, }; -- cgit v0.10.2 From a536d7654338a06356afd0363e6adf51a02cb08b Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 10 Sep 2013 17:06:27 +0530 Subject: spi: atmel: Silence checkpatch errors Fixes the following types of checkpatch errors and warning: ERROR: space required after that ',' (ctx:VxV) WARNING: sizeof *as should be sizeof(*as) Signed-off-by: Sachin Kamat Acked-by: Nicolas Ferre Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 3daf754..6478a97 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -170,18 +170,18 @@ /* Bit manipulation macros */ #define SPI_BIT(name) \ (1 << SPI_##name##_OFFSET) -#define SPI_BF(name,value) \ +#define SPI_BF(name, value) \ (((value) & ((1 << SPI_##name##_SIZE) - 1)) << SPI_##name##_OFFSET) -#define SPI_BFEXT(name,value) \ +#define SPI_BFEXT(name, value) \ (((value) >> SPI_##name##_OFFSET) & ((1 << SPI_##name##_SIZE) - 1)) -#define SPI_BFINS(name,value,old) \ - ( ((old) & ~(((1 << SPI_##name##_SIZE) - 1) << SPI_##name##_OFFSET)) \ - | SPI_BF(name,value)) +#define SPI_BFINS(name, value, old) \ + (((old) & ~(((1 << SPI_##name##_SIZE) - 1) << SPI_##name##_OFFSET)) \ + | SPI_BF(name, value)) /* Register access macros */ -#define spi_readl(port,reg) \ +#define spi_readl(port, reg) \ __raw_readl((port)->regs + SPI_##reg) -#define spi_writel(port,reg,value) \ +#define spi_writel(port, reg, value) \ __raw_writel((value), (port)->regs + SPI_##reg) /* use PIO for small transfers, avoiding DMA setup/teardown overhead and @@ -1516,7 +1516,7 @@ static int atmel_spi_probe(struct platform_device *pdev) /* setup spi core then atmel-specific driver state */ ret = -ENOMEM; - master = spi_alloc_master(&pdev->dev, sizeof *as); + master = spi_alloc_master(&pdev->dev, sizeof(*as)); if (!master) goto out_free; -- cgit v0.10.2 From fbbfd68bc00d7bf431042b2e92cc4e540b827f67 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 9 Sep 2013 17:55:32 +0900 Subject: spi: bfin5xx: convert from legacy pm ops to dev_pm_ops Instead of using legacy suspend/resume methods, using newer dev_pm_ops structure allows better control over power management. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-bfin5xx.c b/drivers/spi/spi-bfin5xx.c index 45bdf73..1e28943 100644 --- a/drivers/spi/spi-bfin5xx.c +++ b/drivers/spi/spi-bfin5xx.c @@ -1410,10 +1410,10 @@ static int bfin_spi_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int bfin_spi_suspend(struct platform_device *pdev, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int bfin_spi_suspend(struct device *dev) { - struct bfin_spi_master_data *drv_data = platform_get_drvdata(pdev); + struct bfin_spi_master_data *drv_data = dev_get_drvdata(dev); int status = 0; status = bfin_spi_stop_queue(drv_data); @@ -1432,9 +1432,9 @@ static int bfin_spi_suspend(struct platform_device *pdev, pm_message_t state) return 0; } -static int bfin_spi_resume(struct platform_device *pdev) +static int bfin_spi_resume(struct device *dev) { - struct bfin_spi_master_data *drv_data = platform_get_drvdata(pdev); + struct bfin_spi_master_data *drv_data = dev_get_drvdata(dev); int status = 0; bfin_write(&drv_data->regs->ctl, drv_data->ctrl_reg); @@ -1443,25 +1443,27 @@ static int bfin_spi_resume(struct platform_device *pdev) /* Start the queue running */ status = bfin_spi_start_queue(drv_data); if (status != 0) { - dev_err(&pdev->dev, "problem starting queue (%d)\n", status); + dev_err(dev, "problem starting queue (%d)\n", status); return status; } return 0; } + +static SIMPLE_DEV_PM_OPS(bfin_spi_pm_ops, bfin_spi_suspend, bfin_spi_resume); + +#define BFIN_SPI_PM_OPS (&bfin_spi_pm_ops) #else -#define bfin_spi_suspend NULL -#define bfin_spi_resume NULL -#endif /* CONFIG_PM */ +#define BFIN_SPI_PM_OPS NULL +#endif MODULE_ALIAS("platform:bfin-spi"); static struct platform_driver bfin_spi_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, + .pm = BFIN_SPI_PM_OPS, }, - .suspend = bfin_spi_suspend, - .resume = bfin_spi_resume, .remove = bfin_spi_remove, }; -- cgit v0.10.2 From 673d6e1767ee06496f0f94e5087c817003983ffd Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 9 Sep 2013 17:56:17 +0900 Subject: spi: bfin-sport: convert from legacy pm ops to dev_pm_ops Instead of using legacy suspend/resume methods, using newer dev_pm_ops structure allows better control over power management. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-bfin-sport.c b/drivers/spi/spi-bfin-sport.c index 91921b5..6c81a59 100644 --- a/drivers/spi/spi-bfin-sport.c +++ b/drivers/spi/spi-bfin-sport.c @@ -879,11 +879,10 @@ static int bfin_sport_spi_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int -bfin_sport_spi_suspend(struct platform_device *pdev, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int bfin_sport_spi_suspend(struct device *dev) { - struct bfin_sport_spi_master_data *drv_data = platform_get_drvdata(pdev); + struct bfin_sport_spi_master_data *drv_data = dev_get_drvdata(dev); int status; status = bfin_sport_spi_stop_queue(drv_data); @@ -896,10 +895,9 @@ bfin_sport_spi_suspend(struct platform_device *pdev, pm_message_t state) return status; } -static int -bfin_sport_spi_resume(struct platform_device *pdev) +static int bfin_sport_spi_resume(struct device *dev) { - struct bfin_sport_spi_master_data *drv_data = platform_get_drvdata(pdev); + struct bfin_sport_spi_master_data *drv_data = dev_get_drvdata(dev); int status; /* Enable the SPI interface */ @@ -912,19 +910,22 @@ bfin_sport_spi_resume(struct platform_device *pdev) return status; } + +static SIMPLE_DEV_PM_OPS(bfin_sport_spi_pm_ops, bfin_sport_spi_suspend, + bfin_sport_spi_resume); + +#define BFIN_SPORT_SPI_PM_OPS (&bfin_sport_spi_pm_ops) #else -# define bfin_sport_spi_suspend NULL -# define bfin_sport_spi_resume NULL +#define BFIN_SPORT_SPI_PM_OPS NULL #endif static struct platform_driver bfin_sport_spi_driver = { .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = BFIN_SPORT_SPI_PM_OPS, }, .probe = bfin_sport_spi_probe, .remove = bfin_sport_spi_remove, - .suspend = bfin_sport_spi_suspend, - .resume = bfin_sport_spi_resume, }; module_platform_driver(bfin_sport_spi_driver); -- cgit v0.10.2 From 32ea3944436ca9e73677f9d844289780f255d45a Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 11 Sep 2013 16:05:04 +0530 Subject: spi: spi-davinci: Fix variable type 'prescale' contains the return value of davinci_spi_get_prescale() which is a signed integer. Convert 'prescale' to integer to silence the following warning: drivers/spi/spi-davinci.c:318 davinci_spi_setup_transfer() warn: unsigned 'prescale' is never less than zero. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index 8fbfe24..8a04f1e 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -279,7 +279,8 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, struct davinci_spi *dspi; struct davinci_spi_config *spicfg; u8 bits_per_word = 0; - u32 hz = 0, spifmt = 0, prescale = 0; + u32 hz = 0, spifmt = 0; + int prescale; dspi = spi_master_get_devdata(spi->master); spicfg = (struct davinci_spi_config *)spi->controller_data; -- cgit v0.10.2 From 1e0d191f62c779cdd6953db2541b6b92969ce1bb Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 2 Sep 2013 11:54:20 +0200 Subject: spi: designware: delete premature free_irq Free_irq is not needed if there has been no request_irq. Free_irq is removed from both the probe and remove functions. The correct request_irq and free_irq appear to be in the add_host and remove_host functions in spi-dw.c. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression e; @@ *e = platform_get_irq(...); ... when != request_irq(e,...) *free_irq(e,...) // Signed-off-by: Julia Lawall Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index 4aa8be8..168c620 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -74,7 +74,7 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) dwsmmio->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(dwsmmio->clk)) { ret = PTR_ERR(dwsmmio->clk); - goto err_irq; + goto err_unmap; } clk_enable(dwsmmio->clk); @@ -94,8 +94,6 @@ err_clk: clk_disable(dwsmmio->clk); clk_put(dwsmmio->clk); dwsmmio->clk = NULL; -err_irq: - free_irq(dws->irq, dws); err_unmap: iounmap(dws->regs); err_release_reg: @@ -115,7 +113,6 @@ static int dw_spi_mmio_remove(struct platform_device *pdev) clk_put(dwsmmio->clk); dwsmmio->clk = NULL; - free_irq(dwsmmio->dws.irq, &dwsmmio->dws); dw_spi_remove_host(&dwsmmio->dws); iounmap(dwsmmio->dws.regs); kfree(dwsmmio); -- cgit v0.10.2 From 7cce62f3aa956a4ce170e53c98cc195b74aba4b4 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 10 Sep 2013 18:54:31 +0900 Subject: spi: dw-pci: remove unnecessary pci_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure. Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-dw-pci.c b/drivers/spi/spi-dw-pci.c index 6055c8d..6a70862 100644 --- a/drivers/spi/spi-dw-pci.c +++ b/drivers/spi/spi-dw-pci.c @@ -109,7 +109,6 @@ static void spi_pci_remove(struct pci_dev *pdev) { struct dw_spi_pci *dwpci = pci_get_drvdata(pdev); - pci_set_drvdata(pdev, NULL); dw_spi_remove_host(&dwpci->dws); iounmap(dwpci->dws.regs); pci_release_region(pdev, 0); -- cgit v0.10.2 From 91a92e12baf3c8cc2e353b3a70ddca9f9a59cf02 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 10 Sep 2013 18:55:20 +0900 Subject: spi: spidev: remove unnecessary spi_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure. Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index ca5bcfe..c53556a 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -629,7 +629,6 @@ static int spidev_remove(struct spi_device *spi) /* make sure ops on existing fds can abort cleanly */ spin_lock_irq(&spidev->spi_lock); spidev->spi = NULL; - spi_set_drvdata(spi, NULL); spin_unlock_irq(&spidev->spi_lock); /* prevent new opens */ -- cgit v0.10.2 From 2ef3599d8181afcbd347226faba75991f9e008fc Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 5 Sep 2013 15:51:21 +0900 Subject: spi: fsl-dspi: add missing __iomem annotation Added missing __iomem annotation in order to fix the following sparse warnings: drivers/spi/spi-fsl-dspi.c:140:16: warning: incorrect type in argument 1 (different address spaces) drivers/spi/spi-fsl-dspi.c:140:16: expected void const volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:140:16: got void * drivers/spi/spi-fsl-dspi.c:143:9: warning: incorrect type in argument 2 (different address spaces) drivers/spi/spi-fsl-dspi.c:143:9: expected void volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:143:9: got void * drivers/spi/spi-fsl-dspi.c:132:18: warning: incorrect type in argument 1 (different address spaces) drivers/spi/spi-fsl-dspi.c:132:18: expected void const volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:132:18: got void * drivers/spi/spi-fsl-dspi.c:241:17: warning: incorrect type in argument 2 (different address spaces) drivers/spi/spi-fsl-dspi.c:241:17: expected void volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:241:17: got void * drivers/spi/spi-fsl-dspi.c:132:18: warning: incorrect type in argument 1 (different address spaces) drivers/spi/spi-fsl-dspi.c:132:18: expected void const volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:132:18: got void * drivers/spi/spi-fsl-dspi.c:259:29: warning: incorrect type in argument 1 (different address spaces) drivers/spi/spi-fsl-dspi.c:259:29: expected void const volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:259:29: got void * drivers/spi/spi-fsl-dspi.c:266:29: warning: incorrect type in argument 1 (different address spaces) drivers/spi/spi-fsl-dspi.c:266:29: expected void const volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:266:29: got void * drivers/spi/spi-fsl-dspi.c:298:9: warning: incorrect type in argument 2 (different address spaces) drivers/spi/spi-fsl-dspi.c:298:9: expected void volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:298:9: got void * drivers/spi/spi-fsl-dspi.c:299:9: warning: incorrect type in argument 2 (different address spaces) drivers/spi/spi-fsl-dspi.c:299:9: expected void volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:299:9: got void * drivers/spi/spi-fsl-dspi.c:300:9: warning: incorrect type in argument 2 (different address spaces) drivers/spi/spi-fsl-dspi.c:300:9: expected void volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:300:9: got void * drivers/spi/spi-fsl-dspi.c:303:17: warning: incorrect type in argument 2 (different address spaces) drivers/spi/spi-fsl-dspi.c:303:17: expected void volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:303:17: got void * drivers/spi/spi-fsl-dspi.c:318:21: warning: incorrect type in argument 1 (different address spaces) drivers/spi/spi-fsl-dspi.c:318:21: expected void const volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:318:21: got void * drivers/spi/spi-fsl-dspi.c:327:9: warning: incorrect type in argument 2 (different address spaces) drivers/spi/spi-fsl-dspi.c:327:9: expected void volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:327:9: got void * drivers/spi/spi-fsl-dspi.c:386:9: warning: incorrect type in argument 2 (different address spaces) drivers/spi/spi-fsl-dspi.c:386:9: expected void volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:386:9: got void * drivers/spi/spi-fsl-dspi.c:485:20: warning: incorrect type in assignment (different address spaces) drivers/spi/spi-fsl-dspi.c:485:20: expected void *base drivers/spi/spi-fsl-dspi.c:485:20: got void [noderef] * Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 6cd07d1..735d4f5 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -108,7 +108,7 @@ struct fsl_dspi { struct spi_bitbang bitbang; struct platform_device *pdev; - void *base; + void __iomem *base; int irq; struct clk *clk; -- cgit v0.10.2 From b444d1dfe296433a93d6b814d924e0ab99ad7e7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 10 Sep 2013 10:46:33 +0200 Subject: spi: fsl-dspi: several minor improvements and fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - improve dependencies using COMPILE_TEST - fix a typo - drop platform_set_drvdata(pdev, NULL) in error path of probe - make MODULE_LICENSE match the header Signed-off-by: Uwe Kleine-König Signed-off-by: Mark Brown diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index b9c53cc..c463d36 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -264,6 +264,7 @@ config SPI_FSL_SPI config SPI_FSL_DSPI tristate "Freescale DSPI controller" select SPI_BITBANG + depends on SOC_VF610 || COMPILE_TEST help This enables support for the Freescale DSPI controller in master mode. VF610 platform uses the controller. diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 735d4f5..dc3d4eb 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -165,7 +165,7 @@ static void hz_to_spi_baud(char *pbr, char *br, int speed_hz, } } - pr_warn("Can not find valid buad rate,speed_hz is %d,clkrate is %ld\ + pr_warn("Can not find valid baud rate,speed_hz is %d,clkrate is %ld\ ,we use the max prescaler value.\n", speed_hz, clkrate); *pbr = ARRAY_SIZE(pbr_tbl) - 1; *br = ARRAY_SIZE(brs) - 1; @@ -526,7 +526,6 @@ out_clk_put: clk_disable_unprepare(dspi->clk); out_master_put: spi_master_put(master); - platform_set_drvdata(pdev, NULL); return ret; } @@ -553,5 +552,5 @@ static struct platform_driver fsl_dspi_driver = { module_platform_driver(fsl_dspi_driver); MODULE_DESCRIPTION("Freescale DSPI Controller Driver"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" DRIVER_NAME); -- cgit v0.10.2 From 6e99897ba0cdeb83e91738683000aeed6a079cae Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 2 Sep 2013 11:56:21 +0530 Subject: spi: pl022: Remove redundant break 'break' immediately after return has no effect. Signed-off-by: Sachin Kamat Acked-by: Linus Walleij Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 9c511a9..1ff2d8e 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -1619,7 +1619,6 @@ static int verify_controller_parameters(struct pl022 *pl022, dev_err(&pl022->adev->dev, "RX FIFO Trigger Level is configured incorrectly\n"); return -EINVAL; - break; } switch (chip_info->tx_lev_trig) { case SSP_TX_1_OR_MORE_EMPTY_LOC: @@ -1645,7 +1644,6 @@ static int verify_controller_parameters(struct pl022 *pl022, dev_err(&pl022->adev->dev, "TX FIFO Trigger Level is configured incorrectly\n"); return -EINVAL; - break; } if (chip_info->iface == SSP_INTERFACE_NATIONAL_MICROWIRE) { if ((chip_info->ctrl_len < SSP_BITS_4) -- cgit v0.10.2 From 4ebfee9118599dece86429270be1bcbb3099f237 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 10 Sep 2013 18:53:36 +0900 Subject: spi: pl022: remove unnecessary amba_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure. Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 1ff2d8e..c13d523 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -2286,7 +2286,6 @@ pl022_remove(struct amba_device *adev) amba_release_regions(adev); tasklet_disable(&pl022->pump_transfers); spi_unregister_master(pl022->master); - amba_set_drvdata(adev, NULL); return 0; } -- cgit v0.10.2 From 5ce0ba88650f2606244a761d92e2b725f4ab3583 Mon Sep 17 00:00:00 2001 From: Hiep Cao Minh Date: Tue, 3 Sep 2013 13:10:26 +0900 Subject: spi: rcar: add Renesas QSPI support on RSPI The R8A7790 has QSPI module which is very similar to RSPI. This patch adds into RSPI module together to supports QSPI module. Signed-off-by: Hiep Cao Minh Signed-off-by: Mark Brown diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index b9c53cc..738facf 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -369,7 +369,7 @@ config SPI_PXA2XX_PCI config SPI_RSPI tristate "Renesas RSPI controller" - depends on SUPERH && SH_DMAE_BASE + depends on (SUPERH || ARCH_SHMOBILE) && SH_DMAE_BASE help SPI driver for Renesas RSPI blocks. diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 8719206..4c8dbac 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -59,6 +59,14 @@ #define RSPI_SPCMD6 0x1c #define RSPI_SPCMD7 0x1e +/*qspi only */ +#define QSPI_SPBFCR 0x18 +#define QSPI_SPBDCR 0x1a +#define QSPI_SPBMUL0 0x1c +#define QSPI_SPBMUL1 0x20 +#define QSPI_SPBMUL2 0x24 +#define QSPI_SPBMUL3 0x28 + /* SPCR */ #define SPCR_SPRIE 0x80 #define SPCR_SPE 0x40 @@ -126,6 +134,8 @@ #define SPCMD_LSBF 0x1000 #define SPCMD_SPB_MASK 0x0f00 #define SPCMD_SPB_8_TO_16(bit) (((bit - 1) << 8) & SPCMD_SPB_MASK) +#define SPCMD_SPB_8BIT 0x0000 /* qspi only */ +#define SPCMD_SPB_16BIT 0x0100 #define SPCMD_SPB_20BIT 0x0000 #define SPCMD_SPB_24BIT 0x0100 #define SPCMD_SPB_32BIT 0x0200 @@ -135,6 +145,10 @@ #define SPCMD_CPOL 0x0002 #define SPCMD_CPHA 0x0001 +/* SPBFCR */ +#define SPBFCR_TXRST 0x80 /* qspi only */ +#define SPBFCR_RXRST 0x40 /* qspi only */ + struct rspi_data { void __iomem *addr; u32 max_speed_hz; @@ -145,6 +159,7 @@ struct rspi_data { spinlock_t lock; struct clk *clk; unsigned char spsr; + const struct spi_ops *ops; /* for dmaengine */ struct dma_chan *chan_tx; @@ -165,6 +180,11 @@ static void rspi_write16(struct rspi_data *rspi, u16 data, u16 offset) iowrite16(data, rspi->addr + offset); } +static void rspi_write32(struct rspi_data *rspi, u32 data, u16 offset) +{ + iowrite32(data, rspi->addr + offset); +} + static u8 rspi_read8(struct rspi_data *rspi, u16 offset) { return ioread8(rspi->addr + offset); @@ -175,17 +195,98 @@ static u16 rspi_read16(struct rspi_data *rspi, u16 offset) return ioread16(rspi->addr + offset); } -static unsigned char rspi_calc_spbr(struct rspi_data *rspi) +/* optional functions */ +struct spi_ops { + int (*set_config_register)(struct rspi_data *rspi, int access_size); +}; + +/* + * functions for RSPI + */ +static int rspi_set_config_register(struct rspi_data *rspi, int access_size) { - int tmp; - unsigned char spbr; + int spbr; + + /* Sets output mode(CMOS) and MOSI signal(from previous transfer) */ + rspi_write8(rspi, 0x00, RSPI_SPPCR); - tmp = clk_get_rate(rspi->clk) / (2 * rspi->max_speed_hz) - 1; - spbr = clamp(tmp, 0, 255); + /* Sets transfer bit rate */ + spbr = clk_get_rate(rspi->clk) / (2 * rspi->max_speed_hz) - 1; + rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR); + + /* Sets number of frames to be used: 1 frame */ + rspi_write8(rspi, 0x00, RSPI_SPDCR); - return spbr; + /* Sets RSPCK, SSL, next-access delay value */ + rspi_write8(rspi, 0x00, RSPI_SPCKD); + rspi_write8(rspi, 0x00, RSPI_SSLND); + rspi_write8(rspi, 0x00, RSPI_SPND); + + /* Sets parity, interrupt mask */ + rspi_write8(rspi, 0x00, RSPI_SPCR2); + + /* Sets SPCMD */ + rspi_write16(rspi, SPCMD_SPB_8_TO_16(access_size) | SPCMD_SSLKP, + RSPI_SPCMD0); + + /* Sets RSPI mode */ + rspi_write8(rspi, SPCR_MSTR, RSPI_SPCR); + + return 0; } +/* + * functions for QSPI + */ +static int qspi_set_config_register(struct rspi_data *rspi, int access_size) +{ + u16 spcmd; + int spbr; + + /* Sets output mode(CMOS) and MOSI signal(from previous transfer) */ + rspi_write8(rspi, 0x00, RSPI_SPPCR); + + /* Sets transfer bit rate */ + spbr = clk_get_rate(rspi->clk) / (2 * rspi->max_speed_hz); + rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR); + + /* Sets number of frames to be used: 1 frame */ + rspi_write8(rspi, 0x00, RSPI_SPDCR); + + /* Sets RSPCK, SSL, next-access delay value */ + rspi_write8(rspi, 0x00, RSPI_SPCKD); + rspi_write8(rspi, 0x00, RSPI_SSLND); + rspi_write8(rspi, 0x00, RSPI_SPND); + + /* Data Length Setting */ + if (access_size == 8) + spcmd = SPCMD_SPB_8BIT; + else if (access_size == 16) + spcmd = SPCMD_SPB_16BIT; + else if (access_size == 32) + spcmd = SPCMD_SPB_32BIT; + + spcmd |= SPCMD_SCKDEN | SPCMD_SLNDEN | SPCMD_SSLKP | SPCMD_SPNDEN; + + /* Resets transfer data length */ + rspi_write32(rspi, 0, QSPI_SPBMUL0); + + /* Resets transmit and receive buffer */ + rspi_write8(rspi, SPBFCR_TXRST | SPBFCR_RXRST, QSPI_SPBFCR); + /* Sets buffer to allow normal operation */ + rspi_write8(rspi, 0x00, QSPI_SPBFCR); + + /* Sets SPCMD */ + rspi_write16(rspi, spcmd, RSPI_SPCMD0); + + /* Enables SPI function in a master mode */ + rspi_write8(rspi, SPCR_SPE | SPCR_MSTR, RSPI_SPCR); + + return 0; +} + +#define set_config_register(spi, n) spi->ops->set_config_register(spi, n) + static void rspi_enable_irq(struct rspi_data *rspi, u8 enable) { rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) | enable, RSPI_SPCR); @@ -220,35 +321,6 @@ static void rspi_negate_ssl(struct rspi_data *rspi) rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) & ~SPCR_SPE, RSPI_SPCR); } -static int rspi_set_config_register(struct rspi_data *rspi, int access_size) -{ - /* Sets output mode(CMOS) and MOSI signal(from previous transfer) */ - rspi_write8(rspi, 0x00, RSPI_SPPCR); - - /* Sets transfer bit rate */ - rspi_write8(rspi, rspi_calc_spbr(rspi), RSPI_SPBR); - - /* Sets number of frames to be used: 1 frame */ - rspi_write8(rspi, 0x00, RSPI_SPDCR); - - /* Sets RSPCK, SSL, next-access delay value */ - rspi_write8(rspi, 0x00, RSPI_SPCKD); - rspi_write8(rspi, 0x00, RSPI_SSLND); - rspi_write8(rspi, 0x00, RSPI_SPND); - - /* Sets parity, interrupt mask */ - rspi_write8(rspi, 0x00, RSPI_SPCR2); - - /* Sets SPCMD */ - rspi_write16(rspi, SPCMD_SPB_8_TO_16(access_size) | SPCMD_SSLKP, - RSPI_SPCMD0); - - /* Sets RSPI mode */ - rspi_write8(rspi, SPCR_MSTR, RSPI_SPCR); - - return 0; -} - static int rspi_send_pio(struct rspi_data *rspi, struct spi_message *mesg, struct spi_transfer *t) { @@ -616,7 +688,7 @@ static int rspi_setup(struct spi_device *spi) spi->bits_per_word = 8; rspi->max_speed_hz = spi->max_speed_hz; - rspi_set_config_register(rspi, 8); + set_config_register(rspi, 8); return 0; } @@ -745,7 +817,16 @@ static int rspi_probe(struct platform_device *pdev) struct rspi_data *rspi; int ret, irq; char clk_name[16]; - + struct rspi_plat_data *rspi_pd = pdev->dev.platform_data; + const struct spi_ops *ops; + const struct platform_device_id *id_entry = pdev->id_entry; + + ops = (struct spi_ops *)id_entry->driver_data; + /* ops parameter check */ + if (!ops->set_config_register) { + dev_err(&pdev->dev, "there is no set_config_register\n"); + return -ENODEV; + } /* get base addr */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (unlikely(res == NULL)) { @@ -767,7 +848,7 @@ static int rspi_probe(struct platform_device *pdev) rspi = spi_master_get_devdata(master); platform_set_drvdata(pdev, rspi); - + rspi->ops = ops; rspi->master = master; rspi->addr = ioremap(res->start, resource_size(res)); if (rspi->addr == NULL) { @@ -776,7 +857,7 @@ static int rspi_probe(struct platform_device *pdev) goto error1; } - snprintf(clk_name, sizeof(clk_name), "rspi%d", pdev->id); + snprintf(clk_name, sizeof(clk_name), "%s%d", id_entry->name, pdev->id); rspi->clk = clk_get(&pdev->dev, clk_name); if (IS_ERR(rspi->clk)) { dev_err(&pdev->dev, "cannot get clock\n"); @@ -790,7 +871,10 @@ static int rspi_probe(struct platform_device *pdev) INIT_WORK(&rspi->ws, rspi_work); init_waitqueue_head(&rspi->wait); - master->num_chipselect = 2; + master->num_chipselect = rspi_pd->num_chipselect; + if (!master->num_chipselect) + master->num_chipselect = 2; /* default */ + master->bus_num = pdev->id; master->setup = rspi_setup; master->transfer = rspi_transfer; @@ -832,11 +916,28 @@ error1: return ret; } +static struct spi_ops rspi_ops = { + .set_config_register = rspi_set_config_register, +}; + +static struct spi_ops qspi_ops = { + .set_config_register = qspi_set_config_register, +}; + +static struct platform_device_id spi_driver_ids[] = { + { "rspi", (kernel_ulong_t)&rspi_ops }, + { "qspi", (kernel_ulong_t)&qspi_ops }, + {}, +}; + +MODULE_DEVICE_TABLE(platform, spi_driver_ids); + static struct platform_driver rspi_driver = { .probe = rspi_probe, .remove = rspi_remove, + .id_table = spi_driver_ids, .driver = { - .name = "rspi", + .name = "renesas_spi", .owner = THIS_MODULE, }, }; diff --git a/include/linux/spi/rspi.h b/include/linux/spi/rspi.h index 900f0e3..a25bd6f 100644 --- a/include/linux/spi/rspi.h +++ b/include/linux/spi/rspi.h @@ -26,6 +26,8 @@ struct rspi_plat_data { unsigned int dma_rx_id; unsigned dma_width_16bit:1; /* DMAC read/write width = 16-bit */ + + u16 num_chipselect; }; #endif -- cgit v0.10.2 From cfeb33127bfd5c248c8633715d75c04186d8fca9 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 10 Sep 2013 11:20:13 +0530 Subject: spi: spi-s3c24xx: Staticize s3c24xx_spi_tryfiq 's3c24xx_spi_tryfiq' is used only in this file. Make it static. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c24xx.c b/drivers/spi/spi-s3c24xx.c index ce318d9..d39589d 100644 --- a/drivers/spi/spi-s3c24xx.c +++ b/drivers/spi/spi-s3c24xx.c @@ -280,7 +280,7 @@ static inline u32 ack_bit(unsigned int irq) * so the caller does not need to do anything more than start the transfer * as normal, since the IRQ will have been re-routed to the FIQ handler. */ -void s3c24xx_spi_tryfiq(struct s3c24xx_spi *hw) +static void s3c24xx_spi_tryfiq(struct s3c24xx_spi *hw) { struct pt_regs regs; enum spi_fiq_mode mode; -- cgit v0.10.2 From 50c959fc3366693174cdf3f1c82d0c01bbee926f Mon Sep 17 00:00:00 2001 From: Lukasz Czerwinski Date: Mon, 9 Sep 2013 16:09:25 +0200 Subject: spi: spi-s3c64xx: Use module_platform_driver() subsys_init_call() initializes driver too early. It's preventing to move DMA channel allocation at the begining (driver probe). This patch reduces and simplifies initalization code by using module_platform_driver() macro. It's also efficiently delaying driver startup. Signed-off-by: Lukasz Czerwinski Signed-off-by: Kyungmin Park Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 512b889..20dd712 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1633,22 +1633,13 @@ static struct platform_driver s3c64xx_spi_driver = { .pm = &s3c64xx_spi_pm, .of_match_table = of_match_ptr(s3c64xx_spi_dt_match), }, + .probe = s3c64xx_spi_probe, .remove = s3c64xx_spi_remove, .id_table = s3c64xx_spi_driver_ids, }; MODULE_ALIAS("platform:s3c64xx-spi"); -static int __init s3c64xx_spi_init(void) -{ - return platform_driver_probe(&s3c64xx_spi_driver, s3c64xx_spi_probe); -} -subsys_initcall(s3c64xx_spi_init); - -static void __exit s3c64xx_spi_exit(void) -{ - platform_driver_unregister(&s3c64xx_spi_driver); -} -module_exit(s3c64xx_spi_exit); +module_platform_driver(s3c64xx_spi_driver); MODULE_AUTHOR("Jaswinder Singh "); MODULE_DESCRIPTION("S3C64XX SPI Controller Driver"); -- cgit v0.10.2 From e91d2352bb6ebd061b362b3c632ec517ac0c23b1 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 30 Aug 2013 11:00:23 +0800 Subject: spi: tegra: Use DIV_ROUND_UP instead of open coded This also makes the intention more clear. Signed-off-by: Axel Lin Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index 145dd43..946ff73 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -267,7 +267,7 @@ static unsigned tegra_spi_calculate_curr_xfer_param( unsigned max_len; unsigned total_fifo_words; - tspi->bytes_per_word = (bits_per_word - 1) / 8 + 1; + tspi->bytes_per_word = DIV_ROUND_UP(bits_per_word, 8); if (bits_per_word == 8 || bits_per_word == 16) { tspi->is_packed = 1; diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c index 1d814dc..64c9cf5 100644 --- a/drivers/spi/spi-tegra20-sflash.c +++ b/drivers/spi/spi-tegra20-sflash.c @@ -173,7 +173,7 @@ static unsigned tegra_sflash_calculate_curr_xfer_param( unsigned remain_len = t->len - tsd->cur_pos; unsigned max_word; - tsd->bytes_per_word = (t->bits_per_word - 1) / 8 + 1; + tsd->bytes_per_word = DIV_ROUND_UP(t->bits_per_word, 8); max_word = remain_len / tsd->bytes_per_word; if (max_word > SPI_FIFO_DEPTH) max_word = SPI_FIFO_DEPTH; diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index c703536..231b845 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -283,7 +283,7 @@ static unsigned tegra_slink_calculate_curr_xfer_param( unsigned total_fifo_words; bits_per_word = t->bits_per_word; - tspi->bytes_per_word = (bits_per_word - 1) / 8 + 1; + tspi->bytes_per_word = DIV_ROUND_UP(bits_per_word, 8); if (bits_per_word == 8 || bits_per_word == 16) { tspi->is_packed = 1; -- cgit v0.10.2 From 0fdfd40aca7046eb0b770d64dfe526a03ed5d092 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 9 Sep 2013 13:49:27 +0200 Subject: regulator: da9063: Add missing initialization of da9063_reg_matches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With gcc 4.1.2: drivers/regulator/da9063-regulator.c: In function ‘da9063_regulator_probe’: drivers/regulator/da9063-regulator.c:847: warning: ‘da9063_reg_matches’ is used uninitialized in this function If the parent device already has platform data, da9063_reg_matches will not be initialized. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown diff --git a/drivers/regulator/da9063-regulator.c b/drivers/regulator/da9063-regulator.c index 1a78163..3622613 100644 --- a/drivers/regulator/da9063-regulator.c +++ b/drivers/regulator/da9063-regulator.c @@ -717,7 +717,7 @@ static int da9063_regulator_probe(struct platform_device *pdev) { struct da9063 *da9063 = dev_get_drvdata(pdev->dev.parent); struct da9063_pdata *da9063_pdata = dev_get_platdata(da9063->dev); - struct of_regulator_match *da9063_reg_matches; + struct of_regulator_match *da9063_reg_matches = NULL; struct da9063_regulators_pdata *regl_pdata; const struct da9063_dev_model *model; struct da9063_regulators *regulators; -- cgit v0.10.2 From 1aa20d27b4d23c18a20a9268e49e5d21a74789ff Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 31 Aug 2013 11:42:46 +0100 Subject: regulator: wm831x-dcdc: Convert to devm_gpio_request_one() Saves code in the unwind path. Signed-off-by: Mark Brown diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c index 11861cb..8f62750 100644 --- a/drivers/regulator/wm831x-dcdc.c +++ b/drivers/regulator/wm831x-dcdc.c @@ -387,8 +387,9 @@ static struct regulator_ops wm831x_buckv_ops = { * Set up DVS control. We just log errors since we can still run * (with reduced performance) if we fail. */ -static void wm831x_buckv_dvs_init(struct wm831x_dcdc *dcdc, - struct wm831x_buckv_pdata *pdata) +static void wm831x_buckv_dvs_init(struct platform_device *pdev, + struct wm831x_dcdc *dcdc, + struct wm831x_buckv_pdata *pdata) { struct wm831x *wm831x = dcdc->wm831x; int ret; @@ -402,9 +403,9 @@ static void wm831x_buckv_dvs_init(struct wm831x_dcdc *dcdc, */ dcdc->dvs_gpio_state = pdata->dvs_init_state; - ret = gpio_request_one(pdata->dvs_gpio, - dcdc->dvs_gpio_state ? GPIOF_INIT_HIGH : 0, - "DCDC DVS"); + ret = devm_gpio_request_one(&pdev->dev, pdata->dvs_gpio, + dcdc->dvs_gpio_state ? GPIOF_INIT_HIGH : 0, + "DCDC DVS"); if (ret < 0) { dev_err(wm831x->dev, "Failed to get %s DVS GPIO: %d\n", dcdc->name, ret); @@ -513,7 +514,8 @@ static int wm831x_buckv_probe(struct platform_device *pdev) dcdc->dvs_vsel = ret & WM831X_DC1_DVS_VSEL_MASK; if (pdata && pdata->dcdc[id]) - wm831x_buckv_dvs_init(dcdc, pdata->dcdc[id]->driver_data); + wm831x_buckv_dvs_init(pdev, dcdc, + pdata->dcdc[id]->driver_data); config.dev = pdev->dev.parent; if (pdata) @@ -557,8 +559,6 @@ err_uv: err_regulator: regulator_unregister(dcdc->regulator); err: - if (dcdc->dvs_gpio) - gpio_free(dcdc->dvs_gpio); return ret; } @@ -572,8 +572,6 @@ static int wm831x_buckv_remove(struct platform_device *pdev) free_irq(wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")), dcdc); regulator_unregister(dcdc->regulator); - if (dcdc->dvs_gpio) - gpio_free(dcdc->dvs_gpio); return 0; } -- cgit v0.10.2 From b0c4c0c68982156bfb94993305e69a03309395c3 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 31 Aug 2013 11:47:39 +0100 Subject: regulator: wm831x-dcdc: Convert to devm_request_threaded_irq() devm guarantees that resources are freed in the opposite order to that in which they are registered. Signed-off-by: Mark Brown diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c index 8f62750..aca2fcb 100644 --- a/drivers/regulator/wm831x-dcdc.c +++ b/drivers/regulator/wm831x-dcdc.c @@ -532,8 +532,9 @@ static int wm831x_buckv_probe(struct platform_device *pdev) } irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")); - ret = request_threaded_irq(irq, NULL, wm831x_dcdc_uv_irq, - IRQF_TRIGGER_RISING, dcdc->name, dcdc); + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + wm831x_dcdc_uv_irq, + IRQF_TRIGGER_RISING, dcdc->name, dcdc); if (ret != 0) { dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n", irq, ret); @@ -541,21 +542,19 @@ static int wm831x_buckv_probe(struct platform_device *pdev) } irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "HC")); - ret = request_threaded_irq(irq, NULL, wm831x_dcdc_oc_irq, - IRQF_TRIGGER_RISING, dcdc->name, dcdc); + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + wm831x_dcdc_oc_irq, + IRQF_TRIGGER_RISING, dcdc->name, dcdc); if (ret != 0) { dev_err(&pdev->dev, "Failed to request HC IRQ %d: %d\n", irq, ret); - goto err_uv; + goto err_regulator; } platform_set_drvdata(pdev, dcdc); return 0; -err_uv: - free_irq(wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")), - dcdc); err_regulator: regulator_unregister(dcdc->regulator); err: @@ -565,12 +564,7 @@ err: static int wm831x_buckv_remove(struct platform_device *pdev) { struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); - struct wm831x *wm831x = dcdc->wm831x; - free_irq(wm831x_irq(wm831x, platform_get_irq_byname(pdev, "HC")), - dcdc); - free_irq(wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")), - dcdc); regulator_unregister(dcdc->regulator); return 0; @@ -688,8 +682,9 @@ static int wm831x_buckp_probe(struct platform_device *pdev) } irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")); - ret = request_threaded_irq(irq, NULL, wm831x_dcdc_uv_irq, - IRQF_TRIGGER_RISING, dcdc->name, dcdc); + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + wm831x_dcdc_uv_irq, + IRQF_TRIGGER_RISING, dcdc->name, dcdc); if (ret != 0) { dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n", irq, ret); @@ -710,8 +705,6 @@ static int wm831x_buckp_remove(struct platform_device *pdev) { struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); - free_irq(wm831x_irq(dcdc->wm831x, platform_get_irq_byname(pdev, "UV")), - dcdc); regulator_unregister(dcdc->regulator); return 0; @@ -820,9 +813,10 @@ static int wm831x_boostp_probe(struct platform_device *pdev) } irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")); - ret = request_threaded_irq(irq, NULL, wm831x_dcdc_uv_irq, - IRQF_TRIGGER_RISING, dcdc->name, - dcdc); + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + wm831x_dcdc_uv_irq, + IRQF_TRIGGER_RISING, dcdc->name, + dcdc); if (ret != 0) { dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n", irq, ret); @@ -843,8 +837,6 @@ static int wm831x_boostp_remove(struct platform_device *pdev) { struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); - free_irq(wm831x_irq(dcdc->wm831x, platform_get_irq_byname(pdev, "UV")), - dcdc); regulator_unregister(dcdc->regulator); return 0; -- cgit v0.10.2 From 63fb3149c8ba2b0194ba7e967f2dafc1c8c0da69 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 31 Aug 2013 11:48:45 +0100 Subject: regulator: wm831x-isink: Use devm_request_threaded_irq() devm guarantees that resources are freed in the opposite order to that in which they are allocated. Signed-off-by: Mark Brown diff --git a/drivers/regulator/wm831x-isink.c b/drivers/regulator/wm831x-isink.c index 4eb373d..841cb52 100644 --- a/drivers/regulator/wm831x-isink.c +++ b/drivers/regulator/wm831x-isink.c @@ -203,8 +203,10 @@ static int wm831x_isink_probe(struct platform_device *pdev) } irq = wm831x_irq(wm831x, platform_get_irq(pdev, 0)); - ret = request_threaded_irq(irq, NULL, wm831x_isink_irq, - IRQF_TRIGGER_RISING, isink->name, isink); + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + wm831x_isink_irq, + IRQF_TRIGGER_RISING, isink->name, + isink); if (ret != 0) { dev_err(&pdev->dev, "Failed to request ISINK IRQ %d: %d\n", irq, ret); @@ -225,8 +227,6 @@ static int wm831x_isink_remove(struct platform_device *pdev) { struct wm831x_isink *isink = platform_get_drvdata(pdev); - free_irq(wm831x_irq(isink->wm831x, platform_get_irq(pdev, 0)), isink); - regulator_unregister(isink->regulator); return 0; -- cgit v0.10.2 From 41c7a879d1d637c0c9731682a822b1c3162b0764 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 31 Aug 2013 11:50:00 +0100 Subject: regulator: wm831x-ldo: Use devm_request_threaded_irq() devm guarantees that resources are freed in the oposite order to that in which they are allocated. Signed-off-by: Mark Brown diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c index 1432b26..5570f3e 100644 --- a/drivers/regulator/wm831x-ldo.c +++ b/drivers/regulator/wm831x-ldo.c @@ -288,9 +288,10 @@ static int wm831x_gp_ldo_probe(struct platform_device *pdev) } irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")); - ret = request_threaded_irq(irq, NULL, wm831x_ldo_uv_irq, - IRQF_TRIGGER_RISING, ldo->name, - ldo); + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + wm831x_ldo_uv_irq, + IRQF_TRIGGER_RISING, ldo->name, + ldo); if (ret != 0) { dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n", irq, ret); @@ -311,8 +312,6 @@ static int wm831x_gp_ldo_remove(struct platform_device *pdev) { struct wm831x_ldo *ldo = platform_get_drvdata(pdev); - free_irq(wm831x_irq(ldo->wm831x, - platform_get_irq_byname(pdev, "UV")), ldo); regulator_unregister(ldo->regulator); return 0; @@ -514,8 +513,9 @@ static int wm831x_aldo_probe(struct platform_device *pdev) } irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")); - ret = request_threaded_irq(irq, NULL, wm831x_ldo_uv_irq, - IRQF_TRIGGER_RISING, ldo->name, ldo); + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + wm831x_ldo_uv_irq, + IRQF_TRIGGER_RISING, ldo->name, ldo); if (ret != 0) { dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n", irq, ret); @@ -536,8 +536,6 @@ static int wm831x_aldo_remove(struct platform_device *pdev) { struct wm831x_ldo *ldo = platform_get_drvdata(pdev); - free_irq(wm831x_irq(ldo->wm831x, platform_get_irq_byname(pdev, "UV")), - ldo); regulator_unregister(ldo->regulator); return 0; -- cgit v0.10.2 From b33e46bcdc4e598d738ed12a5a7906be4e11d786 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 31 Aug 2013 11:58:26 +0100 Subject: regulator: core: Provide managed regulator registration Many regulator drivers have a remove function that consists solely of calling regulator_unregister() so provide a devm_regulator_register() in order to allow this repeated code to be removed and help eliminate error handling code. Signed-off-by: Mark Brown diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index fcb34a5..3d9c2a7 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -283,6 +283,7 @@ REGULATOR devm_regulator_get() devm_regulator_put() devm_regulator_bulk_get() + devm_regulator_register() CLOCK devm_clk_get() diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index a01b8b3..5f995d2 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -3492,6 +3492,44 @@ clean: } EXPORT_SYMBOL_GPL(regulator_register); +static void devm_rdev_release(struct device *dev, void *res) +{ + regulator_unregister(*(struct regulator_dev **)res); +} + +/** + * devm_regulator_register - Resource managed regulator_register() + * @regulator_desc: regulator to register + * @config: runtime configuration for regulator + * + * Called by regulator drivers to register a regulator. Returns a + * valid pointer to struct regulator_dev on success or an ERR_PTR() on + * error. The regulator will automaticall be released when the device + * is unbound. + */ +struct regulator_dev *devm_regulator_register(struct device *dev, + const struct regulator_desc *regulator_desc, + const struct regulator_config *config) +{ + struct regulator_dev **ptr, *rdev; + + ptr = devres_alloc(devm_rdev_release, sizeof(*ptr), + GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + rdev = regulator_register(regulator_desc, config); + if (!IS_ERR(rdev)) { + *ptr = rdev; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return rdev; +} +EXPORT_SYMBOL_GPL(devm_regulator_register); + /** * regulator_unregister - unregister regulator * @rdev: regulator to unregister @@ -3521,6 +3559,34 @@ void regulator_unregister(struct regulator_dev *rdev) } EXPORT_SYMBOL_GPL(regulator_unregister); +static int devm_rdev_match(struct device *dev, void *res, void *data) +{ + struct regulator_dev **r = res; + if (!r || !*r) { + WARN_ON(!r || !*r); + return 0; + } + return *r == data; +} + +/** + * devm_regulator_unregister - Resource managed regulator_unregister() + * @regulator: regulator to free + * + * Unregister a regulator registered with devm_regulator_register(). + * Normally this function will not need to be called and the resource + * management code will ensure that the resource is freed. + */ +void devm_regulator_unregister(struct device *dev, struct regulator_dev *rdev) +{ + int rc; + + rc = devres_release(dev, devm_rdev_release, devm_rdev_match, rdev); + if (rc != 0) + WARN_ON(rc); +} +EXPORT_SYMBOL_GPL(devm_regulator_unregister); + /** * regulator_suspend_prepare - prepare regulators for system wide suspend * @state: system suspend state diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 67e13aa..8474c7f 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -334,7 +334,12 @@ struct regulator_dev { struct regulator_dev * regulator_register(const struct regulator_desc *regulator_desc, const struct regulator_config *config); +struct regulator_dev * +devm_regulator_register(struct device *dev, + const struct regulator_desc *regulator_desc, + const struct regulator_config *config); void regulator_unregister(struct regulator_dev *rdev); +void devm_regulator_unregister(struct device *dev, struct regulator_dev *rdev); int regulator_notifier_call_chain(struct regulator_dev *rdev, unsigned long event, void *data); -- cgit v0.10.2 From 14ffa8882bbd991497f2f87ce80382e5a1e6eb8f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 31 Aug 2013 11:56:47 +0100 Subject: regulator: arizona-ldo1: Convert to devm_regulator_register() Signed-off-by: Mark Brown diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c index 81d8681..4f6c205 100644 --- a/drivers/regulator/arizona-ldo1.c +++ b/drivers/regulator/arizona-ldo1.c @@ -226,7 +226,7 @@ static int arizona_ldo1_probe(struct platform_device *pdev) else config.init_data = &ldo1->init_data; - ldo1->regulator = regulator_register(desc, &config); + ldo1->regulator = devm_regulator_register(&pdev->dev, desc, &config); if (IS_ERR(ldo1->regulator)) { ret = PTR_ERR(ldo1->regulator); dev_err(arizona->dev, "Failed to register LDO1 supply: %d\n", @@ -239,18 +239,8 @@ static int arizona_ldo1_probe(struct platform_device *pdev) return 0; } -static int arizona_ldo1_remove(struct platform_device *pdev) -{ - struct arizona_ldo1 *ldo1 = platform_get_drvdata(pdev); - - regulator_unregister(ldo1->regulator); - - return 0; -} - static struct platform_driver arizona_ldo1_driver = { .probe = arizona_ldo1_probe, - .remove = arizona_ldo1_remove, .driver = { .name = "arizona-ldo1", .owner = THIS_MODULE, -- cgit v0.10.2 From b6b7709cf95c6f5db074f2d725ddf29b0977589f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 31 Aug 2013 11:57:00 +0100 Subject: regulator: arizona-micsupp: Convert to devm_regulator_register() Signed-off-by: Mark Brown diff --git a/drivers/regulator/arizona-micsupp.c b/drivers/regulator/arizona-micsupp.c index e87536b..724706a 100644 --- a/drivers/regulator/arizona-micsupp.c +++ b/drivers/regulator/arizona-micsupp.c @@ -225,7 +225,9 @@ static int arizona_micsupp_probe(struct platform_device *pdev) regmap_update_bits(arizona->regmap, ARIZONA_MIC_CHARGE_PUMP_1, ARIZONA_CPMIC_BYPASS, 0); - micsupp->regulator = regulator_register(&arizona_micsupp, &config); + micsupp->regulator = devm_regulator_register(&pdev->dev, + &arizona_micsupp, + &config); if (IS_ERR(micsupp->regulator)) { ret = PTR_ERR(micsupp->regulator); dev_err(arizona->dev, "Failed to register mic supply: %d\n", @@ -238,18 +240,8 @@ static int arizona_micsupp_probe(struct platform_device *pdev) return 0; } -static int arizona_micsupp_remove(struct platform_device *pdev) -{ - struct arizona_micsupp *micsupp = platform_get_drvdata(pdev); - - regulator_unregister(micsupp->regulator); - - return 0; -} - static struct platform_driver arizona_micsupp_driver = { .probe = arizona_micsupp_probe, - .remove = arizona_micsupp_remove, .driver = { .name = "arizona-micsupp", .owner = THIS_MODULE, -- cgit v0.10.2 From b707a274455f8f80feab24b999dad9e649c98c9c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 31 Aug 2013 11:57:16 +0100 Subject: regulator: s2mps11: Convert to devm_regulator_register() Signed-off-by: Mark Brown Acked-by: Sangbeom Kim diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index 5eba2ff..d7c241e 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -453,28 +453,11 @@ common_reg: ret = PTR_ERR(s2mps11->rdev[i]); dev_err(&pdev->dev, "regulator init failed for %d\n", i); - s2mps11->rdev[i] = NULL; - goto err; + return ret; } } return 0; -err: - for (i = 0; i < S2MPS11_REGULATOR_MAX; i++) - regulator_unregister(s2mps11->rdev[i]); - - return ret; -} - -static int s2mps11_pmic_remove(struct platform_device *pdev) -{ - struct s2mps11_info *s2mps11 = platform_get_drvdata(pdev); - int i; - - for (i = 0; i < S2MPS11_REGULATOR_MAX; i++) - regulator_unregister(s2mps11->rdev[i]); - - return 0; } static const struct platform_device_id s2mps11_pmic_id[] = { @@ -489,7 +472,6 @@ static struct platform_driver s2mps11_pmic_driver = { .owner = THIS_MODULE, }, .probe = s2mps11_pmic_probe, - .remove = s2mps11_pmic_remove, .id_table = s2mps11_pmic_id, }; -- cgit v0.10.2 From f0db475dee8fdae611364829ceb7afcd6aa02166 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 31 Aug 2013 11:57:34 +0100 Subject: regulator: s5m8767: Covert to devm_regulator_register() Signed-off-by: Mark Brown Acked-by: Sangbeom Kim diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index c24448b..2297fdf 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -910,34 +910,17 @@ static int s5m8767_pmic_probe(struct platform_device *pdev) config.regmap = iodev->regmap; config.of_node = pdata->regulators[i].reg_node; - rdev[i] = regulator_register(®ulators[id], &config); + rdev[i] = devm_regulator_register(&pdev->dev, ®ulators[id], + &config); if (IS_ERR(rdev[i])) { ret = PTR_ERR(rdev[i]); dev_err(s5m8767->dev, "regulator init failed for %d\n", id); - rdev[i] = NULL; - goto err; + return ret; } } return 0; -err: - for (i = 0; i < s5m8767->num_regulators; i++) - regulator_unregister(rdev[i]); - - return ret; -} - -static int s5m8767_pmic_remove(struct platform_device *pdev) -{ - struct s5m8767_info *s5m8767 = platform_get_drvdata(pdev); - struct regulator_dev **rdev = s5m8767->rdev; - int i; - - for (i = 0; i < s5m8767->num_regulators; i++) - regulator_unregister(rdev[i]); - - return 0; } static const struct platform_device_id s5m8767_pmic_id[] = { @@ -952,7 +935,6 @@ static struct platform_driver s5m8767_pmic_driver = { .owner = THIS_MODULE, }, .probe = s5m8767_pmic_probe, - .remove = s5m8767_pmic_remove, .id_table = s5m8767_pmic_id, }; -- cgit v0.10.2 From d73b4cb7b3632ccd726746e9cb43002f1adf7956 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 31 Aug 2013 11:58:40 +0100 Subject: regulator: wm831x-dcdc: Convert to devm_regulator_register() Signed-off-by: Mark Brown diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c index aca2fcb..6823e6f 100644 --- a/drivers/regulator/wm831x-dcdc.c +++ b/drivers/regulator/wm831x-dcdc.c @@ -523,7 +523,8 @@ static int wm831x_buckv_probe(struct platform_device *pdev) config.driver_data = dcdc; config.regmap = wm831x->regmap; - dcdc->regulator = regulator_register(&dcdc->desc, &config); + dcdc->regulator = devm_regulator_register(&pdev->dev, &dcdc->desc, + &config); if (IS_ERR(dcdc->regulator)) { ret = PTR_ERR(dcdc->regulator); dev_err(wm831x->dev, "Failed to register DCDC%d: %d\n", @@ -538,7 +539,7 @@ static int wm831x_buckv_probe(struct platform_device *pdev) if (ret != 0) { dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n", irq, ret); - goto err_regulator; + goto err; } irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "HC")); @@ -548,31 +549,19 @@ static int wm831x_buckv_probe(struct platform_device *pdev) if (ret != 0) { dev_err(&pdev->dev, "Failed to request HC IRQ %d: %d\n", irq, ret); - goto err_regulator; + goto err; } platform_set_drvdata(pdev, dcdc); return 0; -err_regulator: - regulator_unregister(dcdc->regulator); err: return ret; } -static int wm831x_buckv_remove(struct platform_device *pdev) -{ - struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); - - regulator_unregister(dcdc->regulator); - - return 0; -} - static struct platform_driver wm831x_buckv_driver = { .probe = wm831x_buckv_probe, - .remove = wm831x_buckv_remove, .driver = { .name = "wm831x-buckv", .owner = THIS_MODULE, @@ -673,7 +662,8 @@ static int wm831x_buckp_probe(struct platform_device *pdev) config.driver_data = dcdc; config.regmap = wm831x->regmap; - dcdc->regulator = regulator_register(&dcdc->desc, &config); + dcdc->regulator = devm_regulator_register(&pdev->dev, &dcdc->desc, + &config); if (IS_ERR(dcdc->regulator)) { ret = PTR_ERR(dcdc->regulator); dev_err(wm831x->dev, "Failed to register DCDC%d: %d\n", @@ -688,31 +678,19 @@ static int wm831x_buckp_probe(struct platform_device *pdev) if (ret != 0) { dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n", irq, ret); - goto err_regulator; + goto err; } platform_set_drvdata(pdev, dcdc); return 0; -err_regulator: - regulator_unregister(dcdc->regulator); err: return ret; } -static int wm831x_buckp_remove(struct platform_device *pdev) -{ - struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); - - regulator_unregister(dcdc->regulator); - - return 0; -} - static struct platform_driver wm831x_buckp_driver = { .probe = wm831x_buckp_probe, - .remove = wm831x_buckp_remove, .driver = { .name = "wm831x-buckp", .owner = THIS_MODULE, @@ -804,7 +782,8 @@ static int wm831x_boostp_probe(struct platform_device *pdev) config.driver_data = dcdc; config.regmap = wm831x->regmap; - dcdc->regulator = regulator_register(&dcdc->desc, &config); + dcdc->regulator = devm_regulator_register(&pdev->dev, &dcdc->desc, + &config); if (IS_ERR(dcdc->regulator)) { ret = PTR_ERR(dcdc->regulator); dev_err(wm831x->dev, "Failed to register DCDC%d: %d\n", @@ -820,31 +799,19 @@ static int wm831x_boostp_probe(struct platform_device *pdev) if (ret != 0) { dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n", irq, ret); - goto err_regulator; + goto err; } platform_set_drvdata(pdev, dcdc); return 0; -err_regulator: - regulator_unregister(dcdc->regulator); err: return ret; } -static int wm831x_boostp_remove(struct platform_device *pdev) -{ - struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); - - regulator_unregister(dcdc->regulator); - - return 0; -} - static struct platform_driver wm831x_boostp_driver = { .probe = wm831x_boostp_probe, - .remove = wm831x_boostp_remove, .driver = { .name = "wm831x-boostp", .owner = THIS_MODULE, @@ -904,7 +871,8 @@ static int wm831x_epe_probe(struct platform_device *pdev) config.driver_data = dcdc; config.regmap = wm831x->regmap; - dcdc->regulator = regulator_register(&dcdc->desc, &config); + dcdc->regulator = devm_regulator_register(&pdev->dev, &dcdc->desc, + &config); if (IS_ERR(dcdc->regulator)) { ret = PTR_ERR(dcdc->regulator); dev_err(wm831x->dev, "Failed to register EPE%d: %d\n", @@ -920,18 +888,8 @@ err: return ret; } -static int wm831x_epe_remove(struct platform_device *pdev) -{ - struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); - - regulator_unregister(dcdc->regulator); - - return 0; -} - static struct platform_driver wm831x_epe_driver = { .probe = wm831x_epe_probe, - .remove = wm831x_epe_remove, .driver = { .name = "wm831x-epe", .owner = THIS_MODULE, -- cgit v0.10.2 From af151ded1b4b22dbedd8cb1600fcaf72e3eaf31e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 31 Aug 2013 11:59:57 +0100 Subject: regulator: wm831x-isink: Convert to devm_regulator_register() Signed-off-by: Mark Brown diff --git a/drivers/regulator/wm831x-isink.c b/drivers/regulator/wm831x-isink.c index 841cb52..0339b88 100644 --- a/drivers/regulator/wm831x-isink.c +++ b/drivers/regulator/wm831x-isink.c @@ -194,7 +194,8 @@ static int wm831x_isink_probe(struct platform_device *pdev) config.init_data = pdata->isink[id]; config.driver_data = isink; - isink->regulator = regulator_register(&isink->desc, &config); + isink->regulator = devm_regulator_register(&pdev->dev, &isink->desc, + &config); if (IS_ERR(isink->regulator)) { ret = PTR_ERR(isink->regulator); dev_err(wm831x->dev, "Failed to register ISINK%d: %d\n", @@ -210,31 +211,19 @@ static int wm831x_isink_probe(struct platform_device *pdev) if (ret != 0) { dev_err(&pdev->dev, "Failed to request ISINK IRQ %d: %d\n", irq, ret); - goto err_regulator; + goto err; } platform_set_drvdata(pdev, isink); return 0; -err_regulator: - regulator_unregister(isink->regulator); err: return ret; } -static int wm831x_isink_remove(struct platform_device *pdev) -{ - struct wm831x_isink *isink = platform_get_drvdata(pdev); - - regulator_unregister(isink->regulator); - - return 0; -} - static struct platform_driver wm831x_isink_driver = { .probe = wm831x_isink_probe, - .remove = wm831x_isink_remove, .driver = { .name = "wm831x-isink", .owner = THIS_MODULE, -- cgit v0.10.2 From fc7c60e390c9b99b28d078c27360cb6cd688cfd3 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 31 Aug 2013 12:00:17 +0100 Subject: regulator: wm831x-ldo: Convert to devm_regulator_register() Signed-off-by: Mark Brown diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c index 5570f3e..2be72fe 100644 --- a/drivers/regulator/wm831x-ldo.c +++ b/drivers/regulator/wm831x-ldo.c @@ -279,7 +279,8 @@ static int wm831x_gp_ldo_probe(struct platform_device *pdev) config.driver_data = ldo; config.regmap = wm831x->regmap; - ldo->regulator = regulator_register(&ldo->desc, &config); + ldo->regulator = devm_regulator_register(&pdev->dev, &ldo->desc, + &config); if (IS_ERR(ldo->regulator)) { ret = PTR_ERR(ldo->regulator); dev_err(wm831x->dev, "Failed to register LDO%d: %d\n", @@ -295,31 +296,19 @@ static int wm831x_gp_ldo_probe(struct platform_device *pdev) if (ret != 0) { dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n", irq, ret); - goto err_regulator; + goto err; } platform_set_drvdata(pdev, ldo); return 0; -err_regulator: - regulator_unregister(ldo->regulator); err: return ret; } -static int wm831x_gp_ldo_remove(struct platform_device *pdev) -{ - struct wm831x_ldo *ldo = platform_get_drvdata(pdev); - - regulator_unregister(ldo->regulator); - - return 0; -} - static struct platform_driver wm831x_gp_ldo_driver = { .probe = wm831x_gp_ldo_probe, - .remove = wm831x_gp_ldo_remove, .driver = { .name = "wm831x-ldo", .owner = THIS_MODULE, @@ -504,7 +493,8 @@ static int wm831x_aldo_probe(struct platform_device *pdev) config.driver_data = ldo; config.regmap = wm831x->regmap; - ldo->regulator = regulator_register(&ldo->desc, &config); + ldo->regulator = devm_regulator_register(&pdev->dev, &ldo->desc, + &config); if (IS_ERR(ldo->regulator)) { ret = PTR_ERR(ldo->regulator); dev_err(wm831x->dev, "Failed to register LDO%d: %d\n", @@ -519,31 +509,19 @@ static int wm831x_aldo_probe(struct platform_device *pdev) if (ret != 0) { dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n", irq, ret); - goto err_regulator; + goto err; } platform_set_drvdata(pdev, ldo); return 0; -err_regulator: - regulator_unregister(ldo->regulator); err: return ret; } -static int wm831x_aldo_remove(struct platform_device *pdev) -{ - struct wm831x_ldo *ldo = platform_get_drvdata(pdev); - - regulator_unregister(ldo->regulator); - - return 0; -} - static struct platform_driver wm831x_aldo_driver = { .probe = wm831x_aldo_probe, - .remove = wm831x_aldo_remove, .driver = { .name = "wm831x-aldo", .owner = THIS_MODULE, @@ -661,7 +639,8 @@ static int wm831x_alive_ldo_probe(struct platform_device *pdev) config.driver_data = ldo; config.regmap = wm831x->regmap; - ldo->regulator = regulator_register(&ldo->desc, &config); + ldo->regulator = devm_regulator_register(&pdev->dev, &ldo->desc, + &config); if (IS_ERR(ldo->regulator)) { ret = PTR_ERR(ldo->regulator); dev_err(wm831x->dev, "Failed to register LDO%d: %d\n", @@ -677,18 +656,8 @@ err: return ret; } -static int wm831x_alive_ldo_remove(struct platform_device *pdev) -{ - struct wm831x_ldo *ldo = platform_get_drvdata(pdev); - - regulator_unregister(ldo->regulator); - - return 0; -} - static struct platform_driver wm831x_alive_ldo_driver = { .probe = wm831x_alive_ldo_probe, - .remove = wm831x_alive_ldo_remove, .driver = { .name = "wm831x-alive-ldo", .owner = THIS_MODULE, -- cgit v0.10.2 From e57e54693371fe1530f376c8a67c33d19bb2a0dc Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 31 Aug 2013 12:00:37 +0100 Subject: regulator: wm8350: Convert to devm_regulator_register() Signed-off-by: Mark Brown diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index 835b5f0..a438e93 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -1206,7 +1206,8 @@ static int wm8350_regulator_probe(struct platform_device *pdev) config.regmap = wm8350->regmap; /* register regulator */ - rdev = regulator_register(&wm8350_reg[pdev->id], &config); + rdev = devm_regulator_register(&pdev->dev, &wm8350_reg[pdev->id], + &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "failed to register %s\n", wm8350_reg[pdev->id].name); @@ -1217,7 +1218,6 @@ static int wm8350_regulator_probe(struct platform_device *pdev) ret = wm8350_register_irq(wm8350, wm8350_reg[pdev->id].irq, pmic_uv_handler, 0, "UV", rdev); if (ret < 0) { - regulator_unregister(rdev); dev_err(&pdev->dev, "failed to register regulator %s IRQ\n", wm8350_reg[pdev->id].name); return ret; @@ -1233,8 +1233,6 @@ static int wm8350_regulator_remove(struct platform_device *pdev) wm8350_free_irq(wm8350, wm8350_reg[pdev->id].irq, rdev); - regulator_unregister(rdev); - return 0; } -- cgit v0.10.2 From eb8b3c8360408b78ca99492f8c1fec080c75dd71 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 31 Aug 2013 12:00:57 +0100 Subject: regulator: wm8400: Convert to devm_regulator_register() Signed-off-by: Mark Brown diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c index 58f51be..870b52f 100644 --- a/drivers/regulator/wm8400-regulator.c +++ b/drivers/regulator/wm8400-regulator.c @@ -219,7 +219,8 @@ static int wm8400_regulator_probe(struct platform_device *pdev) config.driver_data = wm8400; config.regmap = wm8400->regmap; - rdev = regulator_register(®ulators[pdev->id], &config); + rdev = devm_regulator_register(&pdev->dev, ®ulators[pdev->id], + &config); if (IS_ERR(rdev)) return PTR_ERR(rdev); @@ -228,21 +229,11 @@ static int wm8400_regulator_probe(struct platform_device *pdev) return 0; } -static int wm8400_regulator_remove(struct platform_device *pdev) -{ - struct regulator_dev *rdev = platform_get_drvdata(pdev); - - regulator_unregister(rdev); - - return 0; -} - static struct platform_driver wm8400_regulator_driver = { .driver = { .name = "wm8400-regulator", }, .probe = wm8400_regulator_probe, - .remove = wm8400_regulator_remove, }; /** -- cgit v0.10.2 From 9e2bfbbdf203c954a306a5e402a84c24622c55b0 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 31 Aug 2013 12:01:12 +0100 Subject: regulator: wm8994: Convert to devm_regulator_register Signed-off-by: Mark Brown diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c index 5ee2a20..71c5911 100644 --- a/drivers/regulator/wm8994-regulator.c +++ b/drivers/regulator/wm8994-regulator.c @@ -165,7 +165,9 @@ static int wm8994_ldo_probe(struct platform_device *pdev) ldo->init_data = *pdata->ldo[id].init_data; } - ldo->regulator = regulator_register(&wm8994_ldo_desc[id], &config); + ldo->regulator = devm_regulator_register(&pdev->dev, + &wm8994_ldo_desc[id], + &config); if (IS_ERR(ldo->regulator)) { ret = PTR_ERR(ldo->regulator); dev_err(wm8994->dev, "Failed to register LDO%d: %d\n", @@ -181,18 +183,8 @@ err: return ret; } -static int wm8994_ldo_remove(struct platform_device *pdev) -{ - struct wm8994_ldo *ldo = platform_get_drvdata(pdev); - - regulator_unregister(ldo->regulator); - - return 0; -} - static struct platform_driver wm8994_ldo_driver = { .probe = wm8994_ldo_probe, - .remove = wm8994_ldo_remove, .driver = { .name = "wm8994-ldo", .owner = THIS_MODULE, -- cgit v0.10.2 From bcb5fe44875b09756c5e81925de19f9ca5bb9117 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 3 Sep 2013 14:17:36 +0800 Subject: regulator: 88pm8607: Convert to devm_regulator_register Signed-off-by: Axel Lin Signed-off-by: Mark Brown diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c index 7023097..f704d83 100644 --- a/drivers/regulator/88pm8607.c +++ b/drivers/regulator/88pm8607.c @@ -391,7 +391,8 @@ static int pm8607_regulator_probe(struct platform_device *pdev) else config.regmap = chip->regmap_companion; - info->regulator = regulator_register(&info->desc, &config); + info->regulator = devm_regulator_register(&pdev->dev, &info->desc, + &config); if (IS_ERR(info->regulator)) { dev_err(&pdev->dev, "failed to register regulator %s\n", info->desc.name); @@ -402,14 +403,6 @@ static int pm8607_regulator_probe(struct platform_device *pdev) return 0; } -static int pm8607_regulator_remove(struct platform_device *pdev) -{ - struct pm8607_regulator_info *info = platform_get_drvdata(pdev); - - regulator_unregister(info->regulator); - return 0; -} - static struct platform_device_id pm8607_regulator_driver_ids[] = { { .name = "88pm860x-regulator", @@ -428,7 +421,6 @@ static struct platform_driver pm8607_regulator_driver = { .owner = THIS_MODULE, }, .probe = pm8607_regulator_probe, - .remove = pm8607_regulator_remove, .id_table = pm8607_regulator_driver_ids, }; -- cgit v0.10.2 From b1a613d505c53ad4f4af4cf228841a1784a50011 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 3 Sep 2013 14:19:00 +0800 Subject: regulator: aat2870: Convert to devm_regulator_register Signed-off-by: Axel Lin Acked-by: Jinyoung Park Signed-off-by: Mark Brown diff --git a/drivers/regulator/aat2870-regulator.c b/drivers/regulator/aat2870-regulator.c index 881159d..f70a9bf 100644 --- a/drivers/regulator/aat2870-regulator.c +++ b/drivers/regulator/aat2870-regulator.c @@ -176,7 +176,7 @@ static int aat2870_regulator_probe(struct platform_device *pdev) config.driver_data = ri; config.init_data = dev_get_platdata(&pdev->dev); - rdev = regulator_register(&ri->desc, &config); + rdev = devm_regulator_register(&pdev->dev, &ri->desc, &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "Failed to register regulator %s\n", ri->desc.name); @@ -187,21 +187,12 @@ static int aat2870_regulator_probe(struct platform_device *pdev) return 0; } -static int aat2870_regulator_remove(struct platform_device *pdev) -{ - struct regulator_dev *rdev = platform_get_drvdata(pdev); - - regulator_unregister(rdev); - return 0; -} - static struct platform_driver aat2870_regulator_driver = { .driver = { .name = "aat2870-regulator", .owner = THIS_MODULE, }, .probe = aat2870_regulator_probe, - .remove = aat2870_regulator_remove, }; static int __init aat2870_regulator_init(void) -- cgit v0.10.2 From 9b2cdac712448ca182dabeffd36bd532c53f3935 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 3 Sep 2013 14:20:25 +0800 Subject: regulator: ad5398: Convert to devm_regulator_register Signed-off-by: Axel Lin Signed-off-by: Mark Brown diff --git a/drivers/regulator/ad5398.c b/drivers/regulator/ad5398.c index b2b203c..48016a0 100644 --- a/drivers/regulator/ad5398.c +++ b/drivers/regulator/ad5398.c @@ -219,7 +219,6 @@ static int ad5398_probe(struct i2c_client *client, struct ad5398_chip_info *chip; const struct ad5398_current_data_format *df = (struct ad5398_current_data_format *)id->driver_data; - int ret; if (!init_data) return -EINVAL; @@ -240,33 +239,21 @@ static int ad5398_probe(struct i2c_client *client, chip->current_offset = df->current_offset; chip->current_mask = (chip->current_level - 1) << chip->current_offset; - chip->rdev = regulator_register(&ad5398_reg, &config); + chip->rdev = devm_regulator_register(&client->dev, &ad5398_reg, + &config); if (IS_ERR(chip->rdev)) { - ret = PTR_ERR(chip->rdev); dev_err(&client->dev, "failed to register %s %s\n", id->name, ad5398_reg.name); - goto err; + return PTR_ERR(chip->rdev); } i2c_set_clientdata(client, chip); dev_dbg(&client->dev, "%s regulator driver is registered.\n", id->name); return 0; - -err: - return ret; -} - -static int ad5398_remove(struct i2c_client *client) -{ - struct ad5398_chip_info *chip = i2c_get_clientdata(client); - - regulator_unregister(chip->rdev); - return 0; } static struct i2c_driver ad5398_driver = { .probe = ad5398_probe, - .remove = ad5398_remove, .driver = { .name = "ad5398", }, -- cgit v0.10.2 From 27447c934a9e6ea7753f61211ef06327915ed0d2 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 3 Sep 2013 14:22:00 +0800 Subject: regulator: as3711: Convert to devm_regulator_register Signed-off-by: Axel Lin Signed-off-by: Mark Brown diff --git a/drivers/regulator/as3711-regulator.c b/drivers/regulator/as3711-regulator.c index 8406cd7..fb27e6c 100644 --- a/drivers/regulator/as3711-regulator.c +++ b/drivers/regulator/as3711-regulator.c @@ -273,33 +273,16 @@ static int as3711_regulator_probe(struct platform_device *pdev) config.regmap = as3711->regmap; config.of_node = of_node[id]; - rdev = regulator_register(&ri->desc, &config); + rdev = devm_regulator_register(&pdev->dev, &ri->desc, &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "Failed to register regulator %s\n", ri->desc.name); - ret = PTR_ERR(rdev); - goto eregreg; + return PTR_ERR(rdev); } reg->rdev = rdev; } platform_set_drvdata(pdev, regs); return 0; - -eregreg: - while (--id >= 0) - regulator_unregister(regs[id].rdev); - - return ret; -} - -static int as3711_regulator_remove(struct platform_device *pdev) -{ - struct as3711_regulator *regs = platform_get_drvdata(pdev); - int id; - - for (id = 0; id < AS3711_REGULATOR_NUM; ++id) - regulator_unregister(regs[id].rdev); - return 0; } static struct platform_driver as3711_regulator_driver = { @@ -308,7 +291,6 @@ static struct platform_driver as3711_regulator_driver = { .owner = THIS_MODULE, }, .probe = as3711_regulator_probe, - .remove = as3711_regulator_remove, }; static int __init as3711_regulator_init(void) -- cgit v0.10.2 From 4e8d79355da117b4be7ea6f870eabc7f18740f2c Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 3 Sep 2013 14:23:14 +0800 Subject: regulator: da903x: Convert to devm_regulator_register Signed-off-by: Axel Lin Signed-off-by: Mark Brown diff --git a/drivers/regulator/da903x.c b/drivers/regulator/da903x.c index f06854c..c61d96e 100644 --- a/drivers/regulator/da903x.c +++ b/drivers/regulator/da903x.c @@ -463,7 +463,7 @@ static int da903x_regulator_probe(struct platform_device *pdev) config.init_data = dev_get_platdata(&pdev->dev); config.driver_data = ri; - rdev = regulator_register(&ri->desc, &config); + rdev = devm_regulator_register(&pdev->dev, &ri->desc, &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "failed to register regulator %s\n", ri->desc.name); @@ -474,21 +474,12 @@ static int da903x_regulator_probe(struct platform_device *pdev) return 0; } -static int da903x_regulator_remove(struct platform_device *pdev) -{ - struct regulator_dev *rdev = platform_get_drvdata(pdev); - - regulator_unregister(rdev); - return 0; -} - static struct platform_driver da903x_regulator_driver = { .driver = { .name = "da903x-regulator", .owner = THIS_MODULE, }, .probe = da903x_regulator_probe, - .remove = da903x_regulator_remove, }; static int __init da903x_regulator_init(void) -- cgit v0.10.2 From ea49a5ebbbfa982c893d4d6cf68feeb829f58324 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 3 Sep 2013 14:24:46 +0800 Subject: regulator: da9052: Convert to devm_regulator_register Signed-off-by: Axel Lin Signed-off-by: Mark Brown diff --git a/drivers/regulator/da9052-regulator.c b/drivers/regulator/da9052-regulator.c index 1e4d483..c427e42 100644 --- a/drivers/regulator/da9052-regulator.c +++ b/drivers/regulator/da9052-regulator.c @@ -389,8 +389,9 @@ static int da9052_regulator_probe(struct platform_device *pdev) #endif } - regulator->rdev = regulator_register(®ulator->info->reg_desc, - &config); + regulator->rdev = devm_regulator_register(&pdev->dev, + ®ulator->info->reg_desc, + &config); if (IS_ERR(regulator->rdev)) { dev_err(&pdev->dev, "failed to register regulator %s\n", regulator->info->reg_desc.name); @@ -402,17 +403,8 @@ static int da9052_regulator_probe(struct platform_device *pdev) return 0; } -static int da9052_regulator_remove(struct platform_device *pdev) -{ - struct da9052_regulator *regulator = platform_get_drvdata(pdev); - - regulator_unregister(regulator->rdev); - return 0; -} - static struct platform_driver da9052_regulator_driver = { .probe = da9052_regulator_probe, - .remove = da9052_regulator_remove, .driver = { .name = "da9052-regulator", .owner = THIS_MODULE, -- cgit v0.10.2 From 0d3288063b4e45335ea011b89d0850a0fd76096c Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 3 Sep 2013 14:26:10 +0800 Subject: regulator: da9055: Convert to devm_regulator_register Signed-off-by: Axel Lin Signed-off-by: Mark Brown diff --git a/drivers/regulator/da9055-regulator.c b/drivers/regulator/da9055-regulator.c index 77b53e5..7f34020 100644 --- a/drivers/regulator/da9055-regulator.c +++ b/drivers/regulator/da9055-regulator.c @@ -564,13 +564,13 @@ static int da9055_regulator_probe(struct platform_device *pdev) if (ret < 0) return ret; - regulator->rdev = regulator_register(®ulator->info->reg_desc, - &config); + regulator->rdev = devm_regulator_register(&pdev->dev, + ®ulator->info->reg_desc, + &config); if (IS_ERR(regulator->rdev)) { dev_err(&pdev->dev, "Failed to register regulator %s\n", regulator->info->reg_desc.name); - ret = PTR_ERR(regulator->rdev); - return ret; + return PTR_ERR(regulator->rdev); } /* Only LDO 5 and 6 has got the over current interrupt */ @@ -588,7 +588,7 @@ static int da9055_regulator_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to request Regulator IRQ %d: %d\n", irq, ret); - goto err_regulator; + return ret; } } } @@ -596,24 +596,10 @@ static int da9055_regulator_probe(struct platform_device *pdev) platform_set_drvdata(pdev, regulator); return 0; - -err_regulator: - regulator_unregister(regulator->rdev); - return ret; -} - -static int da9055_regulator_remove(struct platform_device *pdev) -{ - struct da9055_regulator *regulator = platform_get_drvdata(pdev); - - regulator_unregister(regulator->rdev); - - return 0; } static struct platform_driver da9055_regulator_driver = { .probe = da9055_regulator_probe, - .remove = da9055_regulator_remove, .driver = { .name = "da9055-regulator", .owner = THIS_MODULE, -- cgit v0.10.2 From b15f5f7603fe9963e2201874f6e6c6cc0410b4d1 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 3 Sep 2013 14:27:56 +0800 Subject: regulator: fan53555: Convert to devm_regulator_register Signed-off-by: Axel Lin Signed-off-by: Mark Brown diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c index 70b7220..7ca3d9e 100644 --- a/drivers/regulator/fan53555.c +++ b/drivers/regulator/fan53555.c @@ -218,9 +218,8 @@ static int fan53555_regulator_register(struct fan53555_device_info *di, rdesc->vsel_mask = VSEL_NSEL_MASK; rdesc->owner = THIS_MODULE; - di->rdev = regulator_register(&di->desc, config); + di->rdev = devm_regulator_register(di->dev, &di->desc, config); return PTR_ERR_OR_ZERO(di->rdev); - } static struct regmap_config fan53555_regmap_config = { @@ -291,14 +290,6 @@ static int fan53555_regulator_probe(struct i2c_client *client, } -static int fan53555_regulator_remove(struct i2c_client *client) -{ - struct fan53555_device_info *di = i2c_get_clientdata(client); - - regulator_unregister(di->rdev); - return 0; -} - static const struct i2c_device_id fan53555_id[] = { {"fan53555", -1}, { }, @@ -309,7 +300,6 @@ static struct i2c_driver fan53555_regulator_driver = { .name = "fan53555-regulator", }, .probe = fan53555_regulator_probe, - .remove = fan53555_regulator_remove, .id_table = fan53555_id, }; -- cgit v0.10.2 From d55cd794d6ae9d0730c0b039c0a2d5d0e5ff12c4 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 4 Sep 2013 12:12:15 +0530 Subject: regulator: s2mps11: Use devm_regulator_register Commit e398b51a ("regulator: s2mps11: Convert to devm_regulator_register()") intended to do this conversion. However the actual conversion to devm_* got missed out. Fix this. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index d7c241e..333677d 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -448,7 +448,8 @@ common_reg: config.of_node = rdata[i].of_node; } - s2mps11->rdev[i] = regulator_register(®ulators[i], &config); + s2mps11->rdev[i] = devm_regulator_register(&pdev->dev, + ®ulators[i], &config); if (IS_ERR(s2mps11->rdev[i])) { ret = PTR_ERR(s2mps11->rdev[i]); dev_err(&pdev->dev, "regulator init failed for %d\n", -- cgit v0.10.2 From 44815b4a619065f510cffa2976722e78381d4fd2 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 4 Sep 2013 11:07:54 +0530 Subject: regulator: max77686: Use devm_regulator_register devm_* simplifies the code. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c index f563057..de5b30e 100644 --- a/drivers/regulator/max77686.c +++ b/drivers/regulator/max77686.c @@ -478,32 +478,17 @@ static int max77686_pmic_probe(struct platform_device *pdev) config.of_node = pdata->regulators[i].of_node; max77686->opmode[i] = regulators[i].enable_mask; - max77686->rdev[i] = regulator_register(®ulators[i], &config); + max77686->rdev[i] = devm_regulator_register(&pdev->dev, + ®ulators[i], &config); if (IS_ERR(max77686->rdev[i])) { - ret = PTR_ERR(max77686->rdev[i]); dev_err(&pdev->dev, "regulator init failed for %d\n", i); max77686->rdev[i] = NULL; - goto err; + return PTR_ERR(max77686->rdev[i]); } } return 0; -err: - while (--i >= 0) - regulator_unregister(max77686->rdev[i]); - return ret; -} - -static int max77686_pmic_remove(struct platform_device *pdev) -{ - struct max77686_data *max77686 = platform_get_drvdata(pdev); - int i; - - for (i = 0; i < MAX77686_REGULATORS; i++) - regulator_unregister(max77686->rdev[i]); - - return 0; } static const struct platform_device_id max77686_pmic_id[] = { @@ -518,7 +503,6 @@ static struct platform_driver max77686_pmic_driver = { .owner = THIS_MODULE, }, .probe = max77686_pmic_probe, - .remove = max77686_pmic_remove, .id_table = max77686_pmic_id, }; -- cgit v0.10.2 From 249cc1f21ee22d9cfead5ae552d91b9669d16c83 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 4 Sep 2013 11:07:55 +0530 Subject: regulator: max1586: Use devm_regulator_register devm_* simplifies the code. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/regulator/max1586.c b/drivers/regulator/max1586.c index 3a599ee..e242dd3 100644 --- a/drivers/regulator/max1586.c +++ b/drivers/regulator/max1586.c @@ -166,7 +166,7 @@ static int max1586_pmic_probe(struct i2c_client *client, struct max1586_platform_data *pdata = dev_get_platdata(&client->dev); struct regulator_config config = { }; struct max1586_data *max1586; - int i, id, ret = -ENOMEM; + int i, id; max1586 = devm_kzalloc(&client->dev, sizeof(struct max1586_data) + sizeof(struct regulator_dev *) * (MAX1586_V6 + 1), @@ -193,7 +193,7 @@ static int max1586_pmic_probe(struct i2c_client *client, continue; if (id < MAX1586_V3 || id > MAX1586_V6) { dev_err(&client->dev, "invalid regulator id %d\n", id); - goto err; + return -EINVAL; } if (id == MAX1586_V3) { @@ -207,33 +207,18 @@ static int max1586_pmic_probe(struct i2c_client *client, config.init_data = pdata->subdevs[i].platform_data; config.driver_data = max1586; - rdev[i] = regulator_register(&max1586_reg[id], &config); + rdev[i] = devm_regulator_register(&client->dev, + &max1586_reg[id], &config); if (IS_ERR(rdev[i])) { - ret = PTR_ERR(rdev[i]); dev_err(&client->dev, "failed to register %s\n", max1586_reg[id].name); - goto err; + return PTR_ERR(rdev[i]); } } i2c_set_clientdata(client, max1586); dev_info(&client->dev, "Maxim 1586 regulator driver loaded\n"); return 0; - -err: - while (--i >= 0) - regulator_unregister(rdev[i]); - return ret; -} - -static int max1586_pmic_remove(struct i2c_client *client) -{ - struct max1586_data *max1586 = i2c_get_clientdata(client); - int i; - - for (i = 0; i <= MAX1586_V6; i++) - regulator_unregister(max1586->rdev[i]); - return 0; } static const struct i2c_device_id max1586_id[] = { @@ -244,7 +229,6 @@ MODULE_DEVICE_TABLE(i2c, max1586_id); static struct i2c_driver max1586_pmic_driver = { .probe = max1586_pmic_probe, - .remove = max1586_pmic_remove, .driver = { .name = "max1586", .owner = THIS_MODULE, -- cgit v0.10.2 From 027a27545cc09dfc392d056437c5eb3f260d7e43 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 4 Sep 2013 11:07:56 +0530 Subject: regulator: max77693: Use devm_regulator_register devm_* simplifies the code. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/regulator/max77693.c b/drivers/regulator/max77693.c index ce4b96c..2054ae1 100644 --- a/drivers/regulator/max77693.c +++ b/drivers/regulator/max77693.c @@ -230,7 +230,7 @@ static int max77693_pmic_probe(struct platform_device *pdev) struct max77693_dev *iodev = dev_get_drvdata(pdev->dev.parent); struct max77693_pmic_dev *max77693_pmic; struct max77693_regulator_data *rdata = NULL; - int num_rdata, i, ret; + int num_rdata, i; struct regulator_config config; num_rdata = max77693_pmic_init_rdata(&pdev->dev, &rdata); @@ -266,36 +266,17 @@ static int max77693_pmic_probe(struct platform_device *pdev) config.init_data = rdata[i].initdata; config.of_node = rdata[i].of_node; - max77693_pmic->rdev[i] = regulator_register(®ulators[id], - &config); + max77693_pmic->rdev[i] = devm_regulator_register(&pdev->dev, + ®ulators[id], &config); if (IS_ERR(max77693_pmic->rdev[i])) { - ret = PTR_ERR(max77693_pmic->rdev[i]); dev_err(max77693_pmic->dev, "Failed to initialize regulator-%d\n", id); max77693_pmic->rdev[i] = NULL; - goto err; + return PTR_ERR(max77693_pmic->rdev[i]); } } return 0; - err: - while (--i >= 0) - regulator_unregister(max77693_pmic->rdev[i]); - - return ret; -} - -static int max77693_pmic_remove(struct platform_device *pdev) -{ - struct max77693_pmic_dev *max77693_pmic = platform_get_drvdata(pdev); - struct regulator_dev **rdev = max77693_pmic->rdev; - int i; - - for (i = 0; i < max77693_pmic->num_regulators; i++) - if (rdev[i]) - regulator_unregister(rdev[i]); - - return 0; } static const struct platform_device_id max77693_pmic_id[] = { @@ -311,7 +292,6 @@ static struct platform_driver max77693_pmic_driver = { .owner = THIS_MODULE, }, .probe = max77693_pmic_probe, - .remove = max77693_pmic_remove, .id_table = max77693_pmic_id, }; -- cgit v0.10.2 From e453f92eec6d72db0d7fe6bab2e0a710dc6cd927 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 4 Sep 2013 11:07:57 +0530 Subject: regulator: max8649: Use devm_regulator_register devm_* simplifies the code. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/regulator/max8649.c b/drivers/regulator/max8649.c index 19c6f08..7f049c9 100644 --- a/drivers/regulator/max8649.c +++ b/drivers/regulator/max8649.c @@ -234,7 +234,8 @@ static int max8649_regulator_probe(struct i2c_client *client, config.driver_data = info; config.regmap = info->regmap; - info->regulator = regulator_register(&dcdc_desc, &config); + info->regulator = devm_regulator_register(&client->dev, &dcdc_desc, + &config); if (IS_ERR(info->regulator)) { dev_err(info->dev, "failed to register regulator %s\n", dcdc_desc.name); @@ -244,16 +245,6 @@ static int max8649_regulator_probe(struct i2c_client *client, return 0; } -static int max8649_regulator_remove(struct i2c_client *client) -{ - struct max8649_regulator_info *info = i2c_get_clientdata(client); - - if (info) - regulator_unregister(info->regulator); - - return 0; -} - static const struct i2c_device_id max8649_id[] = { { "max8649", 0 }, { } @@ -262,7 +253,6 @@ MODULE_DEVICE_TABLE(i2c, max8649_id); static struct i2c_driver max8649_driver = { .probe = max8649_regulator_probe, - .remove = max8649_regulator_remove, .driver = { .name = "max8649", }, -- cgit v0.10.2 From dcfb6b56f18b5d9eff9b784d4e5b4a085f149de2 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 4 Sep 2013 11:07:58 +0530 Subject: regulator: max8660: Use devm_regulator_register devm_* simplifies the code. [Fixups from rebase onto v3.12 code -- broonie] Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/regulator/max8660.c b/drivers/regulator/max8660.c index 144bcac..8d94d3d 100644 --- a/drivers/regulator/max8660.c +++ b/drivers/regulator/max8660.c @@ -439,7 +439,7 @@ static int max8660_probe(struct i2c_client *client, for (i = 0; i < pdata->num_subdevs; i++) { if (!pdata->subdevs[i].platform_data) - goto err_out; + return ret; boot_on = pdata->subdevs[i].platform_data->constraints.boot_on; @@ -465,7 +465,7 @@ static int max8660_probe(struct i2c_client *client, case MAX8660_V7: if (type == MAX8661) { dev_err(dev, "Regulator not on this chip!\n"); - goto err_out; + return -EINVAL; } if (boot_on) @@ -475,7 +475,7 @@ static int max8660_probe(struct i2c_client *client, default: dev_err(dev, "invalid regulator %s\n", pdata->subdevs[i].name); - goto err_out; + return ret; } } @@ -489,33 +489,18 @@ static int max8660_probe(struct i2c_client *client, config.of_node = of_node[i]; config.driver_data = max8660; - rdev[i] = regulator_register(&max8660_reg[id], &config); + rdev[i] = devm_regulator_register(&client->dev, + &max8660_reg[id], &config); if (IS_ERR(rdev[i])) { ret = PTR_ERR(rdev[i]); - dev_err(dev, "failed to register %s\n", + dev_err(&client->dev, "failed to register %s\n", max8660_reg[id].name); - goto err_unregister; + return PTR_ERR(rdev[i]); } } i2c_set_clientdata(client, max8660); return 0; - -err_unregister: - while (--i >= 0) - regulator_unregister(rdev[i]); -err_out: - return ret; -} - -static int max8660_remove(struct i2c_client *client) -{ - struct max8660 *max8660 = i2c_get_clientdata(client); - int i; - - for (i = 0; i < MAX8660_V_END; i++) - regulator_unregister(max8660->rdev[i]); - return 0; } static const struct i2c_device_id max8660_id[] = { @@ -527,7 +512,6 @@ MODULE_DEVICE_TABLE(i2c, max8660_id); static struct i2c_driver max8660_driver = { .probe = max8660_probe, - .remove = max8660_remove, .driver = { .name = "max8660", .owner = THIS_MODULE, -- cgit v0.10.2 From 5ecdf140f21c50c08be34a83b44f28722813366c Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 4 Sep 2013 11:07:59 +0530 Subject: regulator: max8907-regulator: Use devm_regulator_register devm_* simplifies the code. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/regulator/max8907-regulator.c b/drivers/regulator/max8907-regulator.c index 4568c15..0c5fe6c 100644 --- a/drivers/regulator/max8907-regulator.c +++ b/drivers/regulator/max8907-regulator.c @@ -350,33 +350,17 @@ static int max8907_regulator_probe(struct platform_device *pdev) pmic->desc[i].ops = &max8907_out5v_hwctl_ops; } - pmic->rdev[i] = regulator_register(&pmic->desc[i], &config); + pmic->rdev[i] = devm_regulator_register(&pdev->dev, + &pmic->desc[i], &config); if (IS_ERR(pmic->rdev[i])) { dev_err(&pdev->dev, "failed to register %s regulator\n", pmic->desc[i].name); - ret = PTR_ERR(pmic->rdev[i]); - goto err_unregister_regulator; + return PTR_ERR(pmic->rdev[i]); } } return 0; - -err_unregister_regulator: - while (--i >= 0) - regulator_unregister(pmic->rdev[i]); - return ret; -} - -static int max8907_regulator_remove(struct platform_device *pdev) -{ - struct max8907_regulator *pmic = platform_get_drvdata(pdev); - int i; - - for (i = 0; i < MAX8907_NUM_REGULATORS; i++) - regulator_unregister(pmic->rdev[i]); - - return 0; } static struct platform_driver max8907_regulator_driver = { @@ -385,7 +369,6 @@ static struct platform_driver max8907_regulator_driver = { .owner = THIS_MODULE, }, .probe = max8907_regulator_probe, - .remove = max8907_regulator_remove, }; static int __init max8907_regulator_init(void) -- cgit v0.10.2 From 8d581fd01c922f9df586074078d70027156cfff8 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 4 Sep 2013 11:08:00 +0530 Subject: regulator: max8973-regulator: Use devm_regulator_register devm_* simplifies the code. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/regulator/max8973-regulator.c b/drivers/regulator/max8973-regulator.c index 5b77ab7..892aa1e 100644 --- a/drivers/regulator/max8973-regulator.c +++ b/drivers/regulator/max8973-regulator.c @@ -467,7 +467,7 @@ static int max8973_probe(struct i2c_client *client, config.regmap = max->regmap; /* Register the regulators */ - rdev = regulator_register(&max->desc, &config); + rdev = devm_regulator_register(&client->dev, &max->desc, &config); if (IS_ERR(rdev)) { ret = PTR_ERR(rdev); dev_err(max->dev, "regulator register failed, err %d\n", ret); @@ -478,14 +478,6 @@ static int max8973_probe(struct i2c_client *client, return 0; } -static int max8973_remove(struct i2c_client *client) -{ - struct max8973_chip *max = i2c_get_clientdata(client); - - regulator_unregister(max->rdev); - return 0; -} - static const struct i2c_device_id max8973_id[] = { {.name = "max8973",}, {}, @@ -499,7 +491,6 @@ static struct i2c_driver max8973_i2c_driver = { .owner = THIS_MODULE, }, .probe = max8973_probe, - .remove = max8973_remove, .id_table = max8973_id, }; -- cgit v0.10.2 From 4177f95b8433f809228ab485e3303ae4708c98ca Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 4 Sep 2013 11:08:01 +0530 Subject: regulator: max8997: Use devm_regulator_register devm_* simplifies the code. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/regulator/max8997.c b/drivers/regulator/max8997.c index df20069..059e8ed 100644 --- a/drivers/regulator/max8997.c +++ b/drivers/regulator/max8997.c @@ -1081,7 +1081,7 @@ static int max8997_pmic_probe(struct platform_device *pdev) pdata->buck1_voltage[i] + buck1245_voltage_map_desc.step); if (ret < 0) - goto err_out; + return ret; max8997->buck2_vol[i] = ret = max8997_get_voltage_proper_val( @@ -1090,7 +1090,7 @@ static int max8997_pmic_probe(struct platform_device *pdev) pdata->buck2_voltage[i] + buck1245_voltage_map_desc.step); if (ret < 0) - goto err_out; + return ret; max8997->buck5_vol[i] = ret = max8997_get_voltage_proper_val( @@ -1099,7 +1099,7 @@ static int max8997_pmic_probe(struct platform_device *pdev) pdata->buck5_voltage[i] + buck1245_voltage_map_desc.step); if (ret < 0) - goto err_out; + return ret; if (max_buck1 < max8997->buck1_vol[i]) max_buck1 = max8997->buck1_vol[i]; @@ -1143,24 +1143,23 @@ static int max8997_pmic_probe(struct platform_device *pdev) !gpio_is_valid(pdata->buck125_gpios[1]) || !gpio_is_valid(pdata->buck125_gpios[2])) { dev_err(&pdev->dev, "GPIO NOT VALID\n"); - ret = -EINVAL; - goto err_out; + return -EINVAL; } ret = devm_gpio_request(&pdev->dev, pdata->buck125_gpios[0], "MAX8997 SET1"); if (ret) - goto err_out; + return ret; ret = devm_gpio_request(&pdev->dev, pdata->buck125_gpios[1], "MAX8997 SET2"); if (ret) - goto err_out; + return ret; ret = devm_gpio_request(&pdev->dev, pdata->buck125_gpios[2], "MAX8997 SET3"); if (ret) - goto err_out; + return ret; gpio_direction_output(pdata->buck125_gpios[0], (max8997->buck125_gpioindex >> 2) @@ -1205,33 +1204,17 @@ static int max8997_pmic_probe(struct platform_device *pdev) config.driver_data = max8997; config.of_node = pdata->regulators[i].reg_node; - rdev[i] = regulator_register(®ulators[id], &config); + rdev[i] = devm_regulator_register(&pdev->dev, ®ulators[id], + &config); if (IS_ERR(rdev[i])) { - ret = PTR_ERR(rdev[i]); dev_err(max8997->dev, "regulator init failed for %d\n", id); rdev[i] = NULL; - goto err; + return PTR_ERR(rdev[i]); } } return 0; -err: - while (--i >= 0) - regulator_unregister(rdev[i]); -err_out: - return ret; -} - -static int max8997_pmic_remove(struct platform_device *pdev) -{ - struct max8997_data *max8997 = platform_get_drvdata(pdev); - struct regulator_dev **rdev = max8997->rdev; - int i; - - for (i = 0; i < max8997->num_regulators; i++) - regulator_unregister(rdev[i]); - return 0; } static const struct platform_device_id max8997_pmic_id[] = { @@ -1246,7 +1229,6 @@ static struct platform_driver max8997_pmic_driver = { .owner = THIS_MODULE, }, .probe = max8997_pmic_probe, - .remove = max8997_pmic_remove, .id_table = max8997_pmic_id, }; -- cgit v0.10.2 From 8d491bf42e01e8cc7597ee55cdeab20dbc75ec97 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 4 Sep 2013 11:08:02 +0530 Subject: regulator: max8998: Use devm_regulator_register devm_* simplifies the code. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c index a4c53b2..ae3f065 100644 --- a/drivers/regulator/max8998.c +++ b/drivers/regulator/max8998.c @@ -790,16 +790,14 @@ static int max8998_pmic_probe(struct platform_device *pdev) dev_err(&pdev->dev, "MAX8998 SET1 GPIO defined as 0 !\n"); WARN_ON(!pdata->buck1_set1); - ret = -EIO; - goto err_out; + return -EIO; } /* Check if SET2 is not equal to 0 */ if (!pdata->buck1_set2) { dev_err(&pdev->dev, "MAX8998 SET2 GPIO defined as 0 !\n"); WARN_ON(!pdata->buck1_set2); - ret = -EIO; - goto err_out; + return -EIO; } gpio_request(pdata->buck1_set1, "MAX8998 BUCK1_SET1"); @@ -823,7 +821,7 @@ static int max8998_pmic_probe(struct platform_device *pdev) ret = max8998_write_reg(i2c, MAX8998_REG_BUCK1_VOLTAGE1 + v, i); if (ret) - goto err_out; + return ret; } } @@ -833,8 +831,7 @@ static int max8998_pmic_probe(struct platform_device *pdev) dev_err(&pdev->dev, "MAX8998 SET3 GPIO defined as 0 !\n"); WARN_ON(!pdata->buck2_set3); - ret = -EIO; - goto err_out; + return -EIO; } gpio_request(pdata->buck2_set3, "MAX8998 BUCK2_SET3"); gpio_direction_output(pdata->buck2_set3, @@ -852,7 +849,7 @@ static int max8998_pmic_probe(struct platform_device *pdev) ret = max8998_write_reg(i2c, MAX8998_REG_BUCK2_VOLTAGE1 + v, i); if (ret) - goto err_out; + return ret; } } @@ -875,34 +872,19 @@ static int max8998_pmic_probe(struct platform_device *pdev) config.init_data = pdata->regulators[i].initdata; config.driver_data = max8998; - rdev[i] = regulator_register(®ulators[index], &config); + rdev[i] = devm_regulator_register(&pdev->dev, + ®ulators[index], &config); if (IS_ERR(rdev[i])) { ret = PTR_ERR(rdev[i]); dev_err(max8998->dev, "regulator %s init failed (%d)\n", regulators[index].name, ret); rdev[i] = NULL; - goto err; + return ret; } } return 0; -err: - while (--i >= 0) - regulator_unregister(rdev[i]); -err_out: - return ret; -} - -static int max8998_pmic_remove(struct platform_device *pdev) -{ - struct max8998_data *max8998 = platform_get_drvdata(pdev); - struct regulator_dev **rdev = max8998->rdev; - int i; - - for (i = 0; i < max8998->num_regulators; i++) - regulator_unregister(rdev[i]); - return 0; } static const struct platform_device_id max8998_pmic_id[] = { @@ -918,7 +900,6 @@ static struct platform_driver max8998_pmic_driver = { .owner = THIS_MODULE, }, .probe = max8998_pmic_probe, - .remove = max8998_pmic_remove, .id_table = max8998_pmic_id, }; -- cgit v0.10.2 From be1221e893775d8447623475a01f877b902fc8b4 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 4 Sep 2013 12:00:57 +0530 Subject: regulator: anatop-regulator: Use devm_regulator_register devm_* simplifies the code. Signed-off-by: Sachin Kamat Acked-by: Marek Vasut Signed-off-by: Mark Brown diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c index 0d4a8cc..e42bfd1 100644 --- a/drivers/regulator/anatop-regulator.c +++ b/drivers/regulator/anatop-regulator.c @@ -200,7 +200,7 @@ static int anatop_regulator_probe(struct platform_device *pdev) config.regmap = sreg->anatop; /* register regulator */ - rdev = regulator_register(rdesc, &config); + rdev = devm_regulator_register(dev, rdesc, &config); if (IS_ERR(rdev)) { dev_err(dev, "failed to register %s\n", rdesc->name); @@ -223,7 +223,6 @@ static int anatop_regulator_remove(struct platform_device *pdev) struct anatop_regulator *sreg = rdev_get_drvdata(rdev); const char *name = sreg->name; - regulator_unregister(rdev); kfree(name); return 0; -- cgit v0.10.2 From 0ab6e8ca54d22f986a488bbc493e01f1394a3b2b Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 4 Sep 2013 12:00:58 +0530 Subject: regulator: isl6271a-regulator: Use devm_regulator_register devm_* simplifies the code. Signed-off-by: Sachin Kamat Acked-by: Marek Vasut Signed-off-by: Mark Brown diff --git a/drivers/regulator/isl6271a-regulator.c b/drivers/regulator/isl6271a-regulator.c index 88c1a3a..6e5da95 100644 --- a/drivers/regulator/isl6271a-regulator.c +++ b/drivers/regulator/isl6271a-regulator.c @@ -112,7 +112,7 @@ static int isl6271a_probe(struct i2c_client *i2c, struct regulator_config config = { }; struct regulator_init_data *init_data = dev_get_platdata(&i2c->dev); struct isl_pmic *pmic; - int err, i; + int i; if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; @@ -133,32 +133,17 @@ static int isl6271a_probe(struct i2c_client *i2c, config.init_data = NULL; config.driver_data = pmic; - pmic->rdev[i] = regulator_register(&isl_rd[i], &config); + pmic->rdev[i] = devm_regulator_register(&i2c->dev, &isl_rd[i], + &config); if (IS_ERR(pmic->rdev[i])) { dev_err(&i2c->dev, "failed to register %s\n", id->name); - err = PTR_ERR(pmic->rdev[i]); - goto error; + return PTR_ERR(pmic->rdev[i]); } } i2c_set_clientdata(i2c, pmic); return 0; - -error: - while (--i >= 0) - regulator_unregister(pmic->rdev[i]); - return err; -} - -static int isl6271a_remove(struct i2c_client *i2c) -{ - struct isl_pmic *pmic = i2c_get_clientdata(i2c); - int i; - - for (i = 0; i < 3; i++) - regulator_unregister(pmic->rdev[i]); - return 0; } static const struct i2c_device_id isl6271a_id[] = { @@ -174,7 +159,6 @@ static struct i2c_driver isl6271a_i2c_driver = { .owner = THIS_MODULE, }, .probe = isl6271a_probe, - .remove = isl6271a_remove, .id_table = isl6271a_id, }; -- cgit v0.10.2 From 8e568635afcce245d771c2fc527f3902b6d2e723 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 4 Sep 2013 12:00:59 +0530 Subject: regulator: mc13783: Use devm_regulator_register devm_* simplifies the code. Signed-off-by: Sachin Kamat Acked-by: Marek Vasut Signed-off-by: Mark Brown diff --git a/drivers/regulator/mc13783-regulator.c b/drivers/regulator/mc13783-regulator.c index 5ff99d2..f036b26 100644 --- a/drivers/regulator/mc13783-regulator.c +++ b/drivers/regulator/mc13783-regulator.c @@ -400,7 +400,7 @@ static int mc13783_regulator_probe(struct platform_device *pdev) dev_get_platdata(&pdev->dev); struct mc13xxx_regulator_init_data *mc13xxx_data; struct regulator_config config = { }; - int i, ret, num_regulators; + int i, num_regulators; num_regulators = mc13xxx_get_num_regulators_dt(pdev); @@ -444,32 +444,16 @@ static int mc13783_regulator_probe(struct platform_device *pdev) config.driver_data = priv; config.of_node = node; - priv->regulators[i] = regulator_register(desc, &config); + priv->regulators[i] = devm_regulator_register(&pdev->dev, desc, + &config); if (IS_ERR(priv->regulators[i])) { dev_err(&pdev->dev, "failed to register regulator %s\n", mc13783_regulators[i].desc.name); - ret = PTR_ERR(priv->regulators[i]); - goto err; + return PTR_ERR(priv->regulators[i]); } } return 0; -err: - while (--i >= 0) - regulator_unregister(priv->regulators[i]); - - return ret; -} - -static int mc13783_regulator_remove(struct platform_device *pdev) -{ - struct mc13xxx_regulator_priv *priv = platform_get_drvdata(pdev); - int i; - - for (i = 0; i < priv->num_regulators; i++) - regulator_unregister(priv->regulators[i]); - - return 0; } static struct platform_driver mc13783_regulator_driver = { @@ -477,7 +461,6 @@ static struct platform_driver mc13783_regulator_driver = { .name = "mc13783-regulator", .owner = THIS_MODULE, }, - .remove = mc13783_regulator_remove, .probe = mc13783_regulator_probe, }; -- cgit v0.10.2 From 8c0b4ab5024c7b7d3498957cdbe4fea151142f7a Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 4 Sep 2013 12:01:00 +0530 Subject: regulator: mc13892: Use devm_regulator_register devm_* simplifies the code. Signed-off-by: Sachin Kamat Acked-by: Marek Vasut Signed-off-by: Mark Brown diff --git a/drivers/regulator/mc13892-regulator.c b/drivers/regulator/mc13892-regulator.c index 1037e07..96c9f80 100644 --- a/drivers/regulator/mc13892-regulator.c +++ b/drivers/regulator/mc13892-regulator.c @@ -611,43 +611,27 @@ static int mc13892_regulator_probe(struct platform_device *pdev) config.driver_data = priv; config.of_node = node; - priv->regulators[i] = regulator_register(desc, &config); + priv->regulators[i] = devm_regulator_register(&pdev->dev, desc, + &config); if (IS_ERR(priv->regulators[i])) { dev_err(&pdev->dev, "failed to register regulator %s\n", mc13892_regulators[i].desc.name); - ret = PTR_ERR(priv->regulators[i]); - goto err; + return PTR_ERR(priv->regulators[i]); } } return 0; -err: - while (--i >= 0) - regulator_unregister(priv->regulators[i]); - return ret; err_unlock: mc13xxx_unlock(mc13892); return ret; } -static int mc13892_regulator_remove(struct platform_device *pdev) -{ - struct mc13xxx_regulator_priv *priv = platform_get_drvdata(pdev); - int i; - - for (i = 0; i < priv->num_regulators; i++) - regulator_unregister(priv->regulators[i]); - - return 0; -} - static struct platform_driver mc13892_regulator_driver = { .driver = { .name = "mc13892-regulator", .owner = THIS_MODULE, }, - .remove = mc13892_regulator_remove, .probe = mc13892_regulator_probe, }; -- cgit v0.10.2 From 51c86b3eb80ebe0a35aba5ba3e96b22d61043ad1 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 4 Sep 2013 12:01:01 +0530 Subject: regulator: palmas: Use devm_regulator_register devm_* simplifies the code. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c index 488dfe7..6c9670f 100644 --- a/drivers/regulator/palmas-regulator.c +++ b/drivers/regulator/palmas-regulator.c @@ -862,7 +862,7 @@ static int palmas_regulators_probe(struct platform_device *pdev) if (ret < 0) { dev_err(&pdev->dev, "reading TSTEP reg failed: %d\n", ret); - goto err_unregister_regulator; + return ret; } pmic->desc[id].ramp_delay = palmas_smps_ramp_delay[reg & 0x3]; @@ -874,7 +874,7 @@ static int palmas_regulators_probe(struct platform_device *pdev) reg_init = pdata->reg_init[id]; ret = palmas_smps_init(palmas, id, reg_init); if (ret) - goto err_unregister_regulator; + return ret; } /* Register the regulators */ @@ -915,7 +915,7 @@ static int palmas_regulators_probe(struct platform_device *pdev) ret = palmas_smps_read(pmic->palmas, addr, ®); if (ret) - goto err_unregister_regulator; + return ret; if (reg & PALMAS_SMPS12_VOLTAGE_RANGE) pmic->range[id] = 1; @@ -931,7 +931,7 @@ static int palmas_regulators_probe(struct platform_device *pdev) addr = palmas_regs_info[id].ctrl_addr; ret = palmas_smps_read(pmic->palmas, addr, ®); if (ret) - goto err_unregister_regulator; + return ret; pmic->current_reg_mode[id] = reg & PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK; } @@ -947,13 +947,13 @@ static int palmas_regulators_probe(struct platform_device *pdev) pmic->desc[id].supply_name = palmas_regs_info[id].sname; config.of_node = palmas_matches[id].of_node; - rdev = regulator_register(&pmic->desc[id], &config); + rdev = devm_regulator_register(&pdev->dev, &pmic->desc[id], + &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "failed to register %s regulator\n", pdev->name); - ret = PTR_ERR(rdev); - goto err_unregister_regulator; + return PTR_ERR(rdev); } /* Save regulator for cleanup */ @@ -1015,13 +1015,13 @@ static int palmas_regulators_probe(struct platform_device *pdev) pmic->desc[id].supply_name = palmas_regs_info[id].sname; config.of_node = palmas_matches[id].of_node; - rdev = regulator_register(&pmic->desc[id], &config); + rdev = devm_regulator_register(&pdev->dev, &pmic->desc[id], + &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "failed to register %s regulator\n", pdev->name); - ret = PTR_ERR(rdev); - goto err_unregister_regulator; + return PTR_ERR(rdev); } /* Save regulator for cleanup */ @@ -1039,7 +1039,7 @@ static int palmas_regulators_probe(struct platform_device *pdev) id, reg_init); if (ret) { regulator_unregister(pmic->rdev[id]); - goto err_unregister_regulator; + return ret; } } } @@ -1047,21 +1047,6 @@ static int palmas_regulators_probe(struct platform_device *pdev) return 0; - -err_unregister_regulator: - while (--id >= 0) - regulator_unregister(pmic->rdev[id]); - return ret; -} - -static int palmas_regulators_remove(struct platform_device *pdev) -{ - struct palmas_pmic *pmic = platform_get_drvdata(pdev); - int id; - - for (id = 0; id < PALMAS_NUM_REGS; id++) - regulator_unregister(pmic->rdev[id]); - return 0; } static struct of_device_id of_palmas_match_tbl[] = { @@ -1083,7 +1068,6 @@ static struct platform_driver palmas_driver = { .owner = THIS_MODULE, }, .probe = palmas_regulators_probe, - .remove = palmas_regulators_remove, }; static int __init palmas_init(void) -- cgit v0.10.2 From 1377530910da1502b07d09df1a368ce9a246c114 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 4 Sep 2013 12:01:02 +0530 Subject: regulator: rc5t583: Use devm_regulator_register devm_* simplifies the code. Signed-off-by: Sachin Kamat Acked-by: Marek Vasut Signed-off-by: Mark Brown diff --git a/drivers/regulator/rc5t583-regulator.c b/drivers/regulator/rc5t583-regulator.c index 5885b45..b58affb 100644 --- a/drivers/regulator/rc5t583-regulator.c +++ b/drivers/regulator/rc5t583-regulator.c @@ -173,33 +173,16 @@ skip_ext_pwr_config: config.driver_data = reg; config.regmap = rc5t583->regmap; - rdev = regulator_register(&ri->desc, &config); + rdev = devm_regulator_register(&pdev->dev, &ri->desc, &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "Failed to register regulator %s\n", ri->desc.name); - ret = PTR_ERR(rdev); - goto clean_exit; + return PTR_ERR(rdev); } reg->rdev = rdev; } platform_set_drvdata(pdev, regs); return 0; - -clean_exit: - while (--id >= 0) - regulator_unregister(regs[id].rdev); - - return ret; -} - -static int rc5t583_regulator_remove(struct platform_device *pdev) -{ - struct rc5t583_regulator *regs = platform_get_drvdata(pdev); - int id; - - for (id = 0; id < RC5T583_REGULATOR_MAX; ++id) - regulator_unregister(regs[id].rdev); - return 0; } static struct platform_driver rc5t583_regulator_driver = { @@ -208,7 +191,6 @@ static struct platform_driver rc5t583_regulator_driver = { .owner = THIS_MODULE, }, .probe = rc5t583_regulator_probe, - .remove = rc5t583_regulator_remove, }; static int __init rc5t583_regulator_init(void) -- cgit v0.10.2 From 91dfc80d84eee076a835fb34d053618e63667370 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 4 Sep 2013 12:01:03 +0530 Subject: regulator: ti-abb: Use devm_regulator_register devm_* simplifies the code. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/regulator/ti-abb-regulator.c b/drivers/regulator/ti-abb-regulator.c index d8e3e12..8c2a819 100644 --- a/drivers/regulator/ti-abb-regulator.c +++ b/drivers/regulator/ti-abb-regulator.c @@ -696,39 +696,31 @@ static int ti_abb_probe(struct platform_device *pdev) match = of_match_device(ti_abb_of_match, dev); if (!match) { /* We do not expect this to happen */ - ret = -ENODEV; dev_err(dev, "%s: Unable to match device\n", __func__); - goto err; + return -ENODEV; } if (!match->data) { - ret = -EINVAL; dev_err(dev, "%s: Bad data in match\n", __func__); - goto err; + return -EINVAL; } abb = devm_kzalloc(dev, sizeof(struct ti_abb), GFP_KERNEL); - if (!abb) { - dev_err(dev, "%s: Unable to allocate ABB struct\n", __func__); - ret = -ENOMEM; - goto err; - } + if (!abb) + return -ENOMEM; abb->regs = match->data; /* Map ABB resources */ pname = "base-address"; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); abb->base = devm_ioremap_resource(dev, res); - if (IS_ERR(abb->base)) { - ret = PTR_ERR(abb->base); - goto err; - } + if (IS_ERR(abb->base)) + return PTR_ERR(abb->base); pname = "int-address"; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); if (!res) { dev_err(dev, "Missing '%s' IO resource\n", pname); - ret = -ENODEV; - goto err; + return -ENODEV; } /* * We may have shared interrupt register offsets which are @@ -738,8 +730,7 @@ static int ti_abb_probe(struct platform_device *pdev) resource_size(res)); if (!abb->int_base) { dev_err(dev, "Unable to map '%s'\n", pname); - ret = -ENOMEM; - goto err; + return -ENOMEM; } /* Map Optional resources */ @@ -759,17 +750,14 @@ static int ti_abb_probe(struct platform_device *pdev) resource_size(res)); if (!abb->efuse_base) { dev_err(dev, "Unable to map '%s'\n", pname); - ret = -ENOMEM; - goto err; + return -ENOMEM; } pname = "ldo-address"; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); abb->ldo_base = devm_ioremap_resource(dev, res); - if (IS_ERR(abb->ldo_base)) { - ret = PTR_ERR(abb->ldo_base); - goto err; - } + if (IS_ERR(abb->ldo_base)) + return PTR_ERR(abb->ldo_base); /* IF ldo_base is set, the following are mandatory */ pname = "ti,ldovbb-override-mask"; @@ -778,12 +766,11 @@ static int ti_abb_probe(struct platform_device *pdev) &abb->ldovbb_override_mask); if (ret) { dev_err(dev, "Missing '%s' (%d)\n", pname, ret); - goto err; + return ret; } if (!abb->ldovbb_override_mask) { dev_err(dev, "Invalid property:'%s' set as 0!\n", pname); - ret = -EINVAL; - goto err; + return -EINVAL; } pname = "ti,ldovbb-vset-mask"; @@ -792,12 +779,11 @@ static int ti_abb_probe(struct platform_device *pdev) &abb->ldovbb_vset_mask); if (ret) { dev_err(dev, "Missing '%s' (%d)\n", pname, ret); - goto err; + return ret; } if (!abb->ldovbb_vset_mask) { dev_err(dev, "Invalid property:'%s' set as 0!\n", pname); - ret = -EINVAL; - goto err; + return -EINVAL; } skip_opt: @@ -807,31 +793,29 @@ skip_opt: &abb->txdone_mask); if (ret) { dev_err(dev, "Missing '%s' (%d)\n", pname, ret); - goto err; + return ret; } if (!abb->txdone_mask) { dev_err(dev, "Invalid property:'%s' set as 0!\n", pname); - ret = -EINVAL; - goto err; + return -EINVAL; } initdata = of_get_regulator_init_data(dev, pdev->dev.of_node); if (!initdata) { - ret = -ENOMEM; dev_err(dev, "%s: Unable to alloc regulator init data\n", __func__); - goto err; + return -ENOMEM; } /* init ABB opp_sel table */ ret = ti_abb_init_table(dev, abb, initdata); if (ret) - goto err; + return ret; /* init ABB timing */ ret = ti_abb_init_timings(dev, abb); if (ret) - goto err; + return ret; desc = &abb->rdesc; desc->name = dev_name(dev); @@ -849,12 +833,12 @@ skip_opt: config.driver_data = abb; config.of_node = pdev->dev.of_node; - rdev = regulator_register(desc, &config); + rdev = devm_regulator_register(dev, desc, &config); if (IS_ERR(rdev)) { ret = PTR_ERR(rdev); dev_err(dev, "%s: failed to register regulator(%d)\n", __func__, ret); - goto err; + return ret; } platform_set_drvdata(pdev, rdev); @@ -862,31 +846,12 @@ skip_opt: ti_abb_rmw(abb->regs->sr2_en_mask, 1, abb->regs->setup_reg, abb->base); return 0; - -err: - dev_err(dev, "%s: Failed to initialize(%d)\n", __func__, ret); - return ret; -} - -/** - * ti_abb_remove() - cleanups - * @pdev: ABB platform device - * - * Return: 0 - */ -static int ti_abb_remove(struct platform_device *pdev) -{ - struct regulator_dev *rdev = platform_get_drvdata(pdev); - - regulator_unregister(rdev); - return 0; } MODULE_ALIAS("platform:ti_abb"); static struct platform_driver ti_abb_driver = { .probe = ti_abb_probe, - .remove = ti_abb_remove, .driver = { .name = "ti_abb", .owner = THIS_MODULE, -- cgit v0.10.2 From 1084081dc8e5556e91539de082eaae89e39516c9 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 4 Sep 2013 17:17:44 +0530 Subject: regulator: tps51632: Use devm_regulator_register devm_* simplifies the code. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/regulator/tps51632-regulator.c b/drivers/regulator/tps51632-regulator.c index 9392a7c..b0a3f09 100644 --- a/drivers/regulator/tps51632-regulator.c +++ b/drivers/regulator/tps51632-regulator.c @@ -343,7 +343,7 @@ static int tps51632_probe(struct i2c_client *client, config.regmap = tps->regmap; config.of_node = client->dev.of_node; - rdev = regulator_register(&tps->desc, &config); + rdev = devm_regulator_register(&client->dev, &tps->desc, &config); if (IS_ERR(rdev)) { dev_err(tps->dev, "regulator register failed\n"); return PTR_ERR(rdev); @@ -353,14 +353,6 @@ static int tps51632_probe(struct i2c_client *client, return 0; } -static int tps51632_remove(struct i2c_client *client) -{ - struct tps51632_chip *tps = i2c_get_clientdata(client); - - regulator_unregister(tps->rdev); - return 0; -} - static const struct i2c_device_id tps51632_id[] = { {.name = "tps51632",}, {}, @@ -375,7 +367,6 @@ static struct i2c_driver tps51632_i2c_driver = { .of_match_table = of_match_ptr(tps51632_of_match), }, .probe = tps51632_probe, - .remove = tps51632_remove, .id_table = tps51632_id, }; -- cgit v0.10.2 From 58c6e938c00de744d52f739aa426a4b1b13ef22b Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 4 Sep 2013 17:17:45 +0530 Subject: regulator: tps62360: Use devm_regulator_register devm_* simplifies the code. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/regulator/tps62360-regulator.c b/drivers/regulator/tps62360-regulator.c index 0b7ebb1..c2c0185 100644 --- a/drivers/regulator/tps62360-regulator.c +++ b/drivers/regulator/tps62360-regulator.c @@ -476,7 +476,7 @@ static int tps62360_probe(struct i2c_client *client, config.of_node = client->dev.of_node; /* Register the regulators */ - rdev = regulator_register(&tps->desc, &config); + rdev = devm_regulator_register(&client->dev, &tps->desc, &config); if (IS_ERR(rdev)) { dev_err(tps->dev, "%s(): regulator register failed with err %s\n", @@ -488,20 +488,6 @@ static int tps62360_probe(struct i2c_client *client, return 0; } -/** - * tps62360_remove - tps62360 driver i2c remove handler - * @client: i2c driver client device structure - * - * Unregister TPS driver as an i2c client device driver - */ -static int tps62360_remove(struct i2c_client *client) -{ - struct tps62360_chip *tps = i2c_get_clientdata(client); - - regulator_unregister(tps->rdev); - return 0; -} - static void tps62360_shutdown(struct i2c_client *client) { struct tps62360_chip *tps = i2c_get_clientdata(client); @@ -535,7 +521,6 @@ static struct i2c_driver tps62360_i2c_driver = { .of_match_table = of_match_ptr(tps62360_of_match), }, .probe = tps62360_probe, - .remove = tps62360_remove, .shutdown = tps62360_shutdown, .id_table = tps62360_id, }; -- cgit v0.10.2 From 9c7c9eae5f7ac57465c0ac6adc8e3ad3afad2544 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 4 Sep 2013 17:17:46 +0530 Subject: regulator: tps65023: Use devm_regulator_register devm_* simplifies the code. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index a15263d..a957579 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -277,12 +277,12 @@ static int tps_65023_probe(struct i2c_client *client, config.regmap = tps->regmap; /* Register the regulators */ - rdev = regulator_register(&tps->desc[i], &config); + rdev = devm_regulator_register(&client->dev, &tps->desc[i], + &config); if (IS_ERR(rdev)) { dev_err(&client->dev, "failed to register %s\n", id->name); - error = PTR_ERR(rdev); - goto fail; + return PTR_ERR(rdev); } /* Save regulator for cleanup */ @@ -296,21 +296,6 @@ static int tps_65023_probe(struct i2c_client *client, TPS65023_REG_CTRL2_CORE_ADJ, TPS65023_REG_CTRL2_CORE_ADJ); return 0; - - fail: - while (--i >= 0) - regulator_unregister(tps->rdev[i]); - return error; -} - -static int tps_65023_remove(struct i2c_client *client) -{ - struct tps_pmic *tps = i2c_get_clientdata(client); - int i; - - for (i = 0; i < TPS65023_NUM_REGULATOR; i++) - regulator_unregister(tps->rdev[i]); - return 0; } static const struct tps_info tps65020_regs[] = { @@ -430,7 +415,6 @@ static struct i2c_driver tps_65023_i2c_driver = { .owner = THIS_MODULE, }, .probe = tps_65023_probe, - .remove = tps_65023_remove, .id_table = tps_65023_id, }; -- cgit v0.10.2 From 71b710e705b3b152f0e5682aa179a8c2d2cbb4cd Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 4 Sep 2013 17:17:47 +0530 Subject: regulator: tps6507x: Use devm_regulator_register devm_* simplifies the code. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c index 4117ff5..162a0fa 100644 --- a/drivers/regulator/tps6507x-regulator.c +++ b/drivers/regulator/tps6507x-regulator.c @@ -508,13 +508,13 @@ static int tps6507x_pmic_probe(struct platform_device *pdev) config.of_node = tps6507x_reg_matches[i].of_node; } - rdev = regulator_register(&tps->desc[i], &config); + rdev = devm_regulator_register(&pdev->dev, &tps->desc[i], + &config); if (IS_ERR(rdev)) { dev_err(tps6507x_dev->dev, "failed to register %s regulator\n", pdev->name); - error = PTR_ERR(rdev); - goto fail; + return PTR_ERR(rdev); } /* Save regulator for cleanup */ @@ -525,22 +525,6 @@ static int tps6507x_pmic_probe(struct platform_device *pdev) platform_set_drvdata(pdev, tps6507x_dev); return 0; - -fail: - while (--i >= 0) - regulator_unregister(tps->rdev[i]); - return error; -} - -static int tps6507x_pmic_remove(struct platform_device *pdev) -{ - struct tps6507x_dev *tps6507x_dev = platform_get_drvdata(pdev); - struct tps6507x_pmic *tps = tps6507x_dev->pmic; - int i; - - for (i = 0; i < TPS6507X_NUM_REGULATOR; i++) - regulator_unregister(tps->rdev[i]); - return 0; } static struct platform_driver tps6507x_pmic_driver = { @@ -549,7 +533,6 @@ static struct platform_driver tps6507x_pmic_driver = { .owner = THIS_MODULE, }, .probe = tps6507x_pmic_probe, - .remove = tps6507x_pmic_remove, }; static int __init tps6507x_pmic_init(void) -- cgit v0.10.2 From 9738efae695733a9d367bd083e6ae0474dc5bd3c Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 4 Sep 2013 17:17:48 +0530 Subject: regulator: tps65090: Use devm_regulator_register devm_* simplifies the code. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c index c8e7045..bd611cdf 100644 --- a/drivers/regulator/tps65090-regulator.c +++ b/drivers/regulator/tps65090-regulator.c @@ -279,7 +279,7 @@ static int tps65090_regulator_probe(struct platform_device *pdev) if (ret < 0) { dev_err(&pdev->dev, "failed disable ext control\n"); - goto scrub; + return ret; } } } @@ -296,12 +296,11 @@ static int tps65090_regulator_probe(struct platform_device *pdev) else config.of_node = NULL; - rdev = regulator_register(ri->desc, &config); + rdev = devm_regulator_register(&pdev->dev, ri->desc, &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "failed to register regulator %s\n", ri->desc->name); - ret = PTR_ERR(rdev); - goto scrub; + return PTR_ERR(rdev); } ri->rdev = rdev; @@ -309,36 +308,13 @@ static int tps65090_regulator_probe(struct platform_device *pdev) if (tps_pdata && is_dcdc(num) && tps_pdata->reg_init_data && tps_pdata->enable_ext_control) { ret = tps65090_config_ext_control(ri, true); - if (ret < 0) { - /* Increment num to get unregister rdev */ - num++; - goto scrub; - } + if (ret < 0) + return ret; } } platform_set_drvdata(pdev, pmic); return 0; - -scrub: - while (--num >= 0) { - ri = &pmic[num]; - regulator_unregister(ri->rdev); - } - return ret; -} - -static int tps65090_regulator_remove(struct platform_device *pdev) -{ - struct tps65090_regulator *pmic = platform_get_drvdata(pdev); - struct tps65090_regulator *ri; - int num; - - for (num = 0; num < TPS65090_REGULATOR_MAX; ++num) { - ri = &pmic[num]; - regulator_unregister(ri->rdev); - } - return 0; } static struct platform_driver tps65090_regulator_driver = { @@ -347,7 +323,6 @@ static struct platform_driver tps65090_regulator_driver = { .owner = THIS_MODULE, }, .probe = tps65090_regulator_probe, - .remove = tps65090_regulator_remove, }; static int __init tps65090_regulator_init(void) -- cgit v0.10.2 From 4aac198ddcfe38c7cf10cb2d23497b4a5ad24fdf Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 4 Sep 2013 17:17:49 +0530 Subject: regulator: tps65217: Use devm_regulator_register devm_* simplifies the code. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index 90861d6..8860379 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -233,7 +233,7 @@ static int tps65217_regulator_probe(struct platform_device *pdev) struct regulator_init_data *reg_data; struct regulator_dev *rdev; struct regulator_config config = { }; - int i, ret; + int i; if (tps->dev->of_node) pdata = tps65217_parse_dt(pdev); @@ -269,35 +269,18 @@ static int tps65217_regulator_probe(struct platform_device *pdev) if (tps->dev->of_node) config.of_node = pdata->of_node[i]; - rdev = regulator_register(®ulators[i], &config); + rdev = devm_regulator_register(&pdev->dev, ®ulators[i], + &config); if (IS_ERR(rdev)) { dev_err(tps->dev, "failed to register %s regulator\n", pdev->name); - ret = PTR_ERR(rdev); - goto err_unregister_regulator; + return PTR_ERR(rdev); } /* Save regulator for cleanup */ tps->rdev[i] = rdev; } return 0; - -err_unregister_regulator: - while (--i >= 0) - regulator_unregister(tps->rdev[i]); - - return ret; -} - -static int tps65217_regulator_remove(struct platform_device *pdev) -{ - struct tps65217 *tps = platform_get_drvdata(pdev); - unsigned int i; - - for (i = 0; i < TPS65217_NUM_REGULATOR; i++) - regulator_unregister(tps->rdev[i]); - - return 0; } static struct platform_driver tps65217_regulator_driver = { @@ -305,7 +288,6 @@ static struct platform_driver tps65217_regulator_driver = { .name = "tps65217-pmic", }, .probe = tps65217_regulator_probe, - .remove = tps65217_regulator_remove, }; static int __init tps65217_regulator_init(void) -- cgit v0.10.2 From 884ea5570f1eda8a1e0f5235cba4f00bafed450e Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 4 Sep 2013 17:17:50 +0530 Subject: regulator: tps6586x: Use devm_regulator_register devm_* simplifies the code. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c index 2c9155b..45e5d68 100644 --- a/drivers/regulator/tps6586x-regulator.c +++ b/drivers/regulator/tps6586x-regulator.c @@ -379,15 +379,14 @@ static int tps6586x_regulator_probe(struct platform_device *pdev) ri = find_regulator_info(id); if (!ri) { dev_err(&pdev->dev, "invalid regulator ID specified\n"); - err = -EINVAL; - goto fail; + return -EINVAL; } err = tps6586x_regulator_preinit(pdev->dev.parent, ri); if (err) { dev_err(&pdev->dev, "regulator %d preinit failed, e %d\n", id, err); - goto fail; + return err; } config.dev = pdev->dev.parent; @@ -397,12 +396,12 @@ static int tps6586x_regulator_probe(struct platform_device *pdev) if (tps6586x_reg_matches) config.of_node = tps6586x_reg_matches[id].of_node; - rdev[id] = regulator_register(&ri->desc, &config); + rdev[id] = devm_regulator_register(&pdev->dev, &ri->desc, + &config); if (IS_ERR(rdev[id])) { dev_err(&pdev->dev, "failed to register regulator %s\n", ri->desc.name); - err = PTR_ERR(rdev[id]); - goto fail; + return PTR_ERR(rdev[id]); } if (reg_data) { @@ -411,30 +410,13 @@ static int tps6586x_regulator_probe(struct platform_device *pdev) if (err < 0) { dev_err(&pdev->dev, "Slew rate config failed, e %d\n", err); - regulator_unregister(rdev[id]); - goto fail; + return err; } } } platform_set_drvdata(pdev, rdev); return 0; - -fail: - while (--id >= 0) - regulator_unregister(rdev[id]); - return err; -} - -static int tps6586x_regulator_remove(struct platform_device *pdev) -{ - struct regulator_dev **rdev = platform_get_drvdata(pdev); - int id = TPS6586X_ID_MAX_REGULATOR; - - while (--id >= 0) - regulator_unregister(rdev[id]); - - return 0; } static struct platform_driver tps6586x_regulator_driver = { @@ -443,7 +425,6 @@ static struct platform_driver tps6586x_regulator_driver = { .owner = THIS_MODULE, }, .probe = tps6586x_regulator_probe, - .remove = tps6586x_regulator_remove, }; static int __init tps6586x_regulator_init(void) -- cgit v0.10.2 From 95095e422e94afa5a286c2a015aad4c039c9cbd4 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 4 Sep 2013 17:17:51 +0530 Subject: regulator: tps65910: Use devm_regulator_register devm_* simplifies the code. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index 45c1644..b8167df 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -1177,35 +1177,19 @@ static int tps65910_probe(struct platform_device *pdev) if (tps65910_reg_matches) config.of_node = tps65910_reg_matches[i].of_node; - rdev = regulator_register(&pmic->desc[i], &config); + rdev = devm_regulator_register(&pdev->dev, &pmic->desc[i], + &config); if (IS_ERR(rdev)) { dev_err(tps65910->dev, "failed to register %s regulator\n", pdev->name); - err = PTR_ERR(rdev); - goto err_unregister_regulator; + return PTR_ERR(rdev); } /* Save regulator for cleanup */ pmic->rdev[i] = rdev; } return 0; - -err_unregister_regulator: - while (--i >= 0) - regulator_unregister(pmic->rdev[i]); - return err; -} - -static int tps65910_remove(struct platform_device *pdev) -{ - struct tps65910_reg *pmic = platform_get_drvdata(pdev); - int i; - - for (i = 0; i < pmic->num_regulators; i++) - regulator_unregister(pmic->rdev[i]); - - return 0; } static void tps65910_shutdown(struct platform_device *pdev) @@ -1244,7 +1228,6 @@ static struct platform_driver tps65910_driver = { .owner = THIS_MODULE, }, .probe = tps65910_probe, - .remove = tps65910_remove, .shutdown = tps65910_shutdown, }; -- cgit v0.10.2 From ab1d65e03afdafa991bb208ade089d36b0e1d2ad Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 4 Sep 2013 17:17:52 +0530 Subject: regulator: tps65912: Use devm_regulator_register devm_* simplifies the code. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/regulator/tps65912-regulator.c b/drivers/regulator/tps65912-regulator.c index 281e52a..1ed4d04 100644 --- a/drivers/regulator/tps65912-regulator.c +++ b/drivers/regulator/tps65912-regulator.c @@ -461,7 +461,7 @@ static int tps65912_probe(struct platform_device *pdev) struct regulator_dev *rdev; struct tps65912_reg *pmic; struct tps65912_board *pmic_plat_data; - int i, err; + int i; pmic_plat_data = dev_get_platdata(tps65912->dev); if (!pmic_plat_data) @@ -504,34 +504,19 @@ static int tps65912_probe(struct platform_device *pdev) config.init_data = reg_data; config.driver_data = pmic; - rdev = regulator_register(&pmic->desc[i], &config); + rdev = devm_regulator_register(&pdev->dev, &pmic->desc[i], + &config); if (IS_ERR(rdev)) { dev_err(tps65912->dev, "failed to register %s regulator\n", pdev->name); - err = PTR_ERR(rdev); - goto err; + return PTR_ERR(rdev); } /* Save regulator for cleanup */ pmic->rdev[i] = rdev; } return 0; - -err: - while (--i >= 0) - regulator_unregister(pmic->rdev[i]); - return err; -} - -static int tps65912_remove(struct platform_device *pdev) -{ - struct tps65912_reg *tps65912_reg = platform_get_drvdata(pdev); - int i; - - for (i = 0; i < TPS65912_NUM_REGULATOR; i++) - regulator_unregister(tps65912_reg->rdev[i]); - return 0; } static struct platform_driver tps65912_driver = { @@ -540,7 +525,6 @@ static struct platform_driver tps65912_driver = { .owner = THIS_MODULE, }, .probe = tps65912_probe, - .remove = tps65912_remove, }; static int __init tps65912_init(void) -- cgit v0.10.2 From 0fb0c82e8135d3de9eff2a05202f6870ec378392 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 4 Sep 2013 17:17:53 +0530 Subject: regulator: tps80031: Use devm_regulator_register devm_* simplifies the code. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/regulator/tps80031-regulator.c b/drivers/regulator/tps80031-regulator.c index 6511d0b..71f457a 100644 --- a/drivers/regulator/tps80031-regulator.c +++ b/drivers/regulator/tps80031-regulator.c @@ -719,7 +719,7 @@ static int tps80031_regulator_probe(struct platform_device *pdev) if (ret < 0) { dev_err(&pdev->dev, "regulator config failed, e %d\n", ret); - goto fail; + return ret; } ret = tps80031_power_req_config(pdev->dev.parent, @@ -727,41 +727,22 @@ static int tps80031_regulator_probe(struct platform_device *pdev) if (ret < 0) { dev_err(&pdev->dev, "pwr_req config failed, err %d\n", ret); - goto fail; + return ret; } } - rdev = regulator_register(&ri->rinfo->desc, &config); + rdev = devm_regulator_register(&pdev->dev, &ri->rinfo->desc, + &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "register regulator failed %s\n", ri->rinfo->desc.name); - ret = PTR_ERR(rdev); - goto fail; + return PTR_ERR(rdev); } ri->rdev = rdev; } platform_set_drvdata(pdev, pmic); return 0; -fail: - while (--num >= 0) { - ri = &pmic[num]; - regulator_unregister(ri->rdev); - } - return ret; -} - -static int tps80031_regulator_remove(struct platform_device *pdev) -{ - struct tps80031_regulator *pmic = platform_get_drvdata(pdev); - struct tps80031_regulator *ri = NULL; - int num; - - for (num = 0; num < TPS80031_REGULATOR_MAX; ++num) { - ri = &pmic[num]; - regulator_unregister(ri->rdev); - } - return 0; } static struct platform_driver tps80031_regulator_driver = { @@ -770,7 +751,6 @@ static struct platform_driver tps80031_regulator_driver = { .owner = THIS_MODULE, }, .probe = tps80031_regulator_probe, - .remove = tps80031_regulator_remove, }; static int __init tps80031_regulator_init(void) -- cgit v0.10.2 From 42141f22e32a452b02b3dc8764d93223505e1c10 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 5 Sep 2013 09:22:02 +0530 Subject: regulator: core: Fix a trivial typo Changed automaticall -> automatically. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 5f995d2..85e8cbe 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -3504,7 +3504,7 @@ static void devm_rdev_release(struct device *dev, void *res) * * Called by regulator drivers to register a regulator. Returns a * valid pointer to struct regulator_dev on success or an ERR_PTR() on - * error. The regulator will automaticall be released when the device + * error. The regulator will automatically be released when the device * is unbound. */ struct regulator_dev *devm_regulator_register(struct device *dev, -- cgit v0.10.2 From cab87f062d0c57c9734257e4079ced489a3189b1 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 5 Sep 2013 09:22:03 +0530 Subject: regulator: Remove redundant NULL assignment NULL assignment corrupts the error pointer and is not necessary. Reported-by: kbuild test robot Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c index de5b30e..ae001cc 100644 --- a/drivers/regulator/max77686.c +++ b/drivers/regulator/max77686.c @@ -483,7 +483,6 @@ static int max77686_pmic_probe(struct platform_device *pdev) if (IS_ERR(max77686->rdev[i])) { dev_err(&pdev->dev, "regulator init failed for %d\n", i); - max77686->rdev[i] = NULL; return PTR_ERR(max77686->rdev[i]); } } diff --git a/drivers/regulator/max77693.c b/drivers/regulator/max77693.c index 2054ae1..feb20bf 100644 --- a/drivers/regulator/max77693.c +++ b/drivers/regulator/max77693.c @@ -271,7 +271,6 @@ static int max77693_pmic_probe(struct platform_device *pdev) if (IS_ERR(max77693_pmic->rdev[i])) { dev_err(max77693_pmic->dev, "Failed to initialize regulator-%d\n", id); - max77693_pmic->rdev[i] = NULL; return PTR_ERR(max77693_pmic->rdev[i]); } } diff --git a/drivers/regulator/max8997.c b/drivers/regulator/max8997.c index 059e8ed..bcd2488 100644 --- a/drivers/regulator/max8997.c +++ b/drivers/regulator/max8997.c @@ -1209,7 +1209,6 @@ static int max8997_pmic_probe(struct platform_device *pdev) if (IS_ERR(rdev[i])) { dev_err(max8997->dev, "regulator init failed for %d\n", id); - rdev[i] = NULL; return PTR_ERR(rdev[i]); } } -- cgit v0.10.2 From cb2e45e31615bc37b31b44fc257042a860ce82b8 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 5 Sep 2013 11:31:25 +0800 Subject: regulator: palmas: Drop regulator_unregister while using devm_regulator_register Commmit af40a94aba "regulator: palmas: Use devm_regulator_register" missed removing a regulator_unregister() call if palmas_extreg_init falis. Fix it. Signed-off-by: Axel Lin Acked-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c index 6c9670f..b5278ac 100644 --- a/drivers/regulator/palmas-regulator.c +++ b/drivers/regulator/palmas-regulator.c @@ -1037,10 +1037,8 @@ static int palmas_regulators_probe(struct platform_device *pdev) else ret = palmas_extreg_init(palmas, id, reg_init); - if (ret) { - regulator_unregister(pmic->rdev[id]); + if (ret) return ret; - } } } } -- cgit v0.10.2 From cee8e355942c01f408bddf8a53596be1dff7a86b Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 1 Sep 2013 12:24:17 +0800 Subject: regulator: core: Refactor devm_regulator_get* APIs The implementation of devm_regulator_get, devm_regulator_get_exclusive and devm_regulator_get_optional are almost the same. Introduce _devm_regulator_get helper function and refactor the code. Also move devm_regulator_get_exclusive to proper place, put it after regulator_get_exclusive() function. Signed-off-by: Axel Lin Signed-off-by: Mark Brown diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 85e8cbe..388397b 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1334,6 +1334,50 @@ out: return regulator; } +enum { + NORMAL_GET, + EXCLUSIVE_GET, + OPTIONAL_GET, +}; + +static void devm_regulator_release(struct device *dev, void *res) +{ + regulator_put(*(struct regulator **)res); +} + +static struct regulator *_devm_regulator_get(struct device *dev, const char *id, + int get_type) +{ + struct regulator **ptr, *regulator; + + ptr = devres_alloc(devm_regulator_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + switch (get_type) { + case NORMAL_GET: + regulator = regulator_get(dev, id); + break; + case EXCLUSIVE_GET: + regulator = regulator_get_exclusive(dev, id); + break; + case OPTIONAL_GET: + regulator = regulator_get_optional(dev, id); + break; + default: + regulator = ERR_PTR(-EINVAL); + } + + if (!IS_ERR(regulator)) { + *ptr = regulator; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return regulator; +} + /** * regulator_get - lookup and obtain a reference to a regulator. * @dev: device for regulator "consumer" @@ -1353,11 +1397,6 @@ struct regulator *regulator_get(struct device *dev, const char *id) } EXPORT_SYMBOL_GPL(regulator_get); -static void devm_regulator_release(struct device *dev, void *res) -{ - regulator_put(*(struct regulator **)res); -} - /** * devm_regulator_get - Resource managed regulator_get() * @dev: device for regulator "consumer" @@ -1369,21 +1408,7 @@ static void devm_regulator_release(struct device *dev, void *res) */ struct regulator *devm_regulator_get(struct device *dev, const char *id) { - struct regulator **ptr, *regulator; - - ptr = devres_alloc(devm_regulator_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); - - regulator = regulator_get(dev, id); - if (!IS_ERR(regulator)) { - *ptr = regulator; - devres_add(dev, ptr); - } else { - devres_free(ptr); - } - - return regulator; + return _devm_regulator_get(dev, id, NORMAL_GET); } EXPORT_SYMBOL_GPL(devm_regulator_get); @@ -1415,6 +1440,22 @@ struct regulator *regulator_get_exclusive(struct device *dev, const char *id) EXPORT_SYMBOL_GPL(regulator_get_exclusive); /** + * devm_regulator_get_exclusive - Resource managed regulator_get_exclusive() + * @dev: device for regulator "consumer" + * @id: Supply name or regulator ID. + * + * Managed regulator_get_exclusive(). Regulators returned from this function + * are automatically regulator_put() on driver detach. See regulator_get() for + * more information. + */ +struct regulator *devm_regulator_get_exclusive(struct device *dev, + const char *id) +{ + return _devm_regulator_get(dev, id, EXCLUSIVE_GET); +} +EXPORT_SYMBOL_GPL(devm_regulator_get_exclusive); + +/** * regulator_get_optional - obtain optional access to a regulator. * @dev: device for regulator "consumer" * @id: Supply name or regulator ID. @@ -1455,21 +1496,7 @@ EXPORT_SYMBOL_GPL(regulator_get_optional); struct regulator *devm_regulator_get_optional(struct device *dev, const char *id) { - struct regulator **ptr, *regulator; - - ptr = devres_alloc(devm_regulator_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); - - regulator = regulator_get_optional(dev, id); - if (!IS_ERR(regulator)) { - *ptr = regulator; - devres_add(dev, ptr); - } else { - devres_free(ptr); - } - - return regulator; + return _devm_regulator_get(dev, id, OPTIONAL_GET); } EXPORT_SYMBOL_GPL(devm_regulator_get_optional); @@ -1499,36 +1526,6 @@ static void _regulator_put(struct regulator *regulator) } /** - * devm_regulator_get_exclusive - Resource managed regulator_get_exclusive() - * @dev: device for regulator "consumer" - * @id: Supply name or regulator ID. - * - * Managed regulator_get_exclusive(). Regulators returned from this function - * are automatically regulator_put() on driver detach. See regulator_get() for - * more information. - */ -struct regulator *devm_regulator_get_exclusive(struct device *dev, - const char *id) -{ - struct regulator **ptr, *regulator; - - ptr = devres_alloc(devm_regulator_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); - - regulator = _regulator_get(dev, id, 1); - if (!IS_ERR(regulator)) { - *ptr = regulator; - devres_add(dev, ptr); - } else { - devres_free(ptr); - } - - return regulator; -} -EXPORT_SYMBOL_GPL(devm_regulator_get_exclusive); - -/** * regulator_put - "free" the regulator source * @regulator: regulator source * -- cgit v0.10.2 From 0cdfcc0f9352a4c076fbb51299e2b12a35619a51 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 11 Sep 2013 13:15:40 +0100 Subject: regulator: core: Split devres code out into a separate file Cut down on the size of core.c a bit more and ensure that the devres versions of things don't do too much peering inside the internals of the APIs they wrap. Signed-off-by: Mark Brown diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 185cce2..69db4c8 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -3,7 +3,7 @@ # -obj-$(CONFIG_REGULATOR) += core.o dummy.o fixed-helper.o helpers.o +obj-$(CONFIG_REGULATOR) += core.o dummy.o fixed-helper.o helpers.o devres.o obj-$(CONFIG_OF) += of_regulator.o obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 388397b..906deb7 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -36,6 +36,7 @@ #include #include "dummy.h" +#include "internal.h" #define rdev_crit(rdev, fmt, ...) \ pr_crit("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__) @@ -82,25 +83,6 @@ struct regulator_enable_gpio { unsigned int ena_gpio_invert:1; }; -/* - * struct regulator - * - * One for each consumer device. - */ -struct regulator { - struct device *dev; - struct list_head list; - unsigned int always_on:1; - unsigned int bypass:1; - int uA_load; - int min_uV; - int max_uV; - char *supply_name; - struct device_attribute dev_attr; - struct regulator_dev *rdev; - struct dentry *debugfs; -}; - static int _regulator_is_enabled(struct regulator_dev *rdev); static int _regulator_disable(struct regulator_dev *rdev); static int _regulator_get_voltage(struct regulator_dev *rdev); @@ -1334,50 +1316,6 @@ out: return regulator; } -enum { - NORMAL_GET, - EXCLUSIVE_GET, - OPTIONAL_GET, -}; - -static void devm_regulator_release(struct device *dev, void *res) -{ - regulator_put(*(struct regulator **)res); -} - -static struct regulator *_devm_regulator_get(struct device *dev, const char *id, - int get_type) -{ - struct regulator **ptr, *regulator; - - ptr = devres_alloc(devm_regulator_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); - - switch (get_type) { - case NORMAL_GET: - regulator = regulator_get(dev, id); - break; - case EXCLUSIVE_GET: - regulator = regulator_get_exclusive(dev, id); - break; - case OPTIONAL_GET: - regulator = regulator_get_optional(dev, id); - break; - default: - regulator = ERR_PTR(-EINVAL); - } - - if (!IS_ERR(regulator)) { - *ptr = regulator; - devres_add(dev, ptr); - } else { - devres_free(ptr); - } - - return regulator; -} - /** * regulator_get - lookup and obtain a reference to a regulator. * @dev: device for regulator "consumer" @@ -1398,21 +1336,6 @@ struct regulator *regulator_get(struct device *dev, const char *id) EXPORT_SYMBOL_GPL(regulator_get); /** - * devm_regulator_get - Resource managed regulator_get() - * @dev: device for regulator "consumer" - * @id: Supply name or regulator ID. - * - * Managed regulator_get(). Regulators returned from this function are - * automatically regulator_put() on driver detach. See regulator_get() for more - * information. - */ -struct regulator *devm_regulator_get(struct device *dev, const char *id) -{ - return _devm_regulator_get(dev, id, NORMAL_GET); -} -EXPORT_SYMBOL_GPL(devm_regulator_get); - -/** * regulator_get_exclusive - obtain exclusive access to a regulator. * @dev: device for regulator "consumer" * @id: Supply name or regulator ID. @@ -1440,22 +1363,6 @@ struct regulator *regulator_get_exclusive(struct device *dev, const char *id) EXPORT_SYMBOL_GPL(regulator_get_exclusive); /** - * devm_regulator_get_exclusive - Resource managed regulator_get_exclusive() - * @dev: device for regulator "consumer" - * @id: Supply name or regulator ID. - * - * Managed regulator_get_exclusive(). Regulators returned from this function - * are automatically regulator_put() on driver detach. See regulator_get() for - * more information. - */ -struct regulator *devm_regulator_get_exclusive(struct device *dev, - const char *id) -{ - return _devm_regulator_get(dev, id, EXCLUSIVE_GET); -} -EXPORT_SYMBOL_GPL(devm_regulator_get_exclusive); - -/** * regulator_get_optional - obtain optional access to a regulator. * @dev: device for regulator "consumer" * @id: Supply name or regulator ID. @@ -1484,22 +1391,6 @@ struct regulator *regulator_get_optional(struct device *dev, const char *id) } EXPORT_SYMBOL_GPL(regulator_get_optional); -/** - * devm_regulator_get_optional - Resource managed regulator_get_optional() - * @dev: device for regulator "consumer" - * @id: Supply name or regulator ID. - * - * Managed regulator_get_optional(). Regulators returned from this - * function are automatically regulator_put() on driver detach. See - * regulator_get_optional() for more information. - */ -struct regulator *devm_regulator_get_optional(struct device *dev, - const char *id) -{ - return _devm_regulator_get(dev, id, OPTIONAL_GET); -} -EXPORT_SYMBOL_GPL(devm_regulator_get_optional); - /* Locks held by regulator_put() */ static void _regulator_put(struct regulator *regulator) { @@ -1541,35 +1432,6 @@ void regulator_put(struct regulator *regulator) } EXPORT_SYMBOL_GPL(regulator_put); -static int devm_regulator_match(struct device *dev, void *res, void *data) -{ - struct regulator **r = res; - if (!r || !*r) { - WARN_ON(!r || !*r); - return 0; - } - return *r == data; -} - -/** - * devm_regulator_put - Resource managed regulator_put() - * @regulator: regulator to free - * - * Deallocate a regulator allocated with devm_regulator_get(). Normally - * this function will not need to be called and the resource management - * code will ensure that the resource is freed. - */ -void devm_regulator_put(struct regulator *regulator) -{ - int rc; - - rc = devres_release(regulator->dev, devm_regulator_release, - devm_regulator_match, regulator); - if (rc != 0) - WARN_ON(rc); -} -EXPORT_SYMBOL_GPL(devm_regulator_put); - /* Manage enable GPIO list. Same GPIO pin can be shared among regulators */ static int regulator_ena_gpio_request(struct regulator_dev *rdev, const struct regulator_config *config) @@ -2909,52 +2771,6 @@ err: } EXPORT_SYMBOL_GPL(regulator_bulk_get); -/** - * devm_regulator_bulk_get - managed get multiple regulator consumers - * - * @dev: Device to supply - * @num_consumers: Number of consumers to register - * @consumers: Configuration of consumers; clients are stored here. - * - * @return 0 on success, an errno on failure. - * - * This helper function allows drivers to get several regulator - * consumers in one operation with management, the regulators will - * automatically be freed when the device is unbound. If any of the - * regulators cannot be acquired then any regulators that were - * allocated will be freed before returning to the caller. - */ -int devm_regulator_bulk_get(struct device *dev, int num_consumers, - struct regulator_bulk_data *consumers) -{ - int i; - int ret; - - for (i = 0; i < num_consumers; i++) - consumers[i].consumer = NULL; - - for (i = 0; i < num_consumers; i++) { - consumers[i].consumer = devm_regulator_get(dev, - consumers[i].supply); - if (IS_ERR(consumers[i].consumer)) { - ret = PTR_ERR(consumers[i].consumer); - dev_err(dev, "Failed to get supply '%s': %d\n", - consumers[i].supply, ret); - consumers[i].consumer = NULL; - goto err; - } - } - - return 0; - -err: - for (i = 0; i < num_consumers && consumers[i].consumer; i++) - devm_regulator_put(consumers[i].consumer); - - return ret; -} -EXPORT_SYMBOL_GPL(devm_regulator_bulk_get); - static void regulator_bulk_enable_async(void *data, async_cookie_t cookie) { struct regulator_bulk_data *bulk = data; @@ -3489,44 +3305,6 @@ clean: } EXPORT_SYMBOL_GPL(regulator_register); -static void devm_rdev_release(struct device *dev, void *res) -{ - regulator_unregister(*(struct regulator_dev **)res); -} - -/** - * devm_regulator_register - Resource managed regulator_register() - * @regulator_desc: regulator to register - * @config: runtime configuration for regulator - * - * Called by regulator drivers to register a regulator. Returns a - * valid pointer to struct regulator_dev on success or an ERR_PTR() on - * error. The regulator will automatically be released when the device - * is unbound. - */ -struct regulator_dev *devm_regulator_register(struct device *dev, - const struct regulator_desc *regulator_desc, - const struct regulator_config *config) -{ - struct regulator_dev **ptr, *rdev; - - ptr = devres_alloc(devm_rdev_release, sizeof(*ptr), - GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); - - rdev = regulator_register(regulator_desc, config); - if (!IS_ERR(rdev)) { - *ptr = rdev; - devres_add(dev, ptr); - } else { - devres_free(ptr); - } - - return rdev; -} -EXPORT_SYMBOL_GPL(devm_regulator_register); - /** * regulator_unregister - unregister regulator * @rdev: regulator to unregister @@ -3556,34 +3334,6 @@ void regulator_unregister(struct regulator_dev *rdev) } EXPORT_SYMBOL_GPL(regulator_unregister); -static int devm_rdev_match(struct device *dev, void *res, void *data) -{ - struct regulator_dev **r = res; - if (!r || !*r) { - WARN_ON(!r || !*r); - return 0; - } - return *r == data; -} - -/** - * devm_regulator_unregister - Resource managed regulator_unregister() - * @regulator: regulator to free - * - * Unregister a regulator registered with devm_regulator_register(). - * Normally this function will not need to be called and the resource - * management code will ensure that the resource is freed. - */ -void devm_regulator_unregister(struct device *dev, struct regulator_dev *rdev) -{ - int rc; - - rc = devres_release(dev, devm_rdev_release, devm_rdev_match, rdev); - if (rc != 0) - WARN_ON(rc); -} -EXPORT_SYMBOL_GPL(devm_regulator_unregister); - /** * regulator_suspend_prepare - prepare regulators for system wide suspend * @state: system suspend state diff --git a/drivers/regulator/devres.c b/drivers/regulator/devres.c new file mode 100644 index 0000000..2672a02 --- /dev/null +++ b/drivers/regulator/devres.c @@ -0,0 +1,252 @@ +/* + * devres.c -- Voltage/Current Regulator framework devres implementation. + * + * Copyright 2013 Linaro Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +enum { + NORMAL_GET, + EXCLUSIVE_GET, + OPTIONAL_GET, +}; + +static void devm_regulator_release(struct device *dev, void *res) +{ + regulator_put(*(struct regulator **)res); +} + +static struct regulator *_devm_regulator_get(struct device *dev, const char *id, + int get_type) +{ + struct regulator **ptr, *regulator; + + ptr = devres_alloc(devm_regulator_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + switch (get_type) { + case NORMAL_GET: + regulator = regulator_get(dev, id); + break; + case EXCLUSIVE_GET: + regulator = regulator_get_exclusive(dev, id); + break; + case OPTIONAL_GET: + regulator = regulator_get_optional(dev, id); + break; + default: + regulator = ERR_PTR(-EINVAL); + } + + if (!IS_ERR(regulator)) { + *ptr = regulator; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return regulator; +} + +/** + * devm_regulator_get - Resource managed regulator_get() + * @dev: device for regulator "consumer" + * @id: Supply name or regulator ID. + * + * Managed regulator_get(). Regulators returned from this function are + * automatically regulator_put() on driver detach. See regulator_get() for more + * information. + */ +struct regulator *devm_regulator_get(struct device *dev, const char *id) +{ + return _devm_regulator_get(dev, id, NORMAL_GET); +} +EXPORT_SYMBOL_GPL(devm_regulator_get); + +/** + * devm_regulator_get_exclusive - Resource managed regulator_get_exclusive() + * @dev: device for regulator "consumer" + * @id: Supply name or regulator ID. + * + * Managed regulator_get_exclusive(). Regulators returned from this function + * are automatically regulator_put() on driver detach. See regulator_get() for + * more information. + */ +struct regulator *devm_regulator_get_exclusive(struct device *dev, + const char *id) +{ + return _devm_regulator_get(dev, id, EXCLUSIVE_GET); +} +EXPORT_SYMBOL_GPL(devm_regulator_get_exclusive); + +/** + * devm_regulator_get_optional - Resource managed regulator_get_optional() + * @dev: device for regulator "consumer" + * @id: Supply name or regulator ID. + * + * Managed regulator_get_optional(). Regulators returned from this + * function are automatically regulator_put() on driver detach. See + * regulator_get_optional() for more information. + */ +struct regulator *devm_regulator_get_optional(struct device *dev, + const char *id) +{ + return _devm_regulator_get(dev, id, OPTIONAL_GET); +} +EXPORT_SYMBOL_GPL(devm_regulator_get_optional); + +static int devm_regulator_match(struct device *dev, void *res, void *data) +{ + struct regulator **r = res; + if (!r || !*r) { + WARN_ON(!r || !*r); + return 0; + } + return *r == data; +} + +/** + * devm_regulator_put - Resource managed regulator_put() + * @regulator: regulator to free + * + * Deallocate a regulator allocated with devm_regulator_get(). Normally + * this function will not need to be called and the resource management + * code will ensure that the resource is freed. + */ +void devm_regulator_put(struct regulator *regulator) +{ + int rc; + + rc = devres_release(regulator->dev, devm_regulator_release, + devm_regulator_match, regulator); + if (rc != 0) + WARN_ON(rc); +} +EXPORT_SYMBOL_GPL(devm_regulator_put); + +/** + * devm_regulator_bulk_get - managed get multiple regulator consumers + * + * @dev: Device to supply + * @num_consumers: Number of consumers to register + * @consumers: Configuration of consumers; clients are stored here. + * + * @return 0 on success, an errno on failure. + * + * This helper function allows drivers to get several regulator + * consumers in one operation with management, the regulators will + * automatically be freed when the device is unbound. If any of the + * regulators cannot be acquired then any regulators that were + * allocated will be freed before returning to the caller. + */ +int devm_regulator_bulk_get(struct device *dev, int num_consumers, + struct regulator_bulk_data *consumers) +{ + int i; + int ret; + + for (i = 0; i < num_consumers; i++) + consumers[i].consumer = NULL; + + for (i = 0; i < num_consumers; i++) { + consumers[i].consumer = devm_regulator_get(dev, + consumers[i].supply); + if (IS_ERR(consumers[i].consumer)) { + ret = PTR_ERR(consumers[i].consumer); + dev_err(dev, "Failed to get supply '%s': %d\n", + consumers[i].supply, ret); + consumers[i].consumer = NULL; + goto err; + } + } + + return 0; + +err: + for (i = 0; i < num_consumers && consumers[i].consumer; i++) + devm_regulator_put(consumers[i].consumer); + + return ret; +} +EXPORT_SYMBOL_GPL(devm_regulator_bulk_get); + +static void devm_rdev_release(struct device *dev, void *res) +{ + regulator_unregister(*(struct regulator_dev **)res); +} + +/** + * devm_regulator_register - Resource managed regulator_register() + * @regulator_desc: regulator to register + * @config: runtime configuration for regulator + * + * Called by regulator drivers to register a regulator. Returns a + * valid pointer to struct regulator_dev on success or an ERR_PTR() on + * error. The regulator will automatically be released when the device + * is unbound. + */ +struct regulator_dev *devm_regulator_register(struct device *dev, + const struct regulator_desc *regulator_desc, + const struct regulator_config *config) +{ + struct regulator_dev **ptr, *rdev; + + ptr = devres_alloc(devm_rdev_release, sizeof(*ptr), + GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + rdev = regulator_register(regulator_desc, config); + if (!IS_ERR(rdev)) { + *ptr = rdev; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return rdev; +} +EXPORT_SYMBOL_GPL(devm_regulator_register); + +static int devm_rdev_match(struct device *dev, void *res, void *data) +{ + struct regulator_dev **r = res; + if (!r || !*r) { + WARN_ON(!r || !*r); + return 0; + } + return *r == data; +} + +/** + * devm_regulator_unregister - Resource managed regulator_unregister() + * @regulator: regulator to free + * + * Unregister a regulator registered with devm_regulator_register(). + * Normally this function will not need to be called and the resource + * management code will ensure that the resource is freed. + */ +void devm_regulator_unregister(struct device *dev, struct regulator_dev *rdev) +{ + int rc; + + rc = devres_release(dev, devm_rdev_release, devm_rdev_match, rdev); + if (rc != 0) + WARN_ON(rc); +} +EXPORT_SYMBOL_GPL(devm_regulator_unregister); diff --git a/drivers/regulator/internal.h b/drivers/regulator/internal.h new file mode 100644 index 0000000..84bbda1 --- /dev/null +++ b/drivers/regulator/internal.h @@ -0,0 +1,38 @@ +/* + * internal.h -- Voltage/Current Regulator framework internal code + * + * Copyright 2007, 2008 Wolfson Microelectronics PLC. + * Copyright 2008 SlimLogic Ltd. + * + * Author: Liam Girdwood + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef __REGULATOR_INTERNAL_H +#define __REGULATOR_INTERNAL_H + +/* + * struct regulator + * + * One for each consumer device. + */ +struct regulator { + struct device *dev; + struct list_head list; + unsigned int always_on:1; + unsigned int bypass:1; + int uA_load; + int min_uV; + int max_uV; + char *supply_name; + struct device_attribute dev_attr; + struct regulator_dev *rdev; + struct dentry *debugfs; +}; + +#endif -- cgit v0.10.2 From 4ddfebd3b0d5b65c69492408bb67fd1202104643 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 13 Sep 2013 19:50:37 +0100 Subject: regulator: core: Provide a dummy regulator with full constraints When a system has said that it has fully specified constraints for its regulators it is still possible that some supplies may be missing, especially if regulator support has been added to a driver after the board was integrated. We can handle such situations more gracefully by providing a dummy regulator. Unless the caller has specifically indicated that the system design may not include a given regulator by using regulator_get_optional() or that it needs its interactions to have an effect using regulator_get_exclusive() provide a dummy regulator if we can't locate a real one. The kconfig option REGULATOR_DUMMY that provided similar behaviour for all regulators has been removed, systems that need it should flag that they have full constraints instead. Signed-off-by: Mark Brown diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index dfe5809..0417ce3 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -28,16 +28,6 @@ config REGULATOR_DEBUG help Say yes here to enable debugging support. -config REGULATOR_DUMMY - bool "Provide a dummy regulator if regulator lookups fail" - help - If this option is enabled then when a regulator lookup fails - and the board has not specified that it has provided full - constraints the regulator core will provide an always - enabled dummy regulator, allowing consumer drivers to continue. - - A warning will be generated when this substitution is done. - config REGULATOR_FIXED_VOLTAGE tristate "Fixed voltage regulator support" help diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index a01b8b3..8fd1707 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1243,7 +1243,7 @@ static struct regulator_dev *regulator_dev_lookup(struct device *dev, /* Internal regulator request function */ static struct regulator *_regulator_get(struct device *dev, const char *id, - bool exclusive) + bool exclusive, bool allow_dummy) { struct regulator_dev *rdev; struct regulator *regulator = ERR_PTR(-EPROBE_DEFER); @@ -1268,30 +1268,32 @@ static struct regulator *_regulator_get(struct device *dev, const char *id, * If we have return value from dev_lookup fail, we do not expect to * succeed, so, quit with appropriate error value */ - if (ret) { + if (ret && ret != -ENODEV) { regulator = ERR_PTR(ret); goto out; } - if (board_wants_dummy_regulator) { - rdev = dummy_regulator_rdev; - goto found; - } - -#ifdef CONFIG_REGULATOR_DUMMY if (!devname) devname = "deviceless"; - /* If the board didn't flag that it was fully constrained then - * substitute in a dummy regulator so consumers can continue. + /* + * Assume that a regulator is physically present and enabled + * even if it isn't hooked up and just provide a dummy. */ - if (!has_full_constraints) { - pr_warn("%s supply %s not found, using dummy regulator\n", - devname, id); + if (has_full_constraints && allow_dummy) { + /* + * Log the substitution if regulator configuration is + * not complete to help development. + */ + if (!has_full_constraints) + pr_warn("%s supply %s not found, using dummy regulator\n", + devname, id); + rdev = dummy_regulator_rdev; goto found; + } else { + dev_err(dev, "dummy supplies not allowed\n"); } -#endif mutex_unlock(®ulator_list_mutex); return regulator; @@ -1349,7 +1351,7 @@ out: */ struct regulator *regulator_get(struct device *dev, const char *id) { - return _regulator_get(dev, id, false); + return _regulator_get(dev, id, false, true); } EXPORT_SYMBOL_GPL(regulator_get); @@ -1410,7 +1412,7 @@ EXPORT_SYMBOL_GPL(devm_regulator_get); */ struct regulator *regulator_get_exclusive(struct device *dev, const char *id) { - return _regulator_get(dev, id, true); + return _regulator_get(dev, id, true, false); } EXPORT_SYMBOL_GPL(regulator_get_exclusive); @@ -1439,7 +1441,7 @@ EXPORT_SYMBOL_GPL(regulator_get_exclusive); */ struct regulator *regulator_get_optional(struct device *dev, const char *id) { - return _regulator_get(dev, id, 0); + return _regulator_get(dev, id, false, false); } EXPORT_SYMBOL_GPL(regulator_get_optional); -- cgit v0.10.2 From 4f0ac6dabf867095b31f851ba0d0ceaca2f87e2e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 13 Sep 2013 19:51:47 +0100 Subject: regulator: core: Remove unused regulator_use_dummy_regulator() No boards have used this functionality and the new default of providing dummy regulators by default provides a better solution to the problem it was trying to solve. Signed-off-by: Mark Brown diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 8fd1707..ac3a864 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -53,7 +53,6 @@ static LIST_HEAD(regulator_list); static LIST_HEAD(regulator_map_list); static LIST_HEAD(regulator_ena_gpio_list); static bool has_full_constraints; -static bool board_wants_dummy_regulator; static struct dentry *debugfs_root; @@ -3616,22 +3615,6 @@ void regulator_has_full_constraints(void) EXPORT_SYMBOL_GPL(regulator_has_full_constraints); /** - * regulator_use_dummy_regulator - Provide a dummy regulator when none is found - * - * Calling this function will cause the regulator API to provide a - * dummy regulator to consumers if no physical regulator is found, - * allowing most consumers to proceed as though a regulator were - * configured. This allows systems such as those with software - * controllable regulators for the CPU core only to be brought up more - * readily. - */ -void regulator_use_dummy_regulator(void) -{ - board_wants_dummy_regulator = true; -} -EXPORT_SYMBOL_GPL(regulator_use_dummy_regulator); - -/** * rdev_get_drvdata - get rdev regulator driver data * @rdev: regulator * diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h index 999b20c..a9f7c55 100644 --- a/include/linux/regulator/machine.h +++ b/include/linux/regulator/machine.h @@ -193,15 +193,10 @@ int regulator_suspend_finish(void); #ifdef CONFIG_REGULATOR void regulator_has_full_constraints(void); -void regulator_use_dummy_regulator(void); #else static inline void regulator_has_full_constraints(void) { } - -static inline void regulator_use_dummy_regulator(void) -{ -} #endif #endif -- cgit v0.10.2 From 32b6d3f6027a05f42987f74deed48dc13cd5a11d Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 21 Aug 2013 16:18:16 +0530 Subject: regulator: palmas: add support for external control of rails Palmas rails like LDOs, SMPSs, REGENs, SYSENs can be enable and disable by register programming through I2C communication as well as it can be enable/disable with the external control input ENABLE1, ENABLE2 and NSLEEP. Add support for configuring these rails to be controlled by external control inputs. This is require to configure the rail's control register as well as configuration of resource register. Provide the external input names through parameter "roof-floor". Updated the DT binding document to details different value of the roof-floor. Signed-off-by: Laxman Dewangan Acked-by: Stephen Warren Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/regulator/palmas-pmic.txt b/Documentation/devicetree/bindings/regulator/palmas-pmic.txt index 875639a..42e6b6b 100644 --- a/Documentation/devicetree/bindings/regulator/palmas-pmic.txt +++ b/Documentation/devicetree/bindings/regulator/palmas-pmic.txt @@ -26,11 +26,17 @@ Optional nodes: For ti,palmas-pmic - smps12, smps123, smps3 depending on OTP, smps45, smps457, smps7 depending on variant, smps6, smps[8-9], - smps10_out2, smps10_out1, do[1-9], ldoln, ldousb. + smps10_out2, smps10_out1, ldo[1-9], ldoln, ldousb. Optional sub-node properties: ti,warm-reset - maintain voltage during warm reset(boolean) - ti,roof-floor - control voltage selection by pin(boolean) + ti,roof-floor - This takes as optional argument on platform supporting + the rail from desired external control. If there is no argument then + it will be assume that it is controlled by NSLEEP pin. + The valid value for external pins are: + ENABLE1 then 1, + ENABLE2 then 2 or + NSLEEP then 3. ti,mode-sleep - mode to adopt in pmic sleep 0 - off, 1 - auto, 2 - eco, 3 - forced pwm ti,smps-range - OTP has the wrong range set for the hardware so override @@ -61,7 +67,7 @@ pmic { regulator-always-on; regulator-boot-on; ti,warm-reset; - ti,roof-floor; + ti,roof-floor = <1>; /* ENABLE1 control */ ti,mode-sleep = <0>; ti,smps-range = <1>; }; diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c index 488dfe7..3c08e1b 100644 --- a/drivers/regulator/palmas-regulator.c +++ b/drivers/regulator/palmas-regulator.c @@ -33,6 +33,7 @@ struct regs_info { u8 vsel_addr; u8 ctrl_addr; u8 tstep_addr; + int sleep_id; }; static const struct regs_info palmas_regs_info[] = { @@ -42,6 +43,7 @@ static const struct regs_info palmas_regs_info[] = { .vsel_addr = PALMAS_SMPS12_VOLTAGE, .ctrl_addr = PALMAS_SMPS12_CTRL, .tstep_addr = PALMAS_SMPS12_TSTEP, + .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS12, }, { .name = "SMPS123", @@ -49,12 +51,14 @@ static const struct regs_info palmas_regs_info[] = { .vsel_addr = PALMAS_SMPS12_VOLTAGE, .ctrl_addr = PALMAS_SMPS12_CTRL, .tstep_addr = PALMAS_SMPS12_TSTEP, + .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS12, }, { .name = "SMPS3", .sname = "smps3-in", .vsel_addr = PALMAS_SMPS3_VOLTAGE, .ctrl_addr = PALMAS_SMPS3_CTRL, + .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS3, }, { .name = "SMPS45", @@ -62,6 +66,7 @@ static const struct regs_info palmas_regs_info[] = { .vsel_addr = PALMAS_SMPS45_VOLTAGE, .ctrl_addr = PALMAS_SMPS45_CTRL, .tstep_addr = PALMAS_SMPS45_TSTEP, + .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS45, }, { .name = "SMPS457", @@ -69,6 +74,7 @@ static const struct regs_info palmas_regs_info[] = { .vsel_addr = PALMAS_SMPS45_VOLTAGE, .ctrl_addr = PALMAS_SMPS45_CTRL, .tstep_addr = PALMAS_SMPS45_TSTEP, + .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS45, }, { .name = "SMPS6", @@ -76,12 +82,14 @@ static const struct regs_info palmas_regs_info[] = { .vsel_addr = PALMAS_SMPS6_VOLTAGE, .ctrl_addr = PALMAS_SMPS6_CTRL, .tstep_addr = PALMAS_SMPS6_TSTEP, + .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS6, }, { .name = "SMPS7", .sname = "smps7-in", .vsel_addr = PALMAS_SMPS7_VOLTAGE, .ctrl_addr = PALMAS_SMPS7_CTRL, + .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS7, }, { .name = "SMPS8", @@ -89,108 +97,128 @@ static const struct regs_info palmas_regs_info[] = { .vsel_addr = PALMAS_SMPS8_VOLTAGE, .ctrl_addr = PALMAS_SMPS8_CTRL, .tstep_addr = PALMAS_SMPS8_TSTEP, + .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS8, }, { .name = "SMPS9", .sname = "smps9-in", .vsel_addr = PALMAS_SMPS9_VOLTAGE, .ctrl_addr = PALMAS_SMPS9_CTRL, + .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS9, }, { .name = "SMPS10_OUT2", .sname = "smps10-in", .ctrl_addr = PALMAS_SMPS10_CTRL, + .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS10, }, { .name = "SMPS10_OUT1", .sname = "smps10-out2", .ctrl_addr = PALMAS_SMPS10_CTRL, + .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS10, }, { .name = "LDO1", .sname = "ldo1-in", .vsel_addr = PALMAS_LDO1_VOLTAGE, .ctrl_addr = PALMAS_LDO1_CTRL, + .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDO1, }, { .name = "LDO2", .sname = "ldo2-in", .vsel_addr = PALMAS_LDO2_VOLTAGE, .ctrl_addr = PALMAS_LDO2_CTRL, + .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDO2, }, { .name = "LDO3", .sname = "ldo3-in", .vsel_addr = PALMAS_LDO3_VOLTAGE, .ctrl_addr = PALMAS_LDO3_CTRL, + .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDO3, }, { .name = "LDO4", .sname = "ldo4-in", .vsel_addr = PALMAS_LDO4_VOLTAGE, .ctrl_addr = PALMAS_LDO4_CTRL, + .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDO4, }, { .name = "LDO5", .sname = "ldo5-in", .vsel_addr = PALMAS_LDO5_VOLTAGE, .ctrl_addr = PALMAS_LDO5_CTRL, + .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDO5, }, { .name = "LDO6", .sname = "ldo6-in", .vsel_addr = PALMAS_LDO6_VOLTAGE, .ctrl_addr = PALMAS_LDO6_CTRL, + .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDO6, }, { .name = "LDO7", .sname = "ldo7-in", .vsel_addr = PALMAS_LDO7_VOLTAGE, .ctrl_addr = PALMAS_LDO7_CTRL, + .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDO7, }, { .name = "LDO8", .sname = "ldo8-in", .vsel_addr = PALMAS_LDO8_VOLTAGE, .ctrl_addr = PALMAS_LDO8_CTRL, + .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDO8, }, { .name = "LDO9", .sname = "ldo9-in", .vsel_addr = PALMAS_LDO9_VOLTAGE, .ctrl_addr = PALMAS_LDO9_CTRL, + .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDO9, }, { .name = "LDOLN", .sname = "ldoln-in", .vsel_addr = PALMAS_LDOLN_VOLTAGE, .ctrl_addr = PALMAS_LDOLN_CTRL, + .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDOLN, }, { .name = "LDOUSB", .sname = "ldousb-in", .vsel_addr = PALMAS_LDOUSB_VOLTAGE, .ctrl_addr = PALMAS_LDOUSB_CTRL, + .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDOUSB, }, { .name = "REGEN1", .ctrl_addr = PALMAS_REGEN1_CTRL, + .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_REGEN1, }, { .name = "REGEN2", .ctrl_addr = PALMAS_REGEN2_CTRL, + .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_REGEN2, }, { .name = "REGEN3", .ctrl_addr = PALMAS_REGEN3_CTRL, + .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_REGEN3, }, { .name = "SYSEN1", .ctrl_addr = PALMAS_SYSEN1_CTRL, + .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SYSEN1, }, { .name = "SYSEN2", .ctrl_addr = PALMAS_SYSEN2_CTRL, + .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SYSEN2, }, }; @@ -484,6 +512,17 @@ static struct regulator_ops palmas_ops_smps = { .set_ramp_delay = palmas_smps_set_ramp_delay, }; +static struct regulator_ops palmas_ops_ext_control_smps = { + .set_mode = palmas_set_mode_smps, + .get_mode = palmas_get_mode_smps, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = palmas_list_voltage_smps, + .map_voltage = palmas_map_voltage_smps, + .set_voltage_time_sel = palma_smps_set_voltage_smps_time_sel, + .set_ramp_delay = palmas_smps_set_ramp_delay, +}; + static struct regulator_ops palmas_ops_smps10 = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, @@ -519,12 +558,37 @@ static struct regulator_ops palmas_ops_ldo = { .map_voltage = regulator_map_voltage_linear, }; +static struct regulator_ops palmas_ops_ext_control_ldo = { + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, +}; + static struct regulator_ops palmas_ops_extreg = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, }; +static struct regulator_ops palmas_ops_ext_control_extreg = { +}; + +static int palmas_regulator_config_external(struct palmas *palmas, int id, + struct palmas_reg_init *reg_init) +{ + int sleep_id = palmas_regs_info[id].sleep_id; + int ret; + + ret = palmas_ext_control_req_config(palmas, sleep_id, + reg_init->roof_floor, true); + if (ret < 0) + dev_err(palmas->dev, + "Ext control config for regulator %d failed %d\n", + id, ret); + return ret; +} + /* * setup the hardware based sleep configuration of the SMPS/LDO regulators * from the platform data. This is different to the software based control @@ -583,7 +647,22 @@ static int palmas_smps_init(struct palmas *palmas, int id, return ret; } + if (reg_init->roof_floor && (id != PALMAS_REG_SMPS10_OUT1) && + (id != PALMAS_REG_SMPS10_OUT2)) { + /* Enable externally controlled regulator */ + addr = palmas_regs_info[id].ctrl_addr; + ret = palmas_smps_read(palmas, addr, ®); + if (ret < 0) + return ret; + if (!(reg & PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK)) { + reg |= SMPS_CTRL_MODE_ON; + ret = palmas_smps_write(palmas, addr, reg); + if (ret < 0) + return ret; + } + return palmas_regulator_config_external(palmas, id, reg_init); + } return 0; } @@ -614,6 +693,20 @@ static int palmas_ldo_init(struct palmas *palmas, int id, if (ret) return ret; + if (reg_init->roof_floor) { + /* Enable externally controlled regulator */ + addr = palmas_regs_info[id].ctrl_addr; + ret = palmas_update_bits(palmas, PALMAS_LDO_BASE, + addr, PALMAS_LDO1_CTRL_MODE_ACTIVE, + PALMAS_LDO1_CTRL_MODE_ACTIVE); + if (ret < 0) { + dev_err(palmas->dev, + "LDO Register 0x%02x update failed %d\n", + addr, ret); + return ret; + } + return palmas_regulator_config_external(palmas, id, reg_init); + } return 0; } @@ -636,6 +729,21 @@ static int palmas_extreg_init(struct palmas *palmas, int id, addr, ret); return ret; } + + if (reg_init->roof_floor) { + /* Enable externally controlled regulator */ + addr = palmas_regs_info[id].ctrl_addr; + ret = palmas_update_bits(palmas, PALMAS_RESOURCE_BASE, + addr, PALMAS_REGEN1_CTRL_MODE_ACTIVE, + PALMAS_REGEN1_CTRL_MODE_ACTIVE); + if (ret < 0) { + dev_err(palmas->dev, + "Resource Register 0x%02x update failed %d\n", + addr, ret); + return ret; + } + return palmas_regulator_config_external(palmas, id, reg_init); + } return 0; } @@ -746,9 +854,35 @@ static void palmas_dt_to_pdata(struct device *dev, of_property_read_bool(palmas_matches[idx].of_node, "ti,warm-reset"); - pdata->reg_init[idx]->roof_floor = - of_property_read_bool(palmas_matches[idx].of_node, - "ti,roof-floor"); + ret = of_property_read_u32(palmas_matches[idx].of_node, + "ti,roof-floor", &prop); + /* EINVAL: Property not found */ + if (ret != -EINVAL) { + int econtrol; + + /* use default value, when no value is specified */ + econtrol = PALMAS_EXT_CONTROL_NSLEEP; + if (!ret) { + switch (prop) { + case 1: + econtrol = PALMAS_EXT_CONTROL_ENABLE1; + break; + case 2: + econtrol = PALMAS_EXT_CONTROL_ENABLE2; + break; + case 3: + econtrol = PALMAS_EXT_CONTROL_NSLEEP; + break; + default: + WARN_ON(1); + dev_warn(dev, + "%s: Invalid roof-floor option: %u\n", + palmas_matches[idx].name, prop); + break; + } + } + pdata->reg_init[idx]->roof_floor = econtrol; + } ret = of_property_read_u32(palmas_matches[idx].of_node, "ti,mode-sleep", &prop); @@ -875,6 +1009,8 @@ static int palmas_regulators_probe(struct platform_device *pdev) ret = palmas_smps_init(palmas, id, reg_init); if (ret) goto err_unregister_regulator; + } else { + reg_init = NULL; } /* Register the regulators */ @@ -919,7 +1055,11 @@ static int palmas_regulators_probe(struct platform_device *pdev) if (reg & PALMAS_SMPS12_VOLTAGE_RANGE) pmic->range[id] = 1; - pmic->desc[id].ops = &palmas_ops_smps; + if (reg_init && reg_init->roof_floor) + pmic->desc[id].ops = + &palmas_ops_ext_control_smps; + else + pmic->desc[id].ops = &palmas_ops_smps; pmic->desc[id].n_voltages = PALMAS_SMPS_NUM_VOLTAGES; pmic->desc[id].vsel_reg = PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE, @@ -962,6 +1102,10 @@ static int palmas_regulators_probe(struct platform_device *pdev) /* Start this loop from the id left from previous loop */ for (; id < PALMAS_NUM_REGS; id++) { + if (pdata && pdata->reg_init[id]) + reg_init = pdata->reg_init[id]; + else + reg_init = NULL; /* Miss out regulators which are not available due * to alternate functions. @@ -975,7 +1119,11 @@ static int palmas_regulators_probe(struct platform_device *pdev) if (id < PALMAS_REG_REGEN1) { pmic->desc[id].n_voltages = PALMAS_LDO_NUM_VOLTAGES; - pmic->desc[id].ops = &palmas_ops_ldo; + if (reg_init && reg_init->roof_floor) + pmic->desc[id].ops = + &palmas_ops_ext_control_ldo; + else + pmic->desc[id].ops = &palmas_ops_ldo; pmic->desc[id].min_uV = 900000; pmic->desc[id].uV_step = 50000; pmic->desc[id].linear_min_sel = 1; @@ -999,7 +1147,11 @@ static int palmas_regulators_probe(struct platform_device *pdev) } } else { pmic->desc[id].n_voltages = 1; - pmic->desc[id].ops = &palmas_ops_extreg; + if (reg_init && reg_init->roof_floor) + pmic->desc[id].ops = + &palmas_ops_ext_control_extreg; + else + pmic->desc[id].ops = &palmas_ops_extreg; pmic->desc[id].enable_reg = PALMAS_BASE_TO_REG(PALMAS_RESOURCE_BASE, palmas_regs_info[id].ctrl_addr); -- cgit v0.10.2 From 5957e444d2aa01705749190c32ae88fc4a029156 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 10 Sep 2013 16:48:07 +0530 Subject: regulator: palmas: configure enable time for LDOs As per datasheet (Referred TPS65913), the on-time for LDO is 500micro second. If LDO6 is in vibrator mode then the on-time is 2000us. Set the enable_time on regulator descriptor accordingly. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c index 3c08e1b..76ce09c 100644 --- a/drivers/regulator/palmas-regulator.c +++ b/drivers/regulator/palmas-regulator.c @@ -1127,6 +1127,7 @@ static int palmas_regulators_probe(struct platform_device *pdev) pmic->desc[id].min_uV = 900000; pmic->desc[id].uV_step = 50000; pmic->desc[id].linear_min_sel = 1; + pmic->desc[id].enable_time = 500; pmic->desc[id].vsel_reg = PALMAS_BASE_TO_REG(PALMAS_LDO_BASE, palmas_regs_info[id].vsel_addr); @@ -1145,6 +1146,11 @@ static int palmas_regulators_probe(struct platform_device *pdev) pmic->desc[id].min_uV = 450000; pmic->desc[id].uV_step = 25000; } + + /* LOD6 in vibrator mode will have enable time 2000us */ + if (pdata && pdata->ldo6_vibrator && + (id == PALMAS_REG_LDO6)) + pmic->desc[id].enable_time = 2000; } else { pmic->desc[id].n_voltages = 1; if (reg_init && reg_init->roof_floor) -- cgit v0.10.2 From 175ee39e8f4053f95e1948afd75c74552b3a175c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 31 Aug 2013 20:31:11 +0200 Subject: ASoC: Remove support for reg_access_defaults No users of reg_access_defaults are left and new drivers are going to use regmap for this, so support for it can be removed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index d22cb0a..447278a 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -403,12 +403,6 @@ int snd_soc_cache_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value); int snd_soc_cache_read(struct snd_soc_codec *codec, unsigned int reg, unsigned int *value); -int snd_soc_default_volatile_register(struct snd_soc_codec *codec, - unsigned int reg); -int snd_soc_default_readable_register(struct snd_soc_codec *codec, - unsigned int reg); -int snd_soc_default_writable_register(struct snd_soc_codec *codec, - unsigned int reg); int snd_soc_platform_read(struct snd_soc_platform *platform, unsigned int reg); int snd_soc_platform_write(struct snd_soc_platform *platform, @@ -542,22 +536,6 @@ int snd_soc_put_strobe(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); /** - * struct snd_soc_reg_access - Describes whether a given register is - * readable, writable or volatile. - * - * @reg: the register number - * @read: whether this register is readable - * @write: whether this register is writable - * @vol: whether this register is volatile - */ -struct snd_soc_reg_access { - u16 reg; - u16 read; - u16 write; - u16 vol; -}; - -/** * struct snd_soc_jack_pin - Describes a pin to update based on jack detection * * @pin: name of the pin to update @@ -760,8 +738,6 @@ struct snd_soc_codec_driver { short reg_cache_step; short reg_word_size; const void *reg_cache_default; - short reg_access_size; - const struct snd_soc_reg_access *reg_access_default; enum snd_soc_compress_type compress_type; /* codec bias level */ diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index e72f554..eaa898f 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -275,66 +275,3 @@ int snd_soc_cache_sync(struct snd_soc_codec *codec) return ret; } EXPORT_SYMBOL_GPL(snd_soc_cache_sync); - -static int snd_soc_get_reg_access_index(struct snd_soc_codec *codec, - unsigned int reg) -{ - const struct snd_soc_codec_driver *codec_drv; - unsigned int min, max, index; - - codec_drv = codec->driver; - min = 0; - max = codec_drv->reg_access_size - 1; - do { - index = (min + max) / 2; - if (codec_drv->reg_access_default[index].reg == reg) - return index; - if (codec_drv->reg_access_default[index].reg < reg) - min = index + 1; - else - max = index; - } while (min <= max); - return -1; -} - -int snd_soc_default_volatile_register(struct snd_soc_codec *codec, - unsigned int reg) -{ - int index; - - if (reg >= codec->driver->reg_cache_size) - return 1; - index = snd_soc_get_reg_access_index(codec, reg); - if (index < 0) - return 0; - return codec->driver->reg_access_default[index].vol; -} -EXPORT_SYMBOL_GPL(snd_soc_default_volatile_register); - -int snd_soc_default_readable_register(struct snd_soc_codec *codec, - unsigned int reg) -{ - int index; - - if (reg >= codec->driver->reg_cache_size) - return 1; - index = snd_soc_get_reg_access_index(codec, reg); - if (index < 0) - return 0; - return codec->driver->reg_access_default[index].read; -} -EXPORT_SYMBOL_GPL(snd_soc_default_readable_register); - -int snd_soc_default_writable_register(struct snd_soc_codec *codec, - unsigned int reg) -{ - int index; - - if (reg >= codec->driver->reg_cache_size) - return 1; - index = snd_soc_get_reg_access_index(codec, reg); - if (index < 0) - return 0; - return codec->driver->reg_access_default[index].write; -} -EXPORT_SYMBOL_GPL(snd_soc_default_writable_register); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4d05613..f5ec301 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4224,15 +4224,6 @@ int snd_soc_register_codec(struct device *dev, } } - if (codec_drv->reg_access_size && codec_drv->reg_access_default) { - if (!codec->volatile_register) - codec->volatile_register = snd_soc_default_volatile_register; - if (!codec->readable_register) - codec->readable_register = snd_soc_default_readable_register; - if (!codec->writable_register) - codec->writable_register = snd_soc_default_writable_register; - } - for (i = 0; i < num_dai; i++) { fixup_codec_formats(&dai_drv[i].playback); fixup_codec_formats(&dai_drv[i].capture); -- cgit v0.10.2 From 2a1212a8342c469cee240cf69fe3001b898cda8e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 31 Aug 2013 20:31:12 +0200 Subject: ASoC: Remove snd_soc_bulk_write_raw() No users of snd_soc_bulk_write_raw() are left and new drivers are going to use regmap directly for this, so the function can be removed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index 447278a..3f7de6f 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -686,7 +686,6 @@ struct snd_soc_codec { unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int); unsigned int (*read)(struct snd_soc_codec *, unsigned int); int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); - int (*bulk_write_raw)(struct snd_soc_codec *, unsigned int, const void *, size_t); void *reg_cache; const void *reg_def_copy; const struct snd_soc_cache_ops *cache_ops; @@ -1097,8 +1096,6 @@ struct soc_enum { unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg); unsigned int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val); -unsigned int snd_soc_bulk_write_raw(struct snd_soc_codec *codec, - unsigned int reg, const void *data, size_t len); /* device driver data */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index f5ec301..4ce02e6 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2298,13 +2298,6 @@ unsigned int snd_soc_write(struct snd_soc_codec *codec, } EXPORT_SYMBOL_GPL(snd_soc_write); -unsigned int snd_soc_bulk_write_raw(struct snd_soc_codec *codec, - unsigned int reg, const void *data, size_t len) -{ - return codec->bulk_write_raw(codec, reg, data, len); -} -EXPORT_SYMBOL_GPL(snd_soc_bulk_write_raw); - /** * snd_soc_update_bits - update codec register bits * @codec: audio codec diff --git a/sound/soc/soc-io.c b/sound/soc/soc-io.c index 122c0c1..4f11d23 100644 --- a/sound/soc/soc-io.c +++ b/sound/soc/soc-io.c @@ -65,31 +65,6 @@ static unsigned int hw_read(struct snd_soc_codec *codec, unsigned int reg) return val; } -/* Primitive bulk write support for soc-cache. The data pointed to by - * `data' needs to already be in the form the hardware expects. Any - * data written through this function will not go through the cache as - * it only handles writing to volatile or out of bounds registers. - * - * This is currently only supported for devices using the regmap API - * wrappers. - */ -static int snd_soc_hw_bulk_write_raw(struct snd_soc_codec *codec, - unsigned int reg, - const void *data, size_t len) -{ - /* To ensure that we don't get out of sync with the cache, check - * whether the base register is volatile or if we've directly asked - * to bypass the cache. Out of bounds registers are considered - * volatile. - */ - if (!codec->cache_bypass - && !snd_soc_codec_volatile_register(codec, reg) - && reg < codec->driver->reg_cache_size) - return -EINVAL; - - return regmap_raw_write(codec->control_data, reg, data, len); -} - /** * snd_soc_codec_set_cache_io: Set up standard I/O functions. * @@ -119,7 +94,6 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, memset(&config, 0, sizeof(config)); codec->write = hw_write; codec->read = hw_read; - codec->bulk_write_raw = snd_soc_hw_bulk_write_raw; config.reg_bits = addr_bits; config.val_bits = data_bits; -- cgit v0.10.2 From b012aa619e50d22df0835b64a5dcebc221fb8053 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 31 Aug 2013 20:31:13 +0200 Subject: ASoC: Remove reg_def_copy reg_def_copy was introduced in commit 3335ddca ("ASoC: soc-cache: Use reg_def_copy instead of reg_cache_default") to keep a copy of the register defaults around in case the register defaults where placed in the __devinitdata section. With the __devinitdata section gone we effectivly keep the same data around twice. This patch removes reg_def_copy and uses reg_cache_default directly instead. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index 3f7de6f..62f320f 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -687,7 +687,6 @@ struct snd_soc_codec { unsigned int (*read)(struct snd_soc_codec *, unsigned int); int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); void *reg_cache; - const void *reg_def_copy; const struct snd_soc_cache_ops *cache_ops; struct mutex cache_rw_mutex; int val_bytes; diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index eaa898f..a7f83c0 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -78,8 +78,8 @@ static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec) ret = snd_soc_cache_read(codec, i, &val); if (ret) return ret; - if (codec->reg_def_copy) - if (snd_soc_get_cache_val(codec->reg_def_copy, + if (codec_drv->reg_cache_default) + if (snd_soc_get_cache_val(codec_drv->reg_cache_default, i, codec_drv->reg_word_size) == val) continue; @@ -121,8 +121,10 @@ static int snd_soc_flat_cache_exit(struct snd_soc_codec *codec) static int snd_soc_flat_cache_init(struct snd_soc_codec *codec) { - if (codec->reg_def_copy) - codec->reg_cache = kmemdup(codec->reg_def_copy, + const struct snd_soc_codec_driver *codec_drv = codec->driver; + + if (codec_drv->reg_cache_default) + codec->reg_cache = kmemdup(codec_drv->reg_cache_default, codec->reg_size, GFP_KERNEL); else codec->reg_cache = kzalloc(codec->reg_size, GFP_KERNEL); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4ce02e6..bbe833a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4201,20 +4201,6 @@ int snd_soc_register_codec(struct device *dev, if (codec_drv->reg_cache_size && codec_drv->reg_word_size) { reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; codec->reg_size = reg_size; - /* it is necessary to make a copy of the default register cache - * because in the case of using a compression type that requires - * the default register cache to be marked as the - * kernel might have freed the array by the time we initialize - * the cache. - */ - if (codec_drv->reg_cache_default) { - codec->reg_def_copy = kmemdup(codec_drv->reg_cache_default, - reg_size, GFP_KERNEL); - if (!codec->reg_def_copy) { - ret = -ENOMEM; - goto fail_codec_name; - } - } } for (i = 0; i < num_dai; i++) { @@ -4273,7 +4259,6 @@ found: dev_dbg(codec->dev, "ASoC: Unregistered codec '%s'\n", codec->name); snd_soc_cache_exit(codec); - kfree(codec->reg_def_copy); kfree(codec->name); kfree(codec); } -- cgit v0.10.2 From a94ed23436fb28bdcdd66e7fcf68ca5f7967e456 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 31 Aug 2013 20:31:14 +0200 Subject: ASoC: Remove 'reg_size' field from snd_soc_codec struct The reg_size field is calculated in snd_soc_register_codec() and then used exactly once in snd_soc_flat_cache_init(). Since it is calculated based on other fields from the codec struct just move the calculation to snd_soc_flat_cache_init() and remove the 'reg_size' field from the codec struct. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index 62f320f..5772126 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -662,7 +662,6 @@ struct snd_soc_codec { struct list_head card_list; int num_dai; enum snd_soc_compress_type compress_type; - size_t reg_size; /* reg_cache_size * reg_word_size */ int (*volatile_register)(struct snd_soc_codec *, unsigned int); int (*readable_register)(struct snd_soc_codec *, unsigned int); int (*writable_register)(struct snd_soc_codec *, unsigned int); diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index a7f83c0..9542c83 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -122,12 +122,15 @@ static int snd_soc_flat_cache_exit(struct snd_soc_codec *codec) static int snd_soc_flat_cache_init(struct snd_soc_codec *codec) { const struct snd_soc_codec_driver *codec_drv = codec->driver; + size_t reg_size; + + reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; if (codec_drv->reg_cache_default) codec->reg_cache = kmemdup(codec_drv->reg_cache_default, - codec->reg_size, GFP_KERNEL); + reg_size, GFP_KERNEL); else - codec->reg_cache = kzalloc(codec->reg_size, GFP_KERNEL); + codec->reg_cache = kzalloc(reg_size, GFP_KERNEL); if (!codec->reg_cache) return -ENOMEM; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index bbe833a..af96484 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4159,7 +4159,6 @@ int snd_soc_register_codec(struct device *dev, struct snd_soc_dai_driver *dai_drv, int num_dai) { - size_t reg_size; struct snd_soc_codec *codec; int ret, i; @@ -4197,12 +4196,6 @@ int snd_soc_register_codec(struct device *dev, codec->num_dai = num_dai; mutex_init(&codec->mutex); - /* allocate CODEC register cache */ - if (codec_drv->reg_cache_size && codec_drv->reg_word_size) { - reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; - codec->reg_size = reg_size; - } - for (i = 0; i < num_dai; i++) { fixup_codec_formats(&dai_drv[i].playback); fixup_codec_formats(&dai_drv[i].capture); -- cgit v0.10.2 From f90fb3f778042b0b9f9aa1fd48cb76047a25eac0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 31 Aug 2013 20:31:15 +0200 Subject: ASoC: Remove infrastructure for supporting multiple cache types The only cache type left is the flat cache and new other cache types won't be added since new drivers are supposed to use regmap directly for IO and caching. This patch removes the snd_soc_cache_ops indirection that was added to support multiple cache types and modifies the code to always use the flat cache directly. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index 5772126..a72af63 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -330,7 +330,6 @@ struct soc_enum; struct snd_soc_jack; struct snd_soc_jack_zone; struct snd_soc_jack_pin; -struct snd_soc_cache_ops; #include #include @@ -348,10 +347,6 @@ enum snd_soc_control_type { SND_SOC_REGMAP, }; -enum snd_soc_compress_type { - SND_SOC_FLAT_COMPRESSION = 1, -}; - enum snd_soc_pcm_subclass { SND_SOC_PCM_CLASS_PCM = 0, SND_SOC_PCM_CLASS_BE = 1, @@ -635,19 +630,6 @@ struct snd_soc_compr_ops { int (*trigger)(struct snd_compr_stream *); }; -/* SoC cache ops */ -struct snd_soc_cache_ops { - const char *name; - enum snd_soc_compress_type id; - int (*init)(struct snd_soc_codec *codec); - int (*exit)(struct snd_soc_codec *codec); - int (*read)(struct snd_soc_codec *codec, unsigned int reg, - unsigned int *value); - int (*write)(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value); - int (*sync)(struct snd_soc_codec *codec); -}; - /* SoC Audio Codec device */ struct snd_soc_codec { const char *name; @@ -661,7 +643,6 @@ struct snd_soc_codec { struct list_head list; struct list_head card_list; int num_dai; - enum snd_soc_compress_type compress_type; int (*volatile_register)(struct snd_soc_codec *, unsigned int); int (*readable_register)(struct snd_soc_codec *, unsigned int); int (*writable_register)(struct snd_soc_codec *, unsigned int); @@ -686,7 +667,6 @@ struct snd_soc_codec { unsigned int (*read)(struct snd_soc_codec *, unsigned int); int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); void *reg_cache; - const struct snd_soc_cache_ops *cache_ops; struct mutex cache_rw_mutex; int val_bytes; @@ -735,7 +715,6 @@ struct snd_soc_codec_driver { short reg_cache_step; short reg_word_size; const void *reg_cache_default; - enum snd_soc_compress_type compress_type; /* codec bias level */ int (*set_bias_level)(struct snd_soc_codec *, @@ -917,12 +896,6 @@ struct snd_soc_codec_conf { * associated per device */ const char *name_prefix; - - /* - * set this to the desired compression type if you want to - * override the one supplied in codec->driver->compress_type - */ - enum snd_soc_compress_type compress_type; }; struct snd_soc_aux_dev { diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index 9542c83..1b6663f 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -11,12 +11,9 @@ * option) any later version. */ -#include -#include #include -#include -#include #include +#include #include @@ -66,66 +63,18 @@ static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx, return -1; } -static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec) -{ - int i; - int ret; - const struct snd_soc_codec_driver *codec_drv; - unsigned int val; - - codec_drv = codec->driver; - for (i = 0; i < codec_drv->reg_cache_size; ++i) { - ret = snd_soc_cache_read(codec, i, &val); - if (ret) - return ret; - if (codec_drv->reg_cache_default) - if (snd_soc_get_cache_val(codec_drv->reg_cache_default, - i, codec_drv->reg_word_size) == val) - continue; - - WARN_ON(!snd_soc_codec_writable_register(codec, i)); - - ret = snd_soc_write(codec, i, val); - if (ret) - return ret; - dev_dbg(codec->dev, "ASoC: Synced register %#x, value = %#x\n", - i, val); - } - return 0; -} - -static int snd_soc_flat_cache_write(struct snd_soc_codec *codec, - unsigned int reg, unsigned int value) -{ - snd_soc_set_cache_val(codec->reg_cache, reg, value, - codec->driver->reg_word_size); - return 0; -} - -static int snd_soc_flat_cache_read(struct snd_soc_codec *codec, - unsigned int reg, unsigned int *value) -{ - *value = snd_soc_get_cache_val(codec->reg_cache, reg, - codec->driver->reg_word_size); - return 0; -} - -static int snd_soc_flat_cache_exit(struct snd_soc_codec *codec) -{ - if (!codec->reg_cache) - return 0; - kfree(codec->reg_cache); - codec->reg_cache = NULL; - return 0; -} - -static int snd_soc_flat_cache_init(struct snd_soc_codec *codec) +int snd_soc_cache_init(struct snd_soc_codec *codec) { const struct snd_soc_codec_driver *codec_drv = codec->driver; size_t reg_size; reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; + mutex_init(&codec->cache_rw_mutex); + + dev_dbg(codec->dev, "ASoC: Initializing cache for %s codec\n", + codec->name); + if (codec_drv->reg_cache_default) codec->reg_cache = kmemdup(codec_drv->reg_cache_default, reg_size, GFP_KERNEL); @@ -137,60 +86,19 @@ static int snd_soc_flat_cache_init(struct snd_soc_codec *codec) return 0; } -/* an array of all supported compression types */ -static const struct snd_soc_cache_ops cache_types[] = { - /* Flat *must* be the first entry for fallback */ - { - .id = SND_SOC_FLAT_COMPRESSION, - .name = "flat", - .init = snd_soc_flat_cache_init, - .exit = snd_soc_flat_cache_exit, - .read = snd_soc_flat_cache_read, - .write = snd_soc_flat_cache_write, - .sync = snd_soc_flat_cache_sync - }, -}; - -int snd_soc_cache_init(struct snd_soc_codec *codec) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(cache_types); ++i) - if (cache_types[i].id == codec->compress_type) - break; - - /* Fall back to flat compression */ - if (i == ARRAY_SIZE(cache_types)) { - dev_warn(codec->dev, "ASoC: Could not match compress type: %d\n", - codec->compress_type); - i = 0; - } - - mutex_init(&codec->cache_rw_mutex); - codec->cache_ops = &cache_types[i]; - - if (codec->cache_ops->init) { - if (codec->cache_ops->name) - dev_dbg(codec->dev, "ASoC: Initializing %s cache for %s codec\n", - codec->cache_ops->name, codec->name); - return codec->cache_ops->init(codec); - } - return -ENOSYS; -} - /* * NOTE: keep in mind that this function might be called * multiple times. */ int snd_soc_cache_exit(struct snd_soc_codec *codec) { - if (codec->cache_ops && codec->cache_ops->exit) { - if (codec->cache_ops->name) - dev_dbg(codec->dev, "ASoC: Destroying %s cache for %s codec\n", - codec->cache_ops->name, codec->name); - return codec->cache_ops->exit(codec); - } - return -ENOSYS; + dev_dbg(codec->dev, "ASoC: Destroying cache for %s codec\n", + codec->name); + if (!codec->reg_cache) + return 0; + kfree(codec->reg_cache); + codec->reg_cache = NULL; + return 0; } /** @@ -203,18 +111,15 @@ int snd_soc_cache_exit(struct snd_soc_codec *codec) int snd_soc_cache_read(struct snd_soc_codec *codec, unsigned int reg, unsigned int *value) { - int ret; + if (!value) + return -EINVAL; mutex_lock(&codec->cache_rw_mutex); - - if (value && codec->cache_ops && codec->cache_ops->read) { - ret = codec->cache_ops->read(codec, reg, value); - mutex_unlock(&codec->cache_rw_mutex); - return ret; - } - + *value = snd_soc_get_cache_val(codec->reg_cache, reg, + codec->driver->reg_word_size); mutex_unlock(&codec->cache_rw_mutex); - return -ENOSYS; + + return 0; } EXPORT_SYMBOL_GPL(snd_soc_cache_read); @@ -228,20 +133,42 @@ EXPORT_SYMBOL_GPL(snd_soc_cache_read); int snd_soc_cache_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { + mutex_lock(&codec->cache_rw_mutex); + snd_soc_set_cache_val(codec->reg_cache, reg, value, + codec->driver->reg_word_size); + mutex_unlock(&codec->cache_rw_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_cache_write); + +static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec) +{ + int i; int ret; + const struct snd_soc_codec_driver *codec_drv; + unsigned int val; - mutex_lock(&codec->cache_rw_mutex); + codec_drv = codec->driver; + for (i = 0; i < codec_drv->reg_cache_size; ++i) { + ret = snd_soc_cache_read(codec, i, &val); + if (ret) + return ret; + if (codec_drv->reg_cache_default) + if (snd_soc_get_cache_val(codec_drv->reg_cache_default, + i, codec_drv->reg_word_size) == val) + continue; - if (codec->cache_ops && codec->cache_ops->write) { - ret = codec->cache_ops->write(codec, reg, value); - mutex_unlock(&codec->cache_rw_mutex); - return ret; - } + WARN_ON(!snd_soc_codec_writable_register(codec, i)); - mutex_unlock(&codec->cache_rw_mutex); - return -ENOSYS; + ret = snd_soc_write(codec, i, val); + if (ret) + return ret; + dev_dbg(codec->dev, "ASoC: Synced register %#x, value = %#x\n", + i, val); + } + return 0; } -EXPORT_SYMBOL_GPL(snd_soc_cache_write); /** * snd_soc_cache_sync: Sync the register cache with the hardware. @@ -254,26 +181,16 @@ EXPORT_SYMBOL_GPL(snd_soc_cache_write); */ int snd_soc_cache_sync(struct snd_soc_codec *codec) { + const char *name = "flat"; int ret; - const char *name; - if (!codec->cache_sync) { + if (!codec->cache_sync) return 0; - } - - if (!codec->cache_ops || !codec->cache_ops->sync) - return -ENOSYS; - - if (codec->cache_ops->name) - name = codec->cache_ops->name; - else - name = "unknown"; - if (codec->cache_ops->name) - dev_dbg(codec->dev, "ASoC: Syncing %s cache for %s codec\n", - codec->cache_ops->name, codec->name); + dev_dbg(codec->dev, "ASoC: Syncing cache for %s codec\n", + codec->name); trace_snd_soc_cache_sync(codec, name, "start"); - ret = codec->cache_ops->sync(codec); + ret = snd_soc_flat_cache_sync(codec); if (!ret) codec->cache_sync = 0; trace_snd_soc_cache_sync(codec, name, "end"); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index af96484..16a3930 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1590,17 +1590,13 @@ static void soc_remove_aux_dev(struct snd_soc_card *card, int num) soc_remove_codec(codec); } -static int snd_soc_init_codec_cache(struct snd_soc_codec *codec, - enum snd_soc_compress_type compress_type) +static int snd_soc_init_codec_cache(struct snd_soc_codec *codec) { int ret; if (codec->cache_init) return 0; - /* override the compress_type if necessary */ - if (compress_type && codec->compress_type != compress_type) - codec->compress_type = compress_type; ret = snd_soc_cache_init(codec); if (ret < 0) { dev_err(codec->dev, @@ -1615,8 +1611,6 @@ static int snd_soc_init_codec_cache(struct snd_soc_codec *codec, static int snd_soc_instantiate_card(struct snd_soc_card *card) { struct snd_soc_codec *codec; - struct snd_soc_codec_conf *codec_conf; - enum snd_soc_compress_type compress_type; struct snd_soc_dai_link *dai_link; int ret, i, order, dai_fmt; @@ -1640,19 +1634,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) list_for_each_entry(codec, &codec_list, list) { if (codec->cache_init) continue; - /* by default we don't override the compress_type */ - compress_type = 0; - /* check to see if we need to override the compress_type */ - for (i = 0; i < card->num_configs; ++i) { - codec_conf = &card->codec_conf[i]; - if (!strcmp(codec->name, codec_conf->dev_name)) { - compress_type = codec_conf->compress_type; - if (compress_type && compress_type - != codec->compress_type) - break; - } - } - ret = snd_soc_init_codec_cache(codec, compress_type); + ret = snd_soc_init_codec_cache(codec); if (ret < 0) goto base_error; } @@ -4175,11 +4157,6 @@ int snd_soc_register_codec(struct device *dev, goto fail_codec; } - if (codec_drv->compress_type) - codec->compress_type = codec_drv->compress_type; - else - codec->compress_type = SND_SOC_FLAT_COMPRESSION; - codec->write = codec_drv->write; codec->read = codec_drv->read; codec->volatile_register = codec_drv->volatile_register; -- cgit v0.10.2 From 56fb7421d23c63cf22ac885d2db2302cefc9e1f1 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 13 Sep 2013 18:09:46 +0100 Subject: ASoC: trace: Make sure trace header doesnt depend on any headers Fix build so that asoc trace event header doesn't depend on other headers. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/include/trace/events/asoc.h b/include/trace/events/asoc.h index 5fc2dcd..03996b2 100644 --- a/include/trace/events/asoc.h +++ b/include/trace/events/asoc.h @@ -14,6 +14,7 @@ struct snd_soc_codec; struct snd_soc_platform; struct snd_soc_card; struct snd_soc_dapm_widget; +struct snd_soc_dapm_path; /* * Log register events -- cgit v0.10.2 From 7f05cc98bd9b10b9a0173f3f5d20c2223fdf7470 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 13 Sep 2013 18:09:47 +0100 Subject: ASoC: core utils: Dont set DMA params for BE substreams BE substreams dont require dummy DMA configs so dont set any. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index 29b211e..5e63365 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -75,7 +75,11 @@ static const struct snd_pcm_hardware dummy_dma_hardware = { static int dummy_dma_open(struct snd_pcm_substream *substream) { - snd_soc_set_runtime_hwparams(substream, &dummy_dma_hardware); + struct snd_soc_pcm_runtime *rtd = substream->private_data; + + /* BE's dont need dummy params */ + if (!rtd->dai_link->no_pcm) + snd_soc_set_runtime_hwparams(substream, &dummy_dma_hardware); return 0; } -- cgit v0.10.2 From 8ecb5344fd6409c500c9d5757c3a7130d3d7db5b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 31 Aug 2013 13:51:38 +0100 Subject: ASoC: cq93vc: Don't use control data for core driver data The platform data is being used to obtain the core driver data for the device (which is a bit of an abuse but not the issue at hand) so reference it directly in order to support refactoring to use regmap. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c index 23316c8..e2c4c0a8 100644 --- a/sound/soc/codecs/cq93vc.c +++ b/sound/soc/codecs/cq93vc.c @@ -79,7 +79,7 @@ static int cq93vc_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) { struct snd_soc_codec *codec = codec_dai->codec; - struct davinci_vc *davinci_vc = codec->control_data; + struct davinci_vc *davinci_vc = codec->dev->platform_data; switch (freq) { case 22579200: -- cgit v0.10.2 From a851a2bb2d746ccdec0c7cc6ed1c9774921e721e Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 13 Sep 2013 15:22:17 +0530 Subject: ASoC: fsl_ssi: Staticize local symbols Local symbols used only in this file are made static. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index c6b7439..4973be7 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -748,7 +748,7 @@ static void fsl_ssi_ac97_init(void) fsl_ssi_setup(fsl_ac97_data); } -void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, +static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val) { struct ccsr_ssi *ssi = fsl_ac97_data->ssi; @@ -770,7 +770,7 @@ void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, udelay(100); } -unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, +static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, unsigned short reg) { struct ccsr_ssi *ssi = fsl_ac97_data->ssi; -- cgit v0.10.2 From b51600c01979ab1d1c4df17e8910696547ffb9a2 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 31 Aug 2013 13:43:36 +0100 Subject: ASoC: kirkwood-dma: remove IEC958_SUBFRAME formats The Audio block does not support IEC958 subframes as formatted by ALSA: they're very close, but not close enough. The formats differ by: 3 2 2 2 1 1 1 8 4 0 6 2 8 4 0 PCUVDDDDDDDDDDDDDDDD....AAAATTTT - IEC958 subframe PCUV0000........DDDDDDDDDDDDDDDD - Audio block format Where P = parity, C = channel status, U = user data, V = validity, D = sample data, A = aux, T = preamble. As can be seen, the position of the sample is in a different position, and the audio block does not have the aux or preamble bits. Signed-off-by: Russell King Signed-off-by: Mark Brown diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c index b238434..0c85c4e 100644 --- a/sound/soc/kirkwood/kirkwood-dma.c +++ b/sound/soc/kirkwood/kirkwood-dma.c @@ -29,9 +29,7 @@ #define KIRKWOOD_FORMATS \ (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S24_LE | \ - SNDRV_PCM_FMTBIT_S32_LE | \ - SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE | \ - SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE) + SNDRV_PCM_FMTBIT_S32_LE) static struct kirkwood_dma_data *kirkwood_priv(struct snd_pcm_substream *subs) { -- cgit v0.10.2 From 38f2b8cbfb1ef517af8af5a63bdff073b7b078fd Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 2 Sep 2013 17:56:18 -0300 Subject: ASoC: mxs: mxs-sgtl5000: Simplify probe function mxs is a device tree only platform, which allows us to simplify a bit mxs_sgtl5000_probe(), because there is no need to check whether device tree is supported or not. Remove mxs_sgtl5000_probe_dt() and place its content inside mxs_sgtl5000_probe() for making the code simpler. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index 4bb2737..61822cc 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -122,14 +122,12 @@ static struct snd_soc_card mxs_sgtl5000 = { .num_links = ARRAY_SIZE(mxs_sgtl5000_dai), }; -static int mxs_sgtl5000_probe_dt(struct platform_device *pdev) +static int mxs_sgtl5000_probe(struct platform_device *pdev) { + struct snd_soc_card *card = &mxs_sgtl5000; + int ret, i; struct device_node *np = pdev->dev.of_node; struct device_node *saif_np[2], *codec_np; - int i; - - if (!np) - return 1; /* no device tree */ saif_np[0] = of_parse_phandle(np, "saif-controllers", 0); saif_np[1] = of_parse_phandle(np, "saif-controllers", 1); @@ -152,18 +150,6 @@ static int mxs_sgtl5000_probe_dt(struct platform_device *pdev) of_node_put(saif_np[0]); of_node_put(saif_np[1]); - return 0; -} - -static int mxs_sgtl5000_probe(struct platform_device *pdev) -{ - struct snd_soc_card *card = &mxs_sgtl5000; - int ret; - - ret = mxs_sgtl5000_probe_dt(pdev); - if (ret < 0) - return ret; - /* * Set an init clock(11.28Mhz) for sgtl5000 initialization(i2c r/w). * The Sgtl5000 sysclk is derived from saif0 mclk and it's range -- cgit v0.10.2 From 89d051300b845e1001a9d9e9ce94da4250c21613 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 13 Sep 2013 15:22:18 +0530 Subject: ASoC: rt5640: Staticize hp_amp_power_on 'hp_amp_power_on' is used only in this file. Make it static. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index c26a8f8..2f6bb16 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -926,7 +926,7 @@ static int rt5640_set_dmic2_event(struct snd_soc_dapm_widget *w, return 0; } -void hp_amp_power_on(struct snd_soc_codec *codec) +static void hp_amp_power_on(struct snd_soc_codec *codec) { struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); -- cgit v0.10.2 From 9e9cb9b99615180b94d743f1d6ca0f82539c8754 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 13 Sep 2013 17:57:35 +0100 Subject: ASoC: rt5640: Provide more useful hw_params error reasons. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 2f6bb16..de40bd9 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -1609,7 +1609,8 @@ static int rt5640_hw_params(struct snd_pcm_substream *substream, rt5640->lrck[dai->id] = params_rate(params); pre_div = get_clk_info(rt5640->sysclk, rt5640->lrck[dai->id]); if (pre_div < 0) { - dev_err(codec->dev, "Unsupported clock setting\n"); + dev_err(codec->dev, "Unsupported clock setting %d for DAI %d\n", + rt5640->lrck[dai->id], dai->id); return -EINVAL; } frame_size = snd_soc_params_to_frame_size(params); -- cgit v0.10.2 From 02b80773de3732dae11c1cf0c1ce40378901bd0e Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 13 Sep 2013 17:57:36 +0100 Subject: ASoC: rt5640: Add ACPI probing support. Allow the RT5640 to be probed as an ACPI I2C device. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index de40bd9..0bfb960 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -2081,6 +2082,12 @@ static const struct i2c_device_id rt5640_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, rt5640_i2c_id); +static struct acpi_device_id rt5640_acpi_match[] = { + { "INT33CA", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, rt5640_acpi_match); + static int rt5640_parse_dt(struct rt5640_priv *rt5640, struct device_node *np) { rt5640->pdata.in1_diff = of_property_read_bool(np, @@ -2200,6 +2207,7 @@ static struct i2c_driver rt5640_i2c_driver = { .driver = { .name = "rt5640", .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(rt5640_acpi_match), }, .probe = rt5640_i2c_probe, .remove = rt5640_i2c_remove, -- cgit v0.10.2 From 37c83edf9afd3d7b39ace9113a166c03b7a2820f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 31 Aug 2013 13:17:08 +0100 Subject: ASoC: wm8400: Use supplies to manage input power Rather than using a fake register to manage input power create some supply widgets and use those. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c index d2a0928..95c33d1 100644 --- a/sound/soc/codecs/wm8400.c +++ b/sound/soc/codecs/wm8400.c @@ -32,13 +32,6 @@ #include "wm8400.h" -/* Fake register for internal state */ -#define WM8400_INTDRIVBITS (WM8400_REGISTER_COUNT + 1) -#define WM8400_INMIXL_PWR 0 -#define WM8400_AINLMUX_PWR 1 -#define WM8400_INMIXR_PWR 2 -#define WM8400_AINRMUX_PWR 3 - static struct regulator_bulk_data power[] = { { .supply = "I2S1VDD", @@ -79,10 +72,7 @@ static inline unsigned int wm8400_read(struct snd_soc_codec *codec, { struct wm8400_priv *wm8400 = snd_soc_codec_get_drvdata(codec); - if (reg == WM8400_INTDRIVBITS) - return wm8400->fake_register; - else - return wm8400_reg_read(wm8400->wm8400, reg); + return wm8400_reg_read(wm8400->wm8400, reg); } /* @@ -93,11 +83,7 @@ static int wm8400_write(struct snd_soc_codec *codec, unsigned int reg, { struct wm8400_priv *wm8400 = snd_soc_codec_get_drvdata(codec); - if (reg == WM8400_INTDRIVBITS) { - wm8400->fake_register = value; - return 0; - } else - return wm8400_set_bits(wm8400->wm8400, reg, 0xffff, value); + return wm8400_set_bits(wm8400->wm8400, reg, 0xffff, value); } static void wm8400_codec_reset(struct snd_soc_codec *codec) @@ -352,32 +338,6 @@ SOC_SINGLE("RIN34 Mute Switch", WM8400_RIGHT_LINE_INPUT_3_4_VOLUME, * _DAPM_ Controls */ -static int inmixer_event (struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - u16 reg, fakepower; - - reg = snd_soc_read(w->codec, WM8400_POWER_MANAGEMENT_2); - fakepower = snd_soc_read(w->codec, WM8400_INTDRIVBITS); - - if (fakepower & ((1 << WM8400_INMIXL_PWR) | - (1 << WM8400_AINLMUX_PWR))) { - reg |= WM8400_AINL_ENA; - } else { - reg &= ~WM8400_AINL_ENA; - } - - if (fakepower & ((1 << WM8400_INMIXR_PWR) | - (1 << WM8400_AINRMUX_PWR))) { - reg |= WM8400_AINR_ENA; - } else { - reg &= ~WM8400_AINR_ENA; - } - snd_soc_write(w->codec, WM8400_POWER_MANAGEMENT_2, reg); - - return 0; -} - static int outmixer_event (struct snd_soc_dapm_widget *w, struct snd_kcontrol * kcontrol, int event) { @@ -658,27 +618,26 @@ SND_SOC_DAPM_MIXER("RIN34 PGA", WM8400_POWER_MANAGEMENT_2, 0, &wm8400_dapm_rin34_pga_controls[0], ARRAY_SIZE(wm8400_dapm_rin34_pga_controls)), +SND_SOC_DAPM_SUPPLY("INL", WM8400_POWER_MANAGEMENT_2, WM8400_AINL_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("INR", WM8400_POWER_MANAGEMENT_2, WM8400_AINR_ENA_SHIFT, + 0, NULL, 0), + /* INMIXL */ -SND_SOC_DAPM_MIXER_E("INMIXL", WM8400_INTDRIVBITS, WM8400_INMIXL_PWR, 0, +SND_SOC_DAPM_MIXER("INMIXL", SND_SOC_NOPM, 0, 0, &wm8400_dapm_inmixl_controls[0], - ARRAY_SIZE(wm8400_dapm_inmixl_controls), - inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + ARRAY_SIZE(wm8400_dapm_inmixl_controls)), /* AINLMUX */ -SND_SOC_DAPM_MUX_E("AILNMUX", WM8400_INTDRIVBITS, WM8400_AINLMUX_PWR, 0, - &wm8400_dapm_ainlmux_controls, inmixer_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_MUX("AILNMUX", SND_SOC_NOPM, 0, 0, &wm8400_dapm_ainlmux_controls), /* INMIXR */ -SND_SOC_DAPM_MIXER_E("INMIXR", WM8400_INTDRIVBITS, WM8400_INMIXR_PWR, 0, +SND_SOC_DAPM_MIXER("INMIXR", SND_SOC_NOPM, 0, 0, &wm8400_dapm_inmixr_controls[0], - ARRAY_SIZE(wm8400_dapm_inmixr_controls), - inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + ARRAY_SIZE(wm8400_dapm_inmixr_controls)), /* AINRMUX */ -SND_SOC_DAPM_MUX_E("AIRNMUX", WM8400_INTDRIVBITS, WM8400_AINRMUX_PWR, 0, - &wm8400_dapm_ainrmux_controls, inmixer_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_MUX("AIRNMUX", SND_SOC_NOPM, 0, 0, &wm8400_dapm_ainrmux_controls), /* Output Side */ /* DACs */ @@ -789,11 +748,13 @@ static const struct snd_soc_dapm_route wm8400_dapm_routes[] = { {"LIN34 PGA", "LIN3 Switch", "LIN3"}, {"LIN34 PGA", "LIN4 Switch", "LIN4/RXN"}, /* INMIXL */ + {"INMIXL", NULL, "INL"}, {"INMIXL", "Record Left Volume", "LOMIX"}, {"INMIXL", "LIN2 Volume", "LIN2"}, {"INMIXL", "LINPGA12 Switch", "LIN12 PGA"}, {"INMIXL", "LINPGA34 Switch", "LIN34 PGA"}, /* AILNMUX */ + {"AILNMUX", NULL, "INL"}, {"AILNMUX", "INMIXL Mix", "INMIXL"}, {"AILNMUX", "DIFFINL Mix", "LIN12 PGA"}, {"AILNMUX", "DIFFINL Mix", "LIN34 PGA"}, @@ -808,12 +769,14 @@ static const struct snd_soc_dapm_route wm8400_dapm_routes[] = { /* RIN34 PGA */ {"RIN34 PGA", "RIN3 Switch", "RIN3"}, {"RIN34 PGA", "RIN4 Switch", "RIN4/RXP"}, - /* INMIXL */ + /* INMIXR */ + {"INMIXR", NULL, "INR"}, {"INMIXR", "Record Right Volume", "ROMIX"}, {"INMIXR", "RIN2 Volume", "RIN2"}, {"INMIXR", "RINPGA12 Switch", "RIN12 PGA"}, {"INMIXR", "RINPGA34 Switch", "RIN34 PGA"}, /* AIRNMUX */ + {"AIRNMUX", NULL, "INR"}, {"AIRNMUX", "INMIXR Mix", "INMIXR"}, {"AIRNMUX", "DIFFINR Mix", "RIN12 PGA"}, {"AIRNMUX", "DIFFINR Mix", "RIN34 PGA"}, -- cgit v0.10.2 From b8cc4151f8af97e1b573ca399a77f439f401a57e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 31 Aug 2013 13:21:12 +0100 Subject: ASoC: wm8400: Use regmap for I/O Since we no longer have a fake register to simulate we can use the framework for I/O. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c index 95c33d1..48dc7d2 100644 --- a/sound/soc/codecs/wm8400.c +++ b/sound/soc/codecs/wm8400.c @@ -67,25 +67,6 @@ struct wm8400_priv { int fll_in, fll_out; }; -static inline unsigned int wm8400_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - struct wm8400_priv *wm8400 = snd_soc_codec_get_drvdata(codec); - - return wm8400_reg_read(wm8400->wm8400, reg); -} - -/* - * write to the wm8400 register space - */ -static int wm8400_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - struct wm8400_priv *wm8400 = snd_soc_codec_get_drvdata(codec); - - return wm8400_set_bits(wm8400->wm8400, reg, 0xffff, value); -} - static void wm8400_codec_reset(struct snd_soc_codec *codec) { struct wm8400_priv *wm8400 = snd_soc_codec_get_drvdata(codec); @@ -1328,9 +1309,12 @@ static int wm8400_codec_probe(struct snd_soc_codec *codec) return -ENOMEM; snd_soc_codec_set_drvdata(codec, priv); - codec->control_data = priv->wm8400 = wm8400; + priv->wm8400 = wm8400; + codec->control_data = wm8400->regmap; priv->codec = codec; + snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_REGMAP); + ret = devm_regulator_bulk_get(wm8400->dev, ARRAY_SIZE(power), &power[0]); if (ret != 0) { @@ -1377,8 +1361,6 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8400 = { .remove = wm8400_codec_remove, .suspend = wm8400_suspend, .resume = wm8400_resume, - .read = snd_soc_read, - .write = wm8400_write, .set_bias_level = wm8400_set_bias_level, .controls = wm8400_snd_controls, -- cgit v0.10.2 From a0ff6ea24f785ec58bccdbce7b366661c57e3591 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 11 Sep 2013 15:27:29 +0100 Subject: ASoC: samsung: Allow mono in i2s driver Signed-off-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index b302f3b..a7e3519 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1060,7 +1060,7 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec) i2s->i2s_dai_drv.ops = &samsung_i2s_dai_ops; i2s->i2s_dai_drv.suspend = i2s_suspend; i2s->i2s_dai_drv.resume = i2s_resume; - i2s->i2s_dai_drv.playback.channels_min = 2; + i2s->i2s_dai_drv.playback.channels_min = 1; i2s->i2s_dai_drv.playback.channels_max = 2; i2s->i2s_dai_drv.playback.rates = SAMSUNG_I2S_RATES; i2s->i2s_dai_drv.playback.formats = SAMSUNG_I2S_FMTS; -- cgit v0.10.2 From b3a6006e1d106fddcfc121d0ccfa9b7faeeb8f3e Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 16 Sep 2013 15:38:15 +0100 Subject: ASoC: bells: Add missing route to power up DSP clock Signed-off-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/bells.c b/sound/soc/samsung/bells.c index 29e2468..84f5d8b 100644 --- a/sound/soc/samsung/bells.c +++ b/sound/soc/samsung/bells.c @@ -356,6 +356,7 @@ static struct snd_soc_dapm_widget bells_widgets[] = { static struct snd_soc_dapm_route bells_routes[] = { { "Sub CLK_SYS", NULL, "OPCLK" }, + { "CLKIN", NULL, "OPCLK" }, { "DMIC", NULL, "MICBIAS2" }, { "IN2L", NULL, "DMIC" }, -- cgit v0.10.2 From 49c60547daebaa79e8de9d2dff6dee994576c94c Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 16 Sep 2013 15:34:35 +0100 Subject: ASoC: arizona: Improve handling of setting REFCLK to 0 This patch suppresses calculation of REFCLK parameters when the REFCLK source frequency is set to zero, additionally it will consider a source frequency of zero as the REFCLK being disabled and switch to using the SYNCCLK. Reported-by: Kyung Kwee Ryu Signed-off-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 657808b..6f05b17 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1477,21 +1477,25 @@ static void arizona_enable_fll(struct arizona_fll *fll, { struct arizona *arizona = fll->arizona; int ret; + bool use_sync = false; /* * If we have both REFCLK and SYNCCLK then enable both, * otherwise apply the SYNCCLK settings to REFCLK. */ - if (fll->ref_src >= 0 && fll->ref_src != fll->sync_src) { + if (fll->ref_src >= 0 && fll->ref_freq && + fll->ref_src != fll->sync_src) { regmap_update_bits(arizona->regmap, fll->base + 5, ARIZONA_FLL1_OUTDIV_MASK, ref->outdiv << ARIZONA_FLL1_OUTDIV_SHIFT); arizona_apply_fll(arizona, fll->base, ref, fll->ref_src, false); - if (fll->sync_src >= 0) + if (fll->sync_src >= 0) { arizona_apply_fll(arizona, fll->base + 0x10, sync, fll->sync_src, true); + use_sync = true; + } } else if (fll->sync_src >= 0) { regmap_update_bits(arizona->regmap, fll->base + 5, ARIZONA_FLL1_OUTDIV_MASK, @@ -1511,7 +1515,7 @@ static void arizona_enable_fll(struct arizona_fll *fll, * Increase the bandwidth if we're not using a low frequency * sync source. */ - if (fll->sync_src >= 0 && fll->sync_freq > 100000) + if (use_sync && fll->sync_freq > 100000) regmap_update_bits(arizona->regmap, fll->base + 0x17, ARIZONA_FLL1_SYNC_BW, 0); else @@ -1526,8 +1530,7 @@ static void arizona_enable_fll(struct arizona_fll *fll, regmap_update_bits(arizona->regmap, fll->base + 1, ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA); - if (fll->ref_src >= 0 && fll->sync_src >= 0 && - fll->ref_src != fll->sync_src) + if (use_sync) regmap_update_bits(arizona->regmap, fll->base + 0x11, ARIZONA_FLL1_SYNC_ENA, ARIZONA_FLL1_SYNC_ENA); @@ -1561,10 +1564,12 @@ int arizona_set_fll_refclk(struct arizona_fll *fll, int source, if (fll->ref_src == source && fll->ref_freq == Fref) return 0; - if (fll->fout && Fref > 0) { - ret = arizona_calc_fll(fll, &ref, Fref, fll->fout); - if (ret != 0) - return ret; + if (fll->fout) { + if (Fref > 0) { + ret = arizona_calc_fll(fll, &ref, Fref, fll->fout); + if (ret != 0) + return ret; + } if (fll->sync_src >= 0) { ret = arizona_calc_fll(fll, &sync, fll->sync_freq, -- cgit v0.10.2 From 193b2f65b87e9da78b15f3e3a0cae1d37fbafa57 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 16 Sep 2013 18:14:20 +0200 Subject: ASoC: ak4104: provide a module device table Provide a module device table for the SPI subsystem, so the driver can be autoloaded by the SPI core. While at it, get rid of an unnecessary #define. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c index 71059c0..b4819dc 100644 --- a/sound/soc/codecs/ak4104.c +++ b/sound/soc/codecs/ak4104.c @@ -45,8 +45,6 @@ #define AK4104_TX_TXE (1 << 0) #define AK4104_TX_V (1 << 1) -#define DRV_NAME "ak4104-codec" - struct ak4104_private { struct regmap *regmap; }; @@ -291,12 +289,19 @@ static const struct of_device_id ak4104_of_match[] = { }; MODULE_DEVICE_TABLE(of, ak4104_of_match); +static const struct spi_device_id ak4104_id_table[] = { + { "ak4104", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ak4104_id_table); + static struct spi_driver ak4104_spi_driver = { .driver = { - .name = DRV_NAME, + .name = "ak4104", .owner = THIS_MODULE, .of_match_table = ak4104_of_match, }, + .id_table = ak4104_id_table, .probe = ak4104_spi_probe, .remove = ak4104_spi_remove, }; -- cgit v0.10.2 From 3615a34ea1a6c1040744449b3da223569f3221b0 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 13 Sep 2013 21:01:15 +0200 Subject: regulator: add STw481x VMMC driver The ST Microelectronics STw481x PMIC used for the Nomadik has one single software-controlled regulator for VMMC. This driver registers directly to the compatible string as there is just one regulator. Signed-off-by: Linus Walleij Signed-off-by: Mark Brown diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index dfe5809..d957010 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -429,6 +429,14 @@ config REGULATOR_TI_ABB on TI SoCs may be unstable without enabling this as it provides device specific optimized bias to allow/optimize functionality. +config REGULATOR_STW481X_VMMC + bool "ST Microelectronics STW481X VMMC regulator" + depends on MFD_STW481X + default y if MFD_STW481X + help + This driver supports the internal VMMC regulator in the STw481x + PMIC chips. + config REGULATOR_TPS51632 tristate "TI TPS51632 Power Regulator" depends on I2C diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 185cce2..f2cfc45 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o obj-$(CONFIG_REGULATOR_RC5T583) += rc5t583-regulator.o obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o +obj-$(CONFIG_REGULATOR_STW481X_VMMC) += stw481x-vmmc.o obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o obj-$(CONFIG_REGULATOR_TPS62360) += tps62360-regulator.o diff --git a/drivers/regulator/stw481x-vmmc.c b/drivers/regulator/stw481x-vmmc.c new file mode 100644 index 0000000..5ae72a8 --- /dev/null +++ b/drivers/regulator/stw481x-vmmc.c @@ -0,0 +1,110 @@ +/* + * Regulator driver for STw4810/STw4811 VMMC regulator. + * + * Copyright (C) 2013 ST-Ericsson SA + * Written on behalf of Linaro for ST-Ericsson + * + * Author: Linus Walleij + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include +#include +#include +#include + +static const unsigned int stw481x_vmmc_voltages[] = { + 1800000, + 1800000, + 2850000, + 3000000, + 1850000, + 2600000, + 2700000, + 3300000, +}; + +static struct regulator_ops stw481x_vmmc_ops = { + .list_voltage = regulator_list_voltage_table, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, +}; + +static struct regulator_desc vmmc_regulator = { + .name = "VMMC", + .id = 0, + .ops = &stw481x_vmmc_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(stw481x_vmmc_voltages), + .volt_table = stw481x_vmmc_voltages, + .enable_time = 200, /* FIXME: look this up */ + .enable_reg = STW_CONF1, + .enable_mask = STW_CONF1_PDN_VMMC, + .vsel_reg = STW_CONF1, + .vsel_mask = STW_CONF1_VMMC_MASK, +}; + +static int stw481x_vmmc_regulator_probe(struct platform_device *pdev) +{ + struct stw481x *stw481x = dev_get_platdata(&pdev->dev); + struct regulator_config config = { }; + int ret; + + /* First disable the external VMMC if it's active */ + ret = regmap_update_bits(stw481x->map, STW_CONF2, + STW_CONF2_VMMC_EXT, 0); + if (ret) { + dev_err(&pdev->dev, "could not disable external VMMC\n"); + return ret; + } + + /* Register VMMC regulator */ + config.dev = &pdev->dev; + config.driver_data = stw481x; + config.regmap = stw481x->map; + config.of_node = pdev->dev.of_node; + config.init_data = of_get_regulator_init_data(&pdev->dev, + pdev->dev.of_node); + + stw481x->vmmc_regulator = regulator_register(&vmmc_regulator, &config); + if (IS_ERR(stw481x->vmmc_regulator)) { + dev_err(&pdev->dev, + "error initializing STw481x VMMC regulator\n"); + return PTR_ERR(stw481x->vmmc_regulator); + } + + dev_info(&pdev->dev, "initialized STw481x VMMC regulator\n"); + return 0; +} + +static int stw481x_vmmc_regulator_remove(struct platform_device *pdev) +{ + struct stw481x *stw481x = dev_get_platdata(&pdev->dev); + + regulator_unregister(stw481x->vmmc_regulator); + return 0; +} + +static const struct of_device_id stw481x_vmmc_match[] = { + { .compatible = "st,stw481x-vmmc", }, + {}, +}; + +static struct platform_driver stw481x_vmmc_regulator_driver = { + .driver = { + .name = "stw481x-vmmc-regulator", + .owner = THIS_MODULE, + }, + .probe = stw481x_vmmc_regulator_probe, + .remove = stw481x_vmmc_regulator_remove, +}; + +module_platform_driver(stw481x_vmmc_regulator_driver); -- cgit v0.10.2 From bf551413038f74343ec4d1413c3610e2362d0aeb Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 13 Sep 2013 16:16:17 +0530 Subject: ASoC: twl6040: Remove redundant semicolon Redundant semicolon removed. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 3c79dbb..35059a2 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -246,7 +246,7 @@ static bool twl6040_is_path_unmuted(struct snd_soc_codec *codec, return priv->dl2_unmuted; default: return 1; - }; + } } /* @@ -1100,7 +1100,7 @@ static void twl6040_mute_path(struct snd_soc_codec *codec, enum twl6040_dai_id i break; default: break; - }; + } } static int twl6040_digital_mute(struct snd_soc_dai *dai, int mute) -- cgit v0.10.2 From a0b03a616b08cf9d709812ff5cf7e9c0958d6807 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 4 Sep 2013 20:37:34 +0100 Subject: ASoC: core: Implement devm_snd_soc_register_component() Since with the wider use of devres many drivers are now only calling snd_soc_unregister_component() in their remove functions providing a managed version will save a reasonable amount of code. Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index d22cb0a..b970f01 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -386,6 +386,9 @@ void snd_soc_unregister_codec(struct device *dev); int snd_soc_register_component(struct device *dev, const struct snd_soc_component_driver *cmpnt_drv, struct snd_soc_dai_driver *dai_drv, int num_dai); +int devm_snd_soc_register_component(struct device *dev, + const struct snd_soc_component_driver *cmpnt_drv, + struct snd_soc_dai_driver *dai_drv, int num_dai); void snd_soc_unregister_component(struct device *dev); int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, unsigned int reg); diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 61a64d2..8b9e701 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -1,5 +1,5 @@ snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o -snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o +snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o soc-devres.o ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),) snd-soc-core-objs += soc-generic-dmaengine-pcm.o diff --git a/sound/soc/soc-devres.c b/sound/soc/soc-devres.c new file mode 100644 index 0000000..13fe86f --- /dev/null +++ b/sound/soc/soc-devres.c @@ -0,0 +1,52 @@ +/* + * soc-devres.c -- ALSA SoC Audio Layer devres functions + * + * Copyright (C) 2013 Linaro Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include + +static void devm_component_release(struct device *dev, void *res) +{ + snd_soc_unregister_component(*(struct device **)res); +} + +/** + * devm_snd_soc_register_component - resource managed component registration + * @dev: Device used to manage component + * @cmpnt_drv: Component driver + * @dai_drv: DAI driver + * @num_dai: Number of DAIs to register + * + * Register a component with automatic unregistration when the device is + * unregistered. + */ +int devm_snd_soc_register_component(struct device *dev, + const struct snd_soc_component_driver *cmpnt_drv, + struct snd_soc_dai_driver *dai_drv, int num_dai) +{ + struct device **ptr; + int ret; + + ptr = devres_alloc(devm_component_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret = snd_soc_register_component(dev, cmpnt_drv, dai_drv, num_dai); + if (ret == 0) { + *ptr = dev; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return ret; +} +EXPORT_SYMBOL_GPL(devm_snd_soc_register_component); -- cgit v0.10.2 From 0e4ff5c806263bf40ee5409ac283b776f0c11e41 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 16 Sep 2013 18:02:05 +0100 Subject: ASoC: core: Add devm_snd_soc_register_card() Simplify error handling and remove repetitive (and rarely executed) code for unregistration by providing a devm_snd_soc_register() card. Signed-off-by: Mark Brown Acked-by: Liam Girdwood diff --git a/include/sound/soc.h b/include/sound/soc.h index b970f01..d44728a 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -369,6 +369,7 @@ int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source, int snd_soc_register_card(struct snd_soc_card *card); int snd_soc_unregister_card(struct snd_soc_card *card); +int devm_snd_soc_register_card(struct device *dev, struct snd_soc_card *card); int snd_soc_suspend(struct device *dev); int snd_soc_resume(struct device *dev); int snd_soc_poweroff(struct device *dev); diff --git a/sound/soc/soc-devres.c b/sound/soc/soc-devres.c index 13fe86f..b1d7322 100644 --- a/sound/soc/soc-devres.c +++ b/sound/soc/soc-devres.c @@ -50,3 +50,37 @@ int devm_snd_soc_register_component(struct device *dev, return ret; } EXPORT_SYMBOL_GPL(devm_snd_soc_register_component); + +static void devm_card_release(struct device *dev, void *res) +{ + snd_soc_unregister_card(*(struct snd_soc_card **)res); +} + +/** + * devm_snd_soc_register_card - resource managed card registration + * @dev: Device used to manage card + * @card: Card to register + * + * Register a card with automatic unregistration when the device is + * unregistered. + */ +int devm_snd_soc_register_card(struct device *dev, struct snd_soc_card *card) +{ + struct device **ptr; + int ret; + + ptr = devres_alloc(devm_card_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret = snd_soc_register_card(card); + if (ret == 0) { + *ptr = dev; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return ret; +} +EXPORT_SYMBOL_GPL(devm_snd_soc_register_card); -- cgit v0.10.2 From 9a8e0322f0a8c7506f4ced07ec2a7e7e2a9cbe4a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 16 Sep 2013 18:02:28 +0100 Subject: ASoC: smdk_wm8994: Use devm_snd_soc_unregister_card() Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c index 5fd7a05..831972d 100644 --- a/sound/soc/samsung/smdk_wm8994.c +++ b/sound/soc/samsung/smdk_wm8994.c @@ -193,7 +193,7 @@ static int smdk_audio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, board); - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret); @@ -201,15 +201,6 @@ static int smdk_audio_probe(struct platform_device *pdev) return ret; } -static int smdk_audio_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - - return 0; -} - static struct platform_driver smdk_audio_driver = { .driver = { .name = "smdk-audio-wm8894", @@ -217,7 +208,6 @@ static struct platform_driver smdk_audio_driver = { .of_match_table = of_match_ptr(samsung_wm8994_of_match), }, .probe = smdk_audio_probe, - .remove = smdk_audio_remove, }; module_platform_driver(smdk_audio_driver); -- cgit v0.10.2 From d644a115e86433abbb544808c4be1e4b5a048c2b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 4 Sep 2013 20:37:51 +0100 Subject: ASoC: samsung-i2s: Use devm_snd_soc_register_component() Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index a7e3519..32956df 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1143,9 +1143,9 @@ static int samsung_i2s_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Unable to get drvdata\n"); return -EFAULT; } - snd_soc_register_component(&sec_dai->pdev->dev, - &samsung_i2s_component, - &sec_dai->i2s_dai_drv, 1); + devm_snd_soc_register_component(&sec_dai->pdev->dev, + &samsung_i2s_component, + &sec_dai->i2s_dai_drv, 1); samsung_asoc_dma_platform_register(&pdev->dev); return 0; } @@ -1258,8 +1258,9 @@ static int samsung_i2s_probe(struct platform_device *pdev) goto err; } - snd_soc_register_component(&pri_dai->pdev->dev, &samsung_i2s_component, - &pri_dai->i2s_dai_drv, 1); + devm_snd_soc_register_component(&pri_dai->pdev->dev, + &samsung_i2s_component, + &pri_dai->i2s_dai_drv, 1); pm_runtime_enable(&pdev->dev); @@ -1294,7 +1295,6 @@ static int samsung_i2s_remove(struct platform_device *pdev) i2s->sec_dai = NULL; samsung_asoc_dma_platform_unregister(&pdev->dev); - snd_soc_unregister_component(&pdev->dev); return 0; } -- cgit v0.10.2 From 0feb23d1bdf31db903069d3d94892e56b5c11981 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 13 Sep 2013 15:50:50 +0530 Subject: ASoC: ak4642: Remove redundant break 'break' after return statement is redundant. Remove it. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 2d03787..21c35ed 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -352,7 +352,6 @@ static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) */ default: return -EINVAL; - break; } snd_soc_update_bits(codec, MD_CTL1, DIF_MASK, data); @@ -405,7 +404,6 @@ static int ak4642_dai_hw_params(struct snd_pcm_substream *substream, break; default: return -EINVAL; - break; } snd_soc_update_bits(codec, MD_CTL2, FS_MASK, rate); -- cgit v0.10.2 From e54cf76ba2c9ec071a68e98f2830226c0cac8086 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 16 Sep 2013 13:01:46 +0100 Subject: ASoC: core: Add API for configuration of DAI BCLK ratio Some codec drivers when running in slave mode require that BCLK to sample rate ratio is explicitly set by the machine driver as it may not be exactly rate * frame size. Extend the DAI API by adding :- int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio); Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index ae9a227..d8acf0c 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -105,6 +105,8 @@ int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai, int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, unsigned int freq_in, unsigned int freq_out); +int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio); + /* Digital Audio interface formatting */ int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt); @@ -131,6 +133,7 @@ struct snd_soc_dai_ops { int (*set_pll)(struct snd_soc_dai *dai, int pll_id, int source, unsigned int freq_in, unsigned int freq_out); int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div); + int (*set_bclk_ratio)(struct snd_soc_dai *dai, unsigned int ratio); /* * DAI format configuration diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4d05613..31adad0 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3577,6 +3577,22 @@ int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source, EXPORT_SYMBOL_GPL(snd_soc_codec_set_pll); /** + * snd_soc_dai_set_bclk_ratio - configure BCLK to sample rate ratio. + * @dai: DAI + * @ratio Ratio of BCLK to Sample rate. + * + * Configures the DAI for a preset BCLK to sample rate ratio. + */ +int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + if (dai->driver && dai->driver->ops->set_bclk_ratio) + return dai->driver->ops->set_bclk_ratio(dai, ratio); + else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_soc_dai_set_bclk_ratio); + +/** * snd_soc_dai_set_fmt - configure DAI hardware audio format. * @dai: DAI * @fmt: SND_SOC_DAIFMT_ format value. -- cgit v0.10.2 From 666d5b4c742ba666eb68b467d777b7862f362ae5 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 31 Aug 2013 18:50:52 +0100 Subject: spi: core: Add devm_spi_register_master() Help simplify the cleanup code for SPI master drivers by providing a managed master registration function, ensuring that the master is automatically unregistered whenever the device is unbound. Signed-off-by: Mark Brown diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index fcb34a5..84ea821 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -302,3 +302,6 @@ PHY SLAVE DMA ENGINE devm_acpi_dma_controller_register() + +SPI + devm_spi_register_master() diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 9e039c6..a586ceb 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1245,6 +1245,41 @@ done: } EXPORT_SYMBOL_GPL(spi_register_master); +static void devm_spi_unregister(struct device *dev, void *res) +{ + spi_unregister_master(*(struct spi_master **)res); +} + +/** + * dev_spi_register_master - register managed SPI master controller + * @dev: device managing SPI master + * @master: initialized master, originally from spi_alloc_master() + * Context: can sleep + * + * Register a SPI device as with spi_register_master() which will + * automatically be unregister + */ +int devm_spi_register_master(struct device *dev, struct spi_master *master) +{ + struct spi_master **ptr; + int ret; + + ptr = devres_alloc(devm_spi_unregister, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret = spi_register_master(master); + if (ret != 0) { + *ptr = master; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return ret; +} +EXPORT_SYMBOL_GPL(devm_spi_register_master); + static int __unregister(struct device *dev, void *null) { spi_unregister_device(to_spi_device(dev)); diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 887116d..4d634d6 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -434,6 +434,8 @@ extern struct spi_master * spi_alloc_master(struct device *host, unsigned size); extern int spi_register_master(struct spi_master *master); +extern int devm_spi_register_master(struct device *dev, + struct spi_master *master); extern void spi_unregister_master(struct spi_master *master); extern struct spi_master *spi_busnum_to_master(u16 busnum); -- cgit v0.10.2 From 4b08478422040ae8cb11acc15d51f1cdb0ac39c8 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 30 Aug 2013 06:01:49 -0700 Subject: Drop support for Renesas H8/300 (h8300) architecture H8/300 has been dead for several years, and the kernel for it has not compiled for ages. Drop support for it. Cc: Yoshinori Sato Acked-by: Greg Kroah-Hartman Signed-off-by: Guenter Roeck diff --git a/Documentation/scheduler/sched-arch.txt b/Documentation/scheduler/sched-arch.txt index b1b8587..9290de7 100644 --- a/Documentation/scheduler/sched-arch.txt +++ b/Documentation/scheduler/sched-arch.txt @@ -65,11 +65,6 @@ Possible arch/ problems Possible arch problems I found (and either tried to fix or didn't): -h8300 - Is such sleeping racy vs interrupts? (See #4a). - The H8/300 manual I found indicates yes, however disabling IRQs - over the sleep mean only NMIs can wake it up, so can't fix easily - without doing spin waiting. - ia64 - is safe_halt call racy vs interrupts? (does it sleep?) (See #4a) sh64 - Is sleeping racy vs interrupts? (See #4a) diff --git a/arch/h8300/Kconfig b/arch/h8300/Kconfig deleted file mode 100644 index 24b1dc2..0000000 --- a/arch/h8300/Kconfig +++ /dev/null @@ -1,108 +0,0 @@ -config H8300 - bool - default y - select HAVE_IDE - select GENERIC_ATOMIC64 - select HAVE_UID16 - select VIRT_TO_BUS - select ARCH_WANT_IPC_PARSE_VERSION - select GENERIC_IRQ_SHOW - select GENERIC_CPU_DEVICES - select MODULES_USE_ELF_RELA - select OLD_SIGSUSPEND3 - select OLD_SIGACTION - select HAVE_UNDERSCORE_SYMBOL_PREFIX - -config MMU - bool - default n - -config SWAP - bool - default n - -config ZONE_DMA - bool - default y - -config FPU - bool - default n - -config RWSEM_GENERIC_SPINLOCK - bool - default y - -config RWSEM_XCHGADD_ALGORITHM - bool - default n - -config ARCH_HAS_ILOG2_U32 - bool - default n - -config ARCH_HAS_ILOG2_U64 - bool - default n - -config GENERIC_HWEIGHT - bool - default y - -config GENERIC_CALIBRATE_DELAY - bool - default y - -config GENERIC_BUG - bool - depends on BUG - -config TIME_LOW_RES - bool - default y - -config NO_IOPORT - def_bool y - -config NO_DMA - def_bool y - -config ISA - bool - default y - -config PCI - bool - default n - -config HZ - int - default 100 - -source "init/Kconfig" - -source "kernel/Kconfig.freezer" - -source "arch/h8300/Kconfig.cpu" - -menu "Executable file formats" - -source "fs/Kconfig.binfmt" - -endmenu - -source "net/Kconfig" - -source "drivers/Kconfig" - -source "arch/h8300/Kconfig.ide" - -source "fs/Kconfig" - -source "arch/h8300/Kconfig.debug" - -source "security/Kconfig" - -source "crypto/Kconfig" - -source "lib/Kconfig" diff --git a/arch/h8300/Kconfig.cpu b/arch/h8300/Kconfig.cpu deleted file mode 100644 index cdee771..0000000 --- a/arch/h8300/Kconfig.cpu +++ /dev/null @@ -1,171 +0,0 @@ -menu "Processor type and features" - -choice - prompt "H8/300 platform" - default H8300H_GENERIC - -config H8300H_GENERIC - bool "H8/300H Generic" - help - H8/300H CPU Generic Hardware Support - -config H8300H_AKI3068NET - bool "AE-3068/69" - select H83068 - help - AKI-H8/3068F / AKI-H8/3069F Flashmicom LAN Board Support - More Information. (Japanese Only) - - AE-3068/69 Evaluation Board Support - More Information. - - -config H8300H_H8MAX - bool "H8MAX" - select H83068 - help - H8MAX Evaluation Board Support - More Information. (Japanese Only) - - -config H8300H_SIM - bool "H8/300H Simulator" - select H83007 - help - GDB Simulator Support - More Information. - - -config H8S_GENERIC - bool "H8S Generic" - help - H8S CPU Generic Hardware Support - -config H8S_EDOSK2674 - bool "EDOSK-2674" - select H8S2678 - help - Renesas EDOSK-2674 Evaluation Board Support - More Information. - - - -config H8S_SIM - bool "H8S Simulator" - help - GDB Simulator Support - More Information. - - -endchoice - -choice - prompt "CPU Selection" - -config H83002 - bool "H8/3001,3002,3003" - depends on BROKEN - select CPU_H8300H - -config H83007 - bool "H8/3006,3007" - select CPU_H8300H - -config H83048 - bool "H8/3044,3045,3046,3047,3048,3052" - depends on BROKEN - select CPU_H8300H - -config H83068 - bool "H8/3065,3066,3067,3068,3069" - select CPU_H8300H - -config H8S2678 - bool "H8S/2670,2673,2674R,2675,2676" - select CPU_H8S - -endchoice - -config CPU_CLOCK - int "CPU Clock Frequency (/1KHz)" - default "20000" - help - CPU Clock Frequency divide to 1000 - -choice - prompt "Kernel executes from" - ---help--- - Choose the memory type that the kernel will be running in. - -config RAMKERNEL - bool "RAM" - help - The kernel will be resident in RAM when running. - -config ROMKERNEL - bool "ROM" - help - The kernel will be resident in FLASH/ROM when running. -endchoice - - -config CPU_H8300H - bool - depends on (H83002 || H83007 || H83048 || H83068) - default y - -config CPU_H8S - bool - depends on H8S2678 - default y - -choice - prompt "Timer" -config H8300_TIMER8 - bool "8bit timer (2ch cascade)" - depends on (H83007 || H83068 || H8S2678) - -config H8300_TIMER16 - bool "16bit timer" - depends on (H83007 || H83068) - -config H8300_ITU - bool "ITU" - depends on (H83002 || H83048) - -config H8300_TPU - bool "TPU" - depends on H8S2678 -endchoice - -if H8300_TIMER8 -choice - prompt "Timer Channel" -config H8300_TIMER8_CH0 - bool "Channel 0" -config H8300_TIMER8_CH2 - bool "Channel 2" - depends on CPU_H8300H -endchoice -endif - -config H8300_TIMER16_CH - int "16bit timer channel (0 - 2)" - depends on H8300_TIMER16 - range 0 2 - -config H8300_ITU_CH - int "ITU channel" - depends on H8300_ITU - range 0 4 - -config H8300_TPU_CH - int "TPU channel" - depends on H8300_TPU - range 0 4 - -source "kernel/Kconfig.preempt" - -source "mm/Kconfig" - -endmenu diff --git a/arch/h8300/Kconfig.debug b/arch/h8300/Kconfig.debug deleted file mode 100644 index e8d1b23..0000000 --- a/arch/h8300/Kconfig.debug +++ /dev/null @@ -1,68 +0,0 @@ -menu "Kernel hacking" - -source "lib/Kconfig.debug" - -config FULLDEBUG - bool "Full Symbolic/Source Debugging support" - help - Enable debugging symbols on kernel build. - -config HIGHPROFILE - bool "Use fast second timer for profiling" - help - Use a fast secondary clock to produce profiling information. - -config NO_KERNEL_MSG - bool "Suppress Kernel BUG Messages" - help - Do not output any debug BUG messages within the kernel. - -config GDB_MAGICPRINT - bool "Message Output for GDB MagicPrint service" - depends on (H8300H_SIM || H8S_SIM) - help - kernel messages output using MagicPrint service from GDB - -config SYSCALL_PRINT - bool "SystemCall trace print" - help - output history of systemcall - -config GDB_DEBUG - bool "Use gdb stub" - depends on (!H8300H_SIM && !H8S_SIM) - help - gdb stub exception support - -config SH_STANDARD_BIOS - bool "Use gdb protocol serial console" - depends on (!H8300H_SIM && !H8S_SIM) - help - serial console output using GDB protocol. - Require eCos/RedBoot - -config DEFAULT_CMDLINE - bool "Use builtin commandline" - default n - help - builtin kernel commandline enabled. - -config KERNEL_COMMAND - string "Buildin command string" - depends on DEFAULT_CMDLINE - help - builtin kernel commandline strings. - -config BLKDEV_RESERVE - bool "BLKDEV Reserved Memory" - default n - help - Reserved BLKDEV area. - -config BLKDEV_RESERVE_ADDRESS - hex 'start address' - depends on BLKDEV_RESERVE - help - BLKDEV start address. - -endmenu diff --git a/arch/h8300/Kconfig.ide b/arch/h8300/Kconfig.ide deleted file mode 100644 index a38a630..0000000 --- a/arch/h8300/Kconfig.ide +++ /dev/null @@ -1,44 +0,0 @@ -# uClinux H8/300 Target Board Selection Menu (IDE) - -if (H8300H_AKI3068NET) -menu "IDE Extra configuration" - -config H8300_IDE_BASE - hex "IDE register base address" - depends on IDE - default 0 - help - IDE registers base address - -config H8300_IDE_ALT - hex "IDE register alternate address" - depends on IDE - default 0 - help - IDE alternate registers address - -config H8300_IDE_IRQ - int "IDE IRQ no" - depends on IDE - default 0 - help - IDE use IRQ no -endmenu -endif - -if (H8300H_H8MAX) -config H8300_IDE_BASE - hex - depends on IDE - default 0x200000 - -config H8300_IDE_ALT - hex - depends on IDE - default 0x60000c - -config H8300_IDE_IRQ - int - depends on IDE - default 5 -endif diff --git a/arch/h8300/Makefile b/arch/h8300/Makefile deleted file mode 100644 index a556447..0000000 --- a/arch/h8300/Makefile +++ /dev/null @@ -1,71 +0,0 @@ -# -# arch/h8300/Makefile -# -# 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. -# -# (C) Copyright 2002,2003 Yoshinori Sato -# - -platform-$(CONFIG_CPU_H8300H) := h8300h -platform-$(CONFIG_CPU_H8S) := h8s -PLATFORM := $(platform-y) - -board-$(CONFIG_H8300H_GENERIC) := generic -board-$(CONFIG_H8300H_AKI3068NET) := aki3068net -board-$(CONFIG_H8300H_H8MAX) := h8max -board-$(CONFIG_H8300H_SIM) := generic -board-$(CONFIG_H8S_GENERIC) := generic -board-$(CONFIG_H8S_EDOSK2674) := edosk2674 -board-$(CONFIG_H8S_SIM) := generic -BOARD := $(board-y) - -model-$(CONFIG_RAMKERNEL) := ram -model-$(CONFIG_ROMKERNEL) := rom -MODEL := $(model-y) - -cflags-$(CONFIG_CPU_H8300H) := -mh -ldflags-$(CONFIG_CPU_H8300H) := -mh8300helf -cflags-$(CONFIG_CPU_H8S) := -ms -ldflags-$(CONFIG_CPU_H8S) := -mh8300self - -KBUILD_CFLAGS += $(cflags-y) -KBUILD_CFLAGS += -mint32 -fno-builtin -KBUILD_CFLAGS += -g -KBUILD_CFLAGS += -D__linux__ -KBUILD_CFLAGS += -DUTS_SYSNAME=\"uClinux\" -KBUILD_AFLAGS += -DPLATFORM=$(PLATFORM) -DMODEL=$(MODEL) $(cflags-y) -LDFLAGS += $(ldflags-y) - -CROSS_COMPILE = h8300-elf- -LIBGCC := $(shell $(CROSS-COMPILE)$(CC) $(KBUILD_CFLAGS) -print-libgcc-file-name) - -head-y := arch/$(ARCH)/platform/$(PLATFORM)/$(BOARD)/crt0_$(MODEL).o - -core-y += arch/$(ARCH)/kernel/ \ - arch/$(ARCH)/mm/ -ifdef PLATFORM -core-y += arch/$(ARCH)/platform/$(PLATFORM)/ \ - arch/$(ARCH)/platform/$(PLATFORM)/$(BOARD)/ -endif - -libs-y += arch/$(ARCH)/lib/ $(LIBGCC) - -boot := arch/h8300/boot - -export MODEL PLATFORM BOARD - -archmrproper: - -archclean: - $(Q)$(MAKE) $(clean)=$(boot) - -vmlinux.srec vmlinux.bin zImage: vmlinux - $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ - -define archhelp - @echo 'vmlinux.bin - Create raw binary' - @echo 'vmlinux.srec - Create srec binary' - @echo 'zImage - Compressed kernel image' -endef diff --git a/arch/h8300/README b/arch/h8300/README deleted file mode 100644 index efa805f..0000000 --- a/arch/h8300/README +++ /dev/null @@ -1,38 +0,0 @@ -linux-2.6 for H8/300 README -Yoshinori Sato - -* Supported CPU -H8/300H and H8S - -* Supported Target -1.simulator of GDB - require patches. - -2.AE 3068/AE 3069 - more information - MICROTRONIQUE - Akizuki Denshi Tsusho Ltd. (Japanese Only) - -3.H8MAX - see http://ip-sol.jp/h8max/ (Japanese Only) - -4.EDOSK2674 - see http://www.eu.renesas.com/products/mpumcu/tool/edk/support/edosk2674.html - http://www.uclinux.org/pub/uClinux/ports/h8/HITACHI-EDOSK2674-HOWTO - http://www.azpower.com/H8-uClinux/ - -* Toolchain Version -gcc-3.1 or higher and patch -see arch/h8300/tools_patch/README -binutils-2.12 or higher -gdb-5.2 or higher -The environment that can compile a h8300-elf binary is necessary. - -* Userland Develop environment -used h8300-elf toolchains. -see http://www.uclinux.org/pub/uClinux/ports/h8/ - -* A few words of thanks -Porting to H8/300 serieses is support of Information-technology Promotion Agency, Japan. -I thank support. -and All developer/user. diff --git a/arch/h8300/boot/Makefile b/arch/h8300/boot/Makefile deleted file mode 100644 index 0bb62e0..0000000 --- a/arch/h8300/boot/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -# arch/h8300/boot/Makefile - -targets := vmlinux.srec vmlinux.bin zImage -subdir- := compressed - -OBJCOPYFLAGS_vmlinux.srec := -Osrec -OBJCOPYFLAGS_vmlinux.bin := -Obinary -OBJCOPYFLAGS_zImage := -O binary -R .note -R .comment -R .stab -R .stabstr -S - -$(obj)/vmlinux.srec $(obj)/vmlinux.bin: vmlinux FORCE - $(call if_changed,objcopy) - @echo ' Kernel: $@ is ready' - -$(obj)/zImage: $(obj)/compressed/vmlinux FORCE - $(call if_changed,objcopy) - @echo 'Kernel: $@ is ready' - -$(obj)/compressed/vmlinux: FORCE - $(Q)$(MAKE) $(build)=$(obj)/compressed $@ - -CLEAN_FILES += arch/$(ARCH)/vmlinux.bin arch/$(ARCH)/vmlinux.srec - diff --git a/arch/h8300/boot/compressed/Makefile b/arch/h8300/boot/compressed/Makefile deleted file mode 100644 index a6c98fe..0000000 --- a/arch/h8300/boot/compressed/Makefile +++ /dev/null @@ -1,37 +0,0 @@ -# -# linux/arch/sh/boot/compressed/Makefile -# -# create a compressed vmlinux image from the original vmlinux -# - -targets := vmlinux vmlinux.bin vmlinux.bin.gz head.o misc.o piggy.o -asflags-y := -traditional - -OBJECTS = $(obj)/head.o $(obj)/misc.o - -# -# IMAGE_OFFSET is the load offset of the compression loader -# Assign dummy values if these 2 variables are not defined, -# in order to suppress error message. -# -CONFIG_MEMORY_START ?= 0x00400000 -CONFIG_BOOT_LINK_OFFSET ?= 0x00140000 -IMAGE_OFFSET := $(shell printf "0x%08x" $$(($(CONFIG_MEMORY_START)+$(CONFIG_BOOT_LINK_OFFSET)))) - -LDFLAGS_vmlinux := -Ttext $(IMAGE_OFFSET) -estartup $(obj)/vmlinux.lds - -$(obj)/vmlinux: $(OBJECTS) $(obj)/piggy.o FORCE - $(call if_changed,ld) - @: - -$(obj)/vmlinux.bin: vmlinux FORCE - $(call if_changed,objcopy) - -$(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin FORCE - $(call if_changed,gzip) - -LDFLAGS_piggy.o := -r --format binary --oformat elf32-h8300 -T -OBJCOPYFLAGS := -O binary - -$(obj)/piggy.o: $(obj)/vmlinux.scr $(obj)/vmlinux.bin.gz FORCE - $(call if_changed,ld) diff --git a/arch/h8300/boot/compressed/head.S b/arch/h8300/boot/compressed/head.S deleted file mode 100644 index 10e9a2d..0000000 --- a/arch/h8300/boot/compressed/head.S +++ /dev/null @@ -1,47 +0,0 @@ -/* - * linux/arch/h8300/boot/compressed/head.S - * - * Copyright (C) 2006 Yoshinori Sato - */ - - .h8300h -#include - -#define SRAM_START 0xff4000 - - .section .text..startup - .global startup -startup: - mov.l #SRAM_START+0x8000, sp - mov.l #__sbss, er0 - mov.l #__ebss, er1 - sub.l er0, er1 - shlr er1 - shlr er1 - sub.l er2, er2 -1: - mov.l er2, @er0 - adds #4, er0 - dec.l #1, er1 - bne 1b - jsr @_decompress_kernel - jmp @0x400000 - - .align 9 -fake_headers_as_bzImage: - .word 0 - .ascii "HdrS" ; header signature - .word 0x0202 ; header version number (>= 0x0105) - ; or else old loadlin-1.5 will fail) - .word 0 ; default_switch - .word 0 ; SETUPSEG - .word 0x1000 - .word 0 ; pointing to kernel version string - .byte 0 ; = 0, old one (LILO, Loadlin, - ; 0xTV: T=0 for LILO - ; V = version - .byte 1 ; Load flags bzImage=1 - .word 0x8000 ; size to move, when setup is not - .long 0x100000 ; 0x100000 = default for big kernel - .long 0 ; address of loaded ramdisk image - .long 0 ; its size in bytes diff --git a/arch/h8300/boot/compressed/misc.c b/arch/h8300/boot/compressed/misc.c deleted file mode 100644 index 4a1e3dd..0000000 --- a/arch/h8300/boot/compressed/misc.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - * arch/h8300/boot/compressed/misc.c - * - * This is a collection of several routines from gzip-1.0.3 - * adapted for Linux. - * - * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 - * - * Adapted for h8300 by Yoshinori Sato 2006 - */ - -#include - -/* - * gzip declarations - */ - -#define OF(args) args -#define STATIC static - -#undef memset -#undef memcpy -#define memzero(s, n) memset ((s), 0, (n)) - -typedef unsigned char uch; -typedef unsigned short ush; -typedef unsigned long ulg; - -#define WSIZE 0x8000 /* Window size must be at least 32k, */ - /* and a power of two */ - -static uch *inbuf; /* input buffer */ -static uch window[WSIZE]; /* Sliding window buffer */ - -static unsigned insize = 0; /* valid bytes in inbuf */ -static unsigned inptr = 0; /* index of next byte to be processed in inbuf */ -static unsigned outcnt = 0; /* bytes in output buffer */ - -/* gzip flag byte */ -#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ -#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ -#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ -#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ -#define COMMENT 0x10 /* bit 4 set: file comment present */ -#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ -#define RESERVED 0xC0 /* bit 6,7: reserved */ - -#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf()) - -/* Diagnostic functions */ -#ifdef DEBUG -# define Assert(cond,msg) {if(!(cond)) error(msg);} -# define Trace(x) fprintf x -# define Tracev(x) {if (verbose) fprintf x ;} -# define Tracevv(x) {if (verbose>1) fprintf x ;} -# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} -# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} -#else -# define Assert(cond,msg) -# define Trace(x) -# define Tracev(x) -# define Tracevv(x) -# define Tracec(c,x) -# define Tracecv(c,x) -#endif - -static int fill_inbuf(void); -static void flush_window(void); -static void error(char *m); - -extern char input_data[]; -extern int input_len; - -static long bytes_out = 0; -static uch *output_data; -static unsigned long output_ptr = 0; - -static void error(char *m); - -int puts(const char *); - -extern int _end; -static unsigned long free_mem_ptr; -static unsigned long free_mem_end_ptr; - -#define HEAP_SIZE 0x10000 - -#include "../../../../lib/inflate.c" - -#define SCR *((volatile unsigned char *)0xffff8a) -#define TDR *((volatile unsigned char *)0xffff8b) -#define SSR *((volatile unsigned char *)0xffff8c) - -int puts(const char *s) -{ - return 0; -} - -void* memset(void* s, int c, size_t n) -{ - int i; - char *ss = (char*)s; - - for (i=0;i> 8); - } - crc = c; - bytes_out += (ulg)outcnt; - output_ptr += (ulg)outcnt; - outcnt = 0; -} - -static void error(char *x) -{ - puts("\n\n"); - puts(x); - puts("\n\n -- System halted"); - - while(1); /* Halt */ -} - -#define STACK_SIZE (4096) -long user_stack [STACK_SIZE]; -long* stack_start = &user_stack[STACK_SIZE]; - -void decompress_kernel(void) -{ - output_data = 0; - output_ptr = (unsigned long)0x400000; - free_mem_ptr = (unsigned long)&_end; - free_mem_end_ptr = free_mem_ptr + HEAP_SIZE; - - makecrc(); - puts("Uncompressing Linux... "); - gunzip(); - puts("Ok, booting the kernel.\n"); -} diff --git a/arch/h8300/boot/compressed/vmlinux.lds b/arch/h8300/boot/compressed/vmlinux.lds deleted file mode 100644 index a0a3a0e..0000000 --- a/arch/h8300/boot/compressed/vmlinux.lds +++ /dev/null @@ -1,32 +0,0 @@ -SECTIONS -{ - .text : - { - __stext = . ; - __text = .; - *(.text..startup) - *(.text) - __etext = . ; - } - - .rodata : - { - *(.rodata) - } - .data : - - { - __sdata = . ; - ___data_start = . ; - *(.data.*) - } - .bss : - { - . = ALIGN(0x4) ; - __sbss = . ; - *(.bss*) - . = ALIGN(0x4) ; - __ebss = . ; - __end = . ; - } -} diff --git a/arch/h8300/boot/compressed/vmlinux.scr b/arch/h8300/boot/compressed/vmlinux.scr deleted file mode 100644 index a0f6962..0000000 --- a/arch/h8300/boot/compressed/vmlinux.scr +++ /dev/null @@ -1,9 +0,0 @@ -SECTIONS -{ - .data : { - _input_len = .; - LONG(_input_data_end - _input_data) _input_data = .; - *(.data) - _input_data_end = .; - } -} diff --git a/arch/h8300/defconfig b/arch/h8300/defconfig deleted file mode 100644 index 042425a..0000000 --- a/arch/h8300/defconfig +++ /dev/null @@ -1,42 +0,0 @@ -CONFIG_EXPERIMENTAL=y -# CONFIG_LOCALVERSION_AUTO is not set -CONFIG_LOG_BUF_SHIFT=14 -CONFIG_EXPERT=y -# CONFIG_UID16 is not set -# CONFIG_SYSCTL_SYSCALL is not set -# CONFIG_KALLSYMS is not set -# CONFIG_HOTPLUG is not set -# CONFIG_BASE_FULL is not set -# CONFIG_FUTEX is not set -# CONFIG_EPOLL is not set -# CONFIG_SIGNALFD is not set -# CONFIG_TIMERFD is not set -# CONFIG_EVENTFD is not set -# CONFIG_VM_EVENT_COUNTERS is not set -# CONFIG_COMPAT_BRK is not set -CONFIG_SLOB=y -# CONFIG_BLK_DEV_BSG is not set -# CONFIG_IOSCHED_DEADLINE is not set -# CONFIG_IOSCHED_CFQ is not set -CONFIG_H83007=y -CONFIG_BINFMT_FLAT=y -CONFIG_BINFMT_ZFLAT=y -CONFIG_BINFMT_MISC=y -# CONFIG_PREVENT_FIRMWARE_BUILD is not set -CONFIG_MTD=y -CONFIG_MTD_PARTITIONS=y -CONFIG_MTD_REDBOOT_PARTS=y -CONFIG_MTD_CHAR=y -CONFIG_MTD_RAM=y -CONFIG_MTD_ROM=y -CONFIG_MTD_UCLINUX=y -# CONFIG_BLK_DEV is not set -# CONFIG_INPUT is not set -# CONFIG_SERIO is not set -# CONFIG_HWMON is not set -# CONFIG_USB_SUPPORT is not set -# CONFIG_DNOTIFY is not set -CONFIG_ROMFS_FS=y -# CONFIG_ENABLE_WARN_DEPRECATED is not set -# CONFIG_ENABLE_MUST_CHECK is not set -# CONFIG_CRC32 is not set diff --git a/arch/h8300/include/asm/Kbuild b/arch/h8300/include/asm/Kbuild deleted file mode 100644 index 8ada3cf..0000000 --- a/arch/h8300/include/asm/Kbuild +++ /dev/null @@ -1,8 +0,0 @@ - -generic-y += clkdev.h -generic-y += exec.h -generic-y += linkage.h -generic-y += mmu.h -generic-y += module.h -generic-y += trace_clock.h -generic-y += xor.h diff --git a/arch/h8300/include/asm/asm-offsets.h b/arch/h8300/include/asm/asm-offsets.h deleted file mode 100644 index d370ee3..0000000 --- a/arch/h8300/include/asm/asm-offsets.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/arch/h8300/include/asm/atomic.h b/arch/h8300/include/asm/atomic.h deleted file mode 100644 index 40901e3..0000000 --- a/arch/h8300/include/asm/atomic.h +++ /dev/null @@ -1,146 +0,0 @@ -#ifndef __ARCH_H8300_ATOMIC__ -#define __ARCH_H8300_ATOMIC__ - -#include -#include - -/* - * Atomic operations that C can't guarantee us. Useful for - * resource counting etc.. - */ - -#define ATOMIC_INIT(i) { (i) } - -#define atomic_read(v) (*(volatile int *)&(v)->counter) -#define atomic_set(v, i) (((v)->counter) = i) - -#include - -static __inline__ int atomic_add_return(int i, atomic_t *v) -{ - unsigned long flags; - int ret; - local_irq_save(flags); - ret = v->counter += i; - local_irq_restore(flags); - return ret; -} - -#define atomic_add(i, v) atomic_add_return(i, v) -#define atomic_add_negative(a, v) (atomic_add_return((a), (v)) < 0) - -static __inline__ int atomic_sub_return(int i, atomic_t *v) -{ - unsigned long flags; - int ret; - local_irq_save(flags); - ret = v->counter -= i; - local_irq_restore(flags); - return ret; -} - -#define atomic_sub(i, v) atomic_sub_return(i, v) -#define atomic_sub_and_test(i,v) (atomic_sub_return(i, v) == 0) - -static __inline__ int atomic_inc_return(atomic_t *v) -{ - unsigned long flags; - int ret; - local_irq_save(flags); - v->counter++; - ret = v->counter; - local_irq_restore(flags); - return ret; -} - -#define atomic_inc(v) atomic_inc_return(v) - -/* - * atomic_inc_and_test - increment and test - * @v: pointer of type atomic_t - * - * Atomically increments @v by 1 - * and returns true if the result is zero, or false for all - * other cases. - */ -#define atomic_inc_and_test(v) (atomic_inc_return(v) == 0) - -static __inline__ int atomic_dec_return(atomic_t *v) -{ - unsigned long flags; - int ret; - local_irq_save(flags); - --v->counter; - ret = v->counter; - local_irq_restore(flags); - return ret; -} - -#define atomic_dec(v) atomic_dec_return(v) - -static __inline__ int atomic_dec_and_test(atomic_t *v) -{ - unsigned long flags; - int ret; - local_irq_save(flags); - --v->counter; - ret = v->counter; - local_irq_restore(flags); - return ret == 0; -} - -static inline int atomic_cmpxchg(atomic_t *v, int old, int new) -{ - int ret; - unsigned long flags; - - local_irq_save(flags); - ret = v->counter; - if (likely(ret == old)) - v->counter = new; - local_irq_restore(flags); - return ret; -} - -static inline int __atomic_add_unless(atomic_t *v, int a, int u) -{ - int ret; - unsigned long flags; - - local_irq_save(flags); - ret = v->counter; - if (ret != u) - v->counter += a; - local_irq_restore(flags); - return ret; -} - -static __inline__ void atomic_clear_mask(unsigned long mask, unsigned long *v) -{ - __asm__ __volatile__("stc ccr,r1l\n\t" - "orc #0x80,ccr\n\t" - "mov.l %0,er0\n\t" - "and.l %1,er0\n\t" - "mov.l er0,%0\n\t" - "ldc r1l,ccr" - : "=m" (*v) : "g" (~(mask)) :"er0","er1"); -} - -static __inline__ void atomic_set_mask(unsigned long mask, unsigned long *v) -{ - __asm__ __volatile__("stc ccr,r1l\n\t" - "orc #0x80,ccr\n\t" - "mov.l %0,er0\n\t" - "or.l %1,er0\n\t" - "mov.l er0,%0\n\t" - "ldc r1l,ccr" - : "=m" (*v) : "g" (mask) :"er0","er1"); -} - -/* Atomic operations are already serializing */ -#define smp_mb__before_atomic_dec() barrier() -#define smp_mb__after_atomic_dec() barrier() -#define smp_mb__before_atomic_inc() barrier() -#define smp_mb__after_atomic_inc() barrier() - -#endif /* __ARCH_H8300_ATOMIC __ */ diff --git a/arch/h8300/include/asm/barrier.h b/arch/h8300/include/asm/barrier.h deleted file mode 100644 index 9e0aa9f..0000000 --- a/arch/h8300/include/asm/barrier.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef _H8300_BARRIER_H -#define _H8300_BARRIER_H - -#define nop() asm volatile ("nop"::) - -/* - * Force strict CPU ordering. - * Not really required on H8... - */ -#define mb() asm volatile ("" : : :"memory") -#define rmb() asm volatile ("" : : :"memory") -#define wmb() asm volatile ("" : : :"memory") -#define set_mb(var, value) do { xchg(&var, value); } while (0) - -#define read_barrier_depends() do { } while (0) - -#ifdef CONFIG_SMP -#define smp_mb() mb() -#define smp_rmb() rmb() -#define smp_wmb() wmb() -#define smp_read_barrier_depends() read_barrier_depends() -#else -#define smp_mb() barrier() -#define smp_rmb() barrier() -#define smp_wmb() barrier() -#define smp_read_barrier_depends() do { } while(0) -#endif - -#endif /* _H8300_BARRIER_H */ diff --git a/arch/h8300/include/asm/bitops.h b/arch/h8300/include/asm/bitops.h deleted file mode 100644 index eb34e0c..0000000 --- a/arch/h8300/include/asm/bitops.h +++ /dev/null @@ -1,211 +0,0 @@ -#ifndef _H8300_BITOPS_H -#define _H8300_BITOPS_H - -/* - * Copyright 1992, Linus Torvalds. - * Copyright 2002, Yoshinori Sato - */ - -#include - -#ifdef __KERNEL__ - -#ifndef _LINUX_BITOPS_H -#error only can be included directly -#endif - -/* - * Function prototypes to keep gcc -Wall happy - */ - -/* - * ffz = Find First Zero in word. Undefined if no zero exists, - * so code should check against ~0UL first.. - */ -static __inline__ unsigned long ffz(unsigned long word) -{ - unsigned long result; - - result = -1; - __asm__("1:\n\t" - "shlr.l %2\n\t" - "adds #1,%0\n\t" - "bcs 1b" - : "=r" (result) - : "0" (result),"r" (word)); - return result; -} - -#define H8300_GEN_BITOP_CONST(OP,BIT) \ - case BIT: \ - __asm__(OP " #" #BIT ",@%0"::"r"(b_addr):"memory"); \ - break; - -#define H8300_GEN_BITOP(FNAME,OP) \ -static __inline__ void FNAME(int nr, volatile unsigned long* addr) \ -{ \ - volatile unsigned char *b_addr; \ - b_addr = (volatile unsigned char *)addr + ((nr >> 3) ^ 3); \ - if (__builtin_constant_p(nr)) { \ - switch(nr & 7) { \ - H8300_GEN_BITOP_CONST(OP,0) \ - H8300_GEN_BITOP_CONST(OP,1) \ - H8300_GEN_BITOP_CONST(OP,2) \ - H8300_GEN_BITOP_CONST(OP,3) \ - H8300_GEN_BITOP_CONST(OP,4) \ - H8300_GEN_BITOP_CONST(OP,5) \ - H8300_GEN_BITOP_CONST(OP,6) \ - H8300_GEN_BITOP_CONST(OP,7) \ - } \ - } else { \ - __asm__(OP " %w0,@%1"::"r"(nr),"r"(b_addr):"memory"); \ - } \ -} - -/* - * clear_bit() doesn't provide any barrier for the compiler. - */ -#define smp_mb__before_clear_bit() barrier() -#define smp_mb__after_clear_bit() barrier() - -H8300_GEN_BITOP(set_bit ,"bset") -H8300_GEN_BITOP(clear_bit ,"bclr") -H8300_GEN_BITOP(change_bit,"bnot") -#define __set_bit(nr,addr) set_bit((nr),(addr)) -#define __clear_bit(nr,addr) clear_bit((nr),(addr)) -#define __change_bit(nr,addr) change_bit((nr),(addr)) - -#undef H8300_GEN_BITOP -#undef H8300_GEN_BITOP_CONST - -static __inline__ int test_bit(int nr, const unsigned long* addr) -{ - return (*((volatile unsigned char *)addr + - ((nr >> 3) ^ 3)) & (1UL << (nr & 7))) != 0; -} - -#define __test_bit(nr, addr) test_bit(nr, addr) - -#define H8300_GEN_TEST_BITOP_CONST_INT(OP,BIT) \ - case BIT: \ - __asm__("stc ccr,%w1\n\t" \ - "orc #0x80,ccr\n\t" \ - "bld #" #BIT ",@%4\n\t" \ - OP " #" #BIT ",@%4\n\t" \ - "rotxl.l %0\n\t" \ - "ldc %w1,ccr" \ - : "=r"(retval),"=&r"(ccrsave),"=m"(*b_addr) \ - : "0" (retval),"r" (b_addr) \ - : "memory"); \ - break; - -#define H8300_GEN_TEST_BITOP_CONST(OP,BIT) \ - case BIT: \ - __asm__("bld #" #BIT ",@%3\n\t" \ - OP " #" #BIT ",@%3\n\t" \ - "rotxl.l %0\n\t" \ - : "=r"(retval),"=m"(*b_addr) \ - : "0" (retval),"r" (b_addr) \ - : "memory"); \ - break; - -#define H8300_GEN_TEST_BITOP(FNNAME,OP) \ -static __inline__ int FNNAME(int nr, volatile void * addr) \ -{ \ - int retval = 0; \ - char ccrsave; \ - volatile unsigned char *b_addr; \ - b_addr = (volatile unsigned char *)addr + ((nr >> 3) ^ 3); \ - if (__builtin_constant_p(nr)) { \ - switch(nr & 7) { \ - H8300_GEN_TEST_BITOP_CONST_INT(OP,0) \ - H8300_GEN_TEST_BITOP_CONST_INT(OP,1) \ - H8300_GEN_TEST_BITOP_CONST_INT(OP,2) \ - H8300_GEN_TEST_BITOP_CONST_INT(OP,3) \ - H8300_GEN_TEST_BITOP_CONST_INT(OP,4) \ - H8300_GEN_TEST_BITOP_CONST_INT(OP,5) \ - H8300_GEN_TEST_BITOP_CONST_INT(OP,6) \ - H8300_GEN_TEST_BITOP_CONST_INT(OP,7) \ - } \ - } else { \ - __asm__("stc ccr,%w1\n\t" \ - "orc #0x80,ccr\n\t" \ - "btst %w5,@%4\n\t" \ - OP " %w5,@%4\n\t" \ - "beq 1f\n\t" \ - "inc.l #1,%0\n" \ - "1:\n\t" \ - "ldc %w1,ccr" \ - : "=r"(retval),"=&r"(ccrsave),"=m"(*b_addr) \ - : "0" (retval),"r" (b_addr),"r"(nr) \ - : "memory"); \ - } \ - return retval; \ -} \ - \ -static __inline__ int __ ## FNNAME(int nr, volatile void * addr) \ -{ \ - int retval = 0; \ - volatile unsigned char *b_addr; \ - b_addr = (volatile unsigned char *)addr + ((nr >> 3) ^ 3); \ - if (__builtin_constant_p(nr)) { \ - switch(nr & 7) { \ - H8300_GEN_TEST_BITOP_CONST(OP,0) \ - H8300_GEN_TEST_BITOP_CONST(OP,1) \ - H8300_GEN_TEST_BITOP_CONST(OP,2) \ - H8300_GEN_TEST_BITOP_CONST(OP,3) \ - H8300_GEN_TEST_BITOP_CONST(OP,4) \ - H8300_GEN_TEST_BITOP_CONST(OP,5) \ - H8300_GEN_TEST_BITOP_CONST(OP,6) \ - H8300_GEN_TEST_BITOP_CONST(OP,7) \ - } \ - } else { \ - __asm__("btst %w4,@%3\n\t" \ - OP " %w4,@%3\n\t" \ - "beq 1f\n\t" \ - "inc.l #1,%0\n" \ - "1:" \ - : "=r"(retval),"=m"(*b_addr) \ - : "0" (retval),"r" (b_addr),"r"(nr) \ - : "memory"); \ - } \ - return retval; \ -} - -H8300_GEN_TEST_BITOP(test_and_set_bit, "bset") -H8300_GEN_TEST_BITOP(test_and_clear_bit, "bclr") -H8300_GEN_TEST_BITOP(test_and_change_bit,"bnot") -#undef H8300_GEN_TEST_BITOP_CONST -#undef H8300_GEN_TEST_BITOP_CONST_INT -#undef H8300_GEN_TEST_BITOP - -#include - -static __inline__ unsigned long __ffs(unsigned long word) -{ - unsigned long result; - - result = -1; - __asm__("1:\n\t" - "shlr.l %2\n\t" - "adds #1,%0\n\t" - "bcc 1b" - : "=r" (result) - : "0"(result),"r"(word)); - return result; -} - -#include -#include -#include -#include -#include -#include - -#endif /* __KERNEL__ */ - -#include -#include -#include - -#endif /* _H8300_BITOPS_H */ diff --git a/arch/h8300/include/asm/bootinfo.h b/arch/h8300/include/asm/bootinfo.h deleted file mode 100644 index 5bed7e7..0000000 --- a/arch/h8300/include/asm/bootinfo.h +++ /dev/null @@ -1,2 +0,0 @@ - -/* Nothing for h8300 */ diff --git a/arch/h8300/include/asm/bug.h b/arch/h8300/include/asm/bug.h deleted file mode 100644 index 1e1be81..0000000 --- a/arch/h8300/include/asm/bug.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _H8300_BUG_H -#define _H8300_BUG_H - -/* always true */ -#define is_valid_bugaddr(addr) (1) - -#include - -struct pt_regs; -extern void die(const char *str, struct pt_regs *fp, unsigned long err); - -#endif diff --git a/arch/h8300/include/asm/bugs.h b/arch/h8300/include/asm/bugs.h deleted file mode 100644 index 1cb4afb..0000000 --- a/arch/h8300/include/asm/bugs.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * include/asm-h8300/bugs.h - * - * Copyright (C) 1994 Linus Torvalds - */ - -/* - * This is included by init/main.c to check for architecture-dependent bugs. - * - * Needs: - * void check_bugs(void); - */ - -static void check_bugs(void) -{ -} diff --git a/arch/h8300/include/asm/cache.h b/arch/h8300/include/asm/cache.h deleted file mode 100644 index 05887a1..0000000 --- a/arch/h8300/include/asm/cache.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef __ARCH_H8300_CACHE_H -#define __ARCH_H8300_CACHE_H - -/* bytes per L1 cache line */ -#define L1_CACHE_SHIFT 2 -#define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT) - -/* m68k-elf-gcc 2.95.2 doesn't like these */ - -#define __cacheline_aligned -#define ____cacheline_aligned - -#endif diff --git a/arch/h8300/include/asm/cachectl.h b/arch/h8300/include/asm/cachectl.h deleted file mode 100644 index c464022..0000000 --- a/arch/h8300/include/asm/cachectl.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef _H8300_CACHECTL_H -#define _H8300_CACHECTL_H - -/* Definitions for the cacheflush system call. */ - -#define FLUSH_SCOPE_LINE 0 /* Flush a cache line */ -#define FLUSH_SCOPE_PAGE 0 /* Flush a page */ -#define FLUSH_SCOPE_ALL 0 /* Flush the whole cache -- superuser only */ - -#define FLUSH_CACHE_DATA 0 /* Writeback and flush data cache */ -#define FLUSH_CACHE_INSN 0 /* Flush instruction cache */ -#define FLUSH_CACHE_BOTH 0 /* Flush both caches */ - -#endif /* _H8300_CACHECTL_H */ diff --git a/arch/h8300/include/asm/cacheflush.h b/arch/h8300/include/asm/cacheflush.h deleted file mode 100644 index 4cf2df2..0000000 --- a/arch/h8300/include/asm/cacheflush.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * (C) Copyright 2002, Yoshinori Sato - */ - -#ifndef _ASM_H8300_CACHEFLUSH_H -#define _ASM_H8300_CACHEFLUSH_H - -/* - * Cache handling functions - * No Cache memory all dummy functions - */ - -#define flush_cache_all() -#define flush_cache_mm(mm) -#define flush_cache_dup_mm(mm) do { } while (0) -#define flush_cache_range(vma,a,b) -#define flush_cache_page(vma,p,pfn) -#define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 0 -#define flush_dcache_page(page) -#define flush_dcache_mmap_lock(mapping) -#define flush_dcache_mmap_unlock(mapping) -#define flush_icache() -#define flush_icache_page(vma,page) -#define flush_icache_range(start,len) -#define flush_cache_vmap(start, end) -#define flush_cache_vunmap(start, end) -#define cache_push_v(vaddr,len) -#define cache_push(paddr,len) -#define cache_clear(paddr,len) - -#define flush_dcache_range(a,b) - -#define flush_icache_user_range(vma,page,addr,len) - -#define copy_to_user_page(vma, page, vaddr, dst, src, len) \ - memcpy(dst, src, len) -#define copy_from_user_page(vma, page, vaddr, dst, src, len) \ - memcpy(dst, src, len) - -#endif /* _ASM_H8300_CACHEFLUSH_H */ diff --git a/arch/h8300/include/asm/checksum.h b/arch/h8300/include/asm/checksum.h deleted file mode 100644 index 98724e1..0000000 --- a/arch/h8300/include/asm/checksum.h +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef _H8300_CHECKSUM_H -#define _H8300_CHECKSUM_H - -/* - * computes the checksum of a memory block at buff, length len, - * and adds in "sum" (32-bit) - * - * returns a 32-bit number suitable for feeding into itself - * or csum_tcpudp_magic - * - * this function must be called with even lengths, except - * for the last fragment, which may be odd - * - * it's best to have buff aligned on a 32-bit boundary - */ -__wsum csum_partial(const void *buff, int len, __wsum sum); - -/* - * the same as csum_partial, but copies from src while it - * checksums - * - * here even more important to align src and dst on a 32-bit (or even - * better 64-bit) boundary - */ - -__wsum csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum); - - -/* - * the same as csum_partial_copy, but copies from user space. - * - * here even more important to align src and dst on a 32-bit (or even - * better 64-bit) boundary - */ - -extern __wsum csum_partial_copy_from_user(const void __user *src, void *dst, - int len, __wsum sum, int *csum_err); - -__sum16 ip_fast_csum(const void *iph, unsigned int ihl); - - -/* - * Fold a partial checksum - */ - -static inline __sum16 csum_fold(__wsum sum) -{ - __asm__("mov.l %0,er0\n\t" - "add.w e0,r0\n\t" - "xor.w e0,e0\n\t" - "rotxl.w e0\n\t" - "add.w e0,r0\n\t" - "sub.w e0,e0\n\t" - "mov.l er0,%0" - : "=r"(sum) - : "0"(sum) - : "er0"); - return (__force __sum16)~sum; -} - - -/* - * computes the checksum of the TCP/UDP pseudo-header - * returns a 16-bit checksum, already complemented - */ - -static inline __wsum -csum_tcpudp_nofold(__be32 saddr, __be32 daddr, unsigned short len, - unsigned short proto, __wsum sum) -{ - __asm__ ("sub.l er0,er0\n\t" - "add.l %2,%0\n\t" - "addx #0,r0l\n\t" - "add.l %3,%0\n\t" - "addx #0,r0l\n\t" - "add.l %4,%0\n\t" - "addx #0,r0l\n\t" - "add.l er0,%0\n\t" - "bcc 1f\n\t" - "inc.l #1,%0\n" - "1:" - : "=&r" (sum) - : "0" (sum), "r" (daddr), "r" (saddr), "r" (len + proto) - :"er0"); - return sum; -} - -static inline __sum16 -csum_tcpudp_magic(__be32 saddr, __be32 daddr, unsigned short len, - unsigned short proto, __wsum sum) -{ - return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum)); -} - -/* - * this routine is used for miscellaneous IP-like checksums, mainly - * in icmp.c - */ - -extern __sum16 ip_compute_csum(const void *buff, int len); - -#endif /* _H8300_CHECKSUM_H */ diff --git a/arch/h8300/include/asm/cmpxchg.h b/arch/h8300/include/asm/cmpxchg.h deleted file mode 100644 index cdb203e..0000000 --- a/arch/h8300/include/asm/cmpxchg.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef __ARCH_H8300_CMPXCHG__ -#define __ARCH_H8300_CMPXCHG__ - -#include - -#define xchg(ptr,x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr)))) - -struct __xchg_dummy { unsigned long a[100]; }; -#define __xg(x) ((volatile struct __xchg_dummy *)(x)) - -static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int size) -{ - unsigned long tmp, flags; - - local_irq_save(flags); - - switch (size) { - case 1: - __asm__ __volatile__ - ("mov.b %2,%0\n\t" - "mov.b %1,%2" - : "=&r" (tmp) : "r" (x), "m" (*__xg(ptr)) : "memory"); - break; - case 2: - __asm__ __volatile__ - ("mov.w %2,%0\n\t" - "mov.w %1,%2" - : "=&r" (tmp) : "r" (x), "m" (*__xg(ptr)) : "memory"); - break; - case 4: - __asm__ __volatile__ - ("mov.l %2,%0\n\t" - "mov.l %1,%2" - : "=&r" (tmp) : "r" (x), "m" (*__xg(ptr)) : "memory"); - break; - default: - tmp = 0; - } - local_irq_restore(flags); - return tmp; -} - -#include - -/* - * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make - * them available. - */ -#define cmpxchg_local(ptr, o, n) \ - ((__typeof__(*(ptr)))__cmpxchg_local_generic((ptr), (unsigned long)(o),\ - (unsigned long)(n), sizeof(*(ptr)))) -#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) - -#ifndef CONFIG_SMP -#include -#endif - -#define atomic_xchg(v, new) (xchg(&((v)->counter), new)) - -#endif /* __ARCH_H8300_CMPXCHG__ */ diff --git a/arch/h8300/include/asm/cputime.h b/arch/h8300/include/asm/cputime.h deleted file mode 100644 index 092e187..0000000 --- a/arch/h8300/include/asm/cputime.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __H8300_CPUTIME_H -#define __H8300_CPUTIME_H - -#include - -#endif /* __H8300_CPUTIME_H */ diff --git a/arch/h8300/include/asm/current.h b/arch/h8300/include/asm/current.h deleted file mode 100644 index 57d74ee..0000000 --- a/arch/h8300/include/asm/current.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef _H8300_CURRENT_H -#define _H8300_CURRENT_H -/* - * current.h - * (C) Copyright 2000, Lineo, David McCullough - * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) - * - * rather than dedicate a register (as the m68k source does), we - * just keep a global, we should probably just change it all to be - * current and lose _current_task. - */ - -#include -#include - -struct task_struct; - -static inline struct task_struct *get_current(void) -{ - return(current_thread_info()->task); -} - -#define current get_current() - -#endif /* _H8300_CURRENT_H */ diff --git a/arch/h8300/include/asm/dbg.h b/arch/h8300/include/asm/dbg.h deleted file mode 100644 index 2c6d1cb..0000000 --- a/arch/h8300/include/asm/dbg.h +++ /dev/null @@ -1,2 +0,0 @@ -#define DEBUG 1 -#define BREAK asm volatile ("trap #3") diff --git a/arch/h8300/include/asm/delay.h b/arch/h8300/include/asm/delay.h deleted file mode 100644 index 743beba..0000000 --- a/arch/h8300/include/asm/delay.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef _H8300_DELAY_H -#define _H8300_DELAY_H - -#include - -/* - * Copyright (C) 2002 Yoshinori Sato - * - * Delay routines, using a pre-computed "loops_per_second" value. - */ - -static inline void __delay(unsigned long loops) -{ - __asm__ __volatile__ ("1:\n\t" - "dec.l #1,%0\n\t" - "bne 1b" - :"=r" (loops):"0"(loops)); -} - -/* - * Use only for very small delays ( < 1 msec). Should probably use a - * lookup table, really, as the multiplications take much too long with - * short delays. This is a "reasonable" implementation, though (and the - * first constant multiplications gets optimized away if the delay is - * a constant) - */ - -extern unsigned long loops_per_jiffy; - -static inline void udelay(unsigned long usecs) -{ - usecs *= 4295; /* 2**32 / 1000000 */ - usecs /= (loops_per_jiffy*HZ); - if (usecs) - __delay(usecs); -} - -#endif /* _H8300_DELAY_H */ diff --git a/arch/h8300/include/asm/device.h b/arch/h8300/include/asm/device.h deleted file mode 100644 index d8f9872..0000000 --- a/arch/h8300/include/asm/device.h +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Arch specific extensions to struct device - * - * This file is released under the GPLv2 - */ -#include - diff --git a/arch/h8300/include/asm/div64.h b/arch/h8300/include/asm/div64.h deleted file mode 100644 index 6cd978c..0000000 --- a/arch/h8300/include/asm/div64.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/arch/h8300/include/asm/dma.h b/arch/h8300/include/asm/dma.h deleted file mode 100644 index 3edbaaa..0000000 --- a/arch/h8300/include/asm/dma.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef _H8300_DMA_H -#define _H8300_DMA_H - - -/* - * Set number of channels of DMA on ColdFire for different implementations. - */ -#define MAX_DMA_CHANNELS 0 -#define MAX_DMA_ADDRESS PAGE_OFFSET - -/* These are in kernel/dma.c: */ -extern int request_dma(unsigned int dmanr, const char *device_id); /* reserve a DMA channel */ -extern void free_dma(unsigned int dmanr); /* release it again */ - -#endif /* _H8300_DMA_H */ diff --git a/arch/h8300/include/asm/elf.h b/arch/h8300/include/asm/elf.h deleted file mode 100644 index 6db7124..0000000 --- a/arch/h8300/include/asm/elf.h +++ /dev/null @@ -1,101 +0,0 @@ -#ifndef __ASMH8300_ELF_H -#define __ASMH8300_ELF_H - -/* - * ELF register definitions.. - */ - -#include -#include - -typedef unsigned long elf_greg_t; - -#define ELF_NGREG (sizeof(struct user_regs_struct) / sizeof(elf_greg_t)) -typedef elf_greg_t elf_gregset_t[ELF_NGREG]; -typedef unsigned long elf_fpregset_t; - -/* - * This is used to ensure we don't load something for the wrong architecture. - */ -#define elf_check_arch(x) ((x)->e_machine == EM_H8_300) - -/* - * These are used to set parameters in the core dumps. - */ -#define ELF_CLASS ELFCLASS32 -#define ELF_DATA ELFDATA2MSB -#define ELF_ARCH EM_H8_300 -#if defined(__H8300H__) -#define ELF_CORE_EFLAGS 0x810000 -#endif -#if defined(__H8300S__) -#define ELF_CORE_EFLAGS 0x820000 -#endif - -#define ELF_PLAT_INIT(_r) _r->er1 = 0 - -#define ELF_EXEC_PAGESIZE 4096 - -/* This is the location that an ET_DYN program is loaded if exec'ed. Typical - use of this is to invoke "./ld.so someprog" to test out a new version of - the loader. We need to make sure that it is out of the way of the program - that it will "exec", and that there is sufficient room for the brk. */ - -#define ELF_ET_DYN_BASE 0xD0000000UL - -/* This yields a mask that user programs can use to figure out what - instruction set this cpu supports. */ - -#define ELF_HWCAP (0) - -/* This yields a string that ld.so will use to load implementation - specific libraries for optimization. This is more specific in - intent than poking at uname or /proc/cpuinfo. */ - -#define ELF_PLATFORM (NULL) - -#define R_H8_NONE 0 -#define R_H8_DIR32 1 -#define R_H8_DIR32_28 2 -#define R_H8_DIR32_24 3 -#define R_H8_DIR32_16 4 -#define R_H8_DIR32U 6 -#define R_H8_DIR32U_28 7 -#define R_H8_DIR32U_24 8 -#define R_H8_DIR32U_20 9 -#define R_H8_DIR32U_16 10 -#define R_H8_DIR24 11 -#define R_H8_DIR24_20 12 -#define R_H8_DIR24_16 13 -#define R_H8_DIR24U 14 -#define R_H8_DIR24U_20 15 -#define R_H8_DIR24U_16 16 -#define R_H8_DIR16 17 -#define R_H8_DIR16U 18 -#define R_H8_DIR16S_32 19 -#define R_H8_DIR16S_28 20 -#define R_H8_DIR16S_24 21 -#define R_H8_DIR16S_20 22 -#define R_H8_DIR16S 23 -#define R_H8_DIR8 24 -#define R_H8_DIR8U 25 -#define R_H8_DIR8Z_32 26 -#define R_H8_DIR8Z_28 27 -#define R_H8_DIR8Z_24 28 -#define R_H8_DIR8Z_20 29 -#define R_H8_DIR8Z_16 30 -#define R_H8_PCREL16 31 -#define R_H8_PCREL8 32 -#define R_H8_BPOS 33 -#define R_H8_PCREL32 34 -#define R_H8_GOT32O 35 -#define R_H8_GOT16O 36 -#define R_H8_DIR16A8 59 -#define R_H8_DIR16R8 60 -#define R_H8_DIR24A8 61 -#define R_H8_DIR24R8 62 -#define R_H8_DIR32A16 63 -#define R_H8_ABS32 65 -#define R_H8_ABS32A16 127 - -#endif diff --git a/arch/h8300/include/asm/emergency-restart.h b/arch/h8300/include/asm/emergency-restart.h deleted file mode 100644 index 108d8c4..0000000 --- a/arch/h8300/include/asm/emergency-restart.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _ASM_EMERGENCY_RESTART_H -#define _ASM_EMERGENCY_RESTART_H - -#include - -#endif /* _ASM_EMERGENCY_RESTART_H */ diff --git a/arch/h8300/include/asm/fb.h b/arch/h8300/include/asm/fb.h deleted file mode 100644 index c7df380..0000000 --- a/arch/h8300/include/asm/fb.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _ASM_FB_H_ -#define _ASM_FB_H_ -#include - -#define fb_pgprotect(...) do {} while (0) - -static inline int fb_is_primary_device(struct fb_info *info) -{ - return 0; -} - -#endif /* _ASM_FB_H_ */ diff --git a/arch/h8300/include/asm/flat.h b/arch/h8300/include/asm/flat.h deleted file mode 100644 index bd12b31..0000000 --- a/arch/h8300/include/asm/flat.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * include/asm-h8300/flat.h -- uClinux flat-format executables - */ - -#ifndef __H8300_FLAT_H__ -#define __H8300_FLAT_H__ - -#define flat_argvp_envp_on_stack() 1 -#define flat_old_ram_flag(flags) 1 -#define flat_reloc_valid(reloc, size) ((reloc) <= (size)) -#define flat_set_persistent(relval, p) 0 - -/* - * on the H8 a couple of the relocations have an instruction in the - * top byte. As there can only be 24bits of address space, we just - * always preserve that 8bits at the top, when it isn't an instruction - * is is 0 (davidm@snapgear.com) - */ - -#define flat_get_relocate_addr(rel) (rel) -#define flat_get_addr_from_rp(rp, relval, flags, persistent) \ - (get_unaligned(rp) & ((flags & FLAT_FLAG_GOTPIC) ? 0xffffffff: 0x00ffffff)) -#define flat_put_addr_at_rp(rp, addr, rel) \ - put_unaligned (((*(char *)(rp)) << 24) | ((addr) & 0x00ffffff), rp) - -#endif /* __H8300_FLAT_H__ */ diff --git a/arch/h8300/include/asm/fpu.h b/arch/h8300/include/asm/fpu.h deleted file mode 100644 index 4fc416e..0000000 --- a/arch/h8300/include/asm/fpu.h +++ /dev/null @@ -1 +0,0 @@ -/* Nothing do */ diff --git a/arch/h8300/include/asm/ftrace.h b/arch/h8300/include/asm/ftrace.h deleted file mode 100644 index 40a8c17..0000000 --- a/arch/h8300/include/asm/ftrace.h +++ /dev/null @@ -1 +0,0 @@ -/* empty */ diff --git a/arch/h8300/include/asm/futex.h b/arch/h8300/include/asm/futex.h deleted file mode 100644 index 6a332a9..0000000 --- a/arch/h8300/include/asm/futex.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _ASM_FUTEX_H -#define _ASM_FUTEX_H - -#include - -#endif diff --git a/arch/h8300/include/asm/gpio-internal.h b/arch/h8300/include/asm/gpio-internal.h deleted file mode 100644 index a714f0c..0000000 --- a/arch/h8300/include/asm/gpio-internal.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef _H8300_GPIO_H -#define _H8300_GPIO_H - -#define H8300_GPIO_P1 0 -#define H8300_GPIO_P2 1 -#define H8300_GPIO_P3 2 -#define H8300_GPIO_P4 3 -#define H8300_GPIO_P5 4 -#define H8300_GPIO_P6 5 -#define H8300_GPIO_P7 6 -#define H8300_GPIO_P8 7 -#define H8300_GPIO_P9 8 -#define H8300_GPIO_PA 9 -#define H8300_GPIO_PB 10 -#define H8300_GPIO_PC 11 -#define H8300_GPIO_PD 12 -#define H8300_GPIO_PE 13 -#define H8300_GPIO_PF 14 -#define H8300_GPIO_PG 15 -#define H8300_GPIO_PH 16 - -#define H8300_GPIO_B7 0x80 -#define H8300_GPIO_B6 0x40 -#define H8300_GPIO_B5 0x20 -#define H8300_GPIO_B4 0x10 -#define H8300_GPIO_B3 0x08 -#define H8300_GPIO_B2 0x04 -#define H8300_GPIO_B1 0x02 -#define H8300_GPIO_B0 0x01 - -#define H8300_GPIO_INPUT 0 -#define H8300_GPIO_OUTPUT 1 - -#define H8300_GPIO_RESERVE(port, bits) \ - h8300_reserved_gpio(port, bits) - -#define H8300_GPIO_FREE(port, bits) \ - h8300_free_gpio(port, bits) - -#define H8300_GPIO_DDR(port, bit, dir) \ - h8300_set_gpio_dir(((port) << 8) | (bit), dir) - -#define H8300_GPIO_GETDIR(port, bit) \ - h8300_get_gpio_dir(((port) << 8) | (bit)) - -extern int h8300_reserved_gpio(int port, int bits); -extern int h8300_free_gpio(int port, int bits); -extern int h8300_set_gpio_dir(int port_bit, int dir); -extern int h8300_get_gpio_dir(int port_bit); -extern int h8300_init_gpio(void); - -#endif diff --git a/arch/h8300/include/asm/hardirq.h b/arch/h8300/include/asm/hardirq.h deleted file mode 100644 index c2e1aa0..0000000 --- a/arch/h8300/include/asm/hardirq.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef __H8300_HARDIRQ_H -#define __H8300_HARDIRQ_H - -#include - -#define HARDIRQ_BITS 8 - -/* - * The hardirq mask has to be large enough to have - * space for potentially all IRQ sources in the system - * nesting on a single CPU: - */ -#if (1 << HARDIRQ_BITS) < NR_IRQS -# error HARDIRQ_BITS is too low! -#endif - -#include - -#endif diff --git a/arch/h8300/include/asm/hw_irq.h b/arch/h8300/include/asm/hw_irq.h deleted file mode 100644 index d75a5a1..0000000 --- a/arch/h8300/include/asm/hw_irq.h +++ /dev/null @@ -1 +0,0 @@ -/* Do Nothing */ diff --git a/arch/h8300/include/asm/io.h b/arch/h8300/include/asm/io.h deleted file mode 100644 index c1a8df2..0000000 --- a/arch/h8300/include/asm/io.h +++ /dev/null @@ -1,358 +0,0 @@ -#ifndef _H8300_IO_H -#define _H8300_IO_H - -#ifdef __KERNEL__ - -#include - -#if defined(CONFIG_H83007) || defined(CONFIG_H83068) -#include -#elif defined(CONFIG_H8S2678) -#include -#else -#error UNKNOWN CPU TYPE -#endif - - -/* - * These are for ISA/PCI shared memory _only_ and should never be used - * on any other type of memory, including Zorro memory. They are meant to - * access the bus in the bus byte order which is little-endian!. - * - * readX/writeX() are used to access memory mapped devices. On some - * architectures the memory mapped IO stuff needs to be accessed - * differently. On the m68k architecture, we just read/write the - * memory location directly. - */ -/* ++roman: The assignments to temp. vars avoid that gcc sometimes generates - * two accesses to memory, which may be undesirable for some devices. - */ - -/* - * swap functions are sometimes needed to interface little-endian hardware - */ - -static inline unsigned short _swapw(volatile unsigned short v) -{ -#ifndef H8300_IO_NOSWAP - unsigned short r; - __asm__("xor.b %w0,%x0\n\t" - "xor.b %x0,%w0\n\t" - "xor.b %w0,%x0" - :"=r"(r) - :"0"(v)); - return r; -#else - return v; -#endif -} - -static inline unsigned long _swapl(volatile unsigned long v) -{ -#ifndef H8300_IO_NOSWAP - unsigned long r; - __asm__("xor.b %w0,%x0\n\t" - "xor.b %x0,%w0\n\t" - "xor.b %w0,%x0\n\t" - "xor.w %e0,%f0\n\t" - "xor.w %f0,%e0\n\t" - "xor.w %e0,%f0\n\t" - "xor.b %w0,%x0\n\t" - "xor.b %x0,%w0\n\t" - "xor.b %w0,%x0" - :"=r"(r) - :"0"(v)); - return r; -#else - return v; -#endif -} - -#define readb(addr) \ - ({ unsigned char __v = \ - *(volatile unsigned char *)((unsigned long)(addr) & 0x00ffffff); \ - __v; }) -#define readw(addr) \ - ({ unsigned short __v = \ - *(volatile unsigned short *)((unsigned long)(addr) & 0x00ffffff); \ - __v; }) -#define readl(addr) \ - ({ unsigned long __v = \ - *(volatile unsigned long *)((unsigned long)(addr) & 0x00ffffff); \ - __v; }) - -#define writeb(b,addr) (void)((*(volatile unsigned char *) \ - ((unsigned long)(addr) & 0x00ffffff)) = (b)) -#define writew(b,addr) (void)((*(volatile unsigned short *) \ - ((unsigned long)(addr) & 0x00ffffff)) = (b)) -#define writel(b,addr) (void)((*(volatile unsigned long *) \ - ((unsigned long)(addr) & 0x00ffffff)) = (b)) -#define readb_relaxed(addr) readb(addr) -#define readw_relaxed(addr) readw(addr) -#define readl_relaxed(addr) readl(addr) - -#define __raw_readb readb -#define __raw_readw readw -#define __raw_readl readl -#define __raw_writeb writeb -#define __raw_writew writew -#define __raw_writel writel - -static inline int h8300_buswidth(unsigned int addr) -{ - return (*(volatile unsigned char *)ABWCR & (1 << ((addr >> 21) & 7))) == 0; -} - -static inline void io_outsb(unsigned int addr, const void *buf, int len) -{ - volatile unsigned char *ap_b = (volatile unsigned char *) addr; - volatile unsigned short *ap_w = (volatile unsigned short *) addr; - unsigned char *bp = (unsigned char *) buf; - - if(h8300_buswidth(addr) && (addr & 1)) { - while (len--) - *ap_w = *bp++; - } else { - while (len--) - *ap_b = *bp++; - } -} - -static inline void io_outsw(unsigned int addr, const void *buf, int len) -{ - volatile unsigned short *ap = (volatile unsigned short *) addr; - unsigned short *bp = (unsigned short *) buf; - while (len--) - *ap = _swapw(*bp++); -} - -static inline void io_outsl(unsigned int addr, const void *buf, int len) -{ - volatile unsigned long *ap = (volatile unsigned long *) addr; - unsigned long *bp = (unsigned long *) buf; - while (len--) - *ap = _swapl(*bp++); -} - -static inline void io_outsw_noswap(unsigned int addr, const void *buf, int len) -{ - volatile unsigned short *ap = (volatile unsigned short *) addr; - unsigned short *bp = (unsigned short *) buf; - while (len--) - *ap = *bp++; -} - -static inline void io_outsl_noswap(unsigned int addr, const void *buf, int len) -{ - volatile unsigned long *ap = (volatile unsigned long *) addr; - unsigned long *bp = (unsigned long *) buf; - while (len--) - *ap = *bp++; -} - -static inline void io_insb(unsigned int addr, void *buf, int len) -{ - volatile unsigned char *ap_b; - volatile unsigned short *ap_w; - unsigned char *bp = (unsigned char *) buf; - - if(h8300_buswidth(addr)) { - ap_w = (volatile unsigned short *)(addr & ~1); - while (len--) - *bp++ = *ap_w & 0xff; - } else { - ap_b = (volatile unsigned char *)addr; - while (len--) - *bp++ = *ap_b; - } -} - -static inline void io_insw(unsigned int addr, void *buf, int len) -{ - volatile unsigned short *ap = (volatile unsigned short *) addr; - unsigned short *bp = (unsigned short *) buf; - while (len--) - *bp++ = _swapw(*ap); -} - -static inline void io_insl(unsigned int addr, void *buf, int len) -{ - volatile unsigned long *ap = (volatile unsigned long *) addr; - unsigned long *bp = (unsigned long *) buf; - while (len--) - *bp++ = _swapl(*ap); -} - -static inline void io_insw_noswap(unsigned int addr, void *buf, int len) -{ - volatile unsigned short *ap = (volatile unsigned short *) addr; - unsigned short *bp = (unsigned short *) buf; - while (len--) - *bp++ = *ap; -} - -static inline void io_insl_noswap(unsigned int addr, void *buf, int len) -{ - volatile unsigned long *ap = (volatile unsigned long *) addr; - unsigned long *bp = (unsigned long *) buf; - while (len--) - *bp++ = *ap; -} - -/* - * make the short names macros so specific devices - * can override them as required - */ - -#define memset_io(a,b,c) memset((void *)(a),(b),(c)) -#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c)) -#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c)) - -#define mmiowb() - -#define inb(addr) ((h8300_buswidth(addr))?readw((addr) & ~1) & 0xff:readb(addr)) -#define inw(addr) _swapw(readw(addr)) -#define inl(addr) _swapl(readl(addr)) -#define outb(x,addr) ((void)((h8300_buswidth(addr) && \ - ((addr) & 1))?writew(x,(addr) & ~1):writeb(x,addr))) -#define outw(x,addr) ((void) writew(_swapw(x),addr)) -#define outl(x,addr) ((void) writel(_swapl(x),addr)) - -#define inb_p(addr) inb(addr) -#define inw_p(addr) inw(addr) -#define inl_p(addr) inl(addr) -#define outb_p(x,addr) outb(x,addr) -#define outw_p(x,addr) outw(x,addr) -#define outl_p(x,addr) outl(x,addr) - -#define outsb(a,b,l) io_outsb(a,b,l) -#define outsw(a,b,l) io_outsw(a,b,l) -#define outsl(a,b,l) io_outsl(a,b,l) - -#define insb(a,b,l) io_insb(a,b,l) -#define insw(a,b,l) io_insw(a,b,l) -#define insl(a,b,l) io_insl(a,b,l) - -#define IO_SPACE_LIMIT 0xffffff - - -/* Values for nocacheflag and cmode */ -#define IOMAP_FULL_CACHING 0 -#define IOMAP_NOCACHE_SER 1 -#define IOMAP_NOCACHE_NONSER 2 -#define IOMAP_WRITETHROUGH 3 - -extern void *__ioremap(unsigned long physaddr, unsigned long size, int cacheflag); -extern void __iounmap(void *addr, unsigned long size); - -static inline void *ioremap(unsigned long physaddr, unsigned long size) -{ - return __ioremap(physaddr, size, IOMAP_NOCACHE_SER); -} -static inline void *ioremap_nocache(unsigned long physaddr, unsigned long size) -{ - return __ioremap(physaddr, size, IOMAP_NOCACHE_SER); -} -static inline void *ioremap_writethrough(unsigned long physaddr, unsigned long size) -{ - return __ioremap(physaddr, size, IOMAP_WRITETHROUGH); -} -static inline void *ioremap_fullcache(unsigned long physaddr, unsigned long size) -{ - return __ioremap(physaddr, size, IOMAP_FULL_CACHING); -} - -extern void iounmap(void *addr); - -/* H8/300 internal I/O functions */ -static __inline__ unsigned char ctrl_inb(unsigned long addr) -{ - return *(volatile unsigned char*)addr; -} - -static __inline__ unsigned short ctrl_inw(unsigned long addr) -{ - return *(volatile unsigned short*)addr; -} - -static __inline__ unsigned long ctrl_inl(unsigned long addr) -{ - return *(volatile unsigned long*)addr; -} - -static __inline__ void ctrl_outb(unsigned char b, unsigned long addr) -{ - *(volatile unsigned char*)addr = b; -} - -static __inline__ void ctrl_outw(unsigned short b, unsigned long addr) -{ - *(volatile unsigned short*)addr = b; -} - -static __inline__ void ctrl_outl(unsigned long b, unsigned long addr) -{ - *(volatile unsigned long*)addr = b; -} - -static __inline__ void ctrl_bclr(int b, unsigned long addr) -{ - if (__builtin_constant_p(b)) - switch (b) { - case 0: __asm__("bclr #0,@%0"::"r"(addr)); break; - case 1: __asm__("bclr #1,@%0"::"r"(addr)); break; - case 2: __asm__("bclr #2,@%0"::"r"(addr)); break; - case 3: __asm__("bclr #3,@%0"::"r"(addr)); break; - case 4: __asm__("bclr #4,@%0"::"r"(addr)); break; - case 5: __asm__("bclr #5,@%0"::"r"(addr)); break; - case 6: __asm__("bclr #6,@%0"::"r"(addr)); break; - case 7: __asm__("bclr #7,@%0"::"r"(addr)); break; - } - else - __asm__("bclr %w0,@%1"::"r"(b), "r"(addr)); -} - -static __inline__ void ctrl_bset(int b, unsigned long addr) -{ - if (__builtin_constant_p(b)) - switch (b) { - case 0: __asm__("bset #0,@%0"::"r"(addr)); break; - case 1: __asm__("bset #1,@%0"::"r"(addr)); break; - case 2: __asm__("bset #2,@%0"::"r"(addr)); break; - case 3: __asm__("bset #3,@%0"::"r"(addr)); break; - case 4: __asm__("bset #4,@%0"::"r"(addr)); break; - case 5: __asm__("bset #5,@%0"::"r"(addr)); break; - case 6: __asm__("bset #6,@%0"::"r"(addr)); break; - case 7: __asm__("bset #7,@%0"::"r"(addr)); break; - } - else - __asm__("bset %w0,@%1"::"r"(b), "r"(addr)); -} - -/* Pages to physical address... */ -#define page_to_phys(page) ((page - mem_map) << PAGE_SHIFT) -#define page_to_bus(page) ((page - mem_map) << PAGE_SHIFT) - -/* - * Macros used for converting between virtual and physical mappings. - */ -#define phys_to_virt(vaddr) ((void *) (vaddr)) -#define virt_to_phys(vaddr) ((unsigned long) (vaddr)) - -#define virt_to_bus virt_to_phys -#define bus_to_virt phys_to_virt - -/* - * Convert a physical pointer to a virtual kernel pointer for /dev/mem - * access - */ -#define xlate_dev_mem_ptr(p) __va(p) - -/* - * Convert a virtual cached pointer to an uncached pointer - */ -#define xlate_dev_kmem_ptr(p) p - -#endif /* __KERNEL__ */ - -#endif /* _H8300_IO_H */ diff --git a/arch/h8300/include/asm/irq.h b/arch/h8300/include/asm/irq.h deleted file mode 100644 index 13d7c60..0000000 --- a/arch/h8300/include/asm/irq.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef _H8300_IRQ_H_ -#define _H8300_IRQ_H_ - -#include - -#if defined(CONFIG_CPU_H8300H) -#define NR_IRQS 64 -#define EXT_IRQ0 12 -#define EXT_IRQ1 13 -#define EXT_IRQ2 14 -#define EXT_IRQ3 15 -#define EXT_IRQ4 16 -#define EXT_IRQ5 17 -#define EXT_IRQ6 18 -#define EXT_IRQ7 19 -#define EXT_IRQS 5 -#define IER_REGS *(volatile unsigned char *)IER -#endif -#if defined(CONFIG_CPU_H8S) -#define NR_IRQS 128 -#define EXT_IRQ0 16 -#define EXT_IRQ1 17 -#define EXT_IRQ2 18 -#define EXT_IRQ3 19 -#define EXT_IRQ4 20 -#define EXT_IRQ5 21 -#define EXT_IRQ6 22 -#define EXT_IRQ7 23 -#define EXT_IRQ8 24 -#define EXT_IRQ9 25 -#define EXT_IRQ10 26 -#define EXT_IRQ11 27 -#define EXT_IRQ12 28 -#define EXT_IRQ13 29 -#define EXT_IRQ14 30 -#define EXT_IRQ15 31 -#define EXT_IRQS 15 - -#define IER_REGS *(volatile unsigned short *)IER -#endif - -static __inline__ int irq_canonicalize(int irq) -{ - return irq; -} - -typedef void (*h8300_vector)(void); - -#endif /* _H8300_IRQ_H_ */ diff --git a/arch/h8300/include/asm/irq_regs.h b/arch/h8300/include/asm/irq_regs.h deleted file mode 100644 index 3dd9c0b..0000000 --- a/arch/h8300/include/asm/irq_regs.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/arch/h8300/include/asm/irqflags.h b/arch/h8300/include/asm/irqflags.h deleted file mode 100644 index 9617cd5..0000000 --- a/arch/h8300/include/asm/irqflags.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef _H8300_IRQFLAGS_H -#define _H8300_IRQFLAGS_H - -static inline unsigned long arch_local_save_flags(void) -{ - unsigned long flags; - asm volatile ("stc ccr,%w0" : "=r" (flags)); - return flags; -} - -static inline void arch_local_irq_disable(void) -{ - asm volatile ("orc #0x80,ccr" : : : "memory"); -} - -static inline void arch_local_irq_enable(void) -{ - asm volatile ("andc #0x7f,ccr" : : : "memory"); -} - -static inline unsigned long arch_local_irq_save(void) -{ - unsigned long flags = arch_local_save_flags(); - arch_local_irq_disable(); - return flags; -} - -static inline void arch_local_irq_restore(unsigned long flags) -{ - asm volatile ("ldc %w0,ccr" : : "r" (flags) : "memory"); -} - -static inline bool arch_irqs_disabled_flags(unsigned long flags) -{ - return (flags & 0x80) == 0x80; -} - -static inline bool arch_irqs_disabled(void) -{ - return arch_irqs_disabled_flags(arch_local_save_flags()); -} - -#endif /* _H8300_IRQFLAGS_H */ diff --git a/arch/h8300/include/asm/kdebug.h b/arch/h8300/include/asm/kdebug.h deleted file mode 100644 index 6ece1b0..0000000 --- a/arch/h8300/include/asm/kdebug.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/arch/h8300/include/asm/kmap_types.h b/arch/h8300/include/asm/kmap_types.h deleted file mode 100644 index be12a71..0000000 --- a/arch/h8300/include/asm/kmap_types.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _ASM_H8300_KMAP_TYPES_H -#define _ASM_H8300_KMAP_TYPES_H - -#include - -#endif diff --git a/arch/h8300/include/asm/local.h b/arch/h8300/include/asm/local.h deleted file mode 100644 index fdd4efe..0000000 --- a/arch/h8300/include/asm/local.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _H8300_LOCAL_H_ -#define _H8300_LOCAL_H_ - -#include - -#endif diff --git a/arch/h8300/include/asm/local64.h b/arch/h8300/include/asm/local64.h deleted file mode 100644 index 36c93b5..0000000 --- a/arch/h8300/include/asm/local64.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/arch/h8300/include/asm/mc146818rtc.h b/arch/h8300/include/asm/mc146818rtc.h deleted file mode 100644 index ab9d964..0000000 --- a/arch/h8300/include/asm/mc146818rtc.h +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Machine dependent access functions for RTC registers. - */ -#ifndef _H8300_MC146818RTC_H -#define _H8300_MC146818RTC_H - -/* empty include file to satisfy the include in genrtc.c/ide-geometry.c */ - -#endif /* _H8300_MC146818RTC_H */ diff --git a/arch/h8300/include/asm/mmu_context.h b/arch/h8300/include/asm/mmu_context.h deleted file mode 100644 index f44b730..0000000 --- a/arch/h8300/include/asm/mmu_context.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef __H8300_MMU_CONTEXT_H -#define __H8300_MMU_CONTEXT_H - -#include -#include -#include -#include - -static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) -{ -} - -static inline int -init_new_context(struct task_struct *tsk, struct mm_struct *mm) -{ - // mm->context = virt_to_phys(mm->pgd); - return(0); -} - -#define destroy_context(mm) do { } while(0) -#define deactivate_mm(tsk,mm) do { } while(0) - -static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk) -{ -} - -static inline void activate_mm(struct mm_struct *prev_mm, - struct mm_struct *next_mm) -{ -} - -#endif diff --git a/arch/h8300/include/asm/mutex.h b/arch/h8300/include/asm/mutex.h deleted file mode 100644 index 458c1f7..0000000 --- a/arch/h8300/include/asm/mutex.h +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Pull in the generic implementation for the mutex fastpath. - * - * TODO: implement optimized primitives instead, or leave the generic - * implementation in place, or pick the atomic_xchg() based generic - * implementation. (see asm-generic/mutex-xchg.h for details) - */ - -#include diff --git a/arch/h8300/include/asm/page.h b/arch/h8300/include/asm/page.h deleted file mode 100644 index 837381a..0000000 --- a/arch/h8300/include/asm/page.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef _H8300_PAGE_H -#define _H8300_PAGE_H - -/* PAGE_SHIFT determines the page size */ - -#define PAGE_SHIFT (12) -#define PAGE_SIZE (1UL << PAGE_SHIFT) -#define PAGE_MASK (~(PAGE_SIZE-1)) - -#include - -#ifndef __ASSEMBLY__ - -#define get_user_page(vaddr) __get_free_page(GFP_KERNEL) -#define free_user_page(page, addr) free_page(addr) - -#define clear_page(page) memset((page), 0, PAGE_SIZE) -#define copy_page(to,from) memcpy((to), (from), PAGE_SIZE) - -#define clear_user_page(page, vaddr, pg) clear_page(page) -#define copy_user_page(to, from, vaddr, pg) copy_page(to, from) - -#define __alloc_zeroed_user_highpage(movableflags, vma, vaddr) \ - alloc_page_vma(GFP_HIGHUSER | __GFP_ZERO | movableflags, vma, vaddr) -#define __HAVE_ARCH_ALLOC_ZEROED_USER_HIGHPAGE - -/* - * These are used to make use of C type-checking.. - */ -typedef struct { unsigned long pte; } pte_t; -typedef struct { unsigned long pmd[16]; } pmd_t; -typedef struct { unsigned long pgd; } pgd_t; -typedef struct { unsigned long pgprot; } pgprot_t; -typedef struct page *pgtable_t; - -#define pte_val(x) ((x).pte) -#define pmd_val(x) ((&x)->pmd[0]) -#define pgd_val(x) ((x).pgd) -#define pgprot_val(x) ((x).pgprot) - -#define __pte(x) ((pte_t) { (x) } ) -#define __pmd(x) ((pmd_t) { (x) } ) -#define __pgd(x) ((pgd_t) { (x) } ) -#define __pgprot(x) ((pgprot_t) { (x) } ) - -extern unsigned long memory_start; -extern unsigned long memory_end; - -#endif /* !__ASSEMBLY__ */ - -#include - -#define PAGE_OFFSET (PAGE_OFFSET_RAW) - -#ifndef __ASSEMBLY__ - -#define __pa(vaddr) virt_to_phys(vaddr) -#define __va(paddr) phys_to_virt((unsigned long)paddr) - -#define virt_to_pfn(kaddr) (__pa(kaddr) >> PAGE_SHIFT) -#define pfn_to_virt(pfn) __va((pfn) << PAGE_SHIFT) - -#define MAP_NR(addr) (((unsigned long)(addr)-PAGE_OFFSET) >> PAGE_SHIFT) -#define virt_to_page(addr) (mem_map + (((unsigned long)(addr)-PAGE_OFFSET) >> PAGE_SHIFT)) -#define page_to_virt(page) ((((page) - mem_map) << PAGE_SHIFT) + PAGE_OFFSET) -#define pfn_valid(page) (page < max_mapnr) - -#define ARCH_PFN_OFFSET (PAGE_OFFSET >> PAGE_SHIFT) - -#define virt_addr_valid(kaddr) (((void *)(kaddr) >= (void *)PAGE_OFFSET) && \ - ((void *)(kaddr) < (void *)memory_end)) - -#endif /* __ASSEMBLY__ */ - -#include -#include - -#endif /* _H8300_PAGE_H */ diff --git a/arch/h8300/include/asm/page_offset.h b/arch/h8300/include/asm/page_offset.h deleted file mode 100644 index f870646..0000000 --- a/arch/h8300/include/asm/page_offset.h +++ /dev/null @@ -1,3 +0,0 @@ - -#define PAGE_OFFSET_RAW 0x00000000 - diff --git a/arch/h8300/include/asm/param.h b/arch/h8300/include/asm/param.h deleted file mode 100644 index c3909e7..0000000 --- a/arch/h8300/include/asm/param.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _H8300_PARAM_H -#define _H8300_PARAM_H - -#include - -#define HZ CONFIG_HZ -#define USER_HZ HZ -#define CLOCKS_PER_SEC (USER_HZ) -#endif /* _H8300_PARAM_H */ diff --git a/arch/h8300/include/asm/pci.h b/arch/h8300/include/asm/pci.h deleted file mode 100644 index 0b2acaa..0000000 --- a/arch/h8300/include/asm/pci.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef _ASM_H8300_PCI_H -#define _ASM_H8300_PCI_H - -/* - * asm-h8300/pci.h - H8/300 specific PCI declarations. - * - * Yoshinori Sato - */ - -#define pcibios_assign_all_busses() 0 - -static inline void pcibios_penalize_isa_irq(int irq, int active) -{ - /* We don't do dynamic PCI IRQ allocation */ -} - -#define PCI_DMA_BUS_IS_PHYS (1) - -#endif /* _ASM_H8300_PCI_H */ diff --git a/arch/h8300/include/asm/percpu.h b/arch/h8300/include/asm/percpu.h deleted file mode 100644 index 72c03e3..0000000 --- a/arch/h8300/include/asm/percpu.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __ARCH_H8300_PERCPU__ -#define __ARCH_H8300_PERCPU__ - -#include - -#endif /* __ARCH_H8300_PERCPU__ */ diff --git a/arch/h8300/include/asm/pgalloc.h b/arch/h8300/include/asm/pgalloc.h deleted file mode 100644 index c2e89a2..0000000 --- a/arch/h8300/include/asm/pgalloc.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef _H8300_PGALLOC_H -#define _H8300_PGALLOC_H - -#include - -#define check_pgt_cache() do { } while (0) - -#endif /* _H8300_PGALLOC_H */ diff --git a/arch/h8300/include/asm/pgtable.h b/arch/h8300/include/asm/pgtable.h deleted file mode 100644 index 7ca20f8..0000000 --- a/arch/h8300/include/asm/pgtable.h +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef _H8300_PGTABLE_H -#define _H8300_PGTABLE_H - -#include - -#include -#include -#include -#include - -#define pgd_present(pgd) (1) /* pages are always present on NO_MM */ -#define pgd_none(pgd) (0) -#define pgd_bad(pgd) (0) -#define pgd_clear(pgdp) -#define kern_addr_valid(addr) (1) -#define pmd_offset(a, b) ((void *)0) -#define pmd_none(pmd) (1) -#define pgd_offset_k(adrdress) ((pgd_t *)0) -#define pte_offset_kernel(dir, address) ((pte_t *)0) - -#define PAGE_NONE __pgprot(0) /* these mean nothing to NO_MM */ -#define PAGE_SHARED __pgprot(0) /* these mean nothing to NO_MM */ -#define PAGE_COPY __pgprot(0) /* these mean nothing to NO_MM */ -#define PAGE_READONLY __pgprot(0) /* these mean nothing to NO_MM */ -#define PAGE_KERNEL __pgprot(0) /* these mean nothing to NO_MM */ - -extern void paging_init(void); -#define swapper_pg_dir ((pgd_t *) 0) - -#define __swp_type(x) (0) -#define __swp_offset(x) (0) -#define __swp_entry(typ,off) ((swp_entry_t) { ((typ) | ((off) << 7)) }) -#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) -#define __swp_entry_to_pte(x) ((pte_t) { (x).val }) - -static inline int pte_file(pte_t pte) { return 0; } - -/* - * ZERO_PAGE is a global shared page that is always zero: used - * for zero-mapped memory areas etc.. - */ -#define ZERO_PAGE(vaddr) (virt_to_page(0)) - -/* - * These would be in other places but having them here reduces the diffs. - */ -extern unsigned int kobjsize(const void *objp); -extern int is_in_rom(unsigned long); - -/* - * No page table caches to initialise - */ -#define pgtable_cache_init() do { } while (0) - -/* - * All 32bit addresses are effectively valid for vmalloc... - * Sort of meaningless for non-VM targets. - */ -#define VMALLOC_START 0 -#define VMALLOC_END 0xffffffff - -/* - * All 32bit addresses are effectively valid for vmalloc... - * Sort of meaningless for non-VM targets. - */ -#define VMALLOC_START 0 -#define VMALLOC_END 0xffffffff - -#define arch_enter_lazy_cpu_mode() do {} while (0) - -#include - -#endif /* _H8300_PGTABLE_H */ diff --git a/arch/h8300/include/asm/processor.h b/arch/h8300/include/asm/processor.h deleted file mode 100644 index 4b0ca49..0000000 --- a/arch/h8300/include/asm/processor.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * include/asm-h8300/processor.h - * - * Copyright (C) 2002 Yoshinori Sato - * - * Based on: linux/asm-m68nommu/processor.h - * - * Copyright (C) 1995 Hamish Macdonald - */ - -#ifndef __ASM_H8300_PROCESSOR_H -#define __ASM_H8300_PROCESSOR_H - -/* - * Default implementation of macro that returns current - * instruction pointer ("program counter"). - */ -#define current_text_addr() ({ __label__ _l; _l: &&_l;}) - -#include -#include -#include -#include -#include - -static inline unsigned long rdusp(void) { - extern unsigned int sw_usp; - return(sw_usp); -} - -static inline void wrusp(unsigned long usp) { - extern unsigned int sw_usp; - sw_usp = usp; -} - -/* - * User space process size: 3.75GB. This is hardcoded into a few places, - * so don't change it unless you know what you are doing. - */ -#define TASK_SIZE (0xFFFFFFFFUL) - -#ifdef __KERNEL__ -#define STACK_TOP TASK_SIZE -#define STACK_TOP_MAX STACK_TOP -#endif - -/* - * This decides where the kernel will search for a free chunk of vm - * space during mmap's. We won't be using it - */ -#define TASK_UNMAPPED_BASE 0 - -struct thread_struct { - unsigned long ksp; /* kernel stack pointer */ - unsigned long usp; /* user stack pointer */ - unsigned long ccr; /* saved status register */ - unsigned long esp0; /* points to SR of stack frame */ - struct { - unsigned short *addr; - unsigned short inst; - } breakinfo; -}; - -#define INIT_THREAD { \ - .ksp = sizeof(init_stack) + (unsigned long)init_stack, \ - .usp = 0, \ - .ccr = PS_S, \ - .esp0 = 0, \ - .breakinfo = { \ - .addr = (unsigned short *)-1, \ - .inst = 0 \ - } \ -} - -/* - * Do necessary setup to start up a newly executed thread. - * - * pass the data segment into user programs if it exists, - * it can't hurt anything as far as I can tell - */ -#if defined(__H8300H__) -#define start_thread(_regs, _pc, _usp) \ -do { \ - (_regs)->pc = (_pc); \ - (_regs)->ccr = 0x00; /* clear all flags */ \ - (_regs)->er5 = current->mm->start_data; /* GOT base */ \ - wrusp((unsigned long)(_usp) - sizeof(unsigned long)*3); \ -} while(0) -#endif -#if defined(__H8300S__) -#define start_thread(_regs, _pc, _usp) \ -do { \ - (_regs)->pc = (_pc); \ - (_regs)->ccr = 0x00; /* clear kernel flag */ \ - (_regs)->exr = 0x78; /* enable all interrupts */ \ - (_regs)->er5 = current->mm->start_data; /* GOT base */ \ - /* 14 = space for retaddr(4), vector(4), er0(4) and ext(2) on stack */ \ - wrusp(((unsigned long)(_usp)) - 14); \ -} while(0) -#endif - -/* Forward declaration, a strange C thing */ -struct task_struct; - -/* Free all resources held by a thread. */ -static inline void release_thread(struct task_struct *dead_task) -{ -} - -/* - * Free current thread data structures etc.. - */ -static inline void exit_thread(void) -{ -} - -/* - * Return saved PC of a blocked thread. - */ -unsigned long thread_saved_pc(struct task_struct *tsk); -unsigned long get_wchan(struct task_struct *p); - -#define KSTK_EIP(tsk) \ - ({ \ - unsigned long eip = 0; \ - if ((tsk)->thread.esp0 > PAGE_SIZE && \ - MAP_NR((tsk)->thread.esp0) < max_mapnr) \ - eip = ((struct pt_regs *) (tsk)->thread.esp0)->pc; \ - eip; }) -#define KSTK_ESP(tsk) ((tsk) == current ? rdusp() : (tsk)->thread.usp) - -#define cpu_relax() barrier() - -#define HARD_RESET_NOW() ({ \ - local_irq_disable(); \ - asm("jmp @@0"); \ -}) - -#endif diff --git a/arch/h8300/include/asm/ptrace.h b/arch/h8300/include/asm/ptrace.h deleted file mode 100644 index c1826b9..0000000 --- a/arch/h8300/include/asm/ptrace.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef _H8300_PTRACE_H -#define _H8300_PTRACE_H - -#include - -#ifndef __ASSEMBLY__ -#if defined(CONFIG_CPU_H8S) -#endif -#ifndef PS_S -#define PS_S (0x10) -#endif - -#if defined(__H8300H__) -#define H8300_REGS_NO 11 -#endif -#if defined(__H8300S__) -#define H8300_REGS_NO 12 -#endif - -/* Find the stack offset for a register, relative to thread.esp0. */ -#define PT_REG(reg) ((long)&((struct pt_regs *)0)->reg) - -#define arch_has_single_step() (1) - -#define user_mode(regs) (!((regs)->ccr & PS_S)) -#define instruction_pointer(regs) ((regs)->pc) -#define profile_pc(regs) instruction_pointer(regs) -#define current_pt_regs() ((struct pt_regs *) \ - (THREAD_SIZE + (unsigned long)current_thread_info()) - 1) -#define signal_pt_regs() ((struct pt_regs *)current->thread.esp0) -#define current_user_stack_pointer() rdusp() -#endif /* __ASSEMBLY__ */ -#endif /* _H8300_PTRACE_H */ diff --git a/arch/h8300/include/asm/regs267x.h b/arch/h8300/include/asm/regs267x.h deleted file mode 100644 index 1bff731..0000000 --- a/arch/h8300/include/asm/regs267x.h +++ /dev/null @@ -1,336 +0,0 @@ -/* internal Peripherals Register address define */ -/* CPU: H8/306x */ - -#if !defined(__REGS_H8S267x__) -#define __REGS_H8S267x__ - -#if defined(__KERNEL__) - -#define DASTCR 0xFEE01A -#define DADR0 0xFFFFA4 -#define DADR1 0xFFFFA5 -#define DACR01 0xFFFFA6 -#define DADR2 0xFFFFA8 -#define DADR3 0xFFFFA9 -#define DACR23 0xFFFFAA - -#define ADDRA 0xFFFF90 -#define ADDRAH 0xFFFF90 -#define ADDRAL 0xFFFF91 -#define ADDRB 0xFFFF92 -#define ADDRBH 0xFFFF92 -#define ADDRBL 0xFFFF93 -#define ADDRC 0xFFFF94 -#define ADDRCH 0xFFFF94 -#define ADDRCL 0xFFFF95 -#define ADDRD 0xFFFF96 -#define ADDRDH 0xFFFF96 -#define ADDRDL 0xFFFF97 -#define ADDRE 0xFFFF98 -#define ADDREH 0xFFFF98 -#define ADDREL 0xFFFF99 -#define ADDRF 0xFFFF9A -#define ADDRFH 0xFFFF9A -#define ADDRFL 0xFFFF9B -#define ADDRG 0xFFFF9C -#define ADDRGH 0xFFFF9C -#define ADDRGL 0xFFFF9D -#define ADDRH 0xFFFF9E -#define ADDRHH 0xFFFF9E -#define ADDRHL 0xFFFF9F - -#define ADCSR 0xFFFFA0 -#define ADCR 0xFFFFA1 - -#define ABWCR 0xFFFEC0 -#define ASTCR 0xFFFEC1 -#define WTCRAH 0xFFFEC2 -#define WTCRAL 0xFFFEC3 -#define WTCRBH 0xFFFEC4 -#define WTCRBL 0xFFFEC5 -#define RDNCR 0xFFFEC6 -#define CSACRH 0xFFFEC8 -#define CSACRL 0xFFFEC9 -#define BROMCRH 0xFFFECA -#define BROMCRL 0xFFFECB -#define BCR 0xFFFECC -#define DRAMCR 0xFFFED0 -#define DRACCR 0xFFFED2 -#define REFCR 0xFFFED4 -#define RTCNT 0xFFFED6 -#define RTCOR 0xFFFED7 - -#define MAR0AH 0xFFFEE0 -#define MAR0AL 0xFFFEE2 -#define IOAR0A 0xFFFEE4 -#define ETCR0A 0xFFFEE6 -#define MAR0BH 0xFFFEE8 -#define MAR0BL 0xFFFEEA -#define IOAR0B 0xFFFEEC -#define ETCR0B 0xFFFEEE -#define MAR1AH 0xFFFEF0 -#define MAR1AL 0xFFFEF2 -#define IOAR1A 0xFFFEF4 -#define ETCR1A 0xFFFEF6 -#define MAR1BH 0xFFFEF8 -#define MAR1BL 0xFFFEFA -#define IOAR1B 0xFFFEFC -#define ETCR1B 0xFFFEFE -#define DMAWER 0xFFFF20 -#define DMATCR 0xFFFF21 -#define DMACR0A 0xFFFF22 -#define DMACR0B 0xFFFF23 -#define DMACR1A 0xFFFF24 -#define DMACR1B 0xFFFF25 -#define DMABCRH 0xFFFF26 -#define DMABCRL 0xFFFF27 - -#define EDSAR0 0xFFFDC0 -#define EDDAR0 0xFFFDC4 -#define EDTCR0 0xFFFDC8 -#define EDMDR0 0xFFFDCC -#define EDMDR0H 0xFFFDCC -#define EDMDR0L 0xFFFDCD -#define EDACR0 0xFFFDCE -#define EDSAR1 0xFFFDD0 -#define EDDAR1 0xFFFDD4 -#define EDTCR1 0xFFFDD8 -#define EDMDR1 0xFFFDDC -#define EDMDR1H 0xFFFDDC -#define EDMDR1L 0xFFFDDD -#define EDACR1 0xFFFDDE -#define EDSAR2 0xFFFDE0 -#define EDDAR2 0xFFFDE4 -#define EDTCR2 0xFFFDE8 -#define EDMDR2 0xFFFDEC -#define EDMDR2H 0xFFFDEC -#define EDMDR2L 0xFFFDED -#define EDACR2 0xFFFDEE -#define EDSAR3 0xFFFDF0 -#define EDDAR3 0xFFFDF4 -#define EDTCR3 0xFFFDF8 -#define EDMDR3 0xFFFDFC -#define EDMDR3H 0xFFFDFC -#define EDMDR3L 0xFFFDFD -#define EDACR3 0xFFFDFE - -#define IPRA 0xFFFE00 -#define IPRB 0xFFFE02 -#define IPRC 0xFFFE04 -#define IPRD 0xFFFE06 -#define IPRE 0xFFFE08 -#define IPRF 0xFFFE0A -#define IPRG 0xFFFE0C -#define IPRH 0xFFFE0E -#define IPRI 0xFFFE10 -#define IPRJ 0xFFFE12 -#define IPRK 0xFFFE14 -#define ITSR 0xFFFE16 -#define SSIER 0xFFFE18 -#define ISCRH 0xFFFE1A -#define ISCRL 0xFFFE1C - -#define INTCR 0xFFFF31 -#define IER 0xFFFF32 -#define IERH 0xFFFF32 -#define IERL 0xFFFF33 -#define ISR 0xFFFF34 -#define ISRH 0xFFFF34 -#define ISRL 0xFFFF35 - -#define P1DDR 0xFFFE20 -#define P2DDR 0xFFFE21 -#define P3DDR 0xFFFE22 -#define P4DDR 0xFFFE23 -#define P5DDR 0xFFFE24 -#define P6DDR 0xFFFE25 -#define P7DDR 0xFFFE26 -#define P8DDR 0xFFFE27 -#define P9DDR 0xFFFE28 -#define PADDR 0xFFFE29 -#define PBDDR 0xFFFE2A -#define PCDDR 0xFFFE2B -#define PDDDR 0xFFFE2C -#define PEDDR 0xFFFE2D -#define PFDDR 0xFFFE2E -#define PGDDR 0xFFFE2F -#define PHDDR 0xFFFF74 - -#define PFCR0 0xFFFE32 -#define PFCR1 0xFFFE33 -#define PFCR2 0xFFFE34 - -#define PAPCR 0xFFFE36 -#define PBPCR 0xFFFE37 -#define PCPCR 0xFFFE38 -#define PDPCR 0xFFFE39 -#define PEPCR 0xFFFE3A - -#define P3ODR 0xFFFE3C -#define PAODR 0xFFFE3D - -#define P1DR 0xFFFF60 -#define P2DR 0xFFFF61 -#define P3DR 0xFFFF62 -#define P4DR 0xFFFF63 -#define P5DR 0xFFFF64 -#define P6DR 0xFFFF65 -#define P7DR 0xFFFF66 -#define P8DR 0xFFFF67 -#define P9DR 0xFFFF68 -#define PADR 0xFFFF69 -#define PBDR 0xFFFF6A -#define PCDR 0xFFFF6B -#define PDDR 0xFFFF6C -#define PEDR 0xFFFF6D -#define PFDR 0xFFFF6E -#define PGDR 0xFFFF6F -#define PHDR 0xFFFF72 - -#define PORT1 0xFFFF50 -#define PORT2 0xFFFF51 -#define PORT3 0xFFFF52 -#define PORT4 0xFFFF53 -#define PORT5 0xFFFF54 -#define PORT6 0xFFFF55 -#define PORT7 0xFFFF56 -#define PORT8 0xFFFF57 -#define PORT9 0xFFFF58 -#define PORTA 0xFFFF59 -#define PORTB 0xFFFF5A -#define PORTC 0xFFFF5B -#define PORTD 0xFFFF5C -#define PORTE 0xFFFF5D -#define PORTF 0xFFFF5E -#define PORTG 0xFFFF5F -#define PORTH 0xFFFF70 - -#define PCR 0xFFFF46 -#define PMR 0xFFFF47 -#define NDERH 0xFFFF48 -#define NDERL 0xFFFF49 -#define PODRH 0xFFFF4A -#define PODRL 0xFFFF4B -#define NDRH1 0xFFFF4C -#define NDRL1 0xFFFF4D -#define NDRH2 0xFFFF4E -#define NDRL2 0xFFFF4F - -#define SMR0 0xFFFF78 -#define BRR0 0xFFFF79 -#define SCR0 0xFFFF7A -#define TDR0 0xFFFF7B -#define SSR0 0xFFFF7C -#define RDR0 0xFFFF7D -#define SCMR0 0xFFFF7E -#define SMR1 0xFFFF80 -#define BRR1 0xFFFF81 -#define SCR1 0xFFFF82 -#define TDR1 0xFFFF83 -#define SSR1 0xFFFF84 -#define RDR1 0xFFFF85 -#define SCMR1 0xFFFF86 -#define SMR2 0xFFFF88 -#define BRR2 0xFFFF89 -#define SCR2 0xFFFF8A -#define TDR2 0xFFFF8B -#define SSR2 0xFFFF8C -#define RDR2 0xFFFF8D -#define SCMR2 0xFFFF8E - -#define IRCR0 0xFFFE1E -#define SEMR 0xFFFDA8 - -#define MDCR 0xFFFF3E -#define SYSCR 0xFFFF3D -#define MSTPCRH 0xFFFF40 -#define MSTPCRL 0xFFFF41 -#define FLMCR1 0xFFFFC8 -#define FLMCR2 0xFFFFC9 -#define EBR1 0xFFFFCA -#define EBR2 0xFFFFCB -#define CTGARC_RAMCR 0xFFFECE -#define SBYCR 0xFFFF3A -#define SCKCR 0xFFFF3B -#define PLLCR 0xFFFF45 - -#define TSTR 0xFFFFC0 -#define TSNC 0XFFFFC1 - -#define TCR0 0xFFFFD0 -#define TMDR0 0xFFFFD1 -#define TIORH0 0xFFFFD2 -#define TIORL0 0xFFFFD3 -#define TIER0 0xFFFFD4 -#define TSR0 0xFFFFD5 -#define TCNT0 0xFFFFD6 -#define GRA0 0xFFFFD8 -#define GRB0 0xFFFFDA -#define GRC0 0xFFFFDC -#define GRD0 0xFFFFDE -#define TCR1 0xFFFFE0 -#define TMDR1 0xFFFFE1 -#define TIORH1 0xFFFFE2 -#define TIORL1 0xFFFFE3 -#define TIER1 0xFFFFE4 -#define TSR1 0xFFFFE5 -#define TCNT1 0xFFFFE6 -#define GRA1 0xFFFFE8 -#define GRB1 0xFFFFEA -#define TCR2 0xFFFFF0 -#define TMDR2 0xFFFFF1 -#define TIORH2 0xFFFFF2 -#define TIORL2 0xFFFFF3 -#define TIER2 0xFFFFF4 -#define TSR2 0xFFFFF5 -#define TCNT2 0xFFFFF6 -#define GRA2 0xFFFFF8 -#define GRB2 0xFFFFFA -#define TCR3 0xFFFE80 -#define TMDR3 0xFFFE81 -#define TIORH3 0xFFFE82 -#define TIORL3 0xFFFE83 -#define TIER3 0xFFFE84 -#define TSR3 0xFFFE85 -#define TCNT3 0xFFFE86 -#define GRA3 0xFFFE88 -#define GRB3 0xFFFE8A -#define GRC3 0xFFFE8C -#define GRD3 0xFFFE8E -#define TCR4 0xFFFE90 -#define TMDR4 0xFFFE91 -#define TIORH4 0xFFFE92 -#define TIORL4 0xFFFE93 -#define TIER4 0xFFFE94 -#define TSR4 0xFFFE95 -#define TCNT4 0xFFFE96 -#define GRA4 0xFFFE98 -#define GRB4 0xFFFE9A -#define TCR5 0xFFFEA0 -#define TMDR5 0xFFFEA1 -#define TIORH5 0xFFFEA2 -#define TIORL5 0xFFFEA3 -#define TIER5 0xFFFEA4 -#define TSR5 0xFFFEA5 -#define TCNT5 0xFFFEA6 -#define GRA5 0xFFFEA8 -#define GRB5 0xFFFEAA - -#define _8TCR0 0xFFFFB0 -#define _8TCR1 0xFFFFB1 -#define _8TCSR0 0xFFFFB2 -#define _8TCSR1 0xFFFFB3 -#define _8TCORA0 0xFFFFB4 -#define _8TCORA1 0xFFFFB5 -#define _8TCORB0 0xFFFFB6 -#define _8TCORB1 0xFFFFB7 -#define _8TCNT0 0xFFFFB8 -#define _8TCNT1 0xFFFFB9 - -#define TCSR 0xFFFFBC -#define TCNT 0xFFFFBD -#define RSTCSRW 0xFFFFBE -#define RSTCSRR 0xFFFFBF - -#endif /* __KERNEL__ */ -#endif /* __REGS_H8S267x__ */ diff --git a/arch/h8300/include/asm/regs306x.h b/arch/h8300/include/asm/regs306x.h deleted file mode 100644 index 027dd63..0000000 --- a/arch/h8300/include/asm/regs306x.h +++ /dev/null @@ -1,212 +0,0 @@ -/* internal Peripherals Register address define */ -/* CPU: H8/306x */ - -#if !defined(__REGS_H8306x__) -#define __REGS_H8306x__ - -#if defined(__KERNEL__) - -#define DASTCR 0xFEE01A -#define DADR0 0xFEE09C -#define DADR1 0xFEE09D -#define DACR 0xFEE09E - -#define ADDRAH 0xFFFFE0 -#define ADDRAL 0xFFFFE1 -#define ADDRBH 0xFFFFE2 -#define ADDRBL 0xFFFFE3 -#define ADDRCH 0xFFFFE4 -#define ADDRCL 0xFFFFE5 -#define ADDRDH 0xFFFFE6 -#define ADDRDL 0xFFFFE7 -#define ADCSR 0xFFFFE8 -#define ADCR 0xFFFFE9 - -#define BRCR 0xFEE013 -#define ADRCR 0xFEE01E -#define CSCR 0xFEE01F -#define ABWCR 0xFEE020 -#define ASTCR 0xFEE021 -#define WCRH 0xFEE022 -#define WCRL 0xFEE023 -#define BCR 0xFEE024 -#define DRCRA 0xFEE026 -#define DRCRB 0xFEE027 -#define RTMCSR 0xFEE028 -#define RTCNT 0xFEE029 -#define RTCOR 0xFEE02A - -#define MAR0AR 0xFFFF20 -#define MAR0AE 0xFFFF21 -#define MAR0AH 0xFFFF22 -#define MAR0AL 0xFFFF23 -#define ETCR0AL 0xFFFF24 -#define ETCR0AH 0xFFFF25 -#define IOAR0A 0xFFFF26 -#define DTCR0A 0xFFFF27 -#define MAR0BR 0xFFFF28 -#define MAR0BE 0xFFFF29 -#define MAR0BH 0xFFFF2A -#define MAR0BL 0xFFFF2B -#define ETCR0BL 0xFFFF2C -#define ETCR0BH 0xFFFF2D -#define IOAR0B 0xFFFF2E -#define DTCR0B 0xFFFF2F -#define MAR1AR 0xFFFF30 -#define MAR1AE 0xFFFF31 -#define MAR1AH 0xFFFF32 -#define MAR1AL 0xFFFF33 -#define ETCR1AL 0xFFFF34 -#define ETCR1AH 0xFFFF35 -#define IOAR1A 0xFFFF36 -#define DTCR1A 0xFFFF37 -#define MAR1BR 0xFFFF38 -#define MAR1BE 0xFFFF39 -#define MAR1BH 0xFFFF3A -#define MAR1BL 0xFFFF3B -#define ETCR1BL 0xFFFF3C -#define ETCR1BH 0xFFFF3D -#define IOAR1B 0xFFFF3E -#define DTCR1B 0xFFFF3F - -#define ISCR 0xFEE014 -#define IER 0xFEE015 -#define ISR 0xFEE016 -#define IPRA 0xFEE018 -#define IPRB 0xFEE019 - -#define P1DDR 0xFEE000 -#define P2DDR 0xFEE001 -#define P3DDR 0xFEE002 -#define P4DDR 0xFEE003 -#define P5DDR 0xFEE004 -#define P6DDR 0xFEE005 -/*#define P7DDR 0xFEE006*/ -#define P8DDR 0xFEE007 -#define P9DDR 0xFEE008 -#define PADDR 0xFEE009 -#define PBDDR 0xFEE00A - -#define P1DR 0xFFFFD0 -#define P2DR 0xFFFFD1 -#define P3DR 0xFFFFD2 -#define P4DR 0xFFFFD3 -#define P5DR 0xFFFFD4 -#define P6DR 0xFFFFD5 -/*#define P7DR 0xFFFFD6*/ -#define P8DR 0xFFFFD7 -#define P9DR 0xFFFFD8 -#define PADR 0xFFFFD9 -#define PBDR 0xFFFFDA - -#define P2CR 0xFEE03C -#define P4CR 0xFEE03E -#define P5CR 0xFEE03F - -#define SMR0 0xFFFFB0 -#define BRR0 0xFFFFB1 -#define SCR0 0xFFFFB2 -#define TDR0 0xFFFFB3 -#define SSR0 0xFFFFB4 -#define RDR0 0xFFFFB5 -#define SCMR0 0xFFFFB6 -#define SMR1 0xFFFFB8 -#define BRR1 0xFFFFB9 -#define SCR1 0xFFFFBA -#define TDR1 0xFFFFBB -#define SSR1 0xFFFFBC -#define RDR1 0xFFFFBD -#define SCMR1 0xFFFFBE -#define SMR2 0xFFFFC0 -#define BRR2 0xFFFFC1 -#define SCR2 0xFFFFC2 -#define TDR2 0xFFFFC3 -#define SSR2 0xFFFFC4 -#define RDR2 0xFFFFC5 -#define SCMR2 0xFFFFC6 - -#define MDCR 0xFEE011 -#define SYSCR 0xFEE012 -#define DIVCR 0xFEE01B -#define MSTCRH 0xFEE01C -#define MSTCRL 0xFEE01D -#define FLMCR1 0xFEE030 -#define FLMCR2 0xFEE031 -#define EBR1 0xFEE032 -#define EBR2 0xFEE033 -#define RAMCR 0xFEE077 - -#define TSTR 0xFFFF60 -#define TSNC 0XFFFF61 -#define TMDR 0xFFFF62 -#define TOLR 0xFFFF63 -#define TISRA 0xFFFF64 -#define TISRB 0xFFFF65 -#define TISRC 0xFFFF66 -#define TCR0 0xFFFF68 -#define TIOR0 0xFFFF69 -#define TCNT0H 0xFFFF6A -#define TCNT0L 0xFFFF6B -#define GRA0H 0xFFFF6C -#define GRA0L 0xFFFF6D -#define GRB0H 0xFFFF6E -#define GRB0L 0xFFFF6F -#define TCR1 0xFFFF70 -#define TIOR1 0xFFFF71 -#define TCNT1H 0xFFFF72 -#define TCNT1L 0xFFFF73 -#define GRA1H 0xFFFF74 -#define GRA1L 0xFFFF75 -#define GRB1H 0xFFFF76 -#define GRB1L 0xFFFF77 -#define TCR3 0xFFFF78 -#define TIOR3 0xFFFF79 -#define TCNT3H 0xFFFF7A -#define TCNT3L 0xFFFF7B -#define GRA3H 0xFFFF7C -#define GRA3L 0xFFFF7D -#define GRB3H 0xFFFF7E -#define GRB3L 0xFFFF7F - -#define _8TCR0 0xFFFF80 -#define _8TCR1 0xFFFF81 -#define _8TCSR0 0xFFFF82 -#define _8TCSR1 0xFFFF83 -#define TCORA0 0xFFFF84 -#define TCORA1 0xFFFF85 -#define TCORB0 0xFFFF86 -#define TCORB1 0xFFFF87 -#define _8TCNT0 0xFFFF88 -#define _8TCNT1 0xFFFF89 - -#define _8TCR2 0xFFFF90 -#define _8TCR3 0xFFFF91 -#define _8TCSR2 0xFFFF92 -#define _8TCSR3 0xFFFF93 -#define TCORA2 0xFFFF94 -#define TCORA3 0xFFFF95 -#define TCORB2 0xFFFF96 -#define TCORB3 0xFFFF97 -#define _8TCNT2 0xFFFF98 -#define _8TCNT3 0xFFFF99 - -#define TCSR 0xFFFF8C -#define TCNT 0xFFFF8D -#define RSTCSR 0xFFFF8F - -#define TPMR 0xFFFFA0 -#define TPCR 0xFFFFA1 -#define NDERB 0xFFFFA2 -#define NDERA 0xFFFFA3 -#define NDRB1 0xFFFFA4 -#define NDRA1 0xFFFFA5 -#define NDRB2 0xFFFFA6 -#define NDRA2 0xFFFFA7 - -#define TCSR 0xFFFF8C -#define TCNT 0xFFFF8D -#define RSTCSRW 0xFFFF8E -#define RSTCSRR 0xFFFF8F - -#endif /* __KERNEL__ */ -#endif /* __REGS_H8306x__ */ diff --git a/arch/h8300/include/asm/scatterlist.h b/arch/h8300/include/asm/scatterlist.h deleted file mode 100644 index 82130ed..0000000 --- a/arch/h8300/include/asm/scatterlist.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _H8300_SCATTERLIST_H -#define _H8300_SCATTERLIST_H - -#include - -#endif /* !(_H8300_SCATTERLIST_H) */ diff --git a/arch/h8300/include/asm/sections.h b/arch/h8300/include/asm/sections.h deleted file mode 100644 index a81743e..0000000 --- a/arch/h8300/include/asm/sections.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _H8300_SECTIONS_H_ -#define _H8300_SECTIONS_H_ - -#include - -#endif diff --git a/arch/h8300/include/asm/segment.h b/arch/h8300/include/asm/segment.h deleted file mode 100644 index b79a82d..0000000 --- a/arch/h8300/include/asm/segment.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef _H8300_SEGMENT_H -#define _H8300_SEGMENT_H - -/* define constants */ -#define USER_DATA (1) -#ifndef __USER_DS -#define __USER_DS (USER_DATA) -#endif -#define USER_PROGRAM (2) -#define SUPER_DATA (3) -#ifndef __KERNEL_DS -#define __KERNEL_DS (SUPER_DATA) -#endif -#define SUPER_PROGRAM (4) - -#ifndef __ASSEMBLY__ - -typedef struct { - unsigned long seg; -} mm_segment_t; - -#define MAKE_MM_SEG(s) ((mm_segment_t) { (s) }) -#define USER_DS MAKE_MM_SEG(__USER_DS) -#define KERNEL_DS MAKE_MM_SEG(__KERNEL_DS) - -/* - * Get/set the SFC/DFC registers for MOVES instructions - */ - -static inline mm_segment_t get_fs(void) -{ - return USER_DS; -} - -static inline mm_segment_t get_ds(void) -{ - /* return the supervisor data space code */ - return KERNEL_DS; -} - -static inline void set_fs(mm_segment_t val) -{ -} - -#define segment_eq(a,b) ((a).seg == (b).seg) - -#endif /* __ASSEMBLY__ */ - -#endif /* _H8300_SEGMENT_H */ diff --git a/arch/h8300/include/asm/sh_bios.h b/arch/h8300/include/asm/sh_bios.h deleted file mode 100644 index b6bb6e5..0000000 --- a/arch/h8300/include/asm/sh_bios.h +++ /dev/null @@ -1,29 +0,0 @@ -/* eCos HAL interface header */ - -#ifndef SH_BIOS_H -#define SH_BIOS_H - -#define HAL_IF_VECTOR_TABLE 0xfffe20 -#define CALL_IF_SET_CONSOLE_COMM 13 -#define QUERY_CURRENT -1 -#define MANGLER -3 - -/* Checking for GDB stub active */ -/* suggestion Jonathan Larmour */ -static int sh_bios_in_gdb_mode(void) -{ - static int gdb_active = -1; - if (gdb_active == -1) { - int (*set_console_comm)(int); - set_console_comm = ((void **)HAL_IF_VECTOR_TABLE)[CALL_IF_SET_CONSOLE_COMM]; - gdb_active = (set_console_comm(QUERY_CURRENT) == MANGLER); - } - return gdb_active; -} - -static void sh_bios_gdb_detach(void) -{ - -} - -#endif diff --git a/arch/h8300/include/asm/shm.h b/arch/h8300/include/asm/shm.h deleted file mode 100644 index ed6623c..0000000 --- a/arch/h8300/include/asm/shm.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef _H8300_SHM_H -#define _H8300_SHM_H - - -/* format of page table entries that correspond to shared memory pages - currently out in swap space (see also mm/swap.c): - bits 0-1 (PAGE_PRESENT) is = 0 - bits 8..2 (SWP_TYPE) are = SHM_SWP_TYPE - bits 31..9 are used like this: - bits 15..9 (SHM_ID) the id of the shared memory segment - bits 30..16 (SHM_IDX) the index of the page within the shared memory segment - (actually only bits 25..16 get used since SHMMAX is so low) - bit 31 (SHM_READ_ONLY) flag whether the page belongs to a read-only attach -*/ -/* on the m68k both bits 0 and 1 must be zero */ -/* format on the sun3 is similar, but bits 30, 31 are set to zero and all - others are reduced by 2. --m */ - -#ifndef CONFIG_SUN3 -#define SHM_ID_SHIFT 9 -#else -#define SHM_ID_SHIFT 7 -#endif -#define _SHM_ID_BITS 7 -#define SHM_ID_MASK ((1<<_SHM_ID_BITS)-1) - -#define SHM_IDX_SHIFT (SHM_ID_SHIFT+_SHM_ID_BITS) -#define _SHM_IDX_BITS 15 -#define SHM_IDX_MASK ((1<<_SHM_IDX_BITS)-1) - -#endif /* _H8300_SHM_H */ diff --git a/arch/h8300/include/asm/shmparam.h b/arch/h8300/include/asm/shmparam.h deleted file mode 100644 index d186395..0000000 --- a/arch/h8300/include/asm/shmparam.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _H8300_SHMPARAM_H -#define _H8300_SHMPARAM_H - -#define SHMLBA PAGE_SIZE /* attach addr a multiple of this */ - -#endif /* _H8300_SHMPARAM_H */ diff --git a/arch/h8300/include/asm/signal.h b/arch/h8300/include/asm/signal.h deleted file mode 100644 index 6341e36..0000000 --- a/arch/h8300/include/asm/signal.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef _H8300_SIGNAL_H -#define _H8300_SIGNAL_H - -#include - -/* Most things should be clean enough to redefine this at will, if care - is taken to make libc match. */ - -#define _NSIG 64 -#define _NSIG_BPW 32 -#define _NSIG_WORDS (_NSIG / _NSIG_BPW) - -typedef unsigned long old_sigset_t; /* at least 32 bits */ - -typedef struct { - unsigned long sig[_NSIG_WORDS]; -} sigset_t; - -#define __ARCH_HAS_SA_RESTORER - -#include -#undef __HAVE_ARCH_SIG_BITOPS - -#endif /* _H8300_SIGNAL_H */ diff --git a/arch/h8300/include/asm/smp.h b/arch/h8300/include/asm/smp.h deleted file mode 100644 index 9e9bd7e..0000000 --- a/arch/h8300/include/asm/smp.h +++ /dev/null @@ -1 +0,0 @@ -/* nothing required here yet */ diff --git a/arch/h8300/include/asm/spinlock.h b/arch/h8300/include/asm/spinlock.h deleted file mode 100644 index d5407fa..0000000 --- a/arch/h8300/include/asm/spinlock.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __H8300_SPINLOCK_H -#define __H8300_SPINLOCK_H - -#error "H8/300 doesn't do SMP yet" - -#endif diff --git a/arch/h8300/include/asm/string.h b/arch/h8300/include/asm/string.h deleted file mode 100644 index ca50348..0000000 --- a/arch/h8300/include/asm/string.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef _H8300_STRING_H_ -#define _H8300_STRING_H_ - -#ifdef __KERNEL__ /* only set these up for kernel code */ - -#include -#include - -#define __HAVE_ARCH_MEMSET -extern void * memset(void * s, int c, size_t count); - -#define __HAVE_ARCH_MEMCPY -extern void * memcpy(void *d, const void *s, size_t count); - -#else /* KERNEL */ - -/* - * let user libraries deal with these, - * IMHO the kernel has no place defining these functions for user apps - */ - -#define __HAVE_ARCH_STRCPY 1 -#define __HAVE_ARCH_STRNCPY 1 -#define __HAVE_ARCH_STRCAT 1 -#define __HAVE_ARCH_STRNCAT 1 -#define __HAVE_ARCH_STRCMP 1 -#define __HAVE_ARCH_STRNCMP 1 -#define __HAVE_ARCH_STRNICMP 1 -#define __HAVE_ARCH_STRCHR 1 -#define __HAVE_ARCH_STRRCHR 1 -#define __HAVE_ARCH_STRSTR 1 -#define __HAVE_ARCH_STRLEN 1 -#define __HAVE_ARCH_STRNLEN 1 -#define __HAVE_ARCH_MEMSET 1 -#define __HAVE_ARCH_MEMCPY 1 -#define __HAVE_ARCH_MEMMOVE 1 -#define __HAVE_ARCH_MEMSCAN 1 -#define __HAVE_ARCH_MEMCMP 1 -#define __HAVE_ARCH_MEMCHR 1 -#define __HAVE_ARCH_STRTOK 1 - -#endif /* KERNEL */ - -#endif /* _M68K_STRING_H_ */ diff --git a/arch/h8300/include/asm/switch_to.h b/arch/h8300/include/asm/switch_to.h deleted file mode 100644 index cdd8731..0000000 --- a/arch/h8300/include/asm/switch_to.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef _H8300_SWITCH_TO_H -#define _H8300_SWITCH_TO_H - -/* - * switch_to(n) should switch tasks to task ptr, first checking that - * ptr isn't the current task, in which case it does nothing. This - * also clears the TS-flag if the task we switched to has used the - * math co-processor latest. - */ -/* - * switch_to() saves the extra registers, that are not saved - * automatically by SAVE_SWITCH_STACK in resume(), ie. d0-d5 and - * a0-a1. Some of these are used by schedule() and its predecessors - * and so we might get see unexpected behaviors when a task returns - * with unexpected register values. - * - * syscall stores these registers itself and none of them are used - * by syscall after the function in the syscall has been called. - * - * Beware that resume now expects *next to be in d1 and the offset of - * tss to be in a1. This saves a few instructions as we no longer have - * to push them onto the stack and read them back right after. - * - * 02/17/96 - Jes Sorensen (jds@kom.auc.dk) - * - * Changed 96/09/19 by Andreas Schwab - * pass prev in a0, next in a1, offset of tss in d1, and whether - * the mm structures are shared in d2 (to avoid atc flushing). - * - * H8/300 Porting 2002/09/04 Yoshinori Sato - */ - -asmlinkage void resume(void); -#define switch_to(prev,next,last) { \ - void *_last; \ - __asm__ __volatile__( \ - "mov.l %1, er0\n\t" \ - "mov.l %2, er1\n\t" \ - "mov.l %3, er2\n\t" \ - "jsr @_resume\n\t" \ - "mov.l er2,%0\n\t" \ - : "=r" (_last) \ - : "r" (&(prev->thread)), \ - "r" (&(next->thread)), \ - "g" (prev) \ - : "cc", "er0", "er1", "er2", "er3"); \ - (last) = _last; \ -} - -#endif /* _H8300_SWITCH_TO_H */ diff --git a/arch/h8300/include/asm/target_time.h b/arch/h8300/include/asm/target_time.h deleted file mode 100644 index 9f2a9aa..0000000 --- a/arch/h8300/include/asm/target_time.h +++ /dev/null @@ -1,4 +0,0 @@ -extern int platform_timer_setup(void (*timer_int)(int, void *, struct pt_regs *)); -extern void platform_timer_eoi(void); -extern void platform_gettod(unsigned int *year, unsigned int *mon, unsigned int *day, - unsigned int *hour, unsigned int *min, unsigned int *sec); diff --git a/arch/h8300/include/asm/termios.h b/arch/h8300/include/asm/termios.h deleted file mode 100644 index 93a63df..0000000 --- a/arch/h8300/include/asm/termios.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef _H8300_TERMIOS_H -#define _H8300_TERMIOS_H - -#include - -/* intr=^C quit=^| erase=del kill=^U - eof=^D vtime=\0 vmin=\1 sxtc=\0 - start=^Q stop=^S susp=^Z eol=\0 - reprint=^R discard=^U werase=^W lnext=^V - eol2=\0 -*/ -#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0" - -/* - * Translate a "termio" structure into a "termios". Ugh. - */ -#define user_termio_to_kernel_termios(termios, termio) \ -({ \ - unsigned short tmp; \ - get_user(tmp, &(termio)->c_iflag); \ - (termios)->c_iflag = (0xffff0000 & ((termios)->c_iflag)) | tmp; \ - get_user(tmp, &(termio)->c_oflag); \ - (termios)->c_oflag = (0xffff0000 & ((termios)->c_oflag)) | tmp; \ - get_user(tmp, &(termio)->c_cflag); \ - (termios)->c_cflag = (0xffff0000 & ((termios)->c_cflag)) | tmp; \ - get_user(tmp, &(termio)->c_lflag); \ - (termios)->c_lflag = (0xffff0000 & ((termios)->c_lflag)) | tmp; \ - get_user((termios)->c_line, &(termio)->c_line); \ - copy_from_user((termios)->c_cc, (termio)->c_cc, NCC); \ -}) - -/* - * Translate a "termios" structure into a "termio". Ugh. - */ -#define kernel_termios_to_user_termio(termio, termios) \ -({ \ - put_user((termios)->c_iflag, &(termio)->c_iflag); \ - put_user((termios)->c_oflag, &(termio)->c_oflag); \ - put_user((termios)->c_cflag, &(termio)->c_cflag); \ - put_user((termios)->c_lflag, &(termio)->c_lflag); \ - put_user((termios)->c_line, &(termio)->c_line); \ - copy_to_user((termio)->c_cc, (termios)->c_cc, NCC); \ -}) - -#define user_termios_to_kernel_termios(k, u) copy_from_user(k, u, sizeof(struct termios2)) -#define kernel_termios_to_user_termios(u, k) copy_to_user(u, k, sizeof(struct termios2)) -#define user_termios_to_kernel_termios_1(k, u) copy_from_user(k, u, sizeof(struct termios)) -#define kernel_termios_to_user_termios_1(u, k) copy_to_user(u, k, sizeof(struct termios)) - -#endif /* _H8300_TERMIOS_H */ diff --git a/arch/h8300/include/asm/thread_info.h b/arch/h8300/include/asm/thread_info.h deleted file mode 100644 index ec2f777..0000000 --- a/arch/h8300/include/asm/thread_info.h +++ /dev/null @@ -1,103 +0,0 @@ -/* thread_info.h: h8300 low-level thread information - * adapted from the i386 and PPC versions by Yoshinori Sato - * - * Copyright (C) 2002 David Howells (dhowells@redhat.com) - * - Incorporating suggestions made by Linus Torvalds and Dave Miller - */ - -#ifndef _ASM_THREAD_INFO_H -#define _ASM_THREAD_INFO_H - -#include - -#ifdef __KERNEL__ - -#ifndef __ASSEMBLY__ - -/* - * low level task data. - * If you change this, change the TI_* offsets below to match. - */ -struct thread_info { - struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ - unsigned long flags; /* low level flags */ - int cpu; /* cpu we're on */ - int preempt_count; /* 0 => preemptable, <0 => BUG */ - struct restart_block restart_block; -}; - -/* - * macros/functions for gaining access to the thread information structure - */ -#define INIT_THREAD_INFO(tsk) \ -{ \ - .task = &tsk, \ - .exec_domain = &default_exec_domain, \ - .flags = 0, \ - .cpu = 0, \ - .preempt_count = INIT_PREEMPT_COUNT, \ - .restart_block = { \ - .fn = do_no_restart_syscall, \ - }, \ -} - -#define init_thread_info (init_thread_union.thread_info) -#define init_stack (init_thread_union.stack) - - -/* - * Size of kernel stack for each process. This must be a power of 2... - */ -#define THREAD_SIZE_ORDER 1 -#define THREAD_SIZE 8192 /* 2 pages */ - - -/* how to get the thread information struct from C */ -static inline struct thread_info *current_thread_info(void) -{ - struct thread_info *ti; - __asm__( - "mov.l sp, %0 \n\t" - "and.l %1, %0" - : "=&r"(ti) - : "i" (~(THREAD_SIZE-1)) - ); - return ti; -} - -#endif /* __ASSEMBLY__ */ - -/* - * Offsets in thread_info structure, used in assembly code - */ -#define TI_TASK 0 -#define TI_EXECDOMAIN 4 -#define TI_FLAGS 8 -#define TI_CPU 12 -#define TI_PRE_COUNT 16 - -#define PREEMPT_ACTIVE 0x4000000 - -/* - * thread information flag bit numbers - */ -#define TIF_SYSCALL_TRACE 0 /* syscall trace active */ -#define TIF_SIGPENDING 1 /* signal pending */ -#define TIF_NEED_RESCHED 2 /* rescheduling necessary */ -#define TIF_MEMDIE 4 /* is terminating due to OOM killer */ -#define TIF_RESTORE_SIGMASK 5 /* restore signal mask in do_signal() */ -#define TIF_NOTIFY_RESUME 6 /* callback before returning to user */ - -/* as above, but as bit values */ -#define _TIF_SYSCALL_TRACE (1< limit) \ - break; \ - } \ - if (div == ARRAY_SIZE(divide_rate)) \ - panic("Timer counter overflow"); \ - cnt /= divide_rate[div]; \ -} while(0) - -#endif diff --git a/arch/h8300/include/asm/timex.h b/arch/h8300/include/asm/timex.h deleted file mode 100644 index 23e6701..0000000 --- a/arch/h8300/include/asm/timex.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * linux/include/asm-h8300/timex.h - * - * H8/300 architecture timex specifications - */ -#ifndef _ASM_H8300_TIMEX_H -#define _ASM_H8300_TIMEX_H - -#define CLOCK_TICK_RATE (CONFIG_CPU_CLOCK*1000/8192) /* Timer input freq. */ - -typedef unsigned long cycles_t; -extern short h8300_timer_count; - -static inline cycles_t get_cycles(void) -{ - return 0; -} - -#endif diff --git a/arch/h8300/include/asm/tlb.h b/arch/h8300/include/asm/tlb.h deleted file mode 100644 index 7f07430..0000000 --- a/arch/h8300/include/asm/tlb.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef __H8300_TLB_H__ -#define __H8300_TLB_H__ - -#define tlb_flush(tlb) do { } while(0) - -#include - -#endif diff --git a/arch/h8300/include/asm/tlbflush.h b/arch/h8300/include/asm/tlbflush.h deleted file mode 100644 index 41c148a..0000000 --- a/arch/h8300/include/asm/tlbflush.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef _H8300_TLBFLUSH_H -#define _H8300_TLBFLUSH_H - -/* - * Copyright (C) 2000 Lineo, David McCullough - * Copyright (C) 2000-2002, Greg Ungerer - */ - -#include - -/* - * flush all user-space atc entries. - */ -static inline void __flush_tlb(void) -{ - BUG(); -} - -static inline void __flush_tlb_one(unsigned long addr) -{ - BUG(); -} - -#define flush_tlb() __flush_tlb() - -/* - * flush all atc entries (both kernel and user-space entries). - */ -static inline void flush_tlb_all(void) -{ - BUG(); -} - -static inline void flush_tlb_mm(struct mm_struct *mm) -{ - BUG(); -} - -static inline void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) -{ - BUG(); -} - -static inline void flush_tlb_range(struct mm_struct *mm, - unsigned long start, unsigned long end) -{ - BUG(); -} - -static inline void flush_tlb_kernel_page(unsigned long addr) -{ - BUG(); -} - -#endif /* _H8300_TLBFLUSH_H */ diff --git a/arch/h8300/include/asm/topology.h b/arch/h8300/include/asm/topology.h deleted file mode 100644 index fdc1219..0000000 --- a/arch/h8300/include/asm/topology.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _ASM_H8300_TOPOLOGY_H -#define _ASM_H8300_TOPOLOGY_H - -#include - -#endif /* _ASM_H8300_TOPOLOGY_H */ diff --git a/arch/h8300/include/asm/traps.h b/arch/h8300/include/asm/traps.h deleted file mode 100644 index 41cf6be..0000000 --- a/arch/h8300/include/asm/traps.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * linux/include/asm-h8300/traps.h - * - * Copyright (C) 2003 Yoshinori Sato - * - * 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. - */ - -#ifndef _H8300_TRAPS_H -#define _H8300_TRAPS_H - -extern void system_call(void); -extern void interrupt_entry(void); -extern void trace_break(void); - -#define JMP_OP 0x5a000000 -#define JSR_OP 0x5e000000 -#define VECTOR(address) ((JMP_OP)|((unsigned long)address)) -#define REDIRECT(address) ((JSR_OP)|((unsigned long)address)) - -#define TRACE_VEC 5 - -#define TRAP0_VEC 8 -#define TRAP1_VEC 9 -#define TRAP2_VEC 10 -#define TRAP3_VEC 11 - -#if defined(__H8300H__) -#define NR_TRAPS 12 -#endif -#if defined(__H8300S__) -#define NR_TRAPS 16 -#endif - -#endif /* _H8300_TRAPS_H */ diff --git a/arch/h8300/include/asm/types.h b/arch/h8300/include/asm/types.h deleted file mode 100644 index c012707..0000000 --- a/arch/h8300/include/asm/types.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _H8300_TYPES_H -#define _H8300_TYPES_H - -#include - - -#define BITS_PER_LONG 32 - -#endif /* _H8300_TYPES_H */ diff --git a/arch/h8300/include/asm/uaccess.h b/arch/h8300/include/asm/uaccess.h deleted file mode 100644 index 8725d1a..0000000 --- a/arch/h8300/include/asm/uaccess.h +++ /dev/null @@ -1,163 +0,0 @@ -#ifndef __H8300_UACCESS_H -#define __H8300_UACCESS_H - -/* - * User space memory access functions - */ -#include -#include -#include - -#include - -#define VERIFY_READ 0 -#define VERIFY_WRITE 1 - -/* We let the MMU do all checking */ -#define access_ok(type, addr, size) __access_ok((unsigned long)addr,size) -static inline int __access_ok(unsigned long addr, unsigned long size) -{ -#define RANGE_CHECK_OK(addr, size, lower, upper) \ - (((addr) >= (lower)) && (((addr) + (size)) < (upper))) - - extern unsigned long _ramend; - return(RANGE_CHECK_OK(addr, size, 0L, (unsigned long)&_ramend)); -} - -/* - * The exception table consists of pairs of addresses: the first is the - * address of an instruction that is allowed to fault, and the second is - * the address at which the program should continue. No registers are - * modified, so it is entirely up to the continuation code to figure out - * what to do. - * - * All the routines below use bits of fixup code that are out of line - * with the main instruction path. This means when everything is well, - * we don't even have to jump over them. Further, they do not intrude - * on our cache or tlb entries. - */ - -struct exception_table_entry -{ - unsigned long insn, fixup; -}; - -/* Returns 0 if exception not found and fixup otherwise. */ -extern unsigned long search_exception_table(unsigned long); - - -/* - * These are the main single-value transfer routines. They automatically - * use the right size if we just have the right pointer type. - */ - -#define put_user(x, ptr) \ -({ \ - int __pu_err = 0; \ - typeof(*(ptr)) __pu_val = (x); \ - switch (sizeof (*(ptr))) { \ - case 1: \ - case 2: \ - case 4: \ - *(ptr) = (__pu_val); \ - break; \ - case 8: \ - memcpy(ptr, &__pu_val, sizeof (*(ptr))); \ - break; \ - default: \ - __pu_err = __put_user_bad(); \ - break; \ - } \ - __pu_err; \ -}) -#define __put_user(x, ptr) put_user(x, ptr) - -extern int __put_user_bad(void); - -/* - * Tell gcc we read from memory instead of writing: this is because - * we do not write to any memory gcc knows about, so there are no - * aliasing issues. - */ - -#define __ptr(x) ((unsigned long *)(x)) - -/* - * Tell gcc we read from memory instead of writing: this is because - * we do not write to any memory gcc knows about, so there are no - * aliasing issues. - */ - -#define get_user(x, ptr) \ -({ \ - int __gu_err = 0; \ - typeof(*(ptr)) __gu_val = *ptr; \ - switch (sizeof(*(ptr))) { \ - case 1: \ - case 2: \ - case 4: \ - case 8: \ - break; \ - default: \ - __gu_err = __get_user_bad(); \ - break; \ - } \ - (x) = __gu_val; \ - __gu_err; \ -}) -#define __get_user(x, ptr) get_user(x, ptr) - -extern int __get_user_bad(void); - -#define copy_from_user(to, from, n) (memcpy(to, from, n), 0) -#define copy_to_user(to, from, n) (memcpy(to, from, n), 0) - -#define __copy_from_user(to, from, n) copy_from_user(to, from, n) -#define __copy_to_user(to, from, n) copy_to_user(to, from, n) -#define __copy_to_user_inatomic __copy_to_user -#define __copy_from_user_inatomic __copy_from_user - -#define copy_to_user_ret(to,from,n,retval) ({ if (copy_to_user(to,from,n)) return retval; }) - -#define copy_from_user_ret(to,from,n,retval) ({ if (copy_from_user(to,from,n)) return retval; }) - -/* - * Copy a null terminated string from userspace. - */ - -static inline long -strncpy_from_user(char *dst, const char *src, long count) -{ - char *tmp; - strncpy(dst, src, count); - for (tmp = dst; *tmp && count > 0; tmp++, count--) - ; - return(tmp - dst); /* DAVIDM should we count a NUL ? check getname */ -} - -/* - * Return the size of a string (including the ending 0) - * - * Return 0 on exception, a value greater than N if too long - */ -static inline long strnlen_user(const char *src, long n) -{ - return(strlen(src) + 1); /* DAVIDM make safer */ -} - -#define strlen_user(str) strnlen_user(str, 32767) - -/* - * Zero Userspace - */ - -static inline unsigned long -clear_user(void *to, unsigned long n) -{ - memset(to, 0, n); - return 0; -} - -#define __clear_user clear_user - -#endif /* _H8300_UACCESS_H */ diff --git a/arch/h8300/include/asm/ucontext.h b/arch/h8300/include/asm/ucontext.h deleted file mode 100644 index 0bcf8f8..0000000 --- a/arch/h8300/include/asm/ucontext.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _H8300_UCONTEXT_H -#define _H8300_UCONTEXT_H - -struct ucontext { - unsigned long uc_flags; - struct ucontext *uc_link; - stack_t uc_stack; - struct sigcontext uc_mcontext; - sigset_t uc_sigmask; /* mask last for extensibility */ -}; - -#endif diff --git a/arch/h8300/include/asm/unaligned.h b/arch/h8300/include/asm/unaligned.h deleted file mode 100644 index b8d06c7..0000000 --- a/arch/h8300/include/asm/unaligned.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef _ASM_H8300_UNALIGNED_H -#define _ASM_H8300_UNALIGNED_H - -#include -#include -#include - -#define get_unaligned __get_unaligned_be -#define put_unaligned __put_unaligned_be - -#endif /* _ASM_H8300_UNALIGNED_H */ diff --git a/arch/h8300/include/asm/unistd.h b/arch/h8300/include/asm/unistd.h deleted file mode 100644 index ab671ec..0000000 --- a/arch/h8300/include/asm/unistd.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef _ASM_H8300_UNISTD_H_ -#define _ASM_H8300_UNISTD_H_ - -#include - - -#define NR_syscalls 321 - -#define __ARCH_WANT_OLD_READDIR -#define __ARCH_WANT_OLD_STAT -#define __ARCH_WANT_STAT64 -#define __ARCH_WANT_SYS_ALARM -#define __ARCH_WANT_SYS_GETHOSTNAME -#define __ARCH_WANT_SYS_IPC -#define __ARCH_WANT_SYS_PAUSE -#define __ARCH_WANT_SYS_SGETMASK -#define __ARCH_WANT_SYS_SIGNAL -#define __ARCH_WANT_SYS_TIME -#define __ARCH_WANT_SYS_UTIME -#define __ARCH_WANT_SYS_WAITPID -#define __ARCH_WANT_SYS_SOCKETCALL -#define __ARCH_WANT_SYS_FADVISE64 -#define __ARCH_WANT_SYS_GETPGRP -#define __ARCH_WANT_SYS_LLSEEK -#define __ARCH_WANT_SYS_NICE -#define __ARCH_WANT_SYS_OLD_GETRLIMIT -#define __ARCH_WANT_SYS_OLD_MMAP -#define __ARCH_WANT_SYS_OLD_SELECT -#define __ARCH_WANT_SYS_OLDUMOUNT -#define __ARCH_WANT_SYS_SIGPENDING -#define __ARCH_WANT_SYS_SIGPROCMASK -#define __ARCH_WANT_SYS_FORK -#define __ARCH_WANT_SYS_VFORK -#define __ARCH_WANT_SYS_CLONE - -#endif /* _ASM_H8300_UNISTD_H_ */ diff --git a/arch/h8300/include/asm/user.h b/arch/h8300/include/asm/user.h deleted file mode 100644 index 14a9e18..0000000 --- a/arch/h8300/include/asm/user.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef _H8300_USER_H -#define _H8300_USER_H - -#include - -/* Core file format: The core file is written in such a way that gdb - can understand it and provide useful information to the user (under - linux we use the 'trad-core' bfd). There are quite a number of - obstacles to being able to view the contents of the floating point - registers, and until these are solved you will not be able to view the - contents of them. Actually, you can read in the core file and look at - the contents of the user struct to find out what the floating point - registers contain. - The actual file contents are as follows: - UPAGE: 1 page consisting of a user struct that tells gdb what is present - in the file. Directly after this is a copy of the task_struct, which - is currently not used by gdb, but it may come in useful at some point. - All of the registers are stored as part of the upage. The upage should - always be only one page. - DATA: The data area is stored. We use current->end_text to - current->brk to pick up all of the user variables, plus any memory - that may have been malloced. No attempt is made to determine if a page - is demand-zero or if a page is totally unused, we just cover the entire - range. All of the addresses are rounded in such a way that an integral - number of pages is written. - STACK: We need the stack information in order to get a meaningful - backtrace. We need to write the data from (esp) to - current->start_stack, so we round each of these off in order to be able - to write an integer number of pages. - The minimum core file size is 3 pages, or 12288 bytes. -*/ - -/* This is the old layout of "struct pt_regs" as of Linux 1.x, and - is still the layout used by user (the new pt_regs doesn't have - all registers). */ -struct user_regs_struct { - long er1,er2,er3,er4,er5,er6; - long er0; - long usp; - long orig_er0; - short ccr; - long pc; -}; - - -/* When the kernel dumps core, it starts by dumping the user struct - - this will be used by gdb to figure out where the data and stack segments - are within the file, and what virtual addresses to use. */ -struct user{ -/* We start with the registers, to mimic the way that "memory" is returned - from the ptrace(3,...) function. */ - struct user_regs_struct regs; /* Where the registers are actually stored */ -/* ptrace does not yet supply these. Someday.... */ -/* The rest of this junk is to help gdb figure out what goes where */ - unsigned long int u_tsize; /* Text segment size (pages). */ - unsigned long int u_dsize; /* Data segment size (pages). */ - unsigned long int u_ssize; /* Stack segment size (pages). */ - unsigned long start_code; /* Starting virtual address of text. */ - unsigned long start_stack; /* Starting virtual address of stack area. - This is actually the bottom of the stack, - the top of the stack is always found in the - esp register. */ - long int signal; /* Signal that caused the core dump. */ - int reserved; /* No longer used */ - unsigned long u_ar0; /* Used by gdb to help find the values for */ - /* the registers. */ - unsigned long magic; /* To uniquely identify a core file */ - char u_comm[32]; /* User command that was responsible */ -}; -#define NBPG PAGE_SIZE -#define UPAGES 1 -#define HOST_TEXT_START_ADDR (u.start_code) -#define HOST_STACK_END_ADDR (u.start_stack + u.u_ssize * NBPG) - -#endif diff --git a/arch/h8300/include/asm/virtconvert.h b/arch/h8300/include/asm/virtconvert.h deleted file mode 100644 index 19cfd62..0000000 --- a/arch/h8300/include/asm/virtconvert.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef __H8300_VIRT_CONVERT__ -#define __H8300_VIRT_CONVERT__ - -/* - * Macros used for converting between virtual and physical mappings. - */ - -#ifdef __KERNEL__ - -#include -#include - -#define phys_to_virt(vaddr) ((void *) (vaddr)) -#define virt_to_phys(vaddr) ((unsigned long) (vaddr)) - -#define virt_to_bus virt_to_phys -#define bus_to_virt phys_to_virt - -#endif -#endif diff --git a/arch/h8300/include/uapi/asm/Kbuild b/arch/h8300/include/uapi/asm/Kbuild deleted file mode 100644 index 040178c..0000000 --- a/arch/h8300/include/uapi/asm/Kbuild +++ /dev/null @@ -1,34 +0,0 @@ -# UAPI Header export list -include include/uapi/asm-generic/Kbuild.asm - -header-y += auxvec.h -header-y += bitsperlong.h -header-y += byteorder.h -header-y += errno.h -header-y += fcntl.h -header-y += ioctl.h -header-y += ioctls.h -header-y += ipcbuf.h -header-y += kvm_para.h -header-y += mman.h -header-y += msgbuf.h -header-y += param.h -header-y += poll.h -header-y += posix_types.h -header-y += ptrace.h -header-y += resource.h -header-y += sembuf.h -header-y += setup.h -header-y += shmbuf.h -header-y += sigcontext.h -header-y += siginfo.h -header-y += signal.h -header-y += socket.h -header-y += sockios.h -header-y += stat.h -header-y += statfs.h -header-y += swab.h -header-y += termbits.h -header-y += termios.h -header-y += types.h -header-y += unistd.h diff --git a/arch/h8300/include/uapi/asm/auxvec.h b/arch/h8300/include/uapi/asm/auxvec.h deleted file mode 100644 index 1d36fe38..0000000 --- a/arch/h8300/include/uapi/asm/auxvec.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef __ASMH8300_AUXVEC_H -#define __ASMH8300_AUXVEC_H - -#endif diff --git a/arch/h8300/include/uapi/asm/bitsperlong.h b/arch/h8300/include/uapi/asm/bitsperlong.h deleted file mode 100644 index 6dc0bb0..0000000 --- a/arch/h8300/include/uapi/asm/bitsperlong.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/arch/h8300/include/uapi/asm/byteorder.h b/arch/h8300/include/uapi/asm/byteorder.h deleted file mode 100644 index 13539da..0000000 --- a/arch/h8300/include/uapi/asm/byteorder.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _H8300_BYTEORDER_H -#define _H8300_BYTEORDER_H - -#include - -#endif /* _H8300_BYTEORDER_H */ diff --git a/arch/h8300/include/uapi/asm/errno.h b/arch/h8300/include/uapi/asm/errno.h deleted file mode 100644 index 0c2f564..0000000 --- a/arch/h8300/include/uapi/asm/errno.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _H8300_ERRNO_H -#define _H8300_ERRNO_H - -#include - -#endif /* _H8300_ERRNO_H */ diff --git a/arch/h8300/include/uapi/asm/fcntl.h b/arch/h8300/include/uapi/asm/fcntl.h deleted file mode 100644 index 1952cb2e..0000000 --- a/arch/h8300/include/uapi/asm/fcntl.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef _H8300_FCNTL_H -#define _H8300_FCNTL_H - -#define O_DIRECTORY 040000 /* must be a directory */ -#define O_NOFOLLOW 0100000 /* don't follow links */ -#define O_DIRECT 0200000 /* direct disk access hint - currently ignored */ -#define O_LARGEFILE 0400000 - -#include - -#endif /* _H8300_FCNTL_H */ diff --git a/arch/h8300/include/uapi/asm/ioctl.h b/arch/h8300/include/uapi/asm/ioctl.h deleted file mode 100644 index b279fe0..0000000 --- a/arch/h8300/include/uapi/asm/ioctl.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/arch/h8300/include/uapi/asm/ioctls.h b/arch/h8300/include/uapi/asm/ioctls.h deleted file mode 100644 index 30eaed2..0000000 --- a/arch/h8300/include/uapi/asm/ioctls.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef __ARCH_H8300_IOCTLS_H__ -#define __ARCH_H8300_IOCTLS_H__ - -#define FIOQSIZE 0x545E - -#include - -#endif /* __ARCH_H8300_IOCTLS_H__ */ diff --git a/arch/h8300/include/uapi/asm/ipcbuf.h b/arch/h8300/include/uapi/asm/ipcbuf.h deleted file mode 100644 index 84c7e51..0000000 --- a/arch/h8300/include/uapi/asm/ipcbuf.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/arch/h8300/include/uapi/asm/kvm_para.h b/arch/h8300/include/uapi/asm/kvm_para.h deleted file mode 100644 index 14fab8f..0000000 --- a/arch/h8300/include/uapi/asm/kvm_para.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/arch/h8300/include/uapi/asm/mman.h b/arch/h8300/include/uapi/asm/mman.h deleted file mode 100644 index 8eebf89..0000000 --- a/arch/h8300/include/uapi/asm/mman.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/arch/h8300/include/uapi/asm/msgbuf.h b/arch/h8300/include/uapi/asm/msgbuf.h deleted file mode 100644 index 6b148cd..0000000 --- a/arch/h8300/include/uapi/asm/msgbuf.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef _H8300_MSGBUF_H -#define _H8300_MSGBUF_H - -/* - * The msqid64_ds structure for H8/300 architecture. - * Note extra padding because this structure is passed back and forth - * between kernel and user space. - * - * Pad space is left for: - * - 64-bit time_t to solve y2038 problem - * - 2 miscellaneous 32-bit values - */ - -struct msqid64_ds { - struct ipc64_perm msg_perm; - __kernel_time_t msg_stime; /* last msgsnd time */ - unsigned long __unused1; - __kernel_time_t msg_rtime; /* last msgrcv time */ - unsigned long __unused2; - __kernel_time_t msg_ctime; /* last change time */ - unsigned long __unused3; - unsigned long msg_cbytes; /* current number of bytes on queue */ - unsigned long msg_qnum; /* number of messages in queue */ - unsigned long msg_qbytes; /* max number of bytes on queue */ - __kernel_pid_t msg_lspid; /* pid of last msgsnd */ - __kernel_pid_t msg_lrpid; /* last receive pid */ - unsigned long __unused4; - unsigned long __unused5; -}; - -#endif /* _H8300_MSGBUF_H */ diff --git a/arch/h8300/include/uapi/asm/param.h b/arch/h8300/include/uapi/asm/param.h deleted file mode 100644 index 3dd18ae..0000000 --- a/arch/h8300/include/uapi/asm/param.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef _UAPI_H8300_PARAM_H -#define _UAPI_H8300_PARAM_H - -#ifndef __KERNEL__ -#define HZ 100 -#endif - -#define EXEC_PAGESIZE 4096 - -#ifndef NOGROUP -#define NOGROUP (-1) -#endif - -#define MAXHOSTNAMELEN 64 /* max length of hostname */ - -#endif /* _UAPI_H8300_PARAM_H */ diff --git a/arch/h8300/include/uapi/asm/poll.h b/arch/h8300/include/uapi/asm/poll.h deleted file mode 100644 index f61540c..0000000 --- a/arch/h8300/include/uapi/asm/poll.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef __H8300_POLL_H -#define __H8300_POLL_H - -#define POLLWRNORM POLLOUT -#define POLLWRBAND 256 - -#include - -#undef POLLREMOVE - -#endif diff --git a/arch/h8300/include/uapi/asm/posix_types.h b/arch/h8300/include/uapi/asm/posix_types.h deleted file mode 100644 index 91e62ba..0000000 --- a/arch/h8300/include/uapi/asm/posix_types.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef __ARCH_H8300_POSIX_TYPES_H -#define __ARCH_H8300_POSIX_TYPES_H - -/* - * This file is generally used by user-level software, so you need to - * be a little careful about namespace pollution etc. Also, we cannot - * assume GCC is being used. - */ - -typedef unsigned short __kernel_mode_t; -#define __kernel_mode_t __kernel_mode_t - -typedef unsigned short __kernel_ipc_pid_t; -#define __kernel_ipc_pid_t __kernel_ipc_pid_t - -typedef unsigned short __kernel_uid_t; -typedef unsigned short __kernel_gid_t; -#define __kernel_uid_t __kernel_uid_t - -typedef unsigned short __kernel_old_uid_t; -typedef unsigned short __kernel_old_gid_t; -#define __kernel_old_uid_t __kernel_old_uid_t - -#include - -#endif diff --git a/arch/h8300/include/uapi/asm/ptrace.h b/arch/h8300/include/uapi/asm/ptrace.h deleted file mode 100644 index ef39ec5..0000000 --- a/arch/h8300/include/uapi/asm/ptrace.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef _UAPI_H8300_PTRACE_H -#define _UAPI_H8300_PTRACE_H - -#ifndef __ASSEMBLY__ - -#define PT_ER1 0 -#define PT_ER2 1 -#define PT_ER3 2 -#define PT_ER4 3 -#define PT_ER5 4 -#define PT_ER6 5 -#define PT_ER0 6 -#define PT_ORIG_ER0 7 -#define PT_CCR 8 -#define PT_PC 9 -#define PT_USP 10 -#define PT_EXR 12 - -/* this struct defines the way the registers are stored on the - stack during a system call. */ - -struct pt_regs { - long retpc; - long er4; - long er5; - long er6; - long er3; - long er2; - long er1; - long orig_er0; - unsigned short ccr; - long er0; - long vector; -#if defined(CONFIG_CPU_H8S) - unsigned short exr; -#endif - unsigned long pc; -} __attribute__((aligned(2),packed)); - -#define PTRACE_GETREGS 12 -#define PTRACE_SETREGS 13 - -#endif /* __ASSEMBLY__ */ -#endif /* _UAPI_H8300_PTRACE_H */ diff --git a/arch/h8300/include/uapi/asm/resource.h b/arch/h8300/include/uapi/asm/resource.h deleted file mode 100644 index 46c5f43..0000000 --- a/arch/h8300/include/uapi/asm/resource.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _H8300_RESOURCE_H -#define _H8300_RESOURCE_H - -#include - -#endif /* _H8300_RESOURCE_H */ diff --git a/arch/h8300/include/uapi/asm/sembuf.h b/arch/h8300/include/uapi/asm/sembuf.h deleted file mode 100644 index e04a3ec..0000000 --- a/arch/h8300/include/uapi/asm/sembuf.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef _H8300_SEMBUF_H -#define _H8300_SEMBUF_H - -/* - * The semid64_ds structure for m68k architecture. - * Note extra padding because this structure is passed back and forth - * between kernel and user space. - * - * Pad space is left for: - * - 64-bit time_t to solve y2038 problem - * - 2 miscellaneous 32-bit values - */ - -struct semid64_ds { - struct ipc64_perm sem_perm; /* permissions .. see ipc.h */ - __kernel_time_t sem_otime; /* last semop time */ - unsigned long __unused1; - __kernel_time_t sem_ctime; /* last change time */ - unsigned long __unused2; - unsigned long sem_nsems; /* no. of semaphores in array */ - unsigned long __unused3; - unsigned long __unused4; -}; - -#endif /* _H8300_SEMBUF_H */ diff --git a/arch/h8300/include/uapi/asm/setup.h b/arch/h8300/include/uapi/asm/setup.h deleted file mode 100644 index e2c600e..0000000 --- a/arch/h8300/include/uapi/asm/setup.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __H8300_SETUP_H -#define __H8300_SETUP_H - -#define COMMAND_LINE_SIZE 512 - -#endif diff --git a/arch/h8300/include/uapi/asm/shmbuf.h b/arch/h8300/include/uapi/asm/shmbuf.h deleted file mode 100644 index 64e7799..0000000 --- a/arch/h8300/include/uapi/asm/shmbuf.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef _H8300_SHMBUF_H -#define _H8300_SHMBUF_H - -/* - * The shmid64_ds structure for m68k architecture. - * Note extra padding because this structure is passed back and forth - * between kernel and user space. - * - * Pad space is left for: - * - 64-bit time_t to solve y2038 problem - * - 2 miscellaneous 32-bit values - */ - -struct shmid64_ds { - struct ipc64_perm shm_perm; /* operation perms */ - size_t shm_segsz; /* size of segment (bytes) */ - __kernel_time_t shm_atime; /* last attach time */ - unsigned long __unused1; - __kernel_time_t shm_dtime; /* last detach time */ - unsigned long __unused2; - __kernel_time_t shm_ctime; /* last change time */ - unsigned long __unused3; - __kernel_pid_t shm_cpid; /* pid of creator */ - __kernel_pid_t shm_lpid; /* pid of last operator */ - unsigned long shm_nattch; /* no. of current attaches */ - unsigned long __unused4; - unsigned long __unused5; -}; - -struct shminfo64 { - unsigned long shmmax; - unsigned long shmmin; - unsigned long shmmni; - unsigned long shmseg; - unsigned long shmall; - unsigned long __unused1; - unsigned long __unused2; - unsigned long __unused3; - unsigned long __unused4; -}; - -#endif /* _H8300_SHMBUF_H */ diff --git a/arch/h8300/include/uapi/asm/sigcontext.h b/arch/h8300/include/uapi/asm/sigcontext.h deleted file mode 100644 index e4b8150..0000000 --- a/arch/h8300/include/uapi/asm/sigcontext.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef _ASM_H8300_SIGCONTEXT_H -#define _ASM_H8300_SIGCONTEXT_H - -struct sigcontext { - unsigned long sc_mask; /* old sigmask */ - unsigned long sc_usp; /* old user stack pointer */ - unsigned long sc_er0; - unsigned long sc_er1; - unsigned long sc_er2; - unsigned long sc_er3; - unsigned long sc_er4; - unsigned long sc_er5; - unsigned long sc_er6; - unsigned short sc_ccr; - unsigned long sc_pc; -}; - -#endif diff --git a/arch/h8300/include/uapi/asm/siginfo.h b/arch/h8300/include/uapi/asm/siginfo.h deleted file mode 100644 index bc8fbea..0000000 --- a/arch/h8300/include/uapi/asm/siginfo.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _H8300_SIGINFO_H -#define _H8300_SIGINFO_H - -#include - -#endif diff --git a/arch/h8300/include/uapi/asm/signal.h b/arch/h8300/include/uapi/asm/signal.h deleted file mode 100644 index af3a6c3..0000000 --- a/arch/h8300/include/uapi/asm/signal.h +++ /dev/null @@ -1,115 +0,0 @@ -#ifndef _UAPI_H8300_SIGNAL_H -#define _UAPI_H8300_SIGNAL_H - -#include - -/* Avoid too many header ordering problems. */ -struct siginfo; - -#ifndef __KERNEL__ -/* Here we must cater to libcs that poke about in kernel headers. */ - -#define NSIG 32 -typedef unsigned long sigset_t; - -#endif /* __KERNEL__ */ - -#define SIGHUP 1 -#define SIGINT 2 -#define SIGQUIT 3 -#define SIGILL 4 -#define SIGTRAP 5 -#define SIGABRT 6 -#define SIGIOT 6 -#define SIGBUS 7 -#define SIGFPE 8 -#define SIGKILL 9 -#define SIGUSR1 10 -#define SIGSEGV 11 -#define SIGUSR2 12 -#define SIGPIPE 13 -#define SIGALRM 14 -#define SIGTERM 15 -#define SIGSTKFLT 16 -#define SIGCHLD 17 -#define SIGCONT 18 -#define SIGSTOP 19 -#define SIGTSTP 20 -#define SIGTTIN 21 -#define SIGTTOU 22 -#define SIGURG 23 -#define SIGXCPU 24 -#define SIGXFSZ 25 -#define SIGVTALRM 26 -#define SIGPROF 27 -#define SIGWINCH 28 -#define SIGIO 29 -#define SIGPOLL SIGIO -/* -#define SIGLOST 29 -*/ -#define SIGPWR 30 -#define SIGSYS 31 -#define SIGUNUSED 31 - -/* These should not be considered constants from userland. */ -#define SIGRTMIN 32 -#define SIGRTMAX _NSIG - -/* - * SA_FLAGS values: - * - * SA_ONSTACK indicates that a registered stack_t will be used. - * SA_RESTART flag to get restarting signals (which were the default long ago) - * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. - * SA_RESETHAND clears the handler when the signal is delivered. - * SA_NOCLDWAIT flag on SIGCHLD to inhibit zombies. - * SA_NODEFER prevents the current signal from being masked in the handler. - * - * SA_ONESHOT and SA_NOMASK are the historical Linux names for the Single - * Unix names RESETHAND and NODEFER respectively. - */ -#define SA_NOCLDSTOP 0x00000001 -#define SA_NOCLDWAIT 0x00000002 /* not supported yet */ -#define SA_SIGINFO 0x00000004 -#define SA_ONSTACK 0x08000000 -#define SA_RESTART 0x10000000 -#define SA_NODEFER 0x40000000 -#define SA_RESETHAND 0x80000000 - -#define SA_NOMASK SA_NODEFER -#define SA_ONESHOT SA_RESETHAND - -#define SA_RESTORER 0x04000000 - -#define MINSIGSTKSZ 2048 -#define SIGSTKSZ 8192 - -#include - -#ifndef __KERNEL__ -/* Here we must cater to libcs that poke about in kernel headers. */ - -struct sigaction { - union { - __sighandler_t _sa_handler; - void (*_sa_sigaction)(int, struct siginfo *, void *); - } _u; - sigset_t sa_mask; - unsigned long sa_flags; - void (*sa_restorer)(void); -}; - -#define sa_handler _u._sa_handler -#define sa_sigaction _u._sa_sigaction - -#endif /* __KERNEL__ */ - -typedef struct sigaltstack { - void *ss_sp; - int ss_flags; - size_t ss_size; -} stack_t; - - -#endif /* _UAPI_H8300_SIGNAL_H */ diff --git a/arch/h8300/include/uapi/asm/socket.h b/arch/h8300/include/uapi/asm/socket.h deleted file mode 100644 index 9490758..0000000 --- a/arch/h8300/include/uapi/asm/socket.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef _ASM_SOCKET_H -#define _ASM_SOCKET_H - -#include - -/* For setsockoptions(2) */ -#define SOL_SOCKET 1 - -#define SO_DEBUG 1 -#define SO_REUSEADDR 2 -#define SO_TYPE 3 -#define SO_ERROR 4 -#define SO_DONTROUTE 5 -#define SO_BROADCAST 6 -#define SO_SNDBUF 7 -#define SO_RCVBUF 8 -#define SO_SNDBUFFORCE 32 -#define SO_RCVBUFFORCE 33 -#define SO_KEEPALIVE 9 -#define SO_OOBINLINE 10 -#define SO_NO_CHECK 11 -#define SO_PRIORITY 12 -#define SO_LINGER 13 -#define SO_BSDCOMPAT 14 -#define SO_REUSEPORT 15 -#define SO_PASSCRED 16 -#define SO_PEERCRED 17 -#define SO_RCVLOWAT 18 -#define SO_SNDLOWAT 19 -#define SO_RCVTIMEO 20 -#define SO_SNDTIMEO 21 - -/* Security levels - as per NRL IPv6 - don't actually do anything */ -#define SO_SECURITY_AUTHENTICATION 22 -#define SO_SECURITY_ENCRYPTION_TRANSPORT 23 -#define SO_SECURITY_ENCRYPTION_NETWORK 24 - -#define SO_BINDTODEVICE 25 - -/* Socket filtering */ -#define SO_ATTACH_FILTER 26 -#define SO_DETACH_FILTER 27 -#define SO_GET_FILTER SO_ATTACH_FILTER - -#define SO_PEERNAME 28 -#define SO_TIMESTAMP 29 -#define SCM_TIMESTAMP SO_TIMESTAMP - -#define SO_ACCEPTCONN 30 - -#define SO_PEERSEC 31 -#define SO_PASSSEC 34 -#define SO_TIMESTAMPNS 35 -#define SCM_TIMESTAMPNS SO_TIMESTAMPNS - -#define SO_MARK 36 - -#define SO_TIMESTAMPING 37 -#define SCM_TIMESTAMPING SO_TIMESTAMPING - -#define SO_PROTOCOL 38 -#define SO_DOMAIN 39 - -#define SO_RXQ_OVFL 40 - -#define SO_WIFI_STATUS 41 -#define SCM_WIFI_STATUS SO_WIFI_STATUS -#define SO_PEEK_OFF 42 - -/* Instruct lower device to use last 4-bytes of skb data as FCS */ -#define SO_NOFCS 43 - -#define SO_LOCK_FILTER 44 - -#define SO_SELECT_ERR_QUEUE 45 - -#define SO_BUSY_POLL 46 - -#endif /* _ASM_SOCKET_H */ diff --git a/arch/h8300/include/uapi/asm/sockios.h b/arch/h8300/include/uapi/asm/sockios.h deleted file mode 100644 index e9c7ec8..0000000 --- a/arch/h8300/include/uapi/asm/sockios.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef __ARCH_H8300_SOCKIOS__ -#define __ARCH_H8300_SOCKIOS__ - -/* Socket-level I/O control calls. */ -#define FIOSETOWN 0x8901 -#define SIOCSPGRP 0x8902 -#define FIOGETOWN 0x8903 -#define SIOCGPGRP 0x8904 -#define SIOCATMARK 0x8905 -#define SIOCGSTAMP 0x8906 /* Get stamp (timeval) */ -#define SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */ - -#endif /* __ARCH_H8300_SOCKIOS__ */ diff --git a/arch/h8300/include/uapi/asm/stat.h b/arch/h8300/include/uapi/asm/stat.h deleted file mode 100644 index 62c3cc2..0000000 --- a/arch/h8300/include/uapi/asm/stat.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef _H8300_STAT_H -#define _H8300_STAT_H - -struct __old_kernel_stat { - unsigned short st_dev; - unsigned short st_ino; - unsigned short st_mode; - unsigned short st_nlink; - unsigned short st_uid; - unsigned short st_gid; - unsigned short st_rdev; - unsigned long st_size; - unsigned long st_atime; - unsigned long st_mtime; - unsigned long st_ctime; -}; - -struct stat { - unsigned short st_dev; - unsigned short __pad1; - unsigned long st_ino; - unsigned short st_mode; - unsigned short st_nlink; - unsigned short st_uid; - unsigned short st_gid; - unsigned short st_rdev; - unsigned short __pad2; - unsigned long st_size; - unsigned long st_blksize; - unsigned long st_blocks; - unsigned long st_atime; - unsigned long __unused1; - unsigned long st_mtime; - unsigned long __unused2; - unsigned long st_ctime; - unsigned long __unused3; - unsigned long __unused4; - unsigned long __unused5; -}; - -/* This matches struct stat64 in glibc2.1, hence the absolutely - * insane amounts of padding around dev_t's. - */ -struct stat64 { - unsigned long long st_dev; - unsigned char __pad1[2]; - -#define STAT64_HAS_BROKEN_ST_INO 1 - unsigned long __st_ino; - - unsigned int st_mode; - unsigned int st_nlink; - - unsigned long st_uid; - unsigned long st_gid; - - unsigned long long st_rdev; - unsigned char __pad3[2]; - - long long st_size; - unsigned long st_blksize; - - unsigned long __pad4; /* future possible st_blocks high bits */ - unsigned long st_blocks; /* Number 512-byte blocks allocated. */ - - unsigned long st_atime; - unsigned long st_atime_nsec; - - unsigned long st_mtime; - unsigned long st_mtime_nsec; - - unsigned long st_ctime; - unsigned long st_ctime_nsec; - - unsigned long long st_ino; -}; - -#endif /* _H8300_STAT_H */ diff --git a/arch/h8300/include/uapi/asm/statfs.h b/arch/h8300/include/uapi/asm/statfs.h deleted file mode 100644 index b96efa7..0000000 --- a/arch/h8300/include/uapi/asm/statfs.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _H8300_STATFS_H -#define _H8300_STATFS_H - -#include - -#endif /* _H8300_STATFS_H */ diff --git a/arch/h8300/include/uapi/asm/swab.h b/arch/h8300/include/uapi/asm/swab.h deleted file mode 100644 index 39abbf5..0000000 --- a/arch/h8300/include/uapi/asm/swab.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _H8300_SWAB_H -#define _H8300_SWAB_H - -#include - -#if defined(__GNUC__) && !defined(__STRICT_ANSI__) || defined(__KERNEL__) -# define __SWAB_64_THRU_32__ -#endif - -#endif /* _H8300_SWAB_H */ diff --git a/arch/h8300/include/uapi/asm/termbits.h b/arch/h8300/include/uapi/asm/termbits.h deleted file mode 100644 index 3287a62..0000000 --- a/arch/h8300/include/uapi/asm/termbits.h +++ /dev/null @@ -1,201 +0,0 @@ -#ifndef __ARCH_H8300_TERMBITS_H__ -#define __ARCH_H8300_TERMBITS_H__ - -#include - -typedef unsigned char cc_t; -typedef unsigned int speed_t; -typedef unsigned int tcflag_t; - -#define NCCS 19 -struct termios { - tcflag_t c_iflag; /* input mode flags */ - tcflag_t c_oflag; /* output mode flags */ - tcflag_t c_cflag; /* control mode flags */ - tcflag_t c_lflag; /* local mode flags */ - cc_t c_line; /* line discipline */ - cc_t c_cc[NCCS]; /* control characters */ -}; - -struct termios2 { - tcflag_t c_iflag; /* input mode flags */ - tcflag_t c_oflag; /* output mode flags */ - tcflag_t c_cflag; /* control mode flags */ - tcflag_t c_lflag; /* local mode flags */ - cc_t c_line; /* line discipline */ - cc_t c_cc[NCCS]; /* control characters */ - speed_t c_ispeed; /* input speed */ - speed_t c_ospeed; /* output speed */ -}; - -struct ktermios { - tcflag_t c_iflag; /* input mode flags */ - tcflag_t c_oflag; /* output mode flags */ - tcflag_t c_cflag; /* control mode flags */ - tcflag_t c_lflag; /* local mode flags */ - cc_t c_line; /* line discipline */ - cc_t c_cc[NCCS]; /* control characters */ - speed_t c_ispeed; /* input speed */ - speed_t c_ospeed; /* output speed */ -}; - -/* c_cc characters */ -#define VINTR 0 -#define VQUIT 1 -#define VERASE 2 -#define VKILL 3 -#define VEOF 4 -#define VTIME 5 -#define VMIN 6 -#define VSWTC 7 -#define VSTART 8 -#define VSTOP 9 -#define VSUSP 10 -#define VEOL 11 -#define VREPRINT 12 -#define VDISCARD 13 -#define VWERASE 14 -#define VLNEXT 15 -#define VEOL2 16 - - -/* c_iflag bits */ -#define IGNBRK 0000001 -#define BRKINT 0000002 -#define IGNPAR 0000004 -#define PARMRK 0000010 -#define INPCK 0000020 -#define ISTRIP 0000040 -#define INLCR 0000100 -#define IGNCR 0000200 -#define ICRNL 0000400 -#define IUCLC 0001000 -#define IXON 0002000 -#define IXANY 0004000 -#define IXOFF 0010000 -#define IMAXBEL 0020000 -#define IUTF8 0040000 - -/* c_oflag bits */ -#define OPOST 0000001 -#define OLCUC 0000002 -#define ONLCR 0000004 -#define OCRNL 0000010 -#define ONOCR 0000020 -#define ONLRET 0000040 -#define OFILL 0000100 -#define OFDEL 0000200 -#define NLDLY 0000400 -#define NL0 0000000 -#define NL1 0000400 -#define CRDLY 0003000 -#define CR0 0000000 -#define CR1 0001000 -#define CR2 0002000 -#define CR3 0003000 -#define TABDLY 0014000 -#define TAB0 0000000 -#define TAB1 0004000 -#define TAB2 0010000 -#define TAB3 0014000 -#define XTABS 0014000 -#define BSDLY 0020000 -#define BS0 0000000 -#define BS1 0020000 -#define VTDLY 0040000 -#define VT0 0000000 -#define VT1 0040000 -#define FFDLY 0100000 -#define FF0 0000000 -#define FF1 0100000 - -/* c_cflag bit meaning */ -#define CBAUD 0010017 -#define B0 0000000 /* hang up */ -#define B50 0000001 -#define B75 0000002 -#define B110 0000003 -#define B134 0000004 -#define B150 0000005 -#define B200 0000006 -#define B300 0000007 -#define B600 0000010 -#define B1200 0000011 -#define B1800 0000012 -#define B2400 0000013 -#define B4800 0000014 -#define B9600 0000015 -#define B19200 0000016 -#define B38400 0000017 -#define EXTA B19200 -#define EXTB B38400 -#define CSIZE 0000060 -#define CS5 0000000 -#define CS6 0000020 -#define CS7 0000040 -#define CS8 0000060 -#define CSTOPB 0000100 -#define CREAD 0000200 -#define PARENB 0000400 -#define PARODD 0001000 -#define HUPCL 0002000 -#define CLOCAL 0004000 -#define CBAUDEX 0010000 -#define BOTHER 0010000 -#define B57600 0010001 -#define B115200 0010002 -#define B230400 0010003 -#define B460800 0010004 -#define B500000 0010005 -#define B576000 0010006 -#define B921600 0010007 -#define B1000000 0010010 -#define B1152000 0010011 -#define B1500000 0010012 -#define B2000000 0010013 -#define B2500000 0010014 -#define B3000000 0010015 -#define B3500000 0010016 -#define B4000000 0010017 -#define CIBAUD 002003600000 /* input baud rate */ -#define CMSPAR 010000000000 /* mark or space (stick) parity */ -#define CRTSCTS 020000000000 /* flow control */ - -#define IBSHIFT 16 /* shift from CBAUD to CIBAUD */ - -/* c_lflag bits */ -#define ISIG 0000001 -#define ICANON 0000002 -#define XCASE 0000004 -#define ECHO 0000010 -#define ECHOE 0000020 -#define ECHOK 0000040 -#define ECHONL 0000100 -#define NOFLSH 0000200 -#define TOSTOP 0000400 -#define ECHOCTL 0001000 -#define ECHOPRT 0002000 -#define ECHOKE 0004000 -#define FLUSHO 0010000 -#define PENDIN 0040000 -#define IEXTEN 0100000 -#define EXTPROC 0200000 - - -/* tcflow() and TCXONC use these */ -#define TCOOFF 0 -#define TCOON 1 -#define TCIOFF 2 -#define TCION 3 - -/* tcflush() and TCFLSH use these */ -#define TCIFLUSH 0 -#define TCOFLUSH 1 -#define TCIOFLUSH 2 - -/* tcsetattr uses these */ -#define TCSANOW 0 -#define TCSADRAIN 1 -#define TCSAFLUSH 2 - -#endif /* __ARCH_H8300_TERMBITS_H__ */ diff --git a/arch/h8300/include/uapi/asm/termios.h b/arch/h8300/include/uapi/asm/termios.h deleted file mode 100644 index 5a67d7e..0000000 --- a/arch/h8300/include/uapi/asm/termios.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef _UAPI_H8300_TERMIOS_H -#define _UAPI_H8300_TERMIOS_H - -#include -#include - -struct winsize { - unsigned short ws_row; - unsigned short ws_col; - unsigned short ws_xpixel; - unsigned short ws_ypixel; -}; - -#define NCC 8 -struct termio { - unsigned short c_iflag; /* input mode flags */ - unsigned short c_oflag; /* output mode flags */ - unsigned short c_cflag; /* control mode flags */ - unsigned short c_lflag; /* local mode flags */ - unsigned char c_line; /* line discipline */ - unsigned char c_cc[NCC]; /* control characters */ -}; - - -/* modem lines */ -#define TIOCM_LE 0x001 -#define TIOCM_DTR 0x002 -#define TIOCM_RTS 0x004 -#define TIOCM_ST 0x008 -#define TIOCM_SR 0x010 -#define TIOCM_CTS 0x020 -#define TIOCM_CAR 0x040 -#define TIOCM_RNG 0x080 -#define TIOCM_DSR 0x100 -#define TIOCM_CD TIOCM_CAR -#define TIOCM_RI TIOCM_RNG -#define TIOCM_OUT1 0x2000 -#define TIOCM_OUT2 0x4000 -#define TIOCM_LOOP 0x8000 - -/* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */ - - -#endif /* _UAPI_H8300_TERMIOS_H */ diff --git a/arch/h8300/include/uapi/asm/types.h b/arch/h8300/include/uapi/asm/types.h deleted file mode 100644 index 9ec9d4c..0000000 --- a/arch/h8300/include/uapi/asm/types.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/arch/h8300/include/uapi/asm/unistd.h b/arch/h8300/include/uapi/asm/unistd.h deleted file mode 100644 index 8cb5d42..0000000 --- a/arch/h8300/include/uapi/asm/unistd.h +++ /dev/null @@ -1,330 +0,0 @@ -#ifndef _UAPI_ASM_H8300_UNISTD_H_ -#define _UAPI_ASM_H8300_UNISTD_H_ - -/* - * This file contains the system call numbers. - */ - -#define __NR_restart_syscall 0 -#define __NR_exit 1 -#define __NR_fork 2 -#define __NR_read 3 -#define __NR_write 4 -#define __NR_open 5 -#define __NR_close 6 -#define __NR_waitpid 7 -#define __NR_creat 8 -#define __NR_link 9 -#define __NR_unlink 10 -#define __NR_execve 11 -#define __NR_chdir 12 -#define __NR_time 13 -#define __NR_mknod 14 -#define __NR_chmod 15 -#define __NR_lchown 16 -#define __NR_break 17 -#define __NR_oldstat 18 -#define __NR_lseek 19 -#define __NR_getpid 20 -#define __NR_mount 21 -#define __NR_umount 22 -#define __NR_setuid 23 -#define __NR_getuid 24 -#define __NR_stime 25 -#define __NR_ptrace 26 -#define __NR_alarm 27 -#define __NR_oldfstat 28 -#define __NR_pause 29 -#define __NR_utime 30 -#define __NR_stty 31 -#define __NR_gtty 32 -#define __NR_access 33 -#define __NR_nice 34 -#define __NR_ftime 35 -#define __NR_sync 36 -#define __NR_kill 37 -#define __NR_rename 38 -#define __NR_mkdir 39 -#define __NR_rmdir 40 -#define __NR_dup 41 -#define __NR_pipe 42 -#define __NR_times 43 -#define __NR_prof 44 -#define __NR_brk 45 -#define __NR_setgid 46 -#define __NR_getgid 47 -#define __NR_signal 48 -#define __NR_geteuid 49 -#define __NR_getegid 50 -#define __NR_acct 51 -#define __NR_umount2 52 -#define __NR_lock 53 -#define __NR_ioctl 54 -#define __NR_fcntl 55 -#define __NR_mpx 56 -#define __NR_setpgid 57 -#define __NR_ulimit 58 -#define __NR_oldolduname 59 -#define __NR_umask 60 -#define __NR_chroot 61 -#define __NR_ustat 62 -#define __NR_dup2 63 -#define __NR_getppid 64 -#define __NR_getpgrp 65 -#define __NR_setsid 66 -#define __NR_sigaction 67 -#define __NR_sgetmask 68 -#define __NR_ssetmask 69 -#define __NR_setreuid 70 -#define __NR_setregid 71 -#define __NR_sigsuspend 72 -#define __NR_sigpending 73 -#define __NR_sethostname 74 -#define __NR_setrlimit 75 -#define __NR_getrlimit 76 -#define __NR_getrusage 77 -#define __NR_gettimeofday 78 -#define __NR_settimeofday 79 -#define __NR_getgroups 80 -#define __NR_setgroups 81 -#define __NR_select 82 -#define __NR_symlink 83 -#define __NR_oldlstat 84 -#define __NR_readlink 85 -#define __NR_uselib 86 -#define __NR_swapon 87 -#define __NR_reboot 88 -#define __NR_readdir 89 -#define __NR_mmap 90 -#define __NR_munmap 91 -#define __NR_truncate 92 -#define __NR_ftruncate 93 -#define __NR_fchmod 94 -#define __NR_fchown 95 -#define __NR_getpriority 96 -#define __NR_setpriority 97 -#define __NR_profil 98 -#define __NR_statfs 99 -#define __NR_fstatfs 100 -#define __NR_ioperm 101 -#define __NR_socketcall 102 -#define __NR_syslog 103 -#define __NR_setitimer 104 -#define __NR_getitimer 105 -#define __NR_stat 106 -#define __NR_lstat 107 -#define __NR_fstat 108 -#define __NR_olduname 109 -#define __NR_iopl 110 -#define __NR_vhangup 111 -#define __NR_idle 112 -#define __NR_vm86old 113 -#define __NR_wait4 114 -#define __NR_swapoff 115 -#define __NR_sysinfo 116 -#define __NR_ipc 117 -#define __NR_fsync 118 -#define __NR_sigreturn 119 -#define __NR_clone 120 -#define __NR_setdomainname 121 -#define __NR_uname 122 -#define __NR_modify_ldt 123 -#define __NR_adjtimex 124 -#define __NR_mprotect 125 -#define __NR_sigprocmask 126 -#define __NR_create_module 127 -#define __NR_init_module 128 -#define __NR_delete_module 129 -#define __NR_get_kernel_syms 130 -#define __NR_quotactl 131 -#define __NR_getpgid 132 -#define __NR_fchdir 133 -#define __NR_bdflush 134 -#define __NR_sysfs 135 -#define __NR_personality 136 -#define __NR_afs_syscall 137 /* Syscall for Andrew File System */ -#define __NR_setfsuid 138 -#define __NR_setfsgid 139 -#define __NR__llseek 140 -#define __NR_getdents 141 -#define __NR__newselect 142 -#define __NR_flock 143 -#define __NR_msync 144 -#define __NR_readv 145 -#define __NR_writev 146 -#define __NR_getsid 147 -#define __NR_fdatasync 148 -#define __NR__sysctl 149 -#define __NR_mlock 150 -#define __NR_munlock 151 -#define __NR_mlockall 152 -#define __NR_munlockall 153 -#define __NR_sched_setparam 154 -#define __NR_sched_getparam 155 -#define __NR_sched_setscheduler 156 -#define __NR_sched_getscheduler 157 -#define __NR_sched_yield 158 -#define __NR_sched_get_priority_max 159 -#define __NR_sched_get_priority_min 160 -#define __NR_sched_rr_get_interval 161 -#define __NR_nanosleep 162 -#define __NR_mremap 163 -#define __NR_setresuid 164 -#define __NR_getresuid 165 -#define __NR_vm86 166 -#define __NR_query_module 167 -#define __NR_poll 168 -#define __NR_nfsservctl 169 -#define __NR_setresgid 170 -#define __NR_getresgid 171 -#define __NR_prctl 172 -#define __NR_rt_sigreturn 173 -#define __NR_rt_sigaction 174 -#define __NR_rt_sigprocmask 175 -#define __NR_rt_sigpending 176 -#define __NR_rt_sigtimedwait 177 -#define __NR_rt_sigqueueinfo 178 -#define __NR_rt_sigsuspend 179 -#define __NR_pread64 180 -#define __NR_pwrite64 181 -#define __NR_chown 182 -#define __NR_getcwd 183 -#define __NR_capget 184 -#define __NR_capset 185 -#define __NR_sigaltstack 186 -#define __NR_sendfile 187 -#define __NR_getpmsg 188 /* some people actually want streams */ -#define __NR_putpmsg 189 /* some people actually want streams */ -#define __NR_vfork 190 -#define __NR_ugetrlimit 191 -#define __NR_mmap2 192 -#define __NR_truncate64 193 -#define __NR_ftruncate64 194 -#define __NR_stat64 195 -#define __NR_lstat64 196 -#define __NR_fstat64 197 -#define __NR_lchown32 198 -#define __NR_getuid32 199 -#define __NR_getgid32 200 -#define __NR_geteuid32 201 -#define __NR_getegid32 202 -#define __NR_setreuid32 203 -#define __NR_setregid32 204 -#define __NR_getgroups32 205 -#define __NR_setgroups32 206 -#define __NR_fchown32 207 -#define __NR_setresuid32 208 -#define __NR_getresuid32 209 -#define __NR_setresgid32 210 -#define __NR_getresgid32 211 -#define __NR_chown32 212 -#define __NR_setuid32 213 -#define __NR_setgid32 214 -#define __NR_setfsuid32 215 -#define __NR_setfsgid32 216 -#define __NR_pivot_root 217 -#define __NR_mincore 218 -#define __NR_madvise 219 -#define __NR_madvise1 219 -#define __NR_getdents64 220 -#define __NR_fcntl64 221 -/* 223 is unused */ -#define __NR_gettid 224 -#define __NR_readahead 225 -#define __NR_setxattr 226 -#define __NR_lsetxattr 227 -#define __NR_fsetxattr 228 -#define __NR_getxattr 229 -#define __NR_lgetxattr 230 -#define __NR_fgetxattr 231 -#define __NR_listxattr 232 -#define __NR_llistxattr 233 -#define __NR_flistxattr 234 -#define __NR_removexattr 235 -#define __NR_lremovexattr 236 -#define __NR_fremovexattr 237 -#define __NR_tkill 238 -#define __NR_sendfile64 239 -#define __NR_futex 240 -#define __NR_sched_setaffinity 241 -#define __NR_sched_getaffinity 242 -#define __NR_set_thread_area 243 -#define __NR_get_thread_area 244 -#define __NR_io_setup 245 -#define __NR_io_destroy 246 -#define __NR_io_getevents 247 -#define __NR_io_submit 248 -#define __NR_io_cancel 249 -#define __NR_fadvise64 250 -/* 251 is available for reuse (was briefly sys_set_zone_reclaim) */ -#define __NR_exit_group 252 -#define __NR_lookup_dcookie 253 -#define __NR_epoll_create 254 -#define __NR_epoll_ctl 255 -#define __NR_epoll_wait 256 -#define __NR_remap_file_pages 257 -#define __NR_set_tid_address 258 -#define __NR_timer_create 259 -#define __NR_timer_settime (__NR_timer_create+1) -#define __NR_timer_gettime (__NR_timer_create+2) -#define __NR_timer_getoverrun (__NR_timer_create+3) -#define __NR_timer_delete (__NR_timer_create+4) -#define __NR_clock_settime (__NR_timer_create+5) -#define __NR_clock_gettime (__NR_timer_create+6) -#define __NR_clock_getres (__NR_timer_create+7) -#define __NR_clock_nanosleep (__NR_timer_create+8) -#define __NR_statfs64 268 -#define __NR_fstatfs64 269 -#define __NR_tgkill 270 -#define __NR_utimes 271 -#define __NR_fadvise64_64 272 -#define __NR_vserver 273 -#define __NR_mbind 274 -#define __NR_get_mempolicy 275 -#define __NR_set_mempolicy 276 -#define __NR_mq_open 277 -#define __NR_mq_unlink (__NR_mq_open+1) -#define __NR_mq_timedsend (__NR_mq_open+2) -#define __NR_mq_timedreceive (__NR_mq_open+3) -#define __NR_mq_notify (__NR_mq_open+4) -#define __NR_mq_getsetattr (__NR_mq_open+5) -#define __NR_kexec_load 283 -#define __NR_waitid 284 -/* #define __NR_sys_setaltroot 285 */ -#define __NR_add_key 286 -#define __NR_request_key 287 -#define __NR_keyctl 288 -#define __NR_ioprio_set 289 -#define __NR_ioprio_get 290 -#define __NR_inotify_init 291 -#define __NR_inotify_add_watch 292 -#define __NR_inotify_rm_watch 293 -#define __NR_migrate_pages 294 -#define __NR_openat 295 -#define __NR_mkdirat 296 -#define __NR_mknodat 297 -#define __NR_fchownat 298 -#define __NR_futimesat 299 -#define __NR_fstatat64 300 -#define __NR_unlinkat 301 -#define __NR_renameat 302 -#define __NR_linkat 303 -#define __NR_symlinkat 304 -#define __NR_readlinkat 305 -#define __NR_fchmodat 306 -#define __NR_faccessat 307 -#define __NR_pselect6 308 -#define __NR_ppoll 309 -#define __NR_unshare 310 -#define __NR_set_robust_list 311 -#define __NR_get_robust_list 312 -#define __NR_splice 313 -#define __NR_sync_file_range 314 -#define __NR_tee 315 -#define __NR_vmsplice 316 -#define __NR_move_pages 317 -#define __NR_getcpu 318 -#define __NR_epoll_pwait 319 -#define __NR_setns 320 - -#endif /* _UAPI_ASM_H8300_UNISTD_H_ */ diff --git a/arch/h8300/kernel/Makefile b/arch/h8300/kernel/Makefile deleted file mode 100644 index 1cc57f8..0000000 --- a/arch/h8300/kernel/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# -# Makefile for the linux kernel. -# - -extra-y := vmlinux.lds - -obj-y := process.o traps.o ptrace.o irq.o \ - sys_h8300.o time.o signal.o \ - setup.o gpio.o syscalls.o \ - entry.o timer/ - -obj-$(CONFIG_MODULES) += module.o h8300_ksyms.o diff --git a/arch/h8300/kernel/asm-offsets.c b/arch/h8300/kernel/asm-offsets.c deleted file mode 100644 index fd961e0..0000000 --- a/arch/h8300/kernel/asm-offsets.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * This program is used to generate definitions needed by - * assembly language modules. - * - * We use the technique used in the OSF Mach kernel code: - * generate asm statements containing #defines, - * compile this file to assembler, and then extract the - * #defines from the assembly-language output. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -int main(void) -{ - /* offsets into the task struct */ - DEFINE(TASK_STATE, offsetof(struct task_struct, state)); - DEFINE(TASK_FLAGS, offsetof(struct task_struct, flags)); - DEFINE(TASK_PTRACE, offsetof(struct task_struct, ptrace)); - DEFINE(TASK_BLOCKED, offsetof(struct task_struct, blocked)); - DEFINE(TASK_THREAD, offsetof(struct task_struct, thread)); - DEFINE(TASK_THREAD_INFO, offsetof(struct task_struct, stack)); - DEFINE(TASK_MM, offsetof(struct task_struct, mm)); - DEFINE(TASK_ACTIVE_MM, offsetof(struct task_struct, active_mm)); - - /* offsets into the irq_cpustat_t struct */ - DEFINE(CPUSTAT_SOFTIRQ_PENDING, offsetof(irq_cpustat_t, __softirq_pending)); - - /* offsets into the thread struct */ - DEFINE(THREAD_KSP, offsetof(struct thread_struct, ksp)); - DEFINE(THREAD_USP, offsetof(struct thread_struct, usp)); - DEFINE(THREAD_CCR, offsetof(struct thread_struct, ccr)); - - /* offsets into the pt_regs struct */ - DEFINE(LER0, offsetof(struct pt_regs, er0) - sizeof(long)); - DEFINE(LER1, offsetof(struct pt_regs, er1) - sizeof(long)); - DEFINE(LER2, offsetof(struct pt_regs, er2) - sizeof(long)); - DEFINE(LER3, offsetof(struct pt_regs, er3) - sizeof(long)); - DEFINE(LER4, offsetof(struct pt_regs, er4) - sizeof(long)); - DEFINE(LER5, offsetof(struct pt_regs, er5) - sizeof(long)); - DEFINE(LER6, offsetof(struct pt_regs, er6) - sizeof(long)); - DEFINE(LORIG, offsetof(struct pt_regs, orig_er0) - sizeof(long)); - DEFINE(LCCR, offsetof(struct pt_regs, ccr) - sizeof(long)); - DEFINE(LVEC, offsetof(struct pt_regs, vector) - sizeof(long)); -#if defined(__H8300S__) - DEFINE(LEXR, offsetof(struct pt_regs, exr) - sizeof(long)); -#endif - DEFINE(LRET, offsetof(struct pt_regs, pc) - sizeof(long)); - - DEFINE(PT_PTRACED, PT_PTRACED); - - return 0; -} diff --git a/arch/h8300/kernel/entry.S b/arch/h8300/kernel/entry.S deleted file mode 100644 index 94bd30f..0000000 --- a/arch/h8300/kernel/entry.S +++ /dev/null @@ -1,402 +0,0 @@ -/* -*- mode: asm -*- - * - * linux/arch/h8300/platform/h8300h/entry.S - * - * Yoshinori Sato - * David McCullough - * - */ - -/* - * entry.S - * include exception/interrupt gateway - * system call entry - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(CONFIG_CPU_H8300H) -#define USERRET 8 -INTERRUPTS = 64 - .h8300h - .macro SHLL2 reg - shll.l \reg - shll.l \reg - .endm - .macro SHLR2 reg - shlr.l \reg - shlr.l \reg - .endm - .macro SAVEREGS - mov.l er0,@-sp - mov.l er1,@-sp - mov.l er2,@-sp - mov.l er3,@-sp - .endm - .macro RESTOREREGS - mov.l @sp+,er3 - mov.l @sp+,er2 - .endm - .macro SAVEEXR - .endm - .macro RESTOREEXR - .endm -#endif -#if defined(CONFIG_CPU_H8S) -#define USERRET 10 -#define USEREXR 8 -INTERRUPTS = 128 - .h8300s - .macro SHLL2 reg - shll.l #2,\reg - .endm - .macro SHLR2 reg - shlr.l #2,\reg - .endm - .macro SAVEREGS - stm.l er0-er3,@-sp - .endm - .macro RESTOREREGS - ldm.l @sp+,er2-er3 - .endm - .macro SAVEEXR - mov.w @(USEREXR:16,er0),r1 - mov.w r1,@(LEXR-LER3:16,sp) /* copy EXR */ - .endm - .macro RESTOREEXR - mov.w @(LEXR-LER1:16,sp),r1 /* restore EXR */ - mov.b r1l,r1h - mov.w r1,@(USEREXR:16,er0) - .endm -#endif - - -/* CPU context save/restore macros. */ - - .macro SAVE_ALL - mov.l er0,@-sp - stc ccr,r0l /* check kernel mode */ - btst #4,r0l - bne 5f - - /* user mode */ - mov.l sp,@_sw_usp - mov.l @sp,er0 /* restore saved er0 */ - orc #0x10,ccr /* switch kernel stack */ - mov.l @_sw_ksp,sp - sub.l #(LRET-LORIG),sp /* allocate LORIG - LRET */ - SAVEREGS - mov.l @_sw_usp,er0 - mov.l @(USERRET:16,er0),er1 /* copy the RET addr */ - mov.l er1,@(LRET-LER3:16,sp) - SAVEEXR - - mov.l @(LORIG-LER3:16,sp),er0 - mov.l er0,@(LER0-LER3:16,sp) /* copy ER0 */ - mov.w e1,r1 /* e1 highbyte = ccr */ - and #0xef,r1h /* mask mode? flag */ - bra 6f -5: - /* kernel mode */ - mov.l @sp,er0 /* restore saved er0 */ - subs #2,sp /* set dummy ccr */ - SAVEREGS - mov.w @(LRET-LER3:16,sp),r1 /* copy old ccr */ -6: - mov.b r1h,r1l - mov.b #0,r1h - mov.w r1,@(LCCR-LER3:16,sp) /* set ccr */ - mov.l er6,@-sp /* syscall arg #6 */ - mov.l er5,@-sp /* syscall arg #5 */ - mov.l er4,@-sp /* syscall arg #4 */ - .endm /* r1 = ccr */ - - .macro RESTORE_ALL - mov.l @sp+,er4 - mov.l @sp+,er5 - mov.l @sp+,er6 - RESTOREREGS - mov.w @(LCCR-LER1:16,sp),r0 /* check kernel mode */ - btst #4,r0l - bne 7f - - orc #0x80,ccr - mov.l @_sw_usp,er0 - mov.l @(LER0-LER1:16,sp),er1 /* restore ER0 */ - mov.l er1,@er0 - RESTOREEXR - mov.w @(LCCR-LER1:16,sp),r1 /* restore the RET addr */ - mov.b r1l,r1h - mov.b @(LRET+1-LER1:16,sp),r1l - mov.w r1,e1 - mov.w @(LRET+2-LER1:16,sp),r1 - mov.l er1,@(USERRET:16,er0) - - mov.l @sp+,er1 - add.l #(LRET-LER1),sp /* remove LORIG - LRET */ - mov.l sp,@_sw_ksp - andc #0xef,ccr /* switch to user mode */ - mov.l er0,sp - bra 8f -7: - mov.l @sp+,er1 - adds #4,sp - adds #2,sp -8: - mov.l @sp+,er0 - adds #4,sp /* remove the sw created LVEC */ - rte - .endm - -.globl _system_call -.globl _ret_from_exception -.globl _ret_from_fork -.globl _ret_from_kernel_thread -.globl _ret_from_interrupt -.globl _interrupt_redirect_table -.globl _sw_ksp,_sw_usp -.globl _resume -.globl _interrupt_entry -.globl _trace_break - -#if defined(CONFIG_ROMKERNEL) - .section .int_redirect,"ax" -_interrupt_redirect_table: -#if defined(CONFIG_CPU_H8300H) - .rept 7 - .long 0 - .endr -#endif -#if defined(CONFIG_CPU_H8S) - .rept 5 - .long 0 - .endr - jmp @_trace_break - .long 0 -#endif - - jsr @_interrupt_entry /* NMI */ - jmp @_system_call /* TRAPA #0 (System call) */ - .long 0 - .long 0 - jmp @_trace_break /* TRAPA #3 (breakpoint) */ - .rept INTERRUPTS-12 - jsr @_interrupt_entry - .endr -#endif -#if defined(CONFIG_RAMKERNEL) -.globl _interrupt_redirect_table - .section .bss -_interrupt_redirect_table: - .space 4 -#endif - - .section .text - .align 2 -_interrupt_entry: - SAVE_ALL - mov.l sp,er0 - add.l #LVEC,er0 - btst #4,r1l - bne 1f - /* user LVEC */ - mov.l @_sw_usp,er0 - adds #4,er0 -1: - mov.l @er0,er0 /* LVEC address */ -#if defined(CONFIG_ROMKERNEL) - sub.l #_interrupt_redirect_table,er0 -#endif -#if defined(CONFIG_RAMKERNEL) - mov.l @_interrupt_redirect_table,er1 - sub.l er1,er0 -#endif - SHLR2 er0 - dec.l #1,er0 - mov.l sp,er1 - subs #4,er1 /* adjust ret_pc */ - jsr @_do_IRQ - jmp @_ret_from_interrupt - -_system_call: - subs #4,sp /* dummy LVEC */ - SAVE_ALL - andc #0x7f,ccr - mov.l er0,er4 - - /* save top of frame */ - mov.l sp,er0 - jsr @_set_esp0 - mov.l sp,er2 - and.w #0xe000,r2 - mov.b @((TI_FLAGS+3-(TIF_SYSCALL_TRACE >> 3)):16,er2),r2l - btst #(TIF_SYSCALL_TRACE & 7),r2l - beq 1f - jsr @_do_syscall_trace -1: - cmp.l #NR_syscalls,er4 - bcc badsys - SHLL2 er4 - mov.l #_sys_call_table,er0 - add.l er4,er0 - mov.l @er0,er4 - beq _ret_from_exception:16 - mov.l @(LER1:16,sp),er0 - mov.l @(LER2:16,sp),er1 - mov.l @(LER3:16,sp),er2 - jsr @er4 - mov.l er0,@(LER0:16,sp) /* save the return value */ - mov.l sp,er2 - and.w #0xe000,r2 - mov.b @((TI_FLAGS+3-(TIF_SYSCALL_TRACE >> 3)):16,er2),r2l - btst #(TIF_SYSCALL_TRACE & 7),r2l - beq 2f - jsr @_do_syscall_trace -2: -#if defined(CONFIG_SYSCALL_PRINT) - jsr @_syscall_print -#endif - orc #0x80,ccr - bra resume_userspace - -badsys: - mov.l #-ENOSYS,er0 - mov.l er0,@(LER0:16,sp) - bra resume_userspace - -#if !defined(CONFIG_PREEMPT) -#define resume_kernel restore_all -#endif - -_ret_from_exception: -#if defined(CONFIG_PREEMPT) - orc #0x80,ccr -#endif -_ret_from_interrupt: - mov.b @(LCCR+1:16,sp),r0l - btst #4,r0l - bne resume_kernel:8 /* return from kernel */ -resume_userspace: - andc #0x7f,ccr - mov.l sp,er4 - and.w #0xe000,r4 /* er4 <- current thread info */ - mov.l @(TI_FLAGS:16,er4),er1 - and.l #_TIF_WORK_MASK,er1 - beq restore_all:8 -work_pending: - btst #TIF_NEED_RESCHED,r1l - bne work_resched:8 - /* work notifysig */ - mov.l sp,er0 - subs #4,er0 /* er0: pt_regs */ - jsr @_do_notify_resume - bra restore_all:8 -work_resched: - mov.l sp,er0 - jsr @_set_esp0 - jsr @_schedule - bra resume_userspace:8 -restore_all: - RESTORE_ALL /* Does RTE */ - -#if defined(CONFIG_PREEMPT) -resume_kernel: - mov.l @(TI_PRE_COUNT:16,er4),er0 - bne restore_all:8 -need_resched: - mov.l @(TI_FLAGS:16,er4),er0 - btst #TIF_NEED_RESCHED,r0l - beq restore_all:8 - mov.b @(LCCR+1:16,sp),r0l /* Interrupt Enabled? */ - bmi restore_all:8 - mov.l #PREEMPT_ACTIVE,er0 - mov.l er0,@(TI_PRE_COUNT:16,er4) - andc #0x7f,ccr - mov.l sp,er0 - jsr @_set_esp0 - jsr @_schedule - orc #0x80,ccr - bra need_resched:8 -#endif - -_ret_from_fork: - mov.l er2,er0 - jsr @_schedule_tail - jmp @_ret_from_exception - -_ret_from_kernel_thread: - mov.l er2,er0 - jsr @_schedule_tail - mov.l @(LER4:16,sp),er0 - mov.l @(LER5:16,sp),er1 - jsr @er1 - jmp @_ret_from_exception - -_resume: - /* - * Beware - when entering resume, offset of tss is in d1, - * prev (the current task) is in a0, next (the new task) - * is in a1 and d2.b is non-zero if the mm structure is - * shared between the tasks, so don't change these - * registers until their contents are no longer needed. - */ - - /* save sr */ - sub.w r3,r3 - stc ccr,r3l - mov.w r3,@(THREAD_CCR+2:16,er0) - - /* disable interrupts */ - orc #0x80,ccr - mov.l @_sw_usp,er3 - mov.l er3,@(THREAD_USP:16,er0) - mov.l sp,@(THREAD_KSP:16,er0) - - /* Skip address space switching if they are the same. */ - /* FIXME: what did we hack out of here, this does nothing! */ - - mov.l @(THREAD_USP:16,er1),er0 - mov.l er0,@_sw_usp - mov.l @(THREAD_KSP:16,er1),sp - - /* restore status register */ - mov.w @(THREAD_CCR+2:16,er1),r3 - - ldc r3l,ccr - rts - -_trace_break: - subs #4,sp - SAVE_ALL - sub.l er1,er1 - dec.l #1,er1 - mov.l er1,@(LORIG,sp) - mov.l sp,er0 - jsr @_set_esp0 - mov.l @_sw_usp,er0 - mov.l @er0,er1 - mov.w @(-2:16,er1),r2 - cmp.w #0x5730,r2 - beq 1f - subs #2,er1 - mov.l er1,@er0 -1: - and.w #0xff,e1 - mov.l er1,er0 - jsr @_trace_trap - jmp @_ret_from_exception - - .section .bss -_sw_ksp: - .space 4 -_sw_usp: - .space 4 - - .end diff --git a/arch/h8300/kernel/gpio.c b/arch/h8300/kernel/gpio.c deleted file mode 100644 index 084bfd0..0000000 --- a/arch/h8300/kernel/gpio.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * linux/arch/h8300/kernel/gpio.c - * - * Yoshinori Sato - * - */ - -/* - * Internal I/O Port Management - */ - -#include -#include -#include -#include -#include -#include -#include - -#define _(addr) (volatile unsigned char *)(addr) -#if defined(CONFIG_H83007) || defined(CONFIG_H83068) -#include -static volatile unsigned char *ddrs[] = { - _(P1DDR),_(P2DDR),_(P3DDR),_(P4DDR),_(P5DDR),_(P6DDR), - NULL, _(P8DDR),_(P9DDR),_(PADDR),_(PBDDR), -}; -#define MAX_PORT 11 -#endif - - #if defined(CONFIG_H83002) || defined(CONFIG_H8048) -/* Fix me!! */ -#include -static volatile unsigned char *ddrs[] = { - _(P1DDR),_(P2DDR),_(P3DDR),_(P4DDR),_(P5DDR),_(P6DDR), - NULL, _(P8DDR),_(P9DDR),_(PADDR),_(PBDDR), -}; -#define MAX_PORT 11 -#endif - -#if defined(CONFIG_H8S2678) -#include -static volatile unsigned char *ddrs[] = { - _(P1DDR),_(P2DDR),_(P3DDR),NULL ,_(P5DDR),_(P6DDR), - _(P7DDR),_(P8DDR),NULL, _(PADDR),_(PBDDR),_(PCDDR), - _(PDDDR),_(PEDDR),_(PFDDR),_(PGDDR),_(PHDDR), - _(PADDR),_(PBDDR),_(PCDDR),_(PDDDR),_(PEDDR),_(PFDDR), - _(PGDDR),_(PHDDR) -}; -#define MAX_PORT 17 -#endif -#undef _ - -#if !defined(P1DDR) -#error Unsuppoted CPU Selection -#endif - -static struct { - unsigned char used; - unsigned char ddr; -} gpio_regs[MAX_PORT]; - -extern char *_platform_gpio_table(int length); - -int h8300_reserved_gpio(int port, unsigned int bits) -{ - unsigned char *used; - - if (port < 0 || port >= MAX_PORT) - return -1; - used = &(gpio_regs[port].used); - if ((*used & bits) != 0) - return 0; - *used |= bits; - return 1; -} - -int h8300_free_gpio(int port, unsigned int bits) -{ - unsigned char *used; - - if (port < 0 || port >= MAX_PORT) - return -1; - used = &(gpio_regs[port].used); - if ((*used & bits) != bits) - return 0; - *used &= (~bits); - return 1; -} - -int h8300_set_gpio_dir(int port_bit,int dir) -{ - int port = (port_bit >> 8) & 0xff; - int bit = port_bit & 0xff; - - if (ddrs[port] == NULL) - return 0; - if (gpio_regs[port].used & bit) { - if (dir) - gpio_regs[port].ddr |= bit; - else - gpio_regs[port].ddr &= ~bit; - *ddrs[port] = gpio_regs[port].ddr; - return 1; - } else - return 0; -} - -int h8300_get_gpio_dir(int port_bit) -{ - int port = (port_bit >> 8) & 0xff; - int bit = port_bit & 0xff; - - if (ddrs[port] == NULL) - return 0; - if (gpio_regs[port].used & bit) { - return (gpio_regs[port].ddr & bit) != 0; - } else - return -1; -} - -#if defined(CONFIG_PROC_FS) -static char *port_status(int portno) -{ - static char result[10]; - static const char io[2]={'I','O'}; - char *rp; - int c; - unsigned char used,ddr; - - used = gpio_regs[portno].used; - ddr = gpio_regs[portno].ddr; - result[8]='\0'; - rp = result + 7; - for (c = 8; c > 0; c--,rp--,used >>= 1, ddr >>= 1) - if (used & 0x01) - *rp = io[ ddr & 0x01]; - else - *rp = '-'; - return result; -} - -static int gpio_proc_show(struct seq_file *m, void *v) -{ - static const char port_name[]="123456789ABCDEFGH"; - int c; - - for (c = 0; c < MAX_PORT; c++) { - if (ddrs[c] == NULL) - continue; - seq_printf(m, "P%c: %s\n", port_name[c], port_status(c)); - } - return 0; -} - -static int gpio_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, gpio_proc_show, PDE_DATA(inode)); -} - -static const struct file_operations gpio_proc_fops = { - .open = gpio_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static __init int register_proc(void) -{ - return proc_create("gpio", S_IRUGO, NULL, &gpio_proc_fops) != NULL; -} - -__initcall(register_proc); -#endif - -void __init h8300_gpio_init(void) -{ - memcpy(gpio_regs,_platform_gpio_table(sizeof(gpio_regs)),sizeof(gpio_regs)); -} diff --git a/arch/h8300/kernel/h8300_ksyms.c b/arch/h8300/kernel/h8300_ksyms.c deleted file mode 100644 index 53d7c0e..0000000 --- a/arch/h8300/kernel/h8300_ksyms.c +++ /dev/null @@ -1,100 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -//asmlinkage long long __ashrdi3 (long long, int); -//asmlinkage long long __lshrdi3 (long long, int); -extern char h8300_debug_device[]; - -/* platform dependent support */ - -EXPORT_SYMBOL(strnlen); -EXPORT_SYMBOL(strrchr); -EXPORT_SYMBOL(strstr); -EXPORT_SYMBOL(strchr); -EXPORT_SYMBOL(strcat); -EXPORT_SYMBOL(strlen); -EXPORT_SYMBOL(strcmp); -EXPORT_SYMBOL(strncmp); - -EXPORT_SYMBOL(ip_fast_csum); - -EXPORT_SYMBOL(enable_irq); -EXPORT_SYMBOL(disable_irq); - -/* Networking helper routines. */ -EXPORT_SYMBOL(csum_partial_copy_nocheck); - -/* The following are special because they're not called - explicitly (the C compiler generates them). Fortunately, - their interface isn't gonna change any time soon now, so - it's OK to leave it out of version control. */ -//EXPORT_SYMBOL(__ashrdi3); -//EXPORT_SYMBOL(__lshrdi3); -EXPORT_SYMBOL(memcpy); -EXPORT_SYMBOL(memset); -EXPORT_SYMBOL(memcmp); -EXPORT_SYMBOL(memscan); -EXPORT_SYMBOL(memmove); - -/* - * libgcc functions - functions that are used internally by the - * compiler... (prototypes are not correct though, but that - * doesn't really matter since they're not versioned). - */ -extern void __gcc_bcmp(void); -extern void __ashldi3(void); -extern void __ashrdi3(void); -extern void __cmpdi2(void); -extern void __divdi3(void); -extern void __divsi3(void); -extern void __lshrdi3(void); -extern void __moddi3(void); -extern void __modsi3(void); -extern void __muldi3(void); -extern void __mulsi3(void); -extern void __negdi2(void); -extern void __ucmpdi2(void); -extern void __udivdi3(void); -extern void __udivmoddi4(void); -extern void __udivsi3(void); -extern void __umoddi3(void); -extern void __umodsi3(void); - - /* gcc lib functions */ -EXPORT_SYMBOL(__gcc_bcmp); -EXPORT_SYMBOL(__ashldi3); -EXPORT_SYMBOL(__ashrdi3); -EXPORT_SYMBOL(__cmpdi2); -EXPORT_SYMBOL(__divdi3); -EXPORT_SYMBOL(__divsi3); -EXPORT_SYMBOL(__lshrdi3); -EXPORT_SYMBOL(__moddi3); -EXPORT_SYMBOL(__modsi3); -EXPORT_SYMBOL(__muldi3); -EXPORT_SYMBOL(__mulsi3); -EXPORT_SYMBOL(__negdi2); -EXPORT_SYMBOL(__ucmpdi2); -EXPORT_SYMBOL(__udivdi3); -EXPORT_SYMBOL(__udivmoddi4); -EXPORT_SYMBOL(__udivsi3); -EXPORT_SYMBOL(__umoddi3); -EXPORT_SYMBOL(__umodsi3); - -EXPORT_SYMBOL(h8300_reserved_gpio); -EXPORT_SYMBOL(h8300_free_gpio); -EXPORT_SYMBOL(h8300_set_gpio_dir); diff --git a/arch/h8300/kernel/irq.c b/arch/h8300/kernel/irq.c deleted file mode 100644 index 2fa8ac7..0000000 --- a/arch/h8300/kernel/irq.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - * linux/arch/h8300/kernel/irq.c - * - * Copyright 2007 Yoshinori Sato - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/*#define DEBUG*/ - -extern unsigned long *interrupt_redirect_table; -extern const int h8300_saved_vectors[]; -extern const h8300_vector h8300_trap_table[]; -int h8300_enable_irq_pin(unsigned int irq); -void h8300_disable_irq_pin(unsigned int irq); - -#define CPU_VECTOR ((unsigned long *)0x000000) -#define ADDR_MASK (0xffffff) - -static inline int is_ext_irq(unsigned int irq) -{ - return (irq >= EXT_IRQ0 && irq <= (EXT_IRQ0 + EXT_IRQS)); -} - -static void h8300_enable_irq(struct irq_data *data) -{ - if (is_ext_irq(data->irq)) - IER_REGS |= 1 << (data->irq - EXT_IRQ0); -} - -static void h8300_disable_irq(struct irq_data *data) -{ - if (is_ext_irq(data->irq)) - IER_REGS &= ~(1 << (data->irq - EXT_IRQ0)); -} - -static unsigned int h8300_startup_irq(struct irq_data *data) -{ - if (is_ext_irq(data->irq)) - return h8300_enable_irq_pin(data->irq); - else - return 0; -} - -static void h8300_shutdown_irq(struct irq_data *data) -{ - if (is_ext_irq(data->irq)) - h8300_disable_irq_pin(data->irq); -} - -/* - * h8300 interrupt controller implementation - */ -struct irq_chip h8300irq_chip = { - .name = "H8300-INTC", - .irq_startup = h8300_startup_irq, - .irq_shutdown = h8300_shutdown_irq, - .irq_enable = h8300_enable_irq, - .irq_disable = h8300_disable_irq, -}; - -#if defined(CONFIG_RAMKERNEL) -static unsigned long __init *get_vector_address(void) -{ - unsigned long *rom_vector = CPU_VECTOR; - unsigned long base,tmp; - int vec_no; - - base = rom_vector[EXT_IRQ0] & ADDR_MASK; - - /* check romvector format */ - for (vec_no = EXT_IRQ1; vec_no <= EXT_IRQ0+EXT_IRQS; vec_no++) { - if ((base+(vec_no - EXT_IRQ0)*4) != (rom_vector[vec_no] & ADDR_MASK)) - return NULL; - } - - /* ramvector base address */ - base -= EXT_IRQ0*4; - - /* writerble check */ - tmp = ~(*(volatile unsigned long *)base); - (*(volatile unsigned long *)base) = tmp; - if ((*(volatile unsigned long *)base) != tmp) - return NULL; - return (unsigned long *)base; -} - -static void __init setup_vector(void) -{ - int i; - unsigned long *ramvec,*ramvec_p; - const h8300_vector *trap_entry; - const int *saved_vector; - - ramvec = get_vector_address(); - if (ramvec == NULL) - panic("interrupt vector serup failed."); - else - printk(KERN_INFO "virtual vector at 0x%08lx\n",(unsigned long)ramvec); - - /* create redirect table */ - ramvec_p = ramvec; - trap_entry = h8300_trap_table; - saved_vector = h8300_saved_vectors; - for ( i = 0; i < NR_IRQS; i++) { - if (i == *saved_vector) { - ramvec_p++; - saved_vector++; - } else { - if ( i < NR_TRAPS ) { - if (*trap_entry) - *ramvec_p = VECTOR(*trap_entry); - ramvec_p++; - trap_entry++; - } else - *ramvec_p++ = REDIRECT(interrupt_entry); - } - } - interrupt_redirect_table = ramvec; -#ifdef DEBUG - ramvec_p = ramvec; - for (i = 0; i < NR_IRQS; i++) { - if ((i % 8) == 0) - printk(KERN_DEBUG "\n%p: ",ramvec_p); - printk(KERN_DEBUG "%p ",*ramvec_p); - ramvec_p++; - } - printk(KERN_DEBUG "\n"); -#endif -} -#else -#define setup_vector() do { } while(0) -#endif - -void __init init_IRQ(void) -{ - int c; - - setup_vector(); - - for (c = 0; c < NR_IRQS; c++) - irq_set_chip_and_handler(c, &h8300irq_chip, handle_simple_irq); -} - -asmlinkage void do_IRQ(int irq) -{ - irq_enter(); - generic_handle_irq(irq); - irq_exit(); -} diff --git a/arch/h8300/kernel/module.c b/arch/h8300/kernel/module.c deleted file mode 100644 index 1d526e0..0000000 --- a/arch/h8300/kernel/module.c +++ /dev/null @@ -1,75 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(fmt...) -#endif - -int apply_relocate_add(Elf32_Shdr *sechdrs, - const char *strtab, - unsigned int symindex, - unsigned int relsec, - struct module *me) -{ - unsigned int i; - Elf32_Rela *rela = (void *)sechdrs[relsec].sh_addr; - - DEBUGP("Applying relocate section %u to %u\n", relsec, - sechdrs[relsec].sh_info); - for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) { - /* This is where to make the change */ - uint32_t *loc = (uint32_t *)(sechdrs[sechdrs[relsec].sh_info].sh_addr - + rela[i].r_offset); - /* This is the symbol it is referring to. Note that all - undefined symbols have been resolved. */ - Elf32_Sym *sym = (Elf32_Sym *)sechdrs[symindex].sh_addr - + ELF32_R_SYM(rela[i].r_info); - uint32_t v = sym->st_value + rela[i].r_addend; - - switch (ELF32_R_TYPE(rela[i].r_info)) { - case R_H8_DIR24R8: - loc = (uint32_t *)((uint32_t)loc - 1); - *loc = (*loc & 0xff000000) | ((*loc & 0xffffff) + v); - break; - case R_H8_DIR24A8: - if (ELF32_R_SYM(rela[i].r_info)) - *loc += v; - break; - case R_H8_DIR32: - case R_H8_DIR32A16: - *loc += v; - break; - case R_H8_PCREL16: - v -= (unsigned long)loc + 2; - if ((Elf32_Sword)v > 0x7fff || - (Elf32_Sword)v < -(Elf32_Sword)0x8000) - goto overflow; - else - *(unsigned short *)loc = v; - break; - case R_H8_PCREL8: - v -= (unsigned long)loc + 1; - if ((Elf32_Sword)v > 0x7f || - (Elf32_Sword)v < -(Elf32_Sword)0x80) - goto overflow; - else - *(unsigned char *)loc = v; - break; - default: - printk(KERN_ERR "module %s: Unknown relocation: %u\n", - me->name, ELF32_R_TYPE(rela[i].r_info)); - return -ENOEXEC; - } - } - return 0; - overflow: - printk(KERN_ERR "module %s: relocation offset overflow: %08x\n", - me->name, rela[i].r_offset); - return -ENOEXEC; -} diff --git a/arch/h8300/kernel/process.c b/arch/h8300/kernel/process.c deleted file mode 100644 index 1a744ab..0000000 --- a/arch/h8300/kernel/process.c +++ /dev/null @@ -1,154 +0,0 @@ -/* - * linux/arch/h8300/kernel/process.c - * - * Yoshinori Sato - * - * Based on: - * - * linux/arch/m68knommu/kernel/process.c - * - * Copyright (C) 1998 D. Jeff Dionne , - * Kenneth Albanowski , - * The Silver Hammer Group, Ltd. - * - * linux/arch/m68k/kernel/process.c - * - * Copyright (C) 1995 Hamish Macdonald - * - * 68060 fixes by Jesper Skov - */ - -/* - * This file handles the architecture-dependent parts of process handling.. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -void (*pm_power_off)(void) = NULL; -EXPORT_SYMBOL(pm_power_off); - -asmlinkage void ret_from_fork(void); -asmlinkage void ret_from_kernel_thread(void); - -/* - * The idle loop on an H8/300.. - */ -#if !defined(CONFIG_H8300H_SIM) && !defined(CONFIG_H8S_SIM) -void arch_cpu_idle(void) -{ - local_irq_enable(); - /* XXX: race here! What if need_resched() gets set now? */ - __asm__("sleep"); -} -#endif - -void machine_restart(char * __unused) -{ - local_irq_disable(); - __asm__("jmp @@0"); -} - -void machine_halt(void) -{ - local_irq_disable(); - __asm__("sleep"); - for (;;); -} - -void machine_power_off(void) -{ - local_irq_disable(); - __asm__("sleep"); - for (;;); -} - -void show_regs(struct pt_regs * regs) -{ - show_regs_print_info(KERN_DEFAULT); - - printk("\nPC: %08lx Status: %02x", - regs->pc, regs->ccr); - printk("\nORIG_ER0: %08lx ER0: %08lx ER1: %08lx", - regs->orig_er0, regs->er0, regs->er1); - printk("\nER2: %08lx ER3: %08lx ER4: %08lx ER5: %08lx", - regs->er2, regs->er3, regs->er4, regs->er5); - printk("\nER6' %08lx ",regs->er6); - if (user_mode(regs)) - printk("USP: %08lx\n", rdusp()); - else - printk("\n"); -} - -void flush_thread(void) -{ -} - -int copy_thread(unsigned long clone_flags, - unsigned long usp, unsigned long topstk, - struct task_struct * p) -{ - struct pt_regs * childregs; - - childregs = (struct pt_regs *) (THREAD_SIZE + task_stack_page(p)) - 1; - - if (unlikely(p->flags & PF_KTHREAD)) { - memset(childregs, 0, sizeof(struct pt_regs)); - childregs->retpc = (unsigned long) ret_from_kernel_thread; - childregs->er4 = topstk; /* arg */ - childregs->er5 = usp; /* fn */ - p->thread.ksp = (unsigned long)childregs; - } - *childregs = *current_pt_regs(); - childregs->retpc = (unsigned long) ret_from_fork; - childregs->er0 = 0; - p->thread.usp = usp ?: rdusp(); - p->thread.ksp = (unsigned long)childregs; - - return 0; -} - -unsigned long thread_saved_pc(struct task_struct *tsk) -{ - return ((struct pt_regs *)tsk->thread.esp0)->pc; -} - -unsigned long get_wchan(struct task_struct *p) -{ - unsigned long fp, pc; - unsigned long stack_page; - int count = 0; - if (!p || p == current || p->state == TASK_RUNNING) - return 0; - - stack_page = (unsigned long)p; - fp = ((struct pt_regs *)p->thread.ksp)->er6; - do { - if (fp < stack_page+sizeof(struct thread_info) || - fp >= 8184+stack_page) - return 0; - pc = ((unsigned long *)fp)[1]; - if (!in_sched_functions(pc)) - return pc; - fp = *(unsigned long *) fp; - } while (count++ < 16); - return 0; -} diff --git a/arch/h8300/kernel/ptrace.c b/arch/h8300/kernel/ptrace.c deleted file mode 100644 index 748cf65..0000000 --- a/arch/h8300/kernel/ptrace.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * linux/arch/h8300/kernel/ptrace.c - * - * Yoshinori Sato - * - * Based on: - * linux/arch/m68k/kernel/ptrace.c - * - * Copyright (C) 1994 by Hamish Macdonald - * Taken from linux/kernel/ptrace.c and modified for M680x0. - * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds - * - * 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 - -/* cpu depend functions */ -extern long h8300_get_reg(struct task_struct *task, int regno); -extern int h8300_put_reg(struct task_struct *task, int regno, unsigned long data); - - -void user_disable_single_step(struct task_struct *child) -{ -} - -/* - * does not yet catch signals sent when the child dies. - * in exit.c or in signal.c. - */ - -void ptrace_disable(struct task_struct *child) -{ - user_disable_single_step(child); -} - -long arch_ptrace(struct task_struct *child, long request, - unsigned long addr, unsigned long data) -{ - int ret; - int regno = addr >> 2; - unsigned long __user *datap = (unsigned long __user *) data; - - switch (request) { - /* read the word at location addr in the USER area. */ - case PTRACE_PEEKUSR: { - unsigned long tmp = 0; - - if ((addr & 3) || addr >= sizeof(struct user)) { - ret = -EIO; - break ; - } - - ret = 0; /* Default return condition */ - - if (regno < H8300_REGS_NO) - tmp = h8300_get_reg(child, regno); - else { - switch (regno) { - case 49: - tmp = child->mm->start_code; - break ; - case 50: - tmp = child->mm->start_data; - break ; - case 51: - tmp = child->mm->end_code; - break ; - case 52: - tmp = child->mm->end_data; - break ; - default: - ret = -EIO; - } - } - if (!ret) - ret = put_user(tmp, datap); - break ; - } - - /* when I and D space are separate, this will have to be fixed. */ - case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ - if ((addr & 3) || addr >= sizeof(struct user)) { - ret = -EIO; - break ; - } - - if (regno == PT_ORIG_ER0) { - ret = -EIO; - break ; - } - if (regno < H8300_REGS_NO) { - ret = h8300_put_reg(child, regno, data); - break ; - } - ret = -EIO; - break ; - - case PTRACE_GETREGS: { /* Get all gp regs from the child. */ - int i; - unsigned long tmp; - for (i = 0; i < H8300_REGS_NO; i++) { - tmp = h8300_get_reg(child, i); - if (put_user(tmp, datap)) { - ret = -EFAULT; - break; - } - datap++; - } - ret = 0; - break; - } - - case PTRACE_SETREGS: { /* Set all gp regs in the child. */ - int i; - unsigned long tmp; - for (i = 0; i < H8300_REGS_NO; i++) { - if (get_user(tmp, datap)) { - ret = -EFAULT; - break; - } - h8300_put_reg(child, i, tmp); - datap++; - } - ret = 0; - break; - } - - default: - ret = ptrace_request(child, request, addr, data); - break; - } - return ret; -} - -asmlinkage void do_syscall_trace(void) -{ - if (!test_thread_flag(TIF_SYSCALL_TRACE)) - return; - if (!(current->ptrace & PT_PTRACED)) - return; - ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0)); - /* - * this isn't the same as continuing with a signal, but it will do - * for normal use. strace only continues with a signal if the - * stopping signal is not SIGTRAP. -brl - */ - if (current->exit_code) { - send_sig(current->exit_code, current, 1); - current->exit_code = 0; - } -} diff --git a/arch/h8300/kernel/setup.c b/arch/h8300/kernel/setup.c deleted file mode 100644 index d0b1607..0000000 --- a/arch/h8300/kernel/setup.c +++ /dev/null @@ -1,242 +0,0 @@ -/* - * linux/arch/h8300/kernel/setup.c - * - * Copyleft ()) 2000 James D. Schettine {james@telos-systems.com} - * Copyright (C) 1999,2000 Greg Ungerer (gerg@snapgear.com) - * Copyright (C) 1998,1999 D. Jeff Dionne - * Copyright (C) 1998 Kenneth Albanowski - * Copyright (C) 1995 Hamish Macdonald - * Copyright (C) 2000 Lineo Inc. (www.lineo.com) - * Copyright (C) 2001 Lineo, Inc. - * - * H8/300 porting Yoshinori Sato - */ - -/* - * This file handles the architecture-dependent parts of system setup - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#if defined(__H8300H__) -#define CPU "H8/300H" -#include -#endif - -#if defined(__H8300S__) -#define CPU "H8S" -#include -#endif - -#define STUBSIZE 0xc000 - -unsigned long rom_length; -unsigned long memory_start; -unsigned long memory_end; - -char __initdata command_line[COMMAND_LINE_SIZE]; - -extern int _ramstart, _ramend; -extern char _target_name[]; -extern void h8300_gpio_init(void); - -#if (defined(CONFIG_H8300H_SIM) || defined(CONFIG_H8S_SIM)) \ - && defined(CONFIG_GDB_MAGICPRINT) -/* printk with gdb service */ -static void gdb_console_output(struct console *c, const char *msg, unsigned len) -{ - for (; len > 0; len--) { - asm("mov.w %0,r2\n\t" - "jsr @0xc4"::"r"(*msg++):"er2"); - } -} - -/* - * Setup initial baud/bits/parity. We do two things here: - * - construct a cflag setting for the first rs_open() - * - initialize the serial port - * Return non-zero if we didn't find a serial port. - */ -static int __init gdb_console_setup(struct console *co, char *options) -{ - return 0; -} - -static const struct console gdb_console = { - .name = "gdb_con", - .write = gdb_console_output, - .device = NULL, - .setup = gdb_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, -}; -#endif - -void __init setup_arch(char **cmdline_p) -{ - int bootmap_size; - - memory_start = (unsigned long) &_ramstart; - - /* allow for ROMFS on the end of the kernel */ - if (memcmp((void *)memory_start, "-rom1fs-", 8) == 0) { -#if defined(CONFIG_BLK_DEV_INITRD) - initrd_start = memory_start; - initrd_end = memory_start += be32_to_cpu(((unsigned long *) (memory_start))[2]); -#else - memory_start += be32_to_cpu(((unsigned long *) memory_start)[2]); -#endif - } - memory_start = PAGE_ALIGN(memory_start); -#if !defined(CONFIG_BLKDEV_RESERVE) - memory_end = (unsigned long) &_ramend; /* by now the stack is part of the init task */ -#if defined(CONFIG_GDB_DEBUG) - memory_end -= STUBSIZE; -#endif -#else - if ((memory_end < CONFIG_BLKDEV_RESERVE_ADDRESS) && - (memory_end > CONFIG_BLKDEV_RESERVE_ADDRESS)) - /* overlap userarea */ - memory_end = CONFIG_BLKDEV_RESERVE_ADDRESS; -#endif - - init_mm.start_code = (unsigned long) _stext; - init_mm.end_code = (unsigned long) _etext; - init_mm.end_data = (unsigned long) _edata; - init_mm.brk = (unsigned long) 0; - -#if (defined(CONFIG_H8300H_SIM) || defined(CONFIG_H8S_SIM)) && defined(CONFIG_GDB_MAGICPRINT) - register_console((struct console *)&gdb_console); -#endif - - printk(KERN_INFO "\r\n\nuClinux " CPU "\n"); - printk(KERN_INFO "Target Hardware: %s\n",_target_name); - printk(KERN_INFO "Flat model support (C) 1998,1999 Kenneth Albanowski, D. Jeff Dionne\n"); - printk(KERN_INFO "H8/300 series support by Yoshinori Sato \n"); - -#ifdef DEBUG - printk(KERN_DEBUG "KERNEL -> TEXT=0x%p-0x%p DATA=0x%p-0x%p " - "BSS=0x%p-0x%p\n", _stext, _etext, _sdata, _edata, __bss_start, - __bss_stop); - printk(KERN_DEBUG "KERNEL -> ROMFS=0x%p-0x%06lx MEM=0x%06lx-0x%06lx " - "STACK=0x%06lx-0x%p\n", __bss_stop, memory_start, memory_start, - memory_end, memory_end, &_ramend); -#endif - -#ifdef CONFIG_DEFAULT_CMDLINE - /* set from default command line */ - if (*command_line == '\0') - strcpy(command_line,CONFIG_KERNEL_COMMAND); -#endif - /* Keep a copy of command line */ - *cmdline_p = &command_line[0]; - memcpy(boot_command_line, command_line, COMMAND_LINE_SIZE); - boot_command_line[COMMAND_LINE_SIZE-1] = 0; - -#ifdef DEBUG - if (strlen(*cmdline_p)) - printk(KERN_DEBUG "Command line: '%s'\n", *cmdline_p); -#endif - - /* - * give all the memory to the bootmap allocator, tell it to put the - * boot mem_map at the start of memory - */ - bootmap_size = init_bootmem_node( - NODE_DATA(0), - memory_start >> PAGE_SHIFT, /* map goes here */ - PAGE_OFFSET >> PAGE_SHIFT, /* 0 on coldfire */ - memory_end >> PAGE_SHIFT); - /* - * free the usable memory, we have to make sure we do not free - * the bootmem bitmap so we then reserve it after freeing it :-) - */ - free_bootmem(memory_start, memory_end - memory_start); - reserve_bootmem(memory_start, bootmap_size, BOOTMEM_DEFAULT); - /* - * get kmalloc into gear - */ - paging_init(); - h8300_gpio_init(); -#if defined(CONFIG_H8300_AKI3068NET) && defined(CONFIG_IDE) - { -#define AREABIT(addr) (1 << (((addr) >> 21) & 7)) - /* setup BSC */ - volatile unsigned char *abwcr = (volatile unsigned char *)ABWCR; - volatile unsigned char *cscr = (volatile unsigned char *)CSCR; - *abwcr &= ~(AREABIT(CONFIG_H8300_IDE_BASE) | AREABIT(CONFIG_H8300_IDE_ALT)); - *cscr |= (AREABIT(CONFIG_H8300_IDE_BASE) | AREABIT(CONFIG_H8300_IDE_ALT)) | 0x0f; - } -#endif -#ifdef DEBUG - printk(KERN_DEBUG "Done setup_arch\n"); -#endif -} - -/* - * Get CPU information for use by the procfs. - */ - -static int show_cpuinfo(struct seq_file *m, void *v) -{ - char *cpu; - int mode; - u_long clockfreq; - - cpu = CPU; - mode = *(volatile unsigned char *)MDCR & 0x07; - - clockfreq = CONFIG_CPU_CLOCK; - - seq_printf(m, "CPU:\t\t%s (mode:%d)\n" - "Clock:\t\t%lu.%1luMHz\n" - "BogoMips:\t%lu.%02lu\n" - "Calibration:\t%lu loops\n", - cpu,mode, - clockfreq/1000,clockfreq%1000, - (loops_per_jiffy*HZ)/500000,((loops_per_jiffy*HZ)/5000)%100, - (loops_per_jiffy*HZ)); - - return 0; -} - -static void *c_start(struct seq_file *m, loff_t *pos) -{ - return *pos < NR_CPUS ? ((void *) 0x12345678) : NULL; -} - -static void *c_next(struct seq_file *m, void *v, loff_t *pos) -{ - ++*pos; - return c_start(m, pos); -} - -static void c_stop(struct seq_file *m, void *v) -{ -} - -const struct seq_operations cpuinfo_op = { - .start = c_start, - .next = c_next, - .stop = c_stop, - .show = show_cpuinfo, -}; diff --git a/arch/h8300/kernel/signal.c b/arch/h8300/kernel/signal.c deleted file mode 100644 index a65ff3b..0000000 --- a/arch/h8300/kernel/signal.c +++ /dev/null @@ -1,444 +0,0 @@ -/* - * linux/arch/h8300/kernel/signal.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - * - * 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. - */ - -/* - * uClinux H8/300 support by Yoshinori Sato - * and David McCullough - * - * Based on - * Linux/m68k by Hamish Macdonald - */ - -/* - * ++roman (07/09/96): implemented signal stacks (specially for tosemu on - * Atari :-) Current limitation: Only one sigstack can be active at one time. - * If a second signal with SA_ONSTACK set arrives while working on a sigstack, - * SA_ONSTACK is ignored. This behaviour avoids lots of trouble with nested - * signal handlers! - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -/* - * Do a signal return; undo the signal stack. - * - * Keep the return code on the stack quadword aligned! - * That makes the cache flush below easier. - */ - -struct sigframe -{ - long dummy_er0; - long dummy_vector; -#if defined(CONFIG_CPU_H8S) - short dummy_exr; -#endif - long dummy_pc; - char *pretcode; - unsigned char retcode[8]; - unsigned long extramask[_NSIG_WORDS-1]; - struct sigcontext sc; - int sig; -} __attribute__((aligned(2),packed)); - -struct rt_sigframe -{ - long dummy_er0; - long dummy_vector; -#if defined(CONFIG_CPU_H8S) - short dummy_exr; -#endif - long dummy_pc; - char *pretcode; - struct siginfo *pinfo; - void *puc; - unsigned char retcode[8]; - struct siginfo info; - struct ucontext uc; - int sig; -} __attribute__((aligned(2),packed)); - -static inline int -restore_sigcontext(struct sigcontext *usc, int *pd0) -{ - struct pt_regs *regs = current_pt_regs(); - int err = 0; - unsigned int ccr; - unsigned int usp; - unsigned int er0; - - /* Always make any pending restarted system calls return -EINTR */ - current_thread_info()->restart_block.fn = do_no_restart_syscall; - -#define COPY(r) err |= __get_user(regs->r, &usc->sc_##r) /* restore passed registers */ - COPY(er1); - COPY(er2); - COPY(er3); - COPY(er5); - COPY(pc); - ccr = regs->ccr & 0x10; - COPY(ccr); -#undef COPY - regs->ccr &= 0xef; - regs->ccr |= ccr; - regs->orig_er0 = -1; /* disable syscall checks */ - err |= __get_user(usp, &usc->sc_usp); - wrusp(usp); - - err |= __get_user(er0, &usc->sc_er0); - *pd0 = er0; - return err; -} - -asmlinkage int sys_sigreturn(void) -{ - unsigned long usp = rdusp(); - struct sigframe *frame = (struct sigframe *)(usp - 4); - sigset_t set; - int er0; - - if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) - goto badframe; - if (__get_user(set.sig[0], &frame->sc.sc_mask) || - (_NSIG_WORDS > 1 && - __copy_from_user(&set.sig[1], &frame->extramask, - sizeof(frame->extramask)))) - goto badframe; - - set_current_blocked(&set); - - if (restore_sigcontext(&frame->sc, &er0)) - goto badframe; - return er0; - -badframe: - force_sig(SIGSEGV, current); - return 0; -} - -asmlinkage int sys_rt_sigreturn(void) -{ - unsigned long usp = rdusp(); - struct rt_sigframe *frame = (struct rt_sigframe *)(usp - 4); - sigset_t set; - int er0; - - if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) - goto badframe; - if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) - goto badframe; - - set_current_blocked(&set); - - if (restore_sigcontext(&frame->uc.uc_mcontext, &er0)) - goto badframe; - - if (restore_altstack(&frame->uc.uc_stack)) - goto badframe; - - return er0; - -badframe: - force_sig(SIGSEGV, current); - return 0; -} - -static int setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, - unsigned long mask) -{ - int err = 0; - - err |= __put_user(regs->er0, &sc->sc_er0); - err |= __put_user(regs->er1, &sc->sc_er1); - err |= __put_user(regs->er2, &sc->sc_er2); - err |= __put_user(regs->er3, &sc->sc_er3); - err |= __put_user(regs->er4, &sc->sc_er4); - err |= __put_user(regs->er5, &sc->sc_er5); - err |= __put_user(regs->er6, &sc->sc_er6); - err |= __put_user(rdusp(), &sc->sc_usp); - err |= __put_user(regs->pc, &sc->sc_pc); - err |= __put_user(regs->ccr, &sc->sc_ccr); - err |= __put_user(mask, &sc->sc_mask); - - return err; -} - -static inline void * -get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size) -{ - unsigned long usp; - - /* Default to using normal stack. */ - usp = rdusp(); - - /* This is the X/Open sanctioned signal stack switching. */ - if (ka->sa.sa_flags & SA_ONSTACK) { - if (!sas_ss_flags(usp)) - usp = current->sas_ss_sp + current->sas_ss_size; - } - return (void *)((usp - frame_size) & -8UL); -} - -static int setup_frame (int sig, struct k_sigaction *ka, - sigset_t *set, struct pt_regs *regs) -{ - struct sigframe *frame; - int err = 0; - int usig; - unsigned char *ret; - - frame = get_sigframe(ka, regs, sizeof(*frame)); - - if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) - goto give_sigsegv; - - usig = current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32 - ? current_thread_info()->exec_domain->signal_invmap[sig] - : sig; - - err |= __put_user(usig, &frame->sig); - if (err) - goto give_sigsegv; - - err |= setup_sigcontext(&frame->sc, regs, set->sig[0]); - if (err) - goto give_sigsegv; - - if (_NSIG_WORDS > 1) { - err |= copy_to_user(frame->extramask, &set->sig[1], - sizeof(frame->extramask)); - if (err) - goto give_sigsegv; - } - - ret = frame->retcode; - if (ka->sa.sa_flags & SA_RESTORER) - ret = (unsigned char *)(ka->sa.sa_restorer); - else { - /* sub.l er0,er0; mov.b #__NR_sigreturn,r0l; trapa #0 */ - err |= __put_user(0x1a80f800 + (__NR_sigreturn & 0xff), - (unsigned long *)(frame->retcode + 0)); - err |= __put_user(0x5700, (unsigned short *)(frame->retcode + 4)); - } - - /* Set up to return from userspace. */ - err |= __put_user(ret, &frame->pretcode); - - if (err) - goto give_sigsegv; - - /* Set up registers for signal handler */ - wrusp ((unsigned long) frame); - regs->pc = (unsigned long) ka->sa.sa_handler; - regs->er0 = (current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32 - ? current_thread_info()->exec_domain->signal_invmap[sig] - : sig); - regs->er1 = (unsigned long)&(frame->sc); - regs->er5 = current->mm->start_data; /* GOT base */ - - return 0; - -give_sigsegv: - force_sigsegv(sig, current); - return -EFAULT; -} - -static int setup_rt_frame (int sig, struct k_sigaction *ka, siginfo_t *info, - sigset_t *set, struct pt_regs *regs) -{ - struct rt_sigframe *frame; - int err = 0; - int usig; - unsigned char *ret; - - frame = get_sigframe(ka, regs, sizeof(*frame)); - - if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) - goto give_sigsegv; - - usig = current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32 - ? current_thread_info()->exec_domain->signal_invmap[sig] - : sig; - - err |= __put_user(usig, &frame->sig); - if (err) - goto give_sigsegv; - - err |= __put_user(&frame->info, &frame->pinfo); - err |= __put_user(&frame->uc, &frame->puc); - err |= copy_siginfo_to_user(&frame->info, info); - if (err) - goto give_sigsegv; - - /* Create the ucontext. */ - err |= __put_user(0, &frame->uc.uc_flags); - err |= __put_user(0, &frame->uc.uc_link); - err |= __save_altstack(&frame->uc.uc_stack, rdusp()); - err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, set->sig[0]); - err |= copy_to_user (&frame->uc.uc_sigmask, set, sizeof(*set)); - if (err) - goto give_sigsegv; - - /* Set up to return from userspace. */ - ret = frame->retcode; - if (ka->sa.sa_flags & SA_RESTORER) - ret = (unsigned char *)(ka->sa.sa_restorer); - else { - /* sub.l er0,er0; mov.b #__NR_sigreturn,r0l; trapa #0 */ - err |= __put_user(0x1a80f800 + (__NR_sigreturn & 0xff), - (unsigned long *)(frame->retcode + 0)); - err |= __put_user(0x5700, (unsigned short *)(frame->retcode + 4)); - } - err |= __put_user(ret, &frame->pretcode); - - if (err) - goto give_sigsegv; - - /* Set up registers for signal handler */ - wrusp ((unsigned long) frame); - regs->pc = (unsigned long) ka->sa.sa_handler; - regs->er0 = (current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32 - ? current_thread_info()->exec_domain->signal_invmap[sig] - : sig); - regs->er1 = (unsigned long)&(frame->info); - regs->er2 = (unsigned long)&frame->uc; - regs->er5 = current->mm->start_data; /* GOT base */ - - return 0; - -give_sigsegv: - force_sigsegv(sig, current); - return -EFAULT; -} - -/* - * OK, we're invoking a handler - */ -static void -handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, - struct pt_regs * regs) -{ - sigset_t *oldset = sigmask_to_save(); - int ret; - /* are we from a system call? */ - if (regs->orig_er0 >= 0) { - switch (regs->er0) { - case -ERESTART_RESTARTBLOCK: - case -ERESTARTNOHAND: - regs->er0 = -EINTR; - break; - - case -ERESTARTSYS: - if (!(ka->sa.sa_flags & SA_RESTART)) { - regs->er0 = -EINTR; - break; - } - /* fallthrough */ - case -ERESTARTNOINTR: - regs->er0 = regs->orig_er0; - regs->pc -= 2; - } - } - - /* set up the stack frame */ - if (ka->sa.sa_flags & SA_SIGINFO) - ret = setup_rt_frame(sig, ka, info, oldset, regs); - else - ret = setup_frame(sig, ka, oldset, regs); - - if (!ret) - signal_delivered(sig, info, ka, regs, 0); -} - -/* - * Note that 'init' is a special process: it doesn't get signals it doesn't - * want to handle. Thus you cannot kill init even with a SIGKILL even by - * mistake. - */ -static void do_signal(struct pt_regs *regs) -{ - siginfo_t info; - int signr; - struct k_sigaction ka; - - /* - * We want the common case to go fast, which - * is why we may in certain cases get here from - * kernel mode. Just return without doing anything - * if so. - */ - if ((regs->ccr & 0x10)) - return; - - current->thread.esp0 = (unsigned long) regs; - - signr = get_signal_to_deliver(&info, &ka, regs, NULL); - if (signr > 0) { - /* Whee! Actually deliver the signal. */ - handle_signal(signr, &info, &ka, regs); - return; - } - /* Did we come from a system call? */ - if (regs->orig_er0 >= 0) { - /* Restart the system call - no handlers present */ - if (regs->er0 == -ERESTARTNOHAND || - regs->er0 == -ERESTARTSYS || - regs->er0 == -ERESTARTNOINTR) { - regs->er0 = regs->orig_er0; - regs->pc -= 2; - } - if (regs->er0 == -ERESTART_RESTARTBLOCK){ - regs->er0 = __NR_restart_syscall; - regs->pc -= 2; - } - } - - /* If there's no signal to deliver, we just restore the saved mask. */ - restore_saved_sigmask(); -} - -asmlinkage void do_notify_resume(struct pt_regs *regs, u32 thread_info_flags) -{ - if (thread_info_flags & _TIF_SIGPENDING) - do_signal(regs); - - if (thread_info_flags & _TIF_NOTIFY_RESUME) { - clear_thread_flag(TIF_NOTIFY_RESUME); - tracehook_notify_resume(regs); - } -} diff --git a/arch/h8300/kernel/sys_h8300.c b/arch/h8300/kernel/sys_h8300.c deleted file mode 100644 index bf350cb..0000000 --- a/arch/h8300/kernel/sys_h8300.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * linux/arch/h8300/kernel/sys_h8300.c - * - * This file contains various random system calls that - * have a non-standard calling sequence on the H8/300 - * platform. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -/* sys_cacheflush -- no support. */ -asmlinkage int -sys_cacheflush (unsigned long addr, int scope, int cache, unsigned long len) -{ - return -EINVAL; -} - -asmlinkage int sys_getpagesize(void) -{ - return PAGE_SIZE; -} - -#if defined(CONFIG_SYSCALL_PRINT) -asmlinkage void syscall_print(void *dummy,...) -{ - struct pt_regs *regs = (struct pt_regs *) ((unsigned char *)&dummy-4); - printk("call %06lx:%ld 1:%08lx,2:%08lx,3:%08lx,ret:%08lx\n", - ((regs->pc)&0xffffff)-2,regs->orig_er0,regs->er1,regs->er2,regs->er3,regs->er0); -} -#endif diff --git a/arch/h8300/kernel/syscalls.S b/arch/h8300/kernel/syscalls.S deleted file mode 100644 index c55e0ed..0000000 --- a/arch/h8300/kernel/syscalls.S +++ /dev/null @@ -1,338 +0,0 @@ -/* Systemcall Entry Table */ -#include -#include -#include - -#define CALL(x) .long _ ## x - -.globl _sys_call_table - -#if defined(CONFIG_CPU_H8300H) - .h8300h -#endif -#if defined(CONFIG_CPU_H8S) - .h8300s -#endif - .section .text - .align 2 -_sys_call_table: - CALL(sys_ni_syscall) /* 0 - old "setup()" system call*/ - CALL(sys_exit) - CALL(sys_fork) - CALL(sys_read) - CALL(sys_write) - CALL(sys_open) /* 5 */ - CALL(sys_close) - CALL(sys_waitpid) - CALL(sys_creat) - CALL(sys_link) - CALL(sys_unlink) /* 10 */ - CALL(sys_execve) - CALL(sys_chdir) - CALL(sys_time) - CALL(sys_mknod) - CALL(sys_chmod) /* 15 */ - CALL(sys_chown16) - CALL(sys_ni_syscall) /* old break syscall holder */ - CALL(sys_stat) - CALL(sys_lseek) - CALL(sys_getpid) /* 20 */ - CALL(sys_mount) - CALL(sys_oldumount) - CALL(sys_setuid16) - CALL(sys_getuid16) - CALL(sys_stime) /* 25 */ - CALL(sys_ptrace) - CALL(sys_alarm) - CALL(sys_fstat) - CALL(sys_pause) - CALL(sys_utime) /* 30 */ - CALL(sys_ni_syscall) /* old stty syscall holder */ - CALL(sys_ni_syscall) /* old gtty syscall holder */ - CALL(sys_access) - CALL(sys_nice) - CALL(sys_ni_syscall) /* 35 old ftime syscall holder */ - CALL(sys_sync) - CALL(sys_kill) - CALL(sys_rename) - CALL(sys_mkdir) - CALL(sys_rmdir) /* 40 */ - CALL(sys_dup) - CALL(sys_pipe) - CALL(sys_times) - CALL(sys_ni_syscall) /* old prof syscall holder */ - CALL(sys_brk) /* 45 */ - CALL(sys_setgid16) - CALL(sys_getgid16) - CALL(sys_signal) - CALL(sys_geteuid16) - CALL(sys_getegid16) /* 50 */ - CALL(sys_acct) - CALL(sys_umount) /* recycled never used phys() */ - CALL(sys_ni_syscall) /* old lock syscall holder */ - CALL(sys_ioctl) - CALL(sys_fcntl) /* 55 */ - CALL(sys_ni_syscall) /* old mpx syscall holder */ - CALL(sys_setpgid) - CALL(sys_ni_syscall) /* old ulimit syscall holder */ - CALL(sys_ni_syscall) - CALL(sys_umask) /* 60 */ - CALL(sys_chroot) - CALL(sys_ustat) - CALL(sys_dup2) - CALL(sys_getppid) - CALL(sys_getpgrp) /* 65 */ - CALL(sys_setsid) - CALL(sys_sigaction) - CALL(sys_sgetmask) - CALL(sys_ssetmask) - CALL(sys_setreuid16) /* 70 */ - CALL(sys_setregid16) - CALL(sys_sigsuspend) - CALL(sys_sigpending) - CALL(sys_sethostname) - CALL(sys_setrlimit) /* 75 */ - CALL(sys_old_getrlimit) - CALL(sys_getrusage) - CALL(sys_gettimeofday) - CALL(sys_settimeofday) - CALL(sys_getgroups16) /* 80 */ - CALL(sys_setgroups16) - CALL(sys_old_select) - CALL(sys_symlink) - CALL(sys_lstat) - CALL(sys_readlink) /* 85 */ - CALL(sys_uselib) - CALL(sys_swapon) - CALL(sys_reboot) - CALL(sys_old_readdir) - CALL(sys_old_mmap) /* 90 */ - CALL(sys_munmap) - CALL(sys_truncate) - CALL(sys_ftruncate) - CALL(sys_fchmod) - CALL(sys_fchown16) /* 95 */ - CALL(sys_getpriority) - CALL(sys_setpriority) - CALL(sys_ni_syscall) /* old profil syscall holder */ - CALL(sys_statfs) - CALL(sys_fstatfs) /* 100 */ - CALL(sys_ni_syscall) /* ioperm for i386 */ - CALL(sys_socketcall) - CALL(sys_syslog) - CALL(sys_setitimer) - CALL(sys_getitimer) /* 105 */ - CALL(sys_newstat) - CALL(sys_newlstat) - CALL(sys_newfstat) - CALL(sys_ni_syscall) - CALL(sys_ni_syscall) /* iopl for i386 */ /* 110 */ - CALL(sys_vhangup) - CALL(sys_ni_syscall) /* obsolete idle() syscall */ - CALL(sys_ni_syscall) /* vm86old for i386 */ - CALL(sys_wait4) - CALL(sys_swapoff) /* 115 */ - CALL(sys_sysinfo) - CALL(sys_ipc) - CALL(sys_fsync) - CALL(sys_sigreturn) - CALL(sys_clone) /* 120 */ - CALL(sys_setdomainname) - CALL(sys_newuname) - CALL(sys_cacheflush) /* modify_ldt for i386 */ - CALL(sys_adjtimex) - CALL(sys_ni_syscall) /* 125 sys_mprotect */ - CALL(sys_sigprocmask) - CALL(sys_ni_syscall) /* sys_create_module */ - CALL(sys_init_module) - CALL(sys_delete_module) - CALL(sys_ni_syscall) /* 130 sys_get_kernel_syms */ - CALL(sys_quotactl) - CALL(sys_getpgid) - CALL(sys_fchdir) - CALL(sys_bdflush) - CALL(sys_sysfs) /* 135 */ - CALL(sys_personality) - CALL(sys_ni_syscall) /* for afs_syscall */ - CALL(sys_setfsuid16) - CALL(sys_setfsgid16) - CALL(sys_llseek) /* 140 */ - CALL(sys_getdents) - CALL(sys_select) - CALL(sys_flock) - CALL(sys_ni_syscall) /* sys_msync */ - CALL(sys_readv) /* 145 */ - CALL(sys_writev) - CALL(sys_getsid) - CALL(sys_fdatasync) - CALL(sys_sysctl) - CALL(sys_ni_syscall) /* 150 sys_mlock */ - CALL(sys_ni_syscall) /* sys_munlock */ - CALL(sys_ni_syscall) /* sys_mlockall */ - CALL(sys_ni_syscall) /* sys_munlockall */ - CALL(sys_sched_setparam) - CALL(sys_sched_getparam) /* 155 */ - CALL(sys_sched_setscheduler) - CALL(sys_sched_getscheduler) - CALL(sys_sched_yield) - CALL(sys_sched_get_priority_max) - CALL(sys_sched_get_priority_min) /* 160 */ - CALL(sys_sched_rr_get_interval) - CALL(sys_nanosleep) - CALL(sys_ni_syscall) /* sys_mremap */ - CALL(sys_setresuid16) - CALL(sys_getresuid16) /* 165 */ - CALL(sys_ni_syscall) /* for vm86 */ - CALL(sys_ni_syscall) /* sys_query_module */ - CALL(sys_poll) - CALL(sys_ni_syscall) /* old nfsservctl */ - CALL(sys_setresgid16) /* 170 */ - CALL(sys_getresgid16) - CALL(sys_prctl) - CALL(sys_rt_sigreturn) - CALL(sys_rt_sigaction) - CALL(sys_rt_sigprocmask) /* 175 */ - CALL(sys_rt_sigpending) - CALL(sys_rt_sigtimedwait) - CALL(sys_rt_sigqueueinfo) - CALL(sys_rt_sigsuspend) - CALL(sys_pread64) /* 180 */ - CALL(sys_pwrite64) - CALL(sys_lchown16); - CALL(sys_getcwd) - CALL(sys_capget) - CALL(sys_capset) /* 185 */ - CALL(sys_sigaltstack) - CALL(sys_sendfile) - CALL(sys_ni_syscall) /* streams1 */ - CALL(sys_ni_syscall) /* streams2 */ - CALL(sys_vfork) /* 190 */ - CALL(sys_getrlimit) - CALL(sys_mmap_pgoff) - CALL(sys_truncate64) - CALL(sys_ftruncate64) - CALL(sys_stat64) /* 195 */ - CALL(sys_lstat64) - CALL(sys_fstat64) - CALL(sys_chown) - CALL(sys_getuid) - CALL(sys_getgid) /* 200 */ - CALL(sys_geteuid) - CALL(sys_getegid) - CALL(sys_setreuid) - CALL(sys_setregid) - CALL(sys_getgroups) /* 205 */ - CALL(sys_setgroups) - CALL(sys_fchown) - CALL(sys_setresuid) - CALL(sys_getresuid) - CALL(sys_setresgid) /* 210 */ - CALL(sys_getresgid) - CALL(sys_lchown) - CALL(sys_setuid) - CALL(sys_setgid) - CALL(sys_setfsuid) /* 215 */ - CALL(sys_setfsgid) - CALL(sys_pivot_root) - CALL(sys_ni_syscall) - CALL(sys_ni_syscall) - CALL(sys_getdents64) /* 220 */ - CALL(sys_fcntl64) - CALL(sys_ni_syscall) /* reserved TUX */ - CALL(sys_ni_syscall) /* reserved Security */ - CALL(sys_gettid) - CALL(sys_readahead) /* 225 */ - CALL(sys_setxattr) - CALL(sys_lsetxattr) - CALL(sys_fsetxattr) - CALL(sys_getxattr) - CALL(sys_lgetxattr) /* 230 */ - CALL(sys_fgetxattr) - CALL(sys_listxattr) - CALL(sys_llistxattr) - CALL(sys_flistxattr) - CALL(sys_removexattr) /* 235 */ - CALL(sys_lremovexattr) - CALL(sys_fremovexattr) - CALL(sys_tkill) - CALL(sys_sendfile64) - CALL(sys_futex) /* 240 */ - CALL(sys_sched_setaffinity) - CALL(sys_sched_getaffinity) - CALL(sys_ni_syscall) - CALL(sys_ni_syscall) - CALL(sys_io_setup) /* 245 */ - CALL(sys_io_destroy) - CALL(sys_io_getevents) - CALL(sys_io_submit) - CALL(sys_io_cancel) - CALL(sys_fadvise64) /* 250 */ - CALL(sys_ni_syscall) - CALL(sys_exit_group) - CALL(sys_lookup_dcookie) - CALL(sys_epoll_create) - CALL(sys_epoll_ctl) /* 255 */ - CALL(sys_epoll_wait) - CALL(sys_ni_syscall) /* sys_remap_file_pages */ - CALL(sys_set_tid_address) - CALL(sys_timer_create) - CALL(sys_timer_settime) /* 260 */ - CALL(sys_timer_gettime) - CALL(sys_timer_getoverrun) - CALL(sys_timer_delete) - CALL(sys_clock_settime) - CALL(sys_clock_gettime) /* 265 */ - CALL(sys_clock_getres) - CALL(sys_clock_nanosleep) - CALL(sys_statfs64) - CALL(sys_fstatfs64) - CALL(sys_tgkill) /* 270 */ - CALL(sys_utimes) - CALL(sys_fadvise64_64) - CALL(sys_ni_syscall) /* sys_vserver */ - CALL(sys_ni_syscall) - CALL(sys_get_mempolicy) /* 275 */ - CALL(sys_set_mempolicy) - CALL(sys_mq_open) - CALL(sys_mq_unlink) - CALL(sys_mq_timedsend) - CALL(sys_mq_timedreceive) /* 280 */ - CALL(sys_mq_notify) - CALL(sys_mq_getsetattr) - CALL(sys_waitid) - CALL(sys_ni_syscall) /* sys_kexec_load */ - CALL(sys_add_key) /* 285 */ - CALL(sys_request_key) - CALL(sys_keyctl) - CALL(sys_ioprio_set) - CALL(sys_ioprio_get) /* 290 */ - CALL(sys_inotify_init) - CALL(sys_inotify_add_watch) - CALL(sys_inotify_rm_watch) - CALL(sys_migrate_pages) - CALL(sys_openat) /* 295 */ - CALL(sys_mkdirat) - CALL(sys_mknodat) - CALL(sys_fchownat) - CALL(sys_futimesat) - CALL(sys_fstatat64) /* 300 */ - CALL(sys_unlinkat) - CALL(sys_renameat) - CALL(sys_linkat) - CALL(sys_symlinkat) - CALL(sys_readlinkat) /* 305 */ - CALL(sys_fchmodat) - CALL(sys_faccessat) - CALL(sys_ni_syscall) /* sys_pselect6 */ - CALL(sys_ni_syscall) /* sys_ppoll */ - CALL(sys_unshare) /* 310 */ - CALL(sys_set_robust_list) - CALL(sys_get_robust_list) - CALL(sys_splice) - CALL(sys_sync_file_range) - CALL(sys_tee) /* 315 */ - CALL(sys_vmsplice) - CALL(sys_ni_syscall) /* sys_move_pages */ - CALL(sys_getcpu) - CALL(sys_ni_syscall) /* sys_epoll_pwait */ - CALL(sys_setns) /* 320 */ diff --git a/arch/h8300/kernel/time.c b/arch/h8300/kernel/time.c deleted file mode 100644 index e0f7419..0000000 --- a/arch/h8300/kernel/time.c +++ /dev/null @@ -1,66 +0,0 @@ -/* - * linux/arch/h8300/kernel/time.c - * - * Yoshinori Sato - * - * Copied/hacked from: - * - * linux/arch/m68k/kernel/time.c - * - * Copyright (C) 1991, 1992, 1995 Linus Torvalds - * - * This file contains the m68k-specific time handling details. - * Most of the stuff is located in the machine specific files. - * - * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 - * "A Kernel Model for Precision Timekeeping" by Dave Mills - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define TICK_SIZE (tick_nsec / 1000) - -void h8300_timer_tick(void) -{ - if (current->pid) - profile_tick(CPU_PROFILING); - xtime_update(1); - update_process_times(user_mode(get_irq_regs())); -} - -void read_persistent_clock(struct timespec *ts) -{ - unsigned int year, mon, day, hour, min, sec; - - /* FIX by dqg : Set to zero for platforms that don't have tod */ - /* without this time is undefined and can overflow time_t, causing */ - /* very strange errors */ - year = 1980; - mon = day = 1; - hour = min = sec = 0; -#ifdef CONFIG_H8300_GETTOD - h8300_gettod (&year, &mon, &day, &hour, &min, &sec); -#endif - if ((year += 1900) < 1970) - year += 100; - ts->tv_sec = mktime(year, mon, day, hour, min, sec); - ts->tv_nsec = 0; -} - -void __init time_init(void) -{ - - h8300_timer_setup(); -} diff --git a/arch/h8300/kernel/timer/Makefile b/arch/h8300/kernel/timer/Makefile deleted file mode 100644 index bef0510..0000000 --- a/arch/h8300/kernel/timer/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# h8300 internal timer handler - -obj-$(CONFIG_H8300_TIMER8) := timer8.o -obj-$(CONFIG_H8300_TIMER16) := timer16.o -obj-$(CONFIG_H8300_ITU) := itu.o -obj-$(CONFIG_H8300_TPU) := tpu.o diff --git a/arch/h8300/kernel/timer/itu.c b/arch/h8300/kernel/timer/itu.c deleted file mode 100644 index 0a8b5cd..0000000 --- a/arch/h8300/kernel/timer/itu.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * linux/arch/h8300/kernel/timer/itu.c - * - * Yoshinori Sato - * - * ITU Timer Handler - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#if CONFIG_H8300_ITU_CH == 0 -#define ITUBASE 0xffff64 -#define ITUIRQ 24 -#elif CONFIG_H8300_ITU_CH == 1 -#define ITUBASE 0xffff6e -#define ITUIRQ 28 -#elif CONFIG_H8300_ITU_CH == 2 -#define ITUBASE 0xffff78 -#define ITUIRQ 32 -#elif CONFIG_H8300_ITU_CH == 3 -#define ITUBASE 0xffff82 -#define ITUIRQ 36 -#elif CONFIG_H8300_ITU_CH == 4 -#define ITUBASE 0xffff92 -#define ITUIRQ 40 -#else -#error Unknown timer channel. -#endif - -#define TCR 0 -#define TIOR 1 -#define TIER 2 -#define TSR 3 -#define TCNT 4 -#define GRA 6 -#define GRB 8 - -static irqreturn_t timer_interrupt(int irq, void *dev_id) -{ - h8300_timer_tick(); - ctrl_bclr(IMFA, ITUBASE + TSR); - return IRQ_HANDLED; -} - -static struct irqaction itu_irq = { - .name = "itu", - .handler = timer_interrupt, - .flags = IRQF_DISABLED | IRQF_TIMER, -}; - -static const int __initconst divide_rate[] = {1, 2, 4, 8}; - -void __init h8300_timer_setup(void) -{ - unsigned int div; - unsigned int cnt; - - calc_param(cnt, div, divide_rate, 0x10000); - - setup_irq(ITUIRQ, &itu_irq); - - /* initialize timer */ - ctrl_outb(0, TSTR); - ctrl_outb(CCLR0 | div, ITUBASE + TCR); - ctrl_outb(0x01, ITUBASE + TIER); - ctrl_outw(cnt, ITUBASE + GRA); - ctrl_bset(CONFIG_H8300_ITU_CH, TSTR); -} diff --git a/arch/h8300/kernel/timer/timer16.c b/arch/h8300/kernel/timer/timer16.c deleted file mode 100644 index 462d9f58..0000000 --- a/arch/h8300/kernel/timer/timer16.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * linux/arch/h8300/kernel/timer/timer16.c - * - * Yoshinori Sato - * - * 16bit Timer Handler - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* 16bit timer */ -#if CONFIG_H8300_TIMER16_CH == 0 -#define _16BASE 0xffff78 -#define _16IRQ 24 -#elif CONFIG_H8300_TIMER16_CH == 1 -#define _16BASE 0xffff80 -#define _16IRQ 28 -#elif CONFIG_H8300_TIMER16_CH == 2 -#define _16BASE 0xffff88 -#define _16IRQ 32 -#else -#error Unknown timer channel. -#endif - -#define TCR 0 -#define TIOR 1 -#define TCNT 2 -#define GRA 4 -#define GRB 6 - -#define H8300_TIMER_FREQ CONFIG_CPU_CLOCK*10000 /* Timer input freq. */ - -static irqreturn_t timer_interrupt(int irq, void *dev_id) -{ - h8300_timer_tick(); - ctrl_bclr(CONFIG_H8300_TIMER16_CH, TISRA); - return IRQ_HANDLED; -} - -static struct irqaction timer16_irq = { - .name = "timer-16", - .handler = timer_interrupt, - .flags = IRQF_DISABLED | IRQF_TIMER, -}; - -static const int __initconst divide_rate[] = {1, 2, 4, 8}; - -void __init h8300_timer_setup(void) -{ - unsigned int div; - unsigned int cnt; - - calc_param(cnt, div, divide_rate, 0x10000); - - setup_irq(_16IRQ, &timer16_irq); - - /* initialize timer */ - ctrl_outb(0, TSTR); - ctrl_outb(CCLR0 | div, _16BASE + TCR); - ctrl_outw(cnt, _16BASE + GRA); - ctrl_bset(4 + CONFIG_H8300_TIMER16_CH, TISRA); - ctrl_bset(CONFIG_H8300_TIMER16_CH, TSTR); -} diff --git a/arch/h8300/kernel/timer/timer8.c b/arch/h8300/kernel/timer/timer8.c deleted file mode 100644 index 505f341..0000000 --- a/arch/h8300/kernel/timer/timer8.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * linux/arch/h8300/kernel/cpu/timer/timer8.c - * - * Yoshinori Sato - * - * 8bit Timer Handler - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#if defined(CONFIG_CPU_H8300H) -#include -#endif -#if defined(CONFIG_CPU_H8S) -#include -#endif - -/* 8bit timer x2 */ -#define CMFA 6 - -#if defined(CONFIG_H8300_TIMER8_CH0) -#define _8BASE _8TCR0 -#ifdef CONFIG_CPU_H8300H -#define _8IRQ 36 -#endif -#ifdef CONFIG_CPU_H8S -#define _8IRQ 72 -#endif -#elif defined(CONFIG_H8300_TIMER8_CH2) -#ifdef CONFIG_CPU_H8300H -#define _8BASE _8TCR2 -#define _8IRQ 40 -#endif -#endif - -#ifndef _8BASE -#error Unknown timer channel. -#endif - -#define _8TCR 0 -#define _8TCSR 2 -#define TCORA 4 -#define TCORB 6 -#define _8TCNT 8 - -#define CMIEA 0x40 -#define CCLR_CMA 0x08 -#define CKS2 0x04 - -/* - * timer_interrupt() needs to keep up the real-time clock, - * as well as call the "xtime_update()" routine every clocktick - */ - -static irqreturn_t timer_interrupt(int irq, void *dev_id) -{ - h8300_timer_tick(); - ctrl_bclr(CMFA, _8BASE + _8TCSR); - return IRQ_HANDLED; -} - -static struct irqaction timer8_irq = { - .name = "timer-8", - .handler = timer_interrupt, - .flags = IRQF_DISABLED | IRQF_TIMER, -}; - -static const int __initconst divide_rate[] = {8, 64, 8192}; - -void __init h8300_timer_setup(void) -{ - unsigned int div; - unsigned int cnt; - - calc_param(cnt, div, divide_rate, 0x10000); - div++; - - setup_irq(_8IRQ, &timer8_irq); - -#if defined(CONFIG_CPU_H8S) - /* Timer module enable */ - ctrl_bclr(0, MSTPCRL) -#endif - - /* initialize timer */ - ctrl_outw(cnt, _8BASE + TCORA); - ctrl_outw(0x0000, _8BASE + _8TCSR); - ctrl_outw((CMIEA|CCLR_CMA|CKS2) << 8 | div, - _8BASE + _8TCR); -} diff --git a/arch/h8300/kernel/timer/tpu.c b/arch/h8300/kernel/timer/tpu.c deleted file mode 100644 index 0350f62..0000000 --- a/arch/h8300/kernel/timer/tpu.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * linux/arch/h8300/kernel/timer/tpu.c - * - * Yoshinori Sato - * - * TPU Timer Handler - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* TPU */ -#if CONFIG_H8300_TPU_CH == 0 -#define TPUBASE 0xffffd0 -#define TPUIRQ 40 -#elif CONFIG_H8300_TPU_CH == 1 -#define TPUBASE 0xffffe0 -#define TPUIRQ 48 -#elif CONFIG_H8300_TPU_CH == 2 -#define TPUBASE 0xfffff0 -#define TPUIRQ 52 -#elif CONFIG_H8300_TPU_CH == 3 -#define TPUBASE 0xfffe80 -#define TPUIRQ 56 -#elif CONFIG_H8300_TPU_CH == 4 -#define TPUBASE 0xfffe90 -#define TPUIRQ 64 -#else -#error Unknown timer channel. -#endif - -#define _TCR 0 -#define _TMDR 1 -#define _TIOR 2 -#define _TIER 4 -#define _TSR 5 -#define _TCNT 6 -#define _GRA 8 -#define _GRB 10 - -#define CCLR0 0x20 - -static irqreturn_t timer_interrupt(int irq, void *dev_id) -{ - h8300_timer_tick(); - ctrl_bclr(0, TPUBASE + _TSR); - return IRQ_HANDLED; -} - -static struct irqaction tpu_irq = { - .name = "tpu", - .handler = timer_interrupt, - .flags = IRQF_DISABLED | IRQF_TIMER, -}; - -static const int __initconst divide_rate[] = { -#if CONFIG_H8300_TPU_CH == 0 - 1,4,16,64,0,0,0,0, -#elif (CONFIG_H8300_TPU_CH == 1) || (CONFIG_H8300_TPU_CH == 5) - 1,4,16,64,0,0,256,0, -#elif (CONFIG_H8300_TPU_CH == 2) || (CONFIG_H8300_TPU_CH == 4) - 1,4,16,64,0,0,0,1024, -#elif CONFIG_H8300_TPU_CH == 3 - 1,4,16,64,0,1024,256,4096, -#endif -}; - -void __init h8300_timer_setup(void) -{ - unsigned int cnt; - unsigned int div; - - calc_param(cnt, div, divide_rate, 0x10000); - - setup_irq(TPUIRQ, &tpu_irq); - - /* TPU module enabled */ - ctrl_bclr(3, MSTPCRH); - - ctrl_outb(0, TSTR); - ctrl_outb(CCLR0 | div, TPUBASE + _TCR); - ctrl_outb(0, TPUBASE + _TMDR); - ctrl_outw(0, TPUBASE + _TIOR); - ctrl_outb(0x01, TPUBASE + _TIER); - ctrl_outw(cnt, TPUBASE + _GRA); - ctrl_bset(CONFIG_H8300_TPU_CH, TSTR); -} diff --git a/arch/h8300/kernel/traps.c b/arch/h8300/kernel/traps.c deleted file mode 100644 index cfe494d..0000000 --- a/arch/h8300/kernel/traps.c +++ /dev/null @@ -1,166 +0,0 @@ -/* - * linux/arch/h8300/boot/traps.c -- general exception handling code - * H8/300 support Yoshinori Sato - * - * Cloned from Linux/m68k. - * - * No original Copyright holder listed, - * Probable original (C) Roman Zippel (assigned DJD, 1999) - * - * Copyright 1999-2000 D. Jeff Dionne, - * - * 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 - -static DEFINE_SPINLOCK(die_lock); - -/* - * this must be called very early as the kernel might - * use some instruction that are emulated on the 060 - */ - -void __init base_trap_init(void) -{ -} - -void __init trap_init (void) -{ -} - -asmlinkage void set_esp0 (unsigned long ssp) -{ - current->thread.esp0 = ssp; -} - -/* - * Generic dumping code. Used for panic and debug. - */ - -static void dump(struct pt_regs *fp) -{ - unsigned long *sp; - unsigned char *tp; - int i; - - printk("\nCURRENT PROCESS:\n\n"); - printk("COMM=%s PID=%d\n", current->comm, current->pid); - if (current->mm) { - printk("TEXT=%08x-%08x DATA=%08x-%08x BSS=%08x-%08x\n", - (int) current->mm->start_code, - (int) current->mm->end_code, - (int) current->mm->start_data, - (int) current->mm->end_data, - (int) current->mm->end_data, - (int) current->mm->brk); - printk("USER-STACK=%08x KERNEL-STACK=%08lx\n\n", - (int) current->mm->start_stack, - (int) PAGE_SIZE+(unsigned long)current); - } - - show_regs(fp); - printk("\nCODE:"); - tp = ((unsigned char *) fp->pc) - 0x20; - for (sp = (unsigned long *) tp, i = 0; (i < 0x40); i += 4) { - if ((i % 0x10) == 0) - printk("\n%08x: ", (int) (tp + i)); - printk("%08x ", (int) *sp++); - } - printk("\n"); - - printk("\nKERNEL STACK:"); - tp = ((unsigned char *) fp) - 0x40; - for (sp = (unsigned long *) tp, i = 0; (i < 0xc0); i += 4) { - if ((i % 0x10) == 0) - printk("\n%08x: ", (int) (tp + i)); - printk("%08x ", (int) *sp++); - } - printk("\n"); - if (STACK_MAGIC != *(unsigned long *)((unsigned long)current+PAGE_SIZE)) - printk("(Possibly corrupted stack page??)\n"); - - printk("\n\n"); -} - -void die(const char *str, struct pt_regs *fp, unsigned long err) -{ - static int diecount; - - oops_enter(); - - console_verbose(); - spin_lock_irq(&die_lock); - report_bug(fp->pc, fp); - printk(KERN_EMERG "%s: %04lx [#%d] ", str, err & 0xffff, ++diecount); - dump(fp); - - spin_unlock_irq(&die_lock); - do_exit(SIGSEGV); -} - -extern char _start, _etext; -#define check_kernel_text(addr) \ - ((addr >= (unsigned long)(&_start)) && \ - (addr < (unsigned long)(&_etext))) - -static int kstack_depth_to_print = 24; - -void show_stack(struct task_struct *task, unsigned long *esp) -{ - unsigned long *stack, addr; - int i; - - if (esp == NULL) - esp = (unsigned long *) &esp; - - stack = esp; - - printk("Stack from %08lx:", (unsigned long)stack); - for (i = 0; i < kstack_depth_to_print; i++) { - if (((unsigned long)stack & (THREAD_SIZE - 1)) == 0) - break; - if (i % 8 == 0) - printk("\n "); - printk(" %08lx", *stack++); - } - - printk("\nCall Trace:"); - i = 0; - stack = esp; - while (((unsigned long)stack & (THREAD_SIZE - 1)) != 0) { - addr = *stack++; - /* - * If the address is either in the text segment of the - * kernel, or in the region which contains vmalloc'ed - * memory, it *may* be the address of a calling - * routine; if so, print it so that someone tracing - * down the cause of the crash will be able to figure - * out the call path that was taken. - */ - if (check_kernel_text(addr)) { - if (i % 4 == 0) - printk("\n "); - printk(" [<%08lx>]", addr); - i++; - } - } - printk("\n"); -} - -void show_trace_task(struct task_struct *tsk) -{ - show_stack(tsk,(unsigned long *)tsk->thread.esp0); -} diff --git a/arch/h8300/kernel/vmlinux.lds.S b/arch/h8300/kernel/vmlinux.lds.S deleted file mode 100644 index 3253fed..0000000 --- a/arch/h8300/kernel/vmlinux.lds.S +++ /dev/null @@ -1,157 +0,0 @@ -#include -#include - -/* target memory map */ -#ifdef CONFIG_H8300H_GENERIC -#define ROMTOP 0x000000 -#define ROMSIZE 0x400000 -#define RAMTOP 0x400000 -#define RAMSIZE 0x400000 -#endif - -#ifdef CONFIG_H8300H_AKI3068NET -#define ROMTOP 0x000000 -#define ROMSIZE 0x080000 -#define RAMTOP 0x400000 -#define RAMSIZE 0x200000 -#endif - -#ifdef CONFIG_H8300H_H8MAX -#define ROMTOP 0x000000 -#define ROMSIZE 0x080000 -#define RAMTOP 0x400000 -#define RAMSIZE 0x200000 -#endif - -#ifdef CONFIG_H8300H_SIM -#define ROMTOP 0x000000 -#define ROMSIZE 0x400000 -#define RAMTOP 0x400000 -#define RAMSIZE 0x400000 -#endif - -#ifdef CONFIG_H8S_SIM -#define ROMTOP 0x000000 -#define ROMSIZE 0x400000 -#define RAMTOP 0x400000 -#define RAMSIZE 0x800000 -#endif - -#ifdef CONFIG_H8S_EDOSK2674 -#define ROMTOP 0x000000 -#define ROMSIZE 0x400000 -#define RAMTOP 0x400000 -#define RAMSIZE 0x800000 -#endif - -#if defined(CONFIG_H8300H_SIM) || defined(CONFIG_H8S_SIM) -INPUT(romfs.o) -#endif - -_jiffies = _jiffies_64 + 4; - -ENTRY(__start) - -SECTIONS -{ -#if defined(CONFIG_ROMKERNEL) - . = ROMTOP; - .vectors : - { - __vector = . ; - *(.vectors*) - } -#else - . = RAMTOP; - .bootvec : - { - *(.bootvec) - } -#endif - .text : - { - _text = .; -#if defined(CONFIG_ROMKERNEL) - *(.int_redirect) -#endif - __stext = . ; - TEXT_TEXT - SCHED_TEXT - LOCK_TEXT - __etext = . ; - } - EXCEPTION_TABLE(16) - - RODATA -#if defined(CONFIG_ROMKERNEL) - SECURITY_INIT -#endif - ROEND = .; -#if defined(CONFIG_ROMKERNEL) - . = RAMTOP; - .data : AT(ROEND) -#else - .data : -#endif - { - __sdata = . ; - ___data_start = . ; - - INIT_TASK_DATA(0x2000) - . = ALIGN(0x4) ; - DATA_DATA - . = ALIGN(0x4) ; - *(.data.*) - - . = ALIGN(0x4) ; - ___init_begin = .; - __sinittext = .; - INIT_TEXT - __einittext = .; - INIT_DATA - . = ALIGN(0x4) ; - INIT_SETUP(0x4) - ___setup_start = .; - *(.init.setup) - . = ALIGN(0x4) ; - ___setup_end = .; - INIT_CALLS - CON_INITCALL - EXIT_TEXT - EXIT_DATA - INIT_RAM_FS - . = ALIGN(0x4) ; - ___init_end = .; - __edata = . ; - } -#if defined(CONFIG_RAMKERNEL) - SECURITY_INIT -#endif - __begin_data = LOADADDR(.data); - .bss : - { - . = ALIGN(0x4) ; - __sbss = . ; - ___bss_start = . ; - *(.bss*) - . = ALIGN(0x4) ; - *(COMMON) - . = ALIGN(0x4) ; - ___bss_stop = . ; - __ebss = . ; - __end = . ; - __ramstart = .; - } - .romfs : - { - *(.romfs*) - } - . = RAMTOP+RAMSIZE; - .dummy : - { - COMMAND_START = . - 0x200 ; - __ramend = . ; - } - - DISCARDS -} diff --git a/arch/h8300/lib/Makefile b/arch/h8300/lib/Makefile deleted file mode 100644 index 1577f50..0000000 --- a/arch/h8300/lib/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# -# Makefile for H8/300-specific library files.. -# - -lib-y = ashrdi3.o checksum.o memcpy.o memset.o abs.o romfs.o diff --git a/arch/h8300/lib/abs.S b/arch/h8300/lib/abs.S deleted file mode 100644 index ddd1fb3..0000000 --- a/arch/h8300/lib/abs.S +++ /dev/null @@ -1,21 +0,0 @@ -;;; abs.S - -#include - -#if defined(__H8300H__) - .h8300h -#endif -#if defined(__H8300S__) - .h8300s -#endif - .text -.global _abs - -;;; int abs(int n) -_abs: - mov.l er0,er0 - bpl 1f - neg.l er0 -1: - rts - diff --git a/arch/h8300/lib/ashrdi3.c b/arch/h8300/lib/ashrdi3.c deleted file mode 100644 index 78efb65..0000000 --- a/arch/h8300/lib/ashrdi3.c +++ /dev/null @@ -1,63 +0,0 @@ -/* ashrdi3.c extracted from gcc-2.7.2/libgcc2.c which is: */ -/* Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ - -#define BITS_PER_UNIT 8 - -typedef int SItype __attribute__ ((mode (SI))); -typedef unsigned int USItype __attribute__ ((mode (SI))); -typedef int DItype __attribute__ ((mode (DI))); -typedef int word_type __attribute__ ((mode (__word__))); - -struct DIstruct {SItype high, low;}; - -typedef union -{ - struct DIstruct s; - DItype ll; -} DIunion; - -DItype -__ashrdi3 (DItype u, word_type b) -{ - DIunion w; - word_type bm; - DIunion uu; - - if (b == 0) - return u; - - uu.ll = u; - - bm = (sizeof (SItype) * BITS_PER_UNIT) - b; - if (bm <= 0) - { - /* w.s.high = 1..1 or 0..0 */ - w.s.high = uu.s.high >> (sizeof (SItype) * BITS_PER_UNIT - 1); - w.s.low = uu.s.high >> -bm; - } - else - { - USItype carries = (USItype)uu.s.high << bm; - w.s.high = uu.s.high >> b; - w.s.low = ((USItype)uu.s.low >> b) | carries; - } - - return w.ll; -} diff --git a/arch/h8300/lib/checksum.c b/arch/h8300/lib/checksum.c deleted file mode 100644 index bdc5b03..0000000 --- a/arch/h8300/lib/checksum.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - * INET An implementation of the TCP/IP protocol suite for the LINUX - * operating system. INET is implemented using the BSD Socket - * interface as the means of communication with the user level. - * - * IP/TCP/UDP checksumming routines - * - * Authors: Jorge Cwik, - * Arnt Gulbrandsen, - * Tom May, - * Andreas Schwab, - * Lots of code moved from tcp.c and ip.c; see those files - * for more names. - * - * 03/02/96 Jes Sorensen, Andreas Schwab, Roman Hodek: - * Fixed some nasty bugs, causing some horrible crashes. - * A: At some points, the sum (%0) was used as - * length-counter instead of the length counter - * (%1). Thanks to Roman Hodek for pointing this out. - * B: GCC seems to mess up if one uses too many - * data-registers to hold input values and one tries to - * specify d0 and d1 as scratch registers. Letting gcc choose these - * registers itself solves the problem. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - -/* Revised by Kenneth Albanowski for m68knommu. Basic problem: unaligned access kills, so most - of the assembly has to go. */ - -#include -#include - -static inline unsigned short from32to16(unsigned long x) -{ - /* add up 16-bit and 16-bit for 16+c bit */ - x = (x & 0xffff) + (x >> 16); - /* add up carry.. */ - x = (x & 0xffff) + (x >> 16); - return x; -} - -static unsigned long do_csum(const unsigned char * buff, int len) -{ - int odd, count; - unsigned long result = 0; - - if (len <= 0) - goto out; - odd = 1 & (unsigned long) buff; - if (odd) { - result = *buff; - len--; - buff++; - } - count = len >> 1; /* nr of 16-bit words.. */ - if (count) { - if (2 & (unsigned long) buff) { - result += *(unsigned short *) buff; - count--; - len -= 2; - buff += 2; - } - count >>= 1; /* nr of 32-bit words.. */ - if (count) { - unsigned long carry = 0; - do { - unsigned long w = *(unsigned long *) buff; - count--; - buff += 4; - result += carry; - result += w; - carry = (w > result); - } while (count); - result += carry; - result = (result & 0xffff) + (result >> 16); - } - if (len & 2) { - result += *(unsigned short *) buff; - buff += 2; - } - } - if (len & 1) - result += (*buff << 8); - result = from32to16(result); - if (odd) - result = ((result >> 8) & 0xff) | ((result & 0xff) << 8); -out: - return result; -} - -/* - * This is a version of ip_compute_csum() optimized for IP headers, - * which always checksum on 4 octet boundaries. - */ -__sum16 ip_fast_csum(const void *iph, unsigned int ihl) -{ - return (__force __sum16)~do_csum(iph,ihl*4); -} - -/* - * computes the checksum of a memory block at buff, length len, - * and adds in "sum" (32-bit) - * - * returns a 32-bit number suitable for feeding into itself - * or csum_tcpudp_magic - * - * this function must be called with even lengths, except - * for the last fragment, which may be odd - * - * it's best to have buff aligned on a 32-bit boundary - */ -/* - * Egads... That thing apparently assumes that *all* checksums it ever sees will - * be folded. Very likely a bug. - */ -__wsum csum_partial(const void *buff, int len, __wsum sum) -{ - unsigned int result = do_csum(buff, len); - - /* add in old sum, and carry.. */ - result += (__force u32)sum; - /* 16+c bits -> 16 bits */ - result = (result & 0xffff) + (result >> 16); - return (__force __wsum)result; -} - -EXPORT_SYMBOL(csum_partial); - -/* - * this routine is used for miscellaneous IP-like checksums, mainly - * in icmp.c - */ -__sum16 ip_compute_csum(const void *buff, int len) -{ - return (__force __sum16)~do_csum(buff,len); -} - -/* - * copy from fs while checksumming, otherwise like csum_partial - */ - -__wsum -csum_partial_copy_from_user(const void __user *src, void *dst, int len, - __wsum sum, int *csum_err) -{ - if (csum_err) *csum_err = 0; - memcpy(dst, (__force const void *)src, len); - return csum_partial(dst, len, sum); -} - -/* - * copy from ds while checksumming, otherwise like csum_partial - */ - -__wsum -csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum) -{ - memcpy(dst, src, len); - return csum_partial(dst, len, sum); -} diff --git a/arch/h8300/lib/memcpy.S b/arch/h8300/lib/memcpy.S deleted file mode 100644 index cad325e..0000000 --- a/arch/h8300/lib/memcpy.S +++ /dev/null @@ -1,84 +0,0 @@ -;;; memcpy.S - -#include - -#if defined(__H8300H__) - .h8300h -#endif -#if defined(__H8300S__) - .h8300s -#endif - - .text -.global _memcpy - -;;; void *memcpy(void *to, void *from, size_t n) -_memcpy: - mov.l er2,er2 - bne 1f - rts -1: - ;; address check - bld #0,r0l - bxor #0,r1l - bcs 4f - mov.l er4,@-sp - mov.l er0,@-sp - btst #0,r0l - beq 1f - ;; (aligned even) odd address - mov.b @er1,r3l - mov.b r3l,@er0 - adds #1,er1 - adds #1,er0 - dec.l #1,er2 - beq 3f -1: - ;; n < sizeof(unsigned long) check - sub.l er4,er4 - adds #4,er4 ; loop count check value - cmp.l er4,er2 - blo 2f - ;; unsigned long copy -1: - mov.l @er1,er3 - mov.l er3,@er0 - adds #4,er0 - adds #4,er1 - subs #4,er2 - cmp.l er4,er2 - bcc 1b - ;; rest -2: - mov.l er2,er2 - beq 3f -1: - mov.b @er1,r3l - mov.b r3l,@er0 - adds #1,er1 - adds #1,er0 - dec.l #1,er2 - bne 1b -3: - mov.l @sp+,er0 - mov.l @sp+,er4 - rts - - ;; odd <- even / even <- odd -4: - mov.l er4,er3 - mov.l er2,er4 - mov.l er5,er2 - mov.l er1,er5 - mov.l er6,er1 - mov.l er0,er6 -1: - eepmov.w - mov.w r4,r4 - bne 1b - dec.w #1,e4 - bpl 1b - mov.l er1,er6 - mov.l er2,er5 - mov.l er3,er4 - rts diff --git a/arch/h8300/lib/memset.S b/arch/h8300/lib/memset.S deleted file mode 100644 index 4549a64..0000000 --- a/arch/h8300/lib/memset.S +++ /dev/null @@ -1,61 +0,0 @@ -/* memset.S */ - -#include - -#if defined(__H8300H__) - .h8300h -#endif -#if defined(__H8300S__) - .h8300s -#endif - .text - -.global _memset - -;;void *memset(*ptr, int c, size_t count) -;; ptr = er0 -;; c = er1(r1l) -;; count = er2 -_memset: - btst #0,r0l - beq 2f - - ;; odd address -1: - mov.b r1l,@er0 - adds #1,er0 - dec.l #1,er2 - beq 6f - - ;; even address -2: - mov.l er2,er3 - cmp.l #4,er2 - blo 4f - ;; count>=4 -> count/4 -#if defined(__H8300H__) - shlr.l er2 - shlr.l er2 -#endif -#if defined(__H8300S__) - shlr.l #2,er2 -#endif - ;; byte -> long - mov.b r1l,r1h - mov.w r1,e1 -3: - mov.l er1,@er0 - adds #4,er0 - dec.l #1,er2 - bne 3b -4: - ;; count % 4 - and.b #3,r3l - beq 6f -5: - mov.b r1l,@er0 - adds #1,er0 - dec.b r3l - bne 5b -6: - rts diff --git a/arch/h8300/lib/romfs.S b/arch/h8300/lib/romfs.S deleted file mode 100644 index 68910d8..0000000 --- a/arch/h8300/lib/romfs.S +++ /dev/null @@ -1,57 +0,0 @@ -/* romfs move to __ebss */ - -#include - -#if defined(__H8300H__) - .h8300h -#endif -#if defined(__H8300S__) - .h8300s -#endif - -#define BLKOFFSET 512 - - .text -.globl __move_romfs -_romfs_sig_len = 8 - -__move_romfs: - mov.l #__sbss,er0 - mov.l #_romfs_sig,er1 - mov.b #_romfs_sig_len,r3l -1: /* check romfs image */ - mov.b @er0+,r2l - mov.b @er1+,r2h - cmp.b r2l,r2h - bne 2f - dec.b r3l - bne 1b - - /* find romfs image */ - mov.l @__sbss+8,er0 /* romfs length(be) */ - mov.l #__sbss,er1 - add.l er0,er1 /* romfs image end */ - mov.l #__ebss,er2 - add.l er0,er2 /* distination address */ -#if defined(CONFIG_INTELFLASH) - add.l #BLKOFFSET,er2 -#endif - adds #2,er0 - adds #1,er0 - shlr er0 - shlr er0 /* transfer length */ -1: - mov.l @er1,er3 /* copy image */ - mov.l er3,@er2 - subs #4,er1 - subs #4,er2 - dec.l #1,er0 - bpl 1b -2: - rts - - .section .rodata -_romfs_sig: - .ascii "-rom1fs-" - - .end diff --git a/arch/h8300/mm/Makefile b/arch/h8300/mm/Makefile deleted file mode 100644 index 5f4bc42..0000000 --- a/arch/h8300/mm/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# -# Makefile for the linux m68k-specific parts of the memory manager. -# - -obj-y := init.o fault.o memory.o kmap.o diff --git a/arch/h8300/mm/fault.c b/arch/h8300/mm/fault.c deleted file mode 100644 index 4725359..0000000 --- a/arch/h8300/mm/fault.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * linux/arch/h8300/mm/fault.c - * - * Copyright (C) 1998 D. Jeff Dionne , - * Copyright (C) 2000 Lineo, Inc. (www.lineo.com) - * - * Based on: - * - * linux/arch/m68knommu/mm/fault.c - * linux/arch/m68k/mm/fault.c - * - * Copyright (C) 1995 Hamish Macdonald - */ - -#include -#include -#include -#include - -#include - -/* - * This routine handles page faults. It determines the problem, and - * then passes it off to one of the appropriate routines. - * - * error_code: - * bit 0 == 0 means no page found, 1 means protection fault - * bit 1 == 0 means read, 1 means write - * - * If this routine detects a bad access, it returns 1, otherwise it - * returns 0. - */ -asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address, - unsigned long error_code) -{ -#ifdef DEBUG - printk ("regs->sr=%#x, regs->pc=%#lx, address=%#lx, %ld\n", - regs->sr, regs->pc, address, error_code); -#endif - -/* - * Oops. The kernel tried to access some bad page. We'll have to - * terminate things with extreme prejudice. - */ - if ((unsigned long) address < PAGE_SIZE) { - printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); - } else - printk(KERN_ALERT "Unable to handle kernel access"); - printk(" at virtual address %08lx\n",address); - if (!user_mode(regs)) - die("Oops", regs, error_code); - do_exit(SIGKILL); - - return 1; -} - diff --git a/arch/h8300/mm/init.c b/arch/h8300/mm/init.c deleted file mode 100644 index 6c1251e..0000000 --- a/arch/h8300/mm/init.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - * linux/arch/h8300/mm/init.c - * - * Copyright (C) 1998 D. Jeff Dionne , - * Kenneth Albanowski , - * Copyright (C) 2000 Lineo, Inc. (www.lineo.com) - * - * Based on: - * - * linux/arch/m68knommu/mm/init.c - * linux/arch/m68k/mm/init.c - * - * Copyright (C) 1995 Hamish Macdonald - * - * JAN/1999 -- hacked to support ColdFire (gerg@snapgear.com) - * DEC/2000 -- linux 2.4 support - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#undef DEBUG - -/* - * BAD_PAGE is the page that is used for page faults when linux - * is out-of-memory. Older versions of linux just did a - * do_exit(), but using this instead means there is less risk - * for a process dying in kernel mode, possibly leaving a inode - * unused etc.. - * - * BAD_PAGETABLE is the accompanying page-table: it is initialized - * to point to BAD_PAGE entries. - * - * ZERO_PAGE is a special page that is used for zero-initialized - * data and COW. - */ -static unsigned long empty_bad_page_table; - -static unsigned long empty_bad_page; - -unsigned long empty_zero_page; - -extern unsigned long rom_length; - -extern unsigned long memory_start; -extern unsigned long memory_end; - -/* - * paging_init() continues the virtual memory environment setup which - * was begun by the code in arch/head.S. - * The parameters are pointers to where to stick the starting and ending - * addresses of available kernel virtual memory. - */ -void __init paging_init(void) -{ - /* - * Make sure start_mem is page aligned, otherwise bootmem and - * page_alloc get different views og the world. - */ -#ifdef DEBUG - unsigned long start_mem = PAGE_ALIGN(memory_start); -#endif - unsigned long end_mem = memory_end & PAGE_MASK; - -#ifdef DEBUG - printk ("start_mem is %#lx\nvirtual_end is %#lx\n", - start_mem, end_mem); -#endif - - /* - * Initialize the bad page table and bad page to point - * to a couple of allocated pages. - */ - empty_bad_page_table = (unsigned long)alloc_bootmem_pages(PAGE_SIZE); - empty_bad_page = (unsigned long)alloc_bootmem_pages(PAGE_SIZE); - empty_zero_page = (unsigned long)alloc_bootmem_pages(PAGE_SIZE); - memset((void *)empty_zero_page, 0, PAGE_SIZE); - - /* - * Set up SFC/DFC registers (user data space). - */ - set_fs (USER_DS); - -#ifdef DEBUG - printk ("before free_area_init\n"); - - printk ("free_area_init -> start_mem is %#lx\nvirtual_end is %#lx\n", - start_mem, end_mem); -#endif - - { - unsigned long zones_size[MAX_NR_ZONES] = {0, }; - - zones_size[ZONE_DMA] = 0 >> PAGE_SHIFT; - zones_size[ZONE_NORMAL] = (end_mem - PAGE_OFFSET) >> PAGE_SHIFT; -#ifdef CONFIG_HIGHMEM - zones_size[ZONE_HIGHMEM] = 0; -#endif - free_area_init(zones_size); - } -} - -void __init mem_init(void) -{ - unsigned long codesize = _etext - _stext; - - pr_devel("Mem_init: start=%lx, end=%lx\n", memory_start, memory_end); - - high_memory = (void *) (memory_end & PAGE_MASK); - max_mapnr = MAP_NR(high_memory); - - /* this will put all low memory onto the freelists */ - free_all_bootmem(); - - mem_init_print_info(NULL); - if (rom_length > 0 && rom_length > codesize) - pr_info("Memory available: %luK/%luK ROM\n", - (rom_length - codesize) >> 10, rom_length >> 10); -} - - -#ifdef CONFIG_BLK_DEV_INITRD -void free_initrd_mem(unsigned long start, unsigned long end) -{ - free_reserved_area((void *)start, (void *)end, -1, "initrd"); -} -#endif - -void -free_initmem(void) -{ -#ifdef CONFIG_RAMKERNEL - free_initmem_default(-1); -#endif -} - diff --git a/arch/h8300/mm/kmap.c b/arch/h8300/mm/kmap.c deleted file mode 100644 index f79edcd..0000000 --- a/arch/h8300/mm/kmap.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * linux/arch/h8300/mm/kmap.c - * - * Based on - * linux/arch/m68knommu/mm/kmap.c - * - * Copyright (C) 2000 Lineo, - * Copyright (C) 2000-2002 David McCullough - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#undef DEBUG - -#define VIRT_OFFSET (0x01000000) - -/* - * Map some physical address range into the kernel address space. - */ -void *__ioremap(unsigned long physaddr, unsigned long size, int cacheflag) -{ - return (void *)(physaddr + VIRT_OFFSET); -} - -/* - * Unmap a ioremap()ed region again. - */ -void iounmap(void *addr) -{ -} - -/* - * __iounmap unmaps nearly everything, so be careful - * it doesn't free currently pointer/page tables anymore but it - * wans't used anyway and might be added later. - */ -void __iounmap(void *addr, unsigned long size) -{ -} - -/* - * Set new cache mode for some kernel address space. - * The caller must push data for that range itself, if such data may already - * be in the cache. - */ -void kernel_set_cachemode(void *addr, unsigned long size, int cmode) -{ -} diff --git a/arch/h8300/mm/memory.c b/arch/h8300/mm/memory.c deleted file mode 100644 index 06e3646..0000000 --- a/arch/h8300/mm/memory.c +++ /dev/null @@ -1,54 +0,0 @@ -/* - * linux/arch/h8300/mm/memory.c - * - * Copyright (C) 2002 Yoshinori Sato , - * - * Based on: - * - * linux/arch/m68knommu/mm/memory.c - * - * Copyright (C) 1998 Kenneth Albanowski , - * Copyright (C) 1999-2002, Greg Ungerer (gerg@snapgear.com) - * - * Based on: - * - * linux/arch/m68k/mm/memory.c - * - * Copyright (C) 1995 Hamish Macdonald - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -void cache_clear (unsigned long paddr, int len) -{ -} - - -void cache_push (unsigned long paddr, int len) -{ -} - -void cache_push_v (unsigned long vaddr, int len) -{ -} - -/* - * Map some physical address range into the kernel address space. - */ - -unsigned long kernel_map(unsigned long paddr, unsigned long size, - int nocacheflag, unsigned long *memavailp ) -{ - return paddr; -} - diff --git a/arch/h8300/platform/h8300h/Makefile b/arch/h8300/platform/h8300h/Makefile deleted file mode 100644 index 420f73b..0000000 --- a/arch/h8300/platform/h8300h/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# Makefile for the linux kernel. -# -# Reuse any files we can from the H8/300H -# - -obj-y := irq.o ptrace_h8300h.o diff --git a/arch/h8300/platform/h8300h/aki3068net/Makefile b/arch/h8300/platform/h8300h/aki3068net/Makefile deleted file mode 100644 index b7ff780..0000000 --- a/arch/h8300/platform/h8300h/aki3068net/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# -# Makefile for the linux kernel. -# - -extra-y := crt0_ram.o diff --git a/arch/h8300/platform/h8300h/aki3068net/crt0_ram.S b/arch/h8300/platform/h8300h/aki3068net/crt0_ram.S deleted file mode 100644 index b2ad0f2..0000000 --- a/arch/h8300/platform/h8300h/aki3068net/crt0_ram.S +++ /dev/null @@ -1,110 +0,0 @@ -/* - * linux/arch/h8300/platform/h8300h/aki3068net/crt0_ram.S - * - * Yoshinori Sato - * - * Platform depend startup - * Target Archtecture: AE-3068 (aka. aki3068net) - * Memory Layout : RAM - */ - -#define ASSEMBLY - -#include - -#if !defined(CONFIG_BLKDEV_RESERVE) -#if defined(CONFIG_GDB_DEBUG) -#define RAMEND (__ramend - 0xc000) -#else -#define RAMEND __ramend -#endif -#else -#define RAMEND CONFIG_BLKDEV_RESERVE_ADDRESS -#endif - - .global __start - .global _command_line - .global __platform_gpio_table - .global __target_name - - .h8300h - - .section .text - .file "crt0_ram.S" - - /* CPU Reset entry */ -__start: - mov.l #RAMEND,sp - ldc #0x80,ccr - - /* Peripheral Setup */ - -#if defined(CONFIG_MTD_UCLINUX) - /* move romfs image */ - jsr @__move_romfs -#endif - - /* .bss clear */ - mov.l #__sbss,er5 - mov.l #__ebss,er4 - sub.l er5,er4 - shlr er4 - shlr er4 - sub.l er0,er0 -1: - mov.l er0,@er5 - adds #4,er5 - dec.l #1,er4 - bne 1b - - /* copy kernel commandline */ - mov.l #COMMAND_START,er5 - mov.l #_command_line,er6 - mov.w #512,r4 - eepmov.w - - /* uClinux kernel start */ - ldc #0x90,ccr /* running kernel */ - mov.l #_init_thread_union,sp - add.l #0x2000,sp - jsr @_start_kernel -_exit: - - jmp _exit - - rts - - /* I/O port assign information */ -__platform_gpio_table: - mov.l #gpio_table,er0 - rts - -gpio_table: - ;; P1DDR - .byte 0xff,0xff - ;; P2DDR - .byte 0xff,0xff - ;; P3DDR - .byte 0xff,0x00 - ;; P4DDR - .byte 0x00,0x00 - ;; P5DDR - .byte 0x01,0x01 - ;; P6DDR - .byte 0x00,0x00 - ;; dummy - .byte 0x00,0x00 - ;; P8DDR - .byte 0x0c,0x0c - ;; P9DDR - .byte 0x00,0x00 - ;; PADDR - .byte 0x00,0x00 - ;; PBDDR - .byte 0x30,0x30 - -__target_name: - .asciz "AE-3068" - - .section .bootvec,"ax" - jmp @__start diff --git a/arch/h8300/platform/h8300h/generic/Makefile b/arch/h8300/platform/h8300h/generic/Makefile deleted file mode 100644 index 2b12a17..0000000 --- a/arch/h8300/platform/h8300h/generic/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# -# Makefile for the linux kernel. -# - -extra-y := crt0_$(MODEL).o diff --git a/arch/h8300/platform/h8300h/generic/crt0_ram.S b/arch/h8300/platform/h8300h/generic/crt0_ram.S deleted file mode 100644 index 5ab7d9c..0000000 --- a/arch/h8300/platform/h8300h/generic/crt0_ram.S +++ /dev/null @@ -1,107 +0,0 @@ -/* - * linux/arch/h8300/platform/h8300h/generic/crt0_ram.S - * - * Yoshinori Sato - * - * Platform depend startup - * Target Archtecture: AE-3068 (aka. aki3068net) - * Memory Layout : RAM - */ - -#define ASSEMBLY - -#include - -#if !defined(CONFIG_BLKDEV_RESERVE) -#if defined(CONFIG_GDB_DEBUG) -#define RAMEND (__ramend - 0xc000) -#else -#define RAMEND __ramend -#endif -#else -#define RAMEND CONFIG_BLKDEV_RESERVE_ADDRESS -#endif - - .global __start - .global _command_line - .global __platform_gpio_table - .global __target_name - - .h8300h - - .section .text - .file "crt0_ram.S" - - /* CPU Reset entry */ -__start: - mov.l #RAMEND,sp - ldc #0x80,ccr - - /* Peripheral Setup */ - -#if defined(CONFIG_BLK_DEV_BLKMEM) - /* move romfs image */ - jsr @__move_romfs -#endif - - /* .bss clear */ - mov.l #__sbss,er5 - mov.l #__ebss,er4 - sub.l er5,er4 - shlr er4 - shlr er4 - sub.l er0,er0 -1: - mov.l er0,@er5 - adds #4,er5 - dec.l #1,er4 - bne 1b - - /* copy kernel commandline */ - mov.l #COMMAND_START,er5 - mov.l #_command_line,er6 - mov.w #512,r4 - eepmov.w - - /* uClinux kernel start */ - ldc #0x90,ccr /* running kernel */ - mov.l #_init_thread_union,sp - add.l #0x2000,sp - jsr @_start_kernel -_exit: - - jmp _exit - - rts - - /* I/O port assign information */ -__platform_gpio_table: - mov.l #gpio_table,er0 - rts - -gpio_table: - ;; P1DDR - .byte 0x00,0x00 - ;; P2DDR - .byte 0x00,0x00 - ;; P3DDR - .byte 0x00,0x00 - ;; P4DDR - .byte 0x00,0x00 - ;; P5DDR - .byte 0x00,0x00 - ;; P6DDR - .byte 0x00,0x00 - ;; dummy - .byte 0x00,0x00 - ;; P8DDR - .byte 0x00,0x00 - ;; P9DDR - .byte 0x00,0x00 - ;; PADDR - .byte 0x00,0x00 - ;; PBDDR - .byte 0x00,0x00 - -__target_name: - .asciz "generic" diff --git a/arch/h8300/platform/h8300h/generic/crt0_rom.S b/arch/h8300/platform/h8300h/generic/crt0_rom.S deleted file mode 100644 index dda1dfa..0000000 --- a/arch/h8300/platform/h8300h/generic/crt0_rom.S +++ /dev/null @@ -1,122 +0,0 @@ -/* - * linux/arch/h8300/platform/h8300h/generic/crt0_rom.S - * - * Yoshinori Sato - * - * Platform depend startup - * Target Archtecture: generic - * Memory Layout : ROM - */ - -#define ASSEMBLY - -#include - - .global __start - .global __command_line - .global __platform_gpio_table - .global __target_name - - .h8300h - .section .text - .file "crt0_rom.S" - - /* CPU Reset entry */ -__start: - mov.l #__ramend,sp - ldc #0x80,ccr - - /* Peripheral Setup */ - - /* .bss clear */ - mov.l #__sbss,er5 - mov.l #__ebss,er4 - sub.l er5,er4 - shlr er4 - shlr er4 - sub.l er0,er0 -1: - mov.l er0,@er5 - adds #4,er5 - dec.l #1,er4 - bne 1b - - /* copy .data */ -#if !defined(CONFIG_H8300H_SIM) - /* copy .data */ - mov.l #__begin_data,er5 - mov.l #__sdata,er6 - mov.l #__edata,er4 - sub.l er6,er4 - shlr.l er4 - shlr.l er4 -1: - mov.l @er5+,er0 - mov.l er0,@er6 - adds #4,er6 - dec.l #1,er4 - bne 1b -#endif - - /* copy kernel commandline */ - mov.l #COMMAND_START,er5 - mov.l #__command_line,er6 - mov.w #512,r4 - eepmov.w - - /* linux kernel start */ - ldc #0x90,ccr /* running kernel */ - mov.l #_init_thread_union,sp - add.l #0x2000,sp - jsr @_start_kernel -_exit: - - jmp _exit - - rts - - /* I/O port assign information */ -__platform_gpio_table: - mov.l #gpio_table,er0 - rts - -gpio_table: - ;; P1DDR - .byte 0x00,0x00 - ;; P2DDR - .byte 0x00,0x00 - ;; P3DDR - .byte 0x00,0x00 - ;; P4DDR - .byte 0x00,0x00 - ;; P5DDR - .byte 0x00,0x00 - ;; P6DDR - .byte 0x00,0x00 - ;; dummy - .byte 0x00,0x00 - ;; P8DDR - .byte 0x00,0x00 - ;; P9DDR - .byte 0x00,0x00 - ;; PADDR - .byte 0x00,0x00 - ;; PBDDR - .byte 0x00,0x00 - - .section .rodata -__target_name: - .asciz "generic" - - .section .bss -__command_line: - .space 512 - - /* interrupt vector */ - .section .vectors,"ax" - .long __start -vector = 1 - .rept 64-1 - .long _interrupt_redirect_table+vector*4 -vector = vector + 1 - .endr diff --git a/arch/h8300/platform/h8300h/h8max/Makefile b/arch/h8300/platform/h8300h/h8max/Makefile deleted file mode 100644 index b7ff780..0000000 --- a/arch/h8300/platform/h8300h/h8max/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# -# Makefile for the linux kernel. -# - -extra-y := crt0_ram.o diff --git a/arch/h8300/platform/h8300h/h8max/crt0_ram.S b/arch/h8300/platform/h8300h/h8max/crt0_ram.S deleted file mode 100644 index 6a0d4e2..0000000 --- a/arch/h8300/platform/h8300h/h8max/crt0_ram.S +++ /dev/null @@ -1,110 +0,0 @@ -/* - * linux/arch/h8300/platform/h8300h/h8max/crt0_ram.S - * - * Yoshinori Sato - * - * Platform depend startup - * Target Archtecture: H8MAX - * Memory Layout : RAM - */ - -#define ASSEMBLY - -#include - -#if !defined(CONFIG_BLKDEV_RESERVE) -#if defined(CONFIG_GDB_DEBUG) -#define RAMEND (__ramend - 0xc000) -#else -#define RAMEND __ramend -#endif -#else -#define RAMEND CONFIG_BLKDEV_RESERVE_ADDRESS -#endif - - .global __start - .global _command_line - .global __platform_gpio_table - .global __target_name - - .h8300h - - .section .text - .file "crt0_ram.S" - - /* CPU Reset entry */ -__start: - mov.l #RAMEND,sp - ldc #0x80,ccr - - /* Peripheral Setup */ - -#if defined(CONFIG_MTD_UCLINUX) - /* move romfs image */ - jsr @__move_romfs -#endif - - /* .bss clear */ - mov.l #__sbss,er5 - mov.l #__ebss,er4 - sub.l er5,er4 - shlr er4 - shlr er4 - sub.l er0,er0 -1: - mov.l er0,@er5 - adds #4,er5 - dec.l #1,er4 - bne 1b - - /* copy kernel commandline */ - mov.l #COMMAND_START,er5 - mov.l #_command_line,er6 - mov.w #512,r4 - eepmov.w - - /* uClinux kernel start */ - ldc #0x90,ccr /* running kernel */ - mov.l #_init_thread_union,sp - add.l #0x2000,sp - jsr @_start_kernel -_exit: - - jmp _exit - - rts - - /* I/O port assign information */ -__platform_gpio_table: - mov.l #gpio_table,er0 - rts - -gpio_table: - ;; P1DDR - .byte 0xff,0xff - ;; P2DDR - .byte 0xff,0xff - ;; P3DDR - .byte 0x00,0x00 - ;; P4DDR - .byte 0x00,0x00 - ;; P5DDR - .byte 0x01,0x01 - ;; P6DDR - .byte 0xf6,0xf6 - ;; dummy - .byte 0x00,0x00 - ;; P8DDR - .byte 0xee,0xee - ;; P9DDR - .byte 0x00,0x00 - ;; PADDR - .byte 0x00,0x00 - ;; PBDDR - .byte 0x30,0x30 - -__target_name: - .asciz "H8MAX" - - .section .bootvec,"ax" - jmp @__start diff --git a/arch/h8300/platform/h8300h/irq.c b/arch/h8300/platform/h8300h/irq.c deleted file mode 100644 index 0a50353..0000000 --- a/arch/h8300/platform/h8300h/irq.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Interrupt handling H8/300H depend. - * Yoshinori Sato - * - */ - -#include -#include - -#include -#include -#include -#include -#include -#include - -const int __initconst h8300_saved_vectors[] = { -#if defined(CONFIG_GDB_DEBUG) - TRAP3_VEC, /* TRAPA #3 is GDB breakpoint */ -#endif - -1, -}; - -const h8300_vector __initconst h8300_trap_table[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - system_call, - 0, - 0, - trace_break, -}; - -int h8300_enable_irq_pin(unsigned int irq) -{ - int bitmask; - if (irq < EXT_IRQ0 || irq > EXT_IRQ5) - return 0; - - /* initialize IRQ pin */ - bitmask = 1 << (irq - EXT_IRQ0); - switch(irq) { - case EXT_IRQ0: - case EXT_IRQ1: - case EXT_IRQ2: - case EXT_IRQ3: - if (H8300_GPIO_RESERVE(H8300_GPIO_P8, bitmask) == 0) - return -EBUSY; - H8300_GPIO_DDR(H8300_GPIO_P8, bitmask, H8300_GPIO_INPUT); - break; - case EXT_IRQ4: - case EXT_IRQ5: - if (H8300_GPIO_RESERVE(H8300_GPIO_P9, bitmask) == 0) - return -EBUSY; - H8300_GPIO_DDR(H8300_GPIO_P9, bitmask, H8300_GPIO_INPUT); - break; - } - - return 0; -} - -void h8300_disable_irq_pin(unsigned int irq) -{ - int bitmask; - if (irq < EXT_IRQ0 || irq > EXT_IRQ5) - return; - - /* disable interrupt & release IRQ pin */ - bitmask = 1 << (irq - EXT_IRQ0); - switch(irq) { - case EXT_IRQ0: - case EXT_IRQ1: - case EXT_IRQ2: - case EXT_IRQ3: - *(volatile unsigned char *)IER &= ~bitmask; - H8300_GPIO_FREE(H8300_GPIO_P8, bitmask); - break ; - case EXT_IRQ4: - case EXT_IRQ5: - *(volatile unsigned char *)IER &= ~bitmask; - H8300_GPIO_FREE(H8300_GPIO_P9, bitmask); - break; - } -} diff --git a/arch/h8300/platform/h8300h/ptrace_h8300h.c b/arch/h8300/platform/h8300h/ptrace_h8300h.c deleted file mode 100644 index 4f1ed02..0000000 --- a/arch/h8300/platform/h8300h/ptrace_h8300h.c +++ /dev/null @@ -1,284 +0,0 @@ -/* - * linux/arch/h8300/platform/h8300h/ptrace_h8300h.c - * ptrace cpu depend helper functions - * - * Yoshinori Sato - * - * 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 - -#define CCR_MASK 0x6f /* mode/imask not set */ -#define BREAKINST 0x5730 /* trapa #3 */ - -/* Mapping from PT_xxx to the stack offset at which the register is - saved. Notice that usp has no stack-slot and needs to be treated - specially (see get_reg/put_reg below). */ -static const int h8300_register_offset[] = { - PT_REG(er1), PT_REG(er2), PT_REG(er3), PT_REG(er4), - PT_REG(er5), PT_REG(er6), PT_REG(er0), PT_REG(orig_er0), - PT_REG(ccr), PT_REG(pc) -}; - -/* read register */ -long h8300_get_reg(struct task_struct *task, int regno) -{ - switch (regno) { - case PT_USP: - return task->thread.usp + sizeof(long)*2; - case PT_CCR: - return *(unsigned short *)(task->thread.esp0 + h8300_register_offset[regno]); - default: - return *(unsigned long *)(task->thread.esp0 + h8300_register_offset[regno]); - } -} - -/* write register */ -int h8300_put_reg(struct task_struct *task, int regno, unsigned long data) -{ - unsigned short oldccr; - switch (regno) { - case PT_USP: - task->thread.usp = data - sizeof(long)*2; - case PT_CCR: - oldccr = *(unsigned short *)(task->thread.esp0 + h8300_register_offset[regno]); - oldccr &= ~CCR_MASK; - data &= CCR_MASK; - data |= oldccr; - *(unsigned short *)(task->thread.esp0 + h8300_register_offset[regno]) = data; - break; - default: - *(unsigned long *)(task->thread.esp0 + h8300_register_offset[regno]) = data; - break; - } - return 0; -} - -/* disable singlestep */ -void user_disable_single_step(struct task_struct *child) -{ - if((long)child->thread.breakinfo.addr != -1L) { - *child->thread.breakinfo.addr = child->thread.breakinfo.inst; - child->thread.breakinfo.addr = (unsigned short *)-1L; - } -} - -/* calculate next pc */ -enum jump_type {none, /* normal instruction */ - jabs, /* absolute address jump */ - ind, /* indirect address jump */ - ret, /* return to subrutine */ - reg, /* register indexed jump */ - relb, /* pc relative jump (byte offset) */ - relw, /* pc relative jump (word offset) */ - }; - -/* opcode decode table define - ptn: opcode pattern - msk: opcode bitmask - len: instruction length (<0 next table index) - jmp: jump operation mode */ -struct optable { - unsigned char bitpattern; - unsigned char bitmask; - signed char length; - signed char type; -} __attribute__((aligned(1),packed)); - -#define OPTABLE(ptn,msk,len,jmp) \ - { \ - .bitpattern = ptn, \ - .bitmask = msk, \ - .length = len, \ - .type = jmp, \ - } - -static const struct optable optable_0[] = { - OPTABLE(0x00,0xff, 1,none), /* 0x00 */ - OPTABLE(0x01,0xff,-1,none), /* 0x01 */ - OPTABLE(0x02,0xfe, 1,none), /* 0x02-0x03 */ - OPTABLE(0x04,0xee, 1,none), /* 0x04-0x05/0x14-0x15 */ - OPTABLE(0x06,0xfe, 1,none), /* 0x06-0x07 */ - OPTABLE(0x08,0xea, 1,none), /* 0x08-0x09/0x0c-0x0d/0x18-0x19/0x1c-0x1d */ - OPTABLE(0x0a,0xee, 1,none), /* 0x0a-0x0b/0x1a-0x1b */ - OPTABLE(0x0e,0xee, 1,none), /* 0x0e-0x0f/0x1e-0x1f */ - OPTABLE(0x10,0xfc, 1,none), /* 0x10-0x13 */ - OPTABLE(0x16,0xfe, 1,none), /* 0x16-0x17 */ - OPTABLE(0x20,0xe0, 1,none), /* 0x20-0x3f */ - OPTABLE(0x40,0xf0, 1,relb), /* 0x40-0x4f */ - OPTABLE(0x50,0xfc, 1,none), /* 0x50-0x53 */ - OPTABLE(0x54,0xfd, 1,ret ), /* 0x54/0x56 */ - OPTABLE(0x55,0xff, 1,relb), /* 0x55 */ - OPTABLE(0x57,0xff, 1,none), /* 0x57 */ - OPTABLE(0x58,0xfb, 2,relw), /* 0x58/0x5c */ - OPTABLE(0x59,0xfb, 1,reg ), /* 0x59/0x5b */ - OPTABLE(0x5a,0xfb, 2,jabs), /* 0x5a/0x5e */ - OPTABLE(0x5b,0xfb, 2,ind ), /* 0x5b/0x5f */ - OPTABLE(0x60,0xe8, 1,none), /* 0x60-0x67/0x70-0x77 */ - OPTABLE(0x68,0xfa, 1,none), /* 0x68-0x69/0x6c-0x6d */ - OPTABLE(0x6a,0xfe,-2,none), /* 0x6a-0x6b */ - OPTABLE(0x6e,0xfe, 2,none), /* 0x6e-0x6f */ - OPTABLE(0x78,0xff, 4,none), /* 0x78 */ - OPTABLE(0x79,0xff, 2,none), /* 0x79 */ - OPTABLE(0x7a,0xff, 3,none), /* 0x7a */ - OPTABLE(0x7b,0xff, 2,none), /* 0x7b */ - OPTABLE(0x7c,0xfc, 2,none), /* 0x7c-0x7f */ - OPTABLE(0x80,0x80, 1,none), /* 0x80-0xff */ -}; - -static const struct optable optable_1[] = { - OPTABLE(0x00,0xff,-3,none), /* 0x0100 */ - OPTABLE(0x40,0xf0,-3,none), /* 0x0140-0x14f */ - OPTABLE(0x80,0xf0, 1,none), /* 0x0180-0x018f */ - OPTABLE(0xc0,0xc0, 2,none), /* 0x01c0-0x01ff */ -}; - -static const struct optable optable_2[] = { - OPTABLE(0x00,0x20, 2,none), /* 0x6a0?/0x6a8?/0x6b0?/0x6b8? */ - OPTABLE(0x20,0x20, 3,none), /* 0x6a2?/0x6aa?/0x6b2?/0x6ba? */ -}; - -static const struct optable optable_3[] = { - OPTABLE(0x69,0xfb, 2,none), /* 0x010069/0x01006d/014069/0x01406d */ - OPTABLE(0x6b,0xff,-4,none), /* 0x01006b/0x01406b */ - OPTABLE(0x6f,0xff, 3,none), /* 0x01006f/0x01406f */ - OPTABLE(0x78,0xff, 5,none), /* 0x010078/0x014078 */ -}; - -static const struct optable optable_4[] = { - OPTABLE(0x00,0x78, 3,none), /* 0x0100690?/0x01006d0?/0140690/0x01406d0?/0x0100698?/0x01006d8?/0140698?/0x01406d8? */ - OPTABLE(0x20,0x78, 4,none), /* 0x0100692?/0x01006d2?/0140692/0x01406d2?/0x010069a?/0x01006da?/014069a?/0x01406da? */ -}; - -static const struct optables_list { - const struct optable *ptr; - int size; -} optables[] = { -#define OPTABLES(no) \ - { \ - .ptr = optable_##no, \ - .size = sizeof(optable_##no) / sizeof(struct optable), \ - } - OPTABLES(0), - OPTABLES(1), - OPTABLES(2), - OPTABLES(3), - OPTABLES(4), - -}; - -const unsigned char condmask[] = { - 0x00,0x40,0x01,0x04,0x02,0x08,0x10,0x20 -}; - -static int isbranch(struct task_struct *task,int reson) -{ - unsigned char cond = h8300_get_reg(task, PT_CCR); - /* encode complex conditions */ - /* B4: N^V - B5: Z|(N^V) - B6: C|Z */ - __asm__("bld #3,%w0\n\t" - "bxor #1,%w0\n\t" - "bst #4,%w0\n\t" - "bor #2,%w0\n\t" - "bst #5,%w0\n\t" - "bld #2,%w0\n\t" - "bor #0,%w0\n\t" - "bst #6,%w0\n\t" - :"=&r"(cond)::"cc"); - cond &= condmask[reson >> 1]; - if (!(reson & 1)) - return cond == 0; - else - return cond != 0; -} - -static unsigned short *getnextpc(struct task_struct *child, unsigned short *pc) -{ - const struct optable *op; - unsigned char *fetch_p; - unsigned char inst; - unsigned long addr; - unsigned long *sp; - int op_len,regno; - op = optables[0].ptr; - op_len = optables[0].size; - fetch_p = (unsigned char *)pc; - inst = *fetch_p++; - do { - if ((inst & op->bitmask) == op->bitpattern) { - if (op->length < 0) { - op = optables[-op->length].ptr; - op_len = optables[-op->length].size + 1; - inst = *fetch_p++; - } else { - switch (op->type) { - case none: - return pc + op->length; - case jabs: - addr = *(unsigned long *)pc; - return (unsigned short *)(addr & 0x00ffffff); - case ind: - addr = *pc & 0xff; - return (unsigned short *)(*(unsigned long *)addr); - case ret: - sp = (unsigned long *)h8300_get_reg(child, PT_USP); - /* user stack frames - | er0 | temporary saved - +--------+ - | exp | exception stack frames - +--------+ - | ret pc | userspace return address - */ - return (unsigned short *)(*(sp+2) & 0x00ffffff); - case reg: - regno = (*pc >> 4) & 0x07; - if (regno == 0) - addr = h8300_get_reg(child, PT_ER0); - else - addr = h8300_get_reg(child, regno-1+PT_ER1); - return (unsigned short *)addr; - case relb: - if (inst == 0x55 || isbranch(child,inst & 0x0f)) - pc = (unsigned short *)((unsigned long)pc + - ((signed char)(*fetch_p))); - return pc+1; /* skip myself */ - case relw: - if (inst == 0x5c || isbranch(child,(*fetch_p & 0xf0) >> 4)) - pc = (unsigned short *)((unsigned long)pc + - ((signed short)(*(pc+1)))); - return pc+2; /* skip myself */ - } - } - } else - op++; - } while(--op_len > 0); - return NULL; -} - -/* Set breakpoint(s) to simulate a single step from the current PC. */ - -void user_enable_single_step(struct task_struct *child) -{ - unsigned short *nextpc; - nextpc = getnextpc(child,(unsigned short *)h8300_get_reg(child, PT_PC)); - child->thread.breakinfo.addr = nextpc; - child->thread.breakinfo.inst = *nextpc; - *nextpc = BREAKINST; -} - -asmlinkage void trace_trap(unsigned long bp) -{ - if ((unsigned long)current->thread.breakinfo.addr == bp) { - user_disable_single_step(current); - force_sig(SIGTRAP,current); - } else - force_sig(SIGILL,current); -} - diff --git a/arch/h8300/platform/h8s/Makefile b/arch/h8300/platform/h8s/Makefile deleted file mode 100644 index bf12418..0000000 --- a/arch/h8300/platform/h8s/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# Makefile for the linux kernel. -# -# Reuse any files we can from the H8S -# - -obj-y := ints_h8s.o ptrace_h8s.o diff --git a/arch/h8300/platform/h8s/edosk2674/Makefile b/arch/h8300/platform/h8s/edosk2674/Makefile deleted file mode 100644 index 8e34972..0000000 --- a/arch/h8300/platform/h8s/edosk2674/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# -# Makefile for the linux kernel. -# - -extra-y := crt0_$(MODEL).o diff --git a/arch/h8300/platform/h8s/edosk2674/crt0_ram.S b/arch/h8300/platform/h8s/edosk2674/crt0_ram.S deleted file mode 100644 index 5ed191b..0000000 --- a/arch/h8300/platform/h8s/edosk2674/crt0_ram.S +++ /dev/null @@ -1,130 +0,0 @@ -/* - * linux/arch/h8300/platform/h8s/edosk2674/crt0_ram.S - * - * Yoshinori Sato - * - * Platform depend startup - * Target Archtecture: EDOSK-2674 - * Memory Layout : RAM - */ - -#define ASSEMBLY - -#include -#include - -#if !defined(CONFIG_BLKDEV_RESERVE) -#if defined(CONFIG_GDB_DEBUG) -#define RAMEND (__ramend - 0xc000) -#else -#define RAMEND __ramend -#endif -#else -#define RAMEND CONFIG_BLKDEV_RESERVE_ADDRESS -#endif - - .global __start - .global __command_line - .global __platform_gpio_table - .global __target_name - - .h8300s - - .section .text - .file "crt0_ram.S" - - /* CPU Reset entry */ -__start: - mov.l #RAMEND,sp - ldc #0x80,ccr - ldc #0x00,exr - - /* Peripheral Setup */ - bclr #4,@INTCR:8 /* interrupt mode 2 */ - bset #5,@INTCR:8 - bclr #0,@IER+1:16 - bset #1,@ISCRL+1:16 /* IRQ0 Positive Edge */ - bclr #0,@ISCRL+1:16 - -#if defined(CONFIG_MTD_UCLINUX) - /* move romfs image */ - jsr @__move_romfs -#endif - - /* .bss clear */ - mov.l #__sbss,er5 - mov.l er5,er6 - mov.l #__ebss,er4 - sub.l er5,er4 - shlr #2,er4 - sub.l er0,er0 -1: - mov.l er0,@er5 - adds #4,er5 - dec.l #1,er4 - bne 1b - - /* copy kernel commandline */ - mov.l #COMMAND_START,er5 - mov.l #_command_line,er6 - mov.w #512,r4 - eepmov.w - - /* uClinux kernel start */ - ldc #0x90,ccr /* running kernel */ - mov.l #_init_thread_union,sp - add.l #0x2000,sp - jsr @_start_kernel -_exit: - - jmp _exit - - rts - - /* I/O port assign information */ -__platform_gpio_table: - mov.l #gpio_table,er0 - rts - -gpio_table: - ;; P1DDR - ;; used,ddr - .byte 0x00,0x00 - ;; P2DDR - .byte 0x00,0x00 - ;; P3DDR - .byte 0x3f,0x3a - ;; dummy - .byte 0x00,0x00 - ;; P5DDR - .byte 0x00,0x00 - ;; P6DDR - .byte 0x00,0x00 - ;; P7DDR - .byte 0x00,0x00 - ;; P8DDR - .byte 0x00,0x00 - ;; dummy - .byte 0x00,0x00 - ;; PADDR - .byte 0xff,0xff - ;; PBDDR - .byte 0xff,0x00 - ;; PCDDR - .byte 0xff,0x00 - ;; PDDDR - .byte 0xff,0x00 - ;; PEDDR - .byte 0xff,0x00 - ;; PFDDR - .byte 0xff,0xff - ;; PGDDR - .byte 0x0f,0x0f - ;; PHDDR - .byte 0x0f,0x0f - -__target_name: - .asciz "EDOSK-2674" - - .section .bootvec,"ax" - jmp @__start diff --git a/arch/h8300/platform/h8s/edosk2674/crt0_rom.S b/arch/h8300/platform/h8s/edosk2674/crt0_rom.S deleted file mode 100644 index 06d1d7f..0000000 --- a/arch/h8300/platform/h8s/edosk2674/crt0_rom.S +++ /dev/null @@ -1,186 +0,0 @@ -/* - * linux/arch/h8300/platform/h8s/edosk2674/crt0_rom.S - * - * Yoshinori Sato - * - * Platform depend startup - * Target Archtecture: EDOSK-2674 - * Memory Layout : ROM - */ - -#define ASSEMBLY - -#include -#include - - .global __start - .global __command_line - .global __platform_gpio_table - .global __target_name - - .h8300s - .section .text - .file "crt0_rom.S" - - /* CPU Reset entry */ -__start: - mov.l #__ramend,sp - ldc #0x80,ccr - ldc #0,exr - - /* Peripheral Setup */ -;BSC/GPIO setup - mov.l #init_regs,er0 - mov.w #0xffff,e2 -1: - mov.w @er0+,r2 - beq 2f - mov.w @er0+,r1 - mov.b r1l,@er2 - bra 1b - -2: -;SDRAM setup -#define SDRAM_SMR 0x400040 - - mov.b #0,r0l - mov.b r0l,@DRACCR:16 - mov.w #0x188,r0 - mov.w r0,@REFCR:16 - mov.w #0x85b4,r0 - mov.w r0,@DRAMCR:16 - mov.b #0,r1l - mov.b r1l,@SDRAM_SMR - mov.w #0x84b4,r0 - mov.w r0,@DRAMCR:16 -;special thanks to Arizona Cooperative Power - - /* copy .data */ - mov.l #__begin_data,er5 - mov.l #__sdata,er6 - mov.l #__edata,er4 - sub.l er6,er4 - shlr.l #2,er4 -1: - mov.l @er5+,er0 - mov.l er0,@er6 - adds #4,er6 - dec.l #1,er4 - bne 1b - - /* .bss clear */ - mov.l #__sbss,er5 - mov.l #__ebss,er4 - sub.l er5,er4 - shlr.l #2,er4 - sub.l er0,er0 -1: - mov.l er0,@er5 - adds #4,er5 - dec.l #1,er4 - bne 1b - - /* copy kernel commandline */ - mov.l #COMMAND_START,er5 - mov.l #__command_line,er6 - mov.w #512,r4 - eepmov.w - - /* linux kernel start */ - ldc #0x90,ccr /* running kernel */ - mov.l #_init_thread_union,sp - add.l #0x2000,sp - jsr @_start_kernel -_exit: - - jmp _exit - - rts - - /* I/O port assign information */ -__platform_gpio_table: - mov.l #gpio_table,er0 - rts - -#define INIT_REGS_DATA(REGS,DATA) \ - .word ((REGS) & 0xffff),DATA - -init_regs: -INIT_REGS_DATA(ASTCR,0xff) -INIT_REGS_DATA(RDNCR,0x00) -INIT_REGS_DATA(ABWCR,0x80) -INIT_REGS_DATA(WTCRAH,0x27) -INIT_REGS_DATA(WTCRAL,0x77) -INIT_REGS_DATA(WTCRBH,0x71) -INIT_REGS_DATA(WTCRBL,0x22) -INIT_REGS_DATA(CSACRH,0x80) -INIT_REGS_DATA(CSACRL,0x80) -INIT_REGS_DATA(BROMCRH,0xa0) -INIT_REGS_DATA(BROMCRL,0xa0) -INIT_REGS_DATA(P3DDR,0x3a) -INIT_REGS_DATA(P3ODR,0x06) -INIT_REGS_DATA(PADDR,0xff) -INIT_REGS_DATA(PFDDR,0xfe) -INIT_REGS_DATA(PGDDR,0x0f) -INIT_REGS_DATA(PHDDR,0x0f) -INIT_REGS_DATA(PFCR0,0xff) -INIT_REGS_DATA(PFCR2,0x0d) -INIT_REGS_DATA(ITSR, 0x00) -INIT_REGS_DATA(ITSR+1,0x3f) -INIT_REGS_DATA(INTCR,0x20) - - .word 0 - -gpio_table: - ;; P1DDR - .byte 0x00,0x00 - ;; P2DDR - .byte 0x00,0x00 - ;; P3DDR - .byte 0x00,0x00 - ;; dummy - .byte 0x00,0x00 - ;; P5DDR - .byte 0x00,0x00 - ;; P6DDR - .byte 0x00,0x00 - ;; P7DDR - .byte 0x00,0x00 - ;; P8DDR - .byte 0x00,0x00 - ;; dummy - .byte 0x00,0x00 - ;; PADDR - .byte 0x00,0x00 - ;; PBDDR - .byte 0x00,0x00 - ;; PCDDR - .byte 0x00,0x00 - ;; PDDDR - .byte 0x00,0x00 - ;; PEDDR - .byte 0x00,0x00 - ;; PFDDR - .byte 0x00,0x00 - ;; PGDDR - .byte 0x00,0x00 - ;; PHDDR - .byte 0x00,0x00 - - .section .rodata -__target_name: - .asciz "EDOSK-2674" - - .section .bss -__command_line: - .space 512 - - /* interrupt vector */ - .section .vectors,"ax" - .long __start - .long __start -vector = 2 - .rept 126 - .long _interrupt_redirect_table+vector*4 -vector = vector + 1 - .endr diff --git a/arch/h8300/platform/h8s/generic/Makefile b/arch/h8300/platform/h8s/generic/Makefile deleted file mode 100644 index 44b4685..0000000 --- a/arch/h8300/platform/h8s/generic/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# -# Makefile for the linux kernel. -# - -extra-y = crt0_$(MODEL).o diff --git a/arch/h8300/platform/h8s/generic/crt0_ram.S b/arch/h8300/platform/h8s/generic/crt0_ram.S deleted file mode 100644 index 7018915..0000000 --- a/arch/h8300/platform/h8s/generic/crt0_ram.S +++ /dev/null @@ -1,127 +0,0 @@ -/* - * linux/arch/h8300/platform/h8s/edosk2674/crt0_ram.S - * - * Yoshinori Sato - * - * Platform depend startup - * Target Archtecture: generic - * Memory Layout : RAM - */ - -#define ASSEMBLY - -#include -#include - -#if !defined(CONFIG_BLKDEV_RESERVE) -#if defined(CONFIG_GDB_DEBUG) -#define RAMEND (__ramend - 0xc000) -#else -#define RAMEND __ramend -#endif -#else -#define RAMEND CONFIG_BLKDEV_RESERVE_ADDRESS -#endif - - .global __start - .global __command_line - .global __platform_gpio_table - .global __target_name - - .h8300s - - .section .text - .file "crt0_ram.S" - - /* CPU Reset entry */ -__start: - mov.l #RAMEND,sp - ldc #0x80,ccr - ldc #0x00,exr - - /* Peripheral Setup */ - bclr #4,@INTCR:8 /* interrupt mode 2 */ - bset #5,@INTCR:8 - -#if defined(CONFIG_MTD_UCLINUX) - /* move romfs image */ - jsr @__move_romfs -#endif - - /* .bss clear */ - mov.l #__sbss,er5 - mov.l er5,er6 - mov.l #__ebss,er4 - sub.l er5,er4 - shlr #2,er4 - sub.l er0,er0 -1: - mov.l er0,@er5 - adds #4,er5 - dec.l #1,er4 - bne 1b - - /* copy kernel commandline */ - mov.l #COMMAND_START,er5 - mov.l #_command_line,er6 - mov.w #512,r4 - eepmov.w - - /* uClinux kernel start */ - ldc #0x90,ccr /* running kernel */ - mov.l #_init_thread_union,sp - add.l #0x2000,sp - jsr @_start_kernel -_exit: - - jmp _exit - - rts - - /* I/O port assign information */ -__platform_gpio_table: - mov.l #gpio_table,er0 - rts - -gpio_table: - ;; P1DDR - ;; used,ddr - .byte 0x00,0x00 - ;; P2DDR - .byte 0x00,0x00 - ;; P3DDR - .byte 0x00,0x00 - ;; dummy - .byte 0x00,0x00 - ;; P5DDR - .byte 0x00,0x00 - ;; P6DDR - .byte 0x00,0x00 - ;; P7DDR - .byte 0x00,0x00 - ;; P8DDR - .byte 0x00,0x00 - ;; dummy - .byte 0x00,0x00 - ;; PADDR - .byte 0x00,0x00 - ;; PBDDR - .byte 0x00,0x00 - ;; PCDDR - .byte 0x00,0x00 - ;; PDDDR - .byte 0x00,0x00 - ;; PEDDR - .byte 0x00,0x00 - ;; PFDDR - .byte 0x00,0x00 - ;; PGDDR - .byte 0x00,0x00 - ;; PHDDR - .byte 0x00,0x00 - -__target_name: - .asciz "generic" - - .section .bootvec,"ax" - jmp @__start diff --git a/arch/h8300/platform/h8s/generic/crt0_rom.S b/arch/h8300/platform/h8s/generic/crt0_rom.S deleted file mode 100644 index 623ba78..0000000 --- a/arch/h8300/platform/h8s/generic/crt0_rom.S +++ /dev/null @@ -1,128 +0,0 @@ -/* - * linux/arch/h8300/platform/h8s/generic/crt0_rom.S - * - * Yoshinori Sato - * - * Platform depend startup - * Target Archtecture: generic - * Memory Layout : ROM - */ - -#define ASSEMBLY - -#include -#include - - .global __start - .global __command_line - .global __platform_gpio_table - .global __target_name - - .h8300s - .section .text - .file "crt0_rom.S" - - /* CPU Reset entry */ -__start: - mov.l #__ramend,sp - ldc #0x80,ccr - ldc #0,exr - bclr #4,@INTCR:8 - bset #5,@INTCR:8 /* Interrupt mode 2 */ - - /* Peripheral Setup */ - - /* copy .data */ -#if !defined(CONFIG_H8S_SIM) - mov.l #__begin_data,er5 - mov.l #__sdata,er6 - mov.l #__edata,er4 - sub.l er6,er4 - shlr.l #2,er4 -1: - mov.l @er5+,er0 - mov.l er0,@er6 - adds #4,er6 - dec.l #1,er4 - bne 1b -#endif - - /* .bss clear */ - mov.l #__sbss,er5 - mov.l #__ebss,er4 - sub.l er5,er4 - shlr.l #2,er4 - sub.l er0,er0 -1: - mov.l er0,@er5 - adds #4,er5 - dec.l #1,er4 - bne 1b - - /* linux kernel start */ - ldc #0x90,ccr /* running kernel */ - mov.l #_init_thread_union,sp - add.l #0x2000,sp - jsr @_start_kernel -_exit: - - jmp _exit - - rts - - /* I/O port assign information */ -__platform_gpio_table: - mov.l #gpio_table,er0 - rts - -gpio_table: - ;; P1DDR - .byte 0x00,0x00 - ;; P2DDR - .byte 0x00,0x00 - ;; P3DDR - .byte 0x00,0x00 - ;; P4DDR - .byte 0x00,0x00 - ;; P5DDR - .byte 0x00,0x00 - ;; P6DDR - .byte 0x00,0x00 - ;; dummy - .byte 0x00,0x00 - ;; P8DDR - .byte 0x00,0x00 - ;; PADDR - .byte 0x00,0x00 - ;; PBDDR - .byte 0x00,0x00 - ;; PCDDR - .byte 0x00,0x00 - ;; PDDDR - .byte 0x00,0x00 - ;; PEDDR - .byte 0x00,0x00 - ;; PFDDR - .byte 0x00,0x00 - ;; PGDDR - .byte 0x00,0x00 - ;; PHDDR - .byte 0x00,0x00 - - .section .rodata -__target_name: - .asciz "generic" - - .section .bss -__command_line: - .space 512 - - /* interrupt vector */ - .section .vectors,"ax" - .long __start - .long __start -vector = 2 - .rept 126-1 - .long _interrupt_redirect_table+vector*4 -vector = vector + 1 - .endr diff --git a/arch/h8300/platform/h8s/irq.c b/arch/h8300/platform/h8s/irq.c deleted file mode 100644 index f3a5511..0000000 --- a/arch/h8300/platform/h8s/irq.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * linux/arch/h8300/platform/h8s/ints_h8s.c - * Interrupt handling CPU variants - * - * Yoshinori Sato - * - */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/* saved vector list */ -const int __initconst h8300_saved_vectors[] = { -#if defined(CONFIG_GDB_DEBUG) - TRACE_VEC, - TRAP3_VEC, -#endif - -1 -}; - -/* trap entry table */ -const H8300_VECTOR __initconst h8300_trap_table[] = { - 0,0,0,0,0, - trace_break, /* TRACE */ - 0,0, - system_call, /* TRAPA #0 */ - 0,0,0,0,0,0,0 -}; - -/* IRQ pin assignment */ -struct irq_pins { - unsigned char port_no; - unsigned char bit_no; -} __attribute__((aligned(1),packed)); -/* ISTR = 0 */ -static const struct irq_pins irq_assign_table0[16]={ - {H8300_GPIO_P5,H8300_GPIO_B0},{H8300_GPIO_P5,H8300_GPIO_B1}, - {H8300_GPIO_P5,H8300_GPIO_B2},{H8300_GPIO_P5,H8300_GPIO_B3}, - {H8300_GPIO_P5,H8300_GPIO_B4},{H8300_GPIO_P5,H8300_GPIO_B5}, - {H8300_GPIO_P5,H8300_GPIO_B6},{H8300_GPIO_P5,H8300_GPIO_B7}, - {H8300_GPIO_P6,H8300_GPIO_B0},{H8300_GPIO_P6,H8300_GPIO_B1}, - {H8300_GPIO_P6,H8300_GPIO_B2},{H8300_GPIO_P6,H8300_GPIO_B3}, - {H8300_GPIO_P6,H8300_GPIO_B4},{H8300_GPIO_P6,H8300_GPIO_B5}, - {H8300_GPIO_PF,H8300_GPIO_B1},{H8300_GPIO_PF,H8300_GPIO_B2}, -}; -/* ISTR = 1 */ -static const struct irq_pins irq_assign_table1[16]={ - {H8300_GPIO_P8,H8300_GPIO_B0},{H8300_GPIO_P8,H8300_GPIO_B1}, - {H8300_GPIO_P8,H8300_GPIO_B2},{H8300_GPIO_P8,H8300_GPIO_B3}, - {H8300_GPIO_P8,H8300_GPIO_B4},{H8300_GPIO_P8,H8300_GPIO_B5}, - {H8300_GPIO_PH,H8300_GPIO_B2},{H8300_GPIO_PH,H8300_GPIO_B3}, - {H8300_GPIO_P2,H8300_GPIO_B0},{H8300_GPIO_P2,H8300_GPIO_B1}, - {H8300_GPIO_P2,H8300_GPIO_B2},{H8300_GPIO_P2,H8300_GPIO_B3}, - {H8300_GPIO_P2,H8300_GPIO_B4},{H8300_GPIO_P2,H8300_GPIO_B5}, - {H8300_GPIO_P2,H8300_GPIO_B6},{H8300_GPIO_P2,H8300_GPIO_B7}, -}; - -/* IRQ to GPIO pin translation */ -#define IRQ_GPIO_MAP(irqbit,irq,port,bit) \ -do { \ - if (*(volatile unsigned short *)ITSR & irqbit) { \ - port = irq_assign_table1[irq - EXT_IRQ0].port_no; \ - bit = irq_assign_table1[irq - EXT_IRQ0].bit_no; \ - } else { \ - port = irq_assign_table0[irq - EXT_IRQ0].port_no; \ - bit = irq_assign_table0[irq - EXT_IRQ0].bit_no; \ - } \ -} while(0) - -int h8300_enable_irq_pin(unsigned int irq) -{ - if (irq >= EXT_IRQ0 && irq <= EXT_IRQ15) { - unsigned short ptn = 1 << (irq - EXT_IRQ0); - unsigned int port_no,bit_no; - IRQ_GPIO_MAP(ptn, irq, port_no, bit_no); - if (H8300_GPIO_RESERVE(port_no, bit_no) == 0) - return -EBUSY; /* pin already use */ - H8300_GPIO_DDR(port_no, bit_no, H8300_GPIO_INPUT); - *(volatile unsigned short *)ISR &= ~ptn; /* ISR clear */ - } - - return 0; -} - -void h8300_disable_irq_pin(unsigned int irq) -{ - if (irq >= EXT_IRQ0 && irq <= EXT_IRQ15) { - /* disable interrupt & release IRQ pin */ - unsigned short ptn = 1 << (irq - EXT_IRQ0); - unsigned short port_no,bit_no; - *(volatile unsigned short *)ISR &= ~ptn; - *(volatile unsigned short *)IER &= ~ptn; - IRQ_GPIO_MAP(ptn, irq, port_no, bit_no); - H8300_GPIO_FREE(port_no, bit_no); - } -} diff --git a/arch/h8300/platform/h8s/ptrace_h8s.c b/arch/h8300/platform/h8s/ptrace_h8s.c deleted file mode 100644 index c058ab1..0000000 --- a/arch/h8300/platform/h8s/ptrace_h8s.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * linux/arch/h8300/platform/h8s/ptrace_h8s.c - * ptrace cpu depend helper functions - * - * Yoshinori Sato - * - * 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 - -#define CCR_MASK 0x6f -#define EXR_TRACE 0x80 - -/* Mapping from PT_xxx to the stack offset at which the register is - saved. Notice that usp has no stack-slot and needs to be treated - specially (see get_reg/put_reg below). */ -static const int h8300_register_offset[] = { - PT_REG(er1), PT_REG(er2), PT_REG(er3), PT_REG(er4), - PT_REG(er5), PT_REG(er6), PT_REG(er0), PT_REG(orig_er0), - PT_REG(ccr), PT_REG(pc), 0, PT_REG(exr) -}; - -/* read register */ -long h8300_get_reg(struct task_struct *task, int regno) -{ - switch (regno) { - case PT_USP: - return task->thread.usp + sizeof(long)*2 + 2; - case PT_CCR: - case PT_EXR: - return *(unsigned short *)(task->thread.esp0 + h8300_register_offset[regno]); - default: - return *(unsigned long *)(task->thread.esp0 + h8300_register_offset[regno]); - } -} - -/* write register */ -int h8300_put_reg(struct task_struct *task, int regno, unsigned long data) -{ - unsigned short oldccr; - switch (regno) { - case PT_USP: - task->thread.usp = data - sizeof(long)*2 - 2; - case PT_CCR: - oldccr = *(unsigned short *)(task->thread.esp0 + h8300_register_offset[regno]); - oldccr &= ~CCR_MASK; - data &= CCR_MASK; - data |= oldccr; - *(unsigned short *)(task->thread.esp0 + h8300_register_offset[regno]) = data; - break; - case PT_EXR: - /* exr modify not support */ - return -EIO; - default: - *(unsigned long *)(task->thread.esp0 + h8300_register_offset[regno]) = data; - break; - } - return 0; -} - -/* disable singlestep */ -void user_disable_single_step(struct task_struct *child) -{ - *(unsigned short *)(child->thread.esp0 + h8300_register_offset[PT_EXR]) &= ~EXR_TRACE; -} - -/* enable singlestep */ -void user_enable_single_step(struct task_struct *child) -{ - *(unsigned short *)(child->thread.esp0 + h8300_register_offset[PT_EXR]) |= EXR_TRACE; -} - -asmlinkage void trace_trap(unsigned long bp) -{ - (void)bp; - force_sig(SIGTRAP,current); -} - diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h index d340497..50fe651 100644 --- a/include/linux/serial_sci.h +++ b/include/linux/serial_sci.h @@ -5,7 +5,7 @@ #include /* - * Generic header for SuperH (H)SCI(F) (used by sh/sh64/h8300 and related parts) + * Generic header for SuperH (H)SCI(F) (used by sh/sh64 and related parts) */ #define SCIx_NOT_SUPPORTED (-1) diff --git a/tools/testing/ktest/examples/crosstests.conf b/tools/testing/ktest/examples/crosstests.conf index 4673660..a1203148 100644 --- a/tools/testing/ktest/examples/crosstests.conf +++ b/tools/testing/ktest/examples/crosstests.conf @@ -133,12 +133,6 @@ CROSS = frv-linux ARCH = frv GCC_VER = 4.5.1 -# h8300 - failed make defconfig?? -TEST_START IF ${RUN} == h8300 || ${DO_FAILED} -CROSS = h8300-elf -ARCH = h8300 -GCC_VER = 4.5.1 - # m68k fails with error? TEST_START IF ${RUN} == m68k || ${DO_DEFAULT} CROSS = m68k-linux -- cgit v0.10.2 From 77da71b3a03ebb2bd06500ca1d85e1c5083bb005 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 30 Aug 2013 06:03:10 -0700 Subject: ide: Drop H8/300 driver Architecture is gone, so there is no need to keep its ide driver around. Cc: Yoshinori Sato Acked-by: Greg Kroah-Hartman Acked-by: David S. Miller Signed-off-by: Guenter Roeck diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index 02906ca..5dba90a 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -722,13 +722,6 @@ config BLK_DEV_IDE_RAPIDE Say Y here if you want to support the Yellowstone RapIDE controller manufactured for use with Acorn computers. -config IDE_H8300 - tristate "H8300 IDE support" - depends on H8300 - default y - help - Enables the H8300 IDE driver. - config BLK_DEV_GAYLE tristate "Amiga Gayle IDE interface support" depends on AMIGA diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile index af8d016..a04ee82 100644 --- a/drivers/ide/Makefile +++ b/drivers/ide/Makefile @@ -78,8 +78,6 @@ obj-$(CONFIG_BLK_DEV_CMD640) += cmd640.o obj-$(CONFIG_BLK_DEV_IDE_PMAC) += pmac.o -obj-$(CONFIG_IDE_H8300) += ide-h8300.o - obj-$(CONFIG_IDE_GENERIC) += ide-generic.o obj-$(CONFIG_BLK_DEV_IDEPNP) += ide-pnp.o diff --git a/drivers/ide/ide-h8300.c b/drivers/ide/ide-h8300.c deleted file mode 100644 index 520f42c..0000000 --- a/drivers/ide/ide-h8300.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * H8/300 generic IDE interface - */ - -#include -#include - -#include -#include - -#define DRV_NAME "ide-h8300" - -#define bswap(d) \ -({ \ - u16 r; \ - __asm__("mov.b %w1,r1h\n\t" \ - "mov.b %x1,r1l\n\t" \ - "mov.w r1,%0" \ - :"=r"(r) \ - :"r"(d) \ - :"er1"); \ - (r); \ -}) - -static void mm_outsw(unsigned long addr, void *buf, u32 len) -{ - unsigned short *bp = (unsigned short *)buf; - for (; len > 0; len--, bp++) - *(volatile u16 *)addr = bswap(*bp); -} - -static void mm_insw(unsigned long addr, void *buf, u32 len) -{ - unsigned short *bp = (unsigned short *)buf; - for (; len > 0; len--, bp++) - *bp = bswap(*(volatile u16 *)addr); -} - -static void h8300_input_data(ide_drive_t *drive, struct ide_cmd *cmd, - void *buf, unsigned int len) -{ - mm_insw(drive->hwif->io_ports.data_addr, buf, (len + 1) / 2); -} - -static void h8300_output_data(ide_drive_t *drive, struct ide_cmd *cmd, - void *buf, unsigned int len) -{ - mm_outsw(drive->hwif->io_ports.data_addr, buf, (len + 1) / 2); -} - -static const struct ide_tp_ops h8300_tp_ops = { - .exec_command = ide_exec_command, - .read_status = ide_read_status, - .read_altstatus = ide_read_altstatus, - .write_devctl = ide_write_devctl, - - .dev_select = ide_dev_select, - .tf_load = ide_tf_load, - .tf_read = ide_tf_read, - - .input_data = h8300_input_data, - .output_data = h8300_output_data, -}; - -#define H8300_IDE_GAP (2) - -static inline void hw_setup(struct ide_hw *hw) -{ - int i; - - memset(hw, 0, sizeof(*hw)); - for (i = 0; i <= 7; i++) - hw->io_ports_array[i] = CONFIG_H8300_IDE_BASE + H8300_IDE_GAP*i; - hw->io_ports.ctl_addr = CONFIG_H8300_IDE_ALT; - hw->irq = EXT_IRQ0 + CONFIG_H8300_IDE_IRQ; -} - -static const struct ide_port_info h8300_port_info = { - .tp_ops = &h8300_tp_ops, - .host_flags = IDE_HFLAG_NO_IO_32BIT | IDE_HFLAG_NO_DMA, - .chipset = ide_generic, -}; - -static int __init h8300_ide_init(void) -{ - struct ide_hw hw, *hws[] = { &hw }; - - printk(KERN_INFO DRV_NAME ": H8/300 generic IDE interface\n"); - - if (!request_region(CONFIG_H8300_IDE_BASE, H8300_IDE_GAP*8, "ide-h8300")) - goto out_busy; - if (!request_region(CONFIG_H8300_IDE_ALT, H8300_IDE_GAP, "ide-h8300")) { - release_region(CONFIG_H8300_IDE_BASE, H8300_IDE_GAP*8); - goto out_busy; - } - - hw_setup(&hw); - - return ide_host_add(&h8300_port_info, hws, 1, NULL); - -out_busy: - printk(KERN_ERR "ide-h8300: IDE I/F resource already used.\n"); - - return -EBUSY; -} - -module_init(h8300_ide_init); - -MODULE_LICENSE("GPL"); -- cgit v0.10.2 From d95353e47a3ff67b3cd6554e5f4590774a95816c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 30 Aug 2013 06:17:07 -0700 Subject: net/ethernet: smsc9194: Drop conditional code for H8/300 With the H8/300 architecture gone, this code is no longer necessary. Cc: Yoshinori Sato Acked-by: Greg Kroah-Hartman Acked-by: David S. Miller Signed-off-by: Guenter Roeck diff --git a/drivers/net/ethernet/smsc/smc9194.c b/drivers/net/ethernet/smsc/smc9194.c index e85c2e7..afd9873 100644 --- a/drivers/net/ethernet/smsc/smc9194.c +++ b/drivers/net/ethernet/smsc/smc9194.c @@ -95,14 +95,6 @@ static const char version[] = #define USE_32_BIT 1 #endif -#if defined(__H8300H__) || defined(__H8300S__) -#define NO_AUTOPROBE -#undef insl -#undef outsl -#define insl(a,b,l) io_insl_noswap(a,b,l) -#define outsl(a,b,l) io_outsl_noswap(a,b,l) -#endif - /* .the SMC9194 can be at any of the following port addresses. To change, .for a slightly different card, you can add it to the array. Keep in @@ -114,12 +106,6 @@ struct devlist { unsigned int irq; }; -#if defined(CONFIG_H8S_EDOSK2674) -static struct devlist smc_devlist[] __initdata = { - {.port = 0xf80000, .irq = 16}, - {.port = 0, .irq = 0 }, -}; -#else static struct devlist smc_devlist[] __initdata = { {.port = 0x200, .irq = 0}, {.port = 0x220, .irq = 0}, @@ -139,7 +125,6 @@ static struct devlist smc_devlist[] __initdata = { {.port = 0x3E0, .irq = 0}, {.port = 0, .irq = 0}, }; -#endif /* . Wait time for memory to be free. This probably shouldn't be . tuned that much, as waiting for this means nothing else happens @@ -651,11 +636,7 @@ static void smc_hardware_send_packet( struct net_device * dev ) #ifdef USE_32_BIT if ( length & 0x2 ) { outsl(ioaddr + DATA_1, buf, length >> 2 ); -#if !defined(__H8300H__) && !defined(__H8300S__) outw( *((word *)(buf + (length & 0xFFFFFFFC))),ioaddr +DATA_1); -#else - ctrl_outw( *((word *)(buf + (length & 0xFFFFFFFC))),ioaddr +DATA_1); -#endif } else outsl(ioaddr + DATA_1, buf, length >> 2 ); @@ -899,7 +880,6 @@ static int __init smc_probe(struct net_device *dev, int ioaddr) retval = -ENODEV; goto err_out; } -#if !defined(CONFIG_H8S_EDOSK2674) /* well, we've already written once, so hopefully another time won't hurt. This time, I need to switch the bank register to bank 1, so I can access the base address register */ @@ -914,10 +894,6 @@ static int __init smc_probe(struct net_device *dev, int ioaddr) retval = -ENODEV; goto err_out; } -#else - (void)base_address_register; /* Warning suppression */ -#endif - /* check if the revision register is something that I recognize. These might need to be added to later, as future revisions -- cgit v0.10.2 From dd9589c7c00ab41e49d972dc9a01fcac7dc39961 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 30 Aug 2013 06:04:57 -0700 Subject: net/ethernet: Drop H8/300 Ethernet driver Architecture is gone, so this driver is no longer needed. Cc: Yoshinori Sato Acked-by: Greg Kroah-Hartman Acked-by: David S. Miller Signed-off-by: Guenter Roeck diff --git a/drivers/net/Space.c b/drivers/net/Space.c index 3a8c753..a7271e0 100644 --- a/drivers/net/Space.c +++ b/drivers/net/Space.c @@ -102,8 +102,7 @@ static struct devprobe2 isa_probes[] __initdata = { #ifdef CONFIG_WD80x3 {wd_probe, 0}, #endif -#if defined(CONFIG_NE2000) || \ - defined(CONFIG_NE_H8300) /* ISA (use ne2k-pci for PCI cards) */ +#if defined(CONFIG_NE2000) /* ISA (use ne2k-pci for PCI cards) */ {ne_probe, 0}, #endif #ifdef CONFIG_LANCE /* ISA/VLB (use pcnet32 for PCI cards) */ diff --git a/drivers/net/ethernet/8390/Kconfig b/drivers/net/ethernet/8390/Kconfig index becef25..0988811 100644 --- a/drivers/net/ethernet/8390/Kconfig +++ b/drivers/net/ethernet/8390/Kconfig @@ -146,13 +146,6 @@ config PCMCIA_PCNET To compile this driver as a module, choose M here: the module will be called pcnet_cs. If unsure, say N. -config NE_H8300 - tristate "NE2000 compatible support for H8/300" - depends on H8300H_AKI3068NET || H8300H_H8MAX - ---help--- - Say Y here if you want to use the NE2000 compatible - controller on the Renesas H8/300 processor. - config STNIC tristate "National DP83902AV support" depends on SUPERH diff --git a/drivers/net/ethernet/8390/Makefile b/drivers/net/ethernet/8390/Makefile index 588954a..ff3b318 100644 --- a/drivers/net/ethernet/8390/Makefile +++ b/drivers/net/ethernet/8390/Makefile @@ -10,7 +10,6 @@ obj-$(CONFIG_HYDRA) += hydra.o 8390.o obj-$(CONFIG_MCF8390) += mcf8390.o 8390.o obj-$(CONFIG_NE2000) += ne.o 8390p.o obj-$(CONFIG_NE2K_PCI) += ne2k-pci.o 8390.o -obj-$(CONFIG_NE_H8300) += ne-h8300.o 8390.o obj-$(CONFIG_PCMCIA_AXNET) += axnet_cs.o 8390.o obj-$(CONFIG_PCMCIA_PCNET) += pcnet_cs.o 8390.o obj-$(CONFIG_STNIC) += stnic.o 8390.o diff --git a/drivers/net/ethernet/8390/ne-h8300.c b/drivers/net/ethernet/8390/ne-h8300.c deleted file mode 100644 index 7fc28f2..0000000 --- a/drivers/net/ethernet/8390/ne-h8300.c +++ /dev/null @@ -1,684 +0,0 @@ -/* ne-h8300.c: A NE2000 clone on H8/300 driver for linux. */ -/* - original ne.c - Written 1992-94 by Donald Becker. - - Copyright 1993 United States Government as represented by the - Director, National Security Agency. - - This software may be used and distributed according to the terms - of the GNU General Public License, incorporated herein by reference. - - The author may be reached as becker@scyld.com, or C/O - Scyld Computing Corporation, 410 Severn Ave., Suite 210, Annapolis MD 21403 - - H8/300 modified - Yoshinori Sato -*/ - -static const char version1[] = -"ne-h8300.c:v1.00 2004/04/11 ysato\n"; - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define EI_SHIFT(x) (ei_local->reg_offset[x]) - -#include "8390.h" - -#define DRV_NAME "ne-h8300" - -/* Some defines that people can play with if so inclined. */ - -/* Do we perform extra sanity checks on stuff ? */ -/* #define NE_SANITY_CHECK */ - -/* Do we implement the read before write bugfix ? */ -/* #define NE_RW_BUGFIX */ - -/* Do we have a non std. amount of memory? (in units of 256 byte pages) */ -/* #define PACKETBUF_MEMSIZE 0x40 */ - -/* A zero-terminated list of I/O addresses to be probed at boot. */ - -/* ---- No user-serviceable parts below ---- */ - -static const char version[] = - "8390.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; - -#include "lib8390.c" - -#define NE_BASE (dev->base_addr) -#define NE_CMD 0x00 -#define NE_DATAPORT (ei_status.word16?0x20:0x10) /* NatSemi-defined port window offset. */ -#define NE_RESET (ei_status.word16?0x3f:0x1f) /* Issue a read to reset, a write to clear. */ -#define NE_IO_EXTENT (ei_status.word16?0x40:0x20) - -#define NESM_START_PG 0x40 /* First page of TX buffer */ -#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */ - -static int ne_probe1(struct net_device *dev, int ioaddr); - -static int ne_open(struct net_device *dev); -static int ne_close(struct net_device *dev); - -static void ne_reset_8390(struct net_device *dev); -static void ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, - int ring_page); -static void ne_block_input(struct net_device *dev, int count, - struct sk_buff *skb, int ring_offset); -static void ne_block_output(struct net_device *dev, const int count, - const unsigned char *buf, const int start_page); - - -static u32 reg_offset[16]; - -static int __init init_reg_offset(struct net_device *dev,unsigned long base_addr) -{ - struct ei_device *ei_local = netdev_priv(dev); - int i; - unsigned char bus_width; - - bus_width = *(volatile unsigned char *)ABWCR; - bus_width &= 1 << ((base_addr >> 21) & 7); - - for (i = 0; i < ARRAY_SIZE(reg_offset); i++) - if (bus_width == 0) - reg_offset[i] = i * 2 + 1; - else - reg_offset[i] = i; - - ei_local->reg_offset = reg_offset; - return 0; -} - -static int __initdata h8300_ne_count = 0; -#ifdef CONFIG_H8300H_H8MAX -static unsigned long __initdata h8300_ne_base[] = { 0x800600 }; -static int h8300_ne_irq[] = {EXT_IRQ4}; -#endif -#ifdef CONFIG_H8300H_AKI3068NET -static unsigned long __initdata h8300_ne_base[] = { 0x200000 }; -static int h8300_ne_irq[] = {EXT_IRQ5}; -#endif - -static inline int init_dev(struct net_device *dev) -{ - if (h8300_ne_count < ARRAY_SIZE(h8300_ne_base)) { - dev->base_addr = h8300_ne_base[h8300_ne_count]; - dev->irq = h8300_ne_irq[h8300_ne_count]; - h8300_ne_count++; - return 0; - } else - return -ENODEV; -} - -/* Probe for various non-shared-memory ethercards. - - NEx000-clone boards have a Station Address PROM (SAPROM) in the packet - buffer memory space. NE2000 clones have 0x57,0x57 in bytes 0x0e,0x0f of - the SAPROM, while other supposed NE2000 clones must be detected by their - SA prefix. - - Reading the SAPROM from a word-wide card with the 8390 set in byte-wide - mode results in doubled values, which can be detected and compensated for. - - The probe is also responsible for initializing the card and filling - in the 'dev' and 'ei_status' structures. - - We use the minimum memory size for some ethercard product lines, iff we can't - distinguish models. You can increase the packet buffer size by setting - PACKETBUF_MEMSIZE. Reported Cabletron packet buffer locations are: - E1010 starts at 0x100 and ends at 0x2000. - E1010-x starts at 0x100 and ends at 0x8000. ("-x" means "more memory") - E2010 starts at 0x100 and ends at 0x4000. - E2010-x starts at 0x100 and ends at 0xffff. */ - -static int __init do_ne_probe(struct net_device *dev) -{ - unsigned int base_addr = dev->base_addr; - - /* First check any supplied i/o locations. User knows best. */ - if (base_addr > 0x1ff) /* Check a single specified location. */ - return ne_probe1(dev, base_addr); - else if (base_addr != 0) /* Don't probe at all. */ - return -ENXIO; - - return -ENODEV; -} - -static void cleanup_card(struct net_device *dev) -{ - free_irq(dev->irq, dev); - release_region(dev->base_addr, NE_IO_EXTENT); -} - -#ifndef MODULE -struct net_device * __init ne_probe(int unit) -{ - struct net_device *dev = ____alloc_ei_netdev(0); - int err; - - if (!dev) - return ERR_PTR(-ENOMEM); - - if (init_dev(dev)) - return ERR_PTR(-ENODEV); - - sprintf(dev->name, "eth%d", unit); - netdev_boot_setup_check(dev); - - err = init_reg_offset(dev, dev->base_addr); - if (err) - goto out; - - err = do_ne_probe(dev); - if (err) - goto out; - return dev; -out: - free_netdev(dev); - return ERR_PTR(err); -} -#endif - -static const struct net_device_ops ne_netdev_ops = { - .ndo_open = ne_open, - .ndo_stop = ne_close, - - .ndo_start_xmit = __ei_start_xmit, - .ndo_tx_timeout = __ei_tx_timeout, - .ndo_get_stats = __ei_get_stats, - .ndo_set_rx_mode = __ei_set_multicast_list, - .ndo_validate_addr = eth_validate_addr, - .ndo_set_mac_address = eth_mac_addr, - .ndo_change_mtu = eth_change_mtu, -#ifdef CONFIG_NET_POLL_CONTROLLER - .ndo_poll_controller = __ei_poll, -#endif -}; - -static int __init ne_probe1(struct net_device *dev, int ioaddr) -{ - int i; - unsigned char SA_prom[16]; - int wordlength = 2; - const char *name = NULL; - int start_page, stop_page; - int reg0, ret; - static unsigned version_printed; - struct ei_device *ei_local = netdev_priv(dev); - unsigned char bus_width; - - if (!request_region(ioaddr, NE_IO_EXTENT, DRV_NAME)) - return -EBUSY; - - reg0 = inb_p(ioaddr); - if (reg0 == 0xFF) { - ret = -ENODEV; - goto err_out; - } - - /* Do a preliminary verification that we have a 8390. */ - { - int regd; - outb_p(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD); - regd = inb_p(ioaddr + EI_SHIFT(0x0d)); - outb_p(0xff, ioaddr + EI_SHIFT(0x0d)); - outb_p(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD); - inb_p(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */ - if (inb_p(ioaddr + EN0_COUNTER0) != 0) { - outb_p(reg0, ioaddr + EI_SHIFT(0)); - outb_p(regd, ioaddr + EI_SHIFT(0x0d)); /* Restore the old values. */ - ret = -ENODEV; - goto err_out; - } - } - - if (ei_debug && version_printed++ == 0) - printk(KERN_INFO "%s", version1); - - printk(KERN_INFO "NE*000 ethercard probe at %08x:", ioaddr); - - /* Read the 16 bytes of station address PROM. - We must first initialize registers, similar to NS8390_init(eifdev, 0). - We can't reliably read the SAPROM address without this. - (I learned the hard way!). */ - { - struct {unsigned char value, offset; } program_seq[] = - { - {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/ - {0x48, EN0_DCFG}, /* Set byte-wide (0x48) access. */ - {0x00, EN0_RCNTLO}, /* Clear the count regs. */ - {0x00, EN0_RCNTHI}, - {0x00, EN0_IMR}, /* Mask completion irq. */ - {0xFF, EN0_ISR}, - {E8390_RXOFF, EN0_RXCR}, /* 0x20 Set to monitor */ - {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */ - {32, EN0_RCNTLO}, - {0x00, EN0_RCNTHI}, - {0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */ - {0x00, EN0_RSARHI}, - {E8390_RREAD+E8390_START, E8390_CMD}, - }; - - for (i = 0; i < ARRAY_SIZE(program_seq); i++) - outb_p(program_seq[i].value, ioaddr + program_seq[i].offset); - - } - bus_width = *(volatile unsigned char *)ABWCR; - bus_width &= 1 << ((ioaddr >> 21) & 7); - ei_status.word16 = (bus_width == 0); /* temporary setting */ - for(i = 0; i < 16 /*sizeof(SA_prom)*/; i++) { - SA_prom[i] = inb_p(ioaddr + NE_DATAPORT); - inb_p(ioaddr + NE_DATAPORT); /* dummy read */ - } - - start_page = NESM_START_PG; - stop_page = NESM_STOP_PG; - - if (bus_width) - wordlength = 1; - else - outb_p(0x49, ioaddr + EN0_DCFG); - - /* Set up the rest of the parameters. */ - name = (wordlength == 2) ? "NE2000" : "NE1000"; - - if (! dev->irq) { - printk(" failed to detect IRQ line.\n"); - ret = -EAGAIN; - goto err_out; - } - - /* Snarf the interrupt now. There's no point in waiting since we cannot - share and the board will usually be enabled. */ - ret = request_irq(dev->irq, __ei_interrupt, 0, name, dev); - if (ret) { - printk (" unable to get IRQ %d (errno=%d).\n", dev->irq, ret); - goto err_out; - } - - dev->base_addr = ioaddr; - - for (i = 0; i < ETH_ALEN; i++) - dev->dev_addr[i] = SA_prom[i]; - printk(" %pM\n", dev->dev_addr); - - printk("%s: %s found at %#x, using IRQ %d.\n", - dev->name, name, ioaddr, dev->irq); - - ei_status.name = name; - ei_status.tx_start_page = start_page; - ei_status.stop_page = stop_page; - ei_status.word16 = (wordlength == 2); - - ei_status.rx_start_page = start_page + TX_PAGES; -#ifdef PACKETBUF_MEMSIZE - /* Allow the packet buffer size to be overridden by know-it-alls. */ - ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE; -#endif - - ei_status.reset_8390 = &ne_reset_8390; - ei_status.block_input = &ne_block_input; - ei_status.block_output = &ne_block_output; - ei_status.get_8390_hdr = &ne_get_8390_hdr; - ei_status.priv = 0; - - dev->netdev_ops = &ne_netdev_ops; - - __NS8390_init(dev, 0); - - ret = register_netdev(dev); - if (ret) - goto out_irq; - return 0; -out_irq: - free_irq(dev->irq, dev); -err_out: - release_region(ioaddr, NE_IO_EXTENT); - return ret; -} - -static int ne_open(struct net_device *dev) -{ - __ei_open(dev); - return 0; -} - -static int ne_close(struct net_device *dev) -{ - if (ei_debug > 1) - printk(KERN_DEBUG "%s: Shutting down ethercard.\n", dev->name); - __ei_close(dev); - return 0; -} - -/* Hard reset the card. This used to pause for the same period that a - 8390 reset command required, but that shouldn't be necessary. */ - -static void ne_reset_8390(struct net_device *dev) -{ - unsigned long reset_start_time = jiffies; - struct ei_device *ei_local = netdev_priv(dev); - - if (ei_debug > 1) - printk(KERN_DEBUG "resetting the 8390 t=%ld...", jiffies); - - /* DON'T change these to inb_p/outb_p or reset will fail on clones. */ - outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET); - - ei_status.txing = 0; - ei_status.dmaing = 0; - - /* This check _should_not_ be necessary, omit eventually. */ - while ((inb_p(NE_BASE+EN0_ISR) & ENISR_RESET) == 0) - if (time_after(jiffies, reset_start_time + 2*HZ/100)) { - printk(KERN_WARNING "%s: ne_reset_8390() did not complete.\n", dev->name); - break; - } - outb_p(ENISR_RESET, NE_BASE + EN0_ISR); /* Ack intr. */ -} - -/* Grab the 8390 specific header. Similar to the block_input routine, but - we don't need to be concerned with ring wrap as the header will be at - the start of a page, so we optimize accordingly. */ - -static void ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) -{ - struct ei_device *ei_local = netdev_priv(dev); - /* This *shouldn't* happen. If it does, it's the last thing you'll see */ - - if (ei_status.dmaing) - { - printk(KERN_EMERG "%s: DMAing conflict in ne_get_8390_hdr " - "[DMAstat:%d][irqlock:%d].\n", - dev->name, ei_status.dmaing, ei_status.irqlock); - return; - } - - ei_status.dmaing |= 0x01; - outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, NE_BASE + NE_CMD); - outb_p(sizeof(struct e8390_pkt_hdr), NE_BASE + EN0_RCNTLO); - outb_p(0, NE_BASE + EN0_RCNTHI); - outb_p(0, NE_BASE + EN0_RSARLO); /* On page boundary */ - outb_p(ring_page, NE_BASE + EN0_RSARHI); - outb_p(E8390_RREAD+E8390_START, NE_BASE + NE_CMD); - - if (ei_status.word16) { - int len; - unsigned short *p = (unsigned short *)hdr; - for (len = sizeof(struct e8390_pkt_hdr)>>1; len > 0; len--) - *p++ = inw(NE_BASE + NE_DATAPORT); - } else - insb(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)); - - outb_p(ENISR_RDC, NE_BASE + EN0_ISR); /* Ack intr. */ - ei_status.dmaing &= ~0x01; - - le16_to_cpus(&hdr->count); -} - -/* Block input and output, similar to the Crynwr packet driver. If you - are porting to a new ethercard, look at the packet driver source for hints. - The NEx000 doesn't share the on-board packet memory -- you have to put - the packet out through the "remote DMA" dataport using outb. */ - -static void ne_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) -{ - struct ei_device *ei_local = netdev_priv(dev); -#ifdef NE_SANITY_CHECK - int xfer_count = count; -#endif - char *buf = skb->data; - - /* This *shouldn't* happen. If it does, it's the last thing you'll see */ - if (ei_status.dmaing) - { - printk(KERN_EMERG "%s: DMAing conflict in ne_block_input " - "[DMAstat:%d][irqlock:%d].\n", - dev->name, ei_status.dmaing, ei_status.irqlock); - return; - } - ei_status.dmaing |= 0x01; - outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, NE_BASE + NE_CMD); - outb_p(count & 0xff, NE_BASE + EN0_RCNTLO); - outb_p(count >> 8, NE_BASE + EN0_RCNTHI); - outb_p(ring_offset & 0xff, NE_BASE + EN0_RSARLO); - outb_p(ring_offset >> 8, NE_BASE + EN0_RSARHI); - outb_p(E8390_RREAD+E8390_START, NE_BASE + NE_CMD); - if (ei_status.word16) - { - int len; - unsigned short *p = (unsigned short *)buf; - for (len = count>>1; len > 0; len--) - *p++ = inw(NE_BASE + NE_DATAPORT); - if (count & 0x01) - { - buf[count-1] = inb(NE_BASE + NE_DATAPORT); -#ifdef NE_SANITY_CHECK - xfer_count++; -#endif - } - } else { - insb(NE_BASE + NE_DATAPORT, buf, count); - } - -#ifdef NE_SANITY_CHECK - /* This was for the ALPHA version only, but enough people have - been encountering problems so it is still here. If you see - this message you either 1) have a slightly incompatible clone - or 2) have noise/speed problems with your bus. */ - - if (ei_debug > 1) - { - /* DMA termination address check... */ - int addr, tries = 20; - do { - /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here - -- it's broken for Rx on some cards! */ - int high = inb_p(NE_BASE + EN0_RSARHI); - int low = inb_p(NE_BASE + EN0_RSARLO); - addr = (high << 8) + low; - if (((ring_offset + xfer_count) & 0xff) == low) - break; - } while (--tries > 0); - if (tries <= 0) - printk(KERN_WARNING "%s: RX transfer address mismatch," - "%#4.4x (expected) vs. %#4.4x (actual).\n", - dev->name, ring_offset + xfer_count, addr); - } -#endif - outb_p(ENISR_RDC, NE_BASE + EN0_ISR); /* Ack intr. */ - ei_status.dmaing &= ~0x01; -} - -static void ne_block_output(struct net_device *dev, int count, - const unsigned char *buf, const int start_page) -{ - struct ei_device *ei_local = netdev_priv(dev); - unsigned long dma_start; -#ifdef NE_SANITY_CHECK - int retries = 0; -#endif - - /* Round the count up for word writes. Do we need to do this? - What effect will an odd byte count have on the 8390? - I should check someday. */ - - if (ei_status.word16 && (count & 0x01)) - count++; - - /* This *shouldn't* happen. If it does, it's the last thing you'll see */ - if (ei_status.dmaing) - { - printk(KERN_EMERG "%s: DMAing conflict in ne_block_output." - "[DMAstat:%d][irqlock:%d]\n", - dev->name, ei_status.dmaing, ei_status.irqlock); - return; - } - ei_status.dmaing |= 0x01; - /* We should already be in page 0, but to be safe... */ - outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, NE_BASE + NE_CMD); - -#ifdef NE_SANITY_CHECK -retry: -#endif - -#ifdef NE8390_RW_BUGFIX - /* Handle the read-before-write bug the same way as the - Crynwr packet driver -- the NatSemi method doesn't work. - Actually this doesn't always work either, but if you have - problems with your NEx000 this is better than nothing! */ - - outb_p(0x42, NE_BASE + EN0_RCNTLO); - outb_p(0x00, NE_BASE + EN0_RCNTHI); - outb_p(0x42, NE_BASE + EN0_RSARLO); - outb_p(0x00, NE_BASE + EN0_RSARHI); - outb_p(E8390_RREAD+E8390_START, NE_BASE + NE_CMD); - /* Make certain that the dummy read has occurred. */ - udelay(6); -#endif - - outb_p(ENISR_RDC, NE_BASE + EN0_ISR); - - /* Now the normal output. */ - outb_p(count & 0xff, NE_BASE + EN0_RCNTLO); - outb_p(count >> 8, NE_BASE + EN0_RCNTHI); - outb_p(0x00, NE_BASE + EN0_RSARLO); - outb_p(start_page, NE_BASE + EN0_RSARHI); - - outb_p(E8390_RWRITE+E8390_START, NE_BASE + NE_CMD); - if (ei_status.word16) { - int len; - unsigned short *p = (unsigned short *)buf; - for (len = count>>1; len > 0; len--) - outw(*p++, NE_BASE + NE_DATAPORT); - } else { - outsb(NE_BASE + NE_DATAPORT, buf, count); - } - - dma_start = jiffies; - -#ifdef NE_SANITY_CHECK - /* This was for the ALPHA version only, but enough people have - been encountering problems so it is still here. */ - - if (ei_debug > 1) - { - /* DMA termination address check... */ - int addr, tries = 20; - do { - int high = inb_p(NE_BASE + EN0_RSARHI); - int low = inb_p(NE_BASE + EN0_RSARLO); - addr = (high << 8) + low; - if ((start_page << 8) + count == addr) - break; - } while (--tries > 0); - - if (tries <= 0) - { - printk(KERN_WARNING "%s: Tx packet transfer address mismatch," - "%#4.4x (expected) vs. %#4.4x (actual).\n", - dev->name, (start_page << 8) + count, addr); - if (retries++ == 0) - goto retry; - } - } -#endif - - while ((inb_p(NE_BASE + EN0_ISR) & ENISR_RDC) == 0) - if (time_after(jiffies, dma_start + 2*HZ/100)) { /* 20ms */ - printk(KERN_WARNING "%s: timeout waiting for Tx RDC.\n", dev->name); - ne_reset_8390(dev); - __NS8390_init(dev,1); - break; - } - - outb_p(ENISR_RDC, NE_BASE + EN0_ISR); /* Ack intr. */ - ei_status.dmaing &= ~0x01; -} - - -#ifdef MODULE -#define MAX_NE_CARDS 1 /* Max number of NE cards per module */ -static struct net_device *dev_ne[MAX_NE_CARDS]; -static int io[MAX_NE_CARDS]; -static int irq[MAX_NE_CARDS]; -static int bad[MAX_NE_CARDS]; /* 0xbad = bad sig or no reset ack */ - -module_param_array(io, int, NULL, 0); -module_param_array(irq, int, NULL, 0); -module_param_array(bad, int, NULL, 0); -MODULE_PARM_DESC(io, "I/O base address(es)"); -MODULE_PARM_DESC(irq, "IRQ number(s)"); -MODULE_DESCRIPTION("H8/300 NE2000 Ethernet driver"); -MODULE_LICENSE("GPL"); - -/* This is set up so that no ISA autoprobe takes place. We can't guarantee -that the ne2k probe is the last 8390 based probe to take place (as it -is at boot) and so the probe will get confused by any other 8390 cards. -ISA device autoprobes on a running machine are not recommended anyway. */ - -int init_module(void) -{ - int this_dev, found = 0; - int err; - - for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) { - struct net_device *dev = ____alloc_ei_netdev(0); - if (!dev) - break; - if (io[this_dev]) { - dev->irq = irq[this_dev]; - dev->mem_end = bad[this_dev]; - dev->base_addr = io[this_dev]; - } else { - dev->base_addr = h8300_ne_base[this_dev]; - dev->irq = h8300_ne_irq[this_dev]; - } - err = init_reg_offset(dev, dev->base_addr); - if (!err) { - if (do_ne_probe(dev) == 0) { - dev_ne[found++] = dev; - continue; - } - } - free_netdev(dev); - if (found) - break; - if (io[this_dev] != 0) - printk(KERN_WARNING "ne.c: No NE*000 card found at i/o = %#x\n", dev->base_addr); - else - printk(KERN_NOTICE "ne.c: You must supply \"io=0xNNN\" value(s) for ISA cards.\n"); - return -ENXIO; - } - if (found) - return 0; - return -ENODEV; -} - -void cleanup_module(void) -{ - int this_dev; - - for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) { - struct net_device *dev = dev_ne[this_dev]; - if (dev) { - unregister_netdev(dev); - cleanup_card(dev); - free_netdev(dev); - } - } -} -#endif /* MODULE */ -- cgit v0.10.2 From 1700a449fec27c1cdabd9352c0da2c6ddf0bbaea Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 30 Aug 2013 06:09:56 -0700 Subject: watchdog: Drop references to H8300 architecture Architecture is gone. Cc: Yoshinori Sato Acked-by: Greg Kroah-Hartman Acked-by: Wim Van Sebroeck Signed-off-by: Guenter Roeck diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index d1d53f3..6df632e 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -418,8 +418,6 @@ config BFIN_WDT # FRV Architecture -# H8300 Architecture - # X86 (i386 + ia64 + x86_64) Architecture config ACQUIRE_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 6c5bb27..8c7b8bc 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -66,8 +66,6 @@ obj-$(CONFIG_BFIN_WDT) += bfin_wdt.o # FRV Architecture -# H8300 Architecture - # X86 (i386 + ia64 + x86_64) Architecture obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o -- cgit v0.10.2 From 1c927e75f4022368dd2551b153c902b449909ac5 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 30 Aug 2013 06:12:13 -0700 Subject: Drop MAINTAINERS entry for H8/300 The architecture is gone. Cc: Yoshinori Sato Acked-by: Greg Kroah-Hartman Signed-off-by: Guenter Roeck diff --git a/MAINTAINERS b/MAINTAINERS index e61c2e8..6d0dabe 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8570,14 +8570,6 @@ S: Maintained F: arch/m68k/*/*_no.* F: arch/m68k/include/asm/*_no.* -UCLINUX FOR RENESAS H8/300 (H8300) -M: Yoshinori Sato -W: http://uclinux-h8.sourceforge.jp/ -S: Supported -F: arch/h8300/ -F: drivers/ide/ide-h8300.c -F: drivers/net/ethernet/8390/ne-h8300.c - UDF FILESYSTEM M: Jan Kara S: Maintained -- cgit v0.10.2 From 6f6f467eaaa0ac4fb77714d0172d65c781dabb8c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 30 Aug 2013 06:12:40 -0700 Subject: Drop remaining references to H8/300 architecture With the architecture gone, any references to it are no longer needed. Cc: Yoshinori Sato Acked-by: Greg Kroah-Hartman Signed-off-by: Guenter Roeck diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h index 75cef3f..db0b825 100644 --- a/include/uapi/linux/audit.h +++ b/include/uapi/linux/audit.h @@ -329,7 +329,6 @@ enum { #define AUDIT_ARCH_ARMEB (EM_ARM) #define AUDIT_ARCH_CRIS (EM_CRIS|__AUDIT_ARCH_LE) #define AUDIT_ARCH_FRV (EM_FRV) -#define AUDIT_ARCH_H8300 (EM_H8_300) #define AUDIT_ARCH_I386 (EM_386|__AUDIT_ARCH_LE) #define AUDIT_ARCH_IA64 (EM_IA_64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE) #define AUDIT_ARCH_M32R (EM_M32R) diff --git a/include/uapi/linux/elf-em.h b/include/uapi/linux/elf-em.h index 59c17a2..01529bd 100644 --- a/include/uapi/linux/elf-em.h +++ b/include/uapi/linux/elf-em.h @@ -31,7 +31,6 @@ #define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ #define EM_V850 87 /* NEC v850 */ #define EM_M32R 88 /* Renesas M32R */ -#define EM_H8_300 46 /* Renesas H8/300,300H,H8S */ #define EM_MN10300 89 /* Panasonic/MEI MN10300, AM33 */ #define EM_BLACKFIN 106 /* ADI Blackfin Processor */ #define EM_TI_C6000 140 /* TI C6X DSPs */ -- cgit v0.10.2 From ca40e295018f7df46c7b8a12c3e82b88418322fb Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 30 Aug 2013 09:52:51 -0700 Subject: fs/minix: Drop dependency on H8300 The H8/300 architecture is gone. Drop dependencies on it. Cc: Yoshinori Sato Acked-by: Greg Kroah-Hartman Signed-off-by: Guenter Roeck diff --git a/fs/minix/Kconfig b/fs/minix/Kconfig index 6624684..f2a0cfc 100644 --- a/fs/minix/Kconfig +++ b/fs/minix/Kconfig @@ -18,7 +18,7 @@ config MINIX_FS config MINIX_FS_NATIVE_ENDIAN def_bool MINIX_FS - depends on H8300 || M32R || MICROBLAZE || MIPS || S390 || SUPERH || SPARC || XTENSA || (M68K && !MMU) + depends on M32R || MICROBLAZE || MIPS || S390 || SUPERH || SPARC || XTENSA || (M68K && !MMU) config MINIX_FS_BIG_ENDIAN_16BIT_INDEXED def_bool MINIX_FS -- cgit v0.10.2 From 94c69f765f1b4a658d96905ec59928e3e3e07e6a Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 10 Sep 2013 15:43:41 +0800 Subject: spi: bitbang: Let spi_bitbang_start() take a reference to master Many drivers that use bitbang library have a leak on probe error paths. This is because once a spi_master_get() call succeeds, we need an additional spi_master_put() call to free the memory. Fix this issue by moving the code taking a reference to master to spi_bitbang_start(), so spi_bitbang_start() will take a reference to master on success. With this change, the caller is responsible for calling spi_bitbang_stop() to decrement the reference and spi_master_put() as counterpart of spi_alloc_master() to prevent a memory leak. So now we have below patten for drivers using bitbang library: probe: spi_alloc_master -> Init reference count to 1 spi_bitbang_start -> Increment reference count remove: spi_bitbang_stop -> Decrement reference count spi_master_put -> Decrement reference count (reference count reaches 0) Fixup all users accordingly. Signed-off-by: Axel Lin Suggested-by: Uwe Kleine-Koenig Acked-by: Uwe Kleine-Koenig Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-altera.c b/drivers/spi/spi-altera.c index 9a64c3f..595b62c 100644 --- a/drivers/spi/spi-altera.c +++ b/drivers/spi/spi-altera.c @@ -219,7 +219,7 @@ static int altera_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, hw); /* setup the state for the bitbang driver */ - hw->bitbang.master = spi_master_get(master); + hw->bitbang.master = master; if (!hw->bitbang.master) return err; hw->bitbang.chipselect = altera_spi_chipsel; diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index 37bad95..821bf7a 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -231,7 +231,7 @@ static int ath79_spi_probe(struct platform_device *pdev) master->num_chipselect = pdata->num_chipselect; } - sp->bitbang.master = spi_master_get(master); + sp->bitbang.master = master; sp->bitbang.chipselect = ath79_spi_chipselect; sp->bitbang.txrx_word[SPI_MODE_0] = ath79_spi_txrx_mode0; sp->bitbang.setup_transfer = spi_bitbang_setup_transfer; diff --git a/drivers/spi/spi-au1550.c b/drivers/spi/spi-au1550.c index 1d00d9b3..e06400a 100644 --- a/drivers/spi/spi-au1550.c +++ b/drivers/spi/spi-au1550.c @@ -775,7 +775,7 @@ static int au1550_spi_probe(struct platform_device *pdev) hw = spi_master_get_devdata(master); - hw->master = spi_master_get(master); + hw->master = master; hw->pdata = dev_get_platdata(&pdev->dev); hw->dev = &pdev->dev; diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c index 8c11355..0056623 100644 --- a/drivers/spi/spi-bitbang.c +++ b/drivers/spi/spi-bitbang.c @@ -414,10 +414,16 @@ static int spi_bitbang_unprepare_hardware(struct spi_master *spi) * This routine registers the spi_master, which will process requests in a * dedicated task, keeping IRQs unblocked most of the time. To stop * processing those requests, call spi_bitbang_stop(). + * + * On success, this routine will take a reference to master. The caller is + * responsible for calling spi_bitbang_stop() to decrement the reference and + * spi_master_put() as counterpart of spi_alloc_master() to prevent a memory + * leak. */ int spi_bitbang_start(struct spi_bitbang *bitbang) { struct spi_master *master = bitbang->master; + int ret; if (!master || !bitbang->chipselect) return -EINVAL; @@ -449,7 +455,11 @@ int spi_bitbang_start(struct spi_bitbang *bitbang) /* driver may get busy before register() returns, especially * if someone registered boardinfo for devices */ - return spi_register_master(master); + ret = spi_register_master(spi_master_get(master)); + if (ret) + spi_master_put(master); + + return 0; } EXPORT_SYMBOL_GPL(spi_bitbang_start); diff --git a/drivers/spi/spi-butterfly.c b/drivers/spi/spi-butterfly.c index 5ed08e5..b1ecea2 100644 --- a/drivers/spi/spi-butterfly.c +++ b/drivers/spi/spi-butterfly.c @@ -225,7 +225,7 @@ static void butterfly_attach(struct parport *p) master->bus_num = 42; master->num_chipselect = 2; - pp->bitbang.master = spi_master_get(master); + pp->bitbang.master = master; pp->bitbang.chipselect = butterfly_chipselect; pp->bitbang.txrx_word[SPI_MODE_0] = butterfly_txrx_word_mode0; diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index 8fbfe24..af2bb73 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -916,7 +916,7 @@ static int davinci_spi_probe(struct platform_device *pdev) if (ret) goto unmap_io; - dspi->bitbang.master = spi_master_get(master); + dspi->bitbang.master = master; if (dspi->bitbang.master == NULL) { ret = -ENODEV; goto irq_free; @@ -925,7 +925,7 @@ static int davinci_spi_probe(struct platform_device *pdev) dspi->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(dspi->clk)) { ret = -ENODEV; - goto put_master; + goto irq_free; } clk_prepare_enable(dspi->clk); @@ -1015,8 +1015,6 @@ free_dma: free_clk: clk_disable_unprepare(dspi->clk); clk_put(dspi->clk); -put_master: - spi_master_put(master); irq_free: free_irq(dspi->irq, dspi); unmap_io: @@ -1024,7 +1022,7 @@ unmap_io: release_region: release_mem_region(dspi->pbase, resource_size(r)); free_master: - kfree(master); + spi_master_put(master); err: return ret; } @@ -1051,11 +1049,11 @@ static int davinci_spi_remove(struct platform_device *pdev) clk_disable_unprepare(dspi->clk); clk_put(dspi->clk); - spi_master_put(master); free_irq(dspi->irq, dspi); iounmap(dspi->base); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(dspi->pbase, resource_size(r)); + spi_master_put(master); return 0; } diff --git a/drivers/spi/spi-efm32.c b/drivers/spi/spi-efm32.c index 7d84418..d428a40 100644 --- a/drivers/spi/spi-efm32.c +++ b/drivers/spi/spi-efm32.c @@ -347,7 +347,7 @@ static int efm32_spi_probe(struct platform_device *pdev) ddata = spi_master_get_devdata(master); - ddata->bitbang.master = spi_master_get(master); + ddata->bitbang.master = master; ddata->bitbang.chipselect = efm32_spi_chipselect; ddata->bitbang.setup_transfer = efm32_spi_setup_transfer; ddata->bitbang.txrx_bufs = efm32_spi_txrx_bufs; diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 6cd07d1..d338b67 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -450,7 +450,7 @@ static int dspi_probe(struct platform_device *pdev) dspi = spi_master_get_devdata(master); dspi->pdev = pdev; - dspi->bitbang.master = spi_master_get(master); + dspi->bitbang.master = master; dspi->bitbang.chipselect = dspi_chipselect; dspi->bitbang.setup_transfer = dspi_setup_transfer; dspi->bitbang.txrx_bufs = dspi_txrx_transfer; diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index 68b69fe..14c01b4 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -467,7 +467,7 @@ static int spi_gpio_probe(struct platform_device *pdev) } #endif - spi_gpio->bitbang.master = spi_master_get(master); + spi_gpio->bitbang.master = master; spi_gpio->bitbang.chipselect = spi_gpio_chipselect; if ((master_flags & (SPI_MASTER_NO_TX | SPI_MASTER_NO_RX)) == 0) { @@ -486,7 +486,6 @@ static int spi_gpio_probe(struct platform_device *pdev) status = spi_bitbang_start(&spi_gpio->bitbang); if (status < 0) { - spi_master_put(spi_gpio->bitbang.master); gpio_free: if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO) gpio_free(SPI_MISO_GPIO); @@ -510,13 +509,13 @@ static int spi_gpio_remove(struct platform_device *pdev) /* stop() unregisters child devices too */ status = spi_bitbang_stop(&spi_gpio->bitbang); - spi_master_put(spi_gpio->bitbang.master); if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO) gpio_free(SPI_MISO_GPIO); if (SPI_MOSI_GPIO != SPI_GPIO_NO_MOSI) gpio_free(SPI_MOSI_GPIO); gpio_free(SPI_SCK_GPIO); + spi_master_put(spi_gpio->bitbang.master); return status; } diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 15323d8..02d9f46 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -786,7 +786,7 @@ static int spi_imx_probe(struct platform_device *pdev) master->num_chipselect = num_cs; spi_imx = spi_master_get_devdata(master); - spi_imx->bitbang.master = spi_master_get(master); + spi_imx->bitbang.master = master; for (i = 0; i < master->num_chipselect; i++) { int cs_gpio = of_get_named_gpio(np, "cs-gpios", i); diff --git a/drivers/spi/spi-lm70llp.c b/drivers/spi/spi-lm70llp.c index 0759b5d..41c5765 100644 --- a/drivers/spi/spi-lm70llp.c +++ b/drivers/spi/spi-lm70llp.c @@ -222,7 +222,7 @@ static void spi_lm70llp_attach(struct parport *p) /* * SPI and bitbang hookup. */ - pp->bitbang.master = spi_master_get(master); + pp->bitbang.master = master; pp->bitbang.chipselect = lm70_chipselect; pp->bitbang.txrx_word[SPI_MODE_0] = lm70_txrx; pp->bitbang.flags = SPI_3WIRE; diff --git a/drivers/spi/spi-nuc900.c b/drivers/spi/spi-nuc900.c index 47a68b4..e0c32bc 100644 --- a/drivers/spi/spi-nuc900.c +++ b/drivers/spi/spi-nuc900.c @@ -349,7 +349,7 @@ static int nuc900_spi_probe(struct platform_device *pdev) } hw = spi_master_get_devdata(master); - hw->master = spi_master_get(master); + hw->master = master; hw->pdata = dev_get_platdata(&pdev->dev); hw->dev = &pdev->dev; @@ -435,7 +435,6 @@ err_iomap: kfree(hw->ioarea); err_pdata: spi_master_put(hw->master); - err_nomem: return err; } diff --git a/drivers/spi/spi-oc-tiny.c b/drivers/spi/spi-oc-tiny.c index 333cb1b..91c6685 100644 --- a/drivers/spi/spi-oc-tiny.c +++ b/drivers/spi/spi-oc-tiny.c @@ -306,7 +306,7 @@ static int tiny_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, hw); /* setup the state for the bitbang driver */ - hw->bitbang.master = spi_master_get(master); + hw->bitbang.master = master; if (!hw->bitbang.master) return err; hw->bitbang.setup_transfer = tiny_spi_setup_transfer; diff --git a/drivers/spi/spi-ppc4xx.c b/drivers/spi/spi-ppc4xx.c index 0ee53c2..c57740bb7 100644 --- a/drivers/spi/spi-ppc4xx.c +++ b/drivers/spi/spi-ppc4xx.c @@ -396,7 +396,7 @@ static int spi_ppc4xx_of_probe(struct platform_device *op) master->dev.of_node = np; platform_set_drvdata(op, master); hw = spi_master_get_devdata(master); - hw->master = spi_master_get(master); + hw->master = master; hw->dev = dev; init_completion(&hw->done); @@ -558,6 +558,7 @@ static int spi_ppc4xx_of_remove(struct platform_device *op) free_irq(hw->irqnum, hw); iounmap(hw->regs); free_gpios(hw); + spi_master_put(master); return 0; } diff --git a/drivers/spi/spi-s3c24xx.c b/drivers/spi/spi-s3c24xx.c index ce318d9..c9cfdfa 100644 --- a/drivers/spi/spi-s3c24xx.c +++ b/drivers/spi/spi-s3c24xx.c @@ -524,7 +524,7 @@ static int s3c24xx_spi_probe(struct platform_device *pdev) hw = spi_master_get_devdata(master); memset(hw, 0, sizeof(struct s3c24xx_spi)); - hw->master = spi_master_get(master); + hw->master = master; hw->pdata = pdata = dev_get_platdata(&pdev->dev); hw->dev = &pdev->dev; diff --git a/drivers/spi/spi-sh-sci.c b/drivers/spi/spi-sh-sci.c index 8eefeb6..38eb24d 100644 --- a/drivers/spi/spi-sh-sci.c +++ b/drivers/spi/spi-sh-sci.c @@ -133,7 +133,7 @@ static int sh_sci_spi_probe(struct platform_device *dev) sp->info = dev_get_platdata(&dev->dev); /* setup spi bitbang adaptor */ - sp->bitbang.master = spi_master_get(master); + sp->bitbang.master = master; sp->bitbang.master->bus_num = sp->info->bus_num; sp->bitbang.master->num_chipselect = sp->info->num_chipselect; sp->bitbang.chipselect = sh_sci_spi_chipselect; diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index a1f21b7..592b4af 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -632,7 +632,7 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) if (ret) goto free_master; - sspi->bitbang.master = spi_master_get(master); + sspi->bitbang.master = master; sspi->bitbang.chipselect = spi_sirfsoc_chipselect; sspi->bitbang.setup_transfer = spi_sirfsoc_setup_transfer; sspi->bitbang.txrx_bufs = spi_sirfsoc_transfer; diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c index 0bf1b2c..ec3a83f 100644 --- a/drivers/spi/spi-xilinx.c +++ b/drivers/spi/spi-xilinx.c @@ -372,7 +372,7 @@ static int xilinx_spi_probe(struct platform_device *pdev) master->mode_bits = SPI_CPOL | SPI_CPHA; xspi = spi_master_get_devdata(master); - xspi->bitbang.master = spi_master_get(master); + xspi->bitbang.master = master; xspi->bitbang.chipselect = xilinx_spi_chipselect; xspi->bitbang.setup_transfer = xilinx_spi_setup_transfer; xspi->bitbang.txrx_bufs = xilinx_spi_txrx_bufs; -- cgit v0.10.2 From 9191546f69ddefe66e46bbe9ba8d1872bc226553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 10 Sep 2013 09:51:30 +0200 Subject: spi: efm32: add spi_bitbang_stop to device remove callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This call is needed to cleanup the resources requested by spi_bitbang_start in the probe callback. Noticed-by: Axel Lin Signed-off-by: Uwe Kleine-König Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-efm32.c b/drivers/spi/spi-efm32.c index d428a40..18659fc 100644 --- a/drivers/spi/spi-efm32.c +++ b/drivers/spi/spi-efm32.c @@ -478,6 +478,8 @@ static int efm32_spi_remove(struct platform_device *pdev) struct spi_master *master = platform_get_drvdata(pdev); struct efm32_spi_ddata *ddata = spi_master_get_devdata(master); + spi_bitbang_stop(&ddata->bitbang); + efm32_spi_write32(ddata, 0, REG_IEN); free_irq(ddata->txirq, ddata); -- cgit v0.10.2 From d191bd8de8c61619563f2b19f1fdcc0944ff1a72 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 4 Sep 2013 19:39:03 -0700 Subject: ASoC: snd_soc_codec includes snd_soc_component Codec includes component by this patch, and component moved to upside of codec to avoid extra declaration. Codec dai will be registered via component by this patch. Current component register function is used for cpu, and it is using dai/dais functions properly to keep existing cpu dai name. And now, it will be used from codec also. But codec driver had been used dais function only even though it was single dai. This patch adds new flag which can selects dai/dais function on component register function to keep existing codec dai name. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index d22cb0a..9a81e2e 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -670,6 +670,21 @@ struct snd_soc_cache_ops { int (*sync)(struct snd_soc_codec *codec); }; +/* component interface */ +struct snd_soc_component_driver { + const char *name; +}; + +struct snd_soc_component { + const char *name; + int id; + int num_dai; + struct device *dev; + struct list_head list; + + const struct snd_soc_component_driver *driver; +}; + /* SoC Audio Codec device */ struct snd_soc_codec { const char *name; @@ -715,6 +730,9 @@ struct snd_soc_codec { struct mutex cache_rw_mutex; int val_bytes; + /* component */ + struct snd_soc_component component; + /* dapm */ struct snd_soc_dapm_context dapm; unsigned int ignore_pmdown_time:1; /* pmdown_time is ignored at stop */ @@ -733,6 +751,7 @@ struct snd_soc_codec_driver { int (*remove)(struct snd_soc_codec *); int (*suspend)(struct snd_soc_codec *); int (*resume)(struct snd_soc_codec *); + struct snd_soc_component_driver component_driver; /* Default control and setup, added after probe() is run */ const struct snd_kcontrol_new *controls; @@ -849,20 +868,6 @@ struct snd_soc_platform { #endif }; -struct snd_soc_component_driver { - const char *name; -}; - -struct snd_soc_component { - const char *name; - int id; - int num_dai; - struct device *dev; - struct list_head list; - - const struct snd_soc_component_driver *driver; -}; - struct snd_soc_dai_link { /* config - must be set by machine driver */ const char *name; /* Codec name */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4d05613..014ac10 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4021,6 +4021,112 @@ static void snd_soc_unregister_dais(struct device *dev, size_t count) } /** + * snd_soc_register_component - Register a component with the ASoC core + * + */ +static int +__snd_soc_register_component(struct device *dev, + struct snd_soc_component *cmpnt, + const struct snd_soc_component_driver *cmpnt_drv, + struct snd_soc_dai_driver *dai_drv, + int num_dai, bool allow_single_dai) +{ + int ret; + + dev_dbg(dev, "component register %s\n", dev_name(dev)); + + if (!cmpnt) { + dev_err(dev, "ASoC: Failed to connecting component\n"); + return -ENOMEM; + } + + cmpnt->name = fmt_single_name(dev, &cmpnt->id); + if (!cmpnt->name) { + dev_err(dev, "ASoC: Failed to simplifying name\n"); + return -ENOMEM; + } + + cmpnt->dev = dev; + cmpnt->driver = cmpnt_drv; + cmpnt->num_dai = num_dai; + + /* + * snd_soc_register_dai() uses fmt_single_name(), and + * snd_soc_register_dais() uses fmt_multiple_name() + * for dai->name which is used for name based matching + * + * this function is used from cpu/codec. + * allow_single_dai flag can ignore "codec" driver reworking + * since it had been used snd_soc_register_dais(), + */ + if ((1 == num_dai) && allow_single_dai) + ret = snd_soc_register_dai(dev, dai_drv); + else + ret = snd_soc_register_dais(dev, dai_drv, num_dai); + if (ret < 0) { + dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret); + goto error_component_name; + } + + mutex_lock(&client_mutex); + list_add(&cmpnt->list, &component_list); + mutex_unlock(&client_mutex); + + dev_dbg(cmpnt->dev, "ASoC: Registered component '%s'\n", cmpnt->name); + + return ret; + +error_component_name: + kfree(cmpnt->name); + + return ret; +} + +int snd_soc_register_component(struct device *dev, + const struct snd_soc_component_driver *cmpnt_drv, + struct snd_soc_dai_driver *dai_drv, + int num_dai) +{ + struct snd_soc_component *cmpnt; + + cmpnt = devm_kzalloc(dev, sizeof(*cmpnt), GFP_KERNEL); + if (!cmpnt) { + dev_err(dev, "ASoC: Failed to allocate memory\n"); + return -ENOMEM; + } + + return __snd_soc_register_component(dev, cmpnt, cmpnt_drv, + dai_drv, num_dai, true); +} +EXPORT_SYMBOL_GPL(snd_soc_register_component); + +/** + * snd_soc_unregister_component - Unregister a component from the ASoC core + * + */ +void snd_soc_unregister_component(struct device *dev) +{ + struct snd_soc_component *cmpnt; + + list_for_each_entry(cmpnt, &component_list, list) { + if (dev == cmpnt->dev) + goto found; + } + return; + +found: + snd_soc_unregister_dais(dev, cmpnt->num_dai); + + mutex_lock(&client_mutex); + list_del(&cmpnt->list); + mutex_unlock(&client_mutex); + + dev_dbg(dev, "ASoC: Unregistered component '%s'\n", cmpnt->name); + kfree(cmpnt->name); +} +EXPORT_SYMBOL_GPL(snd_soc_unregister_component); + +/** * snd_soc_add_platform - Add a platform to the ASoC core * @dev: The parent device for the platform * @platform: The platform to add @@ -4242,10 +4348,12 @@ int snd_soc_register_codec(struct device *dev, list_add(&codec->list, &codec_list); mutex_unlock(&client_mutex); - /* register any DAIs */ - ret = snd_soc_register_dais(dev, dai_drv, num_dai); + /* register component */ + ret = __snd_soc_register_component(dev, &codec->component, + &codec_drv->component_driver, + dai_drv, num_dai, false); if (ret < 0) { - dev_err(codec->dev, "ASoC: Failed to regster DAIs: %d\n", ret); + dev_err(codec->dev, "ASoC: Failed to regster component: %d\n", ret); goto fail_codec_name; } @@ -4280,7 +4388,7 @@ void snd_soc_unregister_codec(struct device *dev) return; found: - snd_soc_unregister_dais(dev, codec->num_dai); + snd_soc_unregister_component(dev); mutex_lock(&client_mutex); list_del(&codec->list); @@ -4295,92 +4403,6 @@ found: } EXPORT_SYMBOL_GPL(snd_soc_unregister_codec); - -/** - * snd_soc_register_component - Register a component with the ASoC core - * - */ -int snd_soc_register_component(struct device *dev, - const struct snd_soc_component_driver *cmpnt_drv, - struct snd_soc_dai_driver *dai_drv, - int num_dai) -{ - struct snd_soc_component *cmpnt; - int ret; - - dev_dbg(dev, "component register %s\n", dev_name(dev)); - - cmpnt = devm_kzalloc(dev, sizeof(*cmpnt), GFP_KERNEL); - if (!cmpnt) { - dev_err(dev, "ASoC: Failed to allocate memory\n"); - return -ENOMEM; - } - - cmpnt->name = fmt_single_name(dev, &cmpnt->id); - if (!cmpnt->name) { - dev_err(dev, "ASoC: Failed to simplifying name\n"); - return -ENOMEM; - } - - cmpnt->dev = dev; - cmpnt->driver = cmpnt_drv; - cmpnt->num_dai = num_dai; - - /* - * snd_soc_register_dai() uses fmt_single_name(), and - * snd_soc_register_dais() uses fmt_multiple_name() - * for dai->name which is used for name based matching - */ - if (1 == num_dai) - ret = snd_soc_register_dai(dev, dai_drv); - else - ret = snd_soc_register_dais(dev, dai_drv, num_dai); - if (ret < 0) { - dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret); - goto error_component_name; - } - - mutex_lock(&client_mutex); - list_add(&cmpnt->list, &component_list); - mutex_unlock(&client_mutex); - - dev_dbg(cmpnt->dev, "ASoC: Registered component '%s'\n", cmpnt->name); - - return ret; - -error_component_name: - kfree(cmpnt->name); - - return ret; -} -EXPORT_SYMBOL_GPL(snd_soc_register_component); - -/** - * snd_soc_unregister_component - Unregister a component from the ASoC core - * - */ -void snd_soc_unregister_component(struct device *dev) -{ - struct snd_soc_component *cmpnt; - - list_for_each_entry(cmpnt, &component_list, list) { - if (dev == cmpnt->dev) - goto found; - } - return; - -found: - snd_soc_unregister_dais(dev, cmpnt->num_dai); - - mutex_lock(&client_mutex); - list_del(&cmpnt->list); - mutex_unlock(&client_mutex); - - dev_dbg(dev, "ASoC: Unregistered component '%s'\n", cmpnt->name); - kfree(cmpnt->name); -} -EXPORT_SYMBOL_GPL(snd_soc_unregister_component); - /* Retrieve a card's name from device tree */ int snd_soc_of_parse_card_name(struct snd_soc_card *card, const char *propname) -- cgit v0.10.2 From cb470087669a3fab1958fec79dd7db280b33f178 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 10 Sep 2013 17:39:56 -0700 Subject: ASoC: add .of_xlate_dai_name on snd_soc_component_driver ASoC sound driver requires CPU/CODEC drivers for probing, and each CPU/CODEC has some DAI on it. Then, "dai name matching" have been used to identify CPU-CODEC DAI pair on ASoC. But, the "dai port number matching" is now required from DeviceTree. The solution of this issue is to replace the dai port number into dai name. Now, CPU/CODEC are based on struct snd_soc_component, and it can care above as common issue. This patch adds .of_xlate_dai_name callback interface on struct snd_soc_component_driver, and snd_soc_of_get_dai_name() which is using .of_xlate_dai_name. Then, #sound-dai-cells which enables DAI specifier is required on CPU/CODEC device tree properties. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index 9a81e2e..1dd7dc5 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -13,6 +13,7 @@ #ifndef __LINUX_SND_SOC_H #define __LINUX_SND_SOC_H +#include #include #include #include @@ -673,6 +674,11 @@ struct snd_soc_cache_ops { /* component interface */ struct snd_soc_component_driver { const char *name; + + /* DT */ + int (*of_xlate_dai_name)(struct snd_soc_component *component, + struct of_phandle_args *args, + const char **dai_name); }; struct snd_soc_component { @@ -1206,6 +1212,8 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, const char *propname); unsigned int snd_soc_of_parse_daifmt(struct device_node *np, const char *prefix); +int snd_soc_of_get_dai_name(struct device_node *of_node, + const char **dai_name); #include diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 014ac10..711bd36 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4590,6 +4590,41 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np, } EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt); +int snd_soc_of_get_dai_name(struct device_node *of_node, + const char **dai_name) +{ + struct snd_soc_component *pos; + struct of_phandle_args args; + int ret; + + ret = of_parse_phandle_with_args(of_node, "sound-dai", + "#sound-dai-cells", 0, &args); + if (ret) + return ret; + + ret = -EPROBE_DEFER; + + mutex_lock(&client_mutex); + list_for_each_entry(pos, &component_list, list) { + if (pos->dev->of_node != args.np) + continue; + + if (!pos->driver->of_xlate_dai_name) { + ret = -ENOSYS; + break; + } + + ret = pos->driver->of_xlate_dai_name(pos, &args, dai_name); + break; + } + mutex_unlock(&client_mutex); + + of_node_put(args.np); + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_name); + static int __init snd_soc_init(void) { #ifdef CONFIG_DEBUG_FS -- cgit v0.10.2 From e19bcb6b95c0326ca364814b86b32799aa7e20db Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 13 Sep 2013 15:52:42 +0530 Subject: ASoC: fsl_spdif: Remove redundant semicolon Redundant semicolon at the end of brace is removed. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 3920c3e..c0fea02 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -963,7 +963,7 @@ static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg) return true; default: return false; - }; + } } static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg) @@ -982,7 +982,7 @@ static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg) return true; default: return false; - }; + } } static const struct regmap_config fsl_spdif_regmap_config = { -- cgit v0.10.2 From feb8f1147618ebf20ab3e5efc143ceb621063f81 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 13 Sep 2013 15:59:25 +0530 Subject: ASoC: fsl_ssi: Remove redundant dev_set_drvdata Driver core sets the driver data to NULL on detach. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 4973be7..6ac8730 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1135,7 +1135,6 @@ static int fsl_ssi_remove(struct platform_device *pdev) if (ssi_private->ssi_on_imx) imx_pcm_dma_exit(pdev); snd_soc_unregister_component(&pdev->dev); - dev_set_drvdata(&pdev->dev, NULL); device_remove_file(&pdev->dev, &ssi_private->dev_attr); if (ssi_private->ssi_on_imx) clk_disable_unprepare(ssi_private->clk); -- cgit v0.10.2 From 5c21d008713918bcc4289e6ccb327b17b35dc4ef Mon Sep 17 00:00:00 2001 From: George Cherian Date: Wed, 4 Sep 2013 12:33:01 +0530 Subject: gpio: pcf857x: change to devm_request_threaded_irq Remove the request_irq and use devm_request_threaded_irq also cleanup free_irq. devm_* takes care of that. Signed-off-by: George Cherian Acked-by: Kuninori Morimoto Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 9e61bb0..2535322 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -168,6 +168,25 @@ static int pcf857x_to_irq(struct gpio_chip *chip, unsigned offset) return irq_create_mapping(gpio->irq_domain, offset); } +static irqreturn_t pcf857x_irq(int irq, void *data) +{ + struct pcf857x *gpio = data; + unsigned long change, i, status, flags; + + status = gpio->read(gpio->client); + + spin_lock_irqsave(&gpio->slock, flags); + + change = gpio->status ^ status; + for_each_set_bit(i, &change, gpio->chip.ngpio) + generic_handle_irq(irq_find_mapping(gpio->irq_domain, i)); + gpio->status = status; + + spin_unlock_irqrestore(&gpio->slock, flags); + + return IRQ_HANDLED; +} + static void pcf857x_irq_demux_work(struct work_struct *work) { struct pcf857x *gpio = container_of(work, @@ -218,8 +237,6 @@ static void pcf857x_irq_domain_cleanup(struct pcf857x *gpio) if (gpio->irq_domain) irq_domain_remove(gpio->irq_domain); - if (gpio->irq) - free_irq(gpio->irq, gpio); } static int pcf857x_irq_domain_init(struct pcf857x *gpio, @@ -235,8 +252,11 @@ static int pcf857x_irq_domain_init(struct pcf857x *gpio, goto fail; /* enable real irq */ - status = request_irq(client->irq, pcf857x_irq_demux, 0, - dev_name(&client->dev), gpio); + status = devm_request_threaded_irq(&client->dev, client->irq, + NULL, pcf857x_irq, IRQF_ONESHOT | + IRQF_TRIGGER_FALLING, + dev_name(&client->dev), gpio); + if (status) goto fail; -- cgit v0.10.2 From 98ff4490900335586727fff6f613765a094680e4 Mon Sep 17 00:00:00 2001 From: George Cherian Date: Wed, 4 Sep 2013 12:33:02 +0530 Subject: gpio: pcf857x: remove the irq_demux_work and gpio->irq Now that we are using devm_request_threaded_irq no need for irq_demux_work and gpio->irq. Remove all its references. Signed-off-by: George Cherian Acked-by: Kuninori Morimoto Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 2535322..612fb8d 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -28,7 +28,6 @@ #include #include #include -#include static const struct i2c_device_id pcf857x_id[] = { @@ -66,12 +65,10 @@ struct pcf857x { struct gpio_chip chip; struct i2c_client *client; struct mutex lock; /* protect 'out' */ - struct work_struct work; /* irq demux work */ struct irq_domain *irq_domain; /* for irq demux */ spinlock_t slock; /* protect irq demux */ unsigned out; /* software latch */ unsigned status; /* current status */ - int irq; /* real irq number */ int (*write)(struct i2c_client *client, unsigned data); int (*read)(struct i2c_client *client); @@ -187,38 +184,6 @@ static irqreturn_t pcf857x_irq(int irq, void *data) return IRQ_HANDLED; } -static void pcf857x_irq_demux_work(struct work_struct *work) -{ - struct pcf857x *gpio = container_of(work, - struct pcf857x, - work); - unsigned long change, i, status, flags; - - status = gpio->read(gpio->client); - - spin_lock_irqsave(&gpio->slock, flags); - - change = gpio->status ^ status; - for_each_set_bit(i, &change, gpio->chip.ngpio) - generic_handle_irq(irq_find_mapping(gpio->irq_domain, i)); - gpio->status = status; - - spin_unlock_irqrestore(&gpio->slock, flags); -} - -static irqreturn_t pcf857x_irq_demux(int irq, void *data) -{ - struct pcf857x *gpio = data; - - /* - * pcf857x can't read/write data here, - * since i2c data access might go to sleep. - */ - schedule_work(&gpio->work); - - return IRQ_HANDLED; -} - static int pcf857x_irq_domain_map(struct irq_domain *domain, unsigned int virq, irq_hw_number_t hw) { @@ -261,9 +226,7 @@ static int pcf857x_irq_domain_init(struct pcf857x *gpio, goto fail; /* enable gpio_to_irq() */ - INIT_WORK(&gpio->work, pcf857x_irq_demux_work); gpio->chip.to_irq = pcf857x_to_irq; - gpio->irq = client->irq; return 0; -- cgit v0.10.2 From 21fd3cd1874a2ac85990b981a8ab449e9e4ef5b9 Mon Sep 17 00:00:00 2001 From: George Cherian Date: Wed, 4 Sep 2013 12:33:03 +0530 Subject: gpio: pcf857x: call the gpio user handler iff gpio_to_irq is done For pcf857x driver if the initial state is not set properly (proper n_latch is not passed), we get bad irq prints on console. We get this only for the first interrupt and doesnot repeat for further interrupts unles and until there are other gpio pins which are not flipping continously. following prints are seen on console. [ 40.983924] irq 0, desc: ce004080, depth: 1, count: 0, unhandled: 0 [ 40.990511] ->handle_irq(): c00aa538, handle_bad_irq+0x0/0x260 [ 40.996768] ->irq_data.chip(): c080b6ec, no_irq_chip+0x0/0x60 [ 41.002842] ->action(): (null) [ 41.006242] IRQ_NOPROBE set [ 41.009465] IRQ_NOREQUEST set Signed-off-by: George Cherian Acked-by: Kuninori Morimoto Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 612fb8d..815944d 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -69,6 +69,7 @@ struct pcf857x { spinlock_t slock; /* protect irq demux */ unsigned out; /* software latch */ unsigned status; /* current status */ + unsigned irq_mapped; /* mapped gpio irqs */ int (*write)(struct i2c_client *client, unsigned data); int (*read)(struct i2c_client *client); @@ -161,8 +162,13 @@ static void pcf857x_set(struct gpio_chip *chip, unsigned offset, int value) static int pcf857x_to_irq(struct gpio_chip *chip, unsigned offset) { struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); + int ret; - return irq_create_mapping(gpio->irq_domain, offset); + ret = irq_create_mapping(gpio->irq_domain, offset); + if (ret > 0) + gpio->irq_mapped |= (1 << offset); + + return ret; } static irqreturn_t pcf857x_irq(int irq, void *data) @@ -174,7 +180,12 @@ static irqreturn_t pcf857x_irq(int irq, void *data) spin_lock_irqsave(&gpio->slock, flags); - change = gpio->status ^ status; + /* + * call the interrupt handler iff gpio is used as + * interrupt source, just to avoid bad irqs + */ + + change = ((gpio->status ^ status) & gpio->irq_mapped); for_each_set_bit(i, &change, gpio->chip.ngpio) generic_handle_irq(irq_find_mapping(gpio->irq_domain, i)); gpio->status = status; @@ -187,9 +198,14 @@ static irqreturn_t pcf857x_irq(int irq, void *data) static int pcf857x_irq_domain_map(struct irq_domain *domain, unsigned int virq, irq_hw_number_t hw) { + struct pcf857x *gpio = domain->host_data; + irq_set_chip_and_handler(virq, &dummy_irq_chip, handle_level_irq); + set_irq_flags(virq, IRQF_VALID); + gpio->irq_mapped |= (1 << hw); + return 0; } @@ -212,7 +228,7 @@ static int pcf857x_irq_domain_init(struct pcf857x *gpio, gpio->irq_domain = irq_domain_add_linear(client->dev.of_node, gpio->chip.ngpio, &pcf857x_irq_domain_ops, - NULL); + gpio); if (!gpio->irq_domain) goto fail; -- cgit v0.10.2 From a0102375ee82db1e08324b1a21484854cf2c1677 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 1 Sep 2013 20:30:50 -0700 Subject: regmap: Add regmap_fields APIs Current Linux kernel is supporting regmap_field method and it is very useful feature. It needs one regmap_filed for one register access. OTOH, there is multi port device which has many same registers in the market. The difference for each register access is only its address offset. Current API needs many regmap_field for such device, but it is not good. This patch adds new regmap_fileds API which can care about multi port/offset access via regmap. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 57f7778..9010614 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -179,6 +179,9 @@ struct regmap_field { /* lsb */ unsigned int shift; unsigned int reg; + + unsigned int id_size; + unsigned int id_offset; }; #ifdef CONFIG_DEBUG_FS diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 285afa7..00152bf 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -821,6 +821,8 @@ static void regmap_field_init(struct regmap_field *rm_field, rm_field->reg = reg_field.reg; rm_field->shift = reg_field.lsb; rm_field->mask = ((BIT(field_bits) - 1) << reg_field.lsb); + rm_field->id_size = reg_field.id_size; + rm_field->id_offset = reg_field.id_offset; } /** @@ -1389,6 +1391,54 @@ int regmap_field_update_bits(struct regmap_field *field, unsigned int mask, unsi } EXPORT_SYMBOL_GPL(regmap_field_update_bits); +/** + * regmap_fields_write(): Write a value to a single register field with port ID + * + * @field: Register field to write to + * @id: port ID + * @val: Value to be written + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_fields_write(struct regmap_field *field, unsigned int id, + unsigned int val) +{ + if (id >= field->id_size) + return -EINVAL; + + return regmap_update_bits(field->regmap, + field->reg + (field->id_offset * id), + field->mask, val << field->shift); +} +EXPORT_SYMBOL_GPL(regmap_fields_write); + +/** + * regmap_fields_update_bits(): Perform a read/modify/write cycle + * on the register field + * + * @field: Register field to write to + * @id: port ID + * @mask: Bitmask to change + * @val: Value to be written + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_fields_update_bits(struct regmap_field *field, unsigned int id, + unsigned int mask, unsigned int val) +{ + if (id >= field->id_size) + return -EINVAL; + + mask = (mask << field->shift) & field->mask; + + return regmap_update_bits(field->regmap, + field->reg + (field->id_offset * id), + mask, val << field->shift); +} +EXPORT_SYMBOL_GPL(regmap_fields_update_bits); + /* * regmap_bulk_write(): Write multiple registers to the device * @@ -1697,6 +1747,39 @@ int regmap_field_read(struct regmap_field *field, unsigned int *val) EXPORT_SYMBOL_GPL(regmap_field_read); /** + * regmap_fields_read(): Read a value to a single register field with port ID + * + * @field: Register field to read from + * @id: port ID + * @val: Pointer to store read value + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_fields_read(struct regmap_field *field, unsigned int id, + unsigned int *val) +{ + int ret; + unsigned int reg_val; + + if (id >= field->id_size) + return -EINVAL; + + ret = regmap_read(field->regmap, + field->reg + (field->id_offset * id), + ®_val); + if (ret != 0) + return ret; + + reg_val &= field->mask; + reg_val >>= field->shift; + *val = reg_val; + + return ret; +} +EXPORT_SYMBOL_GPL(regmap_fields_read); + +/** * regmap_bulk_read(): Read multiple registers from the device * * @map: Register map to write to diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 4c8c20a..a12bea0 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -425,11 +425,15 @@ bool regmap_reg_in_ranges(unsigned int reg, * @reg: Offset of the register within the regmap bank * @lsb: lsb of the register field. * @reg: msb of the register field. + * @id_size: port size if it has some ports + * @id_offset: address offset for each ports */ struct reg_field { unsigned int reg; unsigned int lsb; unsigned int msb; + unsigned int id_size; + unsigned int id_offset; }; #define REG_FIELD(_reg, _lsb, _msb) { \ @@ -451,6 +455,13 @@ int regmap_field_write(struct regmap_field *field, unsigned int val); int regmap_field_update_bits(struct regmap_field *field, unsigned int mask, unsigned int val); +int regmap_fields_write(struct regmap_field *field, unsigned int id, + unsigned int val); +int regmap_fields_read(struct regmap_field *field, unsigned int id, + unsigned int *val); +int regmap_fields_update_bits(struct regmap_field *field, unsigned int id, + unsigned int mask, unsigned int val); + /** * Description of an IRQ for the generic regmap irq_chip. * -- cgit v0.10.2 From 524c10818c32ed343d5d8af49c04589ab21e64d4 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Tue, 3 Sep 2013 08:31:50 +0800 Subject: gpiolib-acpi: convert acpi_evaluate_object() to acpi_execute_simple_method() acpi_execute_simple_method() is a new ACPI API introduced to invoke an ACPI control method that has single integer parameter and no return value. Convert acpi_evaluate_object() to acpi_execute_simple_method() in drivers/gpio/gpiolib-acpi.c Signed-off-by: Zhang Rui Acked-by: Mika Westerberg Acked-by: Mathias Nyman Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 5c1ef2b..f2beb72 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -73,15 +73,8 @@ static irqreturn_t acpi_gpio_irq_handler(int irq, void *data) static irqreturn_t acpi_gpio_irq_handler_evt(int irq, void *data) { struct acpi_gpio_evt_pin *evt_pin = data; - struct acpi_object_list args; - union acpi_object arg; - arg.type = ACPI_TYPE_INTEGER; - arg.integer.value = evt_pin->pin; - args.count = 1; - args.pointer = &arg; - - acpi_evaluate_object(evt_pin->evt_handle, NULL, &args, NULL); + acpi_execute_simple_method(evt_pin->evt_handle, NULL, evt_pin->pin); return IRQ_HANDLED; } -- cgit v0.10.2 From 072188b61c9b7aedaa15c46226b537345644beee Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 1 Sep 2013 20:31:16 -0700 Subject: ASoC: rsnd: gen: rsnd_gen_ops cares .probe and .remove Current rsnd_gen_ops didn't care about .probe and .remove functions, but it was not good sense. This patch tidyup it Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index babb203..331fc55 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -11,6 +11,11 @@ #include "rsnd.h" struct rsnd_gen_ops { + int (*probe)(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv); + void (*remove)(struct platform_device *pdev, + struct rsnd_priv *priv); int (*path_init)(struct rsnd_priv *priv, struct rsnd_dai *rdai, struct rsnd_dai_stream *io); @@ -98,11 +103,6 @@ static int rsnd_gen1_path_exit(struct rsnd_priv *priv, return ret; } -static struct rsnd_gen_ops rsnd_gen1_ops = { - .path_init = rsnd_gen1_path_init, - .path_exit = rsnd_gen1_path_exit, -}; - #define RSND_GEN1_REG_MAP(g, s, i, oi, oa) \ do { \ (g)->reg_map[RSND_REG_##i].index = RSND_GEN1_##s; \ @@ -163,7 +163,6 @@ static int rsnd_gen1_probe(struct platform_device *pdev, IS_ERR(gen->base[RSND_GEN1_SSI])) return -ENODEV; - gen->ops = &rsnd_gen1_ops; rsnd_gen1_reg_map_init(gen); dev_dbg(dev, "Gen1 device probed\n"); @@ -183,6 +182,13 @@ static void rsnd_gen1_remove(struct platform_device *pdev, { } +static struct rsnd_gen_ops rsnd_gen1_ops = { + .probe = rsnd_gen1_probe, + .remove = rsnd_gen1_remove, + .path_init = rsnd_gen1_path_init, + .path_exit = rsnd_gen1_path_exit, +}; + /* * Gen */ @@ -251,6 +257,14 @@ int rsnd_gen_probe(struct platform_device *pdev, return -ENOMEM; } + if (rsnd_is_gen1(priv)) + gen->ops = &rsnd_gen1_ops; + + if (!gen->ops) { + dev_err(dev, "unknown generation R-Car sound device\n"); + return -ENODEV; + } + priv->gen = gen; /* @@ -261,20 +275,13 @@ int rsnd_gen_probe(struct platform_device *pdev, for (i = 0; i < RSND_REG_MAX; i++) gen->reg_map[i].index = -1; - /* - * init each module - */ - if (rsnd_is_gen1(priv)) - return rsnd_gen1_probe(pdev, info, priv); - - dev_err(dev, "unknown generation R-Car sound device\n"); - - return -ENODEV; + return gen->ops->probe(pdev, info, priv); } void rsnd_gen_remove(struct platform_device *pdev, struct rsnd_priv *priv) { - if (rsnd_is_gen1(priv)) - rsnd_gen1_remove(pdev, priv); + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + gen->ops->remove(pdev, priv); } -- cgit v0.10.2 From 233bcb411cd32d15c4d04271fa06ca8f2dc24eb8 Mon Sep 17 00:00:00 2001 From: Elad Wexler Date: Thu, 12 Sep 2013 13:28:54 +0300 Subject: clocksource: Fix 'ret' data type of sysfs_override_clocksource() and sysfs_unbind_clocksource() sysfs_override_clocksource(): The expression 'if (ret >= 0)' is always true. This will cause clocksource_select() to always run. Thus modified ret to be of type ssize_t. sysfs_unbind_clocksource(): The expression 'if (ret < 0)' is always false. So in case sysfs_get_uname() failed, the expression won't take an effect. Thus modified ret to be of type ssize_t. Signed-off-by: Elad Wexler Signed-off-by: John Stultz diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index 64cf63c..c9317e1 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -940,7 +940,7 @@ static ssize_t sysfs_override_clocksource(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - size_t ret; + ssize_t ret; mutex_lock(&clocksource_mutex); @@ -968,7 +968,7 @@ static ssize_t sysfs_unbind_clocksource(struct device *dev, { struct clocksource *cs; char name[CS_NAME_LEN]; - size_t ret; + ssize_t ret; ret = sysfs_get_uname(buf, name, count); if (ret < 0) -- cgit v0.10.2 From bcf25567ecec6cb0a8078cbf68969baed047fdf4 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Tue, 17 Sep 2013 12:26:00 +0300 Subject: ASoC: davinci-evm: Move sysclk logic away from evm_hw_params The sysclk rate does not change runtime so it should be initialized at init time. Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c index fd7c45b..2f8161c 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/davinci/davinci-evm.c @@ -27,6 +27,10 @@ #include "davinci-i2s.h" #include "davinci-mcasp.h" +struct snd_soc_card_drvdata_davinci { + unsigned sysclk; +}; + #define AUDIO_FORMAT (SND_SOC_DAIFMT_DSP_B | \ SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_NF) static int evm_hw_params(struct snd_pcm_substream *substream, @@ -35,27 +39,11 @@ static int evm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_card *soc_card = codec->card; int ret = 0; - unsigned sysclk; - - /* ASP1 on DM355 EVM is clocked by an external oscillator */ - if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm() || - machine_is_davinci_dm365_evm()) - sysclk = 27000000; - - /* ASP0 in DM6446 EVM is clocked by U55, as configured by - * board-dm644x-evm.c using GPIOs from U18. There are six - * options; here we "know" we use a 48 KHz sample rate. - */ - else if (machine_is_davinci_evm()) - sysclk = 12288000; - - else if (machine_is_davinci_da830_evm() || - machine_is_davinci_da850_evm()) - sysclk = 24576000; - - else - return -EINVAL; + unsigned sysclk = ((struct snd_soc_card_drvdata_davinci *) + snd_soc_card_get_drvdata(soc_card))->sysclk; /* set codec DAI configuration */ ret = snd_soc_dai_set_fmt(codec_dai, AUDIO_FORMAT); @@ -243,35 +231,65 @@ static struct snd_soc_dai_link da850_evm_dai = { }; /* davinci dm6446 evm audio machine driver */ +/* + * ASP0 in DM6446 EVM is clocked by U55, as configured by + * board-dm644x-evm.c using GPIOs from U18. There are six + * options; here we "know" we use a 48 KHz sample rate. + */ +static struct snd_soc_card_drvdata_davinci dm6446_snd_soc_card_drvdata = { + .sysclk = 12288000, +}; + static struct snd_soc_card dm6446_snd_soc_card_evm = { .name = "DaVinci DM6446 EVM", .owner = THIS_MODULE, .dai_link = &dm6446_evm_dai, .num_links = 1, + .drvdata = &dm6446_snd_soc_card_drvdata, }; /* davinci dm355 evm audio machine driver */ +/* ASP1 on DM355 EVM is clocked by an external oscillator */ +static struct snd_soc_card_drvdata_davinci dm355_snd_soc_card_drvdata = { + .sysclk = 27000000, +}; + static struct snd_soc_card dm355_snd_soc_card_evm = { .name = "DaVinci DM355 EVM", .owner = THIS_MODULE, .dai_link = &dm355_evm_dai, .num_links = 1, + .drvdata = &dm355_snd_soc_card_drvdata, }; /* davinci dm365 evm audio machine driver */ +static struct snd_soc_card_drvdata_davinci dm365_snd_soc_card_drvdata = { + .sysclk = 27000000, +}; + static struct snd_soc_card dm365_snd_soc_card_evm = { .name = "DaVinci DM365 EVM", .owner = THIS_MODULE, .dai_link = &dm365_evm_dai, .num_links = 1, + .drvdata = &dm365_snd_soc_card_drvdata, }; /* davinci dm6467 evm audio machine driver */ +static struct snd_soc_card_drvdata_davinci dm6467_snd_soc_card_drvdata = { + .sysclk = 27000000, +}; + static struct snd_soc_card dm6467_snd_soc_card_evm = { .name = "DaVinci DM6467 EVM", .owner = THIS_MODULE, .dai_link = dm6467_evm_dai, .num_links = ARRAY_SIZE(dm6467_evm_dai), + .drvdata = &dm6467_snd_soc_card_drvdata, +}; + +static struct snd_soc_card_drvdata_davinci da830_snd_soc_card_drvdata = { + .sysclk = 24576000, }; static struct snd_soc_card da830_snd_soc_card = { @@ -279,6 +297,11 @@ static struct snd_soc_card da830_snd_soc_card = { .owner = THIS_MODULE, .dai_link = &da830_evm_dai, .num_links = 1, + .drvdata = &da830_snd_soc_card_drvdata, +}; + +static struct snd_soc_card_drvdata_davinci da850_snd_soc_card_drvdata = { + .sysclk = 24576000, }; static struct snd_soc_card da850_snd_soc_card = { @@ -286,6 +309,7 @@ static struct snd_soc_card da850_snd_soc_card = { .owner = THIS_MODULE, .dai_link = &da850_evm_dai, .num_links = 1, + .drvdata = &da850_snd_soc_card_drvdata, }; static struct platform_device *evm_snd_device; -- cgit v0.10.2 From 91800f0e90050a4db4c77e940796f501e02af8be Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 31 Aug 2013 18:55:53 +0100 Subject: spi/s3c64xx: Use managed registration Also improve the error reporting on failure and remove a duplicate put. This provides a small code saving. Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 20dd712..8bed27a 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1428,9 +1428,9 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) S3C64XX_SPI_INT_TX_OVERRUN_EN | S3C64XX_SPI_INT_TX_UNDERRUN_EN, sdd->regs + S3C64XX_SPI_INT_EN); - if (spi_register_master(master)) { - dev_err(&pdev->dev, "cannot register SPI master\n"); - ret = -EBUSY; + ret = devm_spi_register_master(&pdev->dev, master); + if (ret != 0) { + dev_err(&pdev->dev, "cannot register SPI master: %d\n", ret); goto err3; } @@ -1461,16 +1461,12 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); - spi_unregister_master(master); - writel(0, sdd->regs + S3C64XX_SPI_INT_EN); clk_disable_unprepare(sdd->src_clk); clk_disable_unprepare(sdd->clk); - spi_master_put(master); - return 0; } -- cgit v0.10.2 From 5fb7680bd0035525eb1534001f7b7f2ca06a8ab7 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 17 Sep 2013 10:41:36 +0530 Subject: ASoC: SPEAr spdif_in: Use devm_snd_soc_register_component devm_snd_soc_register_component makes code simpler. Signed-off-by: Sachin Kamat Acked-by: Viresh Kumar Signed-off-by: Mark Brown diff --git a/sound/soc/spear/spdif_in.c b/sound/soc/spear/spdif_in.c index 63acfeb..21a8c95 100644 --- a/sound/soc/spear/spdif_in.c +++ b/sound/soc/spear/spdif_in.c @@ -257,20 +257,12 @@ static int spdif_in_probe(struct platform_device *pdev) return ret; } - return snd_soc_register_component(&pdev->dev, &spdif_in_component, - &spdif_in_dai, 1); -} - -static int spdif_in_remove(struct platform_device *pdev) -{ - snd_soc_unregister_component(&pdev->dev); - - return 0; + return devm_snd_soc_register_component(&pdev->dev, &spdif_in_component, + &spdif_in_dai, 1); } static struct platform_driver spdif_in_driver = { .probe = spdif_in_probe, - .remove = spdif_in_remove, .driver = { .name = "spdif-in", .owner = THIS_MODULE, -- cgit v0.10.2 From 77aea716872fd976cbb9706b4588cf1fb9d52826 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 17 Sep 2013 10:41:37 +0530 Subject: ASoC: SPEAr spdif_out: Use devm_snd_soc_register_component devm_snd_soc_register_component makes code simpler. Signed-off-by: Sachin Kamat Acked-by: Viresh Kumar Signed-off-by: Mark Brown diff --git a/sound/soc/spear/spdif_out.c b/sound/soc/spear/spdif_out.c index 2fdf68c..70fc4d6 100644 --- a/sound/soc/spear/spdif_out.c +++ b/sound/soc/spear/spdif_out.c @@ -307,18 +307,11 @@ static int spdif_out_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, host); - ret = snd_soc_register_component(&pdev->dev, &spdif_out_component, - &spdif_out_dai, 1); + ret = devm_snd_soc_register_component(&pdev->dev, &spdif_out_component, + &spdif_out_dai, 1); return ret; } -static int spdif_out_remove(struct platform_device *pdev) -{ - snd_soc_unregister_component(&pdev->dev); - - return 0; -} - #ifdef CONFIG_PM static int spdif_out_suspend(struct device *dev) { @@ -357,7 +350,6 @@ static SIMPLE_DEV_PM_OPS(spdif_out_dev_pm_ops, spdif_out_suspend, \ static struct platform_driver spdif_out_driver = { .probe = spdif_out_probe, - .remove = spdif_out_remove, .driver = { .name = "spdif-out", .owner = THIS_MODULE, -- cgit v0.10.2 From c27769ef6e118d355922d9c4a530b9d68405b39b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 18 Sep 2013 13:12:29 +0200 Subject: gpio: pcf857x: only use set_irq_flags() on ARM As per the pattern from other GPIO drivers, use set_irq_flags() on ARM only, use irq_set_noprobe() on other archs. Also rename the argument "virq" to just "irq", this IRQ isn't any more "virtual" than any other Linux IRQ number, we use "hwirq" for the actual hw-numbers, "virq" is just bogus. Cc: George Cherian Cc: Kuninori Morimoto Reviewed-by: Felipe Balbi Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 815944d..54725a6 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -195,15 +195,19 @@ static irqreturn_t pcf857x_irq(int irq, void *data) return IRQ_HANDLED; } -static int pcf857x_irq_domain_map(struct irq_domain *domain, unsigned int virq, +static int pcf857x_irq_domain_map(struct irq_domain *domain, unsigned int irq, irq_hw_number_t hw) { struct pcf857x *gpio = domain->host_data; - irq_set_chip_and_handler(virq, + irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_level_irq); - set_irq_flags(virq, IRQF_VALID); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + irq_set_noprobe(irq); +#endif gpio->irq_mapped |= (1 << hw); return 0; -- cgit v0.10.2 From be7c5926c22403b6f5895a10a73145925dd560a9 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Tue, 17 Sep 2013 12:26:06 +0300 Subject: ASoC: tlv320aic3x: Add regulators to DT bindings document Add regulator properties to tlv320aic3x DT bindings document. Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/tlv320aic3x.txt b/Documentation/devicetree/bindings/sound/tlv320aic3x.txt index 705a6b1..ba26477 100644 --- a/Documentation/devicetree/bindings/sound/tlv320aic3x.txt +++ b/Documentation/devicetree/bindings/sound/tlv320aic3x.txt @@ -24,10 +24,17 @@ Optional properties: 3 - MICBIAS output is connected to AVDD, If this node is not mentioned or if the value is incorrect, then MicBias is powered down. +- AVDD-supply, IOVDD-supply, DRVDD-supply, DVDD-supply : power supplies for the + device as covered in Documentation/devicetree/bindings/regulator/regulator.txt Example: tlv320aic3x: tlv320aic3x@1b { compatible = "ti,tlv320aic3x"; reg = <0x1b>; + + AVDD-supply = <®ulator>; + IOVDD-supply = <®ulator>; + DRVDD-supply = <®ulator>; + DVDD-supply = <®ulator>; }; -- cgit v0.10.2 From c9f3de41b93ae61da4cd10611e446a4b916245d2 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Tue, 17 Sep 2013 12:26:07 +0300 Subject: ASoC: tlv320aic3x: Add codec pins to DT bindings document Add list of codec pins to tlv320aic3x DT bindings document. Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/tlv320aic3x.txt b/Documentation/devicetree/bindings/sound/tlv320aic3x.txt index ba26477..5e6040c 100644 --- a/Documentation/devicetree/bindings/sound/tlv320aic3x.txt +++ b/Documentation/devicetree/bindings/sound/tlv320aic3x.txt @@ -27,6 +27,25 @@ Optional properties: - AVDD-supply, IOVDD-supply, DRVDD-supply, DVDD-supply : power supplies for the device as covered in Documentation/devicetree/bindings/regulator/regulator.txt +CODEC output pins: + * LLOUT + * RLOUT + * MONO_LOUT + * HPLOUT + * HPROUT + * HPLCOM + * HPRCOM + +CODEC input pins: + * MIC3L + * MIC3R + * LINE1L + * LINE2L + * LINE1R + * LINE2R + +The pins can be used in referring sound node's audio-routing property. + Example: tlv320aic3x: tlv320aic3x@1b { -- cgit v0.10.2 From 00c877c69ba315d6c565a4df51c71b11e82cdeb8 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 18 Sep 2013 18:18:02 +0530 Subject: regulator: core: add support for configuring turn-on time through constraints The turn-on time of the regulator depends on the regulator device's electrical characteristics. Sometimes regulator turn-on time also depends on the capacitive load on the given platform and it can be more than the datasheet value. The driver provides the enable-time as per datasheet. Add support for configure the enable ramp time through regulator constraints so that regulator core can take this value for enable time for that regulator. Signed-off-by: Laxman Dewangan Acked-by: Stephen Warren Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/regulator/regulator.txt b/Documentation/devicetree/bindings/regulator/regulator.txt index 2bd8f09..e2c7f1e 100644 --- a/Documentation/devicetree/bindings/regulator/regulator.txt +++ b/Documentation/devicetree/bindings/regulator/regulator.txt @@ -14,6 +14,11 @@ Optional properties: - regulator-ramp-delay: ramp delay for regulator(in uV/uS) For hardwares which support disabling ramp rate, it should be explicitly intialised to zero (regulator-ramp-delay = <0>) for disabling ramp delay. +- regulator-enable-ramp-delay: The time taken, in microseconds, for the supply + rail to reach the target voltage, plus/minus whatever tolerance the board + design requires. This property describes the total system ramp time + required due to the combination of internal ramping of the regulator itself, + and board design issues such as trace capacitance and load on the supply. Deprecated properties: - regulator-compatible: If a regulator chip contains multiple diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index a01b8b3..5217c19 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1186,6 +1186,8 @@ overflow_err: static int _regulator_get_enable_time(struct regulator_dev *rdev) { + if (rdev->constraints && rdev->constraints->enable_time) + return rdev->constraints->enable_time; if (!rdev->desc->ops->enable_time) return rdev->desc->enable_time; return rdev->desc->ops->enable_time(rdev); diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 7827384..ea4f36f 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -23,6 +23,8 @@ static void of_get_regulation_constraints(struct device_node *np, const __be32 *min_uA, *max_uA, *ramp_delay; struct property *prop; struct regulation_constraints *constraints = &(*init_data)->constraints; + int ret; + u32 pval; constraints->name = of_get_property(np, "regulator-name", NULL); @@ -73,6 +75,10 @@ static void of_get_regulation_constraints(struct device_node *np, else constraints->ramp_disable = true; } + + ret = of_property_read_u32(np, "regulator-enable-ramp-delay", &pval); + if (!ret) + constraints->enable_time = pval; } /** diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h index 999b20c..8108751 100644 --- a/include/linux/regulator/machine.h +++ b/include/linux/regulator/machine.h @@ -95,6 +95,7 @@ struct regulator_state { * @initial_state: Suspend state to set by default. * @initial_mode: Mode to set at startup. * @ramp_delay: Time to settle down after voltage change (unit: uV/us) + * @enable_time: Turn-on time of the rails (unit: microseconds) */ struct regulation_constraints { @@ -129,6 +130,7 @@ struct regulation_constraints { unsigned int initial_mode; unsigned int ramp_delay; + unsigned int enable_time; /* constraint flags */ unsigned always_on:1; /* regulator never off when system is on */ -- cgit v0.10.2 From 07783397c6e3757de805bda5e1139a9e47aa7d74 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Tue, 17 Sep 2013 11:20:21 -0700 Subject: rtc: rtc-pl031: Set wakeup flag prior to registering rtcdev In some recent testing, I noticed the CLOCK_REALTIME_ALARM clockid wasn't functioning on my vexpress qemu environment. Looking into it I noticed the pl031 rtc driver doesn't set the wakeup flag on the device until after registering the device with the RTC subsystem. This causes the alarmtimer subsystem to not see the pl031 driver as a valid backing device, and that resuls in alarm clockids getting ENOTSUPP errors. Thus be sure to set the wakeup flag on the device prior to registering the rtcdev so the pl031 rtc driver can be used as the backing alarmtimer device. Cc: Linus Walleij Cc: Alessandro Zummo Cc: "Jon Medhurst (Tixy)" Cc: linux-arm-kernel@lists.infradead.org Acked-by: Linus Walleij Signed-off-by: John Stultz diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c index 0f0609b..e3b2571 100644 --- a/drivers/rtc/rtc-pl031.c +++ b/drivers/rtc/rtc-pl031.c @@ -371,6 +371,7 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id) } } + device_init_wakeup(&adev->dev, 1); ldata->rtc = rtc_device_register("pl031", &adev->dev, ops, THIS_MODULE); if (IS_ERR(ldata->rtc)) { @@ -384,8 +385,6 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id) goto out_no_irq; } - device_init_wakeup(&adev->dev, 1); - return 0; out_no_irq: -- cgit v0.10.2 From 32fcb97b9f699f63742bcaadca6e0beede86e8e8 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 19 Sep 2013 11:18:06 +0200 Subject: ASoC: rt5640: Omit ACPI match table only if !ACPI The ACPI_PTR() macro evaluates to NULL if ACPI is disabled and hence the ACPI match table won't be used, causing the compiler to complain. Avoid this by protecting the table using an #ifdef CONFIG_ACPI. Signed-off-by: Thierry Reding Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 0bfb960..641eeeb 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -2082,11 +2082,13 @@ static const struct i2c_device_id rt5640_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, rt5640_i2c_id); +#ifdef CONFIG_ACPI static struct acpi_device_id rt5640_acpi_match[] = { { "INT33CA", 0 }, { }, }; MODULE_DEVICE_TABLE(acpi, rt5640_acpi_match); +#endif static int rt5640_parse_dt(struct rt5640_priv *rt5640, struct device_node *np) { -- cgit v0.10.2 From 25db0dc88016ec67ec4e38164482a3d7b7429f75 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 19 Sep 2013 12:23:44 +0530 Subject: ASoC: SPEAr spdif_out: Remove redundant variable Return directly and remove the intermediate local variable. Signed-off-by: Sachin Kamat Acked-by: Viresh Kumar Signed-off-by: Mark Brown diff --git a/sound/soc/spear/spdif_out.c b/sound/soc/spear/spdif_out.c index 70fc4d6..b6ef6f7 100644 --- a/sound/soc/spear/spdif_out.c +++ b/sound/soc/spear/spdif_out.c @@ -280,7 +280,6 @@ static int spdif_out_probe(struct platform_device *pdev) struct spdif_out_dev *host; struct spear_spdif_platform_data *pdata; struct resource *res; - int ret; host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); if (!host) { @@ -307,9 +306,8 @@ static int spdif_out_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, host); - ret = devm_snd_soc_register_component(&pdev->dev, &spdif_out_component, - &spdif_out_dai, 1); - return ret; + return devm_snd_soc_register_component(&pdev->dev, &spdif_out_component, + &spdif_out_dai, 1); } #ifdef CONFIG_PM -- cgit v0.10.2 From e9a03add0c6ed5341fc59ff9c76843c2888a33fa Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Tue, 3 Sep 2013 16:28:59 +0800 Subject: pinctrl: ADI PIN control driver for the GPIO controller on bf54x and bf60x. The new ADI GPIO2 controller was introduced since the BF548 and BF60x processors. It differs a lot from the old one on BF5xx processors. So, create a pinctrl driver under the pinctrl framework. - Define gpio ports and pin interrupt controllers as individual platform devices. - Register a pinctrl driver for the whole GPIO ports and pin interrupt devices. - Probe pint devices before port devices. Put device instances into the global gpio and pint lists. - Define peripheral, irq and gpio reservation bit masks for each gpio port as runtime resources. - Save and restore gpio port and pint status MMRs in syscore PM functions. - Create the plug-in subdrivers to hold the pinctrl soc data for bf54x and bf60x. Add soc data into struct adi_pinctrl. Initialize the soc data in pin controller probe function. Get the pin groups and functions via the soc data reference. - Call gpiochip_add_pin_range() in gpio device probe function to register range cross reference between gpio device and pin control device. - Get range by pinctrl_find_gpio_range_from_pin(), find gpio_port object by container_of() and find adi_pinctrl by pin control device name. - Handle peripheral and gpio requests in pinctrl operation functions. - Demux gpio IRQs via the irq_domain created by each GPIO port. v2-changes: - Remove unlinke() directive. v3-changes: - Rename struct adi_pmx to adi_pinctrl. - Fix the comments of struct gpio_pint. - Remove unused pin_base in struct gpio_port. - Change pint_assign into bool type. - Add comments about the relationship between pint device and port device to the driver header. - Use BIT macro to shift bit. - Remove all bitmap reservation help functions. Inline reservation functions into the actual code. - Remove gpio and offset mutual reference help functions. - Remove all help functions to find gpio_port and adi_pinctrl structs. Get range by pinctrl_find_gpio_range_from_pin(), find gpio_port object by container_of() and find adi_pinctrl by pin control device name. - Pass bool type usage variable to port_setup help function. - Separate long bit operations into several lines and add comments. - Use debugfs to output all GPIO request information. - Avoid to set drvdata to NULL - Add explanation to function adi_gpio_init_int() - Call gpiochip_add_pin_range() in gpio device probe function to register range cross reference between gpio device and pin control device. - Remove the reference to pin control device from the gpio_port struct. Remove the reference list to gpio device from the adi_pinctrl struct. Replace the global adi_pinctrl list with adi_gpio_port_list. Walk through the gpio list to do power suspend and resume operations. - Remove the global GPIO base from struct adi_pinctrl, define pin base in the platform data for each GPIO port device. - Initialize adi_pinctrl_setup in arch_initcall(). - print the status of triggers, whether it is in GPIO mode, if it is flagged to be used as IRQ, etc in adi_pin_dbg_show(). - Create the plug-in subdrivers to hold the pinctrl soc data for bf54x and bf60x. Add soc data into struct adi_pinctrl. Initialize the soc data in pin controller probe function. Get the pin groups and functions via the soc data reference. v4-changes: - remove useless system_state checking. - replace dev_err with dev_warn in both irq and gpio pin cases. - comment on relationship between irq type and invert operation. - It is not necessary to check the reservation mode of the requested pin in IRQ chip operation. Remove the reservation map. - Use existing gpio/pinctrl subsystem debugfs files. Remove pinctrl-adi2 driver specific debugfs output. - Add linkport group and function information for bf60x. - Separate uart and ctsrts pins into 2 groups. - Separate APAPI and alternative ATAPI pins into 2 groups. Signed-off-by: Sonic Zhang Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index b6e864e..f45a21c 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -49,6 +49,23 @@ config PINCTRL_AB8505 bool "AB8505 pin controller driver" depends on PINCTRL_ABX500 && ARCH_U8500 +config PINCTRL_ADI2 + bool "ADI pin controller driver" + select PINMUX + select IRQ_DOMAIN + help + This is the pin controller and gpio driver for ADI BF54x, BF60x and + future processors. This option is selected automatically when specific + machine and arch are selected to build. + +config PINCTRL_BF54x + def_bool y if BF54x + select PINCTRL_ADI2 + +config PINCTRL_BF60x + def_bool y if BF60x + select PINCTRL_ADI2 + config PINCTRL_AT91 bool "AT91 pinctrl driver" depends on OF diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 496d9bf..bbeb980 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -14,6 +14,9 @@ obj-$(CONFIG_PINCTRL_AB8500) += pinctrl-ab8500.o obj-$(CONFIG_PINCTRL_AB8540) += pinctrl-ab8540.o obj-$(CONFIG_PINCTRL_AB9540) += pinctrl-ab9540.o obj-$(CONFIG_PINCTRL_AB8505) += pinctrl-ab8505.o +obj-$(CONFIG_PINCTRL_ADI2) += pinctrl-adi2.o +obj-$(CONFIG_PINCTRL_BF54x) += pinctrl-adi2-bf54x.o +obj-$(CONFIG_PINCTRL_BF60x) += pinctrl-adi2-bf60x.o obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o obj-$(CONFIG_PINCTRL_BCM2835) += pinctrl-bcm2835.o obj-$(CONFIG_PINCTRL_BAYTRAIL) += pinctrl-baytrail.o diff --git a/drivers/pinctrl/pinctrl-adi2-bf54x.c b/drivers/pinctrl/pinctrl-adi2-bf54x.c new file mode 100644 index 0000000..ea9d9ab --- /dev/null +++ b/drivers/pinctrl/pinctrl-adi2-bf54x.c @@ -0,0 +1,592 @@ +/* + * Pinctrl Driver for ADI GPIO2 controller + * + * Copyright 2007-2013 Analog Devices Inc. + * + * Licensed under the GPLv2 or later + */ + +#include +#include "pinctrl-adi2.h" + +static const struct pinctrl_pin_desc adi_pads[] = { + PINCTRL_PIN(0, "PA0"), + PINCTRL_PIN(1, "PA1"), + PINCTRL_PIN(2, "PA2"), + PINCTRL_PIN(3, "PG3"), + PINCTRL_PIN(4, "PA4"), + PINCTRL_PIN(5, "PA5"), + PINCTRL_PIN(6, "PA6"), + PINCTRL_PIN(7, "PA7"), + PINCTRL_PIN(8, "PA8"), + PINCTRL_PIN(9, "PA9"), + PINCTRL_PIN(10, "PA10"), + PINCTRL_PIN(11, "PA11"), + PINCTRL_PIN(12, "PA12"), + PINCTRL_PIN(13, "PA13"), + PINCTRL_PIN(14, "PA14"), + PINCTRL_PIN(15, "PA15"), + PINCTRL_PIN(16, "PB0"), + PINCTRL_PIN(17, "PB1"), + PINCTRL_PIN(18, "PB2"), + PINCTRL_PIN(19, "PB3"), + PINCTRL_PIN(20, "PB4"), + PINCTRL_PIN(21, "PB5"), + PINCTRL_PIN(22, "PB6"), + PINCTRL_PIN(23, "PB7"), + PINCTRL_PIN(24, "PB8"), + PINCTRL_PIN(25, "PB9"), + PINCTRL_PIN(26, "PB10"), + PINCTRL_PIN(27, "PB11"), + PINCTRL_PIN(28, "PB12"), + PINCTRL_PIN(29, "PB13"), + PINCTRL_PIN(30, "PB14"), + PINCTRL_PIN(32, "PC0"), + PINCTRL_PIN(33, "PC1"), + PINCTRL_PIN(34, "PC2"), + PINCTRL_PIN(35, "PC3"), + PINCTRL_PIN(36, "PC4"), + PINCTRL_PIN(37, "PC5"), + PINCTRL_PIN(38, "PC6"), + PINCTRL_PIN(39, "PC7"), + PINCTRL_PIN(40, "PC8"), + PINCTRL_PIN(41, "PC9"), + PINCTRL_PIN(42, "PC10"), + PINCTRL_PIN(43, "PC11"), + PINCTRL_PIN(44, "PC12"), + PINCTRL_PIN(45, "PC13"), + PINCTRL_PIN(48, "PD0"), + PINCTRL_PIN(49, "PD1"), + PINCTRL_PIN(50, "PD2"), + PINCTRL_PIN(51, "PD3"), + PINCTRL_PIN(52, "PD4"), + PINCTRL_PIN(53, "PD5"), + PINCTRL_PIN(54, "PD6"), + PINCTRL_PIN(55, "PD7"), + PINCTRL_PIN(56, "PD8"), + PINCTRL_PIN(57, "PD9"), + PINCTRL_PIN(58, "PD10"), + PINCTRL_PIN(59, "PD11"), + PINCTRL_PIN(60, "PD12"), + PINCTRL_PIN(61, "PD13"), + PINCTRL_PIN(62, "PD14"), + PINCTRL_PIN(63, "PD15"), + PINCTRL_PIN(64, "PE0"), + PINCTRL_PIN(65, "PE1"), + PINCTRL_PIN(66, "PE2"), + PINCTRL_PIN(67, "PE3"), + PINCTRL_PIN(68, "PE4"), + PINCTRL_PIN(69, "PE5"), + PINCTRL_PIN(70, "PE6"), + PINCTRL_PIN(71, "PE7"), + PINCTRL_PIN(72, "PE8"), + PINCTRL_PIN(73, "PE9"), + PINCTRL_PIN(74, "PE10"), + PINCTRL_PIN(75, "PE11"), + PINCTRL_PIN(76, "PE12"), + PINCTRL_PIN(77, "PE13"), + PINCTRL_PIN(78, "PE14"), + PINCTRL_PIN(79, "PE15"), + PINCTRL_PIN(80, "PF0"), + PINCTRL_PIN(81, "PF1"), + PINCTRL_PIN(82, "PF2"), + PINCTRL_PIN(83, "PF3"), + PINCTRL_PIN(84, "PF4"), + PINCTRL_PIN(85, "PF5"), + PINCTRL_PIN(86, "PF6"), + PINCTRL_PIN(87, "PF7"), + PINCTRL_PIN(88, "PF8"), + PINCTRL_PIN(89, "PF9"), + PINCTRL_PIN(90, "PF10"), + PINCTRL_PIN(91, "PF11"), + PINCTRL_PIN(92, "PF12"), + PINCTRL_PIN(93, "PF13"), + PINCTRL_PIN(94, "PF14"), + PINCTRL_PIN(95, "PF15"), + PINCTRL_PIN(96, "PG0"), + PINCTRL_PIN(97, "PG1"), + PINCTRL_PIN(98, "PG2"), + PINCTRL_PIN(99, "PG3"), + PINCTRL_PIN(100, "PG4"), + PINCTRL_PIN(101, "PG5"), + PINCTRL_PIN(102, "PG6"), + PINCTRL_PIN(103, "PG7"), + PINCTRL_PIN(104, "PG8"), + PINCTRL_PIN(105, "PG9"), + PINCTRL_PIN(106, "PG10"), + PINCTRL_PIN(107, "PG11"), + PINCTRL_PIN(108, "PG12"), + PINCTRL_PIN(109, "PG13"), + PINCTRL_PIN(110, "PG14"), + PINCTRL_PIN(111, "PG15"), + PINCTRL_PIN(112, "PH0"), + PINCTRL_PIN(113, "PH1"), + PINCTRL_PIN(114, "PH2"), + PINCTRL_PIN(115, "PH3"), + PINCTRL_PIN(116, "PH4"), + PINCTRL_PIN(117, "PH5"), + PINCTRL_PIN(118, "PH6"), + PINCTRL_PIN(119, "PH7"), + PINCTRL_PIN(120, "PH8"), + PINCTRL_PIN(121, "PH9"), + PINCTRL_PIN(122, "PH10"), + PINCTRL_PIN(123, "PH11"), + PINCTRL_PIN(124, "PH12"), + PINCTRL_PIN(125, "PH13"), + PINCTRL_PIN(128, "PI0"), + PINCTRL_PIN(129, "PI1"), + PINCTRL_PIN(130, "PI2"), + PINCTRL_PIN(131, "PI3"), + PINCTRL_PIN(132, "PI4"), + PINCTRL_PIN(133, "PI5"), + PINCTRL_PIN(134, "PI6"), + PINCTRL_PIN(135, "PI7"), + PINCTRL_PIN(136, "PI8"), + PINCTRL_PIN(137, "PI9"), + PINCTRL_PIN(138, "PI10"), + PINCTRL_PIN(139, "PI11"), + PINCTRL_PIN(140, "PI12"), + PINCTRL_PIN(141, "PI13"), + PINCTRL_PIN(142, "PI14"), + PINCTRL_PIN(143, "PI15"), + PINCTRL_PIN(144, "PJ0"), + PINCTRL_PIN(145, "PJ1"), + PINCTRL_PIN(146, "PJ2"), + PINCTRL_PIN(147, "PJ3"), + PINCTRL_PIN(148, "PJ4"), + PINCTRL_PIN(149, "PJ5"), + PINCTRL_PIN(150, "PJ6"), + PINCTRL_PIN(151, "PJ7"), + PINCTRL_PIN(152, "PJ8"), + PINCTRL_PIN(153, "PJ9"), + PINCTRL_PIN(154, "PJ10"), + PINCTRL_PIN(155, "PJ11"), + PINCTRL_PIN(156, "PJ12"), + PINCTRL_PIN(157, "PJ13"), +}; + +static const unsigned uart0_pins[] = { + GPIO_PE7, GPIO_PE8, +}; + +static const unsigned uart1_pins[] = { + GPIO_PH0, GPIO_PH1, +}; + +static const unsigned uart1_ctsrts_pins[] = { + GPIO_PE9, GPIO_PE10, +}; + +static const unsigned uart2_pins[] = { + GPIO_PB4, GPIO_PB5, +}; + +static const unsigned uart3_pins[] = { + GPIO_PB6, GPIO_PB7, +}; + +static const unsigned uart3_ctsrts_pins[] = { + GPIO_PB2, GPIO_PB3, +}; + +static const unsigned rsi0_pins[] = { + GPIO_PC8, GPIO_PC9, GPIO_PC10, GPIO_PC11, GPIO_PC12, GPIO_PC13, +}; + +static const unsigned spi0_pins[] = { + GPIO_PE0, GPIO_PE1, GPIO_PE2, +}; + +static const unsigned spi1_pins[] = { + GPIO_PG8, GPIO_PG9, GPIO_PG10, +}; + +static const unsigned twi0_pins[] = { + GPIO_PE14, GPIO_PE15, +}; + +static const unsigned twi1_pins[] = { + GPIO_PB0, GPIO_PB1, +}; + +static const unsigned rotary_pins[] = { + GPIO_PH4, GPIO_PH3, GPIO_PH5, +}; + +static const unsigned can0_pins[] = { + GPIO_PG13, GPIO_PG12, +}; + +static const unsigned can1_pins[] = { + GPIO_PG14, GPIO_PG15, +}; + +static const unsigned smc0_pins[] = { + GPIO_PH8, GPIO_PH9, GPIO_PH10, GPIO_PH11, GPIO_PH12, GPIO_PH13, + GPIO_PI0, GPIO_PI1, GPIO_PI2, GPIO_PI3, GPIO_PI4, GPIO_PI5, GPIO_PI6, + GPIO_PI7, GPIO_PI8, GPIO_PI9, GPIO_PI10, GPIO_PI11, + GPIO_PI12, GPIO_PI13, GPIO_PI14, GPIO_PI15, +}; + +static const unsigned sport0_pins[] = { + GPIO_PC0, GPIO_PC2, GPIO_PC3, GPIO_PC4, GPIO_PC6, GPIO_PC7, +}; + +static const unsigned sport1_pins[] = { + GPIO_PD0, GPIO_PD2, GPIO_PD3, GPIO_PD4, GPIO_PD6, GPIO_PD7, +}; + +static const unsigned sport2_pins[] = { + GPIO_PA0, GPIO_PA2, GPIO_PA3, GPIO_PA4, GPIO_PA6, GPIO_PA7, +}; + +static const unsigned sport3_pins[] = { + GPIO_PA8, GPIO_PA10, GPIO_PA11, GPIO_PA12, GPIO_PA14, GPIO_PA15, +}; + +static const unsigned ppi0_8b_pins[] = { + GPIO_PF0, GPIO_PF1, GPIO_PF2, GPIO_PF3, GPIO_PF4, GPIO_PF5, GPIO_PF6, + GPIO_PF7, GPIO_PF13, GPIO_PG0, GPIO_PG1, GPIO_PG2, +}; + +static const unsigned ppi0_16b_pins[] = { + GPIO_PF0, GPIO_PF1, GPIO_PF2, GPIO_PF3, GPIO_PF4, GPIO_PF5, GPIO_PF6, + GPIO_PF7, GPIO_PF9, GPIO_PF10, GPIO_PF11, GPIO_PF12, + GPIO_PF13, GPIO_PF14, GPIO_PF15, + GPIO_PG0, GPIO_PG1, GPIO_PG2, +}; + +static const unsigned ppi0_24b_pins[] = { + GPIO_PF0, GPIO_PF1, GPIO_PF2, GPIO_PF3, GPIO_PF4, GPIO_PF5, GPIO_PF6, + GPIO_PF7, GPIO_PF8, GPIO_PF9, GPIO_PF10, GPIO_PF11, GPIO_PF12, + GPIO_PF13, GPIO_PF14, GPIO_PF15, GPIO_PD0, GPIO_PD1, GPIO_PD2, + GPIO_PD3, GPIO_PD4, GPIO_PD5, GPIO_PG3, GPIO_PG4, + GPIO_PG0, GPIO_PG1, GPIO_PG2, +}; + +static const unsigned ppi1_8b_pins[] = { + GPIO_PD0, GPIO_PD1, GPIO_PD2, GPIO_PD3, GPIO_PD4, GPIO_PD5, GPIO_PD6, + GPIO_PD7, GPIO_PE11, GPIO_PE12, GPIO_PE13, +}; + +static const unsigned ppi1_16b_pins[] = { + GPIO_PD0, GPIO_PD1, GPIO_PD2, GPIO_PD3, GPIO_PD4, GPIO_PD5, GPIO_PD6, + GPIO_PD7, GPIO_PD8, GPIO_PD9, GPIO_PD10, GPIO_PD11, GPIO_PD12, + GPIO_PD13, GPIO_PD14, GPIO_PD15, + GPIO_PE11, GPIO_PE12, GPIO_PE13, +}; + +static const unsigned ppi2_8b_pins[] = { + GPIO_PD8, GPIO_PD9, GPIO_PD10, GPIO_PD11, GPIO_PD12, + GPIO_PD13, GPIO_PD14, GPIO_PD15, + GPIO_PA7, GPIO_PB0, GPIO_PB1, GPIO_PB2, GPIO_PB3, +}; + +static const unsigned atapi_pins[] = { + GPIO_PH2, GPIO_PJ3, GPIO_PJ4, GPIO_PJ5, GPIO_PJ6, + GPIO_PJ7, GPIO_PJ8, GPIO_PJ9, GPIO_PJ10, +}; + +static const unsigned atapi_alter_pins[] = { + GPIO_PF0, GPIO_PF1, GPIO_PF2, GPIO_PF3, GPIO_PF4, GPIO_PF5, GPIO_PF6, + GPIO_PF7, GPIO_PF8, GPIO_PF9, GPIO_PF10, GPIO_PF11, GPIO_PF12, + GPIO_PF13, GPIO_PF14, GPIO_PF15, GPIO_PG2, GPIO_PG3, GPIO_PG4, +}; + +static const unsigned nfc0_pins[] = { + GPIO_PJ1, GPIO_PJ2, +}; + +static const unsigned keys_4x4_pins[] = { + GPIO_PD8, GPIO_PD9, GPIO_PD10, GPIO_PD11, + GPIO_PD12, GPIO_PD13, GPIO_PD14, GPIO_PD15, +}; + +static const unsigned keys_8x8_pins[] = { + GPIO_PD8, GPIO_PD9, GPIO_PD10, GPIO_PD11, + GPIO_PD12, GPIO_PD13, GPIO_PD14, GPIO_PD15, + GPIO_PE0, GPIO_PE1, GPIO_PE2, GPIO_PE3, + GPIO_PE4, GPIO_PE5, GPIO_PE6, GPIO_PE7, +}; + +static const struct adi_pin_group adi_pin_groups[] = { + ADI_PIN_GROUP("uart0grp", uart0_pins), + ADI_PIN_GROUP("uart1grp", uart1_pins), + ADI_PIN_GROUP("uart1ctsrtsgrp", uart1_ctsrts_pins), + ADI_PIN_GROUP("uart2grp", uart2_pins), + ADI_PIN_GROUP("uart3grp", uart3_pins), + ADI_PIN_GROUP("uart3ctsrtsgrp", uart3_ctsrts_pins), + ADI_PIN_GROUP("rsi0grp", rsi0_pins), + ADI_PIN_GROUP("spi0grp", spi0_pins), + ADI_PIN_GROUP("spi1grp", spi1_pins), + ADI_PIN_GROUP("twi0grp", twi0_pins), + ADI_PIN_GROUP("twi1grp", twi1_pins), + ADI_PIN_GROUP("rotarygrp", rotary_pins), + ADI_PIN_GROUP("can0grp", can0_pins), + ADI_PIN_GROUP("can1grp", can1_pins), + ADI_PIN_GROUP("smc0grp", smc0_pins), + ADI_PIN_GROUP("sport0grp", sport0_pins), + ADI_PIN_GROUP("sport1grp", sport1_pins), + ADI_PIN_GROUP("sport2grp", sport2_pins), + ADI_PIN_GROUP("sport3grp", sport3_pins), + ADI_PIN_GROUP("ppi0_8bgrp", ppi0_8b_pins), + ADI_PIN_GROUP("ppi0_16bgrp", ppi0_16b_pins), + ADI_PIN_GROUP("ppi0_24bgrp", ppi0_24b_pins), + ADI_PIN_GROUP("ppi1_8bgrp", ppi1_8b_pins), + ADI_PIN_GROUP("ppi1_16bgrp", ppi1_16b_pins), + ADI_PIN_GROUP("ppi2_8bgrp", ppi2_8b_pins), + ADI_PIN_GROUP("atapigrp", atapi_pins), + ADI_PIN_GROUP("atapialtergrp", atapi_alter_pins), + ADI_PIN_GROUP("nfc0grp", nfc0_pins), + ADI_PIN_GROUP("keys_4x4grp", keys_4x4_pins), + ADI_PIN_GROUP("keys_8x8grp", keys_8x8_pins), +}; + +static const unsigned short uart0_mux[] = { + P_UART0_TX, P_UART0_RX, + 0 +}; + +static const unsigned short uart1_mux[] = { + P_UART1_TX, P_UART1_RX, + 0 +}; + +static const unsigned short uart1_ctsrts_mux[] = { + P_UART1_RTS, P_UART1_CTS, + 0 +}; + +static const unsigned short uart2_mux[] = { + P_UART2_TX, P_UART2_RX, + 0 +}; + +static const unsigned short uart3_mux[] = { + P_UART3_TX, P_UART3_RX, + 0 +}; + +static const unsigned short uart3_ctsrts_mux[] = { + P_UART3_RTS, P_UART3_CTS, + 0 +}; + +static const unsigned short rsi0_mux[] = { + P_SD_D0, P_SD_D1, P_SD_D2, P_SD_D3, P_SD_CLK, P_SD_CMD, + 0 +}; + +static const unsigned short spi0_mux[] = { + P_SPI0_SCK, P_SPI0_MISO, P_SPI0_MOSI, 0 +}; + +static const unsigned short spi1_mux[] = { + P_SPI1_SCK, P_SPI1_MISO, P_SPI1_MOSI, 0 +}; + +static const unsigned short twi0_mux[] = { + P_TWI0_SCL, P_TWI0_SDA, 0 +}; + +static const unsigned short twi1_mux[] = { + P_TWI1_SCL, P_TWI1_SDA, 0 +}; + +static const unsigned short rotary_mux[] = { + P_CNT_CUD, P_CNT_CDG, P_CNT_CZM, 0 +}; + +static const unsigned short sport0_mux[] = { + P_SPORT0_TFS, P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, + P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0 +}; + +static const unsigned short sport1_mux[] = { + P_SPORT1_TFS, P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, + P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0 +}; + +static const unsigned short sport2_mux[] = { + P_SPORT2_TFS, P_SPORT2_DTPRI, P_SPORT2_TSCLK, P_SPORT2_RFS, + P_SPORT2_DRPRI, P_SPORT2_RSCLK, 0 +}; + +static const unsigned short sport3_mux[] = { + P_SPORT3_TFS, P_SPORT3_DTPRI, P_SPORT3_TSCLK, P_SPORT3_RFS, + P_SPORT3_DRPRI, P_SPORT3_RSCLK, 0 +}; + +static const unsigned short can0_mux[] = { + P_CAN0_RX, P_CAN0_TX, 0 +}; + +static const unsigned short can1_mux[] = { + P_CAN1_RX, P_CAN1_TX, 0 +}; + +static const unsigned short smc0_mux[] = { + P_A4, P_A5, P_A6, P_A7, P_A8, P_A9, P_A10, P_A11, P_A12, + P_A13, P_A14, P_A15, P_A16, P_A17, P_A18, P_A19, P_A20, P_A21, + P_A22, P_A23, P_A24, P_A25, P_NOR_CLK, 0, +}; + +static const unsigned short ppi0_8b_mux[] = { + P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, P_PPI0_D3, + P_PPI0_D4, P_PPI0_D5, P_PPI0_D6, P_PPI0_D7, + P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2, + 0, +}; + +static const unsigned short ppi0_16b_mux[] = { + P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, P_PPI0_D3, + P_PPI0_D4, P_PPI0_D5, P_PPI0_D6, P_PPI0_D7, + P_PPI0_D8, P_PPI0_D9, P_PPI0_D10, P_PPI0_D11, + P_PPI0_D12, P_PPI0_D13, P_PPI0_D14, P_PPI0_D15, + P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2, + 0, +}; + +static const unsigned short ppi0_24b_mux[] = { + P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, P_PPI0_D3, + P_PPI0_D4, P_PPI0_D5, P_PPI0_D6, P_PPI0_D7, + P_PPI0_D8, P_PPI0_D9, P_PPI0_D10, P_PPI0_D11, + P_PPI0_D12, P_PPI0_D13, P_PPI0_D14, P_PPI0_D15, + P_PPI0_D16, P_PPI0_D17, P_PPI0_D18, P_PPI0_D19, + P_PPI0_D20, P_PPI0_D21, P_PPI0_D22, P_PPI0_D23, + P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2, + 0, +}; + +static const unsigned short ppi1_8b_mux[] = { + P_PPI1_D0, P_PPI1_D1, P_PPI1_D2, P_PPI1_D3, + P_PPI1_D4, P_PPI1_D5, P_PPI1_D6, P_PPI1_D7, + P_PPI1_CLK, P_PPI1_FS1, P_PPI1_FS2, + 0, +}; + +static const unsigned short ppi1_16b_mux[] = { + P_PPI1_D0, P_PPI1_D1, P_PPI1_D2, P_PPI1_D3, + P_PPI1_D4, P_PPI1_D5, P_PPI1_D6, P_PPI1_D7, + P_PPI1_D8, P_PPI1_D9, P_PPI1_D10, P_PPI1_D11, + P_PPI1_D12, P_PPI1_D13, P_PPI1_D14, P_PPI1_D15, + P_PPI1_CLK, P_PPI1_FS1, P_PPI1_FS2, + 0, +}; + +static const unsigned short ppi2_8b_mux[] = { + P_PPI2_D0, P_PPI2_D1, P_PPI2_D2, P_PPI2_D3, + P_PPI2_D4, P_PPI2_D5, P_PPI2_D6, P_PPI2_D7, + P_PPI2_CLK, P_PPI2_FS1, P_PPI2_FS2, + 0, +}; + +static const unsigned short atapi_mux[] = { + P_ATAPI_RESET, P_ATAPI_DIOR, P_ATAPI_DIOW, P_ATAPI_CS0, P_ATAPI_CS1, + P_ATAPI_DMACK, P_ATAPI_DMARQ, P_ATAPI_INTRQ, P_ATAPI_IORDY, +}; + +static const unsigned short atapi_alter_mux[] = { + P_ATAPI_D0A, P_ATAPI_D1A, P_ATAPI_D2A, P_ATAPI_D3A, P_ATAPI_D4A, + P_ATAPI_D5A, P_ATAPI_D6A, P_ATAPI_D7A, P_ATAPI_D8A, P_ATAPI_D9A, + P_ATAPI_D10A, P_ATAPI_D11A, P_ATAPI_D12A, P_ATAPI_D13A, P_ATAPI_D14A, + P_ATAPI_D15A, P_ATAPI_A0A, P_ATAPI_A1A, P_ATAPI_A2A, + 0 +}; + +static const unsigned short nfc0_mux[] = { + P_NAND_CE, P_NAND_RB, + 0 +}; + +static const unsigned short keys_4x4_mux[] = { + P_KEY_ROW3, P_KEY_ROW2, P_KEY_ROW1, P_KEY_ROW0, + P_KEY_COL3, P_KEY_COL2, P_KEY_COL1, P_KEY_COL0, + 0 +}; + +static const unsigned short keys_8x8_mux[] = { + P_KEY_ROW7, P_KEY_ROW6, P_KEY_ROW5, P_KEY_ROW4, + P_KEY_ROW3, P_KEY_ROW2, P_KEY_ROW1, P_KEY_ROW0, + P_KEY_COL7, P_KEY_COL6, P_KEY_COL5, P_KEY_COL4, + P_KEY_COL3, P_KEY_COL2, P_KEY_COL1, P_KEY_COL0, + 0 +}; + +static const char * const uart0grp[] = { "uart0grp" }; +static const char * const uart1grp[] = { "uart1grp" }; +static const char * const uart1ctsrtsgrp[] = { "uart1ctsrtsgrp" }; +static const char * const uart2grp[] = { "uart2grp" }; +static const char * const uart3grp[] = { "uart3grp" }; +static const char * const uart3ctsrtsgrp[] = { "uart3ctsrtsgrp" }; +static const char * const rsi0grp[] = { "rsi0grp" }; +static const char * const spi0grp[] = { "spi0grp" }; +static const char * const spi1grp[] = { "spi1grp" }; +static const char * const twi0grp[] = { "twi0grp" }; +static const char * const twi1grp[] = { "twi1grp" }; +static const char * const rotarygrp[] = { "rotarygrp" }; +static const char * const can0grp[] = { "can0grp" }; +static const char * const can1grp[] = { "can1grp" }; +static const char * const smc0grp[] = { "smc0grp" }; +static const char * const sport0grp[] = { "sport0grp" }; +static const char * const sport1grp[] = { "sport1grp" }; +static const char * const sport2grp[] = { "sport2grp" }; +static const char * const sport3grp[] = { "sport3grp" }; +static const char * const ppi0_8bgrp[] = { "ppi0_8bgrp" }; +static const char * const ppi0_16bgrp[] = { "ppi0_16bgrp" }; +static const char * const ppi0_24bgrp[] = { "ppi0_24bgrp" }; +static const char * const ppi1_8bgrp[] = { "ppi1_8bgrp" }; +static const char * const ppi1_16bgrp[] = { "ppi1_16bgrp" }; +static const char * const ppi2_8bgrp[] = { "ppi2_8bgrp" }; +static const char * const atapigrp[] = { "atapigrp" }; +static const char * const atapialtergrp[] = { "atapialtergrp" }; +static const char * const nfc0grp[] = { "nfc0grp" }; +static const char * const keys_4x4grp[] = { "keys_4x4grp" }; +static const char * const keys_8x8grp[] = { "keys_8x8grp" }; + +static const struct adi_pmx_func adi_pmx_functions[] = { + ADI_PMX_FUNCTION("uart0", uart0grp, uart0_mux), + ADI_PMX_FUNCTION("uart1", uart1grp, uart1_mux), + ADI_PMX_FUNCTION("uart1_ctsrts", uart1ctsrtsgrp, uart1_ctsrts_mux), + ADI_PMX_FUNCTION("uart2", uart2grp, uart2_mux), + ADI_PMX_FUNCTION("uart3", uart3grp, uart3_mux), + ADI_PMX_FUNCTION("uart3_ctsrts", uart3ctsrtsgrp, uart3_ctsrts_mux), + ADI_PMX_FUNCTION("rsi0", rsi0grp, rsi0_mux), + ADI_PMX_FUNCTION("spi0", spi0grp, spi0_mux), + ADI_PMX_FUNCTION("spi1", spi1grp, spi1_mux), + ADI_PMX_FUNCTION("twi0", twi0grp, twi0_mux), + ADI_PMX_FUNCTION("twi1", twi1grp, twi1_mux), + ADI_PMX_FUNCTION("rotary", rotarygrp, rotary_mux), + ADI_PMX_FUNCTION("can0", can0grp, can0_mux), + ADI_PMX_FUNCTION("can1", can1grp, can1_mux), + ADI_PMX_FUNCTION("smc0", smc0grp, smc0_mux), + ADI_PMX_FUNCTION("sport0", sport0grp, sport0_mux), + ADI_PMX_FUNCTION("sport1", sport1grp, sport1_mux), + ADI_PMX_FUNCTION("sport2", sport2grp, sport2_mux), + ADI_PMX_FUNCTION("sport3", sport3grp, sport3_mux), + ADI_PMX_FUNCTION("ppi0_8b", ppi0_8bgrp, ppi0_8b_mux), + ADI_PMX_FUNCTION("ppi0_16b", ppi0_16bgrp, ppi0_16b_mux), + ADI_PMX_FUNCTION("ppi0_24b", ppi0_24bgrp, ppi0_24b_mux), + ADI_PMX_FUNCTION("ppi1_8b", ppi1_8bgrp, ppi1_8b_mux), + ADI_PMX_FUNCTION("ppi1_16b", ppi1_16bgrp, ppi1_16b_mux), + ADI_PMX_FUNCTION("ppi2_8b", ppi2_8bgrp, ppi2_8b_mux), + ADI_PMX_FUNCTION("atapi", atapigrp, atapi_mux), + ADI_PMX_FUNCTION("atapi_alter", atapialtergrp, atapi_alter_mux), + ADI_PMX_FUNCTION("nfc0", nfc0grp, nfc0_mux), + ADI_PMX_FUNCTION("keys_4x4", keys_4x4grp, keys_4x4_mux), + ADI_PMX_FUNCTION("keys_8x8", keys_8x8grp, keys_8x8_mux), +}; + +static const struct adi_pinctrl_soc_data adi_bf54x_soc = { + .functions = adi_pmx_functions, + .nfunctions = ARRAY_SIZE(adi_pmx_functions), + .groups = adi_pin_groups, + .ngroups = ARRAY_SIZE(adi_pin_groups), + .pins = adi_pads, + .npins = ARRAY_SIZE(adi_pads), +}; + +void adi_pinctrl_soc_init(const struct adi_pinctrl_soc_data **soc) +{ + *soc = &adi_bf54x_soc; +} diff --git a/drivers/pinctrl/pinctrl-adi2-bf60x.c b/drivers/pinctrl/pinctrl-adi2-bf60x.c new file mode 100644 index 0000000..e90cb80 --- /dev/null +++ b/drivers/pinctrl/pinctrl-adi2-bf60x.c @@ -0,0 +1,522 @@ +/* + * Pinctrl Driver for ADI GPIO2 controller + * + * Copyright 2007-2013 Analog Devices Inc. + * + * Licensed under the GPLv2 or later + */ + +#include +#include "pinctrl-adi2.h" + +static const struct pinctrl_pin_desc adi_pads[] = { + PINCTRL_PIN(0, "PA0"), + PINCTRL_PIN(1, "PA1"), + PINCTRL_PIN(2, "PA2"), + PINCTRL_PIN(3, "PG3"), + PINCTRL_PIN(4, "PA4"), + PINCTRL_PIN(5, "PA5"), + PINCTRL_PIN(6, "PA6"), + PINCTRL_PIN(7, "PA7"), + PINCTRL_PIN(8, "PA8"), + PINCTRL_PIN(9, "PA9"), + PINCTRL_PIN(10, "PA10"), + PINCTRL_PIN(11, "PA11"), + PINCTRL_PIN(12, "PA12"), + PINCTRL_PIN(13, "PA13"), + PINCTRL_PIN(14, "PA14"), + PINCTRL_PIN(15, "PA15"), + PINCTRL_PIN(16, "PB0"), + PINCTRL_PIN(17, "PB1"), + PINCTRL_PIN(18, "PB2"), + PINCTRL_PIN(19, "PB3"), + PINCTRL_PIN(20, "PB4"), + PINCTRL_PIN(21, "PB5"), + PINCTRL_PIN(22, "PB6"), + PINCTRL_PIN(23, "PB7"), + PINCTRL_PIN(24, "PB8"), + PINCTRL_PIN(25, "PB9"), + PINCTRL_PIN(26, "PB10"), + PINCTRL_PIN(27, "PB11"), + PINCTRL_PIN(28, "PB12"), + PINCTRL_PIN(29, "PB13"), + PINCTRL_PIN(30, "PB14"), + PINCTRL_PIN(31, "PB15"), + PINCTRL_PIN(32, "PC0"), + PINCTRL_PIN(33, "PC1"), + PINCTRL_PIN(34, "PC2"), + PINCTRL_PIN(35, "PC3"), + PINCTRL_PIN(36, "PC4"), + PINCTRL_PIN(37, "PC5"), + PINCTRL_PIN(38, "PC6"), + PINCTRL_PIN(39, "PC7"), + PINCTRL_PIN(40, "PC8"), + PINCTRL_PIN(41, "PC9"), + PINCTRL_PIN(42, "PC10"), + PINCTRL_PIN(43, "PC11"), + PINCTRL_PIN(44, "PC12"), + PINCTRL_PIN(45, "PC13"), + PINCTRL_PIN(46, "PC14"), + PINCTRL_PIN(47, "PC15"), + PINCTRL_PIN(48, "PD0"), + PINCTRL_PIN(49, "PD1"), + PINCTRL_PIN(50, "PD2"), + PINCTRL_PIN(51, "PD3"), + PINCTRL_PIN(52, "PD4"), + PINCTRL_PIN(53, "PD5"), + PINCTRL_PIN(54, "PD6"), + PINCTRL_PIN(55, "PD7"), + PINCTRL_PIN(56, "PD8"), + PINCTRL_PIN(57, "PD9"), + PINCTRL_PIN(58, "PD10"), + PINCTRL_PIN(59, "PD11"), + PINCTRL_PIN(60, "PD12"), + PINCTRL_PIN(61, "PD13"), + PINCTRL_PIN(62, "PD14"), + PINCTRL_PIN(63, "PD15"), + PINCTRL_PIN(64, "PE0"), + PINCTRL_PIN(65, "PE1"), + PINCTRL_PIN(66, "PE2"), + PINCTRL_PIN(67, "PE3"), + PINCTRL_PIN(68, "PE4"), + PINCTRL_PIN(69, "PE5"), + PINCTRL_PIN(70, "PE6"), + PINCTRL_PIN(71, "PE7"), + PINCTRL_PIN(72, "PE8"), + PINCTRL_PIN(73, "PE9"), + PINCTRL_PIN(74, "PE10"), + PINCTRL_PIN(75, "PE11"), + PINCTRL_PIN(76, "PE12"), + PINCTRL_PIN(77, "PE13"), + PINCTRL_PIN(78, "PE14"), + PINCTRL_PIN(79, "PE15"), + PINCTRL_PIN(80, "PF0"), + PINCTRL_PIN(81, "PF1"), + PINCTRL_PIN(82, "PF2"), + PINCTRL_PIN(83, "PF3"), + PINCTRL_PIN(84, "PF4"), + PINCTRL_PIN(85, "PF5"), + PINCTRL_PIN(86, "PF6"), + PINCTRL_PIN(87, "PF7"), + PINCTRL_PIN(88, "PF8"), + PINCTRL_PIN(89, "PF9"), + PINCTRL_PIN(90, "PF10"), + PINCTRL_PIN(91, "PF11"), + PINCTRL_PIN(92, "PF12"), + PINCTRL_PIN(93, "PF13"), + PINCTRL_PIN(94, "PF14"), + PINCTRL_PIN(95, "PF15"), + PINCTRL_PIN(96, "PG0"), + PINCTRL_PIN(97, "PG1"), + PINCTRL_PIN(98, "PG2"), + PINCTRL_PIN(99, "PG3"), + PINCTRL_PIN(100, "PG4"), + PINCTRL_PIN(101, "PG5"), + PINCTRL_PIN(102, "PG6"), + PINCTRL_PIN(103, "PG7"), + PINCTRL_PIN(104, "PG8"), + PINCTRL_PIN(105, "PG9"), + PINCTRL_PIN(106, "PG10"), + PINCTRL_PIN(107, "PG11"), + PINCTRL_PIN(108, "PG12"), + PINCTRL_PIN(109, "PG13"), + PINCTRL_PIN(110, "PG14"), + PINCTRL_PIN(111, "PG15"), +}; + +static const unsigned uart0_pins[] = { + GPIO_PD7, GPIO_PD8, +}; + +static const unsigned uart0_ctsrts_pins[] = { + GPIO_PD9, GPIO_PD10, +}; + +static const unsigned uart1_pins[] = { + GPIO_PG15, GPIO_PG14, +}; + +static const unsigned uart1_ctsrts_pins[] = { + GPIO_PG10, GPIO_PG13, +}; + +static const unsigned rsi0_pins[] = { + GPIO_PG3, GPIO_PG2, GPIO_PG0, GPIO_PE15, GPIO_PG5, GPIO_PG6, +}; + +static const unsigned eth0_pins[] = { + GPIO_PC6, GPIO_PC7, GPIO_PC2, GPIO_PC0, GPIO_PC3, GPIO_PC1, + GPIO_PB13, GPIO_PD6, GPIO_PC5, GPIO_PC4, GPIO_PB14, GPIO_PB15, +}; + +static const unsigned eth1_pins[] = { + GPIO_PE10, GPIO_PE11, GPIO_PG3, GPIO_PG0, GPIO_PG2, GPIO_PE15, + GPIO_PG5, GPIO_PE12, GPIO_PE13, GPIO_PE14, GPIO_PG6, GPIO_PC9, +}; + +static const unsigned spi0_pins[] = { + GPIO_PD4, GPIO_PD2, GPIO_PD3, +}; + +static const unsigned spi1_pins[] = { + GPIO_PD5, GPIO_PD14, GPIO_PD13, +}; + +static const unsigned twi0_pins[] = { +}; + +static const unsigned twi1_pins[] = { +}; + +static const unsigned rotary_pins[] = { + GPIO_PG7, GPIO_PG11, GPIO_PG12, +}; + +static const unsigned can0_pins[] = { + GPIO_PG1, GPIO_PG4, +}; + +static const unsigned smc0_pins[] = { + GPIO_PA0, GPIO_PA1, GPIO_PA2, GPIO_PA3, GPIO_PA4, GPIO_PA5, GPIO_PA6, + GPIO_PA7, GPIO_PA8, GPIO_PA9, GPIO_PB2, GPIO_PA10, GPIO_PA11, + GPIO_PB3, GPIO_PA12, GPIO_PA13, GPIO_PA14, GPIO_PA15, GPIO_PB6, + GPIO_PB7, GPIO_PB8, GPIO_PB10, GPIO_PB11, GPIO_PB0, +}; + +static const unsigned sport0_pins[] = { + GPIO_PB5, GPIO_PB4, GPIO_PB9, GPIO_PB8, GPIO_PB7, GPIO_PB11, +}; + +static const unsigned sport1_pins[] = { + GPIO_PE2, GPIO_PE5, GPIO_PD15, GPIO_PE4, GPIO_PE3, GPIO_PE1, +}; + +static const unsigned sport2_pins[] = { + GPIO_PG4, GPIO_PG1, GPIO_PG9, GPIO_PG10, GPIO_PG7, GPIO_PB12, +}; + +static const unsigned ppi0_8b_pins[] = { + GPIO_PF0, GPIO_PF1, GPIO_PF2, GPIO_PF3, GPIO_PF4, GPIO_PF5, GPIO_PF6, + GPIO_PF7, GPIO_PF13, GPIO_PF14, GPIO_PF15, + GPIO_PE6, GPIO_PE7, GPIO_PE8, GPIO_PE9, +}; + +static const unsigned ppi0_16b_pins[] = { + GPIO_PF0, GPIO_PF1, GPIO_PF2, GPIO_PF3, GPIO_PF4, GPIO_PF5, GPIO_PF6, + GPIO_PF7, GPIO_PF9, GPIO_PF10, GPIO_PF11, GPIO_PF12, + GPIO_PF13, GPIO_PF14, GPIO_PF15, + GPIO_PE6, GPIO_PE7, GPIO_PE8, GPIO_PE9, +}; + +static const unsigned ppi0_24b_pins[] = { + GPIO_PF0, GPIO_PF1, GPIO_PF2, GPIO_PF3, GPIO_PF4, GPIO_PF5, GPIO_PF6, + GPIO_PF7, GPIO_PF8, GPIO_PF9, GPIO_PF10, GPIO_PF11, GPIO_PF12, + GPIO_PF13, GPIO_PF14, GPIO_PF15, GPIO_PE0, GPIO_PE1, GPIO_PE2, + GPIO_PE3, GPIO_PE4, GPIO_PE5, GPIO_PE6, GPIO_PE7, GPIO_PE8, + GPIO_PE9, GPIO_PD12, GPIO_PD15, +}; + +static const unsigned ppi1_8b_pins[] = { + GPIO_PC0, GPIO_PC1, GPIO_PC2, GPIO_PC3, GPIO_PC4, GPIO_PC5, GPIO_PC6, + GPIO_PC7, GPIO_PC8, GPIO_PB13, GPIO_PB14, GPIO_PB15, GPIO_PD6, +}; + +static const unsigned ppi1_16b_pins[] = { + GPIO_PC0, GPIO_PC1, GPIO_PC2, GPIO_PC3, GPIO_PC4, GPIO_PC5, GPIO_PC6, + GPIO_PC7, GPIO_PC9, GPIO_PC10, GPIO_PC11, GPIO_PC12, + GPIO_PC13, GPIO_PC14, GPIO_PC15, + GPIO_PB13, GPIO_PB14, GPIO_PB15, GPIO_PD6, +}; + +static const unsigned ppi2_8b_pins[] = { + GPIO_PA0, GPIO_PA1, GPIO_PA2, GPIO_PA3, GPIO_PA4, GPIO_PA5, GPIO_PA6, + GPIO_PA7, GPIO_PB0, GPIO_PB1, GPIO_PB2, GPIO_PB3, +}; + +static const unsigned ppi2_16b_pins[] = { + GPIO_PA0, GPIO_PA1, GPIO_PA2, GPIO_PA3, GPIO_PA4, GPIO_PA5, GPIO_PA6, + GPIO_PA7, GPIO_PA8, GPIO_PA9, GPIO_PA10, GPIO_PA11, GPIO_PA12, + GPIO_PA13, GPIO_PA14, GPIO_PA15, + GPIO_PA7, GPIO_PB0, GPIO_PB1, GPIO_PB2, GPIO_PB3, +}; + +static const unsigned lp0_pins[] = { + GPIO_PB0, GPIO_PB1, GPIO_PA0, GPIO_PA1, GPIO_PA2, GPIO_PA3, + GPIO_PA4, GPIO_PA5, GPIO_PA6, GPIO_PA7, +}; + +static const unsigned lp1_pins[] = { + GPIO_PB3, GPIO_PB2, GPIO_PA8, GPIO_PA9, GPIO_PA10, GPIO_PA11, + GPIO_PA12, GPIO_PA13, GPIO_PA14, GPIO_PA15, +}; + +static const unsigned lp2_pins[] = { + GPIO_PE6, GPIO_PE7, GPIO_PF0, GPIO_PF1, GPIO_PF2, GPIO_PF3, + GPIO_PF4, GPIO_PF5, GPIO_PF6, GPIO_PF7, +}; + +static const unsigned lp3_pins[] = { + GPIO_PE9, GPIO_PE8, GPIO_PF8, GPIO_PF9, GPIO_PF10, GPIO_PF11, + GPIO_PF12, GPIO_PF13, GPIO_PF14, GPIO_PF15, +}; + +static const struct adi_pin_group adi_pin_groups[] = { + ADI_PIN_GROUP("uart0grp", uart0_pins), + ADI_PIN_GROUP("uart0ctsrtsgrp", uart0_ctsrts_pins), + ADI_PIN_GROUP("uart1grp", uart1_pins), + ADI_PIN_GROUP("uart1ctsrtsgrp", uart1_ctsrts_pins), + ADI_PIN_GROUP("rsi0grp", rsi0_pins), + ADI_PIN_GROUP("eth0grp", eth0_pins), + ADI_PIN_GROUP("eth1grp", eth1_pins), + ADI_PIN_GROUP("spi0grp", spi0_pins), + ADI_PIN_GROUP("spi1grp", spi1_pins), + ADI_PIN_GROUP("twi0grp", twi0_pins), + ADI_PIN_GROUP("twi1grp", twi1_pins), + ADI_PIN_GROUP("rotarygrp", rotary_pins), + ADI_PIN_GROUP("can0grp", can0_pins), + ADI_PIN_GROUP("smc0grp", smc0_pins), + ADI_PIN_GROUP("sport0grp", sport0_pins), + ADI_PIN_GROUP("sport1grp", sport1_pins), + ADI_PIN_GROUP("sport2grp", sport2_pins), + ADI_PIN_GROUP("ppi0_8bgrp", ppi0_8b_pins), + ADI_PIN_GROUP("ppi0_16bgrp", ppi0_16b_pins), + ADI_PIN_GROUP("ppi0_24bgrp", ppi0_24b_pins), + ADI_PIN_GROUP("ppi1_8bgrp", ppi1_8b_pins), + ADI_PIN_GROUP("ppi1_16bgrp", ppi1_16b_pins), + ADI_PIN_GROUP("ppi2_8bgrp", ppi2_8b_pins), + ADI_PIN_GROUP("ppi2_16bgrp", ppi2_16b_pins), + ADI_PIN_GROUP("lp0grp", lp0_pins), + ADI_PIN_GROUP("lp1grp", lp1_pins), + ADI_PIN_GROUP("lp2grp", lp2_pins), + ADI_PIN_GROUP("lp3grp", lp3_pins), +}; + +static const unsigned short uart0_mux[] = { + P_UART0_TX, P_UART0_RX, + 0 +}; + +static const unsigned short uart0_ctsrts_mux[] = { + P_UART0_RTS, P_UART0_CTS, + 0 +}; + +static const unsigned short uart1_mux[] = { + P_UART1_TX, P_UART1_RX, + 0 +}; + +static const unsigned short uart1_ctsrts_mux[] = { + P_UART1_RTS, P_UART1_CTS, + 0 +}; + +static const unsigned short rsi0_mux[] = { + P_RSI_DATA0, P_RSI_DATA1, P_RSI_DATA2, P_RSI_DATA3, + P_RSI_CMD, P_RSI_CLK, 0 +}; + +static const unsigned short eth0_mux[] = P_RMII0; +static const unsigned short eth1_mux[] = P_RMII1; + +static const unsigned short spi0_mux[] = { + P_SPI0_SCK, P_SPI0_MISO, P_SPI0_MOSI, 0 +}; + +static const unsigned short spi1_mux[] = { + P_SPI1_SCK, P_SPI1_MISO, P_SPI1_MOSI, 0 +}; + +static const unsigned short twi0_mux[] = { + P_TWI0_SCL, P_TWI0_SDA, 0 +}; + +static const unsigned short twi1_mux[] = { + P_TWI1_SCL, P_TWI1_SDA, 0 +}; + +static const unsigned short rotary_mux[] = { + P_CNT_CUD, P_CNT_CDG, P_CNT_CZM, 0 +}; + +static const unsigned short sport0_mux[] = { + P_SPORT0_ACLK, P_SPORT0_AFS, P_SPORT0_AD0, P_SPORT0_BCLK, + P_SPORT0_BFS, P_SPORT0_BD0, 0, +}; + +static const unsigned short sport1_mux[] = { + P_SPORT1_ACLK, P_SPORT1_AFS, P_SPORT1_AD0, P_SPORT1_BCLK, + P_SPORT1_BFS, P_SPORT1_BD0, 0, +}; + +static const unsigned short sport2_mux[] = { + P_SPORT2_ACLK, P_SPORT2_AFS, P_SPORT2_AD0, P_SPORT2_BCLK, + P_SPORT2_BFS, P_SPORT2_BD0, 0, +}; + +static const unsigned short can0_mux[] = { + P_CAN0_RX, P_CAN0_TX, 0 +}; + +static const unsigned short smc0_mux[] = { + P_A3, P_A4, P_A5, P_A6, P_A7, P_A8, P_A9, P_A10, P_A11, P_A12, + P_A13, P_A14, P_A15, P_A16, P_A17, P_A18, P_A19, P_A20, P_A21, + P_A22, P_A23, P_A24, P_A25, P_NORCK, 0, +}; + +static const unsigned short ppi0_8b_mux[] = { + P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, P_PPI0_D3, + P_PPI0_D4, P_PPI0_D5, P_PPI0_D6, P_PPI0_D7, + P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2, + 0, +}; + +static const unsigned short ppi0_16b_mux[] = { + P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, P_PPI0_D3, + P_PPI0_D4, P_PPI0_D5, P_PPI0_D6, P_PPI0_D7, + P_PPI0_D8, P_PPI0_D9, P_PPI0_D10, P_PPI0_D11, + P_PPI0_D12, P_PPI0_D13, P_PPI0_D14, P_PPI0_D15, + P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2, + 0, +}; + +static const unsigned short ppi0_24b_mux[] = { + P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, P_PPI0_D3, + P_PPI0_D4, P_PPI0_D5, P_PPI0_D6, P_PPI0_D7, + P_PPI0_D8, P_PPI0_D9, P_PPI0_D10, P_PPI0_D11, + P_PPI0_D12, P_PPI0_D13, P_PPI0_D14, P_PPI0_D15, + P_PPI0_D16, P_PPI0_D17, P_PPI0_D18, P_PPI0_D19, + P_PPI0_D20, P_PPI0_D21, P_PPI0_D22, P_PPI0_D23, + P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2, + 0, +}; + +static const unsigned short ppi1_8b_mux[] = { + P_PPI1_D0, P_PPI1_D1, P_PPI1_D2, P_PPI1_D3, + P_PPI1_D4, P_PPI1_D5, P_PPI1_D6, P_PPI1_D7, + P_PPI1_CLK, P_PPI1_FS1, P_PPI1_FS2, + 0, +}; + +static const unsigned short ppi1_16b_mux[] = { + P_PPI1_D0, P_PPI1_D1, P_PPI1_D2, P_PPI1_D3, + P_PPI1_D4, P_PPI1_D5, P_PPI1_D6, P_PPI1_D7, + P_PPI1_D8, P_PPI1_D9, P_PPI1_D10, P_PPI1_D11, + P_PPI1_D12, P_PPI1_D13, P_PPI1_D14, P_PPI1_D15, + P_PPI1_CLK, P_PPI1_FS1, P_PPI1_FS2, + 0, +}; + +static const unsigned short ppi2_8b_mux[] = { + P_PPI2_D0, P_PPI2_D1, P_PPI2_D2, P_PPI2_D3, + P_PPI2_D4, P_PPI2_D5, P_PPI2_D6, P_PPI2_D7, + P_PPI2_CLK, P_PPI2_FS1, P_PPI2_FS2, + 0, +}; + +static const unsigned short ppi2_16b_mux[] = { + P_PPI2_D0, P_PPI2_D1, P_PPI2_D2, P_PPI2_D3, + P_PPI2_D4, P_PPI2_D5, P_PPI2_D6, P_PPI2_D7, + P_PPI2_D8, P_PPI2_D9, P_PPI2_D10, P_PPI2_D11, + P_PPI2_D12, P_PPI2_D13, P_PPI2_D14, P_PPI2_D15, + P_PPI2_CLK, P_PPI2_FS1, P_PPI2_FS2, + 0, +}; + +static const unsigned short lp0_mux[] = { + P_LP0_CLK, P_LP0_ACK, P_LP0_D0, P_LP0_D1, P_LP0_D2, + P_LP0_D3, P_LP0_D4, P_LP0_D5, P_LP0_D6, P_LP0_D7, + 0 +}; + +static const unsigned short lp1_mux[] = { + P_LP1_CLK, P_LP1_ACK, P_LP1_D0, P_LP1_D1, P_LP1_D2, + P_LP1_D3, P_LP1_D4, P_LP1_D5, P_LP1_D6, P_LP1_D7, + 0 +}; + +static const unsigned short lp2_mux[] = { + P_LP2_CLK, P_LP2_ACK, P_LP2_D0, P_LP2_D1, P_LP2_D2, + P_LP2_D3, P_LP2_D4, P_LP2_D5, P_LP2_D6, P_LP2_D7, + 0 +}; + +static const unsigned short lp3_mux[] = { + P_LP3_CLK, P_LP3_ACK, P_LP3_D0, P_LP3_D1, P_LP3_D2, + P_LP3_D3, P_LP3_D4, P_LP3_D5, P_LP3_D6, P_LP3_D7, + 0 +}; + +static const char * const uart0grp[] = { "uart0grp" }; +static const char * const uart0ctsrtsgrp[] = { "uart0ctsrtsgrp" }; +static const char * const uart1grp[] = { "uart1grp" }; +static const char * const uart1ctsrtsgrp[] = { "uart1ctsrtsgrp" }; +static const char * const rsi0grp[] = { "rsi0grp" }; +static const char * const eth0grp[] = { "eth0grp" }; +static const char * const eth1grp[] = { "eth1grp" }; +static const char * const spi0grp[] = { "spi0grp" }; +static const char * const spi1grp[] = { "spi1grp" }; +static const char * const twi0grp[] = { "twi0grp" }; +static const char * const twi1grp[] = { "twi1grp" }; +static const char * const rotarygrp[] = { "rotarygrp" }; +static const char * const can0grp[] = { "can0grp" }; +static const char * const smc0grp[] = { "smc0grp" }; +static const char * const sport0grp[] = { "sport0grp" }; +static const char * const sport1grp[] = { "sport1grp" }; +static const char * const sport2grp[] = { "sport2grp" }; +static const char * const ppi0_8bgrp[] = { "ppi0_8bgrp" }; +static const char * const ppi0_16bgrp[] = { "ppi0_16bgrp" }; +static const char * const ppi0_24bgrp[] = { "ppi0_24bgrp" }; +static const char * const ppi1_8bgrp[] = { "ppi1_8bgrp" }; +static const char * const ppi1_16bgrp[] = { "ppi1_16bgrp" }; +static const char * const ppi2_8bgrp[] = { "ppi2_8bgrp" }; +static const char * const ppi2_16bgrp[] = { "ppi2_16bgrp" }; +static const char * const lp0grp[] = { "lp0grp" }; +static const char * const lp1grp[] = { "lp1grp" }; +static const char * const lp2grp[] = { "lp2grp" }; +static const char * const lp3grp[] = { "lp3grp" }; + +static const struct adi_pmx_func adi_pmx_functions[] = { + ADI_PMX_FUNCTION("uart0", uart0grp, uart0_mux), + ADI_PMX_FUNCTION("uart0_ctsrts", uart0ctsrtsgrp, uart0_ctsrts_mux), + ADI_PMX_FUNCTION("uart1", uart1grp, uart1_mux), + ADI_PMX_FUNCTION("uart1_ctsrts", uart1ctsrtsgrp, uart1_ctsrts_mux), + ADI_PMX_FUNCTION("rsi0", rsi0grp, rsi0_mux), + ADI_PMX_FUNCTION("eth0", eth0grp, eth0_mux), + ADI_PMX_FUNCTION("eth1", eth1grp, eth1_mux), + ADI_PMX_FUNCTION("spi0", spi0grp, spi0_mux), + ADI_PMX_FUNCTION("spi1", spi1grp, spi1_mux), + ADI_PMX_FUNCTION("twi0", twi0grp, twi0_mux), + ADI_PMX_FUNCTION("twi1", twi1grp, twi1_mux), + ADI_PMX_FUNCTION("rotary", rotarygrp, rotary_mux), + ADI_PMX_FUNCTION("can0", can0grp, can0_mux), + ADI_PMX_FUNCTION("smc0", smc0grp, smc0_mux), + ADI_PMX_FUNCTION("sport0", sport0grp, sport0_mux), + ADI_PMX_FUNCTION("sport1", sport1grp, sport1_mux), + ADI_PMX_FUNCTION("sport2", sport2grp, sport2_mux), + ADI_PMX_FUNCTION("ppi0_8b", ppi0_8bgrp, ppi0_8b_mux), + ADI_PMX_FUNCTION("ppi0_16b", ppi0_16bgrp, ppi0_16b_mux), + ADI_PMX_FUNCTION("ppi0_24b", ppi0_24bgrp, ppi0_24b_mux), + ADI_PMX_FUNCTION("ppi1_8b", ppi1_8bgrp, ppi1_8b_mux), + ADI_PMX_FUNCTION("ppi1_16b", ppi1_16bgrp, ppi1_16b_mux), + ADI_PMX_FUNCTION("ppi2_8b", ppi2_8bgrp, ppi2_8b_mux), + ADI_PMX_FUNCTION("ppi2_16b", ppi2_16bgrp, ppi2_16b_mux), + ADI_PMX_FUNCTION("lp0", lp0grp, lp0_mux), + ADI_PMX_FUNCTION("lp1", lp1grp, lp1_mux), + ADI_PMX_FUNCTION("lp2", lp2grp, lp2_mux), + ADI_PMX_FUNCTION("lp3", lp3grp, lp3_mux), +}; + +static const struct adi_pinctrl_soc_data adi_bf60x_soc = { + .functions = adi_pmx_functions, + .nfunctions = ARRAY_SIZE(adi_pmx_functions), + .groups = adi_pin_groups, + .ngroups = ARRAY_SIZE(adi_pin_groups), + .pins = adi_pads, + .npins = ARRAY_SIZE(adi_pads), +}; + +void adi_pinctrl_soc_init(const struct adi_pinctrl_soc_data **soc) +{ + *soc = &adi_bf60x_soc; +} diff --git a/drivers/pinctrl/pinctrl-adi2.c b/drivers/pinctrl/pinctrl-adi2.c new file mode 100644 index 0000000..7a24e59 --- /dev/null +++ b/drivers/pinctrl/pinctrl-adi2.c @@ -0,0 +1,1183 @@ +/* + * Pinctrl Driver for ADI GPIO2 controller + * + * Copyright 2007-2013 Analog Devices Inc. + * + * Licensed under the GPLv2 or later + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pinctrl-adi2.h" +#include "core.h" + +/* +According to the BF54x HRM, pint means "pin interrupt". +http://www.analog.com/static/imported-files/processor_manuals/ADSP-BF54x_hwr_rev1.2.pdf + +ADSP-BF54x processor Blackfin processors have four SIC interrupt chan- +nels dedicated to pin interrupt purposes. These channels are managed by +four hardware blocks, called PINT0, PINT1, PINT2, and PINT3. Every PINTx +block can sense to up to 32 pins. While PINT0 and PINT1 can sense the +pins of port A and port B, PINT2 and PINT3 manage all the pins from port +C to port J as shown in Figure 9-2. + +n BF54x HRM: +The ten GPIO ports are subdivided into 8-bit half ports, resulting in lower and +upper half 8-bit units. The PINTx_ASSIGN registers control the 8-bit multi- +plexers shown in Figure 9-3. Lower half units of eight pins can be +forwarded to either byte 0 or byte 2 of either associated PINTx block. +Upper half units can be forwarded to either byte 1 or byte 3 of the pin +interrupt blocks, without further restrictions. + +All MMR registers in the pin interrupt module are 32 bits wide. To simply the +mapping logic, this driver only maps a 16-bit gpio port to the upper or lower +16 bits of a PINTx block. You can find the Figure 9-3 on page 583. + +Each IRQ domain is binding to a GPIO bank device. 2 GPIO bank devices can map +to one PINT device. Two in "struct gpio_pint" are used to ease the PINT +interrupt handler. + +The GPIO bank mapping to the lower 16 bits of the PINT device set its IRQ +domain pointer in domain[0]. The IRQ domain pointer of the other bank is set +to domain[1]. PINT interrupt handler adi_gpio_handle_pint_irq() finds out +the current domain pointer according to whether the interrupt request mask +is in lower 16 bits (domain[0]) or upper 16bits (domain[1]). + +A PINT device is not part of a GPIO port device in Blackfin. Multiple GPIO +port devices can be mapped to the same PINT device. + +*/ + +static LIST_HEAD(adi_pint_list); +static LIST_HEAD(adi_gpio_port_list); + +#define DRIVER_NAME "pinctrl-adi2" + +#define PINT_HI_OFFSET 16 + +/** + * struct gpio_port_saved - GPIO port registers that should be saved between + * power suspend and resume operations. + * + * @fer: PORTx_FER register + * @data: PORTx_DATA register + * @dir: PORTx_DIR register + * @inen: PORTx_INEN register + * @mux: PORTx_MUX register + */ +struct gpio_port_saved { + u16 fer; + u16 data; + u16 dir; + u16 inen; + u32 mux; +}; + +/** + * struct gpio_pint - Pin interrupt controller device. Multiple ADI GPIO + * banks can be mapped into one Pin interrupt controller. + * + * @node: All gpio_pint instances are added to a global list. + * @base: PINT device register base address + * @irq: IRQ of the PINT device, it is the parent IRQ of all + * GPIO IRQs mapping to this device. + * @domain: [0] irq domain of the gpio port, whose hardware interrupts are + * mapping to the low 16-bit of the pint registers. + * [1] irq domain of the gpio port, whose hardware interrupts are + * mapping to the high 16-bit of the pint registers. + * @regs: address pointer to the PINT device + * @map_count: No more than 2 GPIO banks can be mapped to this PINT device. + * @lock: This lock make sure the irq_chip operations to one PINT device + * for different GPIO interrrupts are atomic. + * @pint_map_port: Set up the mapping between one PINT device and + * multiple GPIO banks. + */ +struct gpio_pint { + struct list_head node; + void __iomem *base; + int irq; + struct irq_domain *domain[2]; + struct gpio_pint_regs *regs; + struct adi_pm_pint_save saved_data; + int map_count; + spinlock_t lock; + + int (*pint_map_port)(struct gpio_pint *pint, bool assign, + u8 map, struct irq_domain *domain); +}; + +/** + * ADI pin controller + * + * @dev: a pointer back to containing device + * @pctl: the pinctrl device + * @soc: SoC data for this specific chip + */ +struct adi_pinctrl { + struct device *dev; + struct pinctrl_dev *pctl; + const struct adi_pinctrl_soc_data *soc; +}; + +/** + * struct gpio_port - GPIO bank device. Multiple ADI GPIO banks can be mapped + * into one pin interrupt controller. + * + * @node: All gpio_port instances are added to a list. + * @base: GPIO bank device register base address + * @irq_base: base IRQ of the GPIO bank device + * @width: PIN number of the GPIO bank device + * @regs: address pointer to the GPIO bank device + * @saved_data: registers that should be saved between PM operations. + * @dev: device structure of this GPIO bank + * @pint: GPIO PINT device that this GPIO bank mapped to + * @pint_map: GIOP bank mapping code in PINT device + * @pint_assign: The 32-bit PINT registers can be divided into 2 parts. A + * GPIO bank can be mapped into either low 16 bits[0] or high 16 + * bits[1] of each PINT register. + * @lock: This lock make sure the irq_chip operations to one PINT device + * for different GPIO interrrupts are atomic. + * @chip: abstract a GPIO controller + * @domain: The irq domain owned by the GPIO port. + * @rsvmap: Reservation map array for each pin in the GPIO bank + */ +struct gpio_port { + struct list_head node; + void __iomem *base; + unsigned int irq_base; + unsigned int width; + struct gpio_port_t *regs; + struct gpio_port_saved saved_data; + struct device *dev; + + struct gpio_pint *pint; + u8 pint_map; + bool pint_assign; + + spinlock_t lock; + struct gpio_chip chip; + struct irq_domain *domain; +}; + +static inline u8 pin_to_offset(struct pinctrl_gpio_range *range, unsigned pin) +{ + return pin - range->pin_base; +} + +static inline u32 hwirq_to_pintbit(struct gpio_port *port, int hwirq) +{ + return port->pint_assign ? BIT(hwirq) << PINT_HI_OFFSET : BIT(hwirq); +} + +static struct gpio_pint *find_gpio_pint(unsigned id) +{ + struct gpio_pint *pint; + int i = 0; + + list_for_each_entry(pint, &adi_pint_list, node) { + if (id == i) + return pint; + i++; + } + + return NULL; +} + +static inline void port_setup(struct gpio_port *port, unsigned offset, + bool use_for_gpio) +{ + struct gpio_port_t *regs = port->regs; + + if (use_for_gpio) + writew(readw(®s->port_fer) & ~BIT(offset), + ®s->port_fer); + else + writew(readw(®s->port_fer) | BIT(offset), ®s->port_fer); +} + +static inline void portmux_setup(struct gpio_port *port, unsigned offset, + unsigned short function) +{ + struct gpio_port_t *regs = port->regs; + u32 pmux; + + pmux = readl(®s->port_mux); + + /* The function field of each pin has 2 consecutive bits in + * the mux register. + */ + pmux &= ~(0x3 << (2 * offset)); + pmux |= (function & 0x3) << (2 * offset); + + writel(pmux, ®s->port_mux); +} + +static inline u16 get_portmux(struct gpio_port *port, unsigned offset) +{ + struct gpio_port_t *regs = port->regs; + u32 pmux = readl(®s->port_mux); + + /* The function field of each pin has 2 consecutive bits in + * the mux register. + */ + return pmux >> (2 * offset) & 0x3; +} + +static void adi_gpio_ack_irq(struct irq_data *d) +{ + unsigned long flags; + struct gpio_port *port = irq_data_get_irq_chip_data(d); + struct gpio_pint_regs *regs = port->pint->regs; + unsigned pintbit = hwirq_to_pintbit(port, d->hwirq); + + spin_lock_irqsave(&port->lock, flags); + spin_lock_irqsave(&port->pint->lock, flags); + + if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH) { + if (readl(®s->invert_set) & pintbit) + writel(pintbit, ®s->invert_clear); + else + writel(pintbit, ®s->invert_set); + } + + writel(pintbit, ®s->request); + + spin_unlock_irqrestore(&port->pint->lock, flags); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void adi_gpio_mask_ack_irq(struct irq_data *d) +{ + unsigned long flags; + struct gpio_port *port = irq_data_get_irq_chip_data(d); + struct gpio_pint_regs *regs = port->pint->regs; + unsigned pintbit = hwirq_to_pintbit(port, d->hwirq); + + spin_lock_irqsave(&port->lock, flags); + spin_lock_irqsave(&port->pint->lock, flags); + + if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH) { + if (readl(®s->invert_set) & pintbit) + writel(pintbit, ®s->invert_clear); + else + writel(pintbit, ®s->invert_set); + } + + writel(pintbit, ®s->request); + writel(pintbit, ®s->mask_clear); + + spin_unlock_irqrestore(&port->pint->lock, flags); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void adi_gpio_mask_irq(struct irq_data *d) +{ + unsigned long flags; + struct gpio_port *port = irq_data_get_irq_chip_data(d); + struct gpio_pint_regs *regs = port->pint->regs; + + spin_lock_irqsave(&port->lock, flags); + spin_lock_irqsave(&port->pint->lock, flags); + + writel(hwirq_to_pintbit(port, d->hwirq), ®s->mask_clear); + + spin_unlock_irqrestore(&port->pint->lock, flags); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void adi_gpio_unmask_irq(struct irq_data *d) +{ + unsigned long flags; + struct gpio_port *port = irq_data_get_irq_chip_data(d); + struct gpio_pint_regs *regs = port->pint->regs; + + spin_lock_irqsave(&port->lock, flags); + spin_lock_irqsave(&port->pint->lock, flags); + + writel(hwirq_to_pintbit(port, d->hwirq), ®s->mask_set); + + spin_unlock_irqrestore(&port->pint->lock, flags); + spin_unlock_irqrestore(&port->lock, flags); +} + +static unsigned int adi_gpio_irq_startup(struct irq_data *d) +{ + unsigned long flags; + struct gpio_port *port = irq_data_get_irq_chip_data(d); + struct gpio_pint_regs *regs = port->pint->regs; + + if (!port) { + dev_err(port->dev, "GPIO IRQ %d :Not exist\n", d->irq); + return -ENODEV; + } + + spin_lock_irqsave(&port->lock, flags); + spin_lock_irqsave(&port->pint->lock, flags); + + port_setup(port, d->hwirq, true); + writew(BIT(d->hwirq), &port->regs->dir_clear); + writew(readw(&port->regs->inen) | BIT(d->hwirq), &port->regs->inen); + + writel(hwirq_to_pintbit(port, d->hwirq), ®s->mask_set); + + spin_unlock_irqrestore(&port->pint->lock, flags); + spin_unlock_irqrestore(&port->lock, flags); + + return 0; +} + +static void adi_gpio_irq_shutdown(struct irq_data *d) +{ + unsigned long flags; + struct gpio_port *port = irq_data_get_irq_chip_data(d); + struct gpio_pint_regs *regs = port->pint->regs; + + spin_lock_irqsave(&port->lock, flags); + spin_lock_irqsave(&port->pint->lock, flags); + + writel(hwirq_to_pintbit(port, d->hwirq), ®s->mask_clear); + + spin_unlock_irqrestore(&port->pint->lock, flags); + spin_unlock_irqrestore(&port->lock, flags); +} + +static int adi_gpio_irq_type(struct irq_data *d, unsigned int type) +{ + unsigned long flags; + struct gpio_port *port = irq_data_get_irq_chip_data(d); + struct gpio_pint_regs *pint_regs = port->pint->regs; + unsigned pintmask; + unsigned int irq = d->irq; + int ret = 0; + char buf[16]; + + if (!port) { + dev_err(port->dev, "GPIO IRQ %d :Not exist\n", irq); + return -ENODEV; + } + + pintmask = hwirq_to_pintbit(port, d->hwirq); + + spin_lock_irqsave(&port->lock, flags); + spin_lock_irqsave(&port->pint->lock, flags); + + /* In case of interrupt autodetect, set irq type to edge sensitive. */ + if (type == IRQ_TYPE_PROBE) + type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING; + + if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING | + IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) { + snprintf(buf, 16, "gpio-irq%d", irq); + port_setup(port, d->hwirq, true); + } else + goto out; + + /* The GPIO interrupt is triggered only when its input value + * transfer from 0 to 1. So, invert the input value if the + * irq type is low or falling + */ + if ((type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_LOW))) + writel(pintmask, &pint_regs->invert_set); + else + writel(pintmask, &pint_regs->invert_clear); + + /* In edge sensitive case, if the input value of the requested irq + * is already 1, invert it. + */ + if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) { + if (gpio_get_value(port->chip.base + d->hwirq)) + writel(pintmask, &pint_regs->invert_set); + else + writel(pintmask, &pint_regs->invert_clear); + } + + if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) { + writel(pintmask, &pint_regs->edge_set); + __irq_set_handler_locked(irq, handle_edge_irq); + } else { + writel(pintmask, &pint_regs->edge_clear); + __irq_set_handler_locked(irq, handle_level_irq); + } + +out: + spin_unlock_irqrestore(&port->pint->lock, flags); + spin_unlock_irqrestore(&port->lock, flags); + + return ret; +} + +#ifdef CONFIG_PM +static int adi_gpio_set_wake(struct irq_data *d, unsigned int state) +{ + struct gpio_port *port = irq_data_get_irq_chip_data(d); + + if (!port || !port->pint || port->pint->irq != d->irq) + return -EINVAL; + +#ifndef SEC_GCTL + adi_internal_set_wake(port->pint->irq, state); +#endif + + return 0; +} + +static int adi_pint_suspend(void) +{ + struct gpio_pint *pint; + + list_for_each_entry(pint, &adi_pint_list, node) { + writel(0xffffffff, &pint->regs->mask_clear); + pint->saved_data.assign = readl(&pint->regs->assign); + pint->saved_data.edge_set = readl(&pint->regs->edge_set); + pint->saved_data.invert_set = readl(&pint->regs->invert_set); + } + + return 0; +} + +static void adi_pint_resume(void) +{ + struct gpio_pint *pint; + + list_for_each_entry(pint, &adi_pint_list, node) { + writel(pint->saved_data.assign, &pint->regs->assign); + writel(pint->saved_data.edge_set, &pint->regs->edge_set); + writel(pint->saved_data.invert_set, &pint->regs->invert_set); + } +} + +static int adi_gpio_suspend(void) +{ + struct gpio_port *port; + + list_for_each_entry(port, &adi_gpio_port_list, node) { + port->saved_data.fer = readw(&port->regs->port_fer); + port->saved_data.mux = readl(&port->regs->port_mux); + port->saved_data.data = readw(&port->regs->data); + port->saved_data.inen = readw(&port->regs->inen); + port->saved_data.dir = readw(&port->regs->dir_set); + } + + return adi_pint_suspend(); +} + +static void adi_gpio_resume(void) +{ + struct gpio_port *port; + + adi_pint_resume(); + + list_for_each_entry(port, &adi_gpio_port_list, node) { + writel(port->saved_data.mux, &port->regs->port_mux); + writew(port->saved_data.fer, &port->regs->port_fer); + writew(port->saved_data.inen, &port->regs->inen); + writew(port->saved_data.data & port->saved_data.dir, + &port->regs->data_set); + writew(port->saved_data.dir, &port->regs->dir_set); + } + +} + +static struct syscore_ops gpio_pm_syscore_ops = { + .suspend = adi_gpio_suspend, + .resume = adi_gpio_resume, +}; +#else /* CONFIG_PM */ +#define adi_gpio_set_wake NULL +#endif /* CONFIG_PM */ + +#ifdef CONFIG_IRQ_PREFLOW_FASTEOI +static inline void preflow_handler(struct irq_desc *desc) +{ + if (desc->preflow_handler) + desc->preflow_handler(&desc->irq_data); +} +#else +static inline void preflow_handler(struct irq_desc *desc) { } +#endif + +static void adi_gpio_handle_pint_irq(unsigned int inta_irq, + struct irq_desc *desc) +{ + u32 request; + u32 level_mask, hwirq; + bool umask = false; + struct gpio_pint *pint = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + struct gpio_pint_regs *regs = pint->regs; + struct irq_domain *domain; + + preflow_handler(desc); + chained_irq_enter(chip, desc); + + request = readl(®s->request); + level_mask = readl(®s->edge_set) & request; + + hwirq = 0; + domain = pint->domain[0]; + while (request) { + /* domain pointer need to be changed only once at IRQ 16 when + * we go through IRQ requests from bit 0 to bit 31. + */ + if (hwirq == PINT_HI_OFFSET) + domain = pint->domain[1]; + + if (request & 1) { + if (level_mask & BIT(hwirq)) { + umask = true; + chained_irq_exit(chip, desc); + } + generic_handle_irq(irq_find_mapping(domain, + hwirq % PINT_HI_OFFSET)); + } + + hwirq++; + request >>= 1; + } + + if (!umask) + chained_irq_exit(chip, desc); +} + +static struct irq_chip adi_gpio_irqchip = { + .name = "GPIO", + .irq_ack = adi_gpio_ack_irq, + .irq_mask = adi_gpio_mask_irq, + .irq_mask_ack = adi_gpio_mask_ack_irq, + .irq_unmask = adi_gpio_unmask_irq, + .irq_disable = adi_gpio_mask_irq, + .irq_enable = adi_gpio_unmask_irq, + .irq_set_type = adi_gpio_irq_type, + .irq_startup = adi_gpio_irq_startup, + .irq_shutdown = adi_gpio_irq_shutdown, + .irq_set_wake = adi_gpio_set_wake, +}; + +static int adi_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct adi_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev); + + return pinctrl->soc->ngroups; +} + +static const char *adi_get_group_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct adi_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev); + + return pinctrl->soc->groups[selector].name; +} + +static int adi_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector, + const unsigned **pins, + unsigned *num_pins) +{ + struct adi_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev); + + *pins = pinctrl->soc->groups[selector].pins; + *num_pins = pinctrl->soc->groups[selector].num; + return 0; +} + +static struct pinctrl_ops adi_pctrl_ops = { + .get_groups_count = adi_get_groups_count, + .get_group_name = adi_get_group_name, + .get_group_pins = adi_get_group_pins, +}; + +static int adi_pinmux_enable(struct pinctrl_dev *pctldev, unsigned selector, + unsigned group) +{ + struct adi_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev); + struct gpio_port *port; + struct pinctrl_gpio_range *range; + unsigned long flags; + unsigned short *mux, pin; + + mux = (unsigned short *)pinctrl->soc->functions[selector].mux; + + while (*mux) { + pin = P_IDENT(*mux); + + range = pinctrl_find_gpio_range_from_pin(pctldev, pin); + if (range == NULL) /* should not happen */ + return -ENODEV; + + port = container_of(range->gc, struct gpio_port, chip); + + spin_lock_irqsave(&port->lock, flags); + + portmux_setup(port, pin_to_offset(range, pin), + P_FUNCT2MUX(*mux)); + port_setup(port, pin_to_offset(range, pin), false); + mux++; + + spin_unlock_irqrestore(&port->lock, flags); + } + + return 0; +} + +static void adi_pinmux_disable(struct pinctrl_dev *pctldev, unsigned selector, + unsigned group) +{ + struct adi_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev); + struct gpio_port *port; + struct pinctrl_gpio_range *range; + unsigned long flags; + unsigned short *mux, pin; + + mux = (unsigned short *)pinctrl->soc->functions[selector].mux; + + while (*mux) { + pin = P_IDENT(*mux); + + range = pinctrl_find_gpio_range_from_pin(pctldev, pin); + if (range == NULL) /* should not happen */ + return; + + port = container_of(range->gc, struct gpio_port, chip); + + spin_lock_irqsave(&port->lock, flags); + + port_setup(port, pin_to_offset(range, pin), true); + mux++; + + spin_unlock_irqrestore(&port->lock, flags); + } +} + +static int adi_pinmux_get_funcs_count(struct pinctrl_dev *pctldev) +{ + struct adi_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev); + + return pinctrl->soc->nfunctions; +} + +static const char *adi_pinmux_get_func_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct adi_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev); + + return pinctrl->soc->functions[selector].name; +} + +static int adi_pinmux_get_groups(struct pinctrl_dev *pctldev, unsigned selector, + const char * const **groups, + unsigned * const num_groups) +{ + struct adi_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev); + + *groups = pinctrl->soc->functions[selector].groups; + *num_groups = pinctrl->soc->functions[selector].num_groups; + return 0; +} + +static int adi_pinmux_request_gpio(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, unsigned pin) +{ + struct gpio_port *port; + unsigned long flags; + u8 offset; + + port = container_of(range->gc, struct gpio_port, chip); + offset = pin_to_offset(range, pin); + + spin_lock_irqsave(&port->lock, flags); + + port_setup(port, offset, true); + + spin_unlock_irqrestore(&port->lock, flags); + + return 0; +} + +static struct pinmux_ops adi_pinmux_ops = { + .enable = adi_pinmux_enable, + .disable = adi_pinmux_disable, + .get_functions_count = adi_pinmux_get_funcs_count, + .get_function_name = adi_pinmux_get_func_name, + .get_function_groups = adi_pinmux_get_groups, + .gpio_request_enable = adi_pinmux_request_gpio, +}; + + +static struct pinctrl_desc adi_pinmux_desc = { + .name = DRIVER_NAME, + .pctlops = &adi_pctrl_ops, + .pmxops = &adi_pinmux_ops, + .owner = THIS_MODULE, +}; + +static int adi_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + return pinctrl_request_gpio(chip->base + offset); +} + +static void adi_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + pinctrl_free_gpio(chip->base + offset); +} + +static int adi_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct gpio_port *port; + unsigned long flags; + + port = container_of(chip, struct gpio_port, chip); + + spin_lock_irqsave(&port->lock, flags); + + writew(BIT(offset), &port->regs->dir_clear); + writew(readw(&port->regs->inen) | BIT(offset), &port->regs->inen); + + spin_unlock_irqrestore(&port->lock, flags); + + return 0; +} + +static void adi_gpio_set_value(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct gpio_port *port = container_of(chip, struct gpio_port, chip); + struct gpio_port_t *regs = port->regs; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + if (value) + writew(1 << offset, ®s->data_set); + else + writew(1 << offset, ®s->data_clear); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static int adi_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct gpio_port *port = container_of(chip, struct gpio_port, chip); + struct gpio_port_t *regs = port->regs; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + writew(readw(®s->inen) & ~(1 << offset), ®s->inen); + adi_gpio_set_value(chip, offset, value); + writew(1 << offset, ®s->dir_set); + + spin_unlock_irqrestore(&port->lock, flags); + + return 0; +} + +static int adi_gpio_get_value(struct gpio_chip *chip, unsigned offset) +{ + struct gpio_port *port = container_of(chip, struct gpio_port, chip); + struct gpio_port_t *regs = port->regs; + unsigned long flags; + int ret; + + spin_lock_irqsave(&port->lock, flags); + + ret = !!(readw(®s->data) & BIT(offset)); + + spin_unlock_irqrestore(&port->lock, flags); + + return ret; +} + +static int adi_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct gpio_port *port = container_of(chip, struct gpio_port, chip); + + if (port->irq_base >= 0) + return irq_find_mapping(port->domain, offset); + else + return irq_create_mapping(port->domain, offset); +} + +static int adi_pint_map_port(struct gpio_pint *pint, bool assign, u8 map, + struct irq_domain *domain) +{ + struct gpio_pint_regs *regs = pint->regs; + u32 map_mask; + + if (pint->map_count > 1) + return -EINVAL; + + pint->map_count++; + + /* The map_mask of each gpio port is a 16-bit duplicate + * of the 8-bit map. It can be set to either high 16 bits or low + * 16 bits of the pint assignment register. + */ + map_mask = (map << 8) | map; + if (assign) { + map_mask <<= PINT_HI_OFFSET; + writel((readl(®s->assign) & 0xFFFF) | map_mask, + ®s->assign); + } else + writel((readl(®s->assign) & 0xFFFF0000) | map_mask, + ®s->assign); + + pint->domain[assign] = domain; + + return 0; +} + +static int adi_gpio_pint_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct gpio_pint *pint; + + pint = devm_kzalloc(dev, sizeof(struct gpio_pint), GFP_KERNEL); + if (!pint) { + dev_err(dev, "Memory alloc failed\n"); + return -ENOMEM; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "Invalid mem resource\n"); + return -ENODEV; + } + + if (!devm_request_mem_region(dev, res->start, resource_size(res), + pdev->name)) { + dev_err(dev, "Region already claimed\n"); + return -EBUSY; + } + + pint->base = devm_ioremap(dev, res->start, resource_size(res)); + if (!pint->base) { + dev_err(dev, "Could not ioremap\n"); + return -ENOMEM; + } + + pint->regs = (struct gpio_pint_regs *)pint->base; + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(dev, "Invalid IRQ resource\n"); + return -ENODEV; + } + + spin_lock_init(&pint->lock); + + pint->irq = res->start; + pint->pint_map_port = adi_pint_map_port; + platform_set_drvdata(pdev, pint); + + irq_set_chained_handler(pint->irq, adi_gpio_handle_pint_irq); + irq_set_handler_data(pint->irq, pint); + + list_add_tail(&pint->node, &adi_pint_list); + + return 0; +} + +static int adi_gpio_pint_remove(struct platform_device *pdev) +{ + struct gpio_pint *pint = platform_get_drvdata(pdev); + + list_del(&pint->node); + irq_set_handler(pint->irq, handle_simple_irq); + + return 0; +} + +static int adi_gpio_irq_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + struct gpio_port *port = d->host_data; + + if (!port) + return -EINVAL; + + irq_set_chip_data(irq, port); + irq_set_chip_and_handler(irq, &adi_gpio_irqchip, + handle_level_irq); + + return 0; +} + +const struct irq_domain_ops adi_gpio_irq_domain_ops = { + .map = adi_gpio_irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int adi_gpio_init_int(struct gpio_port *port) +{ + struct device_node *node = port->dev->of_node; + struct gpio_pint *pint = port->pint; + int ret; + + port->domain = irq_domain_add_linear(node, port->width, + &adi_gpio_irq_domain_ops, port); + if (!port->domain) { + dev_err(port->dev, "Failed to create irqdomain\n"); + return -ENOSYS; + } + + /* According to BF54x and BF60x HRM, pin interrupt devices are not + * part of the GPIO port device. in GPIO interrupt mode, the GPIO + * pins of multiple port devices can be routed into one pin interrupt + * device. The mapping can be configured by setting pint assignment + * register with the mapping value of different GPIO port. This is + * done via function pint_map_port(). + */ + ret = pint->pint_map_port(port->pint, port->pint_assign, + port->pint_map, port->domain); + if (ret) + return ret; + + if (port->irq_base >= 0) { + ret = irq_create_strict_mappings(port->domain, port->irq_base, + 0, port->width); + if (ret) { + dev_err(port->dev, "Couldn't associate to domain\n"); + return ret; + } + } + + return 0; +} + +#define DEVNAME_SIZE 16 + +static int adi_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct adi_pinctrl_gpio_platform_data *pdata; + struct resource *res; + struct gpio_port *port; + char pinctrl_devname[DEVNAME_SIZE]; + static int gpio; + int ret = 0, ret1; + + pdata = dev->platform_data; + if (!pdata) + return -EINVAL; + + port = devm_kzalloc(dev, sizeof(struct gpio_port), GFP_KERNEL); + if (!port) { + dev_err(dev, "Memory alloc failed\n"); + return -ENOMEM; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "Invalid mem resource\n"); + return -ENODEV; + } + + if (!devm_request_mem_region(dev, res->start, resource_size(res), + pdev->name)) { + dev_err(dev, "Region already claimed\n"); + return -EBUSY; + } + + port->base = devm_ioremap(dev, res->start, resource_size(res)); + if (!port->base) { + dev_err(dev, "Could not ioremap\n"); + return -ENOMEM; + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) + port->irq_base = -1; + else + port->irq_base = res->start; + + port->width = pdata->port_width; + port->dev = dev; + port->regs = (struct gpio_port_t *)port->base; + port->pint_assign = pdata->pint_assign; + port->pint_map = pdata->pint_map; + + port->pint = find_gpio_pint(pdata->pint_id); + if (port->pint) { + ret = adi_gpio_init_int(port); + if (ret) + return ret; + } + + spin_lock_init(&port->lock); + + platform_set_drvdata(pdev, port); + + port->chip.label = "adi-gpio"; + port->chip.direction_input = adi_gpio_direction_input; + port->chip.get = adi_gpio_get_value; + port->chip.direction_output = adi_gpio_direction_output; + port->chip.set = adi_gpio_set_value; + port->chip.request = adi_gpio_request; + port->chip.free = adi_gpio_free; + port->chip.to_irq = adi_gpio_to_irq; + if (pdata->port_gpio_base > 0) + port->chip.base = pdata->port_gpio_base; + else + port->chip.base = gpio; + port->chip.ngpio = port->width; + gpio = port->chip.base + port->width; + + ret = gpiochip_add(&port->chip); + if (ret) { + dev_err(&pdev->dev, "Fail to add GPIO chip.\n"); + goto out_remove_domain; + } + + /* Add gpio pin range */ + snprintf(pinctrl_devname, DEVNAME_SIZE, "pinctrl-adi2.%d", + pdata->pinctrl_id); + pinctrl_devname[DEVNAME_SIZE - 1] = 0; + ret = gpiochip_add_pin_range(&port->chip, pinctrl_devname, + 0, pdata->port_pin_base, port->width); + if (ret) { + dev_err(&pdev->dev, "Fail to add pin range to %s.\n", + pinctrl_devname); + goto out_remove_gpiochip; + } + + list_add_tail(&port->node, &adi_gpio_port_list); + + return 0; + +out_remove_gpiochip: + ret1 = gpiochip_remove(&port->chip); +out_remove_domain: + if (port->pint) + irq_domain_remove(port->domain); + + return ret; +} + +static int adi_gpio_remove(struct platform_device *pdev) +{ + struct gpio_port *port = platform_get_drvdata(pdev); + int ret; + u8 offset; + + list_del(&port->node); + gpiochip_remove_pin_ranges(&port->chip); + ret = gpiochip_remove(&port->chip); + if (port->pint) { + for (offset = 0; offset < port->width; offset++) + irq_dispose_mapping(irq_find_mapping(port->domain, + offset)); + irq_domain_remove(port->domain); + } + + return ret; +} + +static int adi_pinctrl_probe(struct platform_device *pdev) +{ + struct adi_pinctrl *pinctrl; + + pinctrl = devm_kzalloc(&pdev->dev, sizeof(*pinctrl), GFP_KERNEL); + if (!pinctrl) + return -ENOMEM; + + pinctrl->dev = &pdev->dev; + + adi_pinctrl_soc_init(&pinctrl->soc); + + adi_pinmux_desc.pins = pinctrl->soc->pins; + adi_pinmux_desc.npins = pinctrl->soc->npins; + + /* Now register the pin controller and all pins it handles */ + pinctrl->pctl = pinctrl_register(&adi_pinmux_desc, &pdev->dev, pinctrl); + if (!pinctrl->pctl) { + dev_err(&pdev->dev, "could not register pinctrl ADI2 driver\n"); + return -EINVAL; + } + + platform_set_drvdata(pdev, pinctrl); + + return 0; +} + +static int adi_pinctrl_remove(struct platform_device *pdev) +{ + struct adi_pinctrl *pinctrl = platform_get_drvdata(pdev); + + pinctrl_unregister(pinctrl->pctl); + + return 0; +} + +static struct platform_driver adi_pinctrl_driver = { + .probe = adi_pinctrl_probe, + .remove = adi_pinctrl_remove, + .driver = { + .name = DRIVER_NAME, + }, +}; + +static struct platform_driver adi_gpio_pint_driver = { + .probe = adi_gpio_pint_probe, + .remove = adi_gpio_pint_remove, + .driver = { + .name = "adi-gpio-pint", + }, +}; + +static struct platform_driver adi_gpio_driver = { + .probe = adi_gpio_probe, + .remove = adi_gpio_remove, + .driver = { + .name = "adi-gpio", + }, +}; + +static int __init adi_pinctrl_setup(void) +{ + int ret; + + ret = platform_driver_register(&adi_pinctrl_driver); + if (ret) + return ret; + + ret = platform_driver_register(&adi_gpio_pint_driver); + if (ret) + goto pint_error; + + ret = platform_driver_register(&adi_gpio_driver); + if (ret) + goto gpio_error; + +#ifdef CONFIG_PM + register_syscore_ops(&gpio_pm_syscore_ops); +#endif + return ret; +gpio_error: + platform_driver_unregister(&adi_gpio_pint_driver); +pint_error: + platform_driver_unregister(&adi_pinctrl_driver); + + return ret; +} +arch_initcall(adi_pinctrl_setup); + +MODULE_AUTHOR("Sonic Zhang "); +MODULE_DESCRIPTION("ADI gpio2 pin control driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pinctrl/pinctrl-adi2.h b/drivers/pinctrl/pinctrl-adi2.h new file mode 100644 index 0000000..1f06f8d --- /dev/null +++ b/drivers/pinctrl/pinctrl-adi2.h @@ -0,0 +1,75 @@ +/* + * Pinctrl Driver for ADI GPIO2 controller + * + * Copyright 2007-2013 Analog Devices Inc. + * + * Licensed under the GPLv2 or later + */ + +#ifndef PINCTRL_PINCTRL_ADI2_H +#define PINCTRL_PINCTRL_ADI2_H + +#include + + /** + * struct adi_pin_group - describes a pin group + * @name: the name of this pin group + * @pins: an array of pins + * @num: the number of pins in this array + */ +struct adi_pin_group { + const char *name; + const unsigned *pins; + const unsigned num; +}; + +#define ADI_PIN_GROUP(n, p) \ + { \ + .name = n, \ + .pins = p, \ + .num = ARRAY_SIZE(p), \ + } + + /** + * struct adi_pmx_func - describes function mux setting of pin groups + * @name: the name of this function mux setting + * @groups: an array of pin groups + * @num_groups: the number of pin groups in this array + * @mux: the function mux setting array, end by zero + */ +struct adi_pmx_func { + const char *name; + const char * const *groups; + const unsigned num_groups; + const unsigned short *mux; +}; + +#define ADI_PMX_FUNCTION(n, g, m) \ + { \ + .name = n, \ + .groups = g, \ + .num_groups = ARRAY_SIZE(g), \ + .mux = m, \ + } + +/** + * struct adi_pinctrl_soc_data - ADI pin controller per-SoC configuration + * @functions: The functions supported on this SoC. + * @nfunction: The number of entries in @functions. + * @groups: An array describing all pin groups the pin SoC supports. + * @ngroups: The number of entries in @groups. + * @pins: An array describing all pins the pin controller affects. + * @npins: The number of entries in @pins. + */ +struct adi_pinctrl_soc_data { + const struct adi_pmx_func *functions; + int nfunctions; + const struct adi_pin_group *groups; + int ngroups; + const struct pinctrl_pin_desc *pins; + int npins; +}; + +void adi_pinctrl_soc_init(const struct adi_pinctrl_soc_data **soc); + +#endif /* PINCTRL_PINCTRL_ADI2_H */ diff --git a/include/linux/platform_data/pinctrl-adi2.h b/include/linux/platform_data/pinctrl-adi2.h new file mode 100644 index 0000000..8f91300 --- /dev/null +++ b/include/linux/platform_data/pinctrl-adi2.h @@ -0,0 +1,40 @@ +/* + * Pinctrl Driver for ADI GPIO2 controller + * + * Copyright 2007-2013 Analog Devices Inc. + * + * Licensed under the GPLv2 or later + */ + + +#ifndef PINCTRL_ADI2_H +#define PINCTRL_ADI2_H + +#include +#include + +/** + * struct adi_pinctrl_gpio_platform_data - Pinctrl gpio platform data + * for ADI GPIO2 device. + * + * @port_gpio_base: Optional global GPIO index of the GPIO bank. + * 0 means driver decides. + * @port_pin_base: Pin index of the pin controller device. + * @port_width: PIN number of the GPIO bank device + * @pint_id: GPIO PINT device id that this GPIO bank should map to. + * @pint_assign: The 32-bit GPIO PINT registers can be divided into 2 parts. A + * GPIO bank can be mapped into either low 16 bits[0] or high 16 + * bits[1] of each PINT register. + * @pint_map: GIOP bank mapping code in PINT device + */ +struct adi_pinctrl_gpio_platform_data { + unsigned int port_gpio_base; + unsigned int port_pin_base; + unsigned int port_width; + u8 pinctrl_id; + u8 pint_id; + bool pint_assign; + u8 pint_map; +}; + +#endif -- cgit v0.10.2 From ffb7fc0f6d2d630cb9e6e37e67993aaa342819f1 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Tue, 3 Sep 2013 16:29:00 +0800 Subject: blackfin: gpio: Remove none gpio lib code. - Remove non gpio lib code from blackfin architecture. - Limit the lagecy blackfin gpio driver to bf5xx processors only. - Remove unused definition of the pint power functions. Signed-off-by: Sonic Zhang Signed-off-by: Linus Walleij diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig index f78c9a2..a1199b2 100644 --- a/arch/blackfin/Kconfig +++ b/arch/blackfin/Kconfig @@ -52,6 +52,9 @@ config GENERIC_BUG config ZONE_DMA def_bool y +config GENERIC_GPIO + def_bool y + config FORCE_MAX_ZONEORDER int default "14" @@ -317,6 +320,10 @@ config BF53x depends on (BF531 || BF532 || BF533 || BF534 || BF536 || BF537) default y +config GPIO_ADI + def_bool y + depends on (BF51x || BF52x || BF53x || BF538 || BF539 || BF561) + config MEM_MT48LC64M4A2FB_7E bool depends on (BFIN533_STAMP) diff --git a/arch/blackfin/include/asm/gpio.h b/arch/blackfin/include/asm/gpio.h index 98d0133..99d338c 100644 --- a/arch/blackfin/include/asm/gpio.h +++ b/arch/blackfin/include/asm/gpio.h @@ -25,8 +25,12 @@ #ifndef __ASSEMBLY__ +#ifndef CONFIG_PINCTRL + #include -#include +#include +#include +#include /*********************************************************** * @@ -45,7 +49,6 @@ * MODIFICATION HISTORY : **************************************************************/ -#if !BFIN_GPIO_PINT void set_gpio_dir(unsigned, unsigned short); void set_gpio_inen(unsigned, unsigned short); void set_gpio_polar(unsigned, unsigned short); @@ -115,7 +118,6 @@ struct gpio_port_t { unsigned short dummy16; unsigned short inen; }; -#endif #ifdef BFIN_SPECIAL_GPIO_BANKS void bfin_special_gpio_free(unsigned gpio); @@ -127,25 +129,21 @@ void bfin_special_gpio_pm_hibernate_suspend(void); #endif #ifdef CONFIG_PM -int bfin_pm_standby_ctrl(unsigned ctrl); +void bfin_gpio_pm_hibernate_restore(void); +void bfin_gpio_pm_hibernate_suspend(void); +int bfin_gpio_pm_wakeup_ctrl(unsigned gpio, unsigned ctrl); +int bfin_gpio_pm_standby_ctrl(unsigned ctrl); static inline int bfin_pm_standby_setup(void) { - return bfin_pm_standby_ctrl(1); + return bfin_gpio_pm_standby_ctrl(1); } static inline void bfin_pm_standby_restore(void) { - bfin_pm_standby_ctrl(0); + bfin_gpio_pm_standby_ctrl(0); } -void bfin_gpio_pm_hibernate_restore(void); -void bfin_gpio_pm_hibernate_suspend(void); -void bfin_pint_suspend(void); -void bfin_pint_resume(void); - -# if !BFIN_GPIO_PINT -int gpio_pm_wakeup_ctrl(unsigned gpio, unsigned ctrl); struct gpio_port_s { unsigned short data; @@ -161,7 +159,6 @@ struct gpio_port_s { unsigned short reserved; unsigned short mux; }; -# endif #endif /*CONFIG_PM*/ /*********************************************************** @@ -178,36 +175,29 @@ struct gpio_port_s { ************************************************************* * MODIFICATION HISTORY : **************************************************************/ - -int bfin_gpio_request(unsigned gpio, const char *label); -void bfin_gpio_free(unsigned gpio); int bfin_gpio_irq_request(unsigned gpio, const char *label); void bfin_gpio_irq_free(unsigned gpio); -int bfin_gpio_direction_input(unsigned gpio); -int bfin_gpio_direction_output(unsigned gpio, int value); -int bfin_gpio_get_value(unsigned gpio); -void bfin_gpio_set_value(unsigned gpio, int value); +void bfin_gpio_irq_prepare(unsigned gpio); + +static inline int irq_to_gpio(unsigned irq) +{ + return irq - GPIO_IRQ_BASE; +} +#endif /* CONFIG_PINCTRL */ #include #include -#ifdef CONFIG_GPIOLIB #include /* cansleep wrappers */ static inline int gpio_get_value(unsigned int gpio) { - if (gpio < MAX_BLACKFIN_GPIOS) - return bfin_gpio_get_value(gpio); - else - return __gpio_get_value(gpio); + return __gpio_get_value(gpio); } static inline void gpio_set_value(unsigned int gpio, int value) { - if (gpio < MAX_BLACKFIN_GPIOS) - bfin_gpio_set_value(gpio, value); - else - __gpio_set_value(gpio, value); + __gpio_set_value(gpio, value); } static inline int gpio_cansleep(unsigned int gpio) @@ -219,113 +209,6 @@ static inline int gpio_to_irq(unsigned gpio) { return __gpio_to_irq(gpio); } - -#else /* !CONFIG_GPIOLIB */ - -static inline int gpio_request(unsigned gpio, const char *label) -{ - return bfin_gpio_request(gpio, label); -} - -static inline void gpio_free(unsigned gpio) -{ - return bfin_gpio_free(gpio); -} - -static inline int gpio_direction_input(unsigned gpio) -{ - return bfin_gpio_direction_input(gpio); -} - -static inline int gpio_direction_output(unsigned gpio, int value) -{ - return bfin_gpio_direction_output(gpio, value); -} - -static inline int gpio_set_debounce(unsigned gpio, unsigned debounce) -{ - return -EINVAL; -} - -static inline int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) -{ - int err; - - err = bfin_gpio_request(gpio, label); - if (err) - return err; - - if (flags & GPIOF_DIR_IN) - err = bfin_gpio_direction_input(gpio); - else - err = bfin_gpio_direction_output(gpio, - (flags & GPIOF_INIT_HIGH) ? 1 : 0); - - if (err) - bfin_gpio_free(gpio); - - return err; -} - -static inline int gpio_request_array(const struct gpio *array, size_t num) -{ - int i, err; - - for (i = 0; i < num; i++, array++) { - err = gpio_request_one(array->gpio, array->flags, array->label); - if (err) - goto err_free; - } - return 0; - -err_free: - while (i--) - bfin_gpio_free((--array)->gpio); - return err; -} - -static inline void gpio_free_array(const struct gpio *array, size_t num) -{ - while (num--) - bfin_gpio_free((array++)->gpio); -} - -static inline int __gpio_get_value(unsigned gpio) -{ - return bfin_gpio_get_value(gpio); -} - -static inline void __gpio_set_value(unsigned gpio, int value) -{ - return bfin_gpio_set_value(gpio, value); -} - -static inline int gpio_get_value(unsigned gpio) -{ - return __gpio_get_value(gpio); -} - -static inline void gpio_set_value(unsigned gpio, int value) -{ - return __gpio_set_value(gpio, value); -} - -static inline int gpio_to_irq(unsigned gpio) -{ - if (likely(gpio < MAX_BLACKFIN_GPIOS)) - return gpio + GPIO_IRQ_BASE; - - return -EINVAL; -} - -#include /* cansleep wrappers */ -#endif /* !CONFIG_GPIOLIB */ - -static inline int irq_to_gpio(unsigned irq) -{ - return (irq - GPIO_IRQ_BASE); -} - #endif /* __ASSEMBLY__ */ #endif /* __ARCH_BLACKFIN_GPIO_H__ */ diff --git a/arch/blackfin/kernel/Makefile b/arch/blackfin/kernel/Makefile index 735f24e..703dc7c 100644 --- a/arch/blackfin/kernel/Makefile +++ b/arch/blackfin/kernel/Makefile @@ -7,7 +7,7 @@ extra-y := vmlinux.lds obj-y := \ entry.o process.o bfin_ksyms.o ptrace.o setup.o signal.o \ sys_bfin.o traps.o irqchip.o dma-mapping.o flat.o \ - fixed_code.o reboot.o bfin_gpio.o bfin_dma.o \ + fixed_code.o reboot.o bfin_dma.o \ exception.o dumpstack.o ifeq ($(CONFIG_GENERIC_CLOCKEVENTS),y) @@ -16,6 +16,7 @@ else obj-y += time.o endif +obj-$(CONFIG_GPIO_ADI) += bfin_gpio.o obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o obj-$(CONFIG_FUNCTION_TRACER) += ftrace-entry.o obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o -- cgit v0.10.2 From 6424de5af1942e33ce0267cbfe9ff12e387d93d6 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 9 Sep 2013 10:33:49 +0100 Subject: gpiolib: Provide helper macros for logging of GPIO events Currently many but not all GPIO log messages log the GPIO number and the formats vary. Ensure that this is done consistently by defining logging helpers which take the GPIO descriptor. The will help people pattern matching on logs and providing the number makes the log messages that omitted it more useful. Signed-off-by: Mark Brown Acked-by: Alexandre Courbot Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 86ef346..417ee75 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -102,6 +102,18 @@ static int gpiod_export_link(struct device *dev, const char *name, static int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value); static void gpiod_unexport(struct gpio_desc *desc); +#define gpiod_emerg(desc, fmt, ...) \ + pr_emerg("gpio-%d: " fmt, desc_to_gpio(desc), ##__VA_ARGS__) +#define gpiod_crit(desc, fmt, ...) \ + pr_crit("gpio-%d: " fmt, desc_to_gpio(desc), ##__VA_ARGS__) +#define gpiod_err(desc, fmt, ...) \ + pr_err("gpio-%d: " fmt, desc_to_gpio(desc), ##__VA_ARGS__) +#define gpiod_warn(desc, fmt, ...) \ + pr_warn("gpio-%d: " fmt, desc_to_gpio(desc), ##__VA_ARGS__) +#define gpiod_info(desc, fmt, ...) \ + pr_info("gpio-%d: " fmt, desc_to_gpio(desc), ##__VA_ARGS__) +#define gpiod_dbg(desc, fmt, ...) \ + pr_debug("gpio-%d: " fmt, desc_to_gpio(desc), ##__VA_ARGS__) static inline void desc_set_label(struct gpio_desc *d, const char *label) { @@ -1635,8 +1647,9 @@ static int gpiod_direction_input(struct gpio_desc *desc) chip = desc->chip; if (!chip->get || !chip->direction_input) { - pr_warn("%s: missing get() or direction_input() operations\n", - __func__); + gpiod_warn(desc, + "%s: missing get() or direction_input() operations\n", + __func__); return -EIO; } @@ -1656,8 +1669,7 @@ static int gpiod_direction_input(struct gpio_desc *desc) if (status) { status = chip->request(chip, offset); if (status < 0) { - pr_debug("GPIO-%d: chip request fail, %d\n", - desc_to_gpio(desc), status); + gpiod_dbg(desc, "chip request fail, %d\n", status); /* and it's not available to anyone else ... * gpio_request() is the fully clean solution. */ @@ -1675,8 +1687,7 @@ lose: fail: spin_unlock_irqrestore(&gpio_lock, flags); if (status) - pr_debug("%s: gpio-%d status %d\n", __func__, - desc_to_gpio(desc), status); + gpiod_dbg(desc, "%s status %d\n", __func__, status); return status; } @@ -1708,8 +1719,9 @@ static int gpiod_direction_output(struct gpio_desc *desc, int value) chip = desc->chip; if (!chip->set || !chip->direction_output) { - pr_warn("%s: missing set() or direction_output() operations\n", - __func__); + gpiod_warn(desc, + "%s: missing set() or direction_output() operations\n", + __func__); return -EIO; } @@ -1729,8 +1741,7 @@ static int gpiod_direction_output(struct gpio_desc *desc, int value) if (status) { status = chip->request(chip, offset); if (status < 0) { - pr_debug("GPIO-%d: chip request fail, %d\n", - desc_to_gpio(desc), status); + gpiod_dbg(desc, "chip request fail, %d\n", status); /* and it's not available to anyone else ... * gpio_request() is the fully clean solution. */ @@ -1748,8 +1759,7 @@ lose: fail: spin_unlock_irqrestore(&gpio_lock, flags); if (status) - pr_debug("%s: gpio-%d status %d\n", __func__, - desc_to_gpio(desc), status); + gpiod_dbg(desc, "%s: gpio status %d\n", __func__, status); return status; } @@ -1781,8 +1791,9 @@ static int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) chip = desc->chip; if (!chip->set || !chip->set_debounce) { - pr_debug("%s: missing set() or set_debounce() operations\n", - __func__); + gpiod_dbg(desc, + "%s: missing set() or set_debounce() operations\n", + __func__); return -ENOTSUPP; } @@ -1804,8 +1815,7 @@ static int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) fail: spin_unlock_irqrestore(&gpio_lock, flags); if (status) - pr_debug("%s: gpio-%d status %d\n", __func__, - desc_to_gpio(desc), status); + gpiod_dbg(desc, "%s: status %d\n", __func__, status); return status; } @@ -1893,8 +1903,9 @@ static void _gpio_set_open_drain_value(struct gpio_desc *desc, int value) } trace_gpio_direction(desc_to_gpio(desc), value, err); if (err < 0) - pr_err("%s: Error in set_value for open drain gpio%d err %d\n", - __func__, desc_to_gpio(desc), err); + gpiod_err(desc, + "%s: Error in set_value for open drain err %d\n", + __func__, err); } /* @@ -1920,8 +1931,9 @@ static void _gpio_set_open_source_value(struct gpio_desc *desc, int value) } trace_gpio_direction(desc_to_gpio(desc), !value, err); if (err < 0) - pr_err("%s: Error in set_value for open source gpio%d err %d\n", - __func__, desc_to_gpio(desc), err); + gpiod_err(desc, + "%s: Error in set_value for open source err %d\n", + __func__, err); } /** -- cgit v0.10.2 From 7b17b59feaa8b0a54a333005b87ad7ea804d021f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 9 Sep 2013 10:33:50 +0100 Subject: gpiolib: Include GPIO label in log messages for GPIOs Provide the human readable label for the GPIO as well as the number when we are recording it in order to improve the readability of log messages. Signed-off-by: Mark Brown Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 417ee75..8048c3d 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -102,6 +102,26 @@ static int gpiod_export_link(struct device *dev, const char *name, static int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value); static void gpiod_unexport(struct gpio_desc *desc); +#ifdef CONFIG_DEBUG_FS +#define gpiod_emerg(desc, fmt, ...) \ + pr_emerg("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label, \ + ##__VA_ARGS__) +#define gpiod_crit(desc, fmt, ...) \ + pr_crit("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label, \ + ##__VA_ARGS__) +#define gpiod_err(desc, fmt, ...) \ + pr_err("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label, \ + ##__VA_ARGS__) +#define gpiod_warn(desc, fmt, ...) \ + pr_warn("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label, \ + ##__VA_ARGS__) +#define gpiod_info(desc, fmt, ...) \ + pr_info("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label, \ + ##__VA_ARGS__) +#define gpiod_dbg(desc, fmt, ...) \ + pr_debug("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label, \ + ##__VA_ARGS__) +#else #define gpiod_emerg(desc, fmt, ...) \ pr_emerg("gpio-%d: " fmt, desc_to_gpio(desc), ##__VA_ARGS__) #define gpiod_crit(desc, fmt, ...) \ @@ -114,6 +134,7 @@ static void gpiod_unexport(struct gpio_desc *desc); pr_info("gpio-%d: " fmt, desc_to_gpio(desc), ##__VA_ARGS__) #define gpiod_dbg(desc, fmt, ...) \ pr_debug("gpio-%d: " fmt, desc_to_gpio(desc), ##__VA_ARGS__) +#endif static inline void desc_set_label(struct gpio_desc *d, const char *label) { -- cgit v0.10.2 From ca6af7b96ab621371f5f408e3fe7bd1e5cb063aa Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 3 Sep 2013 19:58:03 +0530 Subject: gpio: palmas: add support for TPS80036 TI Palmas series device TPS80036 supports 16 GPIOs. Register its all 16 gpios when this device is selected. Signed-off-by: Laxman Dewangan Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c index 8588af0..11801e98 100644 --- a/drivers/gpio/gpio-palmas.c +++ b/drivers/gpio/gpio-palmas.c @@ -31,6 +31,10 @@ struct palmas_gpio { struct palmas *palmas; }; +struct palmas_device_data { + int ngpio; +}; + static inline struct palmas_gpio *to_palmas_gpio(struct gpio_chip *chip) { return container_of(chip, struct palmas_gpio, gpio_chip); @@ -42,23 +46,26 @@ static int palmas_gpio_get(struct gpio_chip *gc, unsigned offset) struct palmas *palmas = pg->palmas; unsigned int val; int ret; + unsigned int reg; + int gpio16 = (offset/8); + + offset %= 8; + reg = (gpio16) ? PALMAS_GPIO_DATA_DIR2 : PALMAS_GPIO_DATA_DIR; - ret = palmas_read(palmas, PALMAS_GPIO_BASE, PALMAS_GPIO_DATA_DIR, &val); + ret = palmas_read(palmas, PALMAS_GPIO_BASE, reg, &val); if (ret < 0) { - dev_err(gc->dev, "GPIO_DATA_DIR read failed, err = %d\n", ret); + dev_err(gc->dev, "Reg 0x%02x read failed, %d\n", reg, ret); return ret; } - if (val & (1 << offset)) { - ret = palmas_read(palmas, PALMAS_GPIO_BASE, - PALMAS_GPIO_DATA_OUT, &val); - } else { - ret = palmas_read(palmas, PALMAS_GPIO_BASE, - PALMAS_GPIO_DATA_IN, &val); - } + if (val & BIT(offset)) + reg = (gpio16) ? PALMAS_GPIO_DATA_OUT2 : PALMAS_GPIO_DATA_OUT; + else + reg = (gpio16) ? PALMAS_GPIO_DATA_IN2 : PALMAS_GPIO_DATA_IN; + + ret = palmas_read(palmas, PALMAS_GPIO_BASE, reg, &val); if (ret < 0) { - dev_err(gc->dev, "GPIO_DATA_IN/OUT read failed, err = %d\n", - ret); + dev_err(gc->dev, "Reg 0x%02x read failed, %d\n", reg, ret); return ret; } return !!(val & BIT(offset)); @@ -70,17 +77,20 @@ static void palmas_gpio_set(struct gpio_chip *gc, unsigned offset, struct palmas_gpio *pg = to_palmas_gpio(gc); struct palmas *palmas = pg->palmas; int ret; + unsigned int reg; + int gpio16 = (offset/8); - if (value) - ret = palmas_write(palmas, PALMAS_GPIO_BASE, - PALMAS_GPIO_SET_DATA_OUT, BIT(offset)); + offset %= 8; + if (gpio16) + reg = (value) ? + PALMAS_GPIO_SET_DATA_OUT2 : PALMAS_GPIO_CLEAR_DATA_OUT2; else - ret = palmas_write(palmas, PALMAS_GPIO_BASE, - PALMAS_GPIO_CLEAR_DATA_OUT, BIT(offset)); + reg = (value) ? + PALMAS_GPIO_SET_DATA_OUT : PALMAS_GPIO_CLEAR_DATA_OUT; + + ret = palmas_write(palmas, PALMAS_GPIO_BASE, reg, BIT(offset)); if (ret < 0) - dev_err(gc->dev, "%s write failed, err = %d\n", - (value) ? "GPIO_SET_DATA_OUT" : "GPIO_CLEAR_DATA_OUT", - ret); + dev_err(gc->dev, "Reg 0x%02x write failed, %d\n", reg, ret); } static int palmas_gpio_output(struct gpio_chip *gc, unsigned offset, @@ -89,14 +99,19 @@ static int palmas_gpio_output(struct gpio_chip *gc, unsigned offset, struct palmas_gpio *pg = to_palmas_gpio(gc); struct palmas *palmas = pg->palmas; int ret; + unsigned int reg; + int gpio16 = (offset/8); + + offset %= 8; + reg = (gpio16) ? PALMAS_GPIO_DATA_DIR2 : PALMAS_GPIO_DATA_DIR; /* Set the initial value */ palmas_gpio_set(gc, offset, value); - ret = palmas_update_bits(palmas, PALMAS_GPIO_BASE, - PALMAS_GPIO_DATA_DIR, BIT(offset), BIT(offset)); + ret = palmas_update_bits(palmas, PALMAS_GPIO_BASE, reg, + BIT(offset), BIT(offset)); if (ret < 0) - dev_err(gc->dev, "GPIO_DATA_DIR write failed, err = %d\n", ret); + dev_err(gc->dev, "Reg 0x%02x update failed, %d\n", reg, ret); return ret; } @@ -105,11 +120,15 @@ static int palmas_gpio_input(struct gpio_chip *gc, unsigned offset) struct palmas_gpio *pg = to_palmas_gpio(gc); struct palmas *palmas = pg->palmas; int ret; + unsigned int reg; + int gpio16 = (offset/8); + + offset %= 8; + reg = (gpio16) ? PALMAS_GPIO_DATA_DIR2 : PALMAS_GPIO_DATA_DIR; - ret = palmas_update_bits(palmas, PALMAS_GPIO_BASE, - PALMAS_GPIO_DATA_DIR, BIT(offset), 0); + ret = palmas_update_bits(palmas, PALMAS_GPIO_BASE, reg, BIT(offset), 0); if (ret < 0) - dev_err(gc->dev, "GPIO_DATA_DIR write failed, err = %d\n", ret); + dev_err(gc->dev, "Reg 0x%02x update failed, %d\n", reg, ret); return ret; } @@ -121,12 +140,36 @@ static int palmas_gpio_to_irq(struct gpio_chip *gc, unsigned offset) return palmas_irq_get_virq(palmas, PALMAS_GPIO_0_IRQ + offset); } +static const struct palmas_device_data palmas_dev_data = { + .ngpio = 8, +}; + +static const struct palmas_device_data tps80036_dev_data = { + .ngpio = 16, +}; + +static struct of_device_id of_palmas_gpio_match[] = { + { .compatible = "ti,palmas-gpio", .data = &palmas_dev_data,}, + { .compatible = "ti,tps65913-gpio", .data = &palmas_dev_data,}, + { .compatible = "ti,tps65914-gpio", .data = &palmas_dev_data,}, + { .compatible = "ti,tps80036-gpio", .data = &tps80036_dev_data,}, + { }, +}; +MODULE_DEVICE_TABLE(of, of_palmas_gpio_match); + static int palmas_gpio_probe(struct platform_device *pdev) { struct palmas *palmas = dev_get_drvdata(pdev->dev.parent); struct palmas_platform_data *palmas_pdata; struct palmas_gpio *palmas_gpio; int ret; + const struct of_device_id *match; + const struct palmas_device_data *dev_data; + + match = of_match_device(of_palmas_gpio_match, &pdev->dev); + dev_data = match->data; + if (!dev_data) + dev_data = &palmas_dev_data; palmas_gpio = devm_kzalloc(&pdev->dev, sizeof(*palmas_gpio), GFP_KERNEL); @@ -138,7 +181,7 @@ static int palmas_gpio_probe(struct platform_device *pdev) palmas_gpio->palmas = palmas; palmas_gpio->gpio_chip.owner = THIS_MODULE; palmas_gpio->gpio_chip.label = dev_name(&pdev->dev); - palmas_gpio->gpio_chip.ngpio = 8; + palmas_gpio->gpio_chip.ngpio = dev_data->ngpio; palmas_gpio->gpio_chip.can_sleep = 1; palmas_gpio->gpio_chip.direction_input = palmas_gpio_input; palmas_gpio->gpio_chip.direction_output = palmas_gpio_output; @@ -172,15 +215,6 @@ static int palmas_gpio_remove(struct platform_device *pdev) return gpiochip_remove(&palmas_gpio->gpio_chip); } -static struct of_device_id of_palmas_gpio_match[] = { - { .compatible = "ti,palmas-gpio"}, - { .compatible = "ti,tps65913-gpio"}, - { .compatible = "ti,tps65914-gpio"}, - { .compatible = "ti,tps80036-gpio"}, - { }, -}; -MODULE_DEVICE_TABLE(of, of_palmas_gpio_match); - static struct platform_driver palmas_gpio_driver = { .driver.name = "palmas-gpio", .driver.owner = THIS_MODULE, -- cgit v0.10.2 From b59278548e2383976f7db5fd3389f9116a6f240d Mon Sep 17 00:00:00 2001 From: Ian Molton Date: Mon, 2 Sep 2013 16:44:55 +0100 Subject: emev2: GPIOLIB: Enable support for OF EMEV2 is now a DT platform, however the GPIO driver cannot be used from a DT file since it does not fill out the of_node field in its gpio_chip structure. Signed-off-by: Ian Molton Reviewed-by: Simon Horman Signed-off-by: Linus Walleij diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b6ed304..a6d67c5 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -129,7 +129,7 @@ config GPIO_IT8761E config GPIO_EM tristate "Emma Mobile GPIO" - depends on ARM + depends on ARM && OF_GPIO help Say yes here to support GPIO on Renesas Emma Mobile SoCs. diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c index c6e1f08..160d759 100644 --- a/drivers/gpio/gpio-em.c +++ b/drivers/gpio/gpio-em.c @@ -319,6 +319,7 @@ static int em_gio_probe(struct platform_device *pdev) } gpio_chip = &p->gpio_chip; + gpio_chip->of_node = pdev->dev.of_node; gpio_chip->direction_input = em_gio_direction_input; gpio_chip->get = em_gio_get; gpio_chip->direction_output = em_gio_direction_output; -- cgit v0.10.2 From 01984a47e21a7d36cea0d6c0933c8173391721fc Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 17 Sep 2013 09:42:46 +0530 Subject: ASoC: imx-sgtl5000: Use devm_snd_soc_register_card devm_snd_soc_register_card makes code simpler. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c index 46c5b4f..78f86d8 100644 --- a/sound/soc/fsl/imx-sgtl5000.c +++ b/sound/soc/fsl/imx-sgtl5000.c @@ -159,7 +159,7 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) data->card.dapm_widgets = imx_sgtl5000_dapm_widgets; data->card.num_dapm_widgets = ARRAY_SIZE(imx_sgtl5000_dapm_widgets); - ret = snd_soc_register_card(&data->card); + ret = devm_snd_soc_register_card(&pdev->dev, &data->card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); goto fail; @@ -180,15 +180,6 @@ fail: return ret; } -static int imx_sgtl5000_remove(struct platform_device *pdev) -{ - struct imx_sgtl5000_data *data = platform_get_drvdata(pdev); - - snd_soc_unregister_card(&data->card); - - return 0; -} - static const struct of_device_id imx_sgtl5000_dt_ids[] = { { .compatible = "fsl,imx-audio-sgtl5000", }, { /* sentinel */ } @@ -202,7 +193,6 @@ static struct platform_driver imx_sgtl5000_driver = { .of_match_table = imx_sgtl5000_dt_ids, }, .probe = imx_sgtl5000_probe, - .remove = imx_sgtl5000_remove, }; module_platform_driver(imx_sgtl5000_driver); -- cgit v0.10.2 From ff27d9b3d6dd26013537e4f8162627169ca92af4 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 17 Sep 2013 09:42:47 +0530 Subject: ASoC: imx-spdif: Use devm_snd_soc_register_card devm_snd_soc_register_card makes code simpler. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c index 816013b..8499d52 100644 --- a/sound/soc/fsl/imx-spdif.c +++ b/sound/soc/fsl/imx-spdif.c @@ -87,7 +87,7 @@ static int imx_spdif_audio_probe(struct platform_device *pdev) if (ret) goto error_dir; - ret = snd_soc_register_card(&data->card); + ret = devm_snd_soc_register_card(&pdev->dev, &data->card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card failed: %d\n", ret); goto error_dir; @@ -119,8 +119,6 @@ static int imx_spdif_audio_remove(struct platform_device *pdev) if (data->txdev) platform_device_unregister(data->txdev); - snd_soc_unregister_card(&data->card); - return 0; } -- cgit v0.10.2 From eafbae2919e24b6ed7078464b2c8b2313ac0252c Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 17 Sep 2013 09:42:48 +0530 Subject: ASoC: imx-wm8962: Use devm_snd_soc_register_card devm_snd_soc_register_card makes code simpler. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/imx-wm8962.c b/sound/soc/fsl/imx-wm8962.c index 722afe6..6c60666 100644 --- a/sound/soc/fsl/imx-wm8962.c +++ b/sound/soc/fsl/imx-wm8962.c @@ -266,7 +266,7 @@ static int imx_wm8962_probe(struct platform_device *pdev) data->card.late_probe = imx_wm8962_late_probe; data->card.set_bias_level = imx_wm8962_set_bias_level; - ret = snd_soc_register_card(&data->card); + ret = devm_snd_soc_register_card(&pdev->dev, &data->card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); goto clk_fail; @@ -296,7 +296,6 @@ static int imx_wm8962_remove(struct platform_device *pdev) if (!IS_ERR(data->codec_clk)) clk_disable_unprepare(data->codec_clk); - snd_soc_unregister_card(&data->card); return 0; } -- cgit v0.10.2 From 33c89c30afd7b6a309638d8d9b88481118d0a3ec Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 17 Sep 2013 09:49:04 +0530 Subject: ASoC: mfld: Use devm_snd_soc_register_card devm_snd_soc_register_card makes code simpler. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/sound/soc/mid-x86/mfld_machine.c b/sound/soc/mid-x86/mfld_machine.c index ee36384..d3d4c32 100644 --- a/sound/soc/mid-x86/mfld_machine.c +++ b/sound/soc/mid-x86/mfld_machine.c @@ -400,7 +400,7 @@ static int snd_mfld_mc_probe(struct platform_device *pdev) } /* register the soc card */ snd_soc_card_mfld.dev = &pdev->dev; - ret_val = snd_soc_register_card(&snd_soc_card_mfld); + ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_mfld); if (ret_val) { pr_debug("snd_soc_register_card failed %d\n", ret_val); return ret_val; @@ -410,20 +410,12 @@ static int snd_mfld_mc_probe(struct platform_device *pdev) return 0; } -static int snd_mfld_mc_remove(struct platform_device *pdev) -{ - pr_debug("snd_mfld_mc_remove called\n"); - snd_soc_unregister_card(&snd_soc_card_mfld); - return 0; -} - static struct platform_driver snd_mfld_mc_driver = { .driver = { .owner = THIS_MODULE, .name = "msic_audio", }, .probe = snd_mfld_mc_probe, - .remove = snd_mfld_mc_remove, }; module_platform_driver(snd_mfld_mc_driver); -- cgit v0.10.2 From 67a48b8181b0c981c59acf06f056e00184d3debd Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 17 Sep 2013 09:53:59 +0530 Subject: ASoC: omap-twl4030: Use devm_snd_soc_register_card devm_snd_soc_register_card makes code simpler. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/sound/soc/omap/omap-twl4030.c b/sound/soc/omap/omap-twl4030.c index 2a9324f..6a8d6b5 100644 --- a/sound/soc/omap/omap-twl4030.c +++ b/sound/soc/omap/omap-twl4030.c @@ -338,9 +338,9 @@ static int omap_twl4030_probe(struct platform_device *pdev) } snd_soc_card_set_drvdata(card, priv); - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { - dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", + dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n", ret); return ret; } @@ -357,7 +357,6 @@ static int omap_twl4030_remove(struct platform_device *pdev) snd_soc_jack_free_gpios(&priv->hs_jack, ARRAY_SIZE(hs_jack_gpios), hs_jack_gpios); - snd_soc_unregister_card(card); return 0; } -- cgit v0.10.2 From 256218ae65d2e59ef5d257355791a62af7d31b3c Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 17 Sep 2013 10:13:49 +0530 Subject: ASoC: fsl_spdif: Use devm_snd_soc_register_component devm_snd_soc_register_component makes code simpler. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 3920c3e..44378e6 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -1172,23 +1172,16 @@ static int fsl_spdif_probe(struct platform_device *pdev) /* Register with ASoC */ dev_set_drvdata(&pdev->dev, spdif_priv); - ret = snd_soc_register_component(&pdev->dev, &fsl_spdif_component, - &spdif_priv->cpu_dai_drv, 1); + ret = devm_snd_soc_register_component(&pdev->dev, &fsl_spdif_component, + &spdif_priv->cpu_dai_drv, 1); if (ret) { dev_err(&pdev->dev, "failed to register DAI: %d\n", ret); return ret; } ret = imx_pcm_dma_init(pdev); - if (ret) { + if (ret) dev_err(&pdev->dev, "imx_pcm_dma_init failed: %d\n", ret); - goto error_component; - } - - return ret; - -error_component: - snd_soc_unregister_component(&pdev->dev); return ret; } @@ -1196,7 +1189,6 @@ error_component: static int fsl_spdif_remove(struct platform_device *pdev) { imx_pcm_dma_exit(pdev); - snd_soc_unregister_component(&pdev->dev); return 0; } -- cgit v0.10.2 From fcd70eb50e2d572a67839410aa30f6b545355980 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 17 Sep 2013 10:20:09 +0530 Subject: ASoC: mxs-saif: Use devm_snd_soc_register_component devm_snd_soc_register_component makes code simpler. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index b56b8a0..14152f6 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -768,8 +768,8 @@ static int mxs_saif_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "failed to init clocks\n"); } - ret = snd_soc_register_component(&pdev->dev, &mxs_saif_component, - &mxs_saif_dai, 1); + ret = devm_snd_soc_register_component(&pdev->dev, &mxs_saif_component, + &mxs_saif_dai, 1); if (ret) { dev_err(&pdev->dev, "register DAI failed\n"); return ret; @@ -778,21 +778,15 @@ static int mxs_saif_probe(struct platform_device *pdev) ret = mxs_pcm_platform_register(&pdev->dev); if (ret) { dev_err(&pdev->dev, "register PCM failed: %d\n", ret); - goto failed_pdev_alloc; + return ret; } return 0; - -failed_pdev_alloc: - snd_soc_unregister_component(&pdev->dev); - - return ret; } static int mxs_saif_remove(struct platform_device *pdev) { mxs_pcm_platform_unregister(&pdev->dev); - snd_soc_unregister_component(&pdev->dev); return 0; } -- cgit v0.10.2 From 6c3cc302a48acdd3797d694ae4a2be82bb71a05a Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 17 Sep 2013 10:28:02 +0530 Subject: ASoC: omap-mcpdm: Use devm_snd_soc_register_component devm_snd_soc_register_component makes code simpler. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c index 90d2a7c..cd9ee16 100644 --- a/sound/soc/omap/omap-mcpdm.c +++ b/sound/soc/omap/omap-mcpdm.c @@ -490,14 +490,9 @@ static int asoc_mcpdm_probe(struct platform_device *pdev) mcpdm->dev = &pdev->dev; - return snd_soc_register_component(&pdev->dev, &omap_mcpdm_component, - &omap_mcpdm_dai, 1); -} - -static int asoc_mcpdm_remove(struct platform_device *pdev) -{ - snd_soc_unregister_component(&pdev->dev); - return 0; + return devm_snd_soc_register_component(&pdev->dev, + &omap_mcpdm_component, + &omap_mcpdm_dai, 1); } static const struct of_device_id omap_mcpdm_of_match[] = { @@ -514,7 +509,6 @@ static struct platform_driver asoc_mcpdm_driver = { }, .probe = asoc_mcpdm_probe, - .remove = asoc_mcpdm_remove, }; module_platform_driver(asoc_mcpdm_driver); -- cgit v0.10.2 From 9ff50721e47ab0abb8b93159170f67262886ef0d Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 17 Sep 2013 10:32:48 +0530 Subject: ASoC: mmp-sspa: Use devm_snd_soc_register_component devm_snd_soc_register_component makes code simpler. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/mmp-sspa.c b/sound/soc/pxa/mmp-sspa.c index 41752a5..5bf5f1f 100644 --- a/sound/soc/pxa/mmp-sspa.c +++ b/sound/soc/pxa/mmp-sspa.c @@ -455,8 +455,8 @@ static int asoc_mmp_sspa_probe(struct platform_device *pdev) priv->dai_fmt = (unsigned int) -1; platform_set_drvdata(pdev, priv); - return snd_soc_register_component(&pdev->dev, &mmp_sspa_component, - &mmp_sspa_dai, 1); + return devm_snd_soc_register_component(&pdev->dev, &mmp_sspa_component, + &mmp_sspa_dai, 1); } static int asoc_mmp_sspa_remove(struct platform_device *pdev) @@ -466,7 +466,6 @@ static int asoc_mmp_sspa_remove(struct platform_device *pdev) clk_disable(priv->audio_clk); clk_put(priv->audio_clk); clk_put(priv->sysclk); - snd_soc_unregister_component(&pdev->dev); return 0; } -- cgit v0.10.2 From eb8417aa703eff5ff43d0275f19b0a8e591d818d Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 16 Sep 2013 09:23:02 +0200 Subject: perf/x86/intel: Remove division from the intel_pmu_drain_pebs_nhm() hot path Only do the division in case we have to print the result out in a warning. Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-43nl31erfbajwpfj254f6zji@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index f364c13..655d591 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -944,7 +944,7 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs) struct perf_event *event = NULL; void *at, *top; u64 status = 0; - int bit, n; + int bit; if (!x86_pmu.pebs_active) return; @@ -954,16 +954,16 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs) ds->pebs_index = ds->pebs_buffer_base; - n = (top - at) / x86_pmu.pebs_record_size; - if (n <= 0) + if (unlikely(at > top)) return; /* * Should not happen, we program the threshold at 1 and do not * set a reset value. */ - WARN_ONCE(n > x86_pmu.max_pebs_events, - "Unexpected number of pebs records %d\n", n); + WARN_ONCE(top - at > x86_pmu.max_pebs_events * x86_pmu.pebs_record_size, + "Unexpected number of pebs records %ld\n", + (top - at) / x86_pmu.pebs_record_size); for (; at < top; at += x86_pmu.pebs_record_size) { struct pebs_record_nhm *p = at; -- cgit v0.10.2 From d8e0ac0824cd0868ea73f186d6511d710b068044 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Wed, 4 Sep 2013 20:29:25 +0900 Subject: gpiolib: factorize gpiod_get/set functions gpiod_get/set functions share common code between their regular and cansleep variants. The exporting of the gpiod interface will make the situation worse. This patch factorizes the common code to avoid code redundancy. Signed-off-by: Alexandre Courbot Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 8048c3d..4fc2860 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1869,6 +1869,19 @@ EXPORT_SYMBOL_GPL(gpio_set_debounce); * that the GPIO was actually requested. */ +static int _gpiod_get_value(const struct gpio_desc *desc) +{ + struct gpio_chip *chip; + int value; + int offset; + + chip = desc->chip; + offset = gpio_chip_hwgpio(desc); + value = chip->get ? chip->get(chip, offset) : 0; + trace_gpio_value(desc_to_gpio(desc), 1, value); + return value; +} + /** * __gpio_get_value() - return a gpio's value * @gpio: gpio whose value will be returned @@ -1880,19 +1893,11 @@ EXPORT_SYMBOL_GPL(gpio_set_debounce); */ static int gpiod_get_value(const struct gpio_desc *desc) { - struct gpio_chip *chip; - int value; - int offset; - if (!desc) return 0; - chip = desc->chip; - offset = gpio_chip_hwgpio(desc); /* Should be using gpio_get_value_cansleep() */ - WARN_ON(chip->can_sleep); - value = chip->get ? chip->get(chip, offset) : 0; - trace_gpio_value(desc_to_gpio(desc), 1, value); - return value; + WARN_ON(desc->chip->can_sleep); + return _gpiod_get_value(desc); } int __gpio_get_value(unsigned gpio) @@ -1957,6 +1962,20 @@ static void _gpio_set_open_source_value(struct gpio_desc *desc, int value) __func__, err); } +static void _gpiod_set_value(struct gpio_desc *desc, int value) +{ + struct gpio_chip *chip; + + chip = desc->chip; + trace_gpio_value(desc_to_gpio(desc), 0, value); + if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) + _gpio_set_open_drain_value(desc, value); + else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) + _gpio_set_open_source_value(desc, value); + else + chip->set(chip, gpio_chip_hwgpio(desc), value); +} + /** * __gpio_set_value() - assign a gpio's value * @gpio: gpio whose value will be assigned @@ -1968,20 +1987,12 @@ static void _gpio_set_open_source_value(struct gpio_desc *desc, int value) */ static void gpiod_set_value(struct gpio_desc *desc, int value) { - struct gpio_chip *chip; if (!desc) return; - chip = desc->chip; /* Should be using gpio_set_value_cansleep() */ - WARN_ON(chip->can_sleep); - trace_gpio_value(desc_to_gpio(desc), 0, value); - if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) - _gpio_set_open_drain_value(desc, value); - else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) - _gpio_set_open_source_value(desc, value); - else - chip->set(chip, gpio_chip_hwgpio(desc), value); + WARN_ON(desc->chip->can_sleep); + _gpiod_set_value(desc, value); } void __gpio_set_value(unsigned gpio, int value) @@ -2046,18 +2057,10 @@ EXPORT_SYMBOL_GPL(__gpio_to_irq); static int gpiod_get_value_cansleep(const struct gpio_desc *desc) { - struct gpio_chip *chip; - int value; - int offset; - might_sleep_if(extra_checks); if (!desc) return 0; - chip = desc->chip; - offset = gpio_chip_hwgpio(desc); - value = chip->get ? chip->get(chip, offset) : 0; - trace_gpio_value(desc_to_gpio(desc), 1, value); - return value; + return _gpiod_get_value(desc); } int gpio_get_value_cansleep(unsigned gpio) @@ -2068,19 +2071,10 @@ EXPORT_SYMBOL_GPL(gpio_get_value_cansleep); static void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) { - struct gpio_chip *chip; - might_sleep_if(extra_checks); if (!desc) return; - chip = desc->chip; - trace_gpio_value(desc_to_gpio(desc), 0, value); - if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) - _gpio_set_open_drain_value(desc, value); - else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) - _gpio_set_open_source_value(desc, value); - else - chip->set(chip, gpio_chip_hwgpio(desc), value); + _gpiod_set_value(desc, value); } void gpio_set_value_cansleep(unsigned gpio, int value) -- cgit v0.10.2 From fa129ebeba6db2b4bcea45efe87a71d68181c04c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 19 Sep 2013 18:20:26 +0100 Subject: ASoC: 88pm60x: Don't use control data for i2c In preparation for using the regmap directly in the CODEC driver replace references to the I2C client using control_data with references to the driver private data. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c index 8af0434..3925cf3 100644 --- a/sound/soc/codecs/88pm860x-codec.c +++ b/sound/soc/codecs/88pm860x-codec.c @@ -1166,6 +1166,7 @@ static int pm860x_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai, static int pm860x_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec); int data; switch (level) { @@ -1179,17 +1180,17 @@ static int pm860x_set_bias_level(struct snd_soc_codec *codec, if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { /* Enable Audio PLL & Audio section */ data = AUDIO_PLL | AUDIO_SECTION_ON; - pm860x_reg_write(codec->control_data, REG_MISC2, data); + pm860x_reg_write(pm860x->i2c, REG_MISC2, data); udelay(300); data = AUDIO_PLL | AUDIO_SECTION_RESET | AUDIO_SECTION_ON; - pm860x_reg_write(codec->control_data, REG_MISC2, data); + pm860x_reg_write(pm860x->i2c, REG_MISC2, data); } break; case SND_SOC_BIAS_OFF: data = AUDIO_PLL | AUDIO_SECTION_RESET | AUDIO_SECTION_ON; - pm860x_set_bits(codec->control_data, REG_MISC2, data, 0); + pm860x_set_bits(pm860x->i2c, REG_MISC2, data, 0); break; } codec->dapm.bias_level = level; @@ -1319,17 +1320,17 @@ int pm860x_hs_jack_detect(struct snd_soc_codec *codec, pm860x->det.lo_shrt = lo_shrt; if (det & SND_JACK_HEADPHONE) - pm860x_set_bits(codec->control_data, REG_HS_DET, + pm860x_set_bits(pm860x->i2c, REG_HS_DET, EN_HS_DET, EN_HS_DET); /* headset short detect */ if (hs_shrt) { data = CLR_SHORT_HS2 | CLR_SHORT_HS1; - pm860x_set_bits(codec->control_data, REG_SHORTS, data, data); + pm860x_set_bits(pm860x->i2c, REG_SHORTS, data, data); } /* Lineout short detect */ if (lo_shrt) { data = CLR_SHORT_LO2 | CLR_SHORT_LO1; - pm860x_set_bits(codec->control_data, REG_SHORTS, data, data); + pm860x_set_bits(pm860x->i2c, REG_SHORTS, data, data); } /* sync status */ @@ -1347,7 +1348,7 @@ int pm860x_mic_jack_detect(struct snd_soc_codec *codec, pm860x->det.mic_det = det; if (det & SND_JACK_MICROPHONE) - pm860x_set_bits(codec->control_data, REG_MIC_DET, + pm860x_set_bits(pm860x->i2c, REG_MIC_DET, MICDET_MASK, MICDET_MASK); /* sync status */ @@ -1377,7 +1378,7 @@ static int pm860x_probe(struct snd_soc_codec *codec) pm860x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - ret = pm860x_bulk_read(codec->control_data, REG_CACHE_BASE, + ret = pm860x_bulk_read(pm860x->i2c, REG_CACHE_BASE, REG_CACHE_SIZE, codec->reg_cache); if (ret < 0) { dev_err(codec->dev, "Failed to fill register cache: %d\n", -- cgit v0.10.2 From f9ded3b2e761256301ebb8d90e87eb1b5443e3ea Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 19 Sep 2013 19:00:46 +0100 Subject: ASoC: 88pm860x: Use regmap for I/O As part of a move to remove the duplication of regmap functionality in ASoC convert the 88pm860x driver to use the regmap from the MFD. This means that we no longer cache the registers so performance will be slightly reduced on I/O operations. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c index 3925cf3..4633e51 100644 --- a/sound/soc/codecs/88pm860x-codec.c +++ b/sound/soc/codecs/88pm860x-codec.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -140,6 +141,7 @@ struct pm860x_priv { unsigned int filter; struct snd_soc_codec *codec; struct i2c_client *i2c; + struct regmap *regmap; struct pm860x_chip *chip; struct pm860x_det det; @@ -269,48 +271,6 @@ static struct st_gain st_table[] = { { -86, 29, 0}, { -56, 30, 0}, { -28, 31, 0}, { 0, 0, 0}, }; -static int pm860x_volatile(unsigned int reg) -{ - BUG_ON(reg >= REG_CACHE_SIZE); - - switch (reg) { - case PM860X_AUDIO_SUPPLIES_2: - return 1; - } - - return 0; -} - -static unsigned int pm860x_read_reg_cache(struct snd_soc_codec *codec, - unsigned int reg) -{ - unsigned char *cache = codec->reg_cache; - - BUG_ON(reg >= REG_CACHE_SIZE); - - if (pm860x_volatile(reg)) - return cache[reg]; - - reg += REG_CACHE_BASE; - - return pm860x_reg_read(codec->control_data, reg); -} - -static int pm860x_write_reg_cache(struct snd_soc_codec *codec, - unsigned int reg, unsigned int value) -{ - unsigned char *cache = codec->reg_cache; - - BUG_ON(reg >= REG_CACHE_SIZE); - - if (!pm860x_volatile(reg)) - cache[reg] = (unsigned char)value; - - reg += REG_CACHE_BASE; - - return pm860x_reg_write(codec->control_data, reg, value); -} - static int snd_soc_get_volsw_2r_st(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1364,7 +1324,7 @@ static int pm860x_probe(struct snd_soc_codec *codec) pm860x->codec = codec; - codec->control_data = pm860x->i2c; + codec->control_data = pm860x->regmap; for (i = 0; i < 4; i++) { ret = request_threaded_irq(pm860x->irq[i], NULL, @@ -1378,14 +1338,6 @@ static int pm860x_probe(struct snd_soc_codec *codec) pm860x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - ret = pm860x_bulk_read(pm860x->i2c, REG_CACHE_BASE, - REG_CACHE_SIZE, codec->reg_cache); - if (ret < 0) { - dev_err(codec->dev, "Failed to fill register cache: %d\n", - ret); - goto out; - } - return 0; out: @@ -1408,10 +1360,6 @@ static int pm860x_remove(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_dev_pm860x = { .probe = pm860x_probe, .remove = pm860x_remove, - .read = pm860x_read_reg_cache, - .write = pm860x_write_reg_cache, - .reg_cache_size = REG_CACHE_SIZE, - .reg_word_size = sizeof(u8), .set_bias_level = pm860x_set_bias_level, .controls = pm860x_snd_controls, @@ -1437,6 +1385,8 @@ static int pm860x_codec_probe(struct platform_device *pdev) pm860x->chip = chip; pm860x->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion; + pm860x->regmap = (chip->id == CHIP_PM8607) ? chip->regmap + : chip->regmap_companion; platform_set_drvdata(pdev, pm860x); for (i = 0; i < 4; i++) { diff --git a/sound/soc/codecs/88pm860x-codec.h b/sound/soc/codecs/88pm860x-codec.h index 3364ba4..f7282f4 100644 --- a/sound/soc/codecs/88pm860x-codec.h +++ b/sound/soc/codecs/88pm860x-codec.h @@ -12,67 +12,66 @@ #ifndef __88PM860X_H #define __88PM860X_H -/* The offset of these registers are 0xb0 */ -#define PM860X_PCM_IFACE_1 0x00 -#define PM860X_PCM_IFACE_2 0x01 -#define PM860X_PCM_IFACE_3 0x02 -#define PM860X_PCM_RATE 0x03 -#define PM860X_EC_PATH 0x04 -#define PM860X_SIDETONE_L_GAIN 0x05 -#define PM860X_SIDETONE_R_GAIN 0x06 -#define PM860X_SIDETONE_SHIFT 0x07 -#define PM860X_ADC_OFFSET_1 0x08 -#define PM860X_ADC_OFFSET_2 0x09 -#define PM860X_DMIC_DELAY 0x0a +#define PM860X_PCM_IFACE_1 0xb0 +#define PM860X_PCM_IFACE_2 0xb1 +#define PM860X_PCM_IFACE_3 0xb2 +#define PM860X_PCM_RATE 0xb3 +#define PM860X_EC_PATH 0xb4 +#define PM860X_SIDETONE_L_GAIN 0xb5 +#define PM860X_SIDETONE_R_GAIN 0xb6 +#define PM860X_SIDETONE_SHIFT 0xb7 +#define PM860X_ADC_OFFSET_1 0xb8 +#define PM860X_ADC_OFFSET_2 0xb9 +#define PM860X_DMIC_DELAY 0xba -#define PM860X_I2S_IFACE_1 0x0b -#define PM860X_I2S_IFACE_2 0x0c -#define PM860X_I2S_IFACE_3 0x0d -#define PM860X_I2S_IFACE_4 0x0e -#define PM860X_EQUALIZER_N0_1 0x0f -#define PM860X_EQUALIZER_N0_2 0x10 -#define PM860X_EQUALIZER_N1_1 0x11 -#define PM860X_EQUALIZER_N1_2 0x12 -#define PM860X_EQUALIZER_D1_1 0x13 -#define PM860X_EQUALIZER_D1_2 0x14 -#define PM860X_LOFI_GAIN_LEFT 0x15 -#define PM860X_LOFI_GAIN_RIGHT 0x16 -#define PM860X_HIFIL_GAIN_LEFT 0x17 -#define PM860X_HIFIL_GAIN_RIGHT 0x18 -#define PM860X_HIFIR_GAIN_LEFT 0x19 -#define PM860X_HIFIR_GAIN_RIGHT 0x1a -#define PM860X_DAC_OFFSET 0x1b -#define PM860X_OFFSET_LEFT_1 0x1c -#define PM860X_OFFSET_LEFT_2 0x1d -#define PM860X_OFFSET_RIGHT_1 0x1e -#define PM860X_OFFSET_RIGHT_2 0x1f -#define PM860X_ADC_ANA_1 0x20 -#define PM860X_ADC_ANA_2 0x21 -#define PM860X_ADC_ANA_3 0x22 -#define PM860X_ADC_ANA_4 0x23 -#define PM860X_ANA_TO_ANA 0x24 -#define PM860X_HS1_CTRL 0x25 -#define PM860X_HS2_CTRL 0x26 -#define PM860X_LO1_CTRL 0x27 -#define PM860X_LO2_CTRL 0x28 -#define PM860X_EAR_CTRL_1 0x29 -#define PM860X_EAR_CTRL_2 0x2a -#define PM860X_AUDIO_SUPPLIES_1 0x2b -#define PM860X_AUDIO_SUPPLIES_2 0x2c -#define PM860X_ADC_EN_1 0x2d -#define PM860X_ADC_EN_2 0x2e -#define PM860X_DAC_EN_1 0x2f -#define PM860X_DAC_EN_2 0x31 -#define PM860X_AUDIO_CAL_1 0x32 -#define PM860X_AUDIO_CAL_2 0x33 -#define PM860X_AUDIO_CAL_3 0x34 -#define PM860X_AUDIO_CAL_4 0x35 -#define PM860X_AUDIO_CAL_5 0x36 -#define PM860X_ANA_INPUT_SEL_1 0x37 -#define PM860X_ANA_INPUT_SEL_2 0x38 +#define PM860X_I2S_IFACE_1 0xbb +#define PM860X_I2S_IFACE_2 0xbc +#define PM860X_I2S_IFACE_3 0xbd +#define PM860X_I2S_IFACE_4 0xbe +#define PM860X_EQUALIZER_N0_1 0xbf +#define PM860X_EQUALIZER_N0_2 0xc0 +#define PM860X_EQUALIZER_N1_1 0xc1 +#define PM860X_EQUALIZER_N1_2 0xc2 +#define PM860X_EQUALIZER_D1_1 0xc3 +#define PM860X_EQUALIZER_D1_2 0xc4 +#define PM860X_LOFI_GAIN_LEFT 0xc5 +#define PM860X_LOFI_GAIN_RIGHT 0xc6 +#define PM860X_HIFIL_GAIN_LEFT 0xc7 +#define PM860X_HIFIL_GAIN_RIGHT 0xc8 +#define PM860X_HIFIR_GAIN_LEFT 0xc9 +#define PM860X_HIFIR_GAIN_RIGHT 0xca +#define PM860X_DAC_OFFSET 0xcb +#define PM860X_OFFSET_LEFT_1 0xcc +#define PM860X_OFFSET_LEFT_2 0xcd +#define PM860X_OFFSET_RIGHT_1 0xce +#define PM860X_OFFSET_RIGHT_2 0xcf +#define PM860X_ADC_ANA_1 0xd0 +#define PM860X_ADC_ANA_2 0xd1 +#define PM860X_ADC_ANA_3 0xd2 +#define PM860X_ADC_ANA_4 0xd3 +#define PM860X_ANA_TO_ANA 0xd4 +#define PM860X_HS1_CTRL 0xd5 +#define PM860X_HS2_CTRL 0xd6 +#define PM860X_LO1_CTRL 0xd7 +#define PM860X_LO2_CTRL 0xd8 +#define PM860X_EAR_CTRL_1 0xd9 +#define PM860X_EAR_CTRL_2 0xda +#define PM860X_AUDIO_SUPPLIES_1 0xdb +#define PM860X_AUDIO_SUPPLIES_2 0xdc +#define PM860X_ADC_EN_1 0xdd +#define PM860X_ADC_EN_2 0xde +#define PM860X_DAC_EN_1 0xdf +#define PM860X_DAC_EN_2 0xe1 +#define PM860X_AUDIO_CAL_1 0xe2 +#define PM860X_AUDIO_CAL_2 0xe3 +#define PM860X_AUDIO_CAL_3 0xe4 +#define PM860X_AUDIO_CAL_4 0xe5 +#define PM860X_AUDIO_CAL_5 0xe6 +#define PM860X_ANA_INPUT_SEL_1 0xe7 +#define PM860X_ANA_INPUT_SEL_2 0xe8 -#define PM860X_PCM_IFACE_4 0x39 -#define PM860X_I2S_IFACE_5 0x3a +#define PM860X_PCM_IFACE_4 0xe9 +#define PM860X_I2S_IFACE_5 0xea #define PM860X_SHORTS 0x3b #define PM860X_PLL_ADJ_1 0x3c -- cgit v0.10.2 From 78925b3a791b3ad485fe56d0b8951a5d043b7cae Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 20 Sep 2013 09:45:35 +0530 Subject: regulator: tps6524x: Remove redundant spi_set_drvdata Driver core sets driver data to NULL upon failure or remove. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/regulator/tps6524x-regulator.c b/drivers/regulator/tps6524x-regulator.c index 62e8d28..8b9ee39 100644 --- a/drivers/regulator/tps6524x-regulator.c +++ b/drivers/regulator/tps6524x-regulator.c @@ -588,7 +588,6 @@ static int pmic_remove(struct spi_device *spi) regulator_unregister(hw->rdev[i]); hw->rdev[i] = NULL; } - spi_set_drvdata(spi, NULL); return 0; } -- cgit v0.10.2 From f8c1700dd7d2ce9b2238b20d364317b2968ac76b Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Fri, 20 Sep 2013 13:13:02 +0530 Subject: regulator: core: set current constraints while setting machine constraints Machine constraints is configured during regulator register. If current constraints are provided through machine constraints then it is observed that sometime the current configured on rail is out of range what machine constraint has. Set the current constraints when setting machine constraints to make sure that rail's current is within the range of given machine constraints. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 5217c19..66ae12d 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -923,6 +923,36 @@ static int machine_constraints_voltage(struct regulator_dev *rdev, return 0; } +static int machine_constraints_current(struct regulator_dev *rdev, + struct regulation_constraints *constraints) +{ + struct regulator_ops *ops = rdev->desc->ops; + int ret; + + if (!constraints->min_uA && !constraints->max_uA) + return 0; + + if (constraints->min_uA > constraints->max_uA) { + rdev_err(rdev, "Invalid current constraints\n"); + return -EINVAL; + } + + if (!ops->set_current_limit || !ops->get_current_limit) { + rdev_warn(rdev, "Operation of current configuration missing\n"); + return 0; + } + + /* Set regulator current in constraints range */ + ret = ops->set_current_limit(rdev, constraints->min_uA, + constraints->max_uA); + if (ret < 0) { + rdev_err(rdev, "Failed to set current constraint, %d\n", ret); + return ret; + } + + return 0; +} + /** * set_machine_constraints - sets regulator constraints * @rdev: regulator source @@ -953,6 +983,10 @@ static int set_machine_constraints(struct regulator_dev *rdev, if (ret != 0) goto out; + ret = machine_constraints_current(rdev, rdev->constraints); + if (ret != 0) + goto out; + /* do we need to setup our suspend state */ if (rdev->constraints->initial_state) { ret = suspend_prepare(rdev, rdev->constraints->initial_state); -- cgit v0.10.2 From 7aff2e3a56b724b79fa2d5abd10d8231ef8fb0c5 Mon Sep 17 00:00:00 2001 From: Vladimir Davydov Date: Sun, 15 Sep 2013 21:30:13 +0400 Subject: sched/balancing: Prevent the reselection of a previous env.dst_cpu if some tasks are pinned Currently new_dst_cpu is prevented from being reselected actually, not dst_cpu. This can result in attempting to pull tasks to this_cpu twice. Signed-off-by: Vladimir Davydov Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/281f59b6e596c718dd565ad267fc38f5b8e5c995.1379265590.git.vdavydov@parallels.com Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 71c6ef5..0784ab6 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -5261,15 +5261,15 @@ more_balance: */ if ((env.flags & LBF_DST_PINNED) && env.imbalance > 0) { + /* Prevent to re-select dst_cpu via env's cpus */ + cpumask_clear_cpu(env.dst_cpu, env.cpus); + env.dst_rq = cpu_rq(env.new_dst_cpu); env.dst_cpu = env.new_dst_cpu; env.flags &= ~LBF_DST_PINNED; env.loop = 0; env.loop_break = sched_nr_migrate_break; - /* Prevent to re-select dst_cpu via env's cpus */ - cpumask_clear_cpu(env.dst_cpu, env.cpus); - /* * Go back to "more_balance" rather than "redo" since we * need to continue with same src_cpu. -- cgit v0.10.2 From abfafa54db9aba404e8e6763503f04d35bd07138 Mon Sep 17 00:00:00 2001 From: Jason Low Date: Fri, 13 Sep 2013 11:26:51 -0700 Subject: sched: Reduce overestimating rq->avg_idle When updating avg_idle, if the delta exceeds some max value, then avg_idle gets set to the max, regardless of what the previous avg was. This can cause avg_idle to often be overestimated. This patch modifies the way we update avg_idle by always updating it with the function call to update_avg() first. Then, if avg_idle exceeds the max, we set it to the max. Signed-off-by: Jason Low Reviewed-by: Rik van Riel Reviewed-by: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1379096813-3032-2-git-send-email-jason.low2@hp.com Signed-off-by: Ingo Molnar diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 5ac63c9..048f39e 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -1332,10 +1332,11 @@ ttwu_do_wakeup(struct rq *rq, struct task_struct *p, int wake_flags) u64 delta = rq_clock(rq) - rq->idle_stamp; u64 max = 2*sysctl_sched_migration_cost; - if (delta > max) + update_avg(&rq->avg_idle, delta); + + if (rq->avg_idle > max) rq->avg_idle = max; - else - update_avg(&rq->avg_idle, delta); + rq->idle_stamp = 0; } #endif -- cgit v0.10.2 From 9bd721c55c8a886b938a45198aab0ccb52f1f7fa Mon Sep 17 00:00:00 2001 From: Jason Low Date: Fri, 13 Sep 2013 11:26:52 -0700 Subject: sched/balancing: Consider max cost of idle balance per sched domain In this patch, we keep track of the max cost we spend doing idle load balancing for each sched domain. If the avg time the CPU remains idle is less then the time we have already spent on idle balancing + the max cost of idle balancing in the sched domain, then we don't continue to attempt the balance. We also keep a per rq variable, max_idle_balance_cost, which keeps track of the max time spent on newidle load balances throughout all its domains so that we can determine the avg_idle's max value. By using the max, we avoid overrunning the average. This further reduces the chance we attempt balancing when the CPU is not idle for longer than the cost to balance. Signed-off-by: Jason Low Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1379096813-3032-3-git-send-email-jason.low2@hp.com Signed-off-by: Ingo Molnar diff --git a/arch/metag/include/asm/topology.h b/arch/metag/include/asm/topology.h index 23f5118..db19292 100644 --- a/arch/metag/include/asm/topology.h +++ b/arch/metag/include/asm/topology.h @@ -26,6 +26,7 @@ .last_balance = jiffies, \ .balance_interval = 1, \ .nr_balance_failed = 0, \ + .max_newidle_lb_cost = 0, \ } #define cpu_to_node(cpu) ((void)(cpu), 0) diff --git a/include/linux/sched.h b/include/linux/sched.h index 6682da3..be078ff 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -810,6 +810,7 @@ struct sched_domain { unsigned int nr_balance_failed; /* initialise to 0 */ u64 last_update; + u64 max_newidle_lb_cost; #ifdef CONFIG_SCHEDSTATS /* load_balance() stats */ diff --git a/include/linux/topology.h b/include/linux/topology.h index d3cf0d6..e2a2c3d 100644 --- a/include/linux/topology.h +++ b/include/linux/topology.h @@ -106,6 +106,7 @@ int arch_update_cpu_topology(void); .last_balance = jiffies, \ .balance_interval = 1, \ .smt_gain = 1178, /* 15% */ \ + .max_newidle_lb_cost = 0, \ } #endif #endif /* CONFIG_SCHED_SMT */ @@ -135,6 +136,7 @@ int arch_update_cpu_topology(void); , \ .last_balance = jiffies, \ .balance_interval = 1, \ + .max_newidle_lb_cost = 0, \ } #endif #endif /* CONFIG_SCHED_MC */ @@ -166,6 +168,7 @@ int arch_update_cpu_topology(void); , \ .last_balance = jiffies, \ .balance_interval = 1, \ + .max_newidle_lb_cost = 0, \ } #endif diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 048f39e..c2283c5 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -1330,7 +1330,7 @@ ttwu_do_wakeup(struct rq *rq, struct task_struct *p, int wake_flags) if (rq->idle_stamp) { u64 delta = rq_clock(rq) - rq->idle_stamp; - u64 max = 2*sysctl_sched_migration_cost; + u64 max = 2*rq->max_idle_balance_cost; update_avg(&rq->avg_idle, delta); @@ -6506,6 +6506,7 @@ void __init sched_init(void) rq->online = 0; rq->idle_stamp = 0; rq->avg_idle = 2*sysctl_sched_migration_cost; + rq->max_idle_balance_cost = sysctl_sched_migration_cost; INIT_LIST_HEAD(&rq->cfs_tasks); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 0784ab6..ffc99d8 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -5396,6 +5396,7 @@ void idle_balance(int this_cpu, struct rq *this_rq) struct sched_domain *sd; int pulled_task = 0; unsigned long next_balance = jiffies + HZ; + u64 curr_cost = 0; this_rq->idle_stamp = rq_clock(this_rq); @@ -5412,15 +5413,27 @@ void idle_balance(int this_cpu, struct rq *this_rq) for_each_domain(this_cpu, sd) { unsigned long interval; int continue_balancing = 1; + u64 t0, domain_cost; if (!(sd->flags & SD_LOAD_BALANCE)) continue; + if (this_rq->avg_idle < curr_cost + sd->max_newidle_lb_cost) + break; + if (sd->flags & SD_BALANCE_NEWIDLE) { + t0 = sched_clock_cpu(this_cpu); + /* If we've pulled tasks over stop searching: */ pulled_task = load_balance(this_cpu, this_rq, sd, CPU_NEWLY_IDLE, &continue_balancing); + + domain_cost = sched_clock_cpu(this_cpu) - t0; + if (domain_cost > sd->max_newidle_lb_cost) + sd->max_newidle_lb_cost = domain_cost; + + curr_cost += domain_cost; } interval = msecs_to_jiffies(sd->balance_interval); @@ -5442,6 +5455,9 @@ void idle_balance(int this_cpu, struct rq *this_rq) */ this_rq->next_balance = next_balance; } + + if (curr_cost > this_rq->max_idle_balance_cost) + this_rq->max_idle_balance_cost = curr_cost; } /* diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 0d7544c..e82484d 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -476,6 +476,9 @@ struct rq { u64 age_stamp; u64 idle_stamp; u64 avg_idle; + + /* This is used to determine avg_idle's max value */ + u64 max_idle_balance_cost; #endif #ifdef CONFIG_IRQ_TIME_ACCOUNTING -- cgit v0.10.2 From f48627e686a69f5215cb0761e731edb3d9859dd9 Mon Sep 17 00:00:00 2001 From: Jason Low Date: Fri, 13 Sep 2013 11:26:53 -0700 Subject: sched/balancing: Periodically decay max cost of idle balance This patch builds on patch 2 and periodically decays that max value to do idle balancing per sched domain by approximately 1% per second. Also decay the rq's max_idle_balance_cost value. Signed-off-by: Jason Low Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1379096813-3032-4-git-send-email-jason.low2@hp.com Signed-off-by: Ingo Molnar diff --git a/arch/metag/include/asm/topology.h b/arch/metag/include/asm/topology.h index db19292..8e9c0b3 100644 --- a/arch/metag/include/asm/topology.h +++ b/arch/metag/include/asm/topology.h @@ -27,6 +27,7 @@ .balance_interval = 1, \ .nr_balance_failed = 0, \ .max_newidle_lb_cost = 0, \ + .next_decay_max_lb_cost = jiffies, \ } #define cpu_to_node(cpu) ((void)(cpu), 0) diff --git a/include/linux/sched.h b/include/linux/sched.h index be078ff..b5344de 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -810,7 +810,10 @@ struct sched_domain { unsigned int nr_balance_failed; /* initialise to 0 */ u64 last_update; + + /* idle_balance() stats */ u64 max_newidle_lb_cost; + unsigned long next_decay_max_lb_cost; #ifdef CONFIG_SCHEDSTATS /* load_balance() stats */ diff --git a/include/linux/topology.h b/include/linux/topology.h index e2a2c3d..12ae6ce 100644 --- a/include/linux/topology.h +++ b/include/linux/topology.h @@ -107,6 +107,7 @@ int arch_update_cpu_topology(void); .balance_interval = 1, \ .smt_gain = 1178, /* 15% */ \ .max_newidle_lb_cost = 0, \ + .next_decay_max_lb_cost = jiffies, \ } #endif #endif /* CONFIG_SCHED_SMT */ @@ -137,6 +138,7 @@ int arch_update_cpu_topology(void); .last_balance = jiffies, \ .balance_interval = 1, \ .max_newidle_lb_cost = 0, \ + .next_decay_max_lb_cost = jiffies, \ } #endif #endif /* CONFIG_SCHED_MC */ @@ -169,6 +171,7 @@ int arch_update_cpu_topology(void); .last_balance = jiffies, \ .balance_interval = 1, \ .max_newidle_lb_cost = 0, \ + .next_decay_max_lb_cost = jiffies, \ } #endif diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index ffc99d8..2b89cd2 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -5681,15 +5681,39 @@ static void rebalance_domains(int cpu, enum cpu_idle_type idle) /* Earliest time when we have to do rebalance again */ unsigned long next_balance = jiffies + 60*HZ; int update_next_balance = 0; - int need_serialize; + int need_serialize, need_decay = 0; + u64 max_cost = 0; update_blocked_averages(cpu); rcu_read_lock(); for_each_domain(cpu, sd) { + /* + * Decay the newidle max times here because this is a regular + * visit to all the domains. Decay ~1% per second. + */ + if (time_after(jiffies, sd->next_decay_max_lb_cost)) { + sd->max_newidle_lb_cost = + (sd->max_newidle_lb_cost * 253) / 256; + sd->next_decay_max_lb_cost = jiffies + HZ; + need_decay = 1; + } + max_cost += sd->max_newidle_lb_cost; + if (!(sd->flags & SD_LOAD_BALANCE)) continue; + /* + * Stop the load balance at this level. There is another + * CPU in our sched group which is doing load balancing more + * actively. + */ + if (!continue_balancing) { + if (need_decay) + continue; + break; + } + interval = sd->balance_interval; if (idle != CPU_IDLE) interval *= sd->busy_factor; @@ -5723,14 +5747,14 @@ out: next_balance = sd->last_balance + interval; update_next_balance = 1; } - + } + if (need_decay) { /* - * Stop the load balance at this level. There is another - * CPU in our sched group which is doing load balancing more - * actively. + * Ensure the rq-wide value also decays but keep it at a + * reasonable floor to avoid funnies with rq->avg_idle. */ - if (!continue_balancing) - break; + rq->max_idle_balance_cost = + max((u64)sysctl_sched_migration_cost, max_cost); } rcu_read_unlock(); -- cgit v0.10.2 From 92519bbc8af612975410def52bd462ca9af85cdb Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 20 Sep 2013 13:08:50 +0200 Subject: perf/x86/intel: Fix build warning in intel_pmu_drain_pebs_nhm() Fengguang Wu reported this build warning: arch/x86/kernel/cpu/perf_event_intel_ds.c: In function 'intel_pmu_drain_pebs_nhm': arch/x86/kernel/cpu/perf_event_intel_ds.c:964:2: warning: format '%ld' expects argument of type 'long int', but argument 4 has type 'int' Because pointer arithmetics result type is bitness dependent there's no natural type to use here, cast it to long. Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-jbpauwxJqtf24luewcsdFith@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index 655d591..54ff6ce 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -963,7 +963,7 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs) */ WARN_ONCE(top - at > x86_pmu.max_pebs_events * x86_pmu.pebs_record_size, "Unexpected number of pebs records %ld\n", - (top - at) / x86_pmu.pebs_record_size); + (long)(top - at) / x86_pmu.pebs_record_size); for (; at < top; at += x86_pmu.pebs_record_size) { struct pebs_record_nhm *p = at; -- cgit v0.10.2 From 9b92da1f1205bd2591487051a93624dd6c258eef Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 20 Sep 2013 12:23:30 +0100 Subject: regulator: core: Fix default return value for _get() Now that we are defaulting to providing dummy regulators fix the logic for substituting a dummy by making the default return code -EPROBE_DEFER. Reported-by: Thierry Reding Tested-by: Thierry Reding Signed-off-by: Mark Brown diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index ac3a864..088b41a 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1247,7 +1247,7 @@ static struct regulator *_regulator_get(struct device *dev, const char *id, struct regulator_dev *rdev; struct regulator *regulator = ERR_PTR(-EPROBE_DEFER); const char *devname = NULL; - int ret = 0; + int ret = -EPROBE_DEFER; if (id == NULL) { pr_err("get() with no identifier\n"); -- cgit v0.10.2 From 38bfd48b87c44f6958f75bfcd5ae5a53bd3ca07b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 19 Sep 2013 19:17:14 +0100 Subject: ASoC: ab8500: Downgrade noisy log message Signed-off-by: Mark Brown Acked-by: Lee Jones diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index b8ba0ad..7cea5a8 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -2601,7 +2601,7 @@ static int ab8500_codec_driver_probe(struct platform_device *pdev) static int ab8500_codec_driver_remove(struct platform_device *pdev) { - dev_info(&pdev->dev, "%s Enter.\n", __func__); + dev_dbg(&pdev->dev, "%s Enter.\n", __func__); snd_soc_unregister_codec(&pdev->dev); -- cgit v0.10.2 From 51f20e4cd83e804fb4fd940873763f29616f12a8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 19 Sep 2013 19:25:18 +0100 Subject: ASoC: ab8500: Use ASoC I/O functions In preparation for moving away from implementing the ASoC level register I/O functionality change direct calls to the ab8500 implementation of that to use snd_soc_write() Signed-off-by: Mark Brown Acked-by: Lee Jones diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index 7cea5a8..c2b6636 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -2527,12 +2527,10 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec) } /* Override HW-defaults */ - ab8500_codec_write_reg(codec, - AB8500_ANACONF5, - BIT(AB8500_ANACONF5_HSAUTOEN)); - ab8500_codec_write_reg(codec, - AB8500_SHORTCIRCONF, - BIT(AB8500_SHORTCIRCONF_HSZCDDIS)); + snd_soc_write(codec, AB8500_ANACONF5, + BIT(AB8500_ANACONF5_HSAUTOEN)); + snd_soc_write(codec, AB8500_SHORTCIRCONF, + BIT(AB8500_SHORTCIRCONF_HSZCDDIS)); /* Add filter controls */ status = snd_soc_add_codec_controls(codec, ab8500_filter_controls, -- cgit v0.10.2 From ff795d614bfa62a3c6fc0bcb75cb8842e5a87892 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 20 Sep 2013 10:38:16 +0100 Subject: ASoC: ab8500: Convert register I/O to regmap As part of a general push to eliminate the duplicated register I/O support in ASoC convert ab8500 to use regmap. Signed-off-by: Mark Brown Acked-by: Lee Jones diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index c2b6636..d5a0fc4 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -126,6 +126,8 @@ struct ab8500_codec_drvdata_dbg { /* Private data for AB8500 device-driver */ struct ab8500_codec_drvdata { + struct regmap *regmap; + /* Sidetone */ long *sid_fir_values; enum sid_state sid_status; @@ -166,49 +168,35 @@ static inline const char *amic_type_str(enum amic_type type) */ /* Read a register from the audio-bank of AB8500 */ -static unsigned int ab8500_codec_read_reg(struct snd_soc_codec *codec, - unsigned int reg) +static int ab8500_codec_read_reg(void *context, unsigned int reg, + unsigned int *value) { + struct device *dev = context; int status; - unsigned int value = 0; u8 value8; - status = abx500_get_register_interruptible(codec->dev, AB8500_AUDIO, - reg, &value8); - if (status < 0) { - dev_err(codec->dev, - "%s: ERROR: Register (0x%02x:0x%02x) read failed (%d).\n", - __func__, (u8)AB8500_AUDIO, (u8)reg, status); - } else { - dev_dbg(codec->dev, - "%s: Read 0x%02x from register 0x%02x:0x%02x\n", - __func__, value8, (u8)AB8500_AUDIO, (u8)reg); - value = (unsigned int)value8; - } + status = abx500_get_register_interruptible(dev, AB8500_AUDIO, + reg, &value8); + *value = (unsigned int)value8; - return value; + return status; } /* Write to a register in the audio-bank of AB8500 */ -static int ab8500_codec_write_reg(struct snd_soc_codec *codec, - unsigned int reg, unsigned int value) +static int ab8500_codec_write_reg(void *context, unsigned int reg, + unsigned int value) { - int status; + struct device *dev = context; - status = abx500_set_register_interruptible(codec->dev, AB8500_AUDIO, - reg, value); - if (status < 0) - dev_err(codec->dev, - "%s: ERROR: Register (%02x:%02x) write failed (%d).\n", - __func__, (u8)AB8500_AUDIO, (u8)reg, status); - else - dev_dbg(codec->dev, - "%s: Wrote 0x%02x into register %02x:%02x\n", - __func__, (u8)value, (u8)AB8500_AUDIO, (u8)reg); - - return status; + return abx500_set_register_interruptible(dev, AB8500_AUDIO, + reg, value); } +static const struct regmap_config ab8500_codec_regmap = { + .reg_read = ab8500_codec_read_reg, + .reg_write = ab8500_codec_write_reg, +}; + /* * Controls - DAPM */ @@ -2483,6 +2471,8 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec) /* Setup AB8500 according to board-settings */ pdata = dev_get_platdata(dev->parent); + codec->control_data = drvdata->regmap; + if (np) { if (!pdata) pdata = devm_kzalloc(dev, @@ -2560,9 +2550,6 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec) static struct snd_soc_codec_driver ab8500_codec_driver = { .probe = ab8500_codec_probe, - .read = ab8500_codec_read_reg, - .write = ab8500_codec_write_reg, - .reg_word_size = sizeof(u8), .controls = ab8500_ctrls, .num_controls = ARRAY_SIZE(ab8500_ctrls), .dapm_widgets = ab8500_dapm_widgets, @@ -2585,6 +2572,15 @@ static int ab8500_codec_driver_probe(struct platform_device *pdev) drvdata->anc_status = ANC_UNCONFIGURED; dev_set_drvdata(&pdev->dev, drvdata); + drvdata->regmap = devm_regmap_init(&pdev->dev, NULL, &pdev->dev, + &ab8500_codec_regmap); + if (IS_ERR(drvdata->regmap)) { + status = PTR_ERR(drvdata->regmap); + dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n", + __func__, status); + return status; + } + dev_dbg(&pdev->dev, "%s: Register codec.\n", __func__); status = snd_soc_register_codec(&pdev->dev, &ab8500_codec_driver, ab8500_codec_dai, -- cgit v0.10.2 From 5c889690aa089cc0f36f5cf4abb4d4f0ed81b4da Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 19 Jul 2013 15:09:25 -0700 Subject: mm: Place preemption point in do_mlockall() loop There is a loop in do_mlockall() that lacks a preemption point, which means that the following can happen on non-preemptible builds of the kernel: > My fuzz tester keeps hitting this. Every instance shows the non-irq stack > came in from mlockall. I'm only seeing this on one box, but that has more > ram (8gb) than my other machines, which might explain it. > > Dave > > INFO: rcu_preempt self-detected stall on CPU { 3} (t=6500 jiffies g=470344 c=470343 q=0) > sending NMI to all CPUs: > NMI backtrace for cpu 3 > CPU: 3 PID: 29664 Comm: trinity-child2 Not tainted 3.11.0-rc1+ #32 > task: ffff88023e743fc0 ti: ffff88022f6f2000 task.ti: ffff88022f6f2000 > RIP: 0010:[] [] trace_hardirqs_off_caller+0x21/0xb0 > RSP: 0018:ffff880244e03c30 EFLAGS: 00000046 > RAX: ffff88023e743fc0 RBX: 0000000000000001 RCX: 000000000000003c > RDX: 000000000000000f RSI: 0000000000000004 RDI: ffffffff81033cab > RBP: ffff880244e03c38 R08: ffff880243288a80 R09: 0000000000000001 > R10: 0000000000000000 R11: 0000000000000001 R12: ffff880243288a80 > R13: ffff8802437eda40 R14: 0000000000080000 R15: 000000000000d010 > FS: 00007f50ae33b740(0000) GS:ffff880244e00000(0000) knlGS:0000000000000000 > CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 > CR2: 000000000097f000 CR3: 0000000240fa0000 CR4: 00000000001407e0 > DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 > DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000600 > Stack: > ffffffff810bf86d ffff880244e03c98 ffffffff81033cab 0000000000000096 > 000000000000d008 0000000300000002 0000000000000004 0000000000000003 > 0000000000002710 ffffffff81c50d00 ffffffff81c50d00 ffff880244fcde00 > Call Trace: > > [] ? trace_hardirqs_off+0xd/0x10 > [] __x2apic_send_IPI_mask+0x1ab/0x1c0 > [] x2apic_send_IPI_all+0x1c/0x20 > [] arch_trigger_all_cpu_backtrace+0x65/0xa0 > [] rcu_check_callbacks+0x331/0x8e0 > [] ? hrtimer_run_queues+0x20/0x180 > [] ? sched_clock_cpu+0xb5/0x100 > [] update_process_times+0x47/0x80 > [] tick_sched_handle.isra.16+0x25/0x60 > [] tick_sched_timer+0x41/0x60 > [] __run_hrtimer+0x81/0x4e0 > [] ? tick_sched_do_timer+0x60/0x60 > [] hrtimer_interrupt+0xff/0x240 > [] local_apic_timer_interrupt+0x34/0x60 > [] smp_apic_timer_interrupt+0x3f/0x60 > [] apic_timer_interrupt+0x6f/0x80 > [] ? retint_restore_args+0xe/0xe > [] ? __do_softirq+0xb1/0x440 > [] irq_exit+0xcd/0xe0 > [] smp_apic_timer_interrupt+0x45/0x60 > [] apic_timer_interrupt+0x6f/0x80 > > [] ? retint_restore_args+0xe/0xe > [] ? wait_for_completion_killable+0x170/0x170 > [] ? preempt_schedule_irq+0x53/0x90 > [] retint_kernel+0x26/0x30 > [] ? queue_work_on+0x43/0x90 > [] schedule_on_each_cpu+0xc9/0x1a0 > [] ? lru_add_drain+0x50/0x50 > [] lru_add_drain_all+0x15/0x20 > [] SyS_mlockall+0xa5/0x1a0 > [] tracesys+0xdd/0xe2 This commit addresses this problem by inserting the required preemption point. Reported-by: Dave Jones Signed-off-by: Paul E. McKenney Cc: KOSAKI Motohiro Cc: Michel Lespinasse Cc: Andrew Morton Cc: Linus Torvalds diff --git a/mm/mlock.c b/mm/mlock.c index d638026..67ba6da 100644 --- a/mm/mlock.c +++ b/mm/mlock.c @@ -736,6 +736,7 @@ static int do_mlockall(int flags) /* Ignore errors */ mlock_fixup(vma, &prev, vma->vm_start, vma->vm_end, newflags); + cond_resched(); } out: return 0; -- cgit v0.10.2 From b3f2d02598fcf16933f72a57bbba7edb22ad8eda Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 8 Aug 2013 14:37:47 -0700 Subject: rcu: Use proper cpp macro for ->gp_flags One of the ->gp_flags assignments used a raw number rather than the cpp macro that was intended for this purpose, which this commit fixes. Signed-off-by: Paul E. McKenney diff --git a/kernel/rcutree.c b/kernel/rcutree.c index 32618b3..e0fa192 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -1452,7 +1452,7 @@ static void rcu_gp_cleanup(struct rcu_state *rsp) rdp = this_cpu_ptr(rsp->rda); rcu_advance_cbs(rsp, rnp, rdp); /* Reduce false positives below. */ if (cpu_needs_another_gp(rsp, rdp)) - rsp->gp_flags = 1; + rsp->gp_flags = RCU_GP_FLAG_INIT; raw_spin_unlock_irq(&rnp->lock); } -- cgit v0.10.2 From 6112fe60ac1bd1e68da8cc4248289d6e48015f9b Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Fri, 20 Sep 2013 18:00:10 +0530 Subject: regmap: add helper macro to set min/max range of register Add helper macro to set the min and max value of the register range. This is useful when initialising the register ranges of the device like static const struct regmap_range readable_ranges[] = { regmap_reg_range(DEVICE_REG0, DEVICE_REG10), }; Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown diff --git a/include/linux/regmap.h b/include/linux/regmap.h index a10380b..7d3ae2b 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -70,6 +70,8 @@ struct regmap_range { unsigned int range_max; }; +#define regmap_reg_range(low, high) { .range_min = low, .range_max = high, } + /* * A table of ranges including some yes ranges and some no ranges. * If a register belongs to a no_range, the corresponding check function -- cgit v0.10.2 From bc407334e9a6a745de5360395282beb2690adf48 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Fri, 20 Sep 2013 18:00:13 +0530 Subject: regulator: as3722: add regulator driver for AMS AS3722 The AMS AS3722 is a compact system PMU suitable for mobile phones, tablets etc. It has 4 DCDC step down regulators, 3 DCDC step down controller, 11 LDOs. Add a driver to support accessing the DCDC/LDOs found on the AMS AS3722 PMIC using regulators. Signed-off-by: Laxman Dewangan Signed-off-by: Florian Lobmaier Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/regulator/as3722-regulator.txt b/Documentation/devicetree/bindings/regulator/as3722-regulator.txt new file mode 100644 index 0000000..caad0c8 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/as3722-regulator.txt @@ -0,0 +1,91 @@ +Regulator of AMS AS3722 PMIC. +Name of the regulator subnode must be "regulators". + +Optional properties: +-------------------- +The input supply of regulators are the optional properties on the +regulator node. The AS3722 is having 7 DCDC step-down regulators as +sd[0-6], 10 LDOs as ldo[0-7], ldo[9-11]. The input supply of these +regulators are provided through following properties: +vsup-sd2-supply: Input supply for SD2. +vsup-sd3-supply: Input supply for SD3. +vsup-sd4-supply: Input supply for SD4. +vsup-sd5-supply: Input supply for SD5. +vin-ldo0-supply: Input supply for LDO0. +vin-ldo1-6-supply: Input supply for LDO1 and LDO6. +vin-ldo2-5-7-supply: Input supply for LDO2, LDO5 and LDO7. +vin-ldo3-4-supply: Input supply for LDO3 and LDO4. +vin-ldo9-10-supply: Input supply for LDO9 and LDO10. +vin-ldo11-supply: Input supply for LDO11. + +Optional nodes: +-------------- +- regulators : Must contain a sub-node per regulator from the list below. + Each sub-node should contain the constraints and initialization + information for that regulator. See regulator.txt for a + description of standard properties for these sub-nodes. + Additional custom properties are listed below. + sd[0-6], ldo[0-7], ldo[9-11]. + + Optional sub-node properties: + ---------------------------- + ams,ext-control: External control of the rail. The option of + this properties will tell which external input is + controlling this rail. Valid values are 0, 1, 2 ad 3. + 0: There is no external control of this rail. + 1: Rail is controlled by ENABLE1 input pin. + 2: Rail is controlled by ENABLE2 input pin. + 3: Rail is controlled by ENABLE3 input pin. + ams,enable-tracking: Enable tracking with SD1, only supported + by LDO3. + +Example: +------- + ams3722: ams3722 { + compatible = "ams,as3722"; + reg = <0x40>; + ... + + regulators { + vsup-sd2-supply = <...>; + ... + + sd0 { + regulator-name = "vdd_cpu"; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1400000>; + regulator-always-on; + ams,ext-control = <2>; + }; + + sd1 { + regulator-name = "vdd_core"; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1400000>; + regulator-always-on; + ams,ext-control = <1>; + }; + + sd2 { + regulator-name = "vddio_ddr"; + regulator-min-microvolt = <1350000>; + regulator-max-microvolt = <1350000>; + regulator-always-on; + }; + + sd4 { + regulator-name = "avdd-hdmi-pex"; + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1050000>; + regulator-always-on; + }; + + sd5 { + regulator-name = "vdd-1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + .... + }; + }; diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index dfe5809..9869064 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -133,6 +133,14 @@ config REGULATOR_AS3711 This driver provides support for the voltage regulators on the AS3711 PMIC +config REGULATOR_AS3722 + tristate "AMS AS3722 PMIC Regulators" + depends on MFD_AS3722 + help + This driver provides support for the voltage regulators on the + AS3722 PMIC. This will enable support for all the software + controllable DCDC/LDO regulators. + config REGULATOR_DA903X tristate "Dialog Semiconductor DA9030/DA9034 regulators" depends on PMIC_DA903X diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 185cce2..0b233bb 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o obj-$(CONFIG_REGULATOR_ARIZONA) += arizona-micsupp.o arizona-ldo1.o obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o +obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o obj-$(CONFIG_REGULATOR_DA903X) += da903x.o obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o obj-$(CONFIG_REGULATOR_DA9055) += da9055-regulator.o diff --git a/drivers/regulator/as3722-regulator.c b/drivers/regulator/as3722-regulator.c new file mode 100644 index 0000000..16a5d26 --- /dev/null +++ b/drivers/regulator/as3722-regulator.c @@ -0,0 +1,917 @@ +/* + * Voltage regulator support for AMS AS3722 PMIC + * + * Copyright (C) 2013 ams + * + * Author: Florian Lobmaier + * Author: Laxman Dewangan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Regulator IDs */ +enum as3722_regulators_id { + AS3722_REGULATOR_ID_SD0, + AS3722_REGULATOR_ID_SD1, + AS3722_REGULATOR_ID_SD2, + AS3722_REGULATOR_ID_SD3, + AS3722_REGULATOR_ID_SD4, + AS3722_REGULATOR_ID_SD5, + AS3722_REGULATOR_ID_SD6, + AS3722_REGULATOR_ID_LDO0, + AS3722_REGULATOR_ID_LDO1, + AS3722_REGULATOR_ID_LDO2, + AS3722_REGULATOR_ID_LDO3, + AS3722_REGULATOR_ID_LDO4, + AS3722_REGULATOR_ID_LDO5, + AS3722_REGULATOR_ID_LDO6, + AS3722_REGULATOR_ID_LDO7, + AS3722_REGULATOR_ID_LDO9, + AS3722_REGULATOR_ID_LDO10, + AS3722_REGULATOR_ID_LDO11, + AS3722_REGULATOR_ID_MAX, +}; + +struct as3722_register_mapping { + u8 regulator_id; + const char *name; + const char *sname; + u8 vsel_reg; + u8 vsel_mask; + int n_voltages; + u32 enable_reg; + u8 enable_mask; + u32 control_reg; + u8 mode_mask; + u32 sleep_ctrl_reg; + u8 sleep_ctrl_mask; +}; + +struct as3722_regulator_config_data { + struct regulator_init_data *reg_init; + bool enable_tracking; + int ext_control; +}; + +struct as3722_regulators { + struct device *dev; + struct as3722 *as3722; + struct regulator_dev *rdevs[AS3722_REGULATOR_ID_MAX]; + struct regulator_desc desc[AS3722_REGULATOR_ID_MAX]; + struct as3722_regulator_config_data + reg_config_data[AS3722_REGULATOR_ID_MAX]; +}; + +static const struct as3722_register_mapping as3722_reg_lookup[] = { + { + .regulator_id = AS3722_REGULATOR_ID_SD0, + .name = "as3722-sd0", + .vsel_reg = AS3722_SD0_VOLTAGE_REG, + .vsel_mask = AS3722_SD_VSEL_MASK, + .enable_reg = AS3722_SD_CONTROL_REG, + .enable_mask = AS3722_SDn_CTRL(0), + .sleep_ctrl_reg = AS3722_ENABLE_CTRL1_REG, + .sleep_ctrl_mask = AS3722_SD0_EXT_ENABLE_MASK, + .control_reg = AS3722_SD0_CONTROL_REG, + .mode_mask = AS3722_SD0_MODE_FAST, + .n_voltages = AS3722_SD0_VSEL_MAX, + }, + { + .regulator_id = AS3722_REGULATOR_ID_SD1, + .name = "as3722-sd1", + .vsel_reg = AS3722_SD1_VOLTAGE_REG, + .vsel_mask = AS3722_SD_VSEL_MASK, + .enable_reg = AS3722_SD_CONTROL_REG, + .enable_mask = AS3722_SDn_CTRL(1), + .sleep_ctrl_reg = AS3722_ENABLE_CTRL1_REG, + .sleep_ctrl_mask = AS3722_SD1_EXT_ENABLE_MASK, + .control_reg = AS3722_SD1_CONTROL_REG, + .mode_mask = AS3722_SD1_MODE_FAST, + .n_voltages = AS3722_SD0_VSEL_MAX, + }, + { + .regulator_id = AS3722_REGULATOR_ID_SD2, + .name = "as3722-sd2", + .sname = "vsup-sd2", + .vsel_reg = AS3722_SD2_VOLTAGE_REG, + .vsel_mask = AS3722_SD_VSEL_MASK, + .enable_reg = AS3722_SD_CONTROL_REG, + .enable_mask = AS3722_SDn_CTRL(2), + .sleep_ctrl_reg = AS3722_ENABLE_CTRL1_REG, + .sleep_ctrl_mask = AS3722_SD2_EXT_ENABLE_MASK, + .control_reg = AS3722_SD23_CONTROL_REG, + .mode_mask = AS3722_SD2_MODE_FAST, + .n_voltages = AS3722_SD2_VSEL_MAX, + }, + { + .regulator_id = AS3722_REGULATOR_ID_SD3, + .name = "as3722-sd3", + .sname = "vsup-sd3", + .vsel_reg = AS3722_SD3_VOLTAGE_REG, + .vsel_mask = AS3722_SD_VSEL_MASK, + .enable_reg = AS3722_SD_CONTROL_REG, + .enable_mask = AS3722_SDn_CTRL(3), + .sleep_ctrl_reg = AS3722_ENABLE_CTRL1_REG, + .sleep_ctrl_mask = AS3722_SD3_EXT_ENABLE_MASK, + .control_reg = AS3722_SD23_CONTROL_REG, + .mode_mask = AS3722_SD3_MODE_FAST, + .n_voltages = AS3722_SD2_VSEL_MAX, + }, + { + .regulator_id = AS3722_REGULATOR_ID_SD4, + .name = "as3722-sd4", + .sname = "vsup-sd4", + .vsel_reg = AS3722_SD4_VOLTAGE_REG, + .vsel_mask = AS3722_SD_VSEL_MASK, + .enable_reg = AS3722_SD_CONTROL_REG, + .enable_mask = AS3722_SDn_CTRL(4), + .sleep_ctrl_reg = AS3722_ENABLE_CTRL2_REG, + .sleep_ctrl_mask = AS3722_SD4_EXT_ENABLE_MASK, + .control_reg = AS3722_SD4_CONTROL_REG, + .mode_mask = AS3722_SD4_MODE_FAST, + .n_voltages = AS3722_SD2_VSEL_MAX, + }, + { + .regulator_id = AS3722_REGULATOR_ID_SD5, + .name = "as3722-sd5", + .sname = "vsup-sd5", + .vsel_reg = AS3722_SD5_VOLTAGE_REG, + .vsel_mask = AS3722_SD_VSEL_MASK, + .enable_reg = AS3722_SD_CONTROL_REG, + .enable_mask = AS3722_SDn_CTRL(5), + .sleep_ctrl_reg = AS3722_ENABLE_CTRL2_REG, + .sleep_ctrl_mask = AS3722_SD5_EXT_ENABLE_MASK, + .control_reg = AS3722_SD5_CONTROL_REG, + .mode_mask = AS3722_SD5_MODE_FAST, + .n_voltages = AS3722_SD2_VSEL_MAX, + }, + { + .regulator_id = AS3722_REGULATOR_ID_SD6, + .name = "as3722-sd6", + .vsel_reg = AS3722_SD6_VOLTAGE_REG, + .vsel_mask = AS3722_SD_VSEL_MASK, + .enable_reg = AS3722_SD_CONTROL_REG, + .enable_mask = AS3722_SDn_CTRL(6), + .sleep_ctrl_reg = AS3722_ENABLE_CTRL2_REG, + .sleep_ctrl_mask = AS3722_SD6_EXT_ENABLE_MASK, + .control_reg = AS3722_SD6_CONTROL_REG, + .mode_mask = AS3722_SD6_MODE_FAST, + .n_voltages = AS3722_SD0_VSEL_MAX, + }, + { + .regulator_id = AS3722_REGULATOR_ID_LDO0, + .name = "as3722-ldo0", + .sname = "vin-ldo0", + .vsel_reg = AS3722_LDO0_VOLTAGE_REG, + .vsel_mask = AS3722_LDO0_VSEL_MASK, + .enable_reg = AS3722_LDOCONTROL0_REG, + .enable_mask = AS3722_LDO0_CTRL, + .sleep_ctrl_reg = AS3722_ENABLE_CTRL3_REG, + .sleep_ctrl_mask = AS3722_LDO0_EXT_ENABLE_MASK, + .n_voltages = AS3722_LDO0_NUM_VOLT, + }, + { + .regulator_id = AS3722_REGULATOR_ID_LDO1, + .name = "as3722-ldo1", + .sname = "vin-ldo1-6", + .vsel_reg = AS3722_LDO1_VOLTAGE_REG, + .vsel_mask = AS3722_LDO_VSEL_MASK, + .enable_reg = AS3722_LDOCONTROL0_REG, + .enable_mask = AS3722_LDO1_CTRL, + .sleep_ctrl_reg = AS3722_ENABLE_CTRL3_REG, + .sleep_ctrl_mask = AS3722_LDO1_EXT_ENABLE_MASK, + .n_voltages = AS3722_LDO_NUM_VOLT, + }, + { + .regulator_id = AS3722_REGULATOR_ID_LDO2, + .name = "as3722-ldo2", + .sname = "vin-ldo2-5-7", + .vsel_reg = AS3722_LDO2_VOLTAGE_REG, + .vsel_mask = AS3722_LDO_VSEL_MASK, + .enable_reg = AS3722_LDOCONTROL0_REG, + .enable_mask = AS3722_LDO2_CTRL, + .sleep_ctrl_reg = AS3722_ENABLE_CTRL3_REG, + .sleep_ctrl_mask = AS3722_LDO2_EXT_ENABLE_MASK, + .n_voltages = AS3722_LDO_NUM_VOLT, + }, + { + .regulator_id = AS3722_REGULATOR_ID_LDO3, + .name = "as3722-ldo3", + .name = "vin-ldo3-4", + .vsel_reg = AS3722_LDO3_VOLTAGE_REG, + .vsel_mask = AS3722_LDO3_VSEL_MASK, + .enable_reg = AS3722_LDOCONTROL0_REG, + .enable_mask = AS3722_LDO3_CTRL, + .sleep_ctrl_reg = AS3722_ENABLE_CTRL3_REG, + .sleep_ctrl_mask = AS3722_LDO3_EXT_ENABLE_MASK, + .n_voltages = AS3722_LDO3_NUM_VOLT, + }, + { + .regulator_id = AS3722_REGULATOR_ID_LDO4, + .name = "as3722-ldo4", + .name = "vin-ldo3-4", + .vsel_reg = AS3722_LDO4_VOLTAGE_REG, + .vsel_mask = AS3722_LDO_VSEL_MASK, + .enable_reg = AS3722_LDOCONTROL0_REG, + .enable_mask = AS3722_LDO4_CTRL, + .sleep_ctrl_reg = AS3722_ENABLE_CTRL4_REG, + .sleep_ctrl_mask = AS3722_LDO4_EXT_ENABLE_MASK, + .n_voltages = AS3722_LDO_NUM_VOLT, + }, + { + .regulator_id = AS3722_REGULATOR_ID_LDO5, + .name = "as3722-ldo5", + .sname = "vin-ldo2-5-7", + .vsel_reg = AS3722_LDO5_VOLTAGE_REG, + .vsel_mask = AS3722_LDO_VSEL_MASK, + .enable_reg = AS3722_LDOCONTROL0_REG, + .enable_mask = AS3722_LDO5_CTRL, + .sleep_ctrl_reg = AS3722_ENABLE_CTRL4_REG, + .sleep_ctrl_mask = AS3722_LDO5_EXT_ENABLE_MASK, + .n_voltages = AS3722_LDO_NUM_VOLT, + }, + { + .regulator_id = AS3722_REGULATOR_ID_LDO6, + .name = "as3722-ldo6", + .sname = "vin-ldo1-6", + .vsel_reg = AS3722_LDO6_VOLTAGE_REG, + .vsel_mask = AS3722_LDO_VSEL_MASK, + .enable_reg = AS3722_LDOCONTROL0_REG, + .enable_mask = AS3722_LDO6_CTRL, + .sleep_ctrl_reg = AS3722_ENABLE_CTRL4_REG, + .sleep_ctrl_mask = AS3722_LDO6_EXT_ENABLE_MASK, + .n_voltages = AS3722_LDO_NUM_VOLT, + }, + { + .regulator_id = AS3722_REGULATOR_ID_LDO7, + .name = "as3722-ldo7", + .sname = "vin-ldo2-5-7", + .vsel_reg = AS3722_LDO7_VOLTAGE_REG, + .vsel_mask = AS3722_LDO_VSEL_MASK, + .enable_reg = AS3722_LDOCONTROL0_REG, + .enable_mask = AS3722_LDO7_CTRL, + .sleep_ctrl_reg = AS3722_ENABLE_CTRL4_REG, + .sleep_ctrl_mask = AS3722_LDO7_EXT_ENABLE_MASK, + .n_voltages = AS3722_LDO_NUM_VOLT, + }, + { + .regulator_id = AS3722_REGULATOR_ID_LDO9, + .name = "as3722-ldo9", + .sname = "vin-ldo9-10", + .vsel_reg = AS3722_LDO9_VOLTAGE_REG, + .vsel_mask = AS3722_LDO_VSEL_MASK, + .enable_reg = AS3722_LDOCONTROL1_REG, + .enable_mask = AS3722_LDO9_CTRL, + .sleep_ctrl_reg = AS3722_ENABLE_CTRL5_REG, + .sleep_ctrl_mask = AS3722_LDO9_EXT_ENABLE_MASK, + .n_voltages = AS3722_LDO_NUM_VOLT, + }, + { + .regulator_id = AS3722_REGULATOR_ID_LDO10, + .name = "as3722-ldo10", + .sname = "vin-ldo9-10", + .vsel_reg = AS3722_LDO10_VOLTAGE_REG, + .vsel_mask = AS3722_LDO_VSEL_MASK, + .enable_reg = AS3722_LDOCONTROL1_REG, + .enable_mask = AS3722_LDO10_CTRL, + .sleep_ctrl_reg = AS3722_ENABLE_CTRL5_REG, + .sleep_ctrl_mask = AS3722_LDO10_EXT_ENABLE_MASK, + .n_voltages = AS3722_LDO_NUM_VOLT, + }, + { + .regulator_id = AS3722_REGULATOR_ID_LDO11, + .name = "as3722-ldo11", + .sname = "vin-ldo11", + .vsel_reg = AS3722_LDO11_VOLTAGE_REG, + .vsel_mask = AS3722_LDO_VSEL_MASK, + .enable_reg = AS3722_LDOCONTROL1_REG, + .enable_mask = AS3722_LDO11_CTRL, + .sleep_ctrl_reg = AS3722_ENABLE_CTRL5_REG, + .sleep_ctrl_mask = AS3722_LDO11_EXT_ENABLE_MASK, + .n_voltages = AS3722_LDO_NUM_VOLT, + }, +}; + + +static const int as3722_ldo_current[] = { 150000, 300000 }; +static const int as3722_sd016_current[] = { 2500000, 3000000, 3500000 }; + +static int as3722_current_to_index(int min_uA, int max_uA, + const int *curr_table, int n_currents) +{ + int i; + + for (i = n_currents - 1; i >= 0; i--) { + if ((min_uA <= curr_table[i]) && (curr_table[i] <= max_uA)) + return i; + } + return -EINVAL; +} + +static int as3722_ldo_get_current_limit(struct regulator_dev *rdev) +{ + struct as3722_regulators *as3722_regs = rdev_get_drvdata(rdev); + struct as3722 *as3722 = as3722_regs->as3722; + int id = rdev_get_id(rdev); + u32 val; + int ret; + + ret = as3722_read(as3722, as3722_reg_lookup[id].vsel_reg, &val); + if (ret < 0) { + dev_err(as3722_regs->dev, "Reg 0x%02x read failed: %d\n", + as3722_reg_lookup[id].vsel_reg, ret); + return ret; + } + if (val & AS3722_LDO_ILIMIT_MASK) + return 300000; + return 150000; +} + +static int as3722_ldo_set_current_limit(struct regulator_dev *rdev, + int min_uA, int max_uA) +{ + struct as3722_regulators *as3722_regs = rdev_get_drvdata(rdev); + struct as3722 *as3722 = as3722_regs->as3722; + int id = rdev_get_id(rdev); + int ret; + u32 reg = 0; + + ret = as3722_current_to_index(min_uA, max_uA, as3722_ldo_current, + ARRAY_SIZE(as3722_ldo_current)); + if (ret < 0) { + dev_err(as3722_regs->dev, + "Current range min:max = %d:%d does not support\n", + min_uA, max_uA); + return ret; + } + if (ret) + reg = AS3722_LDO_ILIMIT_BIT; + return as3722_update_bits(as3722, as3722_reg_lookup[id].vsel_reg, + AS3722_LDO_ILIMIT_MASK, reg); +} + +static struct regulator_ops as3722_ldo0_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .list_voltage = regulator_list_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_current_limit = as3722_ldo_get_current_limit, + .set_current_limit = as3722_ldo_set_current_limit, +}; + +static struct regulator_ops as3722_ldo0_extcntrl_ops = { + .list_voltage = regulator_list_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_current_limit = as3722_ldo_get_current_limit, + .set_current_limit = as3722_ldo_set_current_limit, +}; + +static int as3722_ldo3_set_tracking_mode(struct as3722_regulators *as3722_reg, + int id, u8 mode) +{ + struct as3722 *as3722 = as3722_reg->as3722; + + switch (mode) { + case AS3722_LDO3_MODE_PMOS: + case AS3722_LDO3_MODE_PMOS_TRACKING: + case AS3722_LDO3_MODE_NMOS: + case AS3722_LDO3_MODE_SWITCH: + return as3722_update_bits(as3722, + as3722_reg_lookup[id].vsel_reg, + AS3722_LDO3_MODE_MASK, mode); + + default: + return -EINVAL; + } +} + +static int as3722_ldo3_get_current_limit(struct regulator_dev *rdev) +{ + return 150000; +} + +static struct regulator_ops as3722_ldo3_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .list_voltage = regulator_list_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_current_limit = as3722_ldo3_get_current_limit, +}; + +static struct regulator_ops as3722_ldo3_extcntrl_ops = { + .list_voltage = regulator_list_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_current_limit = as3722_ldo3_get_current_limit, +}; + +#define regulator_lin_range(_min_sel, _max_sel, _min_uV, _step_uV) \ + { \ + .min_sel = _min_sel, \ + .max_sel = _max_sel, \ + .uV_step = _step_uV, \ + .min_uV = _min_uV, \ + .max_uV = _min_uV + (_max_sel - _min_sel + 1) * _step_uV, \ + } + +static const struct regulator_linear_range as3722_ldo_ranges[] = { + regulator_lin_range(0x01, 0x24, 800000, 25000), + regulator_lin_range(0x40, 0x7F, 1725000, 25000), +}; + +static struct regulator_ops as3722_ldo_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .map_voltage = regulator_map_voltage_linear_range, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear_range, + .get_current_limit = as3722_ldo_get_current_limit, + .set_current_limit = as3722_ldo_set_current_limit, +}; + +static struct regulator_ops as3722_ldo_extcntrl_ops = { + .map_voltage = regulator_map_voltage_linear_range, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear_range, + .get_current_limit = as3722_ldo_get_current_limit, + .set_current_limit = as3722_ldo_set_current_limit, +}; + +static unsigned int as3722_sd_get_mode(struct regulator_dev *rdev) +{ + struct as3722_regulators *as3722_regs = rdev_get_drvdata(rdev); + struct as3722 *as3722 = as3722_regs->as3722; + int id = rdev_get_id(rdev); + u32 val; + int ret; + + if (!as3722_reg_lookup[id].control_reg) + return -ENOTSUPP; + + ret = as3722_read(as3722, as3722_reg_lookup[id].control_reg, &val); + if (ret < 0) { + dev_err(as3722_regs->dev, "Reg 0x%02x read failed: %d\n", + as3722_reg_lookup[id].control_reg, ret); + return ret; + } + + if (val & as3722_reg_lookup[id].mode_mask) + return REGULATOR_MODE_FAST; + else + return REGULATOR_MODE_NORMAL; +} + +static int as3722_sd_set_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + struct as3722_regulators *as3722_regs = rdev_get_drvdata(rdev); + struct as3722 *as3722 = as3722_regs->as3722; + u8 id = rdev_get_id(rdev); + u8 val = 0; + int ret; + + if (!as3722_reg_lookup[id].control_reg) + return -ERANGE; + + switch (mode) { + case REGULATOR_MODE_FAST: + val = as3722_reg_lookup[id].mode_mask; + case REGULATOR_MODE_NORMAL: /* fall down */ + break; + default: + return -EINVAL; + } + + ret = as3722_update_bits(as3722, as3722_reg_lookup[id].control_reg, + as3722_reg_lookup[id].mode_mask, val); + if (ret < 0) { + dev_err(as3722_regs->dev, "Reg 0x%02x update failed: %d\n", + as3722_reg_lookup[id].control_reg, ret); + return ret; + } + return ret; +} + +static int as3722_sd016_get_current_limit(struct regulator_dev *rdev) +{ + struct as3722_regulators *as3722_regs = rdev_get_drvdata(rdev); + struct as3722 *as3722 = as3722_regs->as3722; + int id = rdev_get_id(rdev); + u32 val, reg; + int mask; + int ret; + + switch (id) { + case AS3722_REGULATOR_ID_SD0: + reg = AS3722_OVCURRENT_REG; + mask = AS3722_OVCURRENT_SD0_TRIP_MASK; + break; + case AS3722_REGULATOR_ID_SD1: + reg = AS3722_OVCURRENT_REG; + mask = AS3722_OVCURRENT_SD1_TRIP_MASK; + break; + case AS3722_REGULATOR_ID_SD6: + reg = AS3722_OVCURRENT_DEB_REG; + mask = AS3722_OVCURRENT_SD6_TRIP_MASK; + break; + default: + return -EINVAL; + } + ret = as3722_read(as3722, reg, &val); + if (ret < 0) { + dev_err(as3722_regs->dev, "Reg 0x%02x read failed: %d\n", + reg, ret); + return ret; + } + val &= mask; + val >>= ffs(mask) - 1; + if (val == 3) + return -EINVAL; + return as3722_sd016_current[val]; +} + +static int as3722_sd016_set_current_limit(struct regulator_dev *rdev, + int min_uA, int max_uA) +{ + struct as3722_regulators *as3722_regs = rdev_get_drvdata(rdev); + struct as3722 *as3722 = as3722_regs->as3722; + int id = rdev_get_id(rdev); + int ret; + int val; + int mask; + u32 reg; + + ret = as3722_current_to_index(min_uA, max_uA, as3722_sd016_current, + ARRAY_SIZE(as3722_sd016_current)); + if (ret < 0) { + dev_err(as3722_regs->dev, + "Current range min:max = %d:%d does not support\n", + min_uA, max_uA); + return ret; + } + + switch (id) { + case AS3722_REGULATOR_ID_SD0: + reg = AS3722_OVCURRENT_REG; + mask = AS3722_OVCURRENT_SD0_TRIP_MASK; + break; + case AS3722_REGULATOR_ID_SD1: + reg = AS3722_OVCURRENT_REG; + mask = AS3722_OVCURRENT_SD1_TRIP_MASK; + break; + case AS3722_REGULATOR_ID_SD6: + reg = AS3722_OVCURRENT_DEB_REG; + mask = AS3722_OVCURRENT_SD6_TRIP_MASK; + break; + default: + return -EINVAL; + } + val = ret & mask; + val <<= ffs(mask) - 1; + return as3722_update_bits(as3722, reg, mask, val); +} + +static const struct regulator_linear_range as3722_sd2345_ranges[] = { + regulator_lin_range(0x01, 0x40, 600000, 12500), + regulator_lin_range(0x41, 0x70, 1400000, 25000), + regulator_lin_range(0x71, 0x7F, 1725000, 50000), +}; + +static struct regulator_ops as3722_sd016_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_current_limit = as3722_sd016_get_current_limit, + .set_current_limit = as3722_sd016_set_current_limit, + .get_mode = as3722_sd_get_mode, + .set_mode = as3722_sd_set_mode, +}; + +static struct regulator_ops as3722_sd016_extcntrl_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_current_limit = as3722_sd016_get_current_limit, + .set_current_limit = as3722_sd016_set_current_limit, + .get_mode = as3722_sd_get_mode, + .set_mode = as3722_sd_set_mode, +}; + +static struct regulator_ops as3722_sd2345_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .get_mode = as3722_sd_get_mode, + .set_mode = as3722_sd_set_mode, +}; + +static struct regulator_ops as3722_sd2345_extcntrl_ops = { + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .get_mode = as3722_sd_get_mode, + .set_mode = as3722_sd_set_mode, +}; + +static int as3722_extreg_init(struct as3722_regulators *as3722_regs, int id, + int ext_pwr_ctrl) +{ + int ret; + unsigned int val; + + if ((ext_pwr_ctrl < AS3722_EXT_CONTROL_ENABLE1) || + (ext_pwr_ctrl > AS3722_EXT_CONTROL_ENABLE3)) + return -EINVAL; + + val = ext_pwr_ctrl << (ffs(as3722_reg_lookup[id].sleep_ctrl_mask) - 1); + ret = as3722_update_bits(as3722_regs->as3722, + as3722_reg_lookup[id].sleep_ctrl_reg, + as3722_reg_lookup[id].sleep_ctrl_mask, val); + if (ret < 0) + dev_err(as3722_regs->dev, "Reg 0x%02x update failed: %d\n", + as3722_reg_lookup[id].sleep_ctrl_reg, ret); + return ret; +} + +static struct of_regulator_match as3722_regulator_matches[] = { + { .name = "sd0", }, + { .name = "sd1", }, + { .name = "sd2", }, + { .name = "sd3", }, + { .name = "sd4", }, + { .name = "sd5", }, + { .name = "sd6", }, + { .name = "ldo0", }, + { .name = "ldo1", }, + { .name = "ldo2", }, + { .name = "ldo3", }, + { .name = "ldo4", }, + { .name = "ldo5", }, + { .name = "ldo6", }, + { .name = "ldo7", }, + { .name = "ldo9", }, + { .name = "ldo10", }, + { .name = "ldo11", }, +}; + +static int as3722_get_regulator_dt_data(struct platform_device *pdev, + struct as3722_regulators *as3722_regs) +{ + struct device_node *np; + struct as3722_regulator_config_data *reg_config; + u32 prop; + int id; + int ret; + + np = of_get_child_by_name(pdev->dev.parent->of_node, "regulators"); + if (!np) { + dev_err(&pdev->dev, "Device is not having regulators node\n"); + return -ENODEV; + } + pdev->dev.of_node = np; + + ret = of_regulator_match(&pdev->dev, np, as3722_regulator_matches, + ARRAY_SIZE(as3722_regulator_matches)); + if (ret < 0) { + dev_err(&pdev->dev, "Parsing of regulator node failed: %d\n", + ret); + return ret; + } + + for (id = 0; id < ARRAY_SIZE(as3722_regulator_matches); ++id) { + struct device_node *reg_node; + + reg_config = &as3722_regs->reg_config_data[id]; + reg_config->reg_init = as3722_regulator_matches[id].init_data; + reg_node = as3722_regulator_matches[id].of_node; + + if (!reg_config->reg_init || !reg_node) + continue; + + ret = of_property_read_u32(reg_node, "ams,ext-control", &prop); + if (!ret) { + if (prop < 3) + reg_config->ext_control = prop; + else + dev_warn(&pdev->dev, + "ext-control have invalid option: %u\n", + prop); + } + reg_config->enable_tracking = + of_property_read_bool(reg_node, "ams,enable-tracking"); + } + return 0; +} + +static int as3722_regulator_probe(struct platform_device *pdev) +{ + struct as3722 *as3722 = dev_get_drvdata(pdev->dev.parent); + struct as3722_regulators *as3722_regs; + struct as3722_regulator_config_data *reg_config; + struct regulator_dev *rdev; + struct regulator_config config = { }; + struct regulator_ops *ops; + int id; + int ret; + + as3722_regs = devm_kzalloc(&pdev->dev, sizeof(*as3722_regs), + GFP_KERNEL); + if (!as3722_regs) + return -ENOMEM; + + as3722_regs->dev = &pdev->dev; + as3722_regs->as3722 = as3722; + platform_set_drvdata(pdev, as3722_regs); + + ret = as3722_get_regulator_dt_data(pdev, as3722_regs); + if (ret < 0) + return ret; + + config.dev = &pdev->dev; + config.driver_data = as3722_regs; + config.regmap = as3722->regmap; + + for (id = 0; id < AS3722_REGULATOR_ID_MAX; id++) { + reg_config = &as3722_regs->reg_config_data[id]; + + as3722_regs->desc[id].name = as3722_reg_lookup[id].name; + as3722_regs->desc[id].supply_name = as3722_reg_lookup[id].sname; + as3722_regs->desc[id].id = as3722_reg_lookup[id].regulator_id; + as3722_regs->desc[id].n_voltages = + as3722_reg_lookup[id].n_voltages; + as3722_regs->desc[id].type = REGULATOR_VOLTAGE; + as3722_regs->desc[id].owner = THIS_MODULE; + as3722_regs->desc[id].enable_reg = + as3722_reg_lookup[id].enable_reg; + as3722_regs->desc[id].enable_mask = + as3722_reg_lookup[id].enable_mask; + as3722_regs->desc[id].vsel_reg = as3722_reg_lookup[id].vsel_reg; + as3722_regs->desc[id].vsel_mask = + as3722_reg_lookup[id].vsel_mask; + switch (id) { + case AS3722_REGULATOR_ID_LDO0: + if (reg_config->ext_control) + ops = &as3722_ldo0_extcntrl_ops; + else + ops = &as3722_ldo0_ops; + as3722_regs->desc[id].min_uV = 825000; + as3722_regs->desc[id].uV_step = 25000; + as3722_regs->desc[id].linear_min_sel = 1; + as3722_regs->desc[id].enable_time = 500; + break; + case AS3722_REGULATOR_ID_LDO3: + if (reg_config->ext_control) + ops = &as3722_ldo3_extcntrl_ops; + else + ops = &as3722_ldo3_ops; + as3722_regs->desc[id].min_uV = 620000; + as3722_regs->desc[id].uV_step = 20000; + as3722_regs->desc[id].linear_min_sel = 1; + as3722_regs->desc[id].enable_time = 500; + if (reg_config->enable_tracking) { + ret = as3722_ldo3_set_tracking_mode(as3722_regs, + id, AS3722_LDO3_MODE_PMOS_TRACKING); + if (ret < 0) { + dev_err(&pdev->dev, + "LDO3 tracking failed: %d\n", + ret); + return ret; + } + } + break; + case AS3722_REGULATOR_ID_SD0: + case AS3722_REGULATOR_ID_SD1: + case AS3722_REGULATOR_ID_SD6: + if (reg_config->ext_control) + ops = &as3722_sd016_extcntrl_ops; + else + ops = &as3722_sd016_ops; + as3722_regs->desc[id].min_uV = 610000; + as3722_regs->desc[id].uV_step = 10000; + as3722_regs->desc[id].linear_min_sel = 1; + break; + case AS3722_REGULATOR_ID_SD2: + case AS3722_REGULATOR_ID_SD3: + case AS3722_REGULATOR_ID_SD4: + case AS3722_REGULATOR_ID_SD5: + if (reg_config->ext_control) + ops = &as3722_sd2345_extcntrl_ops; + else + ops = &as3722_sd2345_ops; + as3722_regs->desc[id].linear_ranges = + as3722_sd2345_ranges; + as3722_regs->desc[id].n_linear_ranges = + ARRAY_SIZE(as3722_sd2345_ranges); + break; + default: + if (reg_config->ext_control) + ops = &as3722_ldo_extcntrl_ops; + else + ops = &as3722_ldo_ops; + as3722_regs->desc[id].min_uV = 825000; + as3722_regs->desc[id].uV_step = 25000; + as3722_regs->desc[id].linear_min_sel = 1; + as3722_regs->desc[id].enable_time = 500; + as3722_regs->desc[id].linear_ranges = as3722_ldo_ranges; + as3722_regs->desc[id].n_linear_ranges = + ARRAY_SIZE(as3722_ldo_ranges); + break; + } + as3722_regs->desc[id].ops = ops; + config.init_data = reg_config->reg_init; + config.of_node = as3722_regulator_matches[id].of_node; + rdev = devm_regulator_register(&pdev->dev, + &as3722_regs->desc[id], &config); + if (IS_ERR(rdev)) { + ret = PTR_ERR(rdev); + dev_err(&pdev->dev, "regulator %d register failed %d\n", + id, ret); + return ret; + } + + as3722_regs->rdevs[id] = rdev; + if (reg_config->ext_control) { + ret = regulator_enable_regmap(rdev); + if (ret < 0) { + dev_err(&pdev->dev, + "Regulator %d enable failed: %d\n", + id, ret); + return ret; + } + ret = as3722_extreg_init(as3722_regs, id, + reg_config->ext_control); + if (ret < 0) { + dev_err(&pdev->dev, + "AS3722 ext control failed: %d", ret); + return ret; + } + } + } + return 0; +} + +static const struct of_device_id of_as3722_regulator_match[] = { + { .compatible = "ams,as3722-regulator", }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_as3722_regulator_match); + +static struct platform_driver as3722_regulator_driver = { + .driver = { + .name = "as3722-regulator", + .owner = THIS_MODULE, + .of_match_table = of_as3722_regulator_match, + }, + .probe = as3722_regulator_probe, +}; + +module_platform_driver(as3722_regulator_driver); + +MODULE_ALIAS("platform:as3722-regulator"); +MODULE_DESCRIPTION("AS3722 regulator driver"); +MODULE_AUTHOR("Florian Lobmaier "); +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 757651e3d60e5bff705743a301d64035b919fd03 Mon Sep 17 00:00:00 2001 From: Markus Mayer Date: Tue, 10 Sep 2013 11:07:01 -0700 Subject: gpio: bcm281xx: Add GPIO driver Add the GPIO driver for the Broadcom bcm281xx family of mobile SoCs. These GPIO controllers may contain up to 8 banks where each bank includes 32 pins that can be driven high or low and act as an edge sensitive interrupt. Signed-off-by: Markus Mayer Reviewed-by: Christian Daudt Reviewed-by: Tim Kryger Reviewed-by: Matt Porter Reviewed-by: Stephen Warren [Added depends on OF_GPIO] Signed-off-by: Linus Walleij diff --git a/Documentation/devicetree/bindings/gpio/gpio-bcm-kona.txt b/Documentation/devicetree/bindings/gpio/gpio-bcm-kona.txt new file mode 100644 index 0000000..4a63bc9 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-bcm-kona.txt @@ -0,0 +1,52 @@ +Broadcom Kona Family GPIO +========================= + +This GPIO driver is used in the following Broadcom SoCs: + BCM11130, BCM11140, BCM11351, BCM28145, BCM28155 + +The Broadcom GPIO Controller IP can be configured prior to synthesis to +support up to 8 banks of 32 GPIOs where each bank has its own IRQ. The +GPIO controller only supports edge, not level, triggering of interrupts. + +Required properties +------------------- + +- compatible: "brcm,bcm11351-gpio", "brcm,kona-gpio" +- reg: Physical base address and length of the controller's registers. +- interrupts: The interrupt outputs from the controller. There is one GPIO + interrupt per GPIO bank. The number of interrupts listed depends on the + number of GPIO banks on the SoC. The interrupts must be ordered by bank, + starting with bank 0. There is always a 1:1 mapping between banks and + IRQs. +- #gpio-cells: Should be <2>. The first cell is the pin number, the second + cell is used to specify optional parameters: + - bit 0 specifies polarity (0 for normal, 1 for inverted) + See also "gpio-specifier" in .../devicetree/bindings/gpio/gpio.txt. +- #interrupt-cells: Should be <2>. The first cell is the GPIO number. The + second cell is used to specify flags. The following subset of flags is + supported: + - trigger type (bits[1:0]): + 1 = low-to-high edge triggered. + 2 = high-to-low edge triggered. + 3 = low-to-high or high-to-low edge triggered + Valid values are 1, 2, 3 + See also .../devicetree/bindings/interrupt-controller/interrupts.txt. +- gpio-controller: Marks the device node as a GPIO controller. +- interrupt-controller: Marks the device node as an interrupt controller. + +Example: + gpio: gpio@35003000 { + compatible = "brcm,bcm11351-gpio", "brcm,kona-gpio"; + reg = <0x35003000 0x800>; + interrupts = + ; + #gpio-cells = <2>; + #interrupt-cells = <2>; + gpio-controller; + interrupt-controller; + }; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index a6d67c5..8e1d4db 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -759,6 +759,12 @@ config GPIO_MSIC Enable support for GPIO on intel MSIC controllers found in intel MID devices +config GPIO_BCM_KONA + bool "Broadcom Kona GPIO" + depends on OF_GPIO + help + Turn on GPIO support for Broadcom "Kona" chips. + comment "USB GPIO expanders:" config GPIO_VIPERBOARD diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 98e23eb..aaf1ada 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o +obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c new file mode 100644 index 0000000..f7d932a --- /dev/null +++ b/drivers/gpio/gpio-bcm-kona.c @@ -0,0 +1,632 @@ +/* + * Copyright (C) 2012-2013 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BCM_GPIO_PASSWD 0x00a5a501 +#define GPIO_PER_BANK 32 +#define GPIO_MAX_BANK_NUM 8 + +#define GPIO_BANK(gpio) ((gpio) >> 5) +#define GPIO_BIT(gpio) ((gpio) & (GPIO_PER_BANK - 1)) + +#define GPIO_OUT_STATUS(bank) (0x00000000 + ((bank) << 2)) +#define GPIO_IN_STATUS(bank) (0x00000020 + ((bank) << 2)) +#define GPIO_OUT_SET(bank) (0x00000040 + ((bank) << 2)) +#define GPIO_OUT_CLEAR(bank) (0x00000060 + ((bank) << 2)) +#define GPIO_INT_STATUS(bank) (0x00000080 + ((bank) << 2)) +#define GPIO_INT_MASK(bank) (0x000000a0 + ((bank) << 2)) +#define GPIO_INT_MSKCLR(bank) (0x000000c0 + ((bank) << 2)) +#define GPIO_CONTROL(bank) (0x00000100 + ((bank) << 2)) +#define GPIO_PWD_STATUS(bank) (0x00000500 + ((bank) << 2)) + +#define GPIO_GPPWR_OFFSET 0x00000520 + +#define GPIO_GPCTR0_DBR_SHIFT 5 +#define GPIO_GPCTR0_DBR_MASK 0x000001e0 + +#define GPIO_GPCTR0_ITR_SHIFT 3 +#define GPIO_GPCTR0_ITR_MASK 0x00000018 +#define GPIO_GPCTR0_ITR_CMD_RISING_EDGE 0x00000001 +#define GPIO_GPCTR0_ITR_CMD_FALLING_EDGE 0x00000002 +#define GPIO_GPCTR0_ITR_CMD_BOTH_EDGE 0x00000003 + +#define GPIO_GPCTR0_IOTR_MASK 0x00000001 +#define GPIO_GPCTR0_IOTR_CMD_0UTPUT 0x00000000 +#define GPIO_GPCTR0_IOTR_CMD_INPUT 0x00000001 + +#define GPIO_GPCTR0_DB_ENABLE_MASK 0x00000100 + +#define LOCK_CODE 0xffffffff +#define UNLOCK_CODE 0x00000000 + +struct bcm_kona_gpio { + void __iomem *reg_base; + int num_bank; + spinlock_t lock; + struct gpio_chip gpio_chip; + struct irq_domain *irq_domain; + struct bcm_kona_gpio_bank *banks; + struct platform_device *pdev; +}; + +struct bcm_kona_gpio_bank { + int id; + int irq; + /* Used in the interrupt handler */ + struct bcm_kona_gpio *kona_gpio; +}; + +static inline struct bcm_kona_gpio *to_kona_gpio(struct gpio_chip *chip) +{ + return container_of(chip, struct bcm_kona_gpio, gpio_chip); +} + +static void bcm_kona_gpio_set_lockcode_bank(void __iomem *reg_base, + int bank_id, int lockcode) +{ + writel(BCM_GPIO_PASSWD, reg_base + GPIO_GPPWR_OFFSET); + writel(lockcode, reg_base + GPIO_PWD_STATUS(bank_id)); +} + +static inline void bcm_kona_gpio_lock_bank(void __iomem *reg_base, int bank_id) +{ + bcm_kona_gpio_set_lockcode_bank(reg_base, bank_id, LOCK_CODE); +} + +static inline void bcm_kona_gpio_unlock_bank(void __iomem *reg_base, + int bank_id) +{ + bcm_kona_gpio_set_lockcode_bank(reg_base, bank_id, UNLOCK_CODE); +} + +static void bcm_kona_gpio_set(struct gpio_chip *chip, unsigned gpio, int value) +{ + struct bcm_kona_gpio *kona_gpio; + void __iomem *reg_base; + int bank_id = GPIO_BANK(gpio); + int bit = GPIO_BIT(gpio); + u32 val, reg_offset; + unsigned long flags; + + kona_gpio = to_kona_gpio(chip); + reg_base = kona_gpio->reg_base; + spin_lock_irqsave(&kona_gpio->lock, flags); + bcm_kona_gpio_unlock_bank(reg_base, bank_id); + + /* determine the GPIO pin direction */ + val = readl(reg_base + GPIO_CONTROL(gpio)); + val &= GPIO_GPCTR0_IOTR_MASK; + + /* this function only applies to output pin */ + if (GPIO_GPCTR0_IOTR_CMD_INPUT == val) + goto out; + + reg_offset = value ? GPIO_OUT_SET(bank_id) : GPIO_OUT_CLEAR(bank_id); + + val = readl(reg_base + reg_offset); + val |= BIT(bit); + writel(val, reg_base + reg_offset); + +out: + bcm_kona_gpio_lock_bank(reg_base, bank_id); + spin_unlock_irqrestore(&kona_gpio->lock, flags); +} + +static int bcm_kona_gpio_get(struct gpio_chip *chip, unsigned gpio) +{ + struct bcm_kona_gpio *kona_gpio; + void __iomem *reg_base; + int bank_id = GPIO_BANK(gpio); + int bit = GPIO_BIT(gpio); + u32 val, reg_offset; + unsigned long flags; + + kona_gpio = to_kona_gpio(chip); + reg_base = kona_gpio->reg_base; + spin_lock_irqsave(&kona_gpio->lock, flags); + bcm_kona_gpio_unlock_bank(reg_base, bank_id); + + /* determine the GPIO pin direction */ + val = readl(reg_base + GPIO_CONTROL(gpio)); + val &= GPIO_GPCTR0_IOTR_MASK; + + /* read the GPIO bank status */ + reg_offset = (GPIO_GPCTR0_IOTR_CMD_INPUT == val) ? + GPIO_IN_STATUS(bank_id) : GPIO_OUT_STATUS(bank_id); + val = readl(reg_base + reg_offset); + + bcm_kona_gpio_lock_bank(reg_base, bank_id); + spin_unlock_irqrestore(&kona_gpio->lock, flags); + + /* return the specified bit status */ + return !!(val & bit); +} + +static int bcm_kona_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) +{ + struct bcm_kona_gpio *kona_gpio; + void __iomem *reg_base; + u32 val; + unsigned long flags; + int bank_id = GPIO_BANK(gpio); + + kona_gpio = to_kona_gpio(chip); + reg_base = kona_gpio->reg_base; + spin_lock_irqsave(&kona_gpio->lock, flags); + bcm_kona_gpio_unlock_bank(reg_base, bank_id); + + val = readl(reg_base + GPIO_CONTROL(gpio)); + val &= ~GPIO_GPCTR0_IOTR_MASK; + val |= GPIO_GPCTR0_IOTR_CMD_INPUT; + writel(val, reg_base + GPIO_CONTROL(gpio)); + + bcm_kona_gpio_lock_bank(reg_base, bank_id); + spin_unlock_irqrestore(&kona_gpio->lock, flags); + + return 0; +} + +static int bcm_kona_gpio_direction_output(struct gpio_chip *chip, + unsigned gpio, int value) +{ + struct bcm_kona_gpio *kona_gpio; + void __iomem *reg_base; + int bank_id = GPIO_BANK(gpio); + int bit = GPIO_BIT(gpio); + u32 val, reg_offset; + unsigned long flags; + + kona_gpio = to_kona_gpio(chip); + reg_base = kona_gpio->reg_base; + spin_lock_irqsave(&kona_gpio->lock, flags); + bcm_kona_gpio_unlock_bank(reg_base, bank_id); + + val = readl(reg_base + GPIO_CONTROL(gpio)); + val &= ~GPIO_GPCTR0_IOTR_MASK; + val |= GPIO_GPCTR0_IOTR_CMD_0UTPUT; + writel(val, reg_base + GPIO_CONTROL(gpio)); + reg_offset = value ? GPIO_OUT_SET(bank_id) : GPIO_OUT_CLEAR(bank_id); + + val = readl(reg_base + reg_offset); + val |= BIT(bit); + writel(val, reg_base + reg_offset); + + bcm_kona_gpio_lock_bank(reg_base, bank_id); + spin_unlock_irqrestore(&kona_gpio->lock, flags); + + return 0; +} + +static int bcm_kona_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) +{ + struct bcm_kona_gpio *kona_gpio; + + kona_gpio = to_kona_gpio(chip); + if (gpio >= kona_gpio->gpio_chip.ngpio) + return -ENXIO; + return irq_create_mapping(kona_gpio->irq_domain, gpio); +} + +static int bcm_kona_gpio_set_debounce(struct gpio_chip *chip, unsigned gpio, + unsigned debounce) +{ + struct bcm_kona_gpio *kona_gpio; + void __iomem *reg_base; + u32 val, res; + unsigned long flags; + int bank_id = GPIO_BANK(gpio); + + kona_gpio = to_kona_gpio(chip); + reg_base = kona_gpio->reg_base; + /* debounce must be 1-128ms (or 0) */ + if ((debounce > 0 && debounce < 1000) || debounce > 128000) { + dev_err(chip->dev, "Debounce value %u not in range\n", + debounce); + return -EINVAL; + } + + /* calculate debounce bit value */ + if (debounce != 0) { + /* Convert to ms */ + debounce /= 1000; + /* find the MSB */ + res = fls(debounce) - 1; + /* Check if MSB-1 is set (round up or down) */ + if (res > 0 && (debounce & BIT(res - 1))) + res++; + } + + /* spin lock for read-modify-write of the GPIO register */ + spin_lock_irqsave(&kona_gpio->lock, flags); + bcm_kona_gpio_unlock_bank(reg_base, bank_id); + + val = readl(reg_base + GPIO_CONTROL(gpio)); + val &= ~GPIO_GPCTR0_DBR_MASK; + + if (debounce == 0) { + /* disable debounce */ + val &= ~GPIO_GPCTR0_DB_ENABLE_MASK; + } else { + val |= GPIO_GPCTR0_DB_ENABLE_MASK | + (res << GPIO_GPCTR0_DBR_SHIFT); + } + + writel(val, reg_base + GPIO_CONTROL(gpio)); + + bcm_kona_gpio_lock_bank(reg_base, bank_id); + spin_unlock_irqrestore(&kona_gpio->lock, flags); + + return 0; +} + +static struct gpio_chip template_chip = { + .label = "bcm-kona-gpio", + .direction_input = bcm_kona_gpio_direction_input, + .get = bcm_kona_gpio_get, + .direction_output = bcm_kona_gpio_direction_output, + .set = bcm_kona_gpio_set, + .set_debounce = bcm_kona_gpio_set_debounce, + .to_irq = bcm_kona_gpio_to_irq, + .base = 0, +}; + +static void bcm_kona_gpio_irq_ack(struct irq_data *d) +{ + struct bcm_kona_gpio *kona_gpio; + void __iomem *reg_base; + int gpio = d->hwirq; + int bank_id = GPIO_BANK(gpio); + int bit = GPIO_BIT(gpio); + u32 val; + unsigned long flags; + + kona_gpio = irq_data_get_irq_chip_data(d); + reg_base = kona_gpio->reg_base; + spin_lock_irqsave(&kona_gpio->lock, flags); + bcm_kona_gpio_unlock_bank(reg_base, bank_id); + + val = readl(reg_base + GPIO_INT_STATUS(bank_id)); + val |= BIT(bit); + writel(val, reg_base + GPIO_INT_STATUS(bank_id)); + + bcm_kona_gpio_lock_bank(reg_base, bank_id); + spin_unlock_irqrestore(&kona_gpio->lock, flags); +} + +static void bcm_kona_gpio_irq_mask(struct irq_data *d) +{ + struct bcm_kona_gpio *kona_gpio; + void __iomem *reg_base; + int gpio = d->hwirq; + int bank_id = GPIO_BANK(gpio); + int bit = GPIO_BIT(gpio); + u32 val; + unsigned long flags; + + kona_gpio = irq_data_get_irq_chip_data(d); + reg_base = kona_gpio->reg_base; + spin_lock_irqsave(&kona_gpio->lock, flags); + bcm_kona_gpio_unlock_bank(reg_base, bank_id); + + val = readl(reg_base + GPIO_INT_MASK(bank_id)); + val |= BIT(bit); + writel(val, reg_base + GPIO_INT_MASK(bank_id)); + + bcm_kona_gpio_lock_bank(reg_base, bank_id); + spin_unlock_irqrestore(&kona_gpio->lock, flags); +} + +static void bcm_kona_gpio_irq_unmask(struct irq_data *d) +{ + struct bcm_kona_gpio *kona_gpio; + void __iomem *reg_base; + int gpio = d->hwirq; + int bank_id = GPIO_BANK(gpio); + int bit = GPIO_BIT(gpio); + u32 val; + unsigned long flags; + + kona_gpio = irq_data_get_irq_chip_data(d); + reg_base = kona_gpio->reg_base; + spin_lock_irqsave(&kona_gpio->lock, flags); + bcm_kona_gpio_unlock_bank(reg_base, bank_id); + + val = readl(reg_base + GPIO_INT_MSKCLR(bank_id)); + val |= BIT(bit); + writel(val, reg_base + GPIO_INT_MSKCLR(bank_id)); + + bcm_kona_gpio_lock_bank(reg_base, bank_id); + spin_unlock_irqrestore(&kona_gpio->lock, flags); +} + +static int bcm_kona_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct bcm_kona_gpio *kona_gpio; + void __iomem *reg_base; + int gpio = d->hwirq; + u32 lvl_type; + u32 val; + unsigned long flags; + int bank_id = GPIO_BANK(gpio); + + kona_gpio = irq_data_get_irq_chip_data(d); + reg_base = kona_gpio->reg_base; + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_RISING: + lvl_type = GPIO_GPCTR0_ITR_CMD_RISING_EDGE; + break; + + case IRQ_TYPE_EDGE_FALLING: + lvl_type = GPIO_GPCTR0_ITR_CMD_FALLING_EDGE; + break; + + case IRQ_TYPE_EDGE_BOTH: + lvl_type = GPIO_GPCTR0_ITR_CMD_BOTH_EDGE; + break; + + case IRQ_TYPE_LEVEL_HIGH: + case IRQ_TYPE_LEVEL_LOW: + /* BCM GPIO doesn't support level triggering */ + default: + dev_err(kona_gpio->gpio_chip.dev, + "Invalid BCM GPIO irq type 0x%x\n", type); + return -EINVAL; + } + + spin_lock_irqsave(&kona_gpio->lock, flags); + bcm_kona_gpio_unlock_bank(reg_base, bank_id); + + val = readl(reg_base + GPIO_CONTROL(gpio)); + val &= ~GPIO_GPCTR0_ITR_MASK; + val |= lvl_type << GPIO_GPCTR0_ITR_SHIFT; + writel(val, reg_base + GPIO_CONTROL(gpio)); + + bcm_kona_gpio_lock_bank(reg_base, bank_id); + spin_unlock_irqrestore(&kona_gpio->lock, flags); + + return 0; +} + +static void bcm_kona_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + void __iomem *reg_base; + int bit, bank_id; + unsigned long sta; + struct bcm_kona_gpio_bank *bank = irq_get_handler_data(irq); + struct irq_chip *chip = irq_desc_get_chip(desc); + + chained_irq_enter(chip, desc); + + /* + * For bank interrupts, we can't use chip_data to store the kona_gpio + * pointer, since GIC needs it for its own purposes. Therefore, we get + * our pointer from the bank structure. + */ + reg_base = bank->kona_gpio->reg_base; + bank_id = bank->id; + bcm_kona_gpio_unlock_bank(reg_base, bank_id); + + while ((sta = readl(reg_base + GPIO_INT_STATUS(bank_id)) & + (~(readl(reg_base + GPIO_INT_MASK(bank_id)))))) { + for_each_set_bit(bit, &sta, 32) { + int gpio = GPIO_PER_BANK * bank_id + bit; + int virq = irq_find_mapping(bank->kona_gpio->irq_domain, + gpio); + /* + * Clear interrupt before handler is called so we don't + * miss any interrupt occurred during executing them. + */ + writel(readl(reg_base + GPIO_INT_STATUS(bank_id)) | + BIT(bit), reg_base + GPIO_INT_STATUS(bank_id)); + /* Invoke interrupt handler */ + generic_handle_irq(virq); + } + } + + bcm_kona_gpio_lock_bank(reg_base, bank_id); + + chained_irq_exit(chip, desc); +} + +static struct irq_chip bcm_gpio_irq_chip = { + .name = "bcm-kona-gpio", + .irq_ack = bcm_kona_gpio_irq_ack, + .irq_mask = bcm_kona_gpio_irq_mask, + .irq_unmask = bcm_kona_gpio_irq_unmask, + .irq_set_type = bcm_kona_gpio_irq_set_type, +}; + +static struct __initconst of_device_id bcm_kona_gpio_of_match[] = { + { .compatible = "brcm,kona-gpio" }, + {} +}; + +MODULE_DEVICE_TABLE(of, bcm_kona_gpio_of_match); + +/* + * This lock class tells lockdep that GPIO irqs are in a different + * category than their parents, so it won't report false recursion. + */ +static struct lock_class_key gpio_lock_class; + +static int bcm_kona_gpio_irq_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hwirq) +{ + int ret; + + ret = irq_set_chip_data(virq, d->host_data); + if (ret < 0) + return ret; + irq_set_lockdep_class(virq, &gpio_lock_class); + irq_set_chip_and_handler(virq, &bcm_gpio_irq_chip, handle_simple_irq); + irq_set_nested_thread(virq, 1); + set_irq_flags(virq, IRQF_VALID); + + return 0; +} + +static void bcm_kona_gpio_irq_unmap(struct irq_domain *d, unsigned int virq) +{ + irq_set_chip_and_handler(virq, NULL, NULL); + irq_set_chip_data(virq, NULL); +} + +static struct irq_domain_ops bcm_kona_irq_ops = { + .map = bcm_kona_gpio_irq_map, + .unmap = bcm_kona_gpio_irq_unmap, + .xlate = irq_domain_xlate_twocell, +}; + +static void bcm_kona_gpio_reset(struct bcm_kona_gpio *kona_gpio) +{ + void __iomem *reg_base; + int i; + + reg_base = kona_gpio->reg_base; + /* disable interrupts and clear status */ + for (i = 0; i < kona_gpio->num_bank; i++) { + bcm_kona_gpio_unlock_bank(reg_base, i); + writel(0xffffffff, reg_base + GPIO_INT_MASK(i)); + writel(0xffffffff, reg_base + GPIO_INT_STATUS(i)); + bcm_kona_gpio_lock_bank(reg_base, i); + } +} + +static int bcm_kona_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct of_device_id *match; + struct resource *res; + struct bcm_kona_gpio_bank *bank; + struct bcm_kona_gpio *kona_gpio; + struct gpio_chip *chip; + int ret; + int i; + + match = of_match_device(bcm_kona_gpio_of_match, dev); + if (!match) { + dev_err(dev, "Failed to find gpio controller\n"); + return -ENODEV; + } + + kona_gpio = devm_kzalloc(dev, sizeof(*kona_gpio), GFP_KERNEL); + if (!kona_gpio) + return -ENOMEM; + + kona_gpio->gpio_chip = template_chip; + chip = &kona_gpio->gpio_chip; + kona_gpio->num_bank = of_irq_count(dev->of_node); + if (kona_gpio->num_bank == 0) { + dev_err(dev, "Couldn't determine # GPIO banks\n"); + return -ENOENT; + } + if (kona_gpio->num_bank > GPIO_MAX_BANK_NUM) { + dev_err(dev, "Too many GPIO banks configured (max=%d)\n", + GPIO_MAX_BANK_NUM); + return -ENXIO; + } + kona_gpio->banks = devm_kzalloc(dev, + kona_gpio->num_bank * + sizeof(*kona_gpio->banks), GFP_KERNEL); + if (!kona_gpio->banks) + return -ENOMEM; + + kona_gpio->pdev = pdev; + platform_set_drvdata(pdev, kona_gpio); + chip->of_node = dev->of_node; + chip->ngpio = kona_gpio->num_bank * GPIO_PER_BANK; + + kona_gpio->irq_domain = irq_domain_add_linear(dev->of_node, + chip->ngpio, + &bcm_kona_irq_ops, + kona_gpio); + if (!kona_gpio->irq_domain) { + dev_err(dev, "Couldn't allocate IRQ domain\n"); + return -ENXIO; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + kona_gpio->reg_base = devm_ioremap_resource(dev, res); + if (IS_ERR(kona_gpio->reg_base)) { + ret = -ENXIO; + goto err_irq_domain; + } + + for (i = 0; i < kona_gpio->num_bank; i++) { + bank = &kona_gpio->banks[i]; + bank->id = i; + bank->irq = platform_get_irq(pdev, i); + bank->kona_gpio = kona_gpio; + if (bank->irq < 0) { + dev_err(dev, "Couldn't get IRQ for bank %d", i); + ret = -ENOENT; + goto err_irq_domain; + } + } + + dev_info(&pdev->dev, "Setting up Kona GPIO at 0x%p (phys %#x)\n", + kona_gpio->reg_base, res->start); + + bcm_kona_gpio_reset(kona_gpio); + + ret = gpiochip_add(chip); + if (ret < 0) { + dev_err(dev, "Couldn't add GPIO chip -- %d\n", ret); + goto err_irq_domain; + } + for (i = 0; i < chip->ngpio; i++) { + int irq = bcm_kona_gpio_to_irq(chip, i); + irq_set_lockdep_class(irq, &gpio_lock_class); + irq_set_chip_and_handler(irq, &bcm_gpio_irq_chip, + handle_simple_irq); + set_irq_flags(irq, IRQF_VALID); + } + for (i = 0; i < kona_gpio->num_bank; i++) { + bank = &kona_gpio->banks[i]; + irq_set_chained_handler(bank->irq, bcm_kona_gpio_irq_handler); + irq_set_handler_data(bank->irq, bank); + } + + spin_lock_init(&kona_gpio->lock); + + return 0; + +err_irq_domain: + irq_domain_remove(kona_gpio->irq_domain); + + return ret; +} + +static struct platform_driver bcm_kona_gpio_driver = { + .driver = { + .name = "bcm-kona-gpio", + .owner = THIS_MODULE, + .of_match_table = bcm_kona_gpio_of_match, + }, + .probe = bcm_kona_gpio_probe, +}; + +module_platform_driver(bcm_kona_gpio_driver); + +MODULE_AUTHOR("Broadcom"); +MODULE_DESCRIPTION("Broadcom Kona GPIO Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From d9cadcc92a476b8dd5c301ebdea36a6459706465 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 11 Sep 2013 14:03:27 +0100 Subject: gpio: arizona: Add wm8997 support to probe Signed-off-by: Charles Keepax Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-arizona.c b/drivers/gpio/gpio-arizona.c index fa8b6a7..a2bec3c 100644 --- a/drivers/gpio/gpio-arizona.c +++ b/drivers/gpio/gpio-arizona.c @@ -113,6 +113,7 @@ static int arizona_gpio_probe(struct platform_device *pdev) switch (arizona->type) { case WM5102: case WM5110: + case WM8997: arizona_gpio->gpio_chip.ngpio = 5; break; default: -- cgit v0.10.2 From 6316a4d34f6dd832cf9d84b654d0753f3000d3da Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 12 Sep 2013 15:48:28 +0900 Subject: gpio: bt8xx: remove unnecessary pci_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure. Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-bt8xx.c b/drivers/gpio/gpio-bt8xx.c index 8369e71..9dfe36f 100644 --- a/drivers/gpio/gpio-bt8xx.c +++ b/drivers/gpio/gpio-bt8xx.c @@ -228,7 +228,6 @@ static int bt8xxgpio_probe(struct pci_dev *dev, err_release_mem: release_mem_region(pci_resource_start(dev, 0), pci_resource_len(dev, 0)); - pci_set_drvdata(dev, NULL); err_disable: pci_disable_device(dev); err_freebg: @@ -252,7 +251,6 @@ static void bt8xxgpio_remove(struct pci_dev *pdev) pci_resource_len(pdev, 0)); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); kfree(bg); } -- cgit v0.10.2 From 7d1815e1e51af36b5b52983cce8bc23cd79a92a9 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 13 Sep 2013 21:41:48 +0200 Subject: gpio: ucb1400: Can be built as a module With the recent code cleanup from Marek Vasut, driver gpio-ucb1400 can be built as a module, so change symbol GPIO_UCB1400 from bool to tristate. Signed-off-by: Jean Delvare Reviewed-by: Marek Vasut Cc: Linus Walleij Signed-off-by: Linus Walleij diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 8e1d4db..7de768e 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -703,7 +703,7 @@ config GPIO_74X164 comment "AC97 GPIO expanders:" config GPIO_UCB1400 - bool "Philips UCB1400 GPIO" + tristate "Philips UCB1400 GPIO" depends on UCB1400_CORE help This enables support for the Philips UCB1400 GPIO pins. -- cgit v0.10.2 From e9004f5039b3840089cb1cb0fe558a81e2bb55f0 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 9 Sep 2013 11:59:51 +0200 Subject: ARM: plat-iop: move the GPIO driver to drivers/gpio Move the IOP GPIO driver to live with its siblings in the GPIO subsystem. Cc: Lennert Buytenhek Cc: Dan Williams Cc: Mikael Pettersson Tested-by: Aaro Koskinen Signed-off-by: Linus Walleij diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 3f7714d..fc8c092 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -457,6 +457,7 @@ config ARCH_IOP32X depends on MMU select ARCH_REQUIRE_GPIOLIB select CPU_XSCALE + select GPIO_IOP select NEED_MACH_GPIO_H select NEED_RET_TO_USER select PCI @@ -470,6 +471,7 @@ config ARCH_IOP33X depends on MMU select ARCH_REQUIRE_GPIOLIB select CPU_XSCALE + select GPIO_IOP select NEED_MACH_GPIO_H select NEED_RET_TO_USER select PCI diff --git a/arch/arm/plat-iop/Makefile b/arch/arm/plat-iop/Makefile index a99dc15..224e56c 100644 --- a/arch/arm/plat-iop/Makefile +++ b/arch/arm/plat-iop/Makefile @@ -5,7 +5,6 @@ obj-y := # IOP32X -obj-$(CONFIG_ARCH_IOP32X) += gpio.o obj-$(CONFIG_ARCH_IOP32X) += i2c.o obj-$(CONFIG_ARCH_IOP32X) += pci.o obj-$(CONFIG_ARCH_IOP32X) += setup.o @@ -16,7 +15,6 @@ obj-$(CONFIG_ARCH_IOP32X) += pmu.o obj-$(CONFIG_ARCH_IOP32X) += restart.o # IOP33X -obj-$(CONFIG_ARCH_IOP33X) += gpio.o obj-$(CONFIG_ARCH_IOP33X) += i2c.o obj-$(CONFIG_ARCH_IOP33X) += pci.o obj-$(CONFIG_ARCH_IOP33X) += setup.o diff --git a/arch/arm/plat-iop/gpio.c b/arch/arm/plat-iop/gpio.c deleted file mode 100644 index 697de6d..0000000 --- a/arch/arm/plat-iop/gpio.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * arch/arm/plat-iop/gpio.c - * GPIO handling for Intel IOP3xx processors. - * - * Copyright (C) 2006 Lennert Buytenhek - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -void gpio_line_config(int line, int direction) -{ - unsigned long flags; - - local_irq_save(flags); - if (direction == GPIO_IN) { - *IOP3XX_GPOE |= 1 << line; - } else if (direction == GPIO_OUT) { - *IOP3XX_GPOE &= ~(1 << line); - } - local_irq_restore(flags); -} -EXPORT_SYMBOL(gpio_line_config); - -int gpio_line_get(int line) -{ - return !!(*IOP3XX_GPID & (1 << line)); -} -EXPORT_SYMBOL(gpio_line_get); - -void gpio_line_set(int line, int value) -{ - unsigned long flags; - - local_irq_save(flags); - if (value == GPIO_LOW) { - *IOP3XX_GPOD &= ~(1 << line); - } else if (value == GPIO_HIGH) { - *IOP3XX_GPOD |= 1 << line; - } - local_irq_restore(flags); -} -EXPORT_SYMBOL(gpio_line_set); - -static int iop3xx_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) -{ - gpio_line_config(gpio, GPIO_IN); - return 0; -} - -static int iop3xx_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, int level) -{ - gpio_line_set(gpio, level); - gpio_line_config(gpio, GPIO_OUT); - return 0; -} - -static int iop3xx_gpio_get_value(struct gpio_chip *chip, unsigned gpio) -{ - return gpio_line_get(gpio); -} - -static void iop3xx_gpio_set_value(struct gpio_chip *chip, unsigned gpio, int value) -{ - gpio_line_set(gpio, value); -} - -static struct gpio_chip iop3xx_chip = { - .label = "iop3xx", - .direction_input = iop3xx_gpio_direction_input, - .get = iop3xx_gpio_get_value, - .direction_output = iop3xx_gpio_direction_output, - .set = iop3xx_gpio_set_value, - .base = 0, - .ngpio = IOP3XX_N_GPIOS, -}; - -static int __init iop3xx_gpio_setup(void) -{ - return gpiochip_add(&iop3xx_chip); -} -arch_initcall(iop3xx_gpio_setup); diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b6ed304..cc30426 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -320,6 +320,15 @@ config GPIO_ICH If unsure, say N. +config GPIO_IOP + tristate "Intel IOP GPIO" + depends on ARM && (ARCH_IOP32X || ARCH_IOP33X) + help + Say yes here to support the GPIO functionality of a number of Intel + IOP32X or IOP33X. + + If unsure, say N. + config GPIO_VX855 tristate "VIA VX855/VX875 GPIO" depends on PCI diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 98e23eb..06e5662 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_GPIO_F7188X) += gpio-f7188x.o obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o obj-$(CONFIG_GPIO_ICH) += gpio-ich.o +obj-$(CONFIG_GPIO_IOP) += gpio-iop.o obj-$(CONFIG_GPIO_IT8761E) += gpio-it8761e.o obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o obj-$(CONFIG_GPIO_KEMPLD) += gpio-kempld.o diff --git a/drivers/gpio/gpio-iop.c b/drivers/gpio/gpio-iop.c new file mode 100644 index 0000000..697de6d --- /dev/null +++ b/drivers/gpio/gpio-iop.c @@ -0,0 +1,93 @@ +/* + * arch/arm/plat-iop/gpio.c + * GPIO handling for Intel IOP3xx processors. + * + * Copyright (C) 2006 Lennert Buytenhek + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +void gpio_line_config(int line, int direction) +{ + unsigned long flags; + + local_irq_save(flags); + if (direction == GPIO_IN) { + *IOP3XX_GPOE |= 1 << line; + } else if (direction == GPIO_OUT) { + *IOP3XX_GPOE &= ~(1 << line); + } + local_irq_restore(flags); +} +EXPORT_SYMBOL(gpio_line_config); + +int gpio_line_get(int line) +{ + return !!(*IOP3XX_GPID & (1 << line)); +} +EXPORT_SYMBOL(gpio_line_get); + +void gpio_line_set(int line, int value) +{ + unsigned long flags; + + local_irq_save(flags); + if (value == GPIO_LOW) { + *IOP3XX_GPOD &= ~(1 << line); + } else if (value == GPIO_HIGH) { + *IOP3XX_GPOD |= 1 << line; + } + local_irq_restore(flags); +} +EXPORT_SYMBOL(gpio_line_set); + +static int iop3xx_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) +{ + gpio_line_config(gpio, GPIO_IN); + return 0; +} + +static int iop3xx_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, int level) +{ + gpio_line_set(gpio, level); + gpio_line_config(gpio, GPIO_OUT); + return 0; +} + +static int iop3xx_gpio_get_value(struct gpio_chip *chip, unsigned gpio) +{ + return gpio_line_get(gpio); +} + +static void iop3xx_gpio_set_value(struct gpio_chip *chip, unsigned gpio, int value) +{ + gpio_line_set(gpio, value); +} + +static struct gpio_chip iop3xx_chip = { + .label = "iop3xx", + .direction_input = iop3xx_gpio_direction_input, + .get = iop3xx_gpio_get_value, + .direction_output = iop3xx_gpio_direction_output, + .set = iop3xx_gpio_set_value, + .base = 0, + .ngpio = IOP3XX_N_GPIOS, +}; + +static int __init iop3xx_gpio_setup(void) +{ + return gpiochip_add(&iop3xx_chip); +} +arch_initcall(iop3xx_gpio_setup); -- cgit v0.10.2 From 7111f8780fcf770d8624d758fd240e585adf7d3c Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 9 Sep 2013 14:45:47 +0200 Subject: ARM: iop32x: request and issue reset using gpio As the IOP GPIO driver supports gpiolib we can use the standard GPIO calls to issue a reset of the machine instead of using the custom gpio_line_set/config calls. Also request the GPIO when initializing the machine. Cc: Lennert Buytenhek Cc: Dan Williams Cc: Mikael Pettersson Tested-by: Aaro Koskinen Signed-off-by: Linus Walleij diff --git a/arch/arm/mach-iop32x/n2100.c b/arch/arm/mach-iop32x/n2100.c index 0691443..6bace5b 100644 --- a/arch/arm/mach-iop32x/n2100.c +++ b/arch/arm/mach-iop32x/n2100.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -288,8 +289,14 @@ static void n2100_power_off(void) static void n2100_restart(enum reboot_mode mode, const char *cmd) { - gpio_line_set(N2100_HARDWARE_RESET, GPIO_LOW); - gpio_line_config(N2100_HARDWARE_RESET, GPIO_OUT); + int ret; + + ret = gpio_direction_output(N2100_HARDWARE_RESET, 0); + if (ret) { + pr_crit("could not drive reset GPIO low\n"); + return; + } + /* Wait for reset to happen */ while (1) ; } @@ -308,6 +315,19 @@ static void power_button_poll(unsigned long dummy) add_timer(&power_button_poll_timer); } +static int __init n2100_request_gpios(void) +{ + int ret; + + if (!machine_is_n2100()) + return 0; + + ret = gpio_request(N2100_HARDWARE_RESET, "reset"); + if (ret) + pr_err("could not request reset GPIO\n"); + return 0; +} +device_initcall(n2100_request_gpios); static void __init n2100_init_machine(void) { -- cgit v0.10.2 From afc3b79f5dbddd4ab210beb9cc6da102a1032b89 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 9 Sep 2013 14:53:02 +0200 Subject: ARM: iop32x: read N2100 power key using gpiolib Refrain from using the custom gpio_line_get() to read the power key on the N2100, use the gpiolib function gpio_get() instead. Also request the line in the GPIOs initicall, and move the poll timer setup to that inicall so the gpio chip is available before we request this GPIO and start to poll it. Cc: Lennert Buytenhek Cc: Dan Williams Cc: Mikael Pettersson Tested-by: Aaro Koskinen Signed-off-by: Linus Walleij diff --git a/arch/arm/mach-iop32x/n2100.c b/arch/arm/mach-iop32x/n2100.c index 6bace5b..bc40a97 100644 --- a/arch/arm/mach-iop32x/n2100.c +++ b/arch/arm/mach-iop32x/n2100.c @@ -306,7 +306,7 @@ static struct timer_list power_button_poll_timer; static void power_button_poll(unsigned long dummy) { - if (gpio_line_get(N2100_POWER_BUTTON) == 0) { + if (gpio_get_value(N2100_POWER_BUTTON) == 0) { ctrl_alt_del(); return; } @@ -325,6 +325,20 @@ static int __init n2100_request_gpios(void) ret = gpio_request(N2100_HARDWARE_RESET, "reset"); if (ret) pr_err("could not request reset GPIO\n"); + + ret = gpio_request(N2100_POWER_BUTTON, "power"); + if (ret) + pr_err("could not request power GPIO\n"); + else { + ret = gpio_direction_input(N2100_POWER_BUTTON); + if (ret) + pr_err("could not set power GPIO as input\n"); + } + /* Set up power button poll timer */ + init_timer(&power_button_poll_timer); + power_button_poll_timer.function = power_button_poll; + power_button_poll_timer.expires = jiffies + (HZ / 10); + add_timer(&power_button_poll_timer); return 0; } device_initcall(n2100_request_gpios); @@ -341,11 +355,6 @@ static void __init n2100_init_machine(void) ARRAY_SIZE(n2100_i2c_devices)); pm_power_off = n2100_power_off; - - init_timer(&power_button_poll_timer); - power_button_poll_timer.function = power_button_poll; - power_button_poll_timer.expires = jiffies + (HZ / 10); - add_timer(&power_button_poll_timer); } MACHINE_START(N2100, "Thecus N2100") -- cgit v0.10.2 From f6ffa5ee039cd0168d82e3edd712ebbb1de93a00 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 9 Sep 2013 15:00:40 +0200 Subject: gpio: decouple the IOP GPIO driver from platform This removes the only dependence between the IOP GPIO driver and the GENERIC_GPIO header in and its common implementation in the namespace by copying the one constant it is using into the driver file. Cc: Lennert Buytenhek Cc: Dan Williams Cc: Mikael Pettersson Tested-by: Aaro Koskinen Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-iop.c b/drivers/gpio/gpio-iop.c index 697de6d..d4a170d 100644 --- a/drivers/gpio/gpio-iop.c +++ b/drivers/gpio/gpio-iop.c @@ -16,8 +16,8 @@ #include #include #include -#include -#include + +#define IOP3XX_N_GPIOS 8 void gpio_line_config(int line, int direction) { -- cgit v0.10.2 From 51a97d829e32b7a1b960d3365e4c2546c9c792aa Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 9 Sep 2013 16:07:42 +0200 Subject: ARM: plat-iop: remove custom complex GPIO implementation The kernel will now only use gpiolib to access GPIOs, so remove the complex GPIO flag and the custom implementation. Cc: Lennert Buytenhek Cc: Dan Williams Cc: Mikael Pettersson Tested-by: Aaro Koskinen Signed-off-by: Linus Walleij diff --git a/arch/arm/include/asm/hardware/iop3xx-gpio.h b/arch/arm/include/asm/hardware/iop3xx-gpio.h index 9eda7dc..e2a0970 100644 --- a/arch/arm/include/asm/hardware/iop3xx-gpio.h +++ b/arch/arm/include/asm/hardware/iop3xx-gpio.h @@ -28,48 +28,5 @@ #include #include -#define __ARM_GPIOLIB_COMPLEX - -#define IOP3XX_N_GPIOS 8 - -static inline int gpio_get_value(unsigned gpio) -{ - if (gpio > IOP3XX_N_GPIOS) - return __gpio_get_value(gpio); - - return gpio_line_get(gpio); -} - -static inline void gpio_set_value(unsigned gpio, int value) -{ - if (gpio > IOP3XX_N_GPIOS) { - __gpio_set_value(gpio, value); - return; - } - gpio_line_set(gpio, value); -} - -static inline int gpio_cansleep(unsigned gpio) -{ - if (gpio < IOP3XX_N_GPIOS) - return 0; - else - return __gpio_cansleep(gpio); -} - -/* - * The GPIOs are not generating any interrupt - * Note : manuals are not clear about this - */ -static inline int gpio_to_irq(int gpio) -{ - return -EINVAL; -} - -static inline int irq_to_gpio(int gpio) -{ - return -EINVAL; -} - #endif diff --git a/arch/arm/include/asm/hardware/iop3xx.h b/arch/arm/include/asm/hardware/iop3xx.h index 423744b..6af9c28 100644 --- a/arch/arm/include/asm/hardware/iop3xx.h +++ b/arch/arm/include/asm/hardware/iop3xx.h @@ -25,9 +25,6 @@ #define IOP3XX_GPIO_LINE(x) (x) #ifndef __ASSEMBLY__ -extern void gpio_line_config(int line, int direction); -extern int gpio_line_get(int line); -extern void gpio_line_set(int line, int value); extern int init_atu; extern int iop3xx_get_init_atu(void); #endif diff --git a/drivers/gpio/gpio-iop.c b/drivers/gpio/gpio-iop.c index d4a170d..17cc701 100644 --- a/drivers/gpio/gpio-iop.c +++ b/drivers/gpio/gpio-iop.c @@ -19,7 +19,7 @@ #define IOP3XX_N_GPIOS 8 -void gpio_line_config(int line, int direction) +static void gpio_line_config(int line, int direction) { unsigned long flags; @@ -31,15 +31,13 @@ void gpio_line_config(int line, int direction) } local_irq_restore(flags); } -EXPORT_SYMBOL(gpio_line_config); -int gpio_line_get(int line) +static int gpio_line_get(int line) { return !!(*IOP3XX_GPID & (1 << line)); } -EXPORT_SYMBOL(gpio_line_get); -void gpio_line_set(int line, int value) +static void gpio_line_set(int line, int value) { unsigned long flags; @@ -51,7 +49,6 @@ void gpio_line_set(int line, int value) } local_irq_restore(flags); } -EXPORT_SYMBOL(gpio_line_set); static int iop3xx_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) { -- cgit v0.10.2 From 7b85b867b99044da93f83851c806d1e324d49ed5 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 9 Sep 2013 16:39:51 +0200 Subject: ARM: plat-iop: instantiate GPIO from platform device This converts the IOP32x and IOP33x platforms to pass their base address offset by a resource attached to a platform device instead of using static offset macros implicitly passed through including . Delete the local and headers and remove the selection of NEED_MACH_GPIO_H. Pass the virtual address as a resource in the platform device at this point for bisectability, next patch will pass the physical address as is custom. Cc: Lennert Buytenhek Cc: Dan Williams Cc: Mikael Pettersson Tested-by: Aaro Koskinen Signed-off-by: Linus Walleij diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index fc8c092..e83529b 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -458,7 +458,6 @@ config ARCH_IOP32X select ARCH_REQUIRE_GPIOLIB select CPU_XSCALE select GPIO_IOP - select NEED_MACH_GPIO_H select NEED_RET_TO_USER select PCI select PLAT_IOP @@ -472,7 +471,6 @@ config ARCH_IOP33X select ARCH_REQUIRE_GPIOLIB select CPU_XSCALE select GPIO_IOP - select NEED_MACH_GPIO_H select NEED_RET_TO_USER select PCI select PLAT_IOP diff --git a/arch/arm/include/asm/hardware/iop3xx-gpio.h b/arch/arm/include/asm/hardware/iop3xx-gpio.h deleted file mode 100644 index e2a0970..0000000 --- a/arch/arm/include/asm/hardware/iop3xx-gpio.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * arch/arm/include/asm/hardware/iop3xx-gpio.h - * - * IOP3xx GPIO wrappers - * - * Copyright (c) 2008 Arnaud Patard - * Based on IXP4XX gpio.h file - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __ASM_ARM_HARDWARE_IOP3XX_GPIO_H -#define __ASM_ARM_HARDWARE_IOP3XX_GPIO_H - -#include -#include - -#endif - diff --git a/arch/arm/include/asm/hardware/iop3xx.h b/arch/arm/include/asm/hardware/iop3xx.h index 6af9c28..2594a95 100644 --- a/arch/arm/include/asm/hardware/iop3xx.h +++ b/arch/arm/include/asm/hardware/iop3xx.h @@ -18,10 +18,6 @@ /* * IOP3XX GPIO handling */ -#define GPIO_IN 0 -#define GPIO_OUT 1 -#define GPIO_LOW 0 -#define GPIO_HIGH 1 #define IOP3XX_GPIO_LINE(x) (x) #ifndef __ASSEMBLY__ @@ -165,11 +161,6 @@ extern int iop3xx_get_init_atu(void); /* PERCR0 DOESN'T EXIST - index from 1! */ #define IOP3XX_PERCR0 (volatile u32 *)IOP3XX_REG_ADDR(0x0710) -/* General Purpose I/O */ -#define IOP3XX_GPOE (volatile u32 *)IOP3XX_GPIO_REG(0x0000) -#define IOP3XX_GPID (volatile u32 *)IOP3XX_GPIO_REG(0x0004) -#define IOP3XX_GPOD (volatile u32 *)IOP3XX_GPIO_REG(0x0008) - /* Timers */ #define IOP3XX_TU_TMR0 (volatile u32 *)IOP3XX_TIMER_REG(0x0000) #define IOP3XX_TU_TMR1 (volatile u32 *)IOP3XX_TIMER_REG(0x0004) diff --git a/arch/arm/mach-iop32x/em7210.c b/arch/arm/mach-iop32x/em7210.c index 31fbb6c..177cd07 100644 --- a/arch/arm/mach-iop32x/em7210.c +++ b/arch/arm/mach-iop32x/em7210.c @@ -32,6 +32,7 @@ #include #include #include +#include "gpio-iop32x.h" static void __init em7210_timer_init(void) { @@ -183,6 +184,7 @@ void em7210_power_off(void) static void __init em7210_init_machine(void) { + register_iop32x_gpio(); platform_device_register(&em7210_serial_device); platform_device_register(&iop3xx_i2c0_device); platform_device_register(&iop3xx_i2c1_device); diff --git a/arch/arm/mach-iop32x/glantank.c b/arch/arm/mach-iop32x/glantank.c index ac30470..547b234 100644 --- a/arch/arm/mach-iop32x/glantank.c +++ b/arch/arm/mach-iop32x/glantank.c @@ -34,6 +34,7 @@ #include #include #include +#include "gpio-iop32x.h" /* * GLAN Tank timer tick configuration. @@ -187,6 +188,7 @@ static void glantank_power_off(void) static void __init glantank_init_machine(void) { + register_iop32x_gpio(); platform_device_register(&iop3xx_i2c0_device); platform_device_register(&iop3xx_i2c1_device); platform_device_register(&glantank_flash_device); diff --git a/arch/arm/mach-iop32x/gpio-iop32x.h b/arch/arm/mach-iop32x/gpio-iop32x.h new file mode 100644 index 0000000..b8710a6 --- /dev/null +++ b/arch/arm/mach-iop32x/gpio-iop32x.h @@ -0,0 +1,10 @@ +static struct resource iop32x_gpio_res[] = { + DEFINE_RES_MEM((IOP3XX_PERIPHERAL_VIRT_BASE + 0x07c4), 0x10), +}; + +static inline void register_iop32x_gpio(void) +{ + platform_device_register_simple("gpio-iop", 0, + iop32x_gpio_res, + ARRAY_SIZE(iop32x_gpio_res)); +} diff --git a/arch/arm/mach-iop32x/include/mach/gpio.h b/arch/arm/mach-iop32x/include/mach/gpio.h deleted file mode 100644 index 708f4ec..0000000 --- a/arch/arm/mach-iop32x/include/mach/gpio.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __ASM_ARCH_IOP32X_GPIO_H -#define __ASM_ARCH_IOP32X_GPIO_H - -#include - -#endif diff --git a/arch/arm/mach-iop32x/include/mach/iop32x.h b/arch/arm/mach-iop32x/include/mach/iop32x.h index 941f363..56ec864 100644 --- a/arch/arm/mach-iop32x/include/mach/iop32x.h +++ b/arch/arm/mach-iop32x/include/mach/iop32x.h @@ -19,7 +19,6 @@ * Peripherals that are shared between the iop32x and iop33x but * located at different addresses. */ -#define IOP3XX_GPIO_REG(reg) (IOP3XX_PERIPHERAL_VIRT_BASE + 0x07c4 + (reg)) #define IOP3XX_TIMER_REG(reg) (IOP3XX_PERIPHERAL_VIRT_BASE + 0x07e0 + (reg)) #include diff --git a/arch/arm/mach-iop32x/iq31244.c b/arch/arm/mach-iop32x/iq31244.c index f2cd296..0e1392b 100644 --- a/arch/arm/mach-iop32x/iq31244.c +++ b/arch/arm/mach-iop32x/iq31244.c @@ -37,6 +37,7 @@ #include #include #include +#include "gpio-iop32x.h" /* * Until March of 2007 iq31244 platforms and ep80219 platforms shared the @@ -283,6 +284,7 @@ void ep80219_power_off(void) static void __init iq31244_init_machine(void) { + register_iop32x_gpio(); platform_device_register(&iop3xx_i2c0_device); platform_device_register(&iop3xx_i2c1_device); platform_device_register(&iq31244_flash_device); diff --git a/arch/arm/mach-iop32x/iq80321.c b/arch/arm/mach-iop32x/iq80321.c index 015435d..66782ff 100644 --- a/arch/arm/mach-iop32x/iq80321.c +++ b/arch/arm/mach-iop32x/iq80321.c @@ -33,6 +33,7 @@ #include #include #include +#include "gpio-iop32x.h" /* * IQ80321 timer tick configuration. @@ -170,6 +171,7 @@ static struct platform_device iq80321_serial_device = { static void __init iq80321_init_machine(void) { + register_iop32x_gpio(); platform_device_register(&iop3xx_i2c0_device); platform_device_register(&iop3xx_i2c1_device); platform_device_register(&iq80321_flash_device); diff --git a/arch/arm/mach-iop32x/n2100.c b/arch/arm/mach-iop32x/n2100.c index bc40a97..c1cd80e 100644 --- a/arch/arm/mach-iop32x/n2100.c +++ b/arch/arm/mach-iop32x/n2100.c @@ -41,6 +41,7 @@ #include #include #include +#include "gpio-iop32x.h" /* * N2100 timer tick configuration. @@ -345,6 +346,7 @@ device_initcall(n2100_request_gpios); static void __init n2100_init_machine(void) { + register_iop32x_gpio(); platform_device_register(&iop3xx_i2c0_device); platform_device_register(&n2100_flash_device); platform_device_register(&n2100_serial_device); diff --git a/arch/arm/mach-iop33x/include/mach/gpio.h b/arch/arm/mach-iop33x/include/mach/gpio.h deleted file mode 100644 index ddd55bb..0000000 --- a/arch/arm/mach-iop33x/include/mach/gpio.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __ASM_ARCH_IOP33X_GPIO_H -#define __ASM_ARCH_IOP33X_GPIO_H - -#include - -#endif diff --git a/arch/arm/mach-iop33x/include/mach/iop33x.h b/arch/arm/mach-iop33x/include/mach/iop33x.h index a89c0a2..c951226 100644 --- a/arch/arm/mach-iop33x/include/mach/iop33x.h +++ b/arch/arm/mach-iop33x/include/mach/iop33x.h @@ -18,7 +18,6 @@ * Peripherals that are shared between the iop32x and iop33x but * located at different addresses. */ -#define IOP3XX_GPIO_REG(reg) (IOP3XX_PERIPHERAL_VIRT_BASE + 0x1780 + (reg)) #define IOP3XX_TIMER_REG(reg) (IOP3XX_PERIPHERAL_VIRT_BASE + 0x07d0 + (reg)) #include diff --git a/arch/arm/mach-iop33x/iq80331.c b/arch/arm/mach-iop33x/iq80331.c index c43304a..25741c4 100644 --- a/arch/arm/mach-iop33x/iq80331.c +++ b/arch/arm/mach-iop33x/iq80331.c @@ -122,8 +122,15 @@ static struct platform_device iq80331_flash_device = { .resource = &iq80331_flash_resource, }; +static struct resource iq80331_gpio_res[] = { + DEFINE_RES_MEM((IOP3XX_PERIPHERAL_VIRT_BASE + 0x1780), 0x10), +}; + static void __init iq80331_init_machine(void) { + platform_device_register_simple("gpio-iop", 0, + iq80331_gpio_res, + ARRAY_SIZE(iq80331_gpio_res)); platform_device_register(&iop3xx_i2c0_device); platform_device_register(&iop3xx_i2c1_device); platform_device_register(&iop33x_uart0_device); diff --git a/arch/arm/mach-iop33x/iq80332.c b/arch/arm/mach-iop33x/iq80332.c index 8192987..a3b56e1 100644 --- a/arch/arm/mach-iop33x/iq80332.c +++ b/arch/arm/mach-iop33x/iq80332.c @@ -122,8 +122,15 @@ static struct platform_device iq80332_flash_device = { .resource = &iq80332_flash_resource, }; +static struct resource iq80332_gpio_res[] = { + DEFINE_RES_MEM((IOP3XX_PERIPHERAL_VIRT_BASE + 0x1780), 0x10), +}; + static void __init iq80332_init_machine(void) { + platform_device_register_simple("gpio-iop", 0, + iq80332_gpio_res, + ARRAY_SIZE(iq80332_gpio_res)); platform_device_register(&iop3xx_i2c0_device); platform_device_register(&iop3xx_i2c1_device); platform_device_register(&iop33x_uart0_device); diff --git a/drivers/gpio/gpio-iop.c b/drivers/gpio/gpio-iop.c index 17cc701..24a86b0 100644 --- a/drivers/gpio/gpio-iop.c +++ b/drivers/gpio/gpio-iop.c @@ -16,9 +16,23 @@ #include #include #include +#include #define IOP3XX_N_GPIOS 8 +#define GPIO_IN 0 +#define GPIO_OUT 1 +#define GPIO_LOW 0 +#define GPIO_HIGH 1 + +/* Memory base offset */ +static void __iomem *base; + +#define IOP3XX_GPIO_REG(reg) (base + (reg)) +#define IOP3XX_GPOE (volatile u32 *)IOP3XX_GPIO_REG(0x0000) +#define IOP3XX_GPID (volatile u32 *)IOP3XX_GPIO_REG(0x0004) +#define IOP3XX_GPOD (volatile u32 *)IOP3XX_GPIO_REG(0x0008) + static void gpio_line_config(int line, int direction) { unsigned long flags; @@ -83,8 +97,26 @@ static struct gpio_chip iop3xx_chip = { .ngpio = IOP3XX_N_GPIOS, }; -static int __init iop3xx_gpio_setup(void) +static int iop3xx_gpio_probe(struct platform_device *pdev) { + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = (void *) res->start; + return gpiochip_add(&iop3xx_chip); } -arch_initcall(iop3xx_gpio_setup); + +static struct platform_driver iop3xx_gpio_driver = { + .driver = { + .name = "gpio-iop", + .owner = THIS_MODULE, + }, + .probe = iop3xx_gpio_probe, +}; + +static int __init iop3xx_gpio_init(void) +{ + return platform_driver_register(&iop3xx_gpio_driver); +} +arch_initcall(iop3xx_gpio_init); -- cgit v0.10.2 From e3f94b385748c10b735f392b69f39f33f02ca3a5 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 9 Sep 2013 16:54:32 +0200 Subject: gpio: iop: use readl/writel accessors Use the standard 32bit I/O accessors instead of just assigning addresses. Cc: Lennert Buytenhek Cc: Dan Williams Cc: Mikael Pettersson Tested-by: Aaro Koskinen Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-iop.c b/drivers/gpio/gpio-iop.c index 24a86b0..0d991d7 100644 --- a/drivers/gpio/gpio-iop.c +++ b/drivers/gpio/gpio-iop.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #define IOP3XX_N_GPIOS 8 @@ -29,38 +31,44 @@ static void __iomem *base; #define IOP3XX_GPIO_REG(reg) (base + (reg)) -#define IOP3XX_GPOE (volatile u32 *)IOP3XX_GPIO_REG(0x0000) -#define IOP3XX_GPID (volatile u32 *)IOP3XX_GPIO_REG(0x0004) -#define IOP3XX_GPOD (volatile u32 *)IOP3XX_GPIO_REG(0x0008) +#define IOP3XX_GPOE IOP3XX_GPIO_REG(0x0000) +#define IOP3XX_GPID IOP3XX_GPIO_REG(0x0004) +#define IOP3XX_GPOD IOP3XX_GPIO_REG(0x0008) static void gpio_line_config(int line, int direction) { unsigned long flags; + u32 val; local_irq_save(flags); + val = readl(IOP3XX_GPOE); if (direction == GPIO_IN) { - *IOP3XX_GPOE |= 1 << line; + val |= BIT(line); } else if (direction == GPIO_OUT) { - *IOP3XX_GPOE &= ~(1 << line); + val &= ~BIT(line); } + writel(val, IOP3XX_GPOE); local_irq_restore(flags); } static int gpio_line_get(int line) { - return !!(*IOP3XX_GPID & (1 << line)); + return !!(readl(IOP3XX_GPID) & BIT(line)); } static void gpio_line_set(int line, int value) { unsigned long flags; + u32 val; local_irq_save(flags); + val = readl(IOP3XX_GPOD); if (value == GPIO_LOW) { - *IOP3XX_GPOD &= ~(1 << line); + val &= ~BIT(line); } else if (value == GPIO_HIGH) { - *IOP3XX_GPOD |= 1 << line; + val |= BIT(line); } + writel(val, IOP3XX_GPOD); local_irq_restore(flags); } -- cgit v0.10.2 From e34ca9de0b3575fc7e94b1f520169fc7024c6dd2 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 9 Sep 2013 16:59:54 +0200 Subject: ARM: plat-iop: pass physical base for GPIO This alters the IOP platforms to pass a physical base for their GPIO blocks and alters the driver to remap it when probing instead of relying on the virtual addresses to be used. Cc: Lennert Buytenhek Cc: Dan Williams Cc: Mikael Pettersson Tested-by: Aaro Koskinen Signed-off-by: Linus Walleij diff --git a/arch/arm/mach-iop32x/gpio-iop32x.h b/arch/arm/mach-iop32x/gpio-iop32x.h index b8710a6..3c7309c 100644 --- a/arch/arm/mach-iop32x/gpio-iop32x.h +++ b/arch/arm/mach-iop32x/gpio-iop32x.h @@ -1,5 +1,5 @@ static struct resource iop32x_gpio_res[] = { - DEFINE_RES_MEM((IOP3XX_PERIPHERAL_VIRT_BASE + 0x07c4), 0x10), + DEFINE_RES_MEM((IOP3XX_PERIPHERAL_PHYS_BASE + 0x07c4), 0x10), }; static inline void register_iop32x_gpio(void) diff --git a/arch/arm/mach-iop33x/iq80331.c b/arch/arm/mach-iop33x/iq80331.c index 25741c4..e2cb65c 100644 --- a/arch/arm/mach-iop33x/iq80331.c +++ b/arch/arm/mach-iop33x/iq80331.c @@ -123,7 +123,7 @@ static struct platform_device iq80331_flash_device = { }; static struct resource iq80331_gpio_res[] = { - DEFINE_RES_MEM((IOP3XX_PERIPHERAL_VIRT_BASE + 0x1780), 0x10), + DEFINE_RES_MEM((IOP3XX_PERIPHERAL_PHYS_BASE + 0x1780), 0x10), }; static void __init iq80331_init_machine(void) diff --git a/arch/arm/mach-iop33x/iq80332.c b/arch/arm/mach-iop33x/iq80332.c index a3b56e1..0b6269d 100644 --- a/arch/arm/mach-iop33x/iq80332.c +++ b/arch/arm/mach-iop33x/iq80332.c @@ -123,7 +123,7 @@ static struct platform_device iq80332_flash_device = { }; static struct resource iq80332_gpio_res[] = { - DEFINE_RES_MEM((IOP3XX_PERIPHERAL_VIRT_BASE + 0x1780), 0x10), + DEFINE_RES_MEM((IOP3XX_PERIPHERAL_PHYS_BASE + 0x1780), 0x10), }; static void __init iq80332_init_machine(void) diff --git a/drivers/gpio/gpio-iop.c b/drivers/gpio/gpio-iop.c index 0d991d7..c22a61b 100644 --- a/drivers/gpio/gpio-iop.c +++ b/drivers/gpio/gpio-iop.c @@ -110,7 +110,7 @@ static int iop3xx_gpio_probe(struct platform_device *pdev) struct resource *res; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = (void *) res->start; + base = devm_ioremap_resource(&pdev->dev, res); return gpiochip_add(&iop3xx_chip); } -- cgit v0.10.2 From 1dc94272117e35c1618516ffe5b129a7663c1d03 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 20 Sep 2013 23:14:18 +0200 Subject: gpio: bcm-kona: only use set_irq_flags() on ARM As per the pattern from other GPIO drivers, use set_irq_flags() on ARM only, use irq_set_noprobe() on other archs. Also rename the argument "virq" to just "irq", this IRQ isn't any more "virtual" than any other Linux IRQ number, we use "hwirq" for the actual hw-numbers, "virq" is just bogus. Reviewed-by: Markus Mayer Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index f7d932a..c0751a8 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -468,18 +468,22 @@ MODULE_DEVICE_TABLE(of, bcm_kona_gpio_of_match); */ static struct lock_class_key gpio_lock_class; -static int bcm_kona_gpio_irq_map(struct irq_domain *d, unsigned int virq, +static int bcm_kona_gpio_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hwirq) { int ret; - ret = irq_set_chip_data(virq, d->host_data); + ret = irq_set_chip_data(irq, d->host_data); if (ret < 0) return ret; - irq_set_lockdep_class(virq, &gpio_lock_class); - irq_set_chip_and_handler(virq, &bcm_gpio_irq_chip, handle_simple_irq); - irq_set_nested_thread(virq, 1); - set_irq_flags(virq, IRQF_VALID); + irq_set_lockdep_class(irq, &gpio_lock_class); + irq_set_chip_and_handler(irq, &bcm_gpio_irq_chip, handle_simple_irq); + irq_set_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + irq_set_noprobe(irq); +#endif return 0; } @@ -598,7 +602,11 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev) irq_set_lockdep_class(irq, &gpio_lock_class); irq_set_chip_and_handler(irq, &bcm_gpio_irq_chip, handle_simple_irq); +#ifdef CONFIG_ARM set_irq_flags(irq, IRQF_VALID); +#else + irq_set_noprobe(irq); +#endif } for (i = 0; i < kona_gpio->num_bank; i++) { bank = &kona_gpio->banks[i]; -- cgit v0.10.2 From 84aac6c79bfdcfbcd8541c814b365c3001cdf5e6 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sat, 21 Sep 2013 12:00:36 +0200 Subject: ASoC: kirkwood: fix loss of external clock at probe time At probe time, when the clock driver is not yet initialized, the external clock of the kirkwood sound device will not be usable. This patch fixes this problem defering the device probe. Signed-off-by: Jean-Francois Moine Signed-off-by: Mark Brown diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c index 0f3d73d..3e59af9 100644 --- a/sound/soc/kirkwood/kirkwood-i2s.c +++ b/sound/soc/kirkwood/kirkwood-i2s.c @@ -496,7 +496,10 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev) return err; priv->extclk = devm_clk_get(&pdev->dev, "extclk"); - if (!IS_ERR(priv->extclk)) { + if (IS_ERR(priv->extclk)) { + if (PTR_ERR(priv->extclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + } else { if (priv->extclk == priv->clk) { devm_clk_put(&pdev->dev, priv->extclk); priv->extclk = ERR_PTR(-EINVAL); -- cgit v0.10.2 From 9d7278d0b4238575739a96f64fb14b30ed0589b5 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Mon, 23 Sep 2013 11:57:00 +0800 Subject: pinctrl: pinctrl-adi2: Add dependency to arch BLACKFIN in Kconfig. Signed-off-by: Sonic Zhang Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index f45a21c..21db201 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -51,6 +51,7 @@ config PINCTRL_AB8505 config PINCTRL_ADI2 bool "ADI pin controller driver" + depends on BLACKFIN select PINMUX select IRQ_DOMAIN help -- cgit v0.10.2 From 7330e345eefedce7675aa5974dd5e79ef1e4fa32 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Mon, 23 Sep 2013 12:07:52 +0800 Subject: pinctrl: pinctrl-adi2-bf60x: remove useless and duplicated GPIO definition for PPI2. Signed-off-by: Sonic Zhang Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/pinctrl-adi2-bf60x.c b/drivers/pinctrl/pinctrl-adi2-bf60x.c index e90cb80..bf57aea 100644 --- a/drivers/pinctrl/pinctrl-adi2-bf60x.c +++ b/drivers/pinctrl/pinctrl-adi2-bf60x.c @@ -236,8 +236,7 @@ static const unsigned ppi2_8b_pins[] = { static const unsigned ppi2_16b_pins[] = { GPIO_PA0, GPIO_PA1, GPIO_PA2, GPIO_PA3, GPIO_PA4, GPIO_PA5, GPIO_PA6, GPIO_PA7, GPIO_PA8, GPIO_PA9, GPIO_PA10, GPIO_PA11, GPIO_PA12, - GPIO_PA13, GPIO_PA14, GPIO_PA15, - GPIO_PA7, GPIO_PB0, GPIO_PB1, GPIO_PB2, GPIO_PB3, + GPIO_PA13, GPIO_PA14, GPIO_PA15, GPIO_PB0, GPIO_PB1, GPIO_PB2, }; static const unsigned lp0_pins[] = { -- cgit v0.10.2 From 741ecef62a37bcc368ff8c7282f810327edc3d94 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Tue, 3 Sep 2013 16:29:01 +0800 Subject: blackfin: pinctrl-adi2: Enable PINCTRL framework for BF54x and BF60x. - Remove unused macro MAX_RESOURCES - Override blackfin legacy peripheral pinmux request and free APIs by devm_pinctrl_get_select_default() to init the peripheral portmux setting. v3-chagnes: - Move pinctrl soc data out of blackfin arch folder. Signed-off-by: Sonic Zhang Acked-by: Steven Miao Signed-off-by: Linus Walleij diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig index a1199b2..74314bd 100644 --- a/arch/blackfin/Kconfig +++ b/arch/blackfin/Kconfig @@ -324,6 +324,10 @@ config GPIO_ADI def_bool y depends on (BF51x || BF52x || BF53x || BF538 || BF539 || BF561) +config PINCTRL + def_bool y + depends on BF54x || BF60x + config MEM_MT48LC64M4A2FB_7E bool depends on (BFIN533_STAMP) diff --git a/arch/blackfin/include/asm/portmux.h b/arch/blackfin/include/asm/portmux.h index 9b1e2c3..7aa2043 100644 --- a/arch/blackfin/include/asm/portmux.h +++ b/arch/blackfin/include/asm/portmux.h @@ -17,14 +17,29 @@ #define P_MAYSHARE 0x2000 #define P_DONTCARE 0x1000 - +#ifdef CONFIG_PINCTRL +#include + +#define gpio_pint_regs bfin_pint_regs +#define adi_internal_set_wake bfin_internal_set_wake + +#define peripheral_request(per, label) 0 +#define peripheral_free(per) +#define peripheral_request_list(per, label) \ + (pdev ? (IS_ERR(devm_pinctrl_get_select_default(&pdev->dev)) \ + ? -EINVAL : 0) : 0) +#define peripheral_free_list(per) +#else int peripheral_request(unsigned short per, const char *label); void peripheral_free(unsigned short per); int peripheral_request_list(const unsigned short per[], const char *label); void peripheral_free_list(const unsigned short per[]); +#endif -#include +#include +#include #include +#include #ifndef P_SPORT2_TFS #define P_SPORT2_TFS P_UNDEF diff --git a/arch/blackfin/mach-bf548/include/mach/portmux.h b/arch/blackfin/mach-bf548/include/mach/portmux.h index e222462..d9f8632 100644 --- a/arch/blackfin/mach-bf548/include/mach/portmux.h +++ b/arch/blackfin/mach-bf548/include/mach/portmux.h @@ -7,8 +7,6 @@ #ifndef _MACH_PORTMUX_H_ #define _MACH_PORTMUX_H_ -#define MAX_RESOURCES MAX_BLACKFIN_GPIOS - #define P_SPORT2_TFS (P_DEFINED | P_IDENT(GPIO_PA0) | P_FUNCT(0)) #define P_SPORT2_DTSEC (P_DEFINED | P_IDENT(GPIO_PA1) | P_FUNCT(0)) #define P_SPORT2_DTPRI (P_DEFINED | P_IDENT(GPIO_PA2) | P_FUNCT(0)) diff --git a/arch/blackfin/mach-bf609/include/mach/portmux.h b/arch/blackfin/mach-bf609/include/mach/portmux.h index 2e1a51c..fe34191 100644 --- a/arch/blackfin/mach-bf609/include/mach/portmux.h +++ b/arch/blackfin/mach-bf609/include/mach/portmux.h @@ -7,8 +7,6 @@ #ifndef _MACH_PORTMUX_H_ #define _MACH_PORTMUX_H_ -#define MAX_RESOURCES MAX_BLACKFIN_GPIOS - /* EMAC RMII Port Mux */ #define P_MII0_MDC (P_DEFINED | P_IDENT(GPIO_PC6) | P_FUNCT(0)) #define P_MII0_MDIO (P_DEFINED | P_IDENT(GPIO_PC7) | P_FUNCT(0)) -- cgit v0.10.2 From 49f1d6bc5add49be7c03d83386f931c224b1d2cb Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 19 Sep 2013 17:28:07 +0530 Subject: gpio: gpio-74x164: Remove redundant spi_set_drvdata Driver core sets driver data to NULL upon failure or remove. Signed-off-by: Sachin Kamat Cc: Gabor Juhos Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c index 5d518d5..a51e893 100644 --- a/drivers/gpio/gpio-74x164.c +++ b/drivers/gpio/gpio-74x164.c @@ -176,7 +176,6 @@ static int gen_74x164_probe(struct spi_device *spi) return ret; exit_destroy: - spi_set_drvdata(spi, NULL); mutex_destroy(&chip->lock); return ret; } @@ -190,8 +189,6 @@ static int gen_74x164_remove(struct spi_device *spi) if (chip == NULL) return -ENODEV; - spi_set_drvdata(spi, NULL); - ret = gpiochip_remove(&chip->gpio_chip); if (!ret) mutex_destroy(&chip->lock); -- cgit v0.10.2 From 187a53a5ebec6a5d14d57ca12eaa4a106397a134 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 19 Sep 2013 17:28:08 +0530 Subject: gpio: gpio-74x164: Remove redundant of_match_ptr 'gen_74x164_dt_ids' is always compiled in. Hence the macro is not necessary. Signed-off-by: Sachin Kamat Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c index a51e893..1e04bf9 100644 --- a/drivers/gpio/gpio-74x164.c +++ b/drivers/gpio/gpio-74x164.c @@ -209,7 +209,7 @@ static struct spi_driver gen_74x164_driver = { .driver = { .name = "74x164", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(gen_74x164_dt_ids), + .of_match_table = gen_74x164_dt_ids, }, .probe = gen_74x164_probe, .remove = gen_74x164_remove, -- cgit v0.10.2 From 8fa82b16304b5cdcddb929afbfebfddfb7adb716 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 19 Sep 2013 17:30:53 +0530 Subject: gpio: gpio-mc33880: Remove redundant spi_set_drvdata Driver core sets driver data to NULL upon failure or remove. Signed-off-by: Sachin Kamat Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-mc33880.c b/drivers/gpio/gpio-mc33880.c index 3fd2caa..c0b7835 100644 --- a/drivers/gpio/gpio-mc33880.c +++ b/drivers/gpio/gpio-mc33880.c @@ -142,7 +142,6 @@ static int mc33880_probe(struct spi_device *spi) return ret; exit_destroy: - spi_set_drvdata(spi, NULL); mutex_destroy(&mc->lock); return ret; } @@ -156,8 +155,6 @@ static int mc33880_remove(struct spi_device *spi) if (mc == NULL) return -ENODEV; - spi_set_drvdata(spi, NULL); - ret = gpiochip_remove(&mc->chip); if (!ret) mutex_destroy(&mc->lock); -- cgit v0.10.2 From e5304db8d7541c1338a6313ae951355b5a72cd19 Mon Sep 17 00:00:00 2001 From: Graeme Smecher Date: Fri, 13 Sep 2013 14:41:48 -0700 Subject: gpio: pca953x: Don't flip bits on PCA957x GPIO expanders when probing them. The pca957x driver supports a handful of I2C GPIO expanders from NXP, Maxim, and TI. For the PCA9574 and PCA9575 devices only, the driver resets the GPIO level and direction in the pca957x_probe function. This seems like the wrong thing to do, since it can cause hardware bit twiddles during warm reboots when the chip state and reset values don't match. This kind of initialization is best left upstream (in a bootloader) or downstream (in userspace). It's also an inconsistency across devices supported by this driver. This patch is NOT boot-tested: the SoC I'm using is stuck on 2.6.37, and the patch doesn't apply trivially. Signed-off-by: Graeme Smecher Acked-by: Haojian Zhuang Tested-by: Gregory CLEMENT Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index cdd1aa1..6e48c07 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -683,17 +683,6 @@ static int device_pca957x_init(struct pca953x_chip *chip, u32 invert) int ret; u8 val[MAX_BANK]; - /* Let every port in proper state, that could save power */ - memset(val, 0, NBANK(chip)); - pca953x_write_regs(chip, PCA957X_PUPD, val); - memset(val, 0xFF, NBANK(chip)); - pca953x_write_regs(chip, PCA957X_CFG, val); - memset(val, 0, NBANK(chip)); - pca953x_write_regs(chip, PCA957X_OUT, val); - - ret = pca953x_read_regs(chip, PCA957X_IN, val); - if (ret) - goto out; ret = pca953x_read_regs(chip, PCA957X_OUT, chip->reg_output); if (ret) goto out; -- cgit v0.10.2 From 043c998f95036e7fc796b240ab5ba49a8de36df3 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 20 Sep 2013 12:32:18 +0100 Subject: regulator: core: Fix return code for invalid parameters We should be returning an error, a repeated call will never succeed. Signed-off-by: Mark Brown diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 66ae12d..ad15424 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1288,7 +1288,7 @@ static struct regulator *_regulator_get(struct device *dev, const char *id, if (id == NULL) { pr_err("get() with no identifier\n"); - return regulator; + return ERR_PTR(-EINVAL); } if (dev) -- cgit v0.10.2 From da5feefeda496f003a2e909762e72843cb9837a6 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 20 Sep 2013 18:19:05 +0100 Subject: ASoC: Docs: Update codec documentation Update the codec class driver documentation and bring it up to date with the current code base. This includes API changes, regmap and multi component. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/Documentation/sound/alsa/soc/codec.txt b/Documentation/sound/alsa/soc/codec.txt index bce23a4..db5f9c9 100644 --- a/Documentation/sound/alsa/soc/codec.txt +++ b/Documentation/sound/alsa/soc/codec.txt @@ -1,22 +1,23 @@ -ASoC Codec Driver -================= +ASoC Codec Class Driver +======================= -The codec driver is generic and hardware independent code that configures the -codec to provide audio capture and playback. It should contain no code that is -specific to the target platform or machine. All platform and machine specific -code should be added to the platform and machine drivers respectively. +The codec class driver is generic and hardware independent code that configures +the codec, FM, MODEM, BT or external DSP to provide audio capture and playback. +It should contain no code that is specific to the target platform or machine. +All platform and machine specific code should be added to the platform and +machine drivers respectively. -Each codec driver *must* provide the following features:- +Each codec class driver *must* provide the following features:- 1) Codec DAI and PCM configuration - 2) Codec control IO - using I2C, 3 Wire(SPI) or both APIs + 2) Codec control IO - using RegMap API 3) Mixers and audio controls 4) Codec audio operations + 5) DAPM description. + 6) DAPM event handler. Optionally, codec drivers can also provide:- - 5) DAPM description. - 6) DAPM event handler. 7) DAC Digital mute control. Its probably best to use this guide in conjunction with the existing codec @@ -64,26 +65,9 @@ struct snd_soc_dai_driver wm8731_dai = { 2 - Codec control IO -------------------- The codec can usually be controlled via an I2C or SPI style interface -(AC97 combines control with data in the DAI). The codec drivers provide -functions to read and write the codec registers along with supplying a -register cache:- - - /* IO control data and register cache */ - void *control_data; /* codec control (i2c/3wire) data */ - void *reg_cache; - -Codec read/write should do any data formatting and call the hardware -read write below to perform the IO. These functions are called by the -core and ALSA when performing DAPM or changing the mixer:- - - unsigned int (*read)(struct snd_soc_codec *, unsigned int); - int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); - -Codec hardware IO functions - usually points to either the I2C, SPI or AC97 -read/write:- - - hw_write_t hw_write; - hw_read_t hw_read; +(AC97 combines control with data in the DAI). The codec driver should use the +Regmap API for all codec IO. Please see include/linux/regmap.h and existing +codec drivers for example regmap usage. 3 - Mixers and audio controls @@ -127,7 +111,7 @@ Defines a stereo enumerated control 4 - Codec Audio Operations -------------------------- -The codec driver also supports the following ALSA operations:- +The codec driver also supports the following ALSA PCM operations:- /* SoC audio ops */ struct snd_soc_ops { -- cgit v0.10.2 From 3eb012834b28585a37fe0684182828efc1ab0512 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 20 Sep 2013 18:19:06 +0100 Subject: ASoC: Docs: Platform update Update the platform class driver documentation and bring it up to date with the current code base. This includes multi component and DSP. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/Documentation/sound/alsa/soc/platform.txt b/Documentation/sound/alsa/soc/platform.txt index d57efad..3a08a2c 100644 --- a/Documentation/sound/alsa/soc/platform.txt +++ b/Documentation/sound/alsa/soc/platform.txt @@ -1,9 +1,9 @@ ASoC Platform Driver ==================== -An ASoC platform driver can be divided into audio DMA and SoC DAI configuration -and control. The platform drivers only target the SoC CPU and must have no board -specific code. +An ASoC platform driver class can be divided into audio DMA drivers, SoC DAI +drivers and DSP drivers. The platform drivers only target the SoC CPU and must +have no board specific code. Audio DMA ========= @@ -64,3 +64,16 @@ Each SoC DAI driver must provide the following features:- 5) Suspend and resume (optional) Please see codec.txt for a description of items 1 - 4. + + +SoC DSP Drivers +=============== + +Each SoC DSP driver usually supplies the following features :- + + 1) DAPM graph + 2) Mixer controls + 3) DMA IO to/from DSP buffers (if applicable) + 4) Definition of DSP front end (FE) PCM devices. + +Please see DPCM.txt for a description of item 4. -- cgit v0.10.2 From 630a1b3b4f0bb40c84c1fde4fec0d97de62ccdac Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 20 Sep 2013 18:19:07 +0100 Subject: ASoC: Docs: update DAPM Update the DAPM documentation and bring it up to date with the current code base. This includes API changes and new widgets. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/Documentation/sound/alsa/soc/dapm.txt b/Documentation/sound/alsa/soc/dapm.txt index 05bf5a0..7dfd88c 100644 --- a/Documentation/sound/alsa/soc/dapm.txt +++ b/Documentation/sound/alsa/soc/dapm.txt @@ -21,7 +21,7 @@ level power systems. There are 4 power domains within DAPM - 1. Codec domain - VREF, VMID (core codec and audio power) + 1. Codec bias domain - VREF, VMID (core codec and audio power) Usually controlled at codec probe/remove and suspend/resume, although can be set at stream time if power is not needed for sidetone, etc. @@ -63,14 +63,22 @@ Audio DAPM widgets fall into a number of types:- o Line - Line Input/Output (and optional Jack) o Speaker - Speaker o Supply - Power or clock supply widget used by other widgets. + o Regulator - External regulator that supplies power to audio components. + o Clock - External clock that supplies clock to audio componnents. + o AIF IN - Audio Interface Input (with TDM slot mask). + o AIF OUT - Audio Interface Output (with TDM slot mask). + o Siggen - Signal Generator. + o DAI IN - Digital Audio Interface Input. + o DAI OUT - Digital Audio Interface Output. + o DAI Link - DAI Link between two DAI structures */ o Pre - Special PRE widget (exec before all others) o Post - Special POST widget (exec after all others) (Widgets are defined in include/sound/soc-dapm.h) -Widgets are usually added in the codec driver and the machine driver. There are -convenience macros defined in soc-dapm.h that can be used to quickly build a -list of widgets of the codecs and machines DAPM widgets. +Widgets can be added to the sound card by any of the component driver types. +There are convenience macros defined in soc-dapm.h that can be used to quickly +build a list of widgets of the codecs and machines DAPM widgets. Most widgets have a name, register, shift and invert. Some widgets have extra parameters for stream name and kcontrols. @@ -80,11 +88,13 @@ parameters for stream name and kcontrols. ------------------------- Stream Widgets relate to the stream power domain and only consist of ADCs -(analog to digital converters) and DACs (digital to analog converters). +(analog to digital converters), DACs (digital to analog converters), +AIF IN and AIF OUT. Stream widgets have the following format:- SND_SOC_DAPM_DAC(name, stream name, reg, shift, invert), +SND_SOC_DAPM_AIF_IN(name, stream, slot, reg, shift, invert) NOTE: the stream name must match the corresponding stream name in your codec snd_soc_codec_dai. @@ -94,6 +104,11 @@ e.g. stream widgets for HiFi playback and capture SND_SOC_DAPM_DAC("HiFi DAC", "HiFi Playback", REG, 3, 1), SND_SOC_DAPM_ADC("HiFi ADC", "HiFi Capture", REG, 2, 1), +e.g. stream widgets for AIF + +SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + 2.2 Path Domain Widgets ----------------------- @@ -121,12 +136,14 @@ If you dont want the mixer elements prefixed with the name of the mixer widget, you can use SND_SOC_DAPM_MIXER_NAMED_CTL instead. the parameters are the same as for SND_SOC_DAPM_MIXER. -2.3 Platform/Machine domain Widgets ------------------------------------ + +2.3 Machine domain Widgets +-------------------------- Machine widgets are different from codec widgets in that they don't have a codec register bit associated with them. A machine widget is assigned to each -machine audio component (non codec) that can be independently powered. e.g. +machine audio component (non codec or DSP) that can be independently +powered. e.g. o Speaker Amp o Microphone Bias @@ -146,12 +163,12 @@ static int spitz_mic_bias(struct snd_soc_dapm_widget* w, int event) SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias), -2.4 Codec Domain ----------------- +2.4 Codec (BIAS) Domain +----------------------- -The codec power domain has no widgets and is handled by the codecs DAPM event -handler. This handler is called when the codec powerstate is changed wrt to any -stream event or by kernel PM events. +The codec bias power domain has no widgets and is handled by the codecs DAPM +event handler. This handler is called when the codec powerstate is changed wrt +to any stream event or by kernel PM events. 2.5 Virtual Widgets @@ -169,15 +186,16 @@ After all the widgets have been defined, they can then be added to the DAPM subsystem individually with a call to snd_soc_dapm_new_control(). -3. Codec Widget Interconnections -================================ +3. Codec/DSP Widget Interconnections +==================================== -Widgets are connected to each other within the codec and machine by audio paths -(called interconnections). Each interconnection must be defined in order to -create a map of all audio paths between widgets. +Widgets are connected to each other within the codec, platform and machine by +audio paths (called interconnections). Each interconnection must be defined in +order to create a map of all audio paths between widgets. -This is easiest with a diagram of the codec (and schematic of the machine audio -system), as it requires joining widgets together via their audio signal paths. +This is easiest with a diagram of the codec or DSP (and schematic of the machine +audio system), as it requires joining widgets together via their audio signal +paths. e.g., from the WM8731 output mixer (wm8731.c) @@ -247,16 +265,9 @@ machine and includes the codec. e.g. o Mic Jack o Codec Pins -When a codec pin is NC it can be marked as not used with a call to - -snd_soc_dapm_set_endpoint(codec, "Widget Name", 0); - -The last argument is 0 for inactive and 1 for active. This way the pin and its -input widget will never be powered up and consume power. - -This also applies to machine widgets. e.g. if a headphone is connected to a -jack then the jack can be marked active. If the headphone is removed, then -the headphone jack can be marked inactive. +Endpoints are added to the DAPM graph so that their usage can be determined in +order to save power. e.g. NC codecs pins will be switched OFF, unconnected +jacks can also be switched OFF. 5 DAPM Widget Events -- cgit v0.10.2 From b5c47df974ddae44d4a1ff935cdda30b0795bc00 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 20 Sep 2013 18:19:08 +0100 Subject: ASoC: Docs: Machine update Update the machine driver documentation and bring it up to date with the current code base. This includes multi component. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/Documentation/sound/alsa/soc/machine.txt b/Documentation/sound/alsa/soc/machine.txt index d50c14d..74056db 100644 --- a/Documentation/sound/alsa/soc/machine.txt +++ b/Documentation/sound/alsa/soc/machine.txt @@ -1,8 +1,10 @@ ASoC Machine Driver =================== -The ASoC machine (or board) driver is the code that glues together the platform -and codec drivers. +The ASoC machine (or board) driver is the code that glues together all the +component drivers (e.g. codecs, platforms and DAIs). It also describes the +relationships between each componnent which include audio paths, GPIOs, +interrupts, clocking, jacks and voltage regulators. The machine driver can contain codec and platform specific code. It registers the audio subsystem with the kernel as a platform device and is represented by -- cgit v0.10.2 From 469b7bc4e6dbfdb173f0901f746e9277f6740ba7 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 20 Sep 2013 18:19:09 +0100 Subject: ASoC: Docs: Add documentation for Dynamic PCM Add documentation describing DPCM with examples of a DSP based smart phone. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/Documentation/sound/alsa/soc/DPCM.txt b/Documentation/sound/alsa/soc/DPCM.txt new file mode 100644 index 0000000..aa8546f --- /dev/null +++ b/Documentation/sound/alsa/soc/DPCM.txt @@ -0,0 +1,380 @@ +Dynamic PCM +=========== + +1. Description +============== + +Dynamic PCM allows an ALSA PCM device to digitally route its PCM audio to +various digital endpoints during the PCM stream runtime. e.g. PCM0 can route +digital audio to I2S DAI0, I2S DAI1 or PDM DAI2. This is useful for on SoC DSP +drivers that expose several ALSA PCMs and can route to multiple DAIs. + +The DPCM runtime routing is determined by the ALSA mixer settings in the same +way as the analog signal is routed in an ASoC codec driver. DPCM uses a DAPM +graph representing the DSP internal audio paths and uses the mixer settings to +determine the patch used by each ALSA PCM. + +DPCM re-uses all the existing component codec, platform and DAI drivers without +any modifications. + + +Phone Audio System with SoC based DSP +------------------------------------- + +Consider the following phone audio subsystem. This will be used in this +document for all examples :- + +| Front End PCMs | SoC DSP | Back End DAIs | Audio devices | + + ************* +PCM0 <------------> * * <----DAI0-----> Codec Headset + * * +PCM1 <------------> * * <----DAI1-----> Codec Speakers + * DSP * +PCM2 <------------> * * <----DAI2-----> MODEM + * * +PCM3 <------------> * * <----DAI3-----> BT + * * + * * <----DAI4-----> DMIC + * * + * * <----DAI5-----> FM + ************* + +This diagram shows a simple smart phone audio subsystem. It supports Bluetooth, +FM digital radio, Speakers, Headset Jack, digital microphones and cellular +modem. This sound card exposes 4 DSP front end (FE) ALSA PCM devices and +supports 6 back end (BE) DAIs. Each FE PCM can digitally route audio data to any +of the BE DAIs. The FE PCM devices can also route audio to more than 1 BE DAI. + + + +Example - DPCM Switching playback from DAI0 to DAI1 +--------------------------------------------------- + +Audio is being played to the Headset. After a while the user removes the headset +and audio continues playing on the speakers. + +Playback on PCM0 to Headset would look like :- + + ************* +PCM0 <============> * * <====DAI0=====> Codec Headset + * * +PCM1 <------------> * * <----DAI1-----> Codec Speakers + * DSP * +PCM2 <------------> * * <----DAI2-----> MODEM + * * +PCM3 <------------> * * <----DAI3-----> BT + * * + * * <----DAI4-----> DMIC + * * + * * <----DAI5-----> FM + ************* + +The headset is removed from the jack by user so the speakers must now be used :- + + ************* +PCM0 <============> * * <----DAI0-----> Codec Headset + * * +PCM1 <------------> * * <====DAI1=====> Codec Speakers + * DSP * +PCM2 <------------> * * <----DAI2-----> MODEM + * * +PCM3 <------------> * * <----DAI3-----> BT + * * + * * <----DAI4-----> DMIC + * * + * * <----DAI5-----> FM + ************* + +The audio driver processes this as follows :- + + 1) Machine driver receives Jack removal event. + + 2) Machine driver OR audio HAL disables the Headset path. + + 3) DPCM runs the PCM trigger(stop), hw_free(), shutdown() operations on DAI0 + for headset since the path is now disabled. + + 4) Machine driver or audio HAL enables the speaker path. + + 5) DPCM runs the PCM ops for startup(), hw_params(), prepapre() and + trigger(start) for DAI1 Speakers since the path is enabled. + +In this example, the machine driver or userspace audio HAL can alter the routing +and then DPCM will take care of managing the DAI PCM operations to either bring +the link up or down. Audio playback does not stop during this transition. + + + +DPCM machine driver +=================== + +The DPCM enabled ASoC machine driver is similar to normal machine drivers +except that we also have to :- + + 1) Define the FE and BE DAI links. + + 2) Define any FE/BE PCM operations. + + 3) Define widget graph connections. + + +1 FE and BE DAI links +--------------------- + +| Front End PCMs | SoC DSP | Back End DAIs | Audio devices | + + ************* +PCM0 <------------> * * <----DAI0-----> Codec Headset + * * +PCM1 <------------> * * <----DAI1-----> Codec Speakers + * DSP * +PCM2 <------------> * * <----DAI2-----> MODEM + * * +PCM3 <------------> * * <----DAI3-----> BT + * * + * * <----DAI4-----> DMIC + * * + * * <----DAI5-----> FM + ************* + +For the example above we have to define 4 FE DAI links and 6 BE DAI links. The +FE DAI links are defined as follows :- + +static struct snd_soc_dai_link machine_dais[] = { + { + .name = "PCM0 System", + .stream_name = "System Playback", + .cpu_dai_name = "System Pin", + .platform_name = "dsp-audio", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .dynamic = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + }, + .....< other FE and BE DAI links here > +}; + +This FE DAI link is pretty similar to a regular DAI link except that we also +set the DAI link to a DPCM FE with the "dynamic = 1". The supported FE stream +directions should also be set with the "dpcm_playback" and "dpcm_capture" +flags. There is also an option to specify the ordering of the trigger call for +each FE. This allows the ASoC core to trigger the DSP before or after the other +components (as some DSPs have strong requirements for the ordering DAI/DSP +start and stop sequences). + +The FE DAI above sets the codec and code DAIs to dummy devices since the BE is +dynamic and will change depending on runtime config. + +The BE DAIs are configured as follows :- + +static struct snd_soc_dai_link machine_dais[] = { + .....< FE DAI links here > + { + .name = "Codec Headset", + .cpu_dai_name = "ssp-dai.0", + .platform_name = "snd-soc-dummy", + .no_pcm = 1, + .codec_name = "rt5640.0-001c", + .codec_dai_name = "rt5640-aif1", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = hswult_ssp0_fixup, + .ops = &haswell_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + .....< other BE DAI links here > +}; + +This BE DAI link connects DAI0 to the codec (in this case RT5460 AIF1). It sets +the "no_pcm" flag to mark it has a BE and sets flags for supported stream +directions using "dpcm_playback" and "dpcm_capture" above. + +The BE has also flags set for ignoreing suspend and PM down time. This allows +the BE to work in a hostless mode where the host CPU is not transferring data +like a BT phone call :- + + ************* +PCM0 <------------> * * <----DAI0-----> Codec Headset + * * +PCM1 <------------> * * <----DAI1-----> Codec Speakers + * DSP * +PCM2 <------------> * * <====DAI2=====> MODEM + * * +PCM3 <------------> * * <====DAI3=====> BT + * * + * * <----DAI4-----> DMIC + * * + * * <----DAI5-----> FM + ************* + +This allows the host CPU to sleep whilst the DSP, MODEM DAI and the BT DAI are +still in operation. + +A BE DAI link can also set the codec to a dummy device if the code is a device +that is managed externally. + +Likewise a BE DAI can also set a dummy cpu DAI if the CPU DAI is managed by the +DSP firmware. + + +2 FE/BE PCM operations +---------------------- + +The BE above also exports some PCM operations and a "fixup" callback. The fixup +callback is used by the machine driver to (re)configure the DAI based upon the +FE hw params. i.e. the DSP may perform SRC or ASRC from the FE to BE. + +e.g. DSP converts all FE hw params to run at fixed rate of 48k, 16bit, stereo for +DAI0. This means all FE hw_params have to be fixed in the machine driver for +DAI0 so that the DAI is running at desired configuration regardless of the FE +configuration. + +static int dai0_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + /* The DSP will covert the FE rate to 48k, stereo */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + /* set DAI0 to 16 bit */ + snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - + SNDRV_PCM_HW_PARAM_FIRST_MASK], + SNDRV_PCM_FORMAT_S16_LE); + return 0; +} + +The other PCM operation are the same as for regular DAI links. Use as necessary. + + +3 Widget graph connections +-------------------------- + +The BE DAI links will normally be connected to the graph at initialisation time +by the ASoC DAPM core. However, if the BE codec or BE DAI is a dummy then this +has to be set explicitly in the driver :- + +/* BE for codec Headset - DAI0 is dummy and managed by DSP FW */ +{"DAI0 CODEC IN", NULL, "AIF1 Capture"}, +{"AIF1 Playback", NULL, "DAI0 CODEC OUT"}, + + +Writing a DPCM DSP driver +========================= + +The DPCM DSP driver looks much like a standard platform class ASoC driver +combined with elements from a codec class driver. A DSP platform driver must +implement :- + + 1) Front End PCM DAIs - i.e. struct snd_soc_dai_driver. + + 2) DAPM graph showing DSP audio routing from FE DAIs to BEs. + + 3) DAPM widgets from DSP graph. + + 4) Mixers for gains, routing, etc. + + 5) DMA configuration. + + 6) BE AIF widgets. + +Items 6 is important for routing the audio outside of the DSP. AIF need to be +defined for each BE and each stream direction. e.g for BE DAI0 above we would +have :- + +SND_SOC_DAPM_AIF_IN("DAI0 RX", NULL, 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_OUT("DAI0 TX", NULL, 0, SND_SOC_NOPM, 0, 0), + +The BE AIF are used to connect the DSP graph to the graphs for the other +component drivers (e.g. codec graph). + + +Hostless PCM streams +==================== + +A hostless PCM stream is a stream that is not routed through the host CPU. An +example of this would be a phone call from handset to modem. + + + ************* +PCM0 <------------> * * <----DAI0-----> Codec Headset + * * +PCM1 <------------> * * <====DAI1=====> Codec Speakers/Mic + * DSP * +PCM2 <------------> * * <====DAI2=====> MODEM + * * +PCM3 <------------> * * <----DAI3-----> BT + * * + * * <----DAI4-----> DMIC + * * + * * <----DAI5-----> FM + ************* + +In this case the PCM data is routed via the DSP. The host CPU in this use case +is only used for control and can sleep during the runtime of the stream. + +The host can control the hostless link either by :- + + 1) Configuring the link as a CODEC <-> CODEC style link. In this case the link + is enabled or disabled by the state of the DAPM graph. This usually means + there is a mixer control that can be used to connect or disconnect the path + between both DAIs. + + 2) Hostless FE. This FE has a virtual connection to the BE DAI links on the DAPM + graph. Control is then carried out by the FE as regualar PCM operations. + This method gives more control over the DAI links, but requires much more + userspace code to control the link. Its recommended to use CODEC<->CODEC + unless your HW needs more fine grained sequencing of the PCM ops. + + +CODEC <-> CODEC link +-------------------- + +This DAI link is enabled when DAPM detects a valid path within the DAPM graph. +The machine driver sets some additional parameters to the DAI link i.e. + +static const struct snd_soc_pcm_stream dai_params = { + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rate_min = 8000, + .rate_max = 8000, + .channels_min = 2, + .channels_max = 2, +}; + +static struct snd_soc_dai_link dais[] = { + < ... more DAI links above ... > + { + .name = "MODEM", + .stream_name = "MODEM", + .cpu_dai_name = "dai2", + .codec_dai_name = "modem-aif1", + .codec_name = "modem", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM, + .params = &dai_params, + } + < ... more DAI links here ... > + +These parameters are used to configure the DAI hw_params() when DAPM detects a +valid path and then calls the PCM operations to start the link. DAPM will also +call the appropriate PCM operations to disable the DAI when the path is no +longer valid. + + +Hostless FE +----------- + +The DAI link(s) are enabled by a FE that does not read or write any PCM data. +This means creating a new FE that is connected with a virtual path to both +DAI links. The DAI links will be started when the FE PCM is started and stopped +when the FE PCM is stopped. Note that the FE PCM cannot read or write data in +this configuration. + + -- cgit v0.10.2 From a3b924df8fa9083d2291efc2d1dca93609953718 Mon Sep 17 00:00:00 2001 From: Mateusz Krawczuk Date: Mon, 23 Sep 2013 11:45:45 +0200 Subject: spi: s3c64xx: Add missing compatibles Add compatibles for s3c6410, s5pc100 and s5pc110/s5pv210 boards. Signed-off-by: Mateusz Krawczuk Signed-off-by: Kyungmin Park Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 8bed27a..84cc6ac 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1612,6 +1612,18 @@ static struct platform_device_id s3c64xx_spi_driver_ids[] = { }; static const struct of_device_id s3c64xx_spi_dt_match[] = { + { .compatible = "samsung,s3c2443-spi", + .data = (void *)&s3c2443_spi_port_config, + }, + { .compatible = "samsung,s3c6410-spi", + .data = (void *)&s3c6410_spi_port_config, + }, + { .compatible = "samsung,s5pc100-spi", + .data = (void *)&s5pc100_spi_port_config, + }, + { .compatible = "samsung,s5pv210-spi", + .data = (void *)&s5pv210_spi_port_config, + }, { .compatible = "samsung,exynos4210-spi", .data = (void *)&exynos4_spi_port_config, }, -- cgit v0.10.2 From 57841439b62e3ddb5ee50e765aa50330dde612d0 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 20 Sep 2013 18:39:18 +0800 Subject: spi: efm32: Don't call kfree() after spi_master_put() Calling kfree() to clean up the memory obtained from spi_alloc_master() is wrong as this is done in spi_master_release() when spi_master->dev's refcount reaches zero. Signed-off-by: Axel Lin Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-efm32.c b/drivers/spi/spi-efm32.c index 7d84418..5b3117f 100644 --- a/drivers/spi/spi-efm32.c +++ b/drivers/spi/spi-efm32.c @@ -467,7 +467,6 @@ err_disable_clk: clk_disable_unprepare(ddata->clk); err: spi_master_put(master); - kfree(master); } return ret; @@ -484,7 +483,6 @@ static int efm32_spi_remove(struct platform_device *pdev) free_irq(ddata->rxirq, ddata); clk_disable_unprepare(ddata->clk); spi_master_put(master); - kfree(master); return 0; } -- cgit v0.10.2 From 01896f7e0a122e8f20082e24f6f9a340034b9c01 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sun, 18 Aug 2013 12:14:32 -0700 Subject: rcu: Convert local functions to static The rcu_cpu_stall_timeout kernel parameter, the rcu_dynticks per-CPU variable, and the rcu_gp_fqs() function are used only locally. This commit therefore marks them as static. Reported-by: kbuild test robot Signed-off-by: Paul E. McKenney diff --git a/kernel/rcupdate.c b/kernel/rcupdate.c index b02a339..3260a10 100644 --- a/kernel/rcupdate.c +++ b/kernel/rcupdate.c @@ -298,7 +298,7 @@ EXPORT_SYMBOL_GPL(do_trace_rcu_torture_read); #endif int rcu_cpu_stall_suppress __read_mostly; /* 1 = suppress stall warnings. */ -int rcu_cpu_stall_timeout __read_mostly = CONFIG_RCU_CPU_STALL_TIMEOUT; +static int rcu_cpu_stall_timeout __read_mostly = CONFIG_RCU_CPU_STALL_TIMEOUT; module_param(rcu_cpu_stall_suppress, int, 0644); module_param(rcu_cpu_stall_timeout, int, 0644); diff --git a/kernel/rcutree.c b/kernel/rcutree.c index e0fa192..2712b89 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -222,7 +222,7 @@ void rcu_note_context_switch(int cpu) } EXPORT_SYMBOL_GPL(rcu_note_context_switch); -DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks) = { +static DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks) = { .dynticks_nesting = DYNTICK_TASK_EXIT_IDLE, .dynticks = ATOMIC_INIT(1), #ifdef CONFIG_NO_HZ_FULL_SYSIDLE @@ -1366,7 +1366,7 @@ static int rcu_gp_init(struct rcu_state *rsp) /* * Do one round of quiescent-state forcing. */ -int rcu_gp_fqs(struct rcu_state *rsp, int fqs_state_in) +static int rcu_gp_fqs(struct rcu_state *rsp, int fqs_state_in) { int fqs_state = fqs_state_in; bool isidle = false; -- cgit v0.10.2 From 829511d8aa7a2179bba57ab4ab277d6f9c77ae5b Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sun, 18 Aug 2013 12:21:57 -0700 Subject: rcu: Fix dubious "if" condition in __call_rcu_nocb_enqueue() This commit replaces an incorrect (but fortunately functional) bitwise OR ("|") operator with the correct logical OR ("||"). Reported-by: kbuild test robot Signed-off-by: Paul E. McKenney diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h index 130c97b..6f9aece 100644 --- a/kernel/rcutree_plugin.h +++ b/kernel/rcutree_plugin.h @@ -2108,7 +2108,7 @@ static void __call_rcu_nocb_enqueue(struct rcu_data *rdp, /* If we are not being polled and there is a kthread, awaken it ... */ t = ACCESS_ONCE(rdp->nocb_kthread); - if (rcu_nocb_poll | !t) + if (rcu_nocb_poll || !t) return; len = atomic_long_read(&rdp->nocb_q_count); if (old_rhpp == &rdp->nocb_head) { -- cgit v0.10.2 From 2a855b644c310d5db5a80b8816c0c7748c167977 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 23 Aug 2013 09:40:42 -0700 Subject: rcu: Make list_splice_init_rcu() account for RCU readers The list_splice_init_rcu() function allows a list visible to RCU readers to be spliced into another list visible to RCU readers. This is OK, except for the use of INIT_LIST_HEAD(), which does pointer updates without doing anything to make those updates safe for concurrent readers. Of course, most of the time INIT_LIST_HEAD() is being used in reader-free contexts, such as initialization or cleanup, so it is OK for it to update pointers in an unsafe-for-RCU-readers manner. This commit therefore creates an INIT_LIST_HEAD_RCU() that uses ACCESS_ONCE() to make the updates reader-safe. The reason that we can use ACCESS_ONCE() instead of the more typical rcu_assign_pointer() is that list_splice_init_rcu() is updating the pointers to reference something that is already visible to readers, so that there is no problem with pre-initialized values. Signed-off-by: Paul E. McKenney diff --git a/include/linux/rculist.h b/include/linux/rculist.h index 4106721..45a0a9e 100644 --- a/include/linux/rculist.h +++ b/include/linux/rculist.h @@ -19,6 +19,21 @@ */ /* + * INIT_LIST_HEAD_RCU - Initialize a list_head visible to RCU readers + * @list: list to be initialized + * + * You should instead use INIT_LIST_HEAD() for normal initialization and + * cleanup tasks, when readers have no access to the list being initialized. + * However, if the list being initialized is visible to readers, you + * need to keep the compiler from being too mischievous. + */ +static inline void INIT_LIST_HEAD_RCU(struct list_head *list) +{ + ACCESS_ONCE(list->next) = list; + ACCESS_ONCE(list->prev) = list; +} + +/* * return the ->next pointer of a list_head in an rcu safe * way, we must not access it directly */ @@ -191,9 +206,13 @@ static inline void list_splice_init_rcu(struct list_head *list, if (list_empty(list)) return; - /* "first" and "last" tracking list, so initialize it. */ + /* + * "first" and "last" tracking list, so initialize it. RCU readers + * have access to this list, so we must use INIT_LIST_HEAD_RCU() + * instead of INIT_LIST_HEAD(). + */ - INIT_LIST_HEAD(list); + INIT_LIST_HEAD_RCU(list); /* * At this point, the list body still points to the source list. -- cgit v0.10.2 From c9d4b0af9e0609cc525c55de18229fde7c926d61 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Sat, 31 Aug 2013 13:34:10 -0700 Subject: rcu: Replace __get_cpu_var() uses __get_cpu_var() is used for multiple purposes in the kernel source. One of them is address calculation via the form &__get_cpu_var(x). This calculates the address for the instance of the percpu variable of the current processor based on an offset. Other use cases are for storing and retrieving data from the current processors percpu area. __get_cpu_var() can be used as an lvalue when writing data or on the right side of an assignment. __get_cpu_var() is defined as : __get_cpu_var() always only does an address determination. However, store and retrieve operations could use a segment prefix (or global register on other platforms) to avoid the address calculation. this_cpu_write() and this_cpu_read() can directly take an offset into a percpu area and use optimized assembly code to read and write per cpu variables. This patch converts __get_cpu_var into either an explicit address calculation using this_cpu_ptr() or into a use of this_cpu operations that use the offset. Thereby address calcualtions are avoided and less registers are used when code is generated. At the end of the patchset all uses of __get_cpu_var have been removed so the macro is removed too. The patchset includes passes over all arches as well. Once these operations are used throughout then specialized macros can be defined in non -x86 arches as well in order to optimize per cpu access by f.e. using a global register that may be set to the per cpu base. Transformations done to __get_cpu_var() 1. Determine the address of the percpu instance of the current processor. DEFINE_PER_CPU(int, y); int *x = &__get_cpu_var(y); Converts to int *x = this_cpu_ptr(&y); 2. Same as #1 but this time an array structure is involved. DEFINE_PER_CPU(int, y[20]); int *x = __get_cpu_var(y); Converts to int *x = this_cpu_ptr(y); 3. Retrieve the content of the current processors instance of a per cpu variable. DEFINE_PER_CPU(int, u); int x = __get_cpu_var(y) Converts to int x = __this_cpu_read(y); 4. Retrieve the content of a percpu struct DEFINE_PER_CPU(struct mystruct, y); struct mystruct x = __get_cpu_var(y); Converts to memcpy(this_cpu_ptr(&x), y, sizeof(x)); 5. Assignment to a per cpu variable DEFINE_PER_CPU(int, y) __get_cpu_var(y) = x; Converts to this_cpu_write(y, x); 6. Increment/Decrement etc of a per cpu variable DEFINE_PER_CPU(int, y); __get_cpu_var(y)++ Converts to this_cpu_inc(y) Signed-off-by: Christoph Lameter [ paulmck: Address conflicts. ] Signed-off-by: Paul E. McKenney diff --git a/kernel/rcutree.c b/kernel/rcutree.c index 2712b89..8eb9cfd 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -407,7 +407,7 @@ static void rcu_eqs_enter(bool user) long long oldval; struct rcu_dynticks *rdtp; - rdtp = &__get_cpu_var(rcu_dynticks); + rdtp = this_cpu_ptr(&rcu_dynticks); oldval = rdtp->dynticks_nesting; WARN_ON_ONCE((oldval & DYNTICK_TASK_NEST_MASK) == 0); if ((oldval & DYNTICK_TASK_NEST_MASK) == DYNTICK_TASK_NEST_VALUE) @@ -435,7 +435,7 @@ void rcu_idle_enter(void) local_irq_save(flags); rcu_eqs_enter(false); - rcu_sysidle_enter(&__get_cpu_var(rcu_dynticks), 0); + rcu_sysidle_enter(this_cpu_ptr(&rcu_dynticks), 0); local_irq_restore(flags); } EXPORT_SYMBOL_GPL(rcu_idle_enter); @@ -478,7 +478,7 @@ void rcu_irq_exit(void) struct rcu_dynticks *rdtp; local_irq_save(flags); - rdtp = &__get_cpu_var(rcu_dynticks); + rdtp = this_cpu_ptr(&rcu_dynticks); oldval = rdtp->dynticks_nesting; rdtp->dynticks_nesting--; WARN_ON_ONCE(rdtp->dynticks_nesting < 0); @@ -528,7 +528,7 @@ static void rcu_eqs_exit(bool user) struct rcu_dynticks *rdtp; long long oldval; - rdtp = &__get_cpu_var(rcu_dynticks); + rdtp = this_cpu_ptr(&rcu_dynticks); oldval = rdtp->dynticks_nesting; WARN_ON_ONCE(oldval < 0); if (oldval & DYNTICK_TASK_NEST_MASK) @@ -555,7 +555,7 @@ void rcu_idle_exit(void) local_irq_save(flags); rcu_eqs_exit(false); - rcu_sysidle_exit(&__get_cpu_var(rcu_dynticks), 0); + rcu_sysidle_exit(this_cpu_ptr(&rcu_dynticks), 0); local_irq_restore(flags); } EXPORT_SYMBOL_GPL(rcu_idle_exit); @@ -599,7 +599,7 @@ void rcu_irq_enter(void) long long oldval; local_irq_save(flags); - rdtp = &__get_cpu_var(rcu_dynticks); + rdtp = this_cpu_ptr(&rcu_dynticks); oldval = rdtp->dynticks_nesting; rdtp->dynticks_nesting++; WARN_ON_ONCE(rdtp->dynticks_nesting == 0); @@ -620,7 +620,7 @@ void rcu_irq_enter(void) */ void rcu_nmi_enter(void) { - struct rcu_dynticks *rdtp = &__get_cpu_var(rcu_dynticks); + struct rcu_dynticks *rdtp = this_cpu_ptr(&rcu_dynticks); if (rdtp->dynticks_nmi_nesting == 0 && (atomic_read(&rdtp->dynticks) & 0x1)) @@ -642,7 +642,7 @@ void rcu_nmi_enter(void) */ void rcu_nmi_exit(void) { - struct rcu_dynticks *rdtp = &__get_cpu_var(rcu_dynticks); + struct rcu_dynticks *rdtp = this_cpu_ptr(&rcu_dynticks); if (rdtp->dynticks_nmi_nesting == 0 || --rdtp->dynticks_nmi_nesting != 0) @@ -665,7 +665,7 @@ int rcu_is_cpu_idle(void) int ret; preempt_disable(); - ret = (atomic_read(&__get_cpu_var(rcu_dynticks).dynticks) & 0x1) == 0; + ret = (atomic_read(this_cpu_ptr(&rcu_dynticks.dynticks)) & 0x1) == 0; preempt_enable(); return ret; } @@ -703,7 +703,7 @@ bool rcu_lockdep_current_cpu_online(void) if (in_nmi()) return 1; preempt_disable(); - rdp = &__get_cpu_var(rcu_sched_data); + rdp = this_cpu_ptr(&rcu_sched_data); rnp = rdp->mynode; ret = (rdp->grpmask & rnp->qsmaskinit) || !rcu_scheduler_fully_active; @@ -723,7 +723,7 @@ EXPORT_SYMBOL_GPL(rcu_lockdep_current_cpu_online); */ static int rcu_is_cpu_rrupt_from_idle(void) { - return __get_cpu_var(rcu_dynticks).dynticks_nesting <= 1; + return __this_cpu_read(rcu_dynticks.dynticks_nesting) <= 1; } /* diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h index 6f9aece..c684f7a 100644 --- a/kernel/rcutree_plugin.h +++ b/kernel/rcutree_plugin.h @@ -660,7 +660,7 @@ static void rcu_preempt_check_callbacks(int cpu) static void rcu_preempt_do_callbacks(void) { - rcu_do_batch(&rcu_preempt_state, &__get_cpu_var(rcu_preempt_data)); + rcu_do_batch(&rcu_preempt_state, this_cpu_ptr(&rcu_preempt_data)); } #endif /* #ifdef CONFIG_RCU_BOOST */ @@ -1332,7 +1332,7 @@ static void invoke_rcu_callbacks_kthread(void) */ static bool rcu_is_callbacks_kthread(void) { - return __get_cpu_var(rcu_cpu_kthread_task) == current; + return __this_cpu_read(rcu_cpu_kthread_task) == current; } #define RCU_BOOST_DELAY_JIFFIES DIV_ROUND_UP(CONFIG_RCU_BOOST_DELAY * HZ, 1000) @@ -1382,8 +1382,8 @@ static int rcu_spawn_one_boost_kthread(struct rcu_state *rsp, static void rcu_kthread_do_work(void) { - rcu_do_batch(&rcu_sched_state, &__get_cpu_var(rcu_sched_data)); - rcu_do_batch(&rcu_bh_state, &__get_cpu_var(rcu_bh_data)); + rcu_do_batch(&rcu_sched_state, this_cpu_ptr(&rcu_sched_data)); + rcu_do_batch(&rcu_bh_state, this_cpu_ptr(&rcu_bh_data)); rcu_preempt_do_callbacks(); } @@ -1402,7 +1402,7 @@ static void rcu_cpu_kthread_park(unsigned int cpu) static int rcu_cpu_kthread_should_run(unsigned int cpu) { - return __get_cpu_var(rcu_cpu_has_work); + return __this_cpu_read(rcu_cpu_has_work); } /* @@ -1412,8 +1412,8 @@ static int rcu_cpu_kthread_should_run(unsigned int cpu) */ static void rcu_cpu_kthread(unsigned int cpu) { - unsigned int *statusp = &__get_cpu_var(rcu_cpu_kthread_status); - char work, *workp = &__get_cpu_var(rcu_cpu_has_work); + unsigned int *statusp = this_cpu_ptr(&rcu_cpu_kthread_status); + char work, *workp = this_cpu_ptr(&rcu_cpu_has_work); int spincnt; for (spincnt = 0; spincnt < 10; spincnt++) { -- cgit v0.10.2 From 289828e62de0334a0d01c0f65df91cd47d3a9e05 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sat, 31 Aug 2013 19:23:29 -0700 Subject: rcu: Silence unused-variable warnings The "idle" variable in both rcu_eqs_enter_common() and rcu_eqs_exit_common() is only used in a WARN_ON_ONCE(). If the kernel is built disabling WARN_ON_ONCE(), the compiler will complain (rightly) that "idle" is unused. This commit therefore adds a __maybe_unused to the declaration of both variables. Signed-off-by: Paul E. McKenney diff --git a/kernel/rcutree.c b/kernel/rcutree.c index 8eb9cfd..e6f2e8f 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -371,7 +371,8 @@ static void rcu_eqs_enter_common(struct rcu_dynticks *rdtp, long long oldval, { trace_rcu_dyntick(TPS("Start"), oldval, rdtp->dynticks_nesting); if (!user && !is_idle_task(current)) { - struct task_struct *idle = idle_task(smp_processor_id()); + struct task_struct *idle __maybe_unused = + idle_task(smp_processor_id()); trace_rcu_dyntick(TPS("Error on entry: not idle task"), oldval, 0); ftrace_dump(DUMP_ORIG); @@ -508,7 +509,8 @@ static void rcu_eqs_exit_common(struct rcu_dynticks *rdtp, long long oldval, rcu_cleanup_after_idle(smp_processor_id()); trace_rcu_dyntick(TPS("End"), oldval, rdtp->dynticks_nesting); if (!user && !is_idle_task(current)) { - struct task_struct *idle = idle_task(smp_processor_id()); + struct task_struct *idle __maybe_unused = + idle_task(smp_processor_id()); trace_rcu_dyntick(TPS("Error on exit: not idle task"), oldval, rdtp->dynticks_nesting); -- cgit v0.10.2 From 69c8d28c96445e28f081fcd987e34ea2afa65039 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 3 Sep 2013 09:52:20 -0700 Subject: rcu: Micro-optimize rcu_cpu_has_callbacks() The for_each_rcu_flavor() loop unconditionally scans all flavors, even when the first flavor might have some non-lazy callbacks. Once the loop has seen a non-lazy callback, further passes through the loop cannot change the state. This is not a huge problem, given that there can be at most three RCU flavors (RCU-bh, RCU-preempt, and RCU-sched), but this code is on the path to idle, so speeding it up even a small amount would have some benefit. This commit therefore does two things: 1. Rearranges the order of the list of RCU flavors in order to place the most active flavor first in the list. The most active RCU flavor is RCU-preempt, or, if there is no RCU-preempt, RCU-sched. 2. Reworks the for_each_rcu_flavor() to exit early when the first non-lazy callback is seen, or, in the case where the caller does not care about non-lazy callbacks (RCU_FAST_NO_HZ=n), when the first callback is seen. Reported-by: Chen Gang Signed-off-by: Paul E. McKenney diff --git a/kernel/rcutree.c b/kernel/rcutree.c index e6f2e8f..49464ad 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -2727,10 +2727,13 @@ static int rcu_cpu_has_callbacks(int cpu, bool *all_lazy) for_each_rcu_flavor(rsp) { rdp = per_cpu_ptr(rsp->rda, cpu); - if (rdp->qlen != rdp->qlen_lazy) + if (!rdp->nxtlist) + continue; + hc = true; + if (rdp->qlen != rdp->qlen_lazy || !all_lazy) { al = false; - if (rdp->nxtlist) - hc = true; + break; + } } if (all_lazy) *all_lazy = al; @@ -3297,8 +3300,8 @@ void __init rcu_init(void) rcu_bootup_announce(); rcu_init_geometry(); - rcu_init_one(&rcu_sched_state, &rcu_sched_data); rcu_init_one(&rcu_bh_state, &rcu_bh_data); + rcu_init_one(&rcu_sched_state, &rcu_sched_data); __rcu_init_preempt(); open_softirq(RCU_SOFTIRQ, rcu_process_callbacks); -- cgit v0.10.2 From 26cdfedf6a902345f8604ea8e0b7dd2566b37a46 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 4 Sep 2013 10:51:13 -0700 Subject: rcu: Reject memory-order-induced stall-warning false positives If a system is idle from an RCU perspective for longer than specified by CONFIG_RCU_CPU_STALL_TIMEOUT, and if one CPU starts a grace period just as a second checks for CPU stalls, and if this second CPU happens to see the old value of rsp->jiffies_stall, it will incorrectly report a CPU stall. This is quite rare, but apparently occurs deterministically on systems with about 6TB of memory. This commit therefore orders accesses to the data used to determine whether or not a CPU stall is in progress. Grace-period initialization and cleanup first increments rsp->completed to mark the end of the previous grace period, then records the current jiffies in rsp->gp_start, then records the jiffies at which a stall can be expected to occur in rsp->jiffies_stall, and finally increments rsp->gpnum to mark the start of the new grace period. Now, this ordering by itself does not prevent false positives. For example, if grace-period initialization was delayed between recording rsp->gp_start and rsp->jiffies_stall, the CPU stall warning code might still see an old value of rsp->jiffies_stall. Therefore, this commit also orders the CPU stall warning accesses as well, loading rsp->gpnum and jiffies, then rsp->jiffies_stall, then rsp->gp_start, and finally rsp->completed. This ordering means that the false-positive scenario in the previous paragraph would result in rsp->completed being greater than or equal to rsp->gpnum, which is never valid for a CPU stall, allowing the false positive to be rejected. Furthermore, any fetch that gets an old value of rsp->jiffies_stall must also get an old value of rsp->gpnum, which will again be rejected by the comparison of rsp->gpnum and rsp->completed. Situations where rsp->gp_start is later than rsp->jiffies_stall are also rejected, as are situations where jiffies is less than rsp->jiffies_stall. Although use of unsynchronized accesses means that there are likely still some false-positive scenarios (synchronization has proven to be a very bad idea on large systems), this should get rid of a large class of these scenarios. Reported-by: Fabian Herschel Reported-by: Michal Hocko Signed-off-by: Paul E. McKenney Reviewed-by: Michal Hocko Tested-by: Jochen Striepe diff --git a/kernel/rcutree.c b/kernel/rcutree.c index 49464ad..b618d72 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -804,8 +804,11 @@ static int rcu_implicit_dynticks_qs(struct rcu_data *rdp, static void record_gp_stall_check_time(struct rcu_state *rsp) { - rsp->gp_start = jiffies; - rsp->jiffies_stall = jiffies + rcu_jiffies_till_stall_check(); + unsigned long j = ACCESS_ONCE(jiffies); + + rsp->gp_start = j; + smp_wmb(); /* Record start time before stall time. */ + rsp->jiffies_stall = j + rcu_jiffies_till_stall_check(); } /* @@ -934,17 +937,48 @@ static void print_cpu_stall(struct rcu_state *rsp) static void check_cpu_stall(struct rcu_state *rsp, struct rcu_data *rdp) { + unsigned long completed; + unsigned long gpnum; + unsigned long gps; unsigned long j; unsigned long js; struct rcu_node *rnp; - if (rcu_cpu_stall_suppress) + if (rcu_cpu_stall_suppress || !rcu_gp_in_progress(rsp)) return; j = ACCESS_ONCE(jiffies); + + /* + * Lots of memory barriers to reject false positives. + * + * The idea is to pick up rsp->gpnum, then rsp->jiffies_stall, + * then rsp->gp_start, and finally rsp->completed. These values + * are updated in the opposite order with memory barriers (or + * equivalent) during grace-period initialization and cleanup. + * Now, a false positive can occur if we get an new value of + * rsp->gp_start and a old value of rsp->jiffies_stall. But given + * the memory barriers, the only way that this can happen is if one + * grace period ends and another starts between these two fetches. + * Detect this by comparing rsp->completed with the previous fetch + * from rsp->gpnum. + * + * Given this check, comparisons of jiffies, rsp->jiffies_stall, + * and rsp->gp_start suffice to forestall false positives. + */ + gpnum = ACCESS_ONCE(rsp->gpnum); + smp_rmb(); /* Pick up ->gpnum first... */ js = ACCESS_ONCE(rsp->jiffies_stall); + smp_rmb(); /* ...then ->jiffies_stall before the rest... */ + gps = ACCESS_ONCE(rsp->gp_start); + smp_rmb(); /* ...and finally ->gp_start before ->completed. */ + completed = ACCESS_ONCE(rsp->completed); + if (ULONG_CMP_GE(completed, gpnum) || + ULONG_CMP_LT(j, js) || + ULONG_CMP_GE(gps, js)) + return; /* No stall or GP completed since entering function. */ rnp = rdp->mynode; if (rcu_gp_in_progress(rsp) && - (ACCESS_ONCE(rnp->qsmask) & rdp->grpmask) && ULONG_CMP_GE(j, js)) { + (ACCESS_ONCE(rnp->qsmask) & rdp->grpmask)) { /* We haven't checked in, so go dump stack. */ print_cpu_stall(rsp); @@ -1317,9 +1351,10 @@ static int rcu_gp_init(struct rcu_state *rsp) } /* Advance to a new grace period and initialize state. */ + record_gp_stall_check_time(rsp); + smp_wmb(); /* Record GP times before starting GP. */ rsp->gpnum++; trace_rcu_grace_period(rsp->name, rsp->gpnum, TPS("start")); - record_gp_stall_check_time(rsp); raw_spin_unlock_irq(&rnp->lock); /* Exclude any concurrent CPU-hotplug operations. */ -- cgit v0.10.2 From 0d75292467b0c8554d70c751a35af6514202ac28 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sat, 17 Aug 2013 18:08:37 -0700 Subject: rcu: Have rcutiny tracepoints use tracepoint_string() This commit extends the work done in f7f7bac9 (rcu: Have the RCU tracepoints use the tracepoint_string infrastructure) to cover rcutiny. Signed-off-by: Paul E. McKenney Cc: Steven Rostedt diff --git a/kernel/rcu.h b/kernel/rcu.h index 7713196..7859a0a 100644 --- a/kernel/rcu.h +++ b/kernel/rcu.h @@ -122,4 +122,11 @@ int rcu_jiffies_till_stall_check(void); #endif /* #ifdef CONFIG_RCU_STALL_COMMON */ +/* + * Strings used in tracepoints need to be exported via the + * tracing system such that tools like perf and trace-cmd can + * translate the string address pointers to actual text. + */ +#define TPS(x) tracepoint_string(x) + #endif /* __LINUX_RCU_H */ diff --git a/kernel/rcutiny.c b/kernel/rcutiny.c index 9ed6075..e99eb5f 100644 --- a/kernel/rcutiny.c +++ b/kernel/rcutiny.c @@ -35,6 +35,7 @@ #include #include #include +#include #ifdef CONFIG_RCU_TRACE #include @@ -58,16 +59,17 @@ static long long rcu_dynticks_nesting = DYNTICK_TASK_EXIT_IDLE; static void rcu_idle_enter_common(long long newval) { if (newval) { - RCU_TRACE(trace_rcu_dyntick("--=", + RCU_TRACE(trace_rcu_dyntick(TPS("--="), rcu_dynticks_nesting, newval)); rcu_dynticks_nesting = newval; return; } - RCU_TRACE(trace_rcu_dyntick("Start", rcu_dynticks_nesting, newval)); + RCU_TRACE(trace_rcu_dyntick(TPS("Start"), + rcu_dynticks_nesting, newval)); if (!is_idle_task(current)) { struct task_struct *idle = idle_task(smp_processor_id()); - RCU_TRACE(trace_rcu_dyntick("Error on entry: not idle task", + RCU_TRACE(trace_rcu_dyntick(TPS("Entry error: not idle task"), rcu_dynticks_nesting, newval)); ftrace_dump(DUMP_ALL); WARN_ONCE(1, "Current pid: %d comm: %s / Idle pid: %d comm: %s", @@ -120,15 +122,15 @@ EXPORT_SYMBOL_GPL(rcu_irq_exit); static void rcu_idle_exit_common(long long oldval) { if (oldval) { - RCU_TRACE(trace_rcu_dyntick("++=", + RCU_TRACE(trace_rcu_dyntick(TPS("++="), oldval, rcu_dynticks_nesting)); return; } - RCU_TRACE(trace_rcu_dyntick("End", oldval, rcu_dynticks_nesting)); + RCU_TRACE(trace_rcu_dyntick(TPS("End"), oldval, rcu_dynticks_nesting)); if (!is_idle_task(current)) { struct task_struct *idle = idle_task(smp_processor_id()); - RCU_TRACE(trace_rcu_dyntick("Error on exit: not idle task", + RCU_TRACE(trace_rcu_dyntick(TPS("Exit error: not idle task"), oldval, rcu_dynticks_nesting)); ftrace_dump(DUMP_ALL); WARN_ONCE(1, "Current pid: %d comm: %s / Idle pid: %d comm: %s", @@ -304,7 +306,8 @@ static void __rcu_process_callbacks(struct rcu_ctrlblk *rcp) RCU_TRACE(cb_count++); } RCU_TRACE(rcu_trace_sub_qlen(rcp, cb_count)); - RCU_TRACE(trace_rcu_batch_end(rcp->name, cb_count, 0, need_resched(), + RCU_TRACE(trace_rcu_batch_end(rcp->name, + cb_count, 0, need_resched(), is_idle_task(current), false)); } diff --git a/kernel/rcutree.c b/kernel/rcutree.c index b618d72..62aab5c 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -61,13 +61,6 @@ #include "rcu.h" -/* - * Strings used in tracepoints need to be exported via the - * tracing system such that tools like perf and trace-cmd can - * translate the string address pointers to actual text. - */ -#define TPS(x) tracepoint_string(x) - /* Data structures. */ static struct lock_class_key rcu_node_class[RCU_NUM_LVLS]; -- cgit v0.10.2 From f7be82093952ee4a74ffc8c729b2811f908cd9a4 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 8 Aug 2013 18:27:52 -0700 Subject: rcu: Improve grace-period start logic This commit improves grace-period start logic by checking ->gp_flags under the lock and by issuing a warning if a grace period is already in progress. Signed-off-by: Paul E. McKenney diff --git a/kernel/rcutree.c b/kernel/rcutree.c index 32618b3..d679a52 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -1297,7 +1297,7 @@ static void note_gp_changes(struct rcu_state *rsp, struct rcu_data *rdp) } /* - * Initialize a new grace period. + * Initialize a new grace period. Return 0 if no grace period required. */ static int rcu_gp_init(struct rcu_state *rsp) { @@ -1306,10 +1306,18 @@ static int rcu_gp_init(struct rcu_state *rsp) rcu_bind_gp_kthread(); raw_spin_lock_irq(&rnp->lock); + if (rsp->gp_flags == 0) { + /* Spurious wakeup, tell caller to go back to sleep. */ + raw_spin_unlock_irq(&rnp->lock); + return 0; + } rsp->gp_flags = 0; /* Clear all flags: New grace period. */ - if (rcu_gp_in_progress(rsp)) { - /* Grace period already in progress, don't start another. */ + if (WARN_ON_ONCE(rcu_gp_in_progress(rsp))) { + /* + * Grace period already in progress, don't start another. + * Not supposed to be able to happen. + */ raw_spin_unlock_irq(&rnp->lock); return 0; } @@ -1474,8 +1482,7 @@ static int __noreturn rcu_gp_kthread(void *arg) wait_event_interruptible(rsp->gp_wq, rsp->gp_flags & RCU_GP_FLAG_INIT); - if ((rsp->gp_flags & RCU_GP_FLAG_INIT) && - rcu_gp_init(rsp)) + if (rcu_gp_init(rsp)) break; cond_resched(); flush_signals(current); -- cgit v0.10.2 From 88d6df612cc3c99f56cc18461fcc531c3a145544 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 8 Aug 2013 21:44:31 -0700 Subject: rcu: Prevent spurious-wakeup DoS attack on rcu_gp_kthread() Spurious wakeups in the force-quiescent-state loop in rcu_gp_kthread() cause the timeout to be recalculated, which would prevent rcu_gp_fqs() from ever being called. This would in turn would prevent the grace period from ever ending for as long as there was at least one CPU in an extended quiescent state that had not yet passed through a quiescent state. This commit therefore avoids recalculating the timeout unless the previous pass's call to wait_event_interruptible_timeout() actually did time out, thus preventing the above scenario. Signed-off-by: Paul E. McKenney diff --git a/kernel/rcutree.c b/kernel/rcutree.c index d679a52..62b67b7 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -1470,6 +1470,7 @@ static void rcu_gp_cleanup(struct rcu_state *rsp) static int __noreturn rcu_gp_kthread(void *arg) { int fqs_state; + int gf; unsigned long j; int ret; struct rcu_state *rsp = arg; @@ -1495,10 +1496,13 @@ static int __noreturn rcu_gp_kthread(void *arg) j = HZ; jiffies_till_first_fqs = HZ; } + ret = 0; for (;;) { - rsp->jiffies_force_qs = jiffies + j; + if (!ret) + rsp->jiffies_force_qs = jiffies + j; ret = wait_event_interruptible_timeout(rsp->gp_wq, - (rsp->gp_flags & RCU_GP_FLAG_FQS) || + ((gf = ACCESS_ONCE(rsp->gp_flags)) & + RCU_GP_FLAG_FQS) || (!ACCESS_ONCE(rnp->qsmask) && !rcu_preempt_blocked_readers_cgp(rnp)), j); @@ -1507,7 +1511,8 @@ static int __noreturn rcu_gp_kthread(void *arg) !rcu_preempt_blocked_readers_cgp(rnp)) break; /* If time for quiescent-state forcing, do it. */ - if (ret == 0 || (rsp->gp_flags & RCU_GP_FLAG_FQS)) { + if (ULONG_CMP_GE(jiffies, rsp->jiffies_force_qs) || + (gf & RCU_GP_FLAG_FQS)) { fqs_state = rcu_gp_fqs(rsp, fqs_state); cond_resched(); } else { -- cgit v0.10.2 From 591c6d1710cd73824057d08eda302cf2a7cfd18a Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 8 Aug 2013 22:26:23 -0700 Subject: rcu: Flag lockless access to ->gp_flags with ACCESS_ONCE() This commit applies ACCESS_ONCE() to an outside-of-lock access to ->gp_flags. Although it is hard to imagine any sane compiler messing this particular case up, the documentation benefits are substantial. Plus the definition of "sane compiler" grows ever looser. Signed-off-by: Paul E. McKenney diff --git a/kernel/rcutree.c b/kernel/rcutree.c index 62b67b7..6d028fd 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -1481,7 +1481,7 @@ static int __noreturn rcu_gp_kthread(void *arg) /* Handle grace-period start. */ for (;;) { wait_event_interruptible(rsp->gp_wq, - rsp->gp_flags & + ACCESS_ONCE(rsp->gp_flags) & RCU_GP_FLAG_INIT); if (rcu_gp_init(rsp)) break; -- cgit v0.10.2 From 63c4db78e80407976e47bccaa2a4d8251b5a10bc Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 9 Aug 2013 12:19:29 -0700 Subject: rcu: Add tracing to rcu_gp_kthread() This commit adds tracing to the rcu_gp_kthread() function in order to help trace down hangs potentially involving this kthread. Reported-by: Clark Williams Reported-by: Carsten Emde Signed-off-by: Paul E. McKenney diff --git a/include/trace/events/rcu.h b/include/trace/events/rcu.h index ee2376c..60077e1 100644 --- a/include/trace/events/rcu.h +++ b/include/trace/events/rcu.h @@ -39,15 +39,25 @@ TRACE_EVENT(rcu_utilization, #if defined(CONFIG_TREE_RCU) || defined(CONFIG_TREE_PREEMPT_RCU) /* - * Tracepoint for grace-period events: starting and ending a grace - * period ("start" and "end", respectively), a CPU noting the start - * of a new grace period or the end of an old grace period ("cpustart" - * and "cpuend", respectively), a CPU passing through a quiescent - * state ("cpuqs"), a CPU coming online or going offline ("cpuonl" - * and "cpuofl", respectively), a CPU being kicked for being too - * long in dyntick-idle mode ("kick"), a CPU accelerating its new - * callbacks to RCU_NEXT_READY_TAIL ("AccReadyCB"), and a CPU - * accelerating its new callbacks to RCU_WAIT_TAIL ("AccWaitCB"). + * Tracepoint for grace-period events. Takes a string identifying the + * RCU flavor, the grace-period number, and a string identifying the + * grace-period-related event as follows: + * + * "AccReadyCB": CPU acclerates new callbacks to RCU_NEXT_READY_TAIL. + * "AccWaitCB": CPU accelerates new callbacks to RCU_WAIT_TAIL. + * "start": Start a grace period. + * "cpustart": CPU first notices a grace-period start. + * "cpuqs": CPU passes through a quiescent state. + * "cpuonl": CPU comes online. + * "cpuofl": CPU goes offline. + * "reqwait": GP kthread sleeps waiting for grace-period request. + * "reqwaitsig": GP kthread awakened by signal from reqwait state. + * "fqswait": GP kthread waiting until time to force quiescent states. + * "fqsstart": GP kthread starts forcing quiescent states. + * "fqsend": GP kthread done forcing quiescent states. + * "fqswaitsig": GP kthread awakened by signal from fqswait state. + * "end": End a grace period. + * "cpuend": CPU first notices a grace-period end. */ TRACE_EVENT(rcu_grace_period, diff --git a/kernel/rcutree.c b/kernel/rcutree.c index 6d028fd..78d3715 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -1480,6 +1480,9 @@ static int __noreturn rcu_gp_kthread(void *arg) /* Handle grace-period start. */ for (;;) { + trace_rcu_grace_period(rsp->name, + ACCESS_ONCE(rsp->gpnum), + TPS("reqwait")); wait_event_interruptible(rsp->gp_wq, ACCESS_ONCE(rsp->gp_flags) & RCU_GP_FLAG_INIT); @@ -1487,6 +1490,9 @@ static int __noreturn rcu_gp_kthread(void *arg) break; cond_resched(); flush_signals(current); + trace_rcu_grace_period(rsp->name, + ACCESS_ONCE(rsp->gpnum), + TPS("reqwaitsig")); } /* Handle quiescent-state forcing. */ @@ -1500,6 +1506,9 @@ static int __noreturn rcu_gp_kthread(void *arg) for (;;) { if (!ret) rsp->jiffies_force_qs = jiffies + j; + trace_rcu_grace_period(rsp->name, + ACCESS_ONCE(rsp->gpnum), + TPS("fqswait")); ret = wait_event_interruptible_timeout(rsp->gp_wq, ((gf = ACCESS_ONCE(rsp->gp_flags)) & RCU_GP_FLAG_FQS) || @@ -1513,12 +1522,21 @@ static int __noreturn rcu_gp_kthread(void *arg) /* If time for quiescent-state forcing, do it. */ if (ULONG_CMP_GE(jiffies, rsp->jiffies_force_qs) || (gf & RCU_GP_FLAG_FQS)) { + trace_rcu_grace_period(rsp->name, + ACCESS_ONCE(rsp->gpnum), + TPS("fqsstart")); fqs_state = rcu_gp_fqs(rsp, fqs_state); + trace_rcu_grace_period(rsp->name, + ACCESS_ONCE(rsp->gpnum), + TPS("fqsend")); cond_resched(); } else { /* Deal with stray signal. */ cond_resched(); flush_signals(current); + trace_rcu_grace_period(rsp->name, + ACCESS_ONCE(rsp->gpnum), + TPS("fqswaitsig")); } j = jiffies_till_next_fqs; if (j > HZ) { -- cgit v0.10.2 From bb311eccbdab974639263060b8452bf304af0b0c Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 9 Aug 2013 16:02:09 -0700 Subject: rcu: Add tracing of normal (non-NOCB) grace-period requests This commit adds tracing to the normal grace-period request points. These are rcu_gp_cleanup(), which checks for the need for another grace period at the end of the previous grace period, and rcu_start_gp_advanced(), which restarts RCU's state machine after an idle period. These trace events are intended to help track down bugs where RCU remains idle despite there being work for it to do. Reported-by: Clark Williams Signed-off-by: Paul E. McKenney diff --git a/include/trace/events/rcu.h b/include/trace/events/rcu.h index 60077e1..98466c6 100644 --- a/include/trace/events/rcu.h +++ b/include/trace/events/rcu.h @@ -45,6 +45,7 @@ TRACE_EVENT(rcu_utilization, * * "AccReadyCB": CPU acclerates new callbacks to RCU_NEXT_READY_TAIL. * "AccWaitCB": CPU accelerates new callbacks to RCU_WAIT_TAIL. + * "newreq": Request a new grace period. * "start": Start a grace period. * "cpustart": CPU first notices a grace-period start. * "cpuqs": CPU passes through a quiescent state. diff --git a/kernel/rcutree.c b/kernel/rcutree.c index 78d3715..54dd6d0 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -1459,8 +1459,12 @@ static void rcu_gp_cleanup(struct rcu_state *rsp) rsp->fqs_state = RCU_GP_IDLE; rdp = this_cpu_ptr(rsp->rda); rcu_advance_cbs(rsp, rnp, rdp); /* Reduce false positives below. */ - if (cpu_needs_another_gp(rsp, rdp)) + if (cpu_needs_another_gp(rsp, rdp)) { rsp->gp_flags = 1; + trace_rcu_grace_period(rsp->name, + ACCESS_ONCE(rsp->gpnum), + TPS("newreq")); + } raw_spin_unlock_irq(&rnp->lock); } @@ -1584,6 +1588,8 @@ rcu_start_gp_advanced(struct rcu_state *rsp, struct rcu_node *rnp, return; } rsp->gp_flags = RCU_GP_FLAG_INIT; + trace_rcu_grace_period(rsp->name, ACCESS_ONCE(rsp->gpnum), + TPS("newreq")); /* * We can't do wakeups while holding the rnp->lock, as that -- cgit v0.10.2 From 9261dd0da6c6432f08670719069449c6efe4f7a9 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 14 Aug 2013 16:24:26 -0700 Subject: rcu: Add tracing for rcuo no-CBs CPU wakeup handshake Lost wakeups from call_rcu() to the rcuo kthreads can result in hangs that are difficult to diagnose. This commit therefore adds tracing to help pin down the cause of these hangs. Reported-by: Clark Williams Reported-by: Carsten Emde Signed-off-by: Paul E. McKenney [ paulmck: Add const per kbuild test robot's advice. ] diff --git a/include/trace/events/rcu.h b/include/trace/events/rcu.h index 98466c6..4301cd9 100644 --- a/include/trace/events/rcu.h +++ b/include/trace/events/rcu.h @@ -172,6 +172,42 @@ TRACE_EVENT(rcu_grace_period_init, ); /* + * Tracepoint for RCU no-CBs CPU callback handoffs. This event is intended + * to assist debugging of these handoffs. + * + * The first argument is the name of the RCU flavor, and the second is + * the number of the offloaded CPU are extracted. The third and final + * argument is a string as follows: + * + * "WakeEmpty": Wake rcuo kthread, first CB to empty list. + * "WakeOvf": Wake rcuo kthread, CB list is huge. + * "WakeNot": Don't wake rcuo kthread. + * "WakeNotPoll": Don't wake rcuo kthread because it is polling. + * "WokeEmpty": rcuo kthread woke to find empty list. + * "WokeNonEmpty": rcuo kthread woke to find non-empty list. + */ +TRACE_EVENT(rcu_nocb_wake, + + TP_PROTO(const char *rcuname, int cpu, const char *reason), + + TP_ARGS(rcuname, cpu, reason), + + TP_STRUCT__entry( + __field(const char *, rcuname) + __field(int, cpu) + __field(const char *, reason) + ), + + TP_fast_assign( + __entry->rcuname = rcuname; + __entry->cpu = cpu; + __entry->reason = reason; + ), + + TP_printk("%s %d %s", __entry->rcuname, __entry->cpu, __entry->reason) +); + +/* * Tracepoint for tasks blocking within preemptible-RCU read-side * critical sections. Track the type of RCU (which one day might * include SRCU), the grace-period number that the task is blocking @@ -667,6 +703,7 @@ TRACE_EVENT(rcu_barrier, #define trace_rcu_future_grace_period(rcuname, gpnum, completed, c, \ level, grplo, grphi, event) \ do { } while (0) +#define trace_rcu_nocb_wake(rcuname, cpu, reason) do { } while (0) #define trace_rcu_preempt_task(rcuname, pid, gpnum) do { } while (0) #define trace_rcu_unlock_preempted_task(rcuname, gpnum, pid) do { } while (0) #define trace_rcu_quiescent_state_report(rcuname, gpnum, mask, qsmask, level, \ diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h index 130c97b..f4ed24b 100644 --- a/kernel/rcutree_plugin.h +++ b/kernel/rcutree_plugin.h @@ -2108,15 +2108,22 @@ static void __call_rcu_nocb_enqueue(struct rcu_data *rdp, /* If we are not being polled and there is a kthread, awaken it ... */ t = ACCESS_ONCE(rdp->nocb_kthread); - if (rcu_nocb_poll | !t) + if (rcu_nocb_poll | !t) { + trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, + TPS("WakeNotPoll")); return; + } len = atomic_long_read(&rdp->nocb_q_count); if (old_rhpp == &rdp->nocb_head) { wake_up(&rdp->nocb_wq); /* ... only if queue was empty ... */ rdp->qlen_last_fqs_check = 0; + trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, TPS("WakeEmpty")); } else if (len > rdp->qlen_last_fqs_check + qhimark) { wake_up_process(t); /* ... or if many callbacks queued. */ rdp->qlen_last_fqs_check = LONG_MAX / 2; + trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, TPS("WakeOvf")); + } else { + trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, TPS("WakeNot")); } return; } @@ -2233,10 +2240,15 @@ static int rcu_nocb_kthread(void *arg) wait_event_interruptible(rdp->nocb_wq, rdp->nocb_head); list = ACCESS_ONCE(rdp->nocb_head); if (!list) { + if (!rcu_nocb_poll) + trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, + TPS("WokeEmpty")); schedule_timeout_interruptible(1); flush_signals(current); continue; } + trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, + TPS("WokeNonEmpty")); /* * Extract queued callbacks, update counts, and wait -- cgit v0.10.2 From 756cbf6befe6f59b0b3e0967d92a66c11e2566ed Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 15 Aug 2013 10:12:12 -0700 Subject: rcu: Distinguish between NOCB and non-NOCB rcu_callback trace events One way to distinguish between NOCB and non-NOCB rcu_callback trace events is that the former always print zero for the lazy and non-lazy queue lengths. Unfortunately, this also means that we cannot see the NOCB queue lengths. This commit therefore accesses the NOCB queue lengths, but negates them. NOCB rcu_callback trace events should therefore have negative queue lengths. Signed-off-by: Paul E. McKenney [ paulmck: Match operand size per kbuild test robot's advice. ] diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h index f4ed24b..24b01b6 100644 --- a/kernel/rcutree_plugin.h +++ b/kernel/rcutree_plugin.h @@ -2147,10 +2147,12 @@ static bool __call_rcu_nocb(struct rcu_data *rdp, struct rcu_head *rhp, if (__is_kfree_rcu_offset((unsigned long)rhp->func)) trace_rcu_kfree_callback(rdp->rsp->name, rhp, (unsigned long)rhp->func, - rdp->qlen_lazy, rdp->qlen); + -atomic_long_read(&rdp->nocb_q_count_lazy), + -atomic_long_read(&rdp->nocb_q_count)); else trace_rcu_callback(rdp->rsp->name, rhp, - rdp->qlen_lazy, rdp->qlen); + -atomic_long_read(&rdp->nocb_q_count_lazy), + -atomic_long_read(&rdp->nocb_q_count)); return 1; } -- cgit v0.10.2 From 69a79bb12a81024d718e73c52e886907a3777b34 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 15 Aug 2013 13:23:23 -0700 Subject: rcu: Track rcu_nocb_kthread()'s sleeping and awakening This commit adds event traces to track all of rcu_nocb_kthread()'s blocking and awakening. Signed-off-by: Paul E. McKenney diff --git a/include/trace/events/rcu.h b/include/trace/events/rcu.h index 4301cd9..a087d82 100644 --- a/include/trace/events/rcu.h +++ b/include/trace/events/rcu.h @@ -183,8 +183,12 @@ TRACE_EVENT(rcu_grace_period_init, * "WakeOvf": Wake rcuo kthread, CB list is huge. * "WakeNot": Don't wake rcuo kthread. * "WakeNotPoll": Don't wake rcuo kthread because it is polling. + * "Poll": Start of new polling cycle for rcu_nocb_poll. + * "Sleep": Sleep waiting for CBs for !rcu_nocb_poll. * "WokeEmpty": rcuo kthread woke to find empty list. * "WokeNonEmpty": rcuo kthread woke to find non-empty list. + * "WaitQueue": Enqueue partially done, timed wait for it to complete. + * "WokeQueue": Partial enqueue now complete. */ TRACE_EVENT(rcu_nocb_wake, diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h index 24b01b6..21205b1 100644 --- a/kernel/rcutree_plugin.h +++ b/kernel/rcutree_plugin.h @@ -2230,6 +2230,7 @@ static void rcu_nocb_wait_gp(struct rcu_data *rdp) static int rcu_nocb_kthread(void *arg) { int c, cl; + bool firsttime = 1; struct rcu_head *list; struct rcu_head *next; struct rcu_head **tail; @@ -2238,8 +2239,15 @@ static int rcu_nocb_kthread(void *arg) /* Each pass through this loop invokes one batch of callbacks */ for (;;) { /* If not polling, wait for next batch of callbacks. */ - if (!rcu_nocb_poll) + if (!rcu_nocb_poll) { + trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, + TPS("Sleep")); wait_event_interruptible(rdp->nocb_wq, rdp->nocb_head); + } else if (firsttime) { + firsttime = 0; + trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, + TPS("Poll")); + } list = ACCESS_ONCE(rdp->nocb_head); if (!list) { if (!rcu_nocb_poll) @@ -2249,6 +2257,7 @@ static int rcu_nocb_kthread(void *arg) flush_signals(current); continue; } + firsttime = 1; trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, TPS("WokeNonEmpty")); @@ -2271,7 +2280,11 @@ static int rcu_nocb_kthread(void *arg) next = list->next; /* Wait for enqueuing to complete, if needed. */ while (next == NULL && &list->next != tail) { + trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, + TPS("WaitQueue")); schedule_timeout_interruptible(1); + trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, + TPS("WokeQueue")); next = list->next; } debug_rcu_head_unqueue(list); -- cgit v0.10.2 From 15f5191b6acbbb38029b06284e8fd20275e7cfe8 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sun, 18 Aug 2013 11:59:25 -0700 Subject: rcu: Avoid sparse warnings in rcu_nocb_wake trace event The event-tracing macros do not like bool tracing arguments, so this commit makes them be of type char. This change has the knock-on effect of making it illegal to pass a pointer into one of these arguments, so also change rcutiny's first call to trace_rcu_batch_end() to convert from pointer to boolean, prefixing with "!!". Reported-by: kbuild test robot Signed-off-by: Paul E. McKenney diff --git a/include/trace/events/rcu.h b/include/trace/events/rcu.h index a087d82..aca3822 100644 --- a/include/trace/events/rcu.h +++ b/include/trace/events/rcu.h @@ -591,17 +591,17 @@ TRACE_EVENT(rcu_invoke_kfree_callback, TRACE_EVENT(rcu_batch_end, TP_PROTO(const char *rcuname, int callbacks_invoked, - bool cb, bool nr, bool iit, bool risk), + char cb, char nr, char iit, char risk), TP_ARGS(rcuname, callbacks_invoked, cb, nr, iit, risk), TP_STRUCT__entry( __field(const char *, rcuname) __field(int, callbacks_invoked) - __field(bool, cb) - __field(bool, nr) - __field(bool, iit) - __field(bool, risk) + __field(char, cb) + __field(char, nr) + __field(char, iit) + __field(char, risk) ), TP_fast_assign( diff --git a/kernel/rcutiny.c b/kernel/rcutiny.c index 9ed6075..80b6e27 100644 --- a/kernel/rcutiny.c +++ b/kernel/rcutiny.c @@ -273,7 +273,7 @@ static void __rcu_process_callbacks(struct rcu_ctrlblk *rcp) if (&rcp->rcucblist == rcp->donetail) { RCU_TRACE(trace_rcu_batch_start(rcp->name, 0, 0, -1)); RCU_TRACE(trace_rcu_batch_end(rcp->name, 0, - ACCESS_ONCE(rcp->rcucblist), + !!ACCESS_ONCE(rcp->rcucblist), need_resched(), is_idle_task(current), false)); -- cgit v0.10.2 From 63f57cd45b0811de9663edf4af6b170c5bd3860d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 21 Sep 2013 20:39:49 +0200 Subject: gpio: pcf857x: Add OF support Add DT bindings for the pcf857x-compatible chips and parse the device tree node in the driver. Signed-off-by: Laurent Pinchart Signed-off-by: Linus Walleij diff --git a/Documentation/devicetree/bindings/gpio/gpio-pcf857x.txt b/Documentation/devicetree/bindings/gpio/gpio-pcf857x.txt new file mode 100644 index 0000000..d63194a --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-pcf857x.txt @@ -0,0 +1,71 @@ +* PCF857x-compatible I/O expanders + +The PCF857x-compatible chips have "quasi-bidirectional" I/O lines that can be +driven high by a pull-up current source or driven low to ground. This combines +the direction and output level into a single bit per line, which can't be read +back. We can't actually know at initialization time whether a line is configured +(a) as output and driving the signal low/high, or (b) as input and reporting a +low/high value, without knowing the last value written since the chip came out +of reset (if any). The only reliable solution for setting up line direction is +thus to do it explicitly. + +Required Properties: + + - compatible: should be one of the following. + - "maxim,max7328": For the Maxim MAX7378 + - "maxim,max7329": For the Maxim MAX7329 + - "nxp,pca8574": For the NXP PCA8574 + - "nxp,pca8575": For the NXP PCA8575 + - "nxp,pca9670": For the NXP PCA9670 + - "nxp,pca9671": For the NXP PCA9671 + - "nxp,pca9672": For the NXP PCA9672 + - "nxp,pca9673": For the NXP PCA9673 + - "nxp,pca9674": For the NXP PCA9674 + - "nxp,pca9675": For the NXP PCA9675 + - "nxp,pcf8574": For the NXP PCF8574 + - "nxp,pcf8574a": For the NXP PCF8574A + - "nxp,pcf8575": For the NXP PCF8575 + - "ti,tca9554": For the TI TCA9554 + + - reg: I2C slave address. + + - gpio-controller: Marks the device node as a gpio controller. + - #gpio-cells: Should be 2. The first cell is the GPIO number and the second + cell specifies GPIO flags, as defined in . Only the + GPIO_ACTIVE_HIGH and GPIO_ACTIVE_LOW flags are supported. + +Optional Properties: + + - lines-initial-states: Bitmask that specifies the initial state of each + line. When a bit is set to zero, the corresponding line will be initialized to + the input (pulled-up) state. When the bit is set to one, the line will be + initialized the the low-level output state. If the property is not specified + all lines will be initialized to the input state. + + The I/O expander can detect input state changes, and thus optionally act as + an interrupt controller. When the expander interrupt line is connected all the + following properties must be set. For more information please see the + interrupt controller device tree bindings documentation available at + Documentation/devicetree/bindings/interrupt-controller/interrupts.txt. + + - interrupt-controller: Identifies the node as an interrupt controller. + - #interrupt-cells: Number of cells to encode an interrupt source, shall be 2. + - interrupt-parent: phandle of the parent interrupt controller. + - interrupts: Interrupt specifier for the controllers interrupt. + + +Please refer to gpio.txt in this directory for details of the common GPIO +bindings used by client devices. + +Example: PCF8575 I/O expander node + + pcf8575: gpio@20 { + compatible = "nxp,pcf8575"; + reg = <0x20>; + interrupt-parent = <&irqpin2>; + interrupts = <3 0>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 54725a6..1535686 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include #include @@ -49,6 +51,27 @@ static const struct i2c_device_id pcf857x_id[] = { }; MODULE_DEVICE_TABLE(i2c, pcf857x_id); +#ifdef CONFIG_OF +static const struct of_device_id pcf857x_of_table[] = { + { .compatible = "nxp,pcf8574" }, + { .compatible = "nxp,pcf8574a" }, + { .compatible = "nxp,pca8574" }, + { .compatible = "nxp,pca9670" }, + { .compatible = "nxp,pca9672" }, + { .compatible = "nxp,pca9674" }, + { .compatible = "nxp,pcf8575" }, + { .compatible = "nxp,pca8575" }, + { .compatible = "nxp,pca9671" }, + { .compatible = "nxp,pca9673" }, + { .compatible = "nxp,pca9675" }, + { .compatible = "maxim,max7328" }, + { .compatible = "maxim,max7329" }, + { .compatible = "ti,tca9554" }, + { } +}; +MODULE_DEVICE_TABLE(of, pcf857x_of_table); +#endif + /* * The pcf857x, pca857x, and pca967x chips only expose one read and one * write register. Writing a "one" bit (to match the reset state) lets @@ -260,14 +283,18 @@ fail: static int pcf857x_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct pcf857x_platform_data *pdata; + struct pcf857x_platform_data *pdata = dev_get_platdata(&client->dev); + struct device_node *np = client->dev.of_node; struct pcf857x *gpio; + unsigned int n_latch = 0; int status; - pdata = dev_get_platdata(&client->dev); - if (!pdata) { + if (IS_ENABLED(CONFIG_OF) && np) + of_property_read_u32(np, "lines-initial-states", &n_latch); + else if (pdata) + n_latch = pdata->n_latch; + else dev_dbg(&client->dev, "no platform data\n"); - } /* Allocate, initialize, and register this gpio_chip. */ gpio = devm_kzalloc(&client->dev, sizeof(*gpio), GFP_KERNEL); @@ -360,11 +387,11 @@ static int pcf857x_probe(struct i2c_client *client, * may cause transient glitching since it can't know the last value * written (some pins may need to be driven low). * - * Using pdata->n_latch avoids that trouble. When left initialized - * to zero, our software copy of the "latch" then matches the chip's - * all-ones reset state. Otherwise it flags pins to be driven low. + * Using n_latch avoids that trouble. When left initialized to zero, + * our software copy of the "latch" then matches the chip's all-ones + * reset state. Otherwise it flags pins to be driven low. */ - gpio->out = pdata ? ~pdata->n_latch : ~0; + gpio->out = ~n_latch; gpio->status = gpio->out; status = gpiochip_add(&gpio->chip); @@ -426,6 +453,7 @@ static struct i2c_driver pcf857x_driver = { .driver = { .name = "pcf857x", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(pcf857x_of_table), }, .probe = pcf857x_probe, .remove = pcf857x_remove, -- cgit v0.10.2 From 5d5a08003d3e678372e375d99c65a24e0d33d2f5 Mon Sep 17 00:00:00 2001 From: Kirill Tkhai Date: Sun, 15 Sep 2013 17:29:17 +0400 Subject: rcu: Fix CONFIG_RCU_NOCB_CPU_ALL panic on machines with sparse CPU mask Some architectures have sparse cpu mask. UltraSparc's cpuinfo for example: CPU0: online CPU2: online So, set only possible CPUs when CONFIG_RCU_NOCB_CPU_ALL is enabled. Also, check that user passes right 'rcu_nocbs=' option. Signed-off-by: Kirill Tkhai CC: Dipankar Sarma [ paulmck: Fix pr_info() issue noted by scripts/checkpatch.pl. ] Signed-off-by: Paul E. McKenney diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h index c684f7a..1855d66 100644 --- a/kernel/rcutree_plugin.h +++ b/kernel/rcutree_plugin.h @@ -96,10 +96,15 @@ static void __init rcu_bootup_announce_oddness(void) #endif /* #ifdef CONFIG_RCU_NOCB_CPU_ZERO */ #ifdef CONFIG_RCU_NOCB_CPU_ALL pr_info("\tOffload RCU callbacks from all CPUs\n"); - cpumask_setall(rcu_nocb_mask); + cpumask_copy(rcu_nocb_mask, cpu_possible_mask); #endif /* #ifdef CONFIG_RCU_NOCB_CPU_ALL */ #endif /* #ifndef CONFIG_RCU_NOCB_CPU_NONE */ if (have_rcu_nocb_mask) { + if (!cpumask_subset(rcu_nocb_mask, cpu_possible_mask)) { + pr_info("\tNote: kernel parameter 'rcu_nocbs=' contains nonexistent CPUs.\n"); + cpumask_and(rcu_nocb_mask, cpu_possible_mask, + rcu_nocb_mask); + } cpulist_scnprintf(nocb_buf, sizeof(nocb_buf), rcu_nocb_mask); pr_info("\tOffload RCU callbacks from CPUs: %s.\n", nocb_buf); if (rcu_nocb_poll) -- cgit v0.10.2 From 1e019421bca68cfae1a61a09d9d49cf6a9e2143b Mon Sep 17 00:00:00 2001 From: Mike Travis Date: Mon, 23 Sep 2013 16:25:00 -0500 Subject: x86/UV: Move NMI support This patch moves the UV NMI support from the x2apic file to a new separate uv_nmi.c file in preparation for the next sequence of patches. It prevents upcoming bloat of the x2apic file, and has the added benefit of putting the upcoming /sys/module parameters under the name 'uv_nmi' instead of 'x2apic_uv_x', which was obscure. Signed-off-by: Mike Travis Reviewed-by: Dimitri Sivanich Reviewed-by: Hedi Berriche Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo Cc: Jason Wessel Link: http://lkml.kernel.org/r/20130923212500.183295611@asylum.americas.sgi.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/uv/uv.h b/arch/x86/include/asm/uv/uv.h index 062921e..6b964a0 100644 --- a/arch/x86/include/asm/uv/uv.h +++ b/arch/x86/include/asm/uv/uv.h @@ -12,6 +12,7 @@ extern enum uv_system_type get_uv_system_type(void); extern int is_uv_system(void); extern void uv_cpu_init(void); extern void uv_nmi_init(void); +extern void uv_register_nmi_notifier(void); extern void uv_system_init(void); extern const struct cpumask *uv_flush_tlb_others(const struct cpumask *cpumask, struct mm_struct *mm, @@ -25,6 +26,7 @@ static inline enum uv_system_type get_uv_system_type(void) { return UV_NONE; } static inline int is_uv_system(void) { return 0; } static inline void uv_cpu_init(void) { } static inline void uv_system_init(void) { } +static inline void uv_register_nmi_notifier(void) { } static inline const struct cpumask * uv_flush_tlb_others(const struct cpumask *cpumask, struct mm_struct *mm, unsigned long start, unsigned long end, unsigned int cpu) diff --git a/arch/x86/kernel/apic/x2apic_uv_x.c b/arch/x86/kernel/apic/x2apic_uv_x.c index 1191ac1..9e47c06 100644 --- a/arch/x86/kernel/apic/x2apic_uv_x.c +++ b/arch/x86/kernel/apic/x2apic_uv_x.c @@ -39,12 +39,6 @@ #include #include -/* BMC sets a bit this MMR non-zero before sending an NMI */ -#define UVH_NMI_MMR UVH_SCRATCH5 -#define UVH_NMI_MMR_CLEAR (UVH_NMI_MMR + 8) -#define UV_NMI_PENDING_MASK (1UL << 63) -DEFINE_PER_CPU(unsigned long, cpu_last_nmi_count); - DEFINE_PER_CPU(int, x2apic_extra_bits); #define PR_DEVEL(fmt, args...) pr_devel("%s: " fmt, __func__, args) @@ -58,7 +52,6 @@ int uv_min_hub_revision_id; EXPORT_SYMBOL_GPL(uv_min_hub_revision_id); unsigned int uv_apicid_hibits; EXPORT_SYMBOL_GPL(uv_apicid_hibits); -static DEFINE_SPINLOCK(uv_nmi_lock); static struct apic apic_x2apic_uv_x; @@ -847,68 +840,6 @@ void uv_cpu_init(void) set_x2apic_extra_bits(uv_hub_info->pnode); } -/* - * When NMI is received, print a stack trace. - */ -int uv_handle_nmi(unsigned int reason, struct pt_regs *regs) -{ - unsigned long real_uv_nmi; - int bid; - - /* - * Each blade has an MMR that indicates when an NMI has been sent - * to cpus on the blade. If an NMI is detected, atomically - * clear the MMR and update a per-blade NMI count used to - * cause each cpu on the blade to notice a new NMI. - */ - bid = uv_numa_blade_id(); - real_uv_nmi = (uv_read_local_mmr(UVH_NMI_MMR) & UV_NMI_PENDING_MASK); - - if (unlikely(real_uv_nmi)) { - spin_lock(&uv_blade_info[bid].nmi_lock); - real_uv_nmi = (uv_read_local_mmr(UVH_NMI_MMR) & UV_NMI_PENDING_MASK); - if (real_uv_nmi) { - uv_blade_info[bid].nmi_count++; - uv_write_local_mmr(UVH_NMI_MMR_CLEAR, UV_NMI_PENDING_MASK); - } - spin_unlock(&uv_blade_info[bid].nmi_lock); - } - - if (likely(__get_cpu_var(cpu_last_nmi_count) == uv_blade_info[bid].nmi_count)) - return NMI_DONE; - - __get_cpu_var(cpu_last_nmi_count) = uv_blade_info[bid].nmi_count; - - /* - * Use a lock so only one cpu prints at a time. - * This prevents intermixed output. - */ - spin_lock(&uv_nmi_lock); - pr_info("UV NMI stack dump cpu %u:\n", smp_processor_id()); - dump_stack(); - spin_unlock(&uv_nmi_lock); - - return NMI_HANDLED; -} - -void uv_register_nmi_notifier(void) -{ - if (register_nmi_handler(NMI_UNKNOWN, uv_handle_nmi, 0, "uv")) - printk(KERN_WARNING "UV NMI handler failed to register\n"); -} - -void uv_nmi_init(void) -{ - unsigned int value; - - /* - * Unmask NMI on all cpus - */ - value = apic_read(APIC_LVT1) | APIC_DM_NMI; - value &= ~APIC_LVT_MASKED; - apic_write(APIC_LVT1, value); -} - void __init uv_system_init(void) { union uvh_rh_gam_config_mmr_u m_n_config; diff --git a/arch/x86/platform/uv/Makefile b/arch/x86/platform/uv/Makefile index 6c40995..52079be 100644 --- a/arch/x86/platform/uv/Makefile +++ b/arch/x86/platform/uv/Makefile @@ -1 +1 @@ -obj-$(CONFIG_X86_UV) += tlb_uv.o bios_uv.o uv_irq.o uv_sysfs.o uv_time.o +obj-$(CONFIG_X86_UV) += tlb_uv.o bios_uv.o uv_irq.o uv_sysfs.o uv_time.o uv_nmi.o diff --git a/arch/x86/platform/uv/uv_nmi.c b/arch/x86/platform/uv/uv_nmi.c new file mode 100644 index 0000000..37feb60 --- /dev/null +++ b/arch/x86/platform/uv/uv_nmi.c @@ -0,0 +1,102 @@ +/* + * SGI NMI support routines + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (c) 2009-2013 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) Mike Travis + */ + +#include +#include + +#include +#include +#include +#include +#include + +/* BMC sets a bit this MMR non-zero before sending an NMI */ +#define UVH_NMI_MMR UVH_SCRATCH5 +#define UVH_NMI_MMR_CLEAR (UVH_NMI_MMR + 8) +#define UV_NMI_PENDING_MASK (1UL << 63) +DEFINE_PER_CPU(unsigned long, cpu_last_nmi_count); +static DEFINE_SPINLOCK(uv_nmi_lock); + +/* + * When NMI is received, print a stack trace. + */ +int uv_handle_nmi(unsigned int reason, struct pt_regs *regs) +{ + unsigned long real_uv_nmi; + int bid; + + /* + * Each blade has an MMR that indicates when an NMI has been sent + * to cpus on the blade. If an NMI is detected, atomically + * clear the MMR and update a per-blade NMI count used to + * cause each cpu on the blade to notice a new NMI. + */ + bid = uv_numa_blade_id(); + real_uv_nmi = (uv_read_local_mmr(UVH_NMI_MMR) & UV_NMI_PENDING_MASK); + + if (unlikely(real_uv_nmi)) { + spin_lock(&uv_blade_info[bid].nmi_lock); + real_uv_nmi = (uv_read_local_mmr(UVH_NMI_MMR) & + UV_NMI_PENDING_MASK); + if (real_uv_nmi) { + uv_blade_info[bid].nmi_count++; + uv_write_local_mmr(UVH_NMI_MMR_CLEAR, + UV_NMI_PENDING_MASK); + } + spin_unlock(&uv_blade_info[bid].nmi_lock); + } + + if (likely(__get_cpu_var(cpu_last_nmi_count) == + uv_blade_info[bid].nmi_count)) + return NMI_DONE; + + __get_cpu_var(cpu_last_nmi_count) = uv_blade_info[bid].nmi_count; + + /* + * Use a lock so only one cpu prints at a time. + * This prevents intermixed output. + */ + spin_lock(&uv_nmi_lock); + pr_info("UV NMI stack dump cpu %u:\n", smp_processor_id()); + dump_stack(); + spin_unlock(&uv_nmi_lock); + + return NMI_HANDLED; +} + +void uv_register_nmi_notifier(void) +{ + if (register_nmi_handler(NMI_UNKNOWN, uv_handle_nmi, 0, "uv")) + pr_warn("UV NMI handler failed to register\n"); +} + +void uv_nmi_init(void) +{ + unsigned int value; + + /* + * Unmask NMI on all cpus + */ + value = apic_read(APIC_LVT1) | APIC_DM_NMI; + value &= ~APIC_LVT_MASKED; + apic_write(APIC_LVT1, value); +} + -- cgit v0.10.2 From 0d12ef0c900078cc1f4e78dff2245521aa5d0c89 Mon Sep 17 00:00:00 2001 From: Mike Travis Date: Mon, 23 Sep 2013 16:25:01 -0500 Subject: x86/UV: Update UV support for external NMI signals The current UV NMI handler has not been updated for the changes in the system NMI handler and the perf operations. The UV NMI handler reads an MMR in the UV Hub to check to see if the NMI event was caused by the external 'system NMI' that the operator can initiate on the System Mgmt Controller. The problem arises when the perf tools are running, causing millions of perf events per second on very large CPU count systems. Previously this was okay because the perf NMI handler ran at a higher priority on the NMI call chain and if the NMI was a perf event, it would stop calling other NMI handlers remaining on the NMI call chain. Now the system NMI handler calls all the handlers on the NMI call chain including the UV NMI handler. This causes the UV NMI handler to read the MMRs at the same millions per second rate. This can lead to significant performance loss and possible system failures. It also can cause thousands of 'Dazed and Confused' messages being sent to the system console. This effectively makes perf tools unusable on UV systems. To avoid this excessive overhead when perf tools are running, this code has been optimized to minimize reading of the MMRs as much as possible, by moving to the NMI_UNKNOWN notifier chain. This chain is called only when all the users on the standard NMI_LOCAL call chain have been called and none of them have claimed this NMI. There is an exception where the NMI_LOCAL notifier chain is used. When the perf tools are in use, it's possible that the UV NMI was captured by some other NMI handler and then either ignored or mistakenly processed as a perf event. We set a per_cpu ('ping') flag for those CPUs that ignored the initial NMI, and then send them an IPI NMI signal. The NMI_LOCAL handler on each cpu does not need to read the MMR, but instead checks the in memory flag indicating it was pinged. There are two module variables, 'ping_count' indicating how many requested NMI events occurred, and 'ping_misses' indicating how many stray NMI events. These most likely are perf events so it shows the overhead of the perf NMI interrupts and how many MMR reads were avoided. This patch also minimizes the reads of the MMRs by having the first cpu entering the NMI handler on each node set a per HUB in-memory atomic value. (Having a per HUB value avoids sending lock traffic over NumaLink.) Both types of UV NMIs from the SMI layer are supported. Signed-off-by: Mike Travis Reviewed-by: Dimitri Sivanich Reviewed-by: Hedi Berriche Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo Cc: Jason Wessel Link: http://lkml.kernel.org/r/20130923212500.353547733@asylum.americas.sgi.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/uv/uv_hub.h b/arch/x86/include/asm/uv/uv_hub.h index 2c32df9..a30836c 100644 --- a/arch/x86/include/asm/uv/uv_hub.h +++ b/arch/x86/include/asm/uv/uv_hub.h @@ -502,8 +502,8 @@ struct uv_blade_info { unsigned short nr_online_cpus; unsigned short pnode; short memory_nid; - spinlock_t nmi_lock; - unsigned long nmi_count; + spinlock_t nmi_lock; /* obsolete, see uv_hub_nmi */ + unsigned long nmi_count; /* obsolete, see uv_hub_nmi */ }; extern struct uv_blade_info *uv_blade_info; extern short *uv_node_to_blade; @@ -576,6 +576,59 @@ static inline int uv_num_possible_blades(void) return uv_possible_blades; } +/* Per Hub NMI support */ +extern void uv_nmi_setup(void); + +/* BMC sets a bit this MMR non-zero before sending an NMI */ +#define UVH_NMI_MMR UVH_SCRATCH5 +#define UVH_NMI_MMR_CLEAR UVH_SCRATCH5_ALIAS +#define UVH_NMI_MMR_SHIFT 63 +#define UVH_NMI_MMR_TYPE "SCRATCH5" + +/* Newer SMM NMI handler, not present in all systems */ +#define UVH_NMI_MMRX UVH_EVENT_OCCURRED0 +#define UVH_NMI_MMRX_CLEAR UVH_EVENT_OCCURRED0_ALIAS +#define UVH_NMI_MMRX_SHIFT (is_uv1_hub() ? \ + UV1H_EVENT_OCCURRED0_EXTIO_INT0_SHFT :\ + UVXH_EVENT_OCCURRED0_EXTIO_INT0_SHFT) +#define UVH_NMI_MMRX_TYPE "EXTIO_INT0" + +/* Non-zero indicates newer SMM NMI handler present */ +#define UVH_NMI_MMRX_SUPPORTED UVH_EXTIO_INT0_BROADCAST + +/* Indicates to BIOS that we want to use the newer SMM NMI handler */ +#define UVH_NMI_MMRX_REQ UVH_SCRATCH5_ALIAS_2 +#define UVH_NMI_MMRX_REQ_SHIFT 62 + +struct uv_hub_nmi_s { + raw_spinlock_t nmi_lock; + atomic_t in_nmi; /* flag this node in UV NMI IRQ */ + atomic_t cpu_owner; /* last locker of this struct */ + atomic_t read_mmr_count; /* count of MMR reads */ + atomic_t nmi_count; /* count of true UV NMIs */ + unsigned long nmi_value; /* last value read from NMI MMR */ +}; + +struct uv_cpu_nmi_s { + struct uv_hub_nmi_s *hub; + atomic_t state; + atomic_t pinging; + int queries; + int pings; +}; + +DECLARE_PER_CPU(struct uv_cpu_nmi_s, __uv_cpu_nmi); +#define uv_cpu_nmi (__get_cpu_var(__uv_cpu_nmi)) +#define uv_hub_nmi (uv_cpu_nmi.hub) +#define uv_cpu_nmi_per(cpu) (per_cpu(__uv_cpu_nmi, cpu)) +#define uv_hub_nmi_per(cpu) (uv_cpu_nmi_per(cpu).hub) + +/* uv_cpu_nmi_states */ +#define UV_NMI_STATE_OUT 0 +#define UV_NMI_STATE_IN 1 +#define UV_NMI_STATE_DUMP 2 +#define UV_NMI_STATE_DUMP_DONE 3 + /* Update SCIR state */ static inline void uv_set_scir_bits(unsigned char value) { diff --git a/arch/x86/include/asm/uv/uv_mmrs.h b/arch/x86/include/asm/uv/uv_mmrs.h index bd5f80e..e42249b 100644 --- a/arch/x86/include/asm/uv/uv_mmrs.h +++ b/arch/x86/include/asm/uv/uv_mmrs.h @@ -461,6 +461,23 @@ union uvh_event_occurred0_u { /* ========================================================================= */ +/* UVH_EXTIO_INT0_BROADCAST */ +/* ========================================================================= */ +#define UVH_EXTIO_INT0_BROADCAST 0x61448UL +#define UVH_EXTIO_INT0_BROADCAST_32 0x3f0 + +#define UVH_EXTIO_INT0_BROADCAST_ENABLE_SHFT 0 +#define UVH_EXTIO_INT0_BROADCAST_ENABLE_MASK 0x0000000000000001UL + +union uvh_extio_int0_broadcast_u { + unsigned long v; + struct uvh_extio_int0_broadcast_s { + unsigned long enable:1; /* RW */ + unsigned long rsvd_1_63:63; + } s; +}; + +/* ========================================================================= */ /* UVH_GR0_TLB_INT0_CONFIG */ /* ========================================================================= */ #define UVH_GR0_TLB_INT0_CONFIG 0x61b00UL @@ -2606,6 +2623,20 @@ union uvh_scratch5_u { }; /* ========================================================================= */ +/* UVH_SCRATCH5_ALIAS */ +/* ========================================================================= */ +#define UVH_SCRATCH5_ALIAS 0x2d0208UL +#define UVH_SCRATCH5_ALIAS_32 0x780 + + +/* ========================================================================= */ +/* UVH_SCRATCH5_ALIAS_2 */ +/* ========================================================================= */ +#define UVH_SCRATCH5_ALIAS_2 0x2d0210UL +#define UVH_SCRATCH5_ALIAS_2_32 0x788 + + +/* ========================================================================= */ /* UVXH_EVENT_OCCURRED2 */ /* ========================================================================= */ #define UVXH_EVENT_OCCURRED2 0x70100UL diff --git a/arch/x86/kernel/apic/x2apic_uv_x.c b/arch/x86/kernel/apic/x2apic_uv_x.c index 9e47c06..0a5a4b8 100644 --- a/arch/x86/kernel/apic/x2apic_uv_x.c +++ b/arch/x86/kernel/apic/x2apic_uv_x.c @@ -977,6 +977,7 @@ void __init uv_system_init(void) map_mmr_high(max_pnode); map_mmioh_high(min_pnode, max_pnode); + uv_nmi_setup(); uv_cpu_init(); uv_scir_register_cpu_notifier(); uv_register_nmi_notifier(); diff --git a/arch/x86/platform/uv/uv_nmi.c b/arch/x86/platform/uv/uv_nmi.c index 37feb60..fb02ea7 100644 --- a/arch/x86/platform/uv/uv_nmi.c +++ b/arch/x86/platform/uv/uv_nmi.c @@ -20,72 +20,518 @@ */ #include +#include +#include #include +#include +#include #include +#include +#include +#include #include #include #include #include -/* BMC sets a bit this MMR non-zero before sending an NMI */ -#define UVH_NMI_MMR UVH_SCRATCH5 -#define UVH_NMI_MMR_CLEAR (UVH_NMI_MMR + 8) -#define UV_NMI_PENDING_MASK (1UL << 63) -DEFINE_PER_CPU(unsigned long, cpu_last_nmi_count); -static DEFINE_SPINLOCK(uv_nmi_lock); +/* + * UV handler for NMI + * + * Handle system-wide NMI events generated by the global 'power nmi' command. + * + * Basic operation is to field the NMI interrupt on each cpu and wait + * until all cpus have arrived into the nmi handler. If some cpus do not + * make it into the handler, try and force them in with the IPI(NMI) signal. + * + * We also have to lessen UV Hub MMR accesses as much as possible as this + * disrupts the UV Hub's primary mission of directing NumaLink traffic and + * can cause system problems to occur. + * + * To do this we register our primary NMI notifier on the NMI_UNKNOWN + * chain. This reduces the number of false NMI calls when the perf + * tools are running which generate an enormous number of NMIs per + * second (~4M/s for 1024 cpu threads). Our secondary NMI handler is + * very short as it only checks that if it has been "pinged" with the + * IPI(NMI) signal as mentioned above, and does not read the UV Hub's MMR. + * + */ + +static struct uv_hub_nmi_s **uv_hub_nmi_list; + +DEFINE_PER_CPU(struct uv_cpu_nmi_s, __uv_cpu_nmi); +EXPORT_PER_CPU_SYMBOL_GPL(__uv_cpu_nmi); + +static unsigned long nmi_mmr; +static unsigned long nmi_mmr_clear; +static unsigned long nmi_mmr_pending; + +static atomic_t uv_in_nmi; +static atomic_t uv_nmi_cpu = ATOMIC_INIT(-1); +static atomic_t uv_nmi_cpus_in_nmi = ATOMIC_INIT(-1); +static atomic_t uv_nmi_slave_continue; +static cpumask_var_t uv_nmi_cpu_mask; + +/* Values for uv_nmi_slave_continue */ +#define SLAVE_CLEAR 0 +#define SLAVE_CONTINUE 1 +#define SLAVE_EXIT 2 /* - * When NMI is received, print a stack trace. + * Default is all stack dumps go to the console and buffer. + * Lower level to send to log buffer only. */ -int uv_handle_nmi(unsigned int reason, struct pt_regs *regs) +static int uv_nmi_loglevel = 7; +module_param_named(dump_loglevel, uv_nmi_loglevel, int, 0644); + +/* + * The following values show statistics on how perf events are affecting + * this system. + */ +static int param_get_local64(char *buffer, const struct kernel_param *kp) { - unsigned long real_uv_nmi; - int bid; + return sprintf(buffer, "%lu\n", local64_read((local64_t *)kp->arg)); +} - /* - * Each blade has an MMR that indicates when an NMI has been sent - * to cpus on the blade. If an NMI is detected, atomically - * clear the MMR and update a per-blade NMI count used to - * cause each cpu on the blade to notice a new NMI. - */ - bid = uv_numa_blade_id(); - real_uv_nmi = (uv_read_local_mmr(UVH_NMI_MMR) & UV_NMI_PENDING_MASK); - - if (unlikely(real_uv_nmi)) { - spin_lock(&uv_blade_info[bid].nmi_lock); - real_uv_nmi = (uv_read_local_mmr(UVH_NMI_MMR) & - UV_NMI_PENDING_MASK); - if (real_uv_nmi) { - uv_blade_info[bid].nmi_count++; - uv_write_local_mmr(UVH_NMI_MMR_CLEAR, - UV_NMI_PENDING_MASK); +static int param_set_local64(const char *val, const struct kernel_param *kp) +{ + /* clear on any write */ + local64_set((local64_t *)kp->arg, 0); + return 0; +} + +static struct kernel_param_ops param_ops_local64 = { + .get = param_get_local64, + .set = param_set_local64, +}; +#define param_check_local64(name, p) __param_check(name, p, local64_t) + +static local64_t uv_nmi_count; +module_param_named(nmi_count, uv_nmi_count, local64, 0644); + +static local64_t uv_nmi_misses; +module_param_named(nmi_misses, uv_nmi_misses, local64, 0644); + +static local64_t uv_nmi_ping_count; +module_param_named(ping_count, uv_nmi_ping_count, local64, 0644); + +static local64_t uv_nmi_ping_misses; +module_param_named(ping_misses, uv_nmi_ping_misses, local64, 0644); + +/* + * Following values allow tuning for large systems under heavy loading + */ +static int uv_nmi_initial_delay = 100; +module_param_named(initial_delay, uv_nmi_initial_delay, int, 0644); + +static int uv_nmi_slave_delay = 100; +module_param_named(slave_delay, uv_nmi_slave_delay, int, 0644); + +static int uv_nmi_loop_delay = 100; +module_param_named(loop_delay, uv_nmi_loop_delay, int, 0644); + +static int uv_nmi_trigger_delay = 10000; +module_param_named(trigger_delay, uv_nmi_trigger_delay, int, 0644); + +static int uv_nmi_wait_count = 100; +module_param_named(wait_count, uv_nmi_wait_count, int, 0644); + +static int uv_nmi_retry_count = 500; +module_param_named(retry_count, uv_nmi_retry_count, int, 0644); + +/* Setup which NMI support is present in system */ +static void uv_nmi_setup_mmrs(void) +{ + if (uv_read_local_mmr(UVH_NMI_MMRX_SUPPORTED)) { + uv_write_local_mmr(UVH_NMI_MMRX_REQ, + 1UL << UVH_NMI_MMRX_REQ_SHIFT); + nmi_mmr = UVH_NMI_MMRX; + nmi_mmr_clear = UVH_NMI_MMRX_CLEAR; + nmi_mmr_pending = 1UL << UVH_NMI_MMRX_SHIFT; + pr_info("UV: SMI NMI support: %s\n", UVH_NMI_MMRX_TYPE); + } else { + nmi_mmr = UVH_NMI_MMR; + nmi_mmr_clear = UVH_NMI_MMR_CLEAR; + nmi_mmr_pending = 1UL << UVH_NMI_MMR_SHIFT; + pr_info("UV: SMI NMI support: %s\n", UVH_NMI_MMR_TYPE); + } +} + +/* Read NMI MMR and check if NMI flag was set by BMC. */ +static inline int uv_nmi_test_mmr(struct uv_hub_nmi_s *hub_nmi) +{ + hub_nmi->nmi_value = uv_read_local_mmr(nmi_mmr); + atomic_inc(&hub_nmi->read_mmr_count); + return !!(hub_nmi->nmi_value & nmi_mmr_pending); +} + +static inline void uv_local_mmr_clear_nmi(void) +{ + uv_write_local_mmr(nmi_mmr_clear, nmi_mmr_pending); +} + +/* + * If first cpu in on this hub, set hub_nmi "in_nmi" and "owner" values and + * return true. If first cpu in on the system, set global "in_nmi" flag. + */ +static int uv_set_in_nmi(int cpu, struct uv_hub_nmi_s *hub_nmi) +{ + int first = atomic_add_unless(&hub_nmi->in_nmi, 1, 1); + + if (first) { + atomic_set(&hub_nmi->cpu_owner, cpu); + if (atomic_add_unless(&uv_in_nmi, 1, 1)) + atomic_set(&uv_nmi_cpu, cpu); + + atomic_inc(&hub_nmi->nmi_count); + } + return first; +} + +/* Check if this is a system NMI event */ +static int uv_check_nmi(struct uv_hub_nmi_s *hub_nmi) +{ + int cpu = smp_processor_id(); + int nmi = 0; + + local64_inc(&uv_nmi_count); + uv_cpu_nmi.queries++; + + do { + nmi = atomic_read(&hub_nmi->in_nmi); + if (nmi) + break; + + if (raw_spin_trylock(&hub_nmi->nmi_lock)) { + + /* check hub MMR NMI flag */ + if (uv_nmi_test_mmr(hub_nmi)) { + uv_set_in_nmi(cpu, hub_nmi); + nmi = 1; + break; + } + + /* MMR NMI flag is clear */ + raw_spin_unlock(&hub_nmi->nmi_lock); + + } else { + /* wait a moment for the hub nmi locker to set flag */ + cpu_relax(); + udelay(uv_nmi_slave_delay); + + /* re-check hub in_nmi flag */ + nmi = atomic_read(&hub_nmi->in_nmi); + if (nmi) + break; + } + + /* check if this BMC missed setting the MMR NMI flag */ + if (!nmi) { + nmi = atomic_read(&uv_in_nmi); + if (nmi) + uv_set_in_nmi(cpu, hub_nmi); + } + + } while (0); + + if (!nmi) + local64_inc(&uv_nmi_misses); + + return nmi; +} + +/* Need to reset the NMI MMR register, but only once per hub. */ +static inline void uv_clear_nmi(int cpu) +{ + struct uv_hub_nmi_s *hub_nmi = uv_hub_nmi; + + if (cpu == atomic_read(&hub_nmi->cpu_owner)) { + atomic_set(&hub_nmi->cpu_owner, -1); + atomic_set(&hub_nmi->in_nmi, 0); + uv_local_mmr_clear_nmi(); + raw_spin_unlock(&hub_nmi->nmi_lock); + } +} + +/* Print non-responding cpus */ +static void uv_nmi_nr_cpus_pr(char *fmt) +{ + static char cpu_list[1024]; + int len = sizeof(cpu_list); + int c = cpumask_weight(uv_nmi_cpu_mask); + int n = cpulist_scnprintf(cpu_list, len, uv_nmi_cpu_mask); + + if (n >= len-1) + strcpy(&cpu_list[len - 6], "...\n"); + + printk(fmt, c, cpu_list); +} + +/* Ping non-responding cpus attemping to force them into the NMI handler */ +static void uv_nmi_nr_cpus_ping(void) +{ + int cpu; + + for_each_cpu(cpu, uv_nmi_cpu_mask) + atomic_set(&uv_cpu_nmi_per(cpu).pinging, 1); + + apic->send_IPI_mask(uv_nmi_cpu_mask, APIC_DM_NMI); +} + +/* Clean up flags for cpus that ignored both NMI and ping */ +static void uv_nmi_cleanup_mask(void) +{ + int cpu; + + for_each_cpu(cpu, uv_nmi_cpu_mask) { + atomic_set(&uv_cpu_nmi_per(cpu).pinging, 0); + atomic_set(&uv_cpu_nmi_per(cpu).state, UV_NMI_STATE_OUT); + cpumask_clear_cpu(cpu, uv_nmi_cpu_mask); + } +} + +/* Loop waiting as cpus enter nmi handler */ +static int uv_nmi_wait_cpus(int first) +{ + int i, j, k, n = num_online_cpus(); + int last_k = 0, waiting = 0; + + if (first) { + cpumask_copy(uv_nmi_cpu_mask, cpu_online_mask); + k = 0; + } else { + k = n - cpumask_weight(uv_nmi_cpu_mask); + } + + udelay(uv_nmi_initial_delay); + for (i = 0; i < uv_nmi_retry_count; i++) { + int loop_delay = uv_nmi_loop_delay; + + for_each_cpu(j, uv_nmi_cpu_mask) { + if (atomic_read(&uv_cpu_nmi_per(j).state)) { + cpumask_clear_cpu(j, uv_nmi_cpu_mask); + if (++k >= n) + break; + } + } + if (k >= n) { /* all in? */ + k = n; + break; + } + if (last_k != k) { /* abort if no new cpus coming in */ + last_k = k; + waiting = 0; + } else if (++waiting > uv_nmi_wait_count) + break; + + /* extend delay if waiting only for cpu 0 */ + if (waiting && (n - k) == 1 && + cpumask_test_cpu(0, uv_nmi_cpu_mask)) + loop_delay *= 100; + + udelay(loop_delay); + } + atomic_set(&uv_nmi_cpus_in_nmi, k); + return n - k; +} + +/* Wait until all slave cpus have entered UV NMI handler */ +static void uv_nmi_wait(int master) +{ + /* indicate this cpu is in */ + atomic_set(&uv_cpu_nmi.state, UV_NMI_STATE_IN); + + /* if not the first cpu in (the master), then we are a slave cpu */ + if (!master) + return; + + do { + /* wait for all other cpus to gather here */ + if (!uv_nmi_wait_cpus(1)) + break; + + /* if not all made it in, send IPI NMI to them */ + uv_nmi_nr_cpus_pr(KERN_ALERT + "UV: Sending NMI IPI to %d non-responding CPUs: %s\n"); + uv_nmi_nr_cpus_ping(); + + /* if all cpus are in, then done */ + if (!uv_nmi_wait_cpus(0)) + break; + + uv_nmi_nr_cpus_pr(KERN_ALERT + "UV: %d CPUs not in NMI loop: %s\n"); + } while (0); + + pr_alert("UV: %d of %d CPUs in NMI\n", + atomic_read(&uv_nmi_cpus_in_nmi), num_online_cpus()); +} + +/* Dump this cpu's state */ +static void uv_nmi_dump_state_cpu(int cpu, struct pt_regs *regs) +{ + const char *dots = " ................................. "; + + printk(KERN_DEFAULT "UV:%sNMI process trace for CPU %d\n", dots, cpu); + show_regs(regs); + atomic_set(&uv_cpu_nmi.state, UV_NMI_STATE_DUMP_DONE); +} + +/* Trigger a slave cpu to dump it's state */ +static void uv_nmi_trigger_dump(int cpu) +{ + int retry = uv_nmi_trigger_delay; + + if (atomic_read(&uv_cpu_nmi_per(cpu).state) != UV_NMI_STATE_IN) + return; + + atomic_set(&uv_cpu_nmi_per(cpu).state, UV_NMI_STATE_DUMP); + do { + cpu_relax(); + udelay(10); + if (atomic_read(&uv_cpu_nmi_per(cpu).state) + != UV_NMI_STATE_DUMP) + return; + } while (--retry > 0); + + pr_crit("UV: CPU %d stuck in process dump function\n", cpu); + atomic_set(&uv_cpu_nmi_per(cpu).state, UV_NMI_STATE_DUMP_DONE); +} + +/* Wait until all cpus ready to exit */ +static void uv_nmi_sync_exit(int master) +{ + atomic_dec(&uv_nmi_cpus_in_nmi); + if (master) { + while (atomic_read(&uv_nmi_cpus_in_nmi) > 0) + cpu_relax(); + atomic_set(&uv_nmi_slave_continue, SLAVE_CLEAR); + } else { + while (atomic_read(&uv_nmi_slave_continue)) + cpu_relax(); + } +} + +/* Walk through cpu list and dump state of each */ +static void uv_nmi_dump_state(int cpu, struct pt_regs *regs, int master) +{ + if (master) { + int tcpu; + int ignored = 0; + int saved_console_loglevel = console_loglevel; + + pr_alert("UV: tracing processes for %d CPUs from CPU %d\n", + atomic_read(&uv_nmi_cpus_in_nmi), cpu); + + console_loglevel = uv_nmi_loglevel; + atomic_set(&uv_nmi_slave_continue, SLAVE_EXIT); + for_each_online_cpu(tcpu) { + if (cpumask_test_cpu(tcpu, uv_nmi_cpu_mask)) + ignored++; + else if (tcpu == cpu) + uv_nmi_dump_state_cpu(tcpu, regs); + else + uv_nmi_trigger_dump(tcpu); } - spin_unlock(&uv_blade_info[bid].nmi_lock); + if (ignored) + printk(KERN_DEFAULT "UV: %d CPUs ignored NMI\n", + ignored); + + console_loglevel = saved_console_loglevel; + pr_alert("UV: process trace complete\n"); + } else { + while (!atomic_read(&uv_nmi_slave_continue)) + cpu_relax(); + while (atomic_read(&uv_cpu_nmi.state) != UV_NMI_STATE_DUMP) + cpu_relax(); + uv_nmi_dump_state_cpu(cpu, regs); } + uv_nmi_sync_exit(master); +} - if (likely(__get_cpu_var(cpu_last_nmi_count) == - uv_blade_info[bid].nmi_count)) +static void uv_nmi_touch_watchdogs(void) +{ + touch_softlockup_watchdog_sync(); + clocksource_touch_watchdog(); + rcu_cpu_stall_reset(); + touch_nmi_watchdog(); +} + +/* + * UV NMI handler + */ +int uv_handle_nmi(unsigned int reason, struct pt_regs *regs) +{ + struct uv_hub_nmi_s *hub_nmi = uv_hub_nmi; + int cpu = smp_processor_id(); + int master = 0; + unsigned long flags; + + local_irq_save(flags); + + /* If not a UV System NMI, ignore */ + if (!atomic_read(&uv_cpu_nmi.pinging) && !uv_check_nmi(hub_nmi)) { + local_irq_restore(flags); return NMI_DONE; + } - __get_cpu_var(cpu_last_nmi_count) = uv_blade_info[bid].nmi_count; + /* Indicate we are the first CPU into the NMI handler */ + master = (atomic_read(&uv_nmi_cpu) == cpu); - /* - * Use a lock so only one cpu prints at a time. - * This prevents intermixed output. - */ - spin_lock(&uv_nmi_lock); - pr_info("UV NMI stack dump cpu %u:\n", smp_processor_id()); - dump_stack(); - spin_unlock(&uv_nmi_lock); + /* Pause as all cpus enter the NMI handler */ + uv_nmi_wait(master); + + /* Dump state of each cpu */ + uv_nmi_dump_state(cpu, regs, master); + + /* Clear per_cpu "in nmi" flag */ + atomic_set(&uv_cpu_nmi.state, UV_NMI_STATE_OUT); + + /* Clear MMR NMI flag on each hub */ + uv_clear_nmi(cpu); + + /* Clear global flags */ + if (master) { + if (cpumask_weight(uv_nmi_cpu_mask)) + uv_nmi_cleanup_mask(); + atomic_set(&uv_nmi_cpus_in_nmi, -1); + atomic_set(&uv_nmi_cpu, -1); + atomic_set(&uv_in_nmi, 0); + } + + uv_nmi_touch_watchdogs(); + local_irq_restore(flags); return NMI_HANDLED; } +/* + * NMI handler for pulling in CPUs when perf events are grabbing our NMI + */ +int uv_handle_nmi_ping(unsigned int reason, struct pt_regs *regs) +{ + int ret; + + uv_cpu_nmi.queries++; + if (!atomic_read(&uv_cpu_nmi.pinging)) { + local64_inc(&uv_nmi_ping_misses); + return NMI_DONE; + } + + uv_cpu_nmi.pings++; + local64_inc(&uv_nmi_ping_count); + ret = uv_handle_nmi(reason, regs); + atomic_set(&uv_cpu_nmi.pinging, 0); + return ret; +} + void uv_register_nmi_notifier(void) { if (register_nmi_handler(NMI_UNKNOWN, uv_handle_nmi, 0, "uv")) - pr_warn("UV NMI handler failed to register\n"); + pr_warn("UV: NMI handler failed to register\n"); + + if (register_nmi_handler(NMI_LOCAL, uv_handle_nmi_ping, 0, "uvping")) + pr_warn("UV: PING NMI handler failed to register\n"); } void uv_nmi_init(void) @@ -100,3 +546,30 @@ void uv_nmi_init(void) apic_write(APIC_LVT1, value); } +void uv_nmi_setup(void) +{ + int size = sizeof(void *) * (1 << NODES_SHIFT); + int cpu, nid; + + /* Setup hub nmi info */ + uv_nmi_setup_mmrs(); + uv_hub_nmi_list = kzalloc(size, GFP_KERNEL); + pr_info("UV: NMI hub list @ 0x%p (%d)\n", uv_hub_nmi_list, size); + BUG_ON(!uv_hub_nmi_list); + size = sizeof(struct uv_hub_nmi_s); + for_each_present_cpu(cpu) { + nid = cpu_to_node(cpu); + if (uv_hub_nmi_list[nid] == NULL) { + uv_hub_nmi_list[nid] = kzalloc_node(size, + GFP_KERNEL, nid); + BUG_ON(!uv_hub_nmi_list[nid]); + raw_spin_lock_init(&(uv_hub_nmi_list[nid]->nmi_lock)); + atomic_set(&uv_hub_nmi_list[nid]->cpu_owner, -1); + } + uv_hub_nmi_per(cpu) = uv_hub_nmi_list[nid]; + } + alloc_cpumask_var(&uv_nmi_cpu_mask, GFP_KERNEL); + BUG_ON(!uv_nmi_cpu_mask); +} + + -- cgit v0.10.2 From 3c121d9a21dc16ef030ad6ca3ebb159b5726fab9 Mon Sep 17 00:00:00 2001 From: Mike Travis Date: Mon, 23 Sep 2013 16:25:02 -0500 Subject: x86/UV: Add summary of cpu activity to UV NMI handler The standard NMI handler dumps the states of all the cpus. This includes a full register dump and stack trace. This can be way more information than what is needed. This patch adds a "summary" dump that is basically a form of the "ps" command. It includes the symbolic IP address as well as the command field and basic process information. It is enabled when the nmi action is changed to "ips". Signed-off-by: Mike Travis Reviewed-by: Dimitri Sivanich Reviewed-by: Hedi Berriche Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo Cc: Jason Wessel Link: http://lkml.kernel.org/r/20130923212500.507922930@asylum.americas.sgi.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/platform/uv/uv_nmi.c b/arch/x86/platform/uv/uv_nmi.c index fb02ea7..4efcde1 100644 --- a/arch/x86/platform/uv/uv_nmi.c +++ b/arch/x86/platform/uv/uv_nmi.c @@ -139,6 +139,19 @@ module_param_named(wait_count, uv_nmi_wait_count, int, 0644); static int uv_nmi_retry_count = 500; module_param_named(retry_count, uv_nmi_retry_count, int, 0644); +/* + * Valid NMI Actions: + * "dump" - dump process stack for each cpu + * "ips" - dump IP info for each cpu + */ +static char uv_nmi_action[8] = "dump"; +module_param_string(action, uv_nmi_action, sizeof(uv_nmi_action), 0644); + +static inline bool uv_nmi_action_is(const char *action) +{ + return (strncmp(uv_nmi_action, action, strlen(action)) == 0); +} + /* Setup which NMI support is present in system */ static void uv_nmi_setup_mmrs(void) { @@ -367,13 +380,38 @@ static void uv_nmi_wait(int master) atomic_read(&uv_nmi_cpus_in_nmi), num_online_cpus()); } +static void uv_nmi_dump_cpu_ip_hdr(void) +{ + printk(KERN_DEFAULT + "\nUV: %4s %6s %-32s %s (Note: PID 0 not listed)\n", + "CPU", "PID", "COMMAND", "IP"); +} + +static void uv_nmi_dump_cpu_ip(int cpu, struct pt_regs *regs) +{ + printk(KERN_DEFAULT "UV: %4d %6d %-32.32s ", + cpu, current->pid, current->comm); + + printk_address(regs->ip, 1); +} + /* Dump this cpu's state */ static void uv_nmi_dump_state_cpu(int cpu, struct pt_regs *regs) { const char *dots = " ................................. "; - printk(KERN_DEFAULT "UV:%sNMI process trace for CPU %d\n", dots, cpu); - show_regs(regs); + if (uv_nmi_action_is("ips")) { + if (cpu == 0) + uv_nmi_dump_cpu_ip_hdr(); + + if (current->pid != 0) + uv_nmi_dump_cpu_ip(cpu, regs); + + } else if (uv_nmi_action_is("dump")) { + printk(KERN_DEFAULT + "UV:%sNMI process trace for CPU %d\n", dots, cpu); + show_regs(regs); + } atomic_set(&uv_cpu_nmi.state, UV_NMI_STATE_DUMP_DONE); } @@ -420,7 +458,8 @@ static void uv_nmi_dump_state(int cpu, struct pt_regs *regs, int master) int ignored = 0; int saved_console_loglevel = console_loglevel; - pr_alert("UV: tracing processes for %d CPUs from CPU %d\n", + pr_alert("UV: tracing %s for %d CPUs from CPU %d\n", + uv_nmi_action_is("ips") ? "IPs" : "processes", atomic_read(&uv_nmi_cpus_in_nmi), cpu); console_loglevel = uv_nmi_loglevel; @@ -482,7 +521,8 @@ int uv_handle_nmi(unsigned int reason, struct pt_regs *regs) uv_nmi_wait(master); /* Dump state of each cpu */ - uv_nmi_dump_state(cpu, regs, master); + if (uv_nmi_action_is("ips") || uv_nmi_action_is("dump")) + uv_nmi_dump_state(cpu, regs, master); /* Clear per_cpu "in nmi" flag */ atomic_set(&uv_cpu_nmi.state, UV_NMI_STATE_OUT); -- cgit v0.10.2 From 12ba6c990fab50fe568f3ad8715e81e356552428 Mon Sep 17 00:00:00 2001 From: Mike Travis Date: Mon, 23 Sep 2013 16:25:03 -0500 Subject: x86/UV: Add kdump to UV NMI handler If a system has hung and it no longer responds to external events, this patch adds the capability of doing a standard kdump and system reboot then triggered by the system NMI command. It is enabled when the nmi action is changed to "kdump" and the kernel is built with CONFIG_KEXEC enabled. Signed-off-by: Mike Travis Reviewed-by: Dimitri Sivanich Reviewed-by: Hedi Berriche Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo Cc: Jason Wessel Link: http://lkml.kernel.org/r/20130923212500.660567460@asylum.americas.sgi.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/platform/uv/uv_nmi.c b/arch/x86/platform/uv/uv_nmi.c index 4efcde1..2579fbd 100644 --- a/arch/x86/platform/uv/uv_nmi.c +++ b/arch/x86/platform/uv/uv_nmi.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -70,6 +71,7 @@ static atomic_t uv_in_nmi; static atomic_t uv_nmi_cpu = ATOMIC_INIT(-1); static atomic_t uv_nmi_cpus_in_nmi = ATOMIC_INIT(-1); static atomic_t uv_nmi_slave_continue; +static atomic_t uv_nmi_kexec_failed; static cpumask_var_t uv_nmi_cpu_mask; /* Values for uv_nmi_slave_continue */ @@ -143,6 +145,7 @@ module_param_named(retry_count, uv_nmi_retry_count, int, 0644); * Valid NMI Actions: * "dump" - dump process stack for each cpu * "ips" - dump IP info for each cpu + * "kdump" - do crash dump */ static char uv_nmi_action[8] = "dump"; module_param_string(action, uv_nmi_action, sizeof(uv_nmi_action), 0644); @@ -496,6 +499,40 @@ static void uv_nmi_touch_watchdogs(void) touch_nmi_watchdog(); } +#if defined(CONFIG_KEXEC) +static void uv_nmi_kdump(int cpu, int master, struct pt_regs *regs) +{ + /* Call crash to dump system state */ + if (master) { + pr_emerg("UV: NMI executing crash_kexec on CPU%d\n", cpu); + crash_kexec(regs); + + pr_emerg("UV: crash_kexec unexpectedly returned, "); + if (!kexec_crash_image) { + pr_cont("crash kernel not loaded\n"); + atomic_set(&uv_nmi_kexec_failed, 1); + uv_nmi_sync_exit(1); + return; + } + pr_cont("kexec busy, stalling cpus while waiting\n"); + } + + /* If crash exec fails the slaves should return, otherwise stall */ + while (atomic_read(&uv_nmi_kexec_failed) == 0) + mdelay(10); + + /* Crash kernel most likely not loaded, return in an orderly fashion */ + uv_nmi_sync_exit(0); +} + +#else /* !CONFIG_KEXEC */ +static inline void uv_nmi_kdump(int cpu, int master, struct pt_regs *regs) +{ + if (master) + pr_err("UV: NMI kdump: KEXEC not supported in this kernel\n"); +} +#endif /* !CONFIG_KEXEC */ + /* * UV NMI handler */ @@ -517,6 +554,10 @@ int uv_handle_nmi(unsigned int reason, struct pt_regs *regs) /* Indicate we are the first CPU into the NMI handler */ master = (atomic_read(&uv_nmi_cpu) == cpu); + /* If NMI action is "kdump", then attempt to do it */ + if (uv_nmi_action_is("kdump")) + uv_nmi_kdump(cpu, master, regs); + /* Pause as all cpus enter the NMI handler */ uv_nmi_wait(master); -- cgit v0.10.2 From 8eba18428ac926f436064ac281e76d36d51bd631 Mon Sep 17 00:00:00 2001 From: Mike Travis Date: Mon, 23 Sep 2013 16:25:06 -0500 Subject: x86/UV: Add uvtrace support This patch adds support for the uvtrace module by providing a skeleton call to the registered trace function. It also provides another separate 'NMI' tracer that is triggered by the system wide 'power nmi' command. Signed-off-by: Mike Travis Reviewed-by: Dimitri Sivanich Reviewed-by: Hedi Berriche Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo Cc: Jason Wessel Link: http://lkml.kernel.org/r/20130923212501.185052551@asylum.americas.sgi.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/uv/uv.h b/arch/x86/include/asm/uv/uv.h index 6b964a0..8b1283d 100644 --- a/arch/x86/include/asm/uv/uv.h +++ b/arch/x86/include/asm/uv/uv.h @@ -14,6 +14,13 @@ extern void uv_cpu_init(void); extern void uv_nmi_init(void); extern void uv_register_nmi_notifier(void); extern void uv_system_init(void); +extern void (*uv_trace_nmi_func)(unsigned int reason, struct pt_regs *regs); +extern void (*uv_trace_func)(const char *f, const int l, const char *fmt, ...); +#define uv_trace(fmt, ...) \ +do { \ + if (unlikely(uv_trace_func)) \ + (uv_trace_func)(__func__, __LINE__, fmt, ##__VA_ARGS__);\ +} while (0) extern const struct cpumask *uv_flush_tlb_others(const struct cpumask *cpumask, struct mm_struct *mm, unsigned long start, @@ -26,6 +33,7 @@ static inline enum uv_system_type get_uv_system_type(void) { return UV_NONE; } static inline int is_uv_system(void) { return 0; } static inline void uv_cpu_init(void) { } static inline void uv_system_init(void) { } +static inline void uv_trace(void *fmt, ...) { } static inline void uv_register_nmi_notifier(void) { } static inline const struct cpumask * uv_flush_tlb_others(const struct cpumask *cpumask, struct mm_struct *mm, diff --git a/arch/x86/platform/uv/uv_nmi.c b/arch/x86/platform/uv/uv_nmi.c index 2579fbd..c171ca5 100644 --- a/arch/x86/platform/uv/uv_nmi.c +++ b/arch/x86/platform/uv/uv_nmi.c @@ -1,5 +1,5 @@ /* - * SGI NMI support routines + * SGI NMI/TRACE support routines * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -36,6 +36,13 @@ #include #include +void (*uv_trace_func)(const char *f, const int l, const char *fmt, ...); +EXPORT_SYMBOL(uv_trace_func); + +void (*uv_trace_nmi_func)(unsigned int reason, struct pt_regs *regs); +EXPORT_SYMBOL(uv_trace_nmi_func); + + /* * UV handler for NMI * @@ -551,6 +558,10 @@ int uv_handle_nmi(unsigned int reason, struct pt_regs *regs) return NMI_DONE; } + /* Call possible NMI trace function */ + if (unlikely(uv_trace_nmi_func)) + (uv_trace_nmi_func)(reason, regs); + /* Indicate we are the first CPU into the NMI handler */ master = (atomic_read(&uv_nmi_cpu) == cpu); -- cgit v0.10.2 From 8a1f4653f27ffd5d61088cf6b95c39bb13bf6132 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Tue, 24 Sep 2013 09:52:40 +0200 Subject: x86/UV: Check for alloc_cpumask_var() failures properly in uv_nmi_setup() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GCC warned about: arch/x86/platform/uv/uv_nmi.c: In function ‘uv_nmi_setup’: arch/x86/platform/uv/uv_nmi.c:664:2: warning: the address of ‘uv_nmi_cpu_mask’ will always evaluate as ‘true’ The reason is this code: alloc_cpumask_var(&uv_nmi_cpu_mask, GFP_KERNEL); BUG_ON(!uv_nmi_cpu_mask); which is not the way to check for alloc_cpumask_var() failures - its return code should be checked instead. Cc: Mike Travis Link: http://lkml.kernel.org/n/tip-2pXRemsjupmvonbpmmnzleo1@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/arch/x86/platform/uv/uv_nmi.c b/arch/x86/platform/uv/uv_nmi.c index c171ca5..9126dfb 100644 --- a/arch/x86/platform/uv/uv_nmi.c +++ b/arch/x86/platform/uv/uv_nmi.c @@ -660,8 +660,7 @@ void uv_nmi_setup(void) } uv_hub_nmi_per(cpu) = uv_hub_nmi_list[nid]; } - alloc_cpumask_var(&uv_nmi_cpu_mask, GFP_KERNEL); - BUG_ON(!uv_nmi_cpu_mask); + BUG_ON(!alloc_cpumask_var(&uv_nmi_cpu_mask, GFP_KERNEL)); } -- cgit v0.10.2 From ef60abbb6b406389245225ab4acfe73f66e7d92c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 23 Sep 2013 16:12:52 +0100 Subject: regulator: core: Always use return value when regulator_dev_lookup() fails Ensure that the return value is always set when we return now that the logic has changed for regulator_get_optional() so we don't get missing codes leaking out. Reported-by: Thierry Reding Tested-by: Thierry Reding Signed-off-by: Mark Brown diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 088b41a..a40055e 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1263,12 +1263,13 @@ static struct regulator *_regulator_get(struct device *dev, const char *id, if (rdev) goto found; + regulator = ERR_PTR(ret); + /* * If we have return value from dev_lookup fail, we do not expect to * succeed, so, quit with appropriate error value */ if (ret && ret != -ENODEV) { - regulator = ERR_PTR(ret); goto out; } -- cgit v0.10.2 From 64d2307c3b7daa03dbc0c3a6b514709dd7b6eaee Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 23 Sep 2013 01:08:25 -0300 Subject: ASoC: fsl: fsl_ssi: Fix simultaneous capture and playback When doing simultaneous capture and playback on a mx6 board we get the following error: $ arecord -f cd | aplay -f cd imx-sgtl5000 sound.13: set sample size in capture stream first fsl-ssi-dai 2028000.ssi: ASoC: can't open interface 2028000.ssi: -11 ALSA lib pcm_dmix.c:1018:(snd_pcm_dmix_ open) unable to open slave aplay: main:660: audio open error: Device or resource busy Recording WAVE 'stdin' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo The 'arecord -f cd | aplay -f cd' always trigger cause the 'if (!first_runtime->sample_bits)' block to be true which returns an error. Adjust the logic inside fsl_ssi_startup(), so that we do not always hit the error when playing 'arecord | aplay' line for the first time. Reported-by: Chris Clepper Suggested-by: Nicolin Chen Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 6ac8730..cdbb641 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -469,19 +469,12 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, * parameters, then the second stream may be * constrained to the wrong sample rate or size. */ - if (!first_runtime->sample_bits) { - dev_err(substream->pcm->card->dev, - "set sample size in %s stream first\n", - substream->stream == - SNDRV_PCM_STREAM_PLAYBACK - ? "capture" : "playback"); - return -EAGAIN; - } - - snd_pcm_hw_constraint_minmax(substream->runtime, - SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + if (first_runtime->sample_bits) { + snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, first_runtime->sample_bits, first_runtime->sample_bits); + } } ssi_private->second_stream = substream; -- cgit v0.10.2 From 9553821ead2b6e813a8b3a5360e20e28bfabc177 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 24 Sep 2013 10:54:51 +0300 Subject: spi: spi-topcliff-pch: fix a pci_iomap() check If pci_iomap() returns NULL, adding "PCH_ADDRESS_SIZE * plat_dev->id" can make it non-NULL which breaks the error handling. Signed-off-by: Dan Carpenter Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index eaeeed5..777c404 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -1410,13 +1410,13 @@ static int pch_spi_pd_probe(struct platform_device *plat_dev) /* baseaddress + address offset) */ data->io_base_addr = pci_resource_start(board_dat->pdev, 1) + PCH_ADDRESS_SIZE * plat_dev->id; - data->io_remap_addr = pci_iomap(board_dat->pdev, 1, 0) + - PCH_ADDRESS_SIZE * plat_dev->id; + data->io_remap_addr = pci_iomap(board_dat->pdev, 1, 0); if (!data->io_remap_addr) { dev_err(&plat_dev->dev, "%s pci_iomap failed\n", __func__); ret = -ENOMEM; goto err_pci_iomap; } + data->io_remap_addr += PCH_ADDRESS_SIZE * plat_dev->id; dev_dbg(&plat_dev->dev, "[ch%d] remap_addr=%p\n", plat_dev->id, data->io_remap_addr); -- cgit v0.10.2 From d3be689e6a07c00123786659b4429b07cf4272ac Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 24 Sep 2013 01:25:08 -0700 Subject: ASoC: rcar: remove unnecessary mach/clock.h ${LINUX}/sound/soc/sh driver can be compiled from SuperH and ARM. but, ${LINUX}/sound/soc/sh/rcar driver included SH-ARM specific header. This patch removes it Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index d80deb7..2935bbf 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -8,7 +8,6 @@ * for more details. */ #include -#include #include "rsnd.h" #define CLKA 0 -- cgit v0.10.2 From 356d86e24850cdc353602b90be73e627f86707c7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 23 Sep 2013 17:22:17 +0100 Subject: ASoC: max98088: Fix indentation Tested-by: Dylan Reid Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index 566a367..391f669 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -38,20 +38,20 @@ struct max98088_cdata { }; struct max98088_priv { - enum max98088_type devtype; - struct max98088_pdata *pdata; - unsigned int sysclk; - struct max98088_cdata dai[2]; - int eq_textcnt; - const char **eq_texts; - struct soc_enum eq_enum; - u8 ina_state; - u8 inb_state; - unsigned int ex_mode; - unsigned int digmic; - unsigned int mic1pre; - unsigned int mic2pre; - unsigned int extmic_mode; + enum max98088_type devtype; + struct max98088_pdata *pdata; + unsigned int sysclk; + struct max98088_cdata dai[2]; + int eq_textcnt; + const char **eq_texts; + struct soc_enum eq_enum; + u8 ina_state; + u8 inb_state; + unsigned int ex_mode; + unsigned int digmic; + unsigned int mic1pre; + unsigned int mic2pre; + unsigned int extmic_mode; }; static const u8 max98088_reg[M98088_REG_CNT] = { @@ -2066,15 +2066,15 @@ static int max98088_remove(struct snd_soc_codec *codec) } static struct snd_soc_codec_driver soc_codec_dev_max98088 = { - .probe = max98088_probe, - .remove = max98088_remove, - .suspend = max98088_suspend, - .resume = max98088_resume, - .set_bias_level = max98088_set_bias_level, - .reg_cache_size = ARRAY_SIZE(max98088_reg), - .reg_word_size = sizeof(u8), - .reg_cache_default = max98088_reg, - .volatile_register = max98088_volatile_register, + .probe = max98088_probe, + .remove = max98088_remove, + .suspend = max98088_suspend, + .resume = max98088_resume, + .set_bias_level = max98088_set_bias_level, + .reg_cache_size = ARRAY_SIZE(max98088_reg), + .reg_word_size = sizeof(u8), + .reg_cache_default = max98088_reg, + .volatile_register = max98088_volatile_register, .dapm_widgets = max98088_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(max98088_dapm_widgets), .dapm_routes = max98088_audio_map, -- cgit v0.10.2 From ad65adf4a3039ecd93d4712ac6524dbd9e0e848a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 23 Sep 2013 17:54:02 +0100 Subject: ASoC: max98088: Use table based control init Tested-by: Dylan Reid Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index 391f669..8896d5e 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -2048,9 +2048,6 @@ static int max98088_probe(struct snd_soc_codec *codec) max98088_handle_pdata(codec); - snd_soc_add_codec_controls(codec, max98088_snd_controls, - ARRAY_SIZE(max98088_snd_controls)); - err_access: return ret; } @@ -2071,6 +2068,8 @@ static struct snd_soc_codec_driver soc_codec_dev_max98088 = { .suspend = max98088_suspend, .resume = max98088_resume, .set_bias_level = max98088_set_bias_level, + .controls = max98088_snd_controls, + .num_controls = ARRAY_SIZE(max98088_snd_controls), .reg_cache_size = ARRAY_SIZE(max98088_reg), .reg_word_size = sizeof(u8), .reg_cache_default = max98088_reg, -- cgit v0.10.2 From 4127d5d59f8135e3c187b8daa2540691761938ce Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 23 Sep 2013 17:56:17 +0100 Subject: ASoC: max98088: Convert to direct regmap API usage This saves code and moves us towards removing the redundant register I/O implementation in ASoC. Tested-by: Dylan Reid Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index 8896d5e..31912d5 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,7 @@ struct max98088_cdata { }; struct max98088_priv { + struct regmap *regmap; enum max98088_type devtype; struct max98088_pdata *pdata; unsigned int sysclk; @@ -54,278 +56,206 @@ struct max98088_priv { unsigned int extmic_mode; }; -static const u8 max98088_reg[M98088_REG_CNT] = { - 0x00, /* 00 IRQ status */ - 0x00, /* 01 MIC status */ - 0x00, /* 02 jack status */ - 0x00, /* 03 battery voltage */ - 0x00, /* 04 */ - 0x00, /* 05 */ - 0x00, /* 06 */ - 0x00, /* 07 */ - 0x00, /* 08 */ - 0x00, /* 09 */ - 0x00, /* 0A */ - 0x00, /* 0B */ - 0x00, /* 0C */ - 0x00, /* 0D */ - 0x00, /* 0E */ - 0x00, /* 0F interrupt enable */ - - 0x00, /* 10 master clock */ - 0x00, /* 11 DAI1 clock mode */ - 0x00, /* 12 DAI1 clock control */ - 0x00, /* 13 DAI1 clock control */ - 0x00, /* 14 DAI1 format */ - 0x00, /* 15 DAI1 clock */ - 0x00, /* 16 DAI1 config */ - 0x00, /* 17 DAI1 TDM */ - 0x00, /* 18 DAI1 filters */ - 0x00, /* 19 DAI2 clock mode */ - 0x00, /* 1A DAI2 clock control */ - 0x00, /* 1B DAI2 clock control */ - 0x00, /* 1C DAI2 format */ - 0x00, /* 1D DAI2 clock */ - 0x00, /* 1E DAI2 config */ - 0x00, /* 1F DAI2 TDM */ - - 0x00, /* 20 DAI2 filters */ - 0x00, /* 21 data config */ - 0x00, /* 22 DAC mixer */ - 0x00, /* 23 left ADC mixer */ - 0x00, /* 24 right ADC mixer */ - 0x00, /* 25 left HP mixer */ - 0x00, /* 26 right HP mixer */ - 0x00, /* 27 HP control */ - 0x00, /* 28 left REC mixer */ - 0x00, /* 29 right REC mixer */ - 0x00, /* 2A REC control */ - 0x00, /* 2B left SPK mixer */ - 0x00, /* 2C right SPK mixer */ - 0x00, /* 2D SPK control */ - 0x00, /* 2E sidetone */ - 0x00, /* 2F DAI1 playback level */ - - 0x00, /* 30 DAI1 playback level */ - 0x00, /* 31 DAI2 playback level */ - 0x00, /* 32 DAI2 playbakc level */ - 0x00, /* 33 left ADC level */ - 0x00, /* 34 right ADC level */ - 0x00, /* 35 MIC1 level */ - 0x00, /* 36 MIC2 level */ - 0x00, /* 37 INA level */ - 0x00, /* 38 INB level */ - 0x00, /* 39 left HP volume */ - 0x00, /* 3A right HP volume */ - 0x00, /* 3B left REC volume */ - 0x00, /* 3C right REC volume */ - 0x00, /* 3D left SPK volume */ - 0x00, /* 3E right SPK volume */ - 0x00, /* 3F MIC config */ - - 0x00, /* 40 MIC threshold */ - 0x00, /* 41 excursion limiter filter */ - 0x00, /* 42 excursion limiter threshold */ - 0x00, /* 43 ALC */ - 0x00, /* 44 power limiter threshold */ - 0x00, /* 45 power limiter config */ - 0x00, /* 46 distortion limiter config */ - 0x00, /* 47 audio input */ - 0x00, /* 48 microphone */ - 0x00, /* 49 level control */ - 0x00, /* 4A bypass switches */ - 0x00, /* 4B jack detect */ - 0x00, /* 4C input enable */ - 0x00, /* 4D output enable */ - 0xF0, /* 4E bias control */ - 0x00, /* 4F DAC power */ - - 0x0F, /* 50 DAC power */ - 0x00, /* 51 system */ - 0x00, /* 52 DAI1 EQ1 */ - 0x00, /* 53 DAI1 EQ1 */ - 0x00, /* 54 DAI1 EQ1 */ - 0x00, /* 55 DAI1 EQ1 */ - 0x00, /* 56 DAI1 EQ1 */ - 0x00, /* 57 DAI1 EQ1 */ - 0x00, /* 58 DAI1 EQ1 */ - 0x00, /* 59 DAI1 EQ1 */ - 0x00, /* 5A DAI1 EQ1 */ - 0x00, /* 5B DAI1 EQ1 */ - 0x00, /* 5C DAI1 EQ2 */ - 0x00, /* 5D DAI1 EQ2 */ - 0x00, /* 5E DAI1 EQ2 */ - 0x00, /* 5F DAI1 EQ2 */ - - 0x00, /* 60 DAI1 EQ2 */ - 0x00, /* 61 DAI1 EQ2 */ - 0x00, /* 62 DAI1 EQ2 */ - 0x00, /* 63 DAI1 EQ2 */ - 0x00, /* 64 DAI1 EQ2 */ - 0x00, /* 65 DAI1 EQ2 */ - 0x00, /* 66 DAI1 EQ3 */ - 0x00, /* 67 DAI1 EQ3 */ - 0x00, /* 68 DAI1 EQ3 */ - 0x00, /* 69 DAI1 EQ3 */ - 0x00, /* 6A DAI1 EQ3 */ - 0x00, /* 6B DAI1 EQ3 */ - 0x00, /* 6C DAI1 EQ3 */ - 0x00, /* 6D DAI1 EQ3 */ - 0x00, /* 6E DAI1 EQ3 */ - 0x00, /* 6F DAI1 EQ3 */ - - 0x00, /* 70 DAI1 EQ4 */ - 0x00, /* 71 DAI1 EQ4 */ - 0x00, /* 72 DAI1 EQ4 */ - 0x00, /* 73 DAI1 EQ4 */ - 0x00, /* 74 DAI1 EQ4 */ - 0x00, /* 75 DAI1 EQ4 */ - 0x00, /* 76 DAI1 EQ4 */ - 0x00, /* 77 DAI1 EQ4 */ - 0x00, /* 78 DAI1 EQ4 */ - 0x00, /* 79 DAI1 EQ4 */ - 0x00, /* 7A DAI1 EQ5 */ - 0x00, /* 7B DAI1 EQ5 */ - 0x00, /* 7C DAI1 EQ5 */ - 0x00, /* 7D DAI1 EQ5 */ - 0x00, /* 7E DAI1 EQ5 */ - 0x00, /* 7F DAI1 EQ5 */ - - 0x00, /* 80 DAI1 EQ5 */ - 0x00, /* 81 DAI1 EQ5 */ - 0x00, /* 82 DAI1 EQ5 */ - 0x00, /* 83 DAI1 EQ5 */ - 0x00, /* 84 DAI2 EQ1 */ - 0x00, /* 85 DAI2 EQ1 */ - 0x00, /* 86 DAI2 EQ1 */ - 0x00, /* 87 DAI2 EQ1 */ - 0x00, /* 88 DAI2 EQ1 */ - 0x00, /* 89 DAI2 EQ1 */ - 0x00, /* 8A DAI2 EQ1 */ - 0x00, /* 8B DAI2 EQ1 */ - 0x00, /* 8C DAI2 EQ1 */ - 0x00, /* 8D DAI2 EQ1 */ - 0x00, /* 8E DAI2 EQ2 */ - 0x00, /* 8F DAI2 EQ2 */ - - 0x00, /* 90 DAI2 EQ2 */ - 0x00, /* 91 DAI2 EQ2 */ - 0x00, /* 92 DAI2 EQ2 */ - 0x00, /* 93 DAI2 EQ2 */ - 0x00, /* 94 DAI2 EQ2 */ - 0x00, /* 95 DAI2 EQ2 */ - 0x00, /* 96 DAI2 EQ2 */ - 0x00, /* 97 DAI2 EQ2 */ - 0x00, /* 98 DAI2 EQ3 */ - 0x00, /* 99 DAI2 EQ3 */ - 0x00, /* 9A DAI2 EQ3 */ - 0x00, /* 9B DAI2 EQ3 */ - 0x00, /* 9C DAI2 EQ3 */ - 0x00, /* 9D DAI2 EQ3 */ - 0x00, /* 9E DAI2 EQ3 */ - 0x00, /* 9F DAI2 EQ3 */ - - 0x00, /* A0 DAI2 EQ3 */ - 0x00, /* A1 DAI2 EQ3 */ - 0x00, /* A2 DAI2 EQ4 */ - 0x00, /* A3 DAI2 EQ4 */ - 0x00, /* A4 DAI2 EQ4 */ - 0x00, /* A5 DAI2 EQ4 */ - 0x00, /* A6 DAI2 EQ4 */ - 0x00, /* A7 DAI2 EQ4 */ - 0x00, /* A8 DAI2 EQ4 */ - 0x00, /* A9 DAI2 EQ4 */ - 0x00, /* AA DAI2 EQ4 */ - 0x00, /* AB DAI2 EQ4 */ - 0x00, /* AC DAI2 EQ5 */ - 0x00, /* AD DAI2 EQ5 */ - 0x00, /* AE DAI2 EQ5 */ - 0x00, /* AF DAI2 EQ5 */ - - 0x00, /* B0 DAI2 EQ5 */ - 0x00, /* B1 DAI2 EQ5 */ - 0x00, /* B2 DAI2 EQ5 */ - 0x00, /* B3 DAI2 EQ5 */ - 0x00, /* B4 DAI2 EQ5 */ - 0x00, /* B5 DAI2 EQ5 */ - 0x00, /* B6 DAI1 biquad */ - 0x00, /* B7 DAI1 biquad */ - 0x00, /* B8 DAI1 biquad */ - 0x00, /* B9 DAI1 biquad */ - 0x00, /* BA DAI1 biquad */ - 0x00, /* BB DAI1 biquad */ - 0x00, /* BC DAI1 biquad */ - 0x00, /* BD DAI1 biquad */ - 0x00, /* BE DAI1 biquad */ - 0x00, /* BF DAI1 biquad */ - - 0x00, /* C0 DAI2 biquad */ - 0x00, /* C1 DAI2 biquad */ - 0x00, /* C2 DAI2 biquad */ - 0x00, /* C3 DAI2 biquad */ - 0x00, /* C4 DAI2 biquad */ - 0x00, /* C5 DAI2 biquad */ - 0x00, /* C6 DAI2 biquad */ - 0x00, /* C7 DAI2 biquad */ - 0x00, /* C8 DAI2 biquad */ - 0x00, /* C9 DAI2 biquad */ - 0x00, /* CA */ - 0x00, /* CB */ - 0x00, /* CC */ - 0x00, /* CD */ - 0x00, /* CE */ - 0x00, /* CF */ - - 0x00, /* D0 */ - 0x00, /* D1 */ - 0x00, /* D2 */ - 0x00, /* D3 */ - 0x00, /* D4 */ - 0x00, /* D5 */ - 0x00, /* D6 */ - 0x00, /* D7 */ - 0x00, /* D8 */ - 0x00, /* D9 */ - 0x00, /* DA */ - 0x70, /* DB */ - 0x00, /* DC */ - 0x00, /* DD */ - 0x00, /* DE */ - 0x00, /* DF */ - - 0x00, /* E0 */ - 0x00, /* E1 */ - 0x00, /* E2 */ - 0x00, /* E3 */ - 0x00, /* E4 */ - 0x00, /* E5 */ - 0x00, /* E6 */ - 0x00, /* E7 */ - 0x00, /* E8 */ - 0x00, /* E9 */ - 0x00, /* EA */ - 0x00, /* EB */ - 0x00, /* EC */ - 0x00, /* ED */ - 0x00, /* EE */ - 0x00, /* EF */ - - 0x00, /* F0 */ - 0x00, /* F1 */ - 0x00, /* F2 */ - 0x00, /* F3 */ - 0x00, /* F4 */ - 0x00, /* F5 */ - 0x00, /* F6 */ - 0x00, /* F7 */ - 0x00, /* F8 */ - 0x00, /* F9 */ - 0x00, /* FA */ - 0x00, /* FB */ - 0x00, /* FC */ - 0x00, /* FD */ - 0x00, /* FE */ - 0x00, /* FF */ +static const struct reg_default max98088_reg[] = { + { 0xf, 0x00 }, /* 0F interrupt enable */ + + { 0x10, 0x00 }, /* 10 master clock */ + { 0x11, 0x00 }, /* 11 DAI1 clock mode */ + { 0x12, 0x00 }, /* 12 DAI1 clock control */ + { 0x13, 0x00 }, /* 13 DAI1 clock control */ + { 0x14, 0x00 }, /* 14 DAI1 format */ + { 0x15, 0x00 }, /* 15 DAI1 clock */ + { 0x16, 0x00 }, /* 16 DAI1 config */ + { 0x17, 0x00 }, /* 17 DAI1 TDM */ + { 0x18, 0x00 }, /* 18 DAI1 filters */ + { 0x19, 0x00 }, /* 19 DAI2 clock mode */ + { 0x1a, 0x00 }, /* 1A DAI2 clock control */ + { 0x1b, 0x00 }, /* 1B DAI2 clock control */ + { 0x1c, 0x00 }, /* 1C DAI2 format */ + { 0x1d, 0x00 }, /* 1D DAI2 clock */ + { 0x1e, 0x00 }, /* 1E DAI2 config */ + { 0x1f, 0x00 }, /* 1F DAI2 TDM */ + + { 0x20, 0x00 }, /* 20 DAI2 filters */ + { 0x21, 0x00 }, /* 21 data config */ + { 0x22, 0x00 }, /* 22 DAC mixer */ + { 0x23, 0x00 }, /* 23 left ADC mixer */ + { 0x24, 0x00 }, /* 24 right ADC mixer */ + { 0x25, 0x00 }, /* 25 left HP mixer */ + { 0x26, 0x00 }, /* 26 right HP mixer */ + { 0x27, 0x00 }, /* 27 HP control */ + { 0x28, 0x00 }, /* 28 left REC mixer */ + { 0x29, 0x00 }, /* 29 right REC mixer */ + { 0x2a, 0x00 }, /* 2A REC control */ + { 0x2b, 0x00 }, /* 2B left SPK mixer */ + { 0x2c, 0x00 }, /* 2C right SPK mixer */ + { 0x2d, 0x00 }, /* 2D SPK control */ + { 0x2e, 0x00 }, /* 2E sidetone */ + { 0x2f, 0x00 }, /* 2F DAI1 playback level */ + + { 0x30, 0x00 }, /* 30 DAI1 playback level */ + { 0x31, 0x00 }, /* 31 DAI2 playback level */ + { 0x32, 0x00 }, /* 32 DAI2 playbakc level */ + { 0x33, 0x00 }, /* 33 left ADC level */ + { 0x34, 0x00 }, /* 34 right ADC level */ + { 0x35, 0x00 }, /* 35 MIC1 level */ + { 0x36, 0x00 }, /* 36 MIC2 level */ + { 0x37, 0x00 }, /* 37 INA level */ + { 0x38, 0x00 }, /* 38 INB level */ + { 0x39, 0x00 }, /* 39 left HP volume */ + { 0x3a, 0x00 }, /* 3A right HP volume */ + { 0x3b, 0x00 }, /* 3B left REC volume */ + { 0x3c, 0x00 }, /* 3C right REC volume */ + { 0x3d, 0x00 }, /* 3D left SPK volume */ + { 0x3e, 0x00 }, /* 3E right SPK volume */ + { 0x3f, 0x00 }, /* 3F MIC config */ + + { 0x40, 0x00 }, /* 40 MIC threshold */ + { 0x41, 0x00 }, /* 41 excursion limiter filter */ + { 0x42, 0x00 }, /* 42 excursion limiter threshold */ + { 0x43, 0x00 }, /* 43 ALC */ + { 0x44, 0x00 }, /* 44 power limiter threshold */ + { 0x45, 0x00 }, /* 45 power limiter config */ + { 0x46, 0x00 }, /* 46 distortion limiter config */ + { 0x47, 0x00 }, /* 47 audio input */ + { 0x48, 0x00 }, /* 48 microphone */ + { 0x49, 0x00 }, /* 49 level control */ + { 0x4a, 0x00 }, /* 4A bypass switches */ + { 0x4b, 0x00 }, /* 4B jack detect */ + { 0x4c, 0x00 }, /* 4C input enable */ + { 0x4d, 0x00 }, /* 4D output enable */ + { 0x4e, 0xF0 }, /* 4E bias control */ + { 0x4f, 0x00 }, /* 4F DAC power */ + + { 0x50, 0x0F }, /* 50 DAC power */ + { 0x51, 0x00 }, /* 51 system */ + { 0x52, 0x00 }, /* 52 DAI1 EQ1 */ + { 0x53, 0x00 }, /* 53 DAI1 EQ1 */ + { 0x54, 0x00 }, /* 54 DAI1 EQ1 */ + { 0x55, 0x00 }, /* 55 DAI1 EQ1 */ + { 0x56, 0x00 }, /* 56 DAI1 EQ1 */ + { 0x57, 0x00 }, /* 57 DAI1 EQ1 */ + { 0x58, 0x00 }, /* 58 DAI1 EQ1 */ + { 0x59, 0x00 }, /* 59 DAI1 EQ1 */ + { 0x5a, 0x00 }, /* 5A DAI1 EQ1 */ + { 0x5b, 0x00 }, /* 5B DAI1 EQ1 */ + { 0x5c, 0x00 }, /* 5C DAI1 EQ2 */ + { 0x5d, 0x00 }, /* 5D DAI1 EQ2 */ + { 0x5e, 0x00 }, /* 5E DAI1 EQ2 */ + { 0x5f, 0x00 }, /* 5F DAI1 EQ2 */ + + { 0x60, 0x00 }, /* 60 DAI1 EQ2 */ + { 0x61, 0x00 }, /* 61 DAI1 EQ2 */ + { 0x62, 0x00 }, /* 62 DAI1 EQ2 */ + { 0x63, 0x00 }, /* 63 DAI1 EQ2 */ + { 0x64, 0x00 }, /* 64 DAI1 EQ2 */ + { 0x65, 0x00 }, /* 65 DAI1 EQ2 */ + { 0x66, 0x00 }, /* 66 DAI1 EQ3 */ + { 0x67, 0x00 }, /* 67 DAI1 EQ3 */ + { 0x68, 0x00 }, /* 68 DAI1 EQ3 */ + { 0x69, 0x00 }, /* 69 DAI1 EQ3 */ + { 0x6a, 0x00 }, /* 6A DAI1 EQ3 */ + { 0x6b, 0x00 }, /* 6B DAI1 EQ3 */ + { 0x6c, 0x00 }, /* 6C DAI1 EQ3 */ + { 0x6d, 0x00 }, /* 6D DAI1 EQ3 */ + { 0x6e, 0x00 }, /* 6E DAI1 EQ3 */ + { 0x6f, 0x00 }, /* 6F DAI1 EQ3 */ + + { 0x70, 0x00 }, /* 70 DAI1 EQ4 */ + { 0x71, 0x00 }, /* 71 DAI1 EQ4 */ + { 0x72, 0x00 }, /* 72 DAI1 EQ4 */ + { 0x73, 0x00 }, /* 73 DAI1 EQ4 */ + { 0x74, 0x00 }, /* 74 DAI1 EQ4 */ + { 0x75, 0x00 }, /* 75 DAI1 EQ4 */ + { 0x76, 0x00 }, /* 76 DAI1 EQ4 */ + { 0x77, 0x00 }, /* 77 DAI1 EQ4 */ + { 0x78, 0x00 }, /* 78 DAI1 EQ4 */ + { 0x79, 0x00 }, /* 79 DAI1 EQ4 */ + { 0x7a, 0x00 }, /* 7A DAI1 EQ5 */ + { 0x7b, 0x00 }, /* 7B DAI1 EQ5 */ + { 0x7c, 0x00 }, /* 7C DAI1 EQ5 */ + { 0x7d, 0x00 }, /* 7D DAI1 EQ5 */ + { 0x7e, 0x00 }, /* 7E DAI1 EQ5 */ + { 0x7f, 0x00 }, /* 7F DAI1 EQ5 */ + + { 0x80, 0x00 }, /* 80 DAI1 EQ5 */ + { 0x81, 0x00 }, /* 81 DAI1 EQ5 */ + { 0x82, 0x00 }, /* 82 DAI1 EQ5 */ + { 0x83, 0x00 }, /* 83 DAI1 EQ5 */ + { 0x84, 0x00 }, /* 84 DAI2 EQ1 */ + { 0x85, 0x00 }, /* 85 DAI2 EQ1 */ + { 0x86, 0x00 }, /* 86 DAI2 EQ1 */ + { 0x87, 0x00 }, /* 87 DAI2 EQ1 */ + { 0x88, 0x00 }, /* 88 DAI2 EQ1 */ + { 0x89, 0x00 }, /* 89 DAI2 EQ1 */ + { 0x8a, 0x00 }, /* 8A DAI2 EQ1 */ + { 0x8b, 0x00 }, /* 8B DAI2 EQ1 */ + { 0x8c, 0x00 }, /* 8C DAI2 EQ1 */ + { 0x8d, 0x00 }, /* 8D DAI2 EQ1 */ + { 0x8e, 0x00 }, /* 8E DAI2 EQ2 */ + { 0x8f, 0x00 }, /* 8F DAI2 EQ2 */ + + { 0x90, 0x00 }, /* 90 DAI2 EQ2 */ + { 0x91, 0x00 }, /* 91 DAI2 EQ2 */ + { 0x92, 0x00 }, /* 92 DAI2 EQ2 */ + { 0x93, 0x00 }, /* 93 DAI2 EQ2 */ + { 0x94, 0x00 }, /* 94 DAI2 EQ2 */ + { 0x95, 0x00 }, /* 95 DAI2 EQ2 */ + { 0x96, 0x00 }, /* 96 DAI2 EQ2 */ + { 0x97, 0x00 }, /* 97 DAI2 EQ2 */ + { 0x98, 0x00 }, /* 98 DAI2 EQ3 */ + { 0x99, 0x00 }, /* 99 DAI2 EQ3 */ + { 0x9a, 0x00 }, /* 9A DAI2 EQ3 */ + { 0x9b, 0x00 }, /* 9B DAI2 EQ3 */ + { 0x9c, 0x00 }, /* 9C DAI2 EQ3 */ + { 0x9d, 0x00 }, /* 9D DAI2 EQ3 */ + { 0x9e, 0x00 }, /* 9E DAI2 EQ3 */ + { 0x9f, 0x00 }, /* 9F DAI2 EQ3 */ + + { 0xa0, 0x00 }, /* A0 DAI2 EQ3 */ + { 0xa1, 0x00 }, /* A1 DAI2 EQ3 */ + { 0xa2, 0x00 }, /* A2 DAI2 EQ4 */ + { 0xa3, 0x00 }, /* A3 DAI2 EQ4 */ + { 0xa4, 0x00 }, /* A4 DAI2 EQ4 */ + { 0xa5, 0x00 }, /* A5 DAI2 EQ4 */ + { 0xa6, 0x00 }, /* A6 DAI2 EQ4 */ + { 0xa7, 0x00 }, /* A7 DAI2 EQ4 */ + { 0xa8, 0x00 }, /* A8 DAI2 EQ4 */ + { 0xa9, 0x00 }, /* A9 DAI2 EQ4 */ + { 0xaa, 0x00 }, /* AA DAI2 EQ4 */ + { 0xab, 0x00 }, /* AB DAI2 EQ4 */ + { 0xac, 0x00 }, /* AC DAI2 EQ5 */ + { 0xad, 0x00 }, /* AD DAI2 EQ5 */ + { 0xae, 0x00 }, /* AE DAI2 EQ5 */ + { 0xaf, 0x00 }, /* AF DAI2 EQ5 */ + + { 0xb0, 0x00 }, /* B0 DAI2 EQ5 */ + { 0xb1, 0x00 }, /* B1 DAI2 EQ5 */ + { 0xb2, 0x00 }, /* B2 DAI2 EQ5 */ + { 0xb3, 0x00 }, /* B3 DAI2 EQ5 */ + { 0xb4, 0x00 }, /* B4 DAI2 EQ5 */ + { 0xb5, 0x00 }, /* B5 DAI2 EQ5 */ + { 0xb6, 0x00 }, /* B6 DAI1 biquad */ + { 0xb7, 0x00 }, /* B7 DAI1 biquad */ + { 0xb8 ,0x00 }, /* B8 DAI1 biquad */ + { 0xb9, 0x00 }, /* B9 DAI1 biquad */ + { 0xba, 0x00 }, /* BA DAI1 biquad */ + { 0xbb, 0x00 }, /* BB DAI1 biquad */ + { 0xbc, 0x00 }, /* BC DAI1 biquad */ + { 0xbd, 0x00 }, /* BD DAI1 biquad */ + { 0xbe, 0x00 }, /* BE DAI1 biquad */ + { 0xbf, 0x00 }, /* BF DAI1 biquad */ + + { 0xc0, 0x00 }, /* C0 DAI2 biquad */ + { 0xc1, 0x00 }, /* C1 DAI2 biquad */ + { 0xc2, 0x00 }, /* C2 DAI2 biquad */ + { 0xc3, 0x00 }, /* C3 DAI2 biquad */ + { 0xc4, 0x00 }, /* C4 DAI2 biquad */ + { 0xc5, 0x00 }, /* C5 DAI2 biquad */ + { 0xc6, 0x00 }, /* C6 DAI2 biquad */ + { 0xc7, 0x00 }, /* C7 DAI2 biquad */ + { 0xc8, 0x00 }, /* C8 DAI2 biquad */ + { 0xc9, 0x00 }, /* C9 DAI2 biquad */ }; static struct { @@ -606,11 +536,27 @@ static struct { { 0xFF, 0x00, 1 }, /* FF */ }; -static int max98088_volatile_register(struct snd_soc_codec *codec, unsigned int reg) +static bool max98088_readable_register(struct device *dev, unsigned int reg) +{ + return max98088_access[reg].readable; +} + +static bool max98088_volatile_register(struct device *dev, unsigned int reg) { return max98088_access[reg].vol; } +static const struct regmap_config max98088_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .readable_reg = max98088_readable_register, + .volatile_reg = max98088_volatile_register, + + .reg_defaults = max98088_reg, + .num_reg_defaults = ARRAY_SIZE(max98088_reg), + .cache_type = REGCACHE_RBTREE, +}; /* * Load equalizer DSP coefficient configurations registers @@ -1610,58 +1556,34 @@ static int max98088_dai2_digital_mute(struct snd_soc_dai *codec_dai, int mute) return 0; } -static void max98088_sync_cache(struct snd_soc_codec *codec) -{ - u8 *reg_cache = codec->reg_cache; - int i; - - if (!codec->cache_sync) - return; - - codec->cache_only = 0; - - /* write back cached values if they're writeable and - * different from the hardware default. - */ - for (i = 1; i < codec->driver->reg_cache_size; i++) { - if (!max98088_access[i].writable) - continue; - - if (reg_cache[i] == max98088_reg[i]) - continue; - - snd_soc_write(codec, i, reg_cache[i]); - } - - codec->cache_sync = 0; -} - static int max98088_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - switch (level) { - case SND_SOC_BIAS_ON: - break; - - case SND_SOC_BIAS_PREPARE: - break; - - case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) - max98088_sync_cache(codec); - - snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN, - M98088_MBEN, M98088_MBEN); - break; - - case SND_SOC_BIAS_OFF: - snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN, - M98088_MBEN, 0); - codec->cache_sync = 1; - break; - } - codec->dapm.bias_level = level; - return 0; + struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + regcache_sync(max98088->regmap); + + snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN, + M98088_MBEN, M98088_MBEN); + break; + + case SND_SOC_BIAS_OFF: + snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN, + M98088_MBEN, 0); + regcache_mark_dirty(max98088->regmap); + break; + } + codec->dapm.bias_level = level; + return 0; } #define MAX98088_RATES SNDRV_PCM_RATE_8000_96000 @@ -1988,9 +1910,9 @@ static int max98088_probe(struct snd_soc_codec *codec) struct max98088_cdata *cdata; int ret = 0; - codec->cache_sync = 1; + regcache_mark_dirty(max98088->regmap); - ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C); + ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP); if (ret != 0) { dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); return ret; @@ -2070,10 +1992,6 @@ static struct snd_soc_codec_driver soc_codec_dev_max98088 = { .set_bias_level = max98088_set_bias_level, .controls = max98088_snd_controls, .num_controls = ARRAY_SIZE(max98088_snd_controls), - .reg_cache_size = ARRAY_SIZE(max98088_reg), - .reg_word_size = sizeof(u8), - .reg_cache_default = max98088_reg, - .volatile_register = max98088_volatile_register, .dapm_widgets = max98088_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(max98088_dapm_widgets), .dapm_routes = max98088_audio_map, @@ -2081,7 +1999,7 @@ static struct snd_soc_codec_driver soc_codec_dev_max98088 = { }; static int max98088_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { struct max98088_priv *max98088; int ret; @@ -2091,6 +2009,10 @@ static int max98088_i2c_probe(struct i2c_client *i2c, if (max98088 == NULL) return -ENOMEM; + max98088->regmap = devm_regmap_init_i2c(i2c, &max98088_regmap); + if (IS_ERR(max98088->regmap)) + return PTR_ERR(max98088->regmap); + max98088->devtype = id->driver_data; i2c_set_clientdata(i2c, max98088); -- cgit v0.10.2 From 3ad8219a50eab201abf89b25d7797d6695b73e4e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 26 Aug 2013 01:51:22 -0700 Subject: sh-pfc: r8a7778: Add SRU/SSI pin support Signed-off-by: Kuninori Morimoto Signed-off-by: Laurent Pinchart diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7778.c b/drivers/pinctrl/sh-pfc/pfc-r8a7778.c index 428d2a6..20b1d0d 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7778.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7778.c @@ -1288,6 +1288,22 @@ static struct sh_pfc_pin pinmux_pins[] = { arg5##_MARK, arg6##_MARK, \ arg7##_MARK, arg8##_MARK, } +/* - AUDIO macro -------------------------------------------------------------*/ +#define AUDIO_PFC_PIN(name, pin) SH_PFC_PINS(name, pin) +#define AUDIO_PFC_DAT(name, pin) SH_PFC_MUX1(name, pin) + +/* - AUDIO clock -------------------------------------------------------------*/ +AUDIO_PFC_PIN(audio_clk_a, RCAR_GP_PIN(2, 22)); +AUDIO_PFC_DAT(audio_clk_a, AUDIO_CLKA); +AUDIO_PFC_PIN(audio_clk_b, RCAR_GP_PIN(2, 23)); +AUDIO_PFC_DAT(audio_clk_b, AUDIO_CLKB); +AUDIO_PFC_PIN(audio_clk_c, RCAR_GP_PIN(2, 7)); +AUDIO_PFC_DAT(audio_clk_c, AUDIO_CLKC); +AUDIO_PFC_PIN(audio_clkout_a, RCAR_GP_PIN(2, 16)); +AUDIO_PFC_DAT(audio_clkout_a, AUDIO_CLKOUT_A); +AUDIO_PFC_PIN(audio_clkout_b, RCAR_GP_PIN(1, 16)); +AUDIO_PFC_DAT(audio_clkout_b, AUDIO_CLKOUT_B); + /* - Ether ------------------------------------------------------------------ */ SH_PFC_PINS(ether_rmii, RCAR_GP_PIN(4, 10), RCAR_GP_PIN(4, 11), RCAR_GP_PIN(4, 13), RCAR_GP_PIN(4, 9), @@ -1577,6 +1593,59 @@ SDHI_PFC_WPPN(sdhi2_wp_a, SD2_WP_A); SDHI_PFC_PINS(sdhi2_wp_b, RCAR_GP_PIN(3, 28)); SDHI_PFC_WPPN(sdhi2_wp_b, SD2_WP_B); +/* - SSI macro -------------------------------------------------------------- */ +#define SSI_PFC_PINS(name, args...) SH_PFC_PINS(name, args) +#define SSI_PFC_CTRL(name, sck, ws) SH_PFC_MUX2(name, sck, ws) +#define SSI_PFC_DATA(name, d) SH_PFC_MUX1(name, d) + +/* - SSI 0/1/2 -------------------------------------------------------------- */ +SSI_PFC_PINS(ssi012_ctrl, RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7)); +SSI_PFC_CTRL(ssi012_ctrl, SSI_SCK012, SSI_WS012); +SSI_PFC_PINS(ssi0_data, RCAR_GP_PIN(3, 10)); +SSI_PFC_DATA(ssi0_data, SSI_SDATA0); +SSI_PFC_PINS(ssi1_a_ctrl, RCAR_GP_PIN(2, 20), RCAR_GP_PIN(2, 21)); +SSI_PFC_CTRL(ssi1_a_ctrl, SSI_SCK1_A, SSI_WS1_A); +SSI_PFC_PINS(ssi1_b_ctrl, PIN_NUMBER(3, 20), RCAR_GP_PIN(1, 3)); +SSI_PFC_CTRL(ssi1_b_ctrl, SSI_SCK1_B, SSI_WS1_B); +SSI_PFC_PINS(ssi1_data, RCAR_GP_PIN(3, 9)); +SSI_PFC_DATA(ssi1_data, SSI_SDATA1); +SSI_PFC_PINS(ssi2_a_ctrl, RCAR_GP_PIN(2, 26), RCAR_GP_PIN(3, 4)); +SSI_PFC_CTRL(ssi2_a_ctrl, SSI_SCK2_A, SSI_WS2_A); +SSI_PFC_PINS(ssi2_b_ctrl, RCAR_GP_PIN(2, 6), RCAR_GP_PIN(2, 17)); +SSI_PFC_CTRL(ssi2_b_ctrl, SSI_SCK2_B, SSI_WS2_B); +SSI_PFC_PINS(ssi2_data, RCAR_GP_PIN(3, 8)); +SSI_PFC_DATA(ssi2_data, SSI_SDATA2); + +/* - SSI 3/4 ---------------------------------------------------------------- */ +SSI_PFC_PINS(ssi34_ctrl, RCAR_GP_PIN(3, 2), RCAR_GP_PIN(3, 3)); +SSI_PFC_CTRL(ssi34_ctrl, SSI_SCK34, SSI_WS34); +SSI_PFC_PINS(ssi3_data, RCAR_GP_PIN(3, 5)); +SSI_PFC_DATA(ssi3_data, SSI_SDATA3); +SSI_PFC_PINS(ssi4_ctrl, RCAR_GP_PIN(1, 22), RCAR_GP_PIN(1, 23)); +SSI_PFC_CTRL(ssi4_ctrl, SSI_SCK4, SSI_WS4); +SSI_PFC_PINS(ssi4_data, RCAR_GP_PIN(3, 4)); +SSI_PFC_DATA(ssi4_data, SSI_SDATA4); + +/* - SSI 5 ------------------------------------------------------------------ */ +SSI_PFC_PINS(ssi5_ctrl, RCAR_GP_PIN(2, 31), RCAR_GP_PIN(3, 0)); +SSI_PFC_CTRL(ssi5_ctrl, SSI_SCK5, SSI_WS5); +SSI_PFC_PINS(ssi5_data, RCAR_GP_PIN(3, 1)); +SSI_PFC_DATA(ssi5_data, SSI_SDATA5); + +/* - SSI 6 ------------------------------------------------------------------ */ +SSI_PFC_PINS(ssi6_ctrl, RCAR_GP_PIN(2, 28), RCAR_GP_PIN(2, 29)); +SSI_PFC_CTRL(ssi6_ctrl, SSI_SCK6, SSI_WS6); +SSI_PFC_PINS(ssi6_data, RCAR_GP_PIN(2, 30)); +SSI_PFC_DATA(ssi6_data, SSI_SDATA6); + +/* - SSI 7/8 --------------------------------------------------------------- */ +SSI_PFC_PINS(ssi78_ctrl, RCAR_GP_PIN(2, 24), RCAR_GP_PIN(2, 25)); +SSI_PFC_CTRL(ssi78_ctrl, SSI_SCK78, SSI_WS78); +SSI_PFC_PINS(ssi7_data, RCAR_GP_PIN(2, 27)); +SSI_PFC_DATA(ssi7_data, SSI_SDATA7); +SSI_PFC_PINS(ssi8_data, RCAR_GP_PIN(2, 26)); +SSI_PFC_DATA(ssi8_data, SSI_SDATA8); + /* - USB0 ------------------------------------------------------------------- */ SH_PFC_PINS(usb0, RCAR_GP_PIN(0, 1)); SH_PFC_MUX1(usb0, PENC0); @@ -1624,6 +1693,11 @@ VIN_PFC_PINS(vin1_sync, RCAR_GP_PIN(3, 21), RCAR_GP_PIN(3, 22)); VIN_PFC_SYNC(vin1_sync, VI1_HSYNC, VI1_VSYNC); static const struct sh_pfc_pin_group pinmux_groups[] = { + SH_PFC_PIN_GROUP(audio_clk_a), + SH_PFC_PIN_GROUP(audio_clk_b), + SH_PFC_PIN_GROUP(audio_clk_c), + SH_PFC_PIN_GROUP(audio_clkout_a), + SH_PFC_PIN_GROUP(audio_clkout_b), SH_PFC_PIN_GROUP(ether_rmii), SH_PFC_PIN_GROUP(ether_link), SH_PFC_PIN_GROUP(ether_magic), @@ -1713,6 +1787,25 @@ static const struct sh_pfc_pin_group pinmux_groups[] = { SH_PFC_PIN_GROUP(sdhi2_data4_b), SH_PFC_PIN_GROUP(sdhi2_wp_a), SH_PFC_PIN_GROUP(sdhi2_wp_b), + SH_PFC_PIN_GROUP(ssi012_ctrl), + SH_PFC_PIN_GROUP(ssi0_data), + SH_PFC_PIN_GROUP(ssi1_a_ctrl), + SH_PFC_PIN_GROUP(ssi1_b_ctrl), + SH_PFC_PIN_GROUP(ssi1_data), + SH_PFC_PIN_GROUP(ssi2_a_ctrl), + SH_PFC_PIN_GROUP(ssi2_b_ctrl), + SH_PFC_PIN_GROUP(ssi2_data), + SH_PFC_PIN_GROUP(ssi34_ctrl), + SH_PFC_PIN_GROUP(ssi3_data), + SH_PFC_PIN_GROUP(ssi4_ctrl), + SH_PFC_PIN_GROUP(ssi4_data), + SH_PFC_PIN_GROUP(ssi5_ctrl), + SH_PFC_PIN_GROUP(ssi5_data), + SH_PFC_PIN_GROUP(ssi6_ctrl), + SH_PFC_PIN_GROUP(ssi6_data), + SH_PFC_PIN_GROUP(ssi78_ctrl), + SH_PFC_PIN_GROUP(ssi7_data), + SH_PFC_PIN_GROUP(ssi8_data), SH_PFC_PIN_GROUP(usb0), SH_PFC_PIN_GROUP(usb0_ovc), SH_PFC_PIN_GROUP(usb1), @@ -1725,6 +1818,14 @@ static const struct sh_pfc_pin_group pinmux_groups[] = { SH_PFC_PIN_GROUP(vin1_sync), }; +static const char * const audio_clk_groups[] = { + "audio_clk_a", + "audio_clk_b", + "audio_clk_c", + "audio_clkout_a", + "audio_clkout_b", +}; + static const char * const ether_groups[] = { "ether_rmii", "ether_link", @@ -1875,6 +1976,28 @@ static const char * const sdhi2_groups[] = { "sdhi2_wp_b", }; +static const char * const ssi_groups[] = { + "ssi012_ctrl", + "ssi0_data", + "ssi1_a_ctrl", + "ssi1_b_ctrl", + "ssi1_data", + "ssi2_a_ctrl", + "ssi2_b_ctrl", + "ssi2_data", + "ssi34_ctrl", + "ssi3_data", + "ssi4_ctrl", + "ssi4_data", + "ssi5_ctrl", + "ssi5_data", + "ssi6_ctrl", + "ssi6_data", + "ssi78_ctrl", + "ssi7_data", + "ssi8_data", +}; + static const char * const usb0_groups[] = { "usb0", "usb0_ovc", @@ -1898,6 +2021,7 @@ static const char * const vin1_groups[] = { }; static const struct sh_pfc_function pinmux_functions[] = { + SH_PFC_FUNCTION(audio_clk), SH_PFC_FUNCTION(ether), SH_PFC_FUNCTION(hscif0), SH_PFC_FUNCTION(hscif1), @@ -1918,6 +2042,7 @@ static const struct sh_pfc_function pinmux_functions[] = { SH_PFC_FUNCTION(sdhi0), SH_PFC_FUNCTION(sdhi1), SH_PFC_FUNCTION(sdhi2), + SH_PFC_FUNCTION(ssi), SH_PFC_FUNCTION(usb0), SH_PFC_FUNCTION(usb1), SH_PFC_FUNCTION(vin0), -- cgit v0.10.2 From 70702bfc13c4f96f2f05d4ce2eb110cd1735fef5 Mon Sep 17 00:00:00 2001 From: Ulrich Hecht Date: Fri, 30 Aug 2013 14:37:41 +0200 Subject: sh-pfc: r8a7790: Add I2C pin groups and functions Adds pinmux for i2c bus 1 and 2. (Pins for 0 and 3 are not multiplexed.) Signed-off-by: Ulrich Hecht Signed-off-by: Laurent Pinchart diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7790.c b/drivers/pinctrl/sh-pfc/pfc-r8a7790.c index 64fcc006..5c2657b 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7790.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7790.c @@ -1990,6 +1990,64 @@ static const unsigned int hscif1_ctrl_b_pins[] = { static const unsigned int hscif1_ctrl_b_mux[] = { HRTS1_N_B_MARK, HCTS1_N_B_MARK, }; +/* - I2C1 ------------------------------------------------------------------- */ +static const unsigned int i2c1_pins[] = { + /* SCL, SDA */ + RCAR_GP_PIN(1, 16), RCAR_GP_PIN(1, 17), +}; +static const unsigned int i2c1_mux[] = { + I2C1_SCL_MARK, I2C1_SDA_MARK, +}; +static const unsigned int i2c1_b_pins[] = { + /* SCL, SDA */ + RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7), +}; +static const unsigned int i2c1_b_mux[] = { + I2C1_SCL_B_MARK, I2C1_SDA_B_MARK, +}; +static const unsigned int i2c1_c_pins[] = { + /* SCL, SDA */ + RCAR_GP_PIN(4, 30), RCAR_GP_PIN(4, 27), +}; +static const unsigned int i2c1_c_mux[] = { + I2C1_SCL_C_MARK, I2C1_SDA_C_MARK, +}; +/* - I2C2 ------------------------------------------------------------------- */ +static const unsigned int i2c2_pins[] = { + /* SCL, SDA */ + RCAR_GP_PIN(5, 5), RCAR_GP_PIN(5, 6), +}; +static const unsigned int i2c2_mux[] = { + I2C2_SCL_MARK, I2C2_SDA_MARK, +}; +static const unsigned int i2c2_b_pins[] = { + /* SCL, SDA */ + RCAR_GP_PIN(4, 0), RCAR_GP_PIN(4, 1), +}; +static const unsigned int i2c2_b_mux[] = { + I2C2_SCL_B_MARK, I2C2_SDA_B_MARK, +}; +static const unsigned int i2c2_c_pins[] = { + /* SCL, SDA */ + RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7), +}; +static const unsigned int i2c2_c_mux[] = { + I2C2_SCL_C_MARK, I2C2_SDA_C_MARK, +}; +static const unsigned int i2c2_d_pins[] = { + /* SCL, SDA */ + RCAR_GP_PIN(3, 14), RCAR_GP_PIN(3, 15), +}; +static const unsigned int i2c2_d_mux[] = { + I2C2_SCL_D_MARK, I2C2_SDA_D_MARK, +}; +static const unsigned int i2c2_e_pins[] = { + /* SCL, SDA */ + RCAR_GP_PIN(2, 18), RCAR_GP_PIN(2, 19), +}; +static const unsigned int i2c2_e_mux[] = { + I2C2_SCL_E_MARK, I2C2_SDA_E_MARK, +}; /* - INTC ------------------------------------------------------------------- */ static const unsigned int intc_irq0_pins[] = { /* IRQ */ @@ -3047,6 +3105,14 @@ static const struct sh_pfc_pin_group pinmux_groups[] = { SH_PFC_PIN_GROUP(hscif1_data_b), SH_PFC_PIN_GROUP(hscif1_clk_b), SH_PFC_PIN_GROUP(hscif1_ctrl_b), + SH_PFC_PIN_GROUP(i2c1), + SH_PFC_PIN_GROUP(i2c1_b), + SH_PFC_PIN_GROUP(i2c1_c), + SH_PFC_PIN_GROUP(i2c2), + SH_PFC_PIN_GROUP(i2c2_b), + SH_PFC_PIN_GROUP(i2c2_c), + SH_PFC_PIN_GROUP(i2c2_d), + SH_PFC_PIN_GROUP(i2c2_e), SH_PFC_PIN_GROUP(intc_irq0), SH_PFC_PIN_GROUP(intc_irq1), SH_PFC_PIN_GROUP(intc_irq2), @@ -3243,6 +3309,20 @@ static const char * const hscif1_groups[] = { "hscif1_ctrl_b", }; +static const char * const i2c1_groups[] = { + "i2c1", + "i2c1_b", + "i2c1_c", +}; + +static const char * const i2c2_groups[] = { + "i2c2", + "i2c2_b", + "i2c2_c", + "i2c2_d", + "i2c2_e", +}; + static const char * const intc_groups[] = { "intc_irq0", "intc_irq1", @@ -3469,6 +3549,8 @@ static const struct sh_pfc_function pinmux_functions[] = { SH_PFC_FUNCTION(eth), SH_PFC_FUNCTION(hscif0), SH_PFC_FUNCTION(hscif1), + SH_PFC_FUNCTION(i2c1), + SH_PFC_FUNCTION(i2c2), SH_PFC_FUNCTION(intc), SH_PFC_FUNCTION(mmc0), SH_PFC_FUNCTION(mmc1), -- cgit v0.10.2 From 2245e3c31c15c2d2a26926c4b734f4d3a37ae252 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 24 Sep 2013 11:50:10 +0100 Subject: ASoC: ab8500: Explicitly set I/O up We do some I/O in probe so we need to ensure the I/O operations are fully set up then. Reported-by: Olof Johansson Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index d5a0fc4..7f6ca11 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -2468,6 +2468,8 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec) dev_dbg(dev, "%s: Enter.\n", __func__); + snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP); + /* Setup AB8500 according to board-settings */ pdata = dev_get_platdata(dev->parent); -- cgit v0.10.2 From d36126ac5674a83e1d426877709437b24f058f47 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 23 Sep 2013 18:58:59 +0100 Subject: ASoC: max98095: Remove custom hw_write() implementation The registers that are being kept uncached are marked as volatile anyway so the call has no practical impact. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index 41cdd16..65aba5e 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -612,23 +612,6 @@ static int max98095_volatile(struct snd_soc_codec *codec, unsigned int reg) } /* - * Filter coefficients are in a separate register segment - * and they share the address space of the normal registers. - * The coefficient registers do not need or share the cache. - */ -static int max98095_hw_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - int ret; - - codec->cache_bypass = 1; - ret = snd_soc_write(codec, reg, value); - codec->cache_bypass = 0; - - return ret ? -EIO : 0; -} - -/* * Load equalizer DSP coefficient configurations registers */ static void m98095_eq_band(struct snd_soc_codec *codec, unsigned int dai, @@ -648,8 +631,8 @@ static void m98095_eq_band(struct snd_soc_codec *codec, unsigned int dai, /* Step through the registers and coefs */ for (i = 0; i < M98095_COEFS_PER_BAND; i++) { - max98095_hw_write(codec, eq_reg++, M98095_BYTE1(coefs[i])); - max98095_hw_write(codec, eq_reg++, M98095_BYTE0(coefs[i])); + snd_soc_write(codec, eq_reg++, M98095_BYTE1(coefs[i])); + snd_soc_write(codec, eq_reg++, M98095_BYTE0(coefs[i])); } } @@ -673,8 +656,8 @@ static void m98095_biquad_band(struct snd_soc_codec *codec, unsigned int dai, /* Step through the registers and coefs */ for (i = 0; i < M98095_COEFS_PER_BAND; i++) { - max98095_hw_write(codec, bq_reg++, M98095_BYTE1(coefs[i])); - max98095_hw_write(codec, bq_reg++, M98095_BYTE0(coefs[i])); + snd_soc_write(codec, bq_reg++, M98095_BYTE1(coefs[i])); + snd_soc_write(codec, bq_reg++, M98095_BYTE0(coefs[i])); } } -- cgit v0.10.2 From c6b3283f6d8818177349eb7cc0549286a55140c7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 23 Sep 2013 19:05:16 +0100 Subject: ASoC: max90895: Convert to table based control init Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index 65aba5e..1a4585a 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -1268,14 +1268,6 @@ static const struct snd_soc_dapm_route max98095_audio_map[] = { {"MIC2 Input", NULL, "MIC2"}, }; -static int max98095_add_widgets(struct snd_soc_codec *codec) -{ - snd_soc_add_codec_controls(codec, max98095_snd_controls, - ARRAY_SIZE(max98095_snd_controls)); - - return 0; -} - /* codec mclk clock divider coefficients */ static const struct { u32 rate; @@ -2430,8 +2422,6 @@ static int max98095_probe(struct snd_soc_codec *codec) snd_soc_update_bits(codec, M98095_097_PWR_SYS, M98095_SHDNRUN, M98095_SHDNRUN); - max98095_add_widgets(codec); - return 0; err_irq: @@ -2463,6 +2453,8 @@ static struct snd_soc_codec_driver soc_codec_dev_max98095 = { .suspend = max98095_suspend, .resume = max98095_resume, .set_bias_level = max98095_set_bias_level, + .controls = max98095_snd_controls, + .num_controls = ARRAY_SIZE(max98095_snd_controls), .reg_cache_size = ARRAY_SIZE(max98095_reg_def), .reg_word_size = sizeof(u8), .reg_cache_default = max98095_reg_def, -- cgit v0.10.2 From 14acbbbbc649c4c6057f601396b8000cd616d9ad Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 23 Sep 2013 19:08:35 +0100 Subject: ASoC: max98095: Convert to direct regmap API usage Saves code and moves us towards being able to remove the duplicate ASoC level register I/O functionality. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index 1a4585a..5c9f6b5 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -39,6 +39,7 @@ struct max98095_cdata { }; struct max98095_priv { + struct regmap *regmap; enum max98095_type devtype; struct max98095_pdata *pdata; unsigned int sysclk; @@ -56,263 +57,145 @@ struct max98095_priv { struct snd_soc_jack *mic_jack; }; -static const u8 max98095_reg_def[M98095_REG_CNT] = { - 0x00, /* 00 */ - 0x00, /* 01 */ - 0x00, /* 02 */ - 0x00, /* 03 */ - 0x00, /* 04 */ - 0x00, /* 05 */ - 0x00, /* 06 */ - 0x00, /* 07 */ - 0x00, /* 08 */ - 0x00, /* 09 */ - 0x00, /* 0A */ - 0x00, /* 0B */ - 0x00, /* 0C */ - 0x00, /* 0D */ - 0x00, /* 0E */ - 0x00, /* 0F */ - 0x00, /* 10 */ - 0x00, /* 11 */ - 0x00, /* 12 */ - 0x00, /* 13 */ - 0x00, /* 14 */ - 0x00, /* 15 */ - 0x00, /* 16 */ - 0x00, /* 17 */ - 0x00, /* 18 */ - 0x00, /* 19 */ - 0x00, /* 1A */ - 0x00, /* 1B */ - 0x00, /* 1C */ - 0x00, /* 1D */ - 0x00, /* 1E */ - 0x00, /* 1F */ - 0x00, /* 20 */ - 0x00, /* 21 */ - 0x00, /* 22 */ - 0x00, /* 23 */ - 0x00, /* 24 */ - 0x00, /* 25 */ - 0x00, /* 26 */ - 0x00, /* 27 */ - 0x00, /* 28 */ - 0x00, /* 29 */ - 0x00, /* 2A */ - 0x00, /* 2B */ - 0x00, /* 2C */ - 0x00, /* 2D */ - 0x00, /* 2E */ - 0x00, /* 2F */ - 0x00, /* 30 */ - 0x00, /* 31 */ - 0x00, /* 32 */ - 0x00, /* 33 */ - 0x00, /* 34 */ - 0x00, /* 35 */ - 0x00, /* 36 */ - 0x00, /* 37 */ - 0x00, /* 38 */ - 0x00, /* 39 */ - 0x00, /* 3A */ - 0x00, /* 3B */ - 0x00, /* 3C */ - 0x00, /* 3D */ - 0x00, /* 3E */ - 0x00, /* 3F */ - 0x00, /* 40 */ - 0x00, /* 41 */ - 0x00, /* 42 */ - 0x00, /* 43 */ - 0x00, /* 44 */ - 0x00, /* 45 */ - 0x00, /* 46 */ - 0x00, /* 47 */ - 0x00, /* 48 */ - 0x00, /* 49 */ - 0x00, /* 4A */ - 0x00, /* 4B */ - 0x00, /* 4C */ - 0x00, /* 4D */ - 0x00, /* 4E */ - 0x00, /* 4F */ - 0x00, /* 50 */ - 0x00, /* 51 */ - 0x00, /* 52 */ - 0x00, /* 53 */ - 0x00, /* 54 */ - 0x00, /* 55 */ - 0x00, /* 56 */ - 0x00, /* 57 */ - 0x00, /* 58 */ - 0x00, /* 59 */ - 0x00, /* 5A */ - 0x00, /* 5B */ - 0x00, /* 5C */ - 0x00, /* 5D */ - 0x00, /* 5E */ - 0x00, /* 5F */ - 0x00, /* 60 */ - 0x00, /* 61 */ - 0x00, /* 62 */ - 0x00, /* 63 */ - 0x00, /* 64 */ - 0x00, /* 65 */ - 0x00, /* 66 */ - 0x00, /* 67 */ - 0x00, /* 68 */ - 0x00, /* 69 */ - 0x00, /* 6A */ - 0x00, /* 6B */ - 0x00, /* 6C */ - 0x00, /* 6D */ - 0x00, /* 6E */ - 0x00, /* 6F */ - 0x00, /* 70 */ - 0x00, /* 71 */ - 0x00, /* 72 */ - 0x00, /* 73 */ - 0x00, /* 74 */ - 0x00, /* 75 */ - 0x00, /* 76 */ - 0x00, /* 77 */ - 0x00, /* 78 */ - 0x00, /* 79 */ - 0x00, /* 7A */ - 0x00, /* 7B */ - 0x00, /* 7C */ - 0x00, /* 7D */ - 0x00, /* 7E */ - 0x00, /* 7F */ - 0x00, /* 80 */ - 0x00, /* 81 */ - 0x00, /* 82 */ - 0x00, /* 83 */ - 0x00, /* 84 */ - 0x00, /* 85 */ - 0x00, /* 86 */ - 0x00, /* 87 */ - 0x00, /* 88 */ - 0x00, /* 89 */ - 0x00, /* 8A */ - 0x00, /* 8B */ - 0x00, /* 8C */ - 0x00, /* 8D */ - 0x00, /* 8E */ - 0x00, /* 8F */ - 0x00, /* 90 */ - 0x00, /* 91 */ - 0x30, /* 92 */ - 0xF0, /* 93 */ - 0x00, /* 94 */ - 0x00, /* 95 */ - 0x3F, /* 96 */ - 0x00, /* 97 */ - 0x00, /* 98 */ - 0x00, /* 99 */ - 0x00, /* 9A */ - 0x00, /* 9B */ - 0x00, /* 9C */ - 0x00, /* 9D */ - 0x00, /* 9E */ - 0x00, /* 9F */ - 0x00, /* A0 */ - 0x00, /* A1 */ - 0x00, /* A2 */ - 0x00, /* A3 */ - 0x00, /* A4 */ - 0x00, /* A5 */ - 0x00, /* A6 */ - 0x00, /* A7 */ - 0x00, /* A8 */ - 0x00, /* A9 */ - 0x00, /* AA */ - 0x00, /* AB */ - 0x00, /* AC */ - 0x00, /* AD */ - 0x00, /* AE */ - 0x00, /* AF */ - 0x00, /* B0 */ - 0x00, /* B1 */ - 0x00, /* B2 */ - 0x00, /* B3 */ - 0x00, /* B4 */ - 0x00, /* B5 */ - 0x00, /* B6 */ - 0x00, /* B7 */ - 0x00, /* B8 */ - 0x00, /* B9 */ - 0x00, /* BA */ - 0x00, /* BB */ - 0x00, /* BC */ - 0x00, /* BD */ - 0x00, /* BE */ - 0x00, /* BF */ - 0x00, /* C0 */ - 0x00, /* C1 */ - 0x00, /* C2 */ - 0x00, /* C3 */ - 0x00, /* C4 */ - 0x00, /* C5 */ - 0x00, /* C6 */ - 0x00, /* C7 */ - 0x00, /* C8 */ - 0x00, /* C9 */ - 0x00, /* CA */ - 0x00, /* CB */ - 0x00, /* CC */ - 0x00, /* CD */ - 0x00, /* CE */ - 0x00, /* CF */ - 0x00, /* D0 */ - 0x00, /* D1 */ - 0x00, /* D2 */ - 0x00, /* D3 */ - 0x00, /* D4 */ - 0x00, /* D5 */ - 0x00, /* D6 */ - 0x00, /* D7 */ - 0x00, /* D8 */ - 0x00, /* D9 */ - 0x00, /* DA */ - 0x00, /* DB */ - 0x00, /* DC */ - 0x00, /* DD */ - 0x00, /* DE */ - 0x00, /* DF */ - 0x00, /* E0 */ - 0x00, /* E1 */ - 0x00, /* E2 */ - 0x00, /* E3 */ - 0x00, /* E4 */ - 0x00, /* E5 */ - 0x00, /* E6 */ - 0x00, /* E7 */ - 0x00, /* E8 */ - 0x00, /* E9 */ - 0x00, /* EA */ - 0x00, /* EB */ - 0x00, /* EC */ - 0x00, /* ED */ - 0x00, /* EE */ - 0x00, /* EF */ - 0x00, /* F0 */ - 0x00, /* F1 */ - 0x00, /* F2 */ - 0x00, /* F3 */ - 0x00, /* F4 */ - 0x00, /* F5 */ - 0x00, /* F6 */ - 0x00, /* F7 */ - 0x00, /* F8 */ - 0x00, /* F9 */ - 0x00, /* FA */ - 0x00, /* FB */ - 0x00, /* FC */ - 0x00, /* FD */ - 0x00, /* FE */ - 0x00, /* FF */ +static const struct reg_default max98095_reg_def[] = { + { 0xf, 0x00 }, /* 0F */ + { 0x10, 0x00 }, /* 10 */ + { 0x11, 0x00 }, /* 11 */ + { 0x12, 0x00 }, /* 12 */ + { 0x13, 0x00 }, /* 13 */ + { 0x14, 0x00 }, /* 14 */ + { 0x15, 0x00 }, /* 15 */ + { 0x16, 0x00 }, /* 16 */ + { 0x17, 0x00 }, /* 17 */ + { 0x18, 0x00 }, /* 18 */ + { 0x19, 0x00 }, /* 19 */ + { 0x1a, 0x00 }, /* 1A */ + { 0x1b, 0x00 }, /* 1B */ + { 0x1c, 0x00 }, /* 1C */ + { 0x1d, 0x00 }, /* 1D */ + { 0x1e, 0x00 }, /* 1E */ + { 0x1f, 0x00 }, /* 1F */ + { 0x20, 0x00 }, /* 20 */ + { 0x21, 0x00 }, /* 21 */ + { 0x22, 0x00 }, /* 22 */ + { 0x23, 0x00 }, /* 23 */ + { 0x24, 0x00 }, /* 24 */ + { 0x25, 0x00 }, /* 25 */ + { 0x26, 0x00 }, /* 26 */ + { 0x27, 0x00 }, /* 27 */ + { 0x28, 0x00 }, /* 28 */ + { 0x29, 0x00 }, /* 29 */ + { 0x2a, 0x00 }, /* 2A */ + { 0x2b, 0x00 }, /* 2B */ + { 0x2c, 0x00 }, /* 2C */ + { 0x2d, 0x00 }, /* 2D */ + { 0x2e, 0x00 }, /* 2E */ + { 0x2f, 0x00 }, /* 2F */ + { 0x30, 0x00 }, /* 30 */ + { 0x31, 0x00 }, /* 31 */ + { 0x32, 0x00 }, /* 32 */ + { 0x33, 0x00 }, /* 33 */ + { 0x34, 0x00 }, /* 34 */ + { 0x35, 0x00 }, /* 35 */ + { 0x36, 0x00 }, /* 36 */ + { 0x37, 0x00 }, /* 37 */ + { 0x38, 0x00 }, /* 38 */ + { 0x39, 0x00 }, /* 39 */ + { 0x3a, 0x00 }, /* 3A */ + { 0x3b, 0x00 }, /* 3B */ + { 0x3c, 0x00 }, /* 3C */ + { 0x3d, 0x00 }, /* 3D */ + { 0x3e, 0x00 }, /* 3E */ + { 0x3f, 0x00 }, /* 3F */ + { 0x40, 0x00 }, /* 40 */ + { 0x41, 0x00 }, /* 41 */ + { 0x42, 0x00 }, /* 42 */ + { 0x43, 0x00 }, /* 43 */ + { 0x44, 0x00 }, /* 44 */ + { 0x45, 0x00 }, /* 45 */ + { 0x46, 0x00 }, /* 46 */ + { 0x47, 0x00 }, /* 47 */ + { 0x48, 0x00 }, /* 48 */ + { 0x49, 0x00 }, /* 49 */ + { 0x4a, 0x00 }, /* 4A */ + { 0x4b, 0x00 }, /* 4B */ + { 0x4c, 0x00 }, /* 4C */ + { 0x4d, 0x00 }, /* 4D */ + { 0x4e, 0x00 }, /* 4E */ + { 0x4f, 0x00 }, /* 4F */ + { 0x50, 0x00 }, /* 50 */ + { 0x51, 0x00 }, /* 51 */ + { 0x52, 0x00 }, /* 52 */ + { 0x53, 0x00 }, /* 53 */ + { 0x54, 0x00 }, /* 54 */ + { 0x55, 0x00 }, /* 55 */ + { 0x56, 0x00 }, /* 56 */ + { 0x57, 0x00 }, /* 57 */ + { 0x58, 0x00 }, /* 58 */ + { 0x59, 0x00 }, /* 59 */ + { 0x5a, 0x00 }, /* 5A */ + { 0x5b, 0x00 }, /* 5B */ + { 0x5c, 0x00 }, /* 5C */ + { 0x5d, 0x00 }, /* 5D */ + { 0x5e, 0x00 }, /* 5E */ + { 0x5f, 0x00 }, /* 5F */ + { 0x60, 0x00 }, /* 60 */ + { 0x61, 0x00 }, /* 61 */ + { 0x62, 0x00 }, /* 62 */ + { 0x63, 0x00 }, /* 63 */ + { 0x64, 0x00 }, /* 64 */ + { 0x65, 0x00 }, /* 65 */ + { 0x66, 0x00 }, /* 66 */ + { 0x67, 0x00 }, /* 67 */ + { 0x68, 0x00 }, /* 68 */ + { 0x69, 0x00 }, /* 69 */ + { 0x6a, 0x00 }, /* 6A */ + { 0x6b, 0x00 }, /* 6B */ + { 0x6c, 0x00 }, /* 6C */ + { 0x6d, 0x00 }, /* 6D */ + { 0x6e, 0x00 }, /* 6E */ + { 0x6f, 0x00 }, /* 6F */ + { 0x70, 0x00 }, /* 70 */ + { 0x71, 0x00 }, /* 71 */ + { 0x72, 0x00 }, /* 72 */ + { 0x73, 0x00 }, /* 73 */ + { 0x74, 0x00 }, /* 74 */ + { 0x75, 0x00 }, /* 75 */ + { 0x76, 0x00 }, /* 76 */ + { 0x77, 0x00 }, /* 77 */ + { 0x78, 0x00 }, /* 78 */ + { 0x79, 0x00 }, /* 79 */ + { 0x7a, 0x00 }, /* 7A */ + { 0x7b, 0x00 }, /* 7B */ + { 0x7c, 0x00 }, /* 7C */ + { 0x7d, 0x00 }, /* 7D */ + { 0x7e, 0x00 }, /* 7E */ + { 0x7f, 0x00 }, /* 7F */ + { 0x80, 0x00 }, /* 80 */ + { 0x81, 0x00 }, /* 81 */ + { 0x82, 0x00 }, /* 82 */ + { 0x83, 0x00 }, /* 83 */ + { 0x84, 0x00 }, /* 84 */ + { 0x85, 0x00 }, /* 85 */ + { 0x86, 0x00 }, /* 86 */ + { 0x87, 0x00 }, /* 87 */ + { 0x88, 0x00 }, /* 88 */ + { 0x89, 0x00 }, /* 89 */ + { 0x8a, 0x00 }, /* 8A */ + { 0x8b, 0x00 }, /* 8B */ + { 0x8c, 0x00 }, /* 8C */ + { 0x8d, 0x00 }, /* 8D */ + { 0x8e, 0x00 }, /* 8E */ + { 0x8f, 0x00 }, /* 8F */ + { 0x90, 0x00 }, /* 90 */ + { 0x91, 0x00 }, /* 91 */ + { 0x92, 0x30 }, /* 92 */ + { 0x93, 0xF0 }, /* 93 */ + { 0x94, 0x00 }, /* 94 */ + { 0x95, 0x00 }, /* 95 */ + { 0x96, 0x3F }, /* 96 */ + { 0x97, 0x00 }, /* 97 */ + { 0xff, 0x00 }, /* FF */ }; static struct { @@ -577,14 +460,14 @@ static struct { { 0xFF, 0x00 }, /* FF */ }; -static int max98095_readable(struct snd_soc_codec *codec, unsigned int reg) +static bool max98095_readable(struct device *dev, unsigned int reg) { if (reg >= M98095_REG_CNT) return 0; return max98095_access[reg].readable != 0; } -static int max98095_volatile(struct snd_soc_codec *codec, unsigned int reg) +static bool max98095_volatile(struct device *dev, unsigned int reg) { if (reg > M98095_REG_MAX_CACHED) return 1; @@ -611,6 +494,19 @@ static int max98095_volatile(struct snd_soc_codec *codec, unsigned int reg) return 0; } +static const struct regmap_config max98095_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .reg_defaults = max98095_reg_def, + .num_reg_defaults = ARRAY_SIZE(max98095_reg_def), + .max_register = M98095_0FF_REV_ID, + .cache_type = REGCACHE_RBTREE, + + .readable_reg = max98095_readable, + .volatile_reg = max98095_volatile, +}; + /* * Load equalizer DSP coefficient configurations registers */ @@ -1723,6 +1619,7 @@ static int max98095_dai3_set_fmt(struct snd_soc_dai *codec_dai, static int max98095_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); int ret; switch (level) { @@ -1734,7 +1631,7 @@ static int max98095_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_STANDBY: if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { - ret = snd_soc_cache_sync(codec); + ret = regcache_sync(max98095->regmap); if (ret != 0) { dev_err(codec->dev, "Failed to sync cache: %d\n", ret); @@ -1749,7 +1646,7 @@ static int max98095_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_OFF: snd_soc_update_bits(codec, M98095_090_PWR_EN_IN, M98095_MBEN, 0); - codec->cache_sync = 1; + regcache_mark_dirty(max98095->regmap); break; } codec->dapm.bias_level = level; @@ -2316,7 +2213,7 @@ static int max98095_reset(struct snd_soc_codec *codec) /* Reset to hardware default for registers, as there is not * a soft reset hardware control register */ for (i = M98095_010_HOST_INT_CFG; i < M98095_REG_MAX_CACHED; i++) { - ret = snd_soc_write(codec, i, max98095_reg_def[i]); + ret = snd_soc_write(codec, i, snd_soc_read(codec, i)); if (ret < 0) { dev_err(codec->dev, "Failed to reset: %d\n", ret); return ret; @@ -2333,7 +2230,7 @@ static int max98095_probe(struct snd_soc_codec *codec) struct i2c_client *client; int ret = 0; - ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C); + ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP); if (ret != 0) { dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); return ret; @@ -2455,11 +2352,6 @@ static struct snd_soc_codec_driver soc_codec_dev_max98095 = { .set_bias_level = max98095_set_bias_level, .controls = max98095_snd_controls, .num_controls = ARRAY_SIZE(max98095_snd_controls), - .reg_cache_size = ARRAY_SIZE(max98095_reg_def), - .reg_word_size = sizeof(u8), - .reg_cache_default = max98095_reg_def, - .readable_register = max98095_readable, - .volatile_register = max98095_volatile, .dapm_widgets = max98095_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(max98095_dapm_widgets), .dapm_routes = max98095_audio_map, @@ -2477,6 +2369,13 @@ static int max98095_i2c_probe(struct i2c_client *i2c, if (max98095 == NULL) return -ENOMEM; + max98095->regmap = devm_regmap_init_i2c(i2c, &max98095_regmap); + if (IS_ERR(max98095->regmap)) { + ret = PTR_ERR(max98095->regmap); + dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret); + return ret; + } + max98095->devtype = id->driver_data; i2c_set_clientdata(i2c, max98095); max98095->pdata = i2c->dev.platform_data; -- cgit v0.10.2 From 2c142c61f79c14a120c0f4d2954e35b6404b2d0d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 24 Sep 2013 18:49:54 +0100 Subject: ASoC: tlv320aic23: Remove #defines for I2C The only control interface supported by this driver is I2C so there is no need for conditional compilation around the control interface. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c index 31762eb..3299459 100644 --- a/sound/soc/codecs/tlv320aic23.c +++ b/sound/soc/codecs/tlv320aic23.c @@ -613,7 +613,6 @@ static struct snd_soc_codec_driver soc_codec_dev_tlv320aic23 = { .num_dapm_routes = ARRAY_SIZE(tlv320aic23_intercon), }; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) /* * If the i2c layer weren't so broken, we could pass this kind of data * around @@ -660,29 +659,7 @@ static struct i2c_driver tlv320aic23_i2c_driver = { .id_table = tlv320aic23_id, }; -#endif - -static int __init tlv320aic23_modinit(void) -{ - int ret; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - ret = i2c_add_driver(&tlv320aic23_i2c_driver); - if (ret != 0) { - printk(KERN_ERR "Failed to register TLV320AIC23 I2C driver: %d\n", - ret); - } -#endif - return ret; -} -module_init(tlv320aic23_modinit); - -static void __exit tlv320aic23_exit(void) -{ -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - i2c_del_driver(&tlv320aic23_i2c_driver); -#endif -} -module_exit(tlv320aic23_exit); +module_i2c_driver(tlv320aic23_i2c_driver); MODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver"); MODULE_AUTHOR("Arun KS "); -- cgit v0.10.2 From b07c443fabb97f909c8cc406bfd2d0ecc002bc3b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 24 Sep 2013 18:51:26 +0100 Subject: ASoC: tlv320aic23: Convert to table based control init Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c index 3299459..3a6be8c 100644 --- a/sound/soc/codecs/tlv320aic23.c +++ b/sound/soc/codecs/tlv320aic23.c @@ -586,9 +586,6 @@ static int tlv320aic23_probe(struct snd_soc_codec *codec) snd_soc_write(codec, TLV320AIC23_ACTIVE, 0x1); - snd_soc_add_codec_controls(codec, tlv320aic23_snd_controls, - ARRAY_SIZE(tlv320aic23_snd_controls)); - return 0; } @@ -607,6 +604,8 @@ static struct snd_soc_codec_driver soc_codec_dev_tlv320aic23 = { .suspend = tlv320aic23_suspend, .resume = tlv320aic23_resume, .set_bias_level = tlv320aic23_set_bias_level, + .controls = tlv320aic23_snd_controls, + .num_controls = ARRAY_SIZE(tlv320aic23_snd_controls), .dapm_widgets = tlv320aic23_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets), .dapm_routes = tlv320aic23_intercon, -- cgit v0.10.2 From a16bbe4d685c1465b98d3fabdb95310eafcd383e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 23 Sep 2013 20:07:12 +0100 Subject: ASoC: tlv320aic3x: Remove nonsense comment for register cache Every statement in this comment is incorrect either through bitrot or (mostly) through never having corresponded to reality in the first place. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 6e3f269..3abbff3 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -90,12 +90,6 @@ struct aic3x_priv { enum aic3x_micbias_voltage micbias_vg; }; -/* - * AIC3X register cache - * We can't read the AIC3X register space when we are - * using 2 wire for device control, so we cache them instead. - * There is no point in caching the reset register - */ static const u8 aic3x_reg[AIC3X_CACHEREGNUM] = { 0x00, 0x00, 0x00, 0x10, /* 0 */ 0x04, 0x00, 0x00, 0x00, /* 4 */ -- cgit v0.10.2 From 6f818e04fc8d3d413eeb3a689c7607f2a89ab568 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 23 Sep 2013 19:48:45 +0100 Subject: ASoC: tlv320aic3x: Move resource acquisition to I2C probe This is more idiomatic and interacts better with deferred probing. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 3abbff3..de17a36 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1345,23 +1345,6 @@ static int aic3x_probe(struct snd_soc_codec *codec) return ret; } - if (gpio_is_valid(aic3x->gpio_reset) && - !aic3x_is_shared_reset(aic3x)) { - ret = gpio_request(aic3x->gpio_reset, "tlv320aic3x reset"); - if (ret != 0) - goto err_gpio; - gpio_direction_output(aic3x->gpio_reset, 0); - } - - for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) - aic3x->supplies[i].supply = aic3x_supply_names[i]; - - ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(aic3x->supplies), - aic3x->supplies); - if (ret != 0) { - dev_err(codec->dev, "Failed to request supplies: %d\n", ret); - goto err_get; - } for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) { aic3x->disable_nb[i].nb.notifier_call = aic3x_regulator_event; aic3x->disable_nb[i].aic3x = aic3x; @@ -1418,12 +1401,6 @@ err_notif: while (i--) regulator_unregister_notifier(aic3x->supplies[i].consumer, &aic3x->disable_nb[i].nb); - regulator_bulk_free(ARRAY_SIZE(aic3x->supplies), aic3x->supplies); -err_get: - if (gpio_is_valid(aic3x->gpio_reset) && - !aic3x_is_shared_reset(aic3x)) - gpio_free(aic3x->gpio_reset); -err_gpio: return ret; } @@ -1434,15 +1411,9 @@ static int aic3x_remove(struct snd_soc_codec *codec) aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF); list_del(&aic3x->list); - if (gpio_is_valid(aic3x->gpio_reset) && - !aic3x_is_shared_reset(aic3x)) { - gpio_set_value(aic3x->gpio_reset, 0); - gpio_free(aic3x->gpio_reset); - } for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) regulator_unregister_notifier(aic3x->supplies[i].consumer, &aic3x->disable_nb[i].nb); - regulator_bulk_free(ARRAY_SIZE(aic3x->supplies), aic3x->supplies); return 0; } @@ -1484,7 +1455,7 @@ static int aic3x_i2c_probe(struct i2c_client *i2c, struct aic3x_priv *aic3x; struct aic3x_setup_data *ai3x_setup; struct device_node *np = i2c->dev.of_node; - int ret; + int ret, i; u32 value; aic3x = devm_kzalloc(&i2c->dev, sizeof(struct aic3x_priv), GFP_KERNEL); @@ -1545,14 +1516,46 @@ static int aic3x_i2c_probe(struct i2c_client *i2c, aic3x->model = id->driver_data; + if (gpio_is_valid(aic3x->gpio_reset) && + !aic3x_is_shared_reset(aic3x)) { + ret = gpio_request(aic3x->gpio_reset, "tlv320aic3x reset"); + if (ret != 0) + goto err; + gpio_direction_output(aic3x->gpio_reset, 0); + } + + for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) + aic3x->supplies[i].supply = aic3x_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(aic3x->supplies), + aic3x->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + goto err_gpio; + } + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_aic3x, &aic3x_dai, 1); return ret; + +err_gpio: + if (gpio_is_valid(aic3x->gpio_reset) && + !aic3x_is_shared_reset(aic3x)) + gpio_free(aic3x->gpio_reset); +err: + return ret; } static int aic3x_i2c_remove(struct i2c_client *client) { + struct aic3x_priv *aic3x = i2c_get_clientdata(client); + snd_soc_unregister_codec(&client->dev); + if (gpio_is_valid(aic3x->gpio_reset) && + !aic3x_is_shared_reset(aic3x)) { + gpio_set_value(aic3x->gpio_reset, 0); + gpio_free(aic3x->gpio_reset); + } return 0; } -- cgit v0.10.2 From f9df1ae6b59e5bb16d3094e9c1c8b6feeaf32aae Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 23 Sep 2013 23:53:16 +0100 Subject: ASoC: tlv320aic3x: Move to table based control init Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index de17a36..397a213 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1369,8 +1369,6 @@ static int aic3x_probe(struct snd_soc_codec *codec) (aic3x->setup->gpio_func[1] & 0xf) << 4); } - snd_soc_add_codec_controls(codec, aic3x_snd_controls, - ARRAY_SIZE(aic3x_snd_controls)); if (aic3x->model == AIC3X_MODEL_3007) snd_soc_add_codec_controls(codec, &aic3x_classd_amp_gain_ctrl, 1); @@ -1428,6 +1426,8 @@ static struct snd_soc_codec_driver soc_codec_dev_aic3x = { .remove = aic3x_remove, .suspend = aic3x_suspend, .resume = aic3x_resume, + .controls = aic3x_snd_controls, + .num_controls = ARRAY_SIZE(aic3x_snd_controls), }; /* -- cgit v0.10.2 From 58a63fbd7c80510140a94442b2ca9199bb6d51c3 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 23 Sep 2013 23:57:36 +0100 Subject: ASoC: tlv320aic3x: Move to table based DAPM init Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 397a213..16fc74c 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -818,12 +818,6 @@ static int aic3x_add_widgets(struct snd_soc_codec *codec) struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_new_controls(dapm, aic3x_dapm_widgets, - ARRAY_SIZE(aic3x_dapm_widgets)); - - /* set up audio path interconnects */ - snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); - if (aic3x->model == AIC3X_MODEL_3007) { snd_soc_dapm_new_controls(dapm, aic3007_dapm_widgets, ARRAY_SIZE(aic3007_dapm_widgets)); @@ -1428,6 +1422,10 @@ static struct snd_soc_codec_driver soc_codec_dev_aic3x = { .resume = aic3x_resume, .controls = aic3x_snd_controls, .num_controls = ARRAY_SIZE(aic3x_snd_controls), + .dapm_widgets = aic3x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(aic3x_dapm_widgets), + .dapm_routes = intercon, + .num_dapm_routes = ARRAY_SIZE(intercon), }; /* -- cgit v0.10.2 From 2677b4bb7316c07dd53535e01bd9b2ec699d0314 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 23 Sep 2013 19:55:39 +0100 Subject: ASoC: tlv320aic3x: Don't reference cache datastructure directly Rather than referencing the cache directly read back the values we are going to restore, supporting refactoring to use regmap. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 16fc74c..83e7d85 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1068,14 +1068,14 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai, static int aic3x_init_3007(struct snd_soc_codec *codec) { - u8 tmp1, tmp2, *cache = codec->reg_cache; + unsigned int tmp1, tmp2; /* * There is no need to cache writes to undocumented page 0xD but * respective page 0 register cache entries must be preserved */ - tmp1 = cache[0xD]; - tmp2 = cache[0x8]; + tmp1 = snd_soc_read(codec, 0xD); + tmp2 = snd_soc_read(codec, 0x8); /* Class-D speaker driver init; datasheet p. 46 */ snd_soc_write(codec, AIC3X_PAGE_SELECT, 0x0D); snd_soc_write(codec, 0xD, 0x0D); @@ -1083,8 +1083,9 @@ static int aic3x_init_3007(struct snd_soc_codec *codec) snd_soc_write(codec, 0x8, 0x5D); snd_soc_write(codec, 0x8, 0x5C); snd_soc_write(codec, AIC3X_PAGE_SELECT, 0x00); - cache[0xD] = tmp1; - cache[0x8] = tmp2; + + snd_soc_write(codec, 0xD, tmp1); + snd_soc_write(codec, 0x8, tmp2); return 0; } -- cgit v0.10.2 From 2a6fedec195b9bd20e60f9825ba7cc6315e54652 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 24 Sep 2013 00:07:13 +0100 Subject: ASoC: tlv320aic3x: Convert to direct regmap API usage This is slightly more complex than a standard regmap conversion due to the moderately detailed cache control and the open coding of a register patch for the class D speaker on the TLV320AIC3007. Although the device supports paging this is not currently implemented as the additional pages are only used during the application of the patch for the TLV320AIC3007. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 83e7d85..892c108 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -72,9 +72,9 @@ struct aic3x_disable_nb { /* codec private data */ struct aic3x_priv { struct snd_soc_codec *codec; + struct regmap *regmap; struct regulator_bulk_data supplies[AIC3X_NUM_SUPPLIES]; struct aic3x_disable_nb disable_nb[AIC3X_NUM_SUPPLIES]; - enum snd_soc_control_type control_type; struct aic3x_setup_data *setup; unsigned int sysclk; struct list_head list; @@ -90,35 +90,45 @@ struct aic3x_priv { enum aic3x_micbias_voltage micbias_vg; }; -static const u8 aic3x_reg[AIC3X_CACHEREGNUM] = { - 0x00, 0x00, 0x00, 0x10, /* 0 */ - 0x04, 0x00, 0x00, 0x00, /* 4 */ - 0x00, 0x00, 0x00, 0x01, /* 8 */ - 0x00, 0x00, 0x00, 0x80, /* 12 */ - 0x80, 0xff, 0xff, 0x78, /* 16 */ - 0x78, 0x78, 0x78, 0x78, /* 20 */ - 0x78, 0x00, 0x00, 0xfe, /* 24 */ - 0x00, 0x00, 0xfe, 0x00, /* 28 */ - 0x18, 0x18, 0x00, 0x00, /* 32 */ - 0x00, 0x00, 0x00, 0x00, /* 36 */ - 0x00, 0x00, 0x00, 0x80, /* 40 */ - 0x80, 0x00, 0x00, 0x00, /* 44 */ - 0x00, 0x00, 0x00, 0x04, /* 48 */ - 0x00, 0x00, 0x00, 0x00, /* 52 */ - 0x00, 0x00, 0x04, 0x00, /* 56 */ - 0x00, 0x00, 0x00, 0x00, /* 60 */ - 0x00, 0x04, 0x00, 0x00, /* 64 */ - 0x00, 0x00, 0x00, 0x00, /* 68 */ - 0x04, 0x00, 0x00, 0x00, /* 72 */ - 0x00, 0x00, 0x00, 0x00, /* 76 */ - 0x00, 0x00, 0x00, 0x00, /* 80 */ - 0x00, 0x00, 0x00, 0x00, /* 84 */ - 0x00, 0x00, 0x00, 0x00, /* 88 */ - 0x00, 0x00, 0x00, 0x00, /* 92 */ - 0x00, 0x00, 0x00, 0x00, /* 96 */ - 0x00, 0x00, 0x02, 0x00, /* 100 */ - 0x00, 0x00, 0x00, 0x00, /* 104 */ - 0x00, 0x00, /* 108 */ +static const struct reg_default aic3x_reg[] = { + { 0, 0x00 }, { 1, 0x00 }, { 2, 0x00 }, { 3, 0x10 }, + { 4, 0x04 }, { 5, 0x00 }, { 6, 0x00 }, { 7, 0x00 }, + { 8, 0x00 }, { 9, 0x00 }, { 10, 0x00 }, { 11, 0x01 }, + { 12, 0x00 }, { 13, 0x00 }, { 14, 0x00 }, { 15, 0x80 }, + { 16, 0x80 }, { 17, 0xff }, { 18, 0xff }, { 19, 0x78 }, + { 20, 0x78 }, { 21, 0x78 }, { 22, 0x78 }, { 23, 0x78 }, + { 24, 0x78 }, { 25, 0x00 }, { 26, 0x00 }, { 27, 0xfe }, + { 28, 0x00 }, { 29, 0x00 }, { 30, 0xfe }, { 31, 0x00 }, + { 32, 0x18 }, { 33, 0x18 }, { 34, 0x00 }, { 35, 0x00 }, + { 36, 0x00 }, { 37, 0x00 }, { 38, 0x00 }, { 39, 0x00 }, + { 40, 0x00 }, { 41, 0x00 }, { 42, 0x00 }, { 43, 0x80 }, + { 44, 0x80 }, { 45, 0x00 }, { 46, 0x00 }, { 47, 0x00 }, + { 48, 0x00 }, { 49, 0x00 }, { 50, 0x00 }, { 51, 0x04 }, + { 52, 0x00 }, { 53, 0x00 }, { 54, 0x00 }, { 55, 0x00 }, + { 56, 0x00 }, { 57, 0x00 }, { 58, 0x04 }, { 59, 0x00 }, + { 60, 0x00 }, { 61, 0x00 }, { 62, 0x00 }, { 63, 0x00 }, + { 64, 0x00 }, { 65, 0x04 }, { 66, 0x00 }, { 67, 0x00 }, + { 68, 0x00 }, { 69, 0x00 }, { 70, 0x00 }, { 71, 0x00 }, + { 72, 0x04 }, { 73, 0x00 }, { 74, 0x00 }, { 75, 0x00 }, + { 76, 0x00 }, { 77, 0x00 }, { 78, 0x00 }, { 79, 0x00 }, + { 80, 0x00 }, { 81, 0x00 }, { 82, 0x00 }, { 83, 0x00 }, + { 84, 0x00 }, { 85, 0x00 }, { 86, 0x00 }, { 87, 0x00 }, + { 88, 0x00 }, { 89, 0x00 }, { 90, 0x00 }, { 91, 0x00 }, + { 92, 0x00 }, { 93, 0x00 }, { 94, 0x00 }, { 95, 0x00 }, + { 96, 0x00 }, { 97, 0x00 }, { 98, 0x00 }, { 99, 0x00 }, + { 100, 0x00 }, { 101, 0x00 }, { 102, 0x02 }, { 103, 0x00 }, + { 104, 0x00 }, { 105, 0x00 }, { 106, 0x00 }, { 107, 0x00 }, + { 108, 0x00 }, { 109, 0x00 }, +}; + +static const struct regmap_config aic3x_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = DAC_ICC_ADJ, + .reg_defaults = aic3x_reg, + .num_reg_defaults = ARRAY_SIZE(aic3x_reg), + .cache_type = REGCACHE_RBTREE, }; #define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \ @@ -1066,30 +1076,6 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai, return 0; } -static int aic3x_init_3007(struct snd_soc_codec *codec) -{ - unsigned int tmp1, tmp2; - - /* - * There is no need to cache writes to undocumented page 0xD but - * respective page 0 register cache entries must be preserved - */ - tmp1 = snd_soc_read(codec, 0xD); - tmp2 = snd_soc_read(codec, 0x8); - /* Class-D speaker driver init; datasheet p. 46 */ - snd_soc_write(codec, AIC3X_PAGE_SELECT, 0x0D); - snd_soc_write(codec, 0xD, 0x0D); - snd_soc_write(codec, 0x8, 0x5C); - snd_soc_write(codec, 0x8, 0x5D); - snd_soc_write(codec, 0x8, 0x5C); - snd_soc_write(codec, AIC3X_PAGE_SELECT, 0x00); - - snd_soc_write(codec, 0xD, tmp1); - snd_soc_write(codec, 0x8, tmp2); - - return 0; -} - static int aic3x_regulator_event(struct notifier_block *nb, unsigned long event, void *data) { @@ -1104,7 +1090,7 @@ static int aic3x_regulator_event(struct notifier_block *nb, */ if (gpio_is_valid(aic3x->gpio_reset)) gpio_set_value(aic3x->gpio_reset, 0); - aic3x->codec->cache_sync = 1; + regcache_mark_dirty(aic3x->regmap); } return 0; @@ -1113,8 +1099,7 @@ static int aic3x_regulator_event(struct notifier_block *nb, static int aic3x_set_power(struct snd_soc_codec *codec, int power) { struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); - int i, ret; - u8 *cache = codec->reg_cache; + int ret; if (power) { ret = regulator_bulk_enable(ARRAY_SIZE(aic3x->supplies), @@ -1122,12 +1107,6 @@ static int aic3x_set_power(struct snd_soc_codec *codec, int power) if (ret) goto out; aic3x->power = 1; - /* - * Reset release and cache sync is necessary only if some - * supply was off or if there were cached writes - */ - if (!codec->cache_sync) - goto out; if (gpio_is_valid(aic3x->gpio_reset)) { udelay(1); @@ -1135,12 +1114,8 @@ static int aic3x_set_power(struct snd_soc_codec *codec, int power) } /* Sync reg_cache with the hardware */ - codec->cache_only = 0; - for (i = AIC3X_SAMPLE_RATE_SEL_REG; i < ARRAY_SIZE(aic3x_reg); i++) - snd_soc_write(codec, i, cache[i]); - if (aic3x->model == AIC3X_MODEL_3007) - aic3x_init_3007(codec); - codec->cache_sync = 0; + regcache_cache_only(aic3x->regmap, false); + regcache_sync(aic3x->regmap); } else { /* * Do soft reset to this codec instance in order to clear @@ -1148,10 +1123,10 @@ static int aic3x_set_power(struct snd_soc_codec *codec, int power) * remain on */ snd_soc_write(codec, AIC3X_RESET, SOFT_RESET); - codec->cache_sync = 1; + regcache_mark_dirty(aic3x->regmap); aic3x->power = 0; /* HW writes are needless when bias is off */ - codec->cache_only = 1; + regcache_cache_only(aic3x->regmap, true); ret = regulator_bulk_disable(ARRAY_SIZE(aic3x->supplies), aic3x->supplies); } @@ -1306,7 +1281,6 @@ static int aic3x_init(struct snd_soc_codec *codec) snd_soc_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL); if (aic3x->model == AIC3X_MODEL_3007) { - aic3x_init_3007(codec); snd_soc_write(codec, CLASSD_CTRL, 0); } @@ -1334,7 +1308,7 @@ static int aic3x_probe(struct snd_soc_codec *codec) INIT_LIST_HEAD(&aic3x->list); aic3x->codec = codec; - ret = snd_soc_codec_set_cache_io(codec, 8, 8, aic3x->control_type); + ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP); if (ret != 0) { dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); return ret; @@ -1353,7 +1327,7 @@ static int aic3x_probe(struct snd_soc_codec *codec) } } - codec->cache_only = 1; + regcache_mark_dirty(aic3x->regmap); aic3x_init(codec); if (aic3x->setup) { @@ -1414,9 +1388,6 @@ static int aic3x_remove(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_dev_aic3x = { .set_bias_level = aic3x_set_bias_level, .idle_bias_off = true, - .reg_cache_size = ARRAY_SIZE(aic3x_reg), - .reg_word_size = sizeof(u8), - .reg_cache_default = aic3x_reg, .probe = aic3x_probe, .remove = aic3x_remove, .suspend = aic3x_suspend, @@ -1443,6 +1414,16 @@ static const struct i2c_device_id aic3x_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id); +static const struct reg_default aic3007_class_d[] = { + /* Class-D speaker driver init; datasheet p. 46 */ + { AIC3X_PAGE_SELECT, 0x0D }, + { 0xD, 0x0D }, + { 0x8, 0x5C }, + { 0x8, 0x5D }, + { 0x8, 0x5C }, + { AIC3X_PAGE_SELECT, 0x00 }, +}; + /* * If the i2c layer weren't so broken, we could pass this kind of data * around @@ -1463,7 +1444,13 @@ static int aic3x_i2c_probe(struct i2c_client *i2c, return -ENOMEM; } - aic3x->control_type = SND_SOC_I2C; + aic3x->regmap = devm_regmap_init_i2c(i2c, &aic3x_regmap); + if (IS_ERR(aic3x->regmap)) { + ret = PTR_ERR(aic3x->regmap); + return ret; + } + + regcache_cache_only(aic3x->regmap, true); i2c_set_clientdata(i2c, aic3x); if (pdata) { @@ -1533,6 +1520,14 @@ static int aic3x_i2c_probe(struct i2c_client *i2c, goto err_gpio; } + if (aic3x->model == AIC3X_MODEL_3007) { + ret = regmap_register_patch(aic3x->regmap, aic3007_class_d, + ARRAY_SIZE(aic3007_class_d)); + if (ret != 0) + dev_err(&i2c->dev, "Failed to init class D: %d\n", + ret); + } + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_aic3x, &aic3x_dai, 1); return ret; -- cgit v0.10.2 From 19ab2a7a24539d6c80dfe301d2970b075ad3b9ab Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 24 Sep 2013 11:14:39 +0100 Subject: ASoC: max98088: Set max_register Makes some of the debug functions more useful. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index 31912d5..66ceee2 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -552,6 +552,7 @@ static const struct regmap_config max98088_regmap = { .readable_reg = max98088_readable_register, .volatile_reg = max98088_volatile_register, + .max_register = 0xff, .reg_defaults = max98088_reg, .num_reg_defaults = ARRAY_SIZE(max98088_reg), -- cgit v0.10.2 From 068416620c0d956b3b382d19dd3000119e280f8c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 23 Sep 2013 18:12:51 +0100 Subject: ASoC: max9850: Convert to direct regmap API usage This prepares for removal of the duplicated register I/O functionality in ASoC. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c index 58c38a5..c5dd617 100644 --- a/sound/soc/codecs/max9850.c +++ b/sound/soc/codecs/max9850.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -27,18 +28,26 @@ #include "max9850.h" struct max9850_priv { + struct regmap *regmap; unsigned int sysclk; }; /* max9850 register cache */ -static const u8 max9850_reg[MAX9850_CACHEREGNUM] = { - 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +static const struct reg_default max9850_reg[] = { + { 2, 0x0c }, + { 3, 0x00 }, + { 4, 0x00 }, + { 5, 0x00 }, + { 6, 0x00 }, + { 7, 0x00 }, + { 8, 0x00 }, + { 9, 0x00 }, + { 10, 0x00 }, }; /* these registers are not used at the moment but provided for the sake of * completeness */ -static int max9850_volatile_register(struct snd_soc_codec *codec, - unsigned int reg) +static bool max9850_volatile_register(struct device *dev, unsigned int reg) { switch (reg) { case MAX9850_STATUSA: @@ -49,6 +58,15 @@ static int max9850_volatile_register(struct snd_soc_codec *codec, } } +static const struct regmap_config max9850_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = MAX9850_DIGITAL_AUDIO, + .volatile_reg = max9850_volatile_register, + .cache_type = REGCACHE_RBTREE, +}; + static const unsigned int max9850_tlv[] = { TLV_DB_RANGE_HEAD(4), 0x18, 0x1f, TLV_DB_SCALE_ITEM(-7450, 400, 0), @@ -225,6 +243,7 @@ static int max9850_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) static int max9850_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct max9850_priv *max9850 = snd_soc_codec_get_drvdata(codec); int ret; switch (level) { @@ -234,7 +253,7 @@ static int max9850_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { - ret = snd_soc_cache_sync(codec); + ret = regcache_sync(max9850->regmap); if (ret) { dev_err(codec->dev, "Failed to sync cache: %d\n", ret); @@ -295,7 +314,7 @@ static int max9850_probe(struct snd_soc_codec *codec) { int ret; - ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C); + ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP); if (ret < 0) { dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); return ret; @@ -316,10 +335,6 @@ static struct snd_soc_codec_driver soc_codec_dev_max9850 = { .suspend = max9850_suspend, .resume = max9850_resume, .set_bias_level = max9850_set_bias_level, - .reg_cache_size = ARRAY_SIZE(max9850_reg), - .reg_word_size = sizeof(u8), - .reg_cache_default = max9850_reg, - .volatile_register = max9850_volatile_register, .controls = max9850_controls, .num_controls = ARRAY_SIZE(max9850_controls), @@ -340,6 +355,10 @@ static int max9850_i2c_probe(struct i2c_client *i2c, if (max9850 == NULL) return -ENOMEM; + max9850->regmap = devm_regmap_init_i2c(i2c, &max9850_regmap); + if (IS_ERR(max9850->regmap)) + return PTR_ERR(max9850->regmap); + i2c_set_clientdata(i2c, max9850); ret = snd_soc_register_codec(&i2c->dev, -- cgit v0.10.2 From 6f88063c1474b8cdd9254d3934047b6087222145 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 31 Aug 2013 13:46:05 +0100 Subject: ASoC: cq93vc: Use table based control registration Saves a little code. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c index e2c4c0a8..e538f4e 100644 --- a/sound/soc/codecs/cq93vc.c +++ b/sound/soc/codecs/cq93vc.c @@ -156,10 +156,6 @@ static int cq93vc_probe(struct snd_soc_codec *codec) davinci_vc->cq93vc.codec = codec; codec->control_data = davinci_vc; - /* Set controls */ - snd_soc_add_codec_controls(codec, cq93vc_snd_controls, - ARRAY_SIZE(cq93vc_snd_controls)); - /* Off, with power on */ cq93vc_set_bias_level(codec, SND_SOC_BIAS_STANDBY); @@ -180,6 +176,8 @@ static struct snd_soc_codec_driver soc_codec_dev_cq93vc = { .probe = cq93vc_probe, .remove = cq93vc_remove, .resume = cq93vc_resume, + .controls = cq93vc_snd_controls, + .num_controls = ARRAY_SIZE(cq93vc_snd_controls), }; static int cq93vc_platform_probe(struct platform_device *pdev) -- cgit v0.10.2 From 1201939a6f981cb656872784e39ef443540078cd Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 31 Aug 2013 13:38:16 +0100 Subject: ASoC: cq93vc: Use core I/O functions Support future refactoring by using the core I/O functions rather than calling the driver provided I/O functions directly. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c index e538f4e..2cbb584 100644 --- a/sound/soc/codecs/cq93vc.c +++ b/sound/soc/codecs/cq93vc.c @@ -64,13 +64,15 @@ static const struct snd_kcontrol_new cq93vc_snd_controls[] = { static int cq93vc_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; - u8 reg = cq93vc_read(codec, DAVINCI_VC_REG09) & ~DAVINCI_VC_REG09_MUTE; + u8 reg; if (mute) - cq93vc_write(codec, DAVINCI_VC_REG09, - reg | DAVINCI_VC_REG09_MUTE); + reg = DAVINCI_VC_REG09_MUTE; else - cq93vc_write(codec, DAVINCI_VC_REG09, reg); + reg = 0; + + snd_soc_update_bits(codec, DAVINCI_VC_REG09, DAVINCI_VC_REG09_MUTE, + reg); return 0; } @@ -97,18 +99,18 @@ static int cq93vc_set_bias_level(struct snd_soc_codec *codec, { switch (level) { case SND_SOC_BIAS_ON: - cq93vc_write(codec, DAVINCI_VC_REG12, + snd_soc_write(codec, DAVINCI_VC_REG12, DAVINCI_VC_REG12_POWER_ALL_ON); break; case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - cq93vc_write(codec, DAVINCI_VC_REG12, + snd_soc_write(codec, DAVINCI_VC_REG12, DAVINCI_VC_REG12_POWER_ALL_OFF); break; case SND_SOC_BIAS_OFF: /* force all power off */ - cq93vc_write(codec, DAVINCI_VC_REG12, + snd_soc_write(codec, DAVINCI_VC_REG12, DAVINCI_VC_REG12_POWER_ALL_OFF); break; } -- cgit v0.10.2 From d33c33352b3228ca3a422e55981f80fc12dc30f8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 31 Aug 2013 13:52:41 +0100 Subject: ASoC: cq93vc: Use regmap for I/O Avoid use of the ASoC-specific register I/O functions by converting to use the MMIO regmap provided the core MFD. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c index 2cbb584..43737a27 100644 --- a/sound/soc/codecs/cq93vc.c +++ b/sound/soc/codecs/cq93vc.c @@ -38,24 +38,6 @@ #include #include -static inline unsigned int cq93vc_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - struct davinci_vc *davinci_vc = codec->control_data; - - return readl(davinci_vc->base + reg); -} - -static inline int cq93vc_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - struct davinci_vc *davinci_vc = codec->control_data; - - writel(value, davinci_vc->base + reg); - - return 0; -} - static const struct snd_kcontrol_new cq93vc_snd_controls[] = { SOC_SINGLE("PGA Capture Volume", DAVINCI_VC_REG05, 0, 0x03, 0), SOC_SINGLE("Mono DAC Playback Volume", DAVINCI_VC_REG09, 0, 0x3f, 0), @@ -156,7 +138,9 @@ static int cq93vc_probe(struct snd_soc_codec *codec) struct davinci_vc *davinci_vc = codec->dev->platform_data; davinci_vc->cq93vc.codec = codec; - codec->control_data = davinci_vc; + codec->control_data = davinci_vc->regmap; + + snd_soc_codec_set_cache_io(codec, 32, 32, SND_SOC_REGMAP); /* Off, with power on */ cq93vc_set_bias_level(codec, SND_SOC_BIAS_STANDBY); @@ -172,8 +156,6 @@ static int cq93vc_remove(struct snd_soc_codec *codec) } static struct snd_soc_codec_driver soc_codec_dev_cq93vc = { - .read = cq93vc_read, - .write = cq93vc_write, .set_bias_level = cq93vc_set_bias_level, .probe = cq93vc_probe, .remove = cq93vc_remove, -- cgit v0.10.2 From efeb970ee799b80c984a42d5706081af6047e160 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 23 Sep 2013 23:12:17 -0700 Subject: ASoC: rsnd: remove rsnd_priv_read/write/bset() adg.c only used rsnd_priv_read/write/bset() which is the only user of NULL mod. but, it can be removed. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 2935bbf..9430097 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -21,6 +21,7 @@ struct rsnd_adg { int rate_of_441khz_div_6; int rate_of_48khz_div_6; + u32 ckr; }; #define for_each_rsnd_clk(pos, adg, i) \ @@ -115,6 +116,11 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate) found_clock: + /* see rsnd_adg_ssi_clk_init() */ + rsnd_mod_bset(mod, SSICKR, 0x00FF0000, adg->ckr); + rsnd_mod_write(mod, BRRA, 0x00000002); /* 1/6 */ + rsnd_mod_write(mod, BRRB, 0x00000002); /* 1/6 */ + /* * This "mod" = "ssi" here. * we can get "ssi id" from mod @@ -181,9 +187,7 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) } } - rsnd_priv_bset(priv, SSICKR, 0x00FF0000, ckr); - rsnd_priv_write(priv, BRRA, 0x00000002); /* 1/6 */ - rsnd_priv_write(priv, BRRB, 0x00000002); /* 1/6 */ + adg->ckr = ckr; } int rsnd_adg_probe(struct platform_device *pdev, diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 9cc6986..3868aaf 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -78,10 +78,6 @@ struct rsnd_dai_stream; #define rsnd_mod_bset(m, r, s, d) \ rsnd_bset(rsnd_mod_to_priv(m), m, RSND_REG_##r, s, d) -#define rsnd_priv_read(p, r) rsnd_read(p, NULL, RSND_REG_##r) -#define rsnd_priv_write(p, r, d) rsnd_write(p, NULL, RSND_REG_##r, d) -#define rsnd_priv_bset(p, r, s, d) rsnd_bset(p, NULL, RSND_REG_##r, s, d) - u32 rsnd_read(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg); void rsnd_write(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, u32 data); -- cgit v0.10.2 From 55e5b6fd5af04b6d8b0ac6635edf49476ff298ba Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 23 Sep 2013 23:12:27 -0700 Subject: ASoC: rsnd: use regmap instead of original register mapping method Current Linux kernel is supporting regmap/regmap_field, and, it is good match for Renesas Sound Gen1/Gen2 register mapping. This patch uses regmap instead of original method for register access Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index a357060..fc83f0f 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -106,51 +106,6 @@ (!(priv->info->func) ? -ENODEV : \ priv->info->func(param)) - -/* - * basic function - */ -u32 rsnd_read(struct rsnd_priv *priv, - struct rsnd_mod *mod, enum rsnd_reg reg) -{ - void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); - - BUG_ON(!base); - - return ioread32(base); -} - -void rsnd_write(struct rsnd_priv *priv, - struct rsnd_mod *mod, - enum rsnd_reg reg, u32 data) -{ - void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); - struct device *dev = rsnd_priv_to_dev(priv); - - BUG_ON(!base); - - dev_dbg(dev, "w %p : %08x\n", base, data); - - iowrite32(data, base); -} - -void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, - enum rsnd_reg reg, u32 mask, u32 data) -{ - void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); - struct device *dev = rsnd_priv_to_dev(priv); - u32 val; - - BUG_ON(!base); - - val = ioread32(base); - val &= ~mask; - val |= data & mask; - iowrite32(val, base); - - dev_dbg(dev, "s %p : %08x\n", base, val); -} - /* * rsnd_mod functions */ diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 331fc55..61212ee 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -24,21 +24,97 @@ struct rsnd_gen_ops { struct rsnd_dai_stream *io); }; -struct rsnd_gen_reg_map { - int index; /* -1 : not supported */ - u32 offset_id; /* offset of ssi0, ssi1, ssi2... */ - u32 offset_adr; /* offset of SSICR, SSISR, ... */ -}; - struct rsnd_gen { void __iomem *base[RSND_BASE_MAX]; - struct rsnd_gen_reg_map reg_map[RSND_REG_MAX]; struct rsnd_gen_ops *ops; + + struct regmap *regmap; + struct regmap_field *regs[RSND_REG_MAX]; }; #define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen) +#define RSND_REG_SET(gen, id, reg_id, offset, _id_offset, _id_size) \ + [id] = { \ + .reg = (unsigned int)gen->base[reg_id] + offset, \ + .lsb = 0, \ + .msb = 31, \ + .id_size = _id_size, \ + .id_offset = _id_offset, \ + } + +/* + * basic function + */ +static int rsnd_regmap_write32(void *context, const void *_data, size_t count) +{ + struct rsnd_priv *priv = context; + struct device *dev = rsnd_priv_to_dev(priv); + u32 *data = (u32 *)_data; + u32 val = data[1]; + void __iomem *reg = (void *)data[0]; + + iowrite32(val, reg); + + dev_dbg(dev, "w %p : %08x\n", reg, val); + + return 0; +} + +static int rsnd_regmap_read32(void *context, + const void *_data, size_t reg_size, + void *_val, size_t val_size) +{ + struct rsnd_priv *priv = context; + struct device *dev = rsnd_priv_to_dev(priv); + u32 *data = (u32 *)_data; + u32 *val = (u32 *)_val; + void __iomem *reg = (void *)data[0]; + + *val = ioread32(reg); + + dev_dbg(dev, "r %p : %08x\n", reg, *val); + + return 0; +} + +static struct regmap_bus rsnd_regmap_bus = { + .write = rsnd_regmap_write32, + .read = rsnd_regmap_read32, + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, + .val_format_endian_default = REGMAP_ENDIAN_NATIVE, +}; + +u32 rsnd_read(struct rsnd_priv *priv, + struct rsnd_mod *mod, enum rsnd_reg reg) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + u32 val; + + regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val); + + return val; +} + +void rsnd_write(struct rsnd_priv *priv, + struct rsnd_mod *mod, + enum rsnd_reg reg, u32 data) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data); +} + +void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, + enum rsnd_reg reg, u32 mask, u32 data) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod), + mask, data); +} + /* * Gen2 * will be filled in the future @@ -103,39 +179,64 @@ static int rsnd_gen1_path_exit(struct rsnd_priv *priv, return ret; } -#define RSND_GEN1_REG_MAP(g, s, i, oi, oa) \ - do { \ - (g)->reg_map[RSND_REG_##i].index = RSND_GEN1_##s; \ - (g)->reg_map[RSND_REG_##i].offset_id = oi; \ - (g)->reg_map[RSND_REG_##i].offset_adr = oa; \ - } while (0) +/* single address mapping */ +#define RSND_GEN1_S_REG(gen, reg, id, offset) \ + RSND_REG_SET(gen, RSND_REG_##id, RSND_GEN1_##reg, offset, 0, 9) -static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen) +/* multi address mapping */ +#define RSND_GEN1_M_REG(gen, reg, id, offset, _id_offset) \ + RSND_REG_SET(gen, RSND_REG_##id, RSND_GEN1_##reg, offset, _id_offset, 9) + +static int rsnd_gen1_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen) { - RSND_GEN1_REG_MAP(gen, SRU, SRC_ROUTE_SEL, 0x0, 0x00); - RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL0, 0x0, 0x08); - RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL1, 0x0, 0x0c); - RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL2, 0x0, 0x10); - RSND_GEN1_REG_MAP(gen, SRU, SRC_CTRL, 0x0, 0xc0); - RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE0, 0x0, 0xD0); - RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE1, 0x0, 0xD4); - RSND_GEN1_REG_MAP(gen, SRU, BUSIF_MODE, 0x4, 0x20); - RSND_GEN1_REG_MAP(gen, SRU, BUSIF_ADINR, 0x40, 0x214); - - RSND_GEN1_REG_MAP(gen, ADG, BRRA, 0x0, 0x00); - RSND_GEN1_REG_MAP(gen, ADG, BRRB, 0x0, 0x04); - RSND_GEN1_REG_MAP(gen, ADG, SSICKR, 0x0, 0x08); - RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL0, 0x0, 0x0c); - RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL1, 0x0, 0x10); - RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL3, 0x0, 0x18); - RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL4, 0x0, 0x1c); - RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL5, 0x0, 0x20); - - RSND_GEN1_REG_MAP(gen, SSI, SSICR, 0x40, 0x00); - RSND_GEN1_REG_MAP(gen, SSI, SSISR, 0x40, 0x04); - RSND_GEN1_REG_MAP(gen, SSI, SSITDR, 0x40, 0x08); - RSND_GEN1_REG_MAP(gen, SSI, SSIRDR, 0x40, 0x0c); - RSND_GEN1_REG_MAP(gen, SSI, SSIWSR, 0x40, 0x20); + int i; + struct device *dev = rsnd_priv_to_dev(priv); + struct regmap_config regc; + struct reg_field regf[RSND_REG_MAX] = { + RSND_GEN1_S_REG(gen, SRU, SRC_ROUTE_SEL, 0x00), + RSND_GEN1_S_REG(gen, SRU, SRC_TMG_SEL0, 0x08), + RSND_GEN1_S_REG(gen, SRU, SRC_TMG_SEL1, 0x0c), + RSND_GEN1_S_REG(gen, SRU, SRC_TMG_SEL2, 0x10), + RSND_GEN1_S_REG(gen, SRU, SRC_CTRL, 0xc0), + RSND_GEN1_S_REG(gen, SRU, SSI_MODE0, 0xD0), + RSND_GEN1_S_REG(gen, SRU, SSI_MODE1, 0xD4), + RSND_GEN1_M_REG(gen, SRU, BUSIF_MODE, 0x20, 0x4), + RSND_GEN1_M_REG(gen, SRU, BUSIF_ADINR, 0x214, 0x40), + + RSND_GEN1_S_REG(gen, ADG, BRRA, 0x00), + RSND_GEN1_S_REG(gen, ADG, BRRB, 0x04), + RSND_GEN1_S_REG(gen, ADG, SSICKR, 0x08), + RSND_GEN1_S_REG(gen, ADG, AUDIO_CLK_SEL0, 0x0c), + RSND_GEN1_S_REG(gen, ADG, AUDIO_CLK_SEL1, 0x10), + RSND_GEN1_S_REG(gen, ADG, AUDIO_CLK_SEL3, 0x18), + RSND_GEN1_S_REG(gen, ADG, AUDIO_CLK_SEL4, 0x1c), + RSND_GEN1_S_REG(gen, ADG, AUDIO_CLK_SEL5, 0x20), + + RSND_GEN1_M_REG(gen, SSI, SSICR, 0x00, 0x40), + RSND_GEN1_M_REG(gen, SSI, SSISR, 0x04, 0x40), + RSND_GEN1_M_REG(gen, SSI, SSITDR, 0x08, 0x40), + RSND_GEN1_M_REG(gen, SSI, SSIRDR, 0x0c, 0x40), + RSND_GEN1_M_REG(gen, SSI, SSIWSR, 0x20, 0x40), + }; + + memset(®c, 0, sizeof(regc)); + regc.reg_bits = 32; + regc.val_bits = 32; + + gen->regmap = devm_regmap_init(dev, &rsnd_regmap_bus, priv, ®c); + if (IS_ERR(gen->regmap)) { + dev_err(dev, "regmap error %ld\n", PTR_ERR(gen->regmap)); + return PTR_ERR(gen->regmap); + } + + for (i = 0; i < RSND_REG_MAX; i++) { + gen->regs[i] = devm_regmap_field_alloc(dev, gen->regmap, regf[i]); + if (IS_ERR(gen->regs[i])) + return PTR_ERR(gen->regs[i]); + + } + + return 0; } static int rsnd_gen1_probe(struct platform_device *pdev, @@ -147,6 +248,7 @@ static int rsnd_gen1_probe(struct platform_device *pdev, struct resource *sru_res; struct resource *adg_res; struct resource *ssi_res; + int ret; /* * map address @@ -163,7 +265,9 @@ static int rsnd_gen1_probe(struct platform_device *pdev, IS_ERR(gen->base[RSND_GEN1_SSI])) return -ENODEV; - rsnd_gen1_reg_map_init(gen); + ret = rsnd_gen1_regmap_init(priv, gen); + if (ret < 0) + return ret; dev_dbg(dev, "Gen1 device probed\n"); dev_dbg(dev, "SRU : %08x => %p\n", sru_res->start, @@ -210,46 +314,12 @@ int rsnd_gen_path_exit(struct rsnd_priv *priv, return gen->ops->path_exit(priv, rdai, io); } -void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, - struct rsnd_mod *mod, - enum rsnd_reg reg) -{ - struct rsnd_gen *gen = rsnd_priv_to_gen(priv); - struct device *dev = rsnd_priv_to_dev(priv); - int index; - u32 offset_id, offset_adr; - - if (reg >= RSND_REG_MAX) { - dev_err(dev, "rsnd_reg reg error\n"); - return NULL; - } - - index = gen->reg_map[reg].index; - offset_id = gen->reg_map[reg].offset_id; - offset_adr = gen->reg_map[reg].offset_adr; - - if (index < 0) { - dev_err(dev, "unsupported reg access %d\n", reg); - return NULL; - } - - if (offset_id && mod) - offset_id *= rsnd_mod_id(mod); - - /* - * index/offset were set on gen1/gen2 - */ - - return gen->base[index] + offset_id + offset_adr; -} - int rsnd_gen_probe(struct platform_device *pdev, struct rcar_snd_info *info, struct rsnd_priv *priv) { struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_gen *gen; - int i; gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL); if (!gen) { @@ -267,14 +337,6 @@ int rsnd_gen_probe(struct platform_device *pdev, priv->gen = gen; - /* - * see - * rsnd_reg_get() - * rsnd_gen_probe() - */ - for (i = 0; i < RSND_REG_MAX; i++) - gen->reg_map[i].index = -1; - return gen->ops->probe(pdev, info, priv); } -- cgit v0.10.2 From 4aa11d67b66a84189d25f301e7ef206c4f541692 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 24 Sep 2013 19:26:08 +0100 Subject: ASoC: tlv320aic23: Convert to direct regmap API usage This moves us towards being able to remove the duplicated register I/O code in ASoC. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c index 3a6be8c..5d430cc 100644 --- a/sound/soc/codecs/tlv320aic23.c +++ b/sound/soc/codecs/tlv320aic23.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -37,11 +38,27 @@ /* * AIC23 register cache */ -static const u16 tlv320aic23_reg[] = { - 0x0097, 0x0097, 0x00F9, 0x00F9, /* 0 */ - 0x001A, 0x0004, 0x0007, 0x0001, /* 4 */ - 0x0020, 0x0000, 0x0000, 0x0000, /* 8 */ - 0x0000, 0x0000, 0x0000, 0x0000, /* 12 */ +static const struct reg_default tlv320aic23_reg[] = { + { 0, 0x0097 }, + { 1, 0x0097 }, + { 2, 0x00F9 }, + { 3, 0x00F9 }, + { 4, 0x001A }, + { 5, 0x0004 }, + { 6, 0x0007 }, + { 7, 0x0001 }, + { 8, 0x0020 }, + { 9, 0x0000 }, +}; + +static const struct regmap_config tlv320aic23_regmap = { + .reg_bits = 7, + .val_bits = 9, + + .max_register = TLV320AIC23_RESET, + .reg_defaults = tlv320aic23_reg, + .num_reg_defaults = ARRAY_SIZE(tlv320aic23_reg), + .cache_type = REGCACHE_RBTREE, }; static const char *rec_src_text[] = { "Line", "Mic" }; @@ -171,7 +188,7 @@ static const struct snd_soc_dapm_route tlv320aic23_intercon[] = { /* AIC23 driver data */ struct aic23 { - enum snd_soc_control_type control_type; + struct regmap *regmap; int mclk; int requested_adc; int requested_dac; @@ -532,7 +549,9 @@ static int tlv320aic23_suspend(struct snd_soc_codec *codec) static int tlv320aic23_resume(struct snd_soc_codec *codec) { - snd_soc_cache_sync(codec); + struct aic23 *aic23 = snd_soc_codec_get_drvdata(codec); + regcache_mark_dirty(aic23->regmap); + regcache_sync(aic23->regmap); tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_STANDBY); return 0; @@ -540,10 +559,9 @@ static int tlv320aic23_resume(struct snd_soc_codec *codec) static int tlv320aic23_probe(struct snd_soc_codec *codec) { - struct aic23 *aic23 = snd_soc_codec_get_drvdata(codec); int ret; - ret = snd_soc_codec_set_cache_io(codec, 7, 9, aic23->control_type); + ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_REGMAP); if (ret < 0) { dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); return ret; @@ -552,16 +570,6 @@ static int tlv320aic23_probe(struct snd_soc_codec *codec) /* Reset codec */ snd_soc_write(codec, TLV320AIC23_RESET, 0); - /* Write the register default value to cache for reserved registers, - * so the write to the these registers are suppressed by the cache - * restore code when it skips writes of default registers. - */ - snd_soc_cache_write(codec, 0x0A, 0); - snd_soc_cache_write(codec, 0x0B, 0); - snd_soc_cache_write(codec, 0x0C, 0); - snd_soc_cache_write(codec, 0x0D, 0); - snd_soc_cache_write(codec, 0x0E, 0); - /* power on device */ tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_STANDBY); @@ -596,9 +604,6 @@ static int tlv320aic23_remove(struct snd_soc_codec *codec) } static struct snd_soc_codec_driver soc_codec_dev_tlv320aic23 = { - .reg_cache_size = ARRAY_SIZE(tlv320aic23_reg), - .reg_word_size = sizeof(u16), - .reg_cache_default = tlv320aic23_reg, .probe = tlv320aic23_probe, .remove = tlv320aic23_remove, .suspend = tlv320aic23_suspend, @@ -629,8 +634,11 @@ static int tlv320aic23_codec_probe(struct i2c_client *i2c, if (aic23 == NULL) return -ENOMEM; + aic23->regmap = devm_regmap_init_i2c(i2c, &tlv320aic23_regmap); + if (IS_ERR(aic23->regmap)) + return PTR_ERR(aic23->regmap); + i2c_set_clientdata(i2c, aic23); - aic23->control_type = SND_SOC_I2C; ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_tlv320aic23, &tlv320aic23_dai, 1); -- cgit v0.10.2 From 4172fe2f8a479e2237459918edc83b027efa8808 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:25 -0700 Subject: EFI stub documentation updates Move efi-stub.txt out of x86 directory and into common directory in preparation for adding ARM EFI stub support. Signed-off-by: Roy Franz Signed-off-by: Matt Fleming diff --git a/Documentation/efi-stub.txt b/Documentation/efi-stub.txt new file mode 100644 index 0000000..44e6bb6 --- /dev/null +++ b/Documentation/efi-stub.txt @@ -0,0 +1,65 @@ + The EFI Boot Stub + --------------------------- + +On the x86 platform, a bzImage can masquerade as a PE/COFF image, +thereby convincing EFI firmware loaders to load it as an EFI +executable. The code that modifies the bzImage header, along with the +EFI-specific entry point that the firmware loader jumps to are +collectively known as the "EFI boot stub", and live in +arch/x86/boot/header.S and arch/x86/boot/compressed/eboot.c, +respectively. + +By using the EFI boot stub it's possible to boot a Linux kernel +without the use of a conventional EFI boot loader, such as grub or +elilo. Since the EFI boot stub performs the jobs of a boot loader, in +a certain sense it *IS* the boot loader. + +The EFI boot stub is enabled with the CONFIG_EFI_STUB kernel option. + + +**** How to install bzImage.efi + +The bzImage located in arch/x86/boot/bzImage must be copied to the EFI +System Partiion (ESP) and renamed with the extension ".efi". Without +the extension the EFI firmware loader will refuse to execute it. It's +not possible to execute bzImage.efi from the usual Linux file systems +because EFI firmware doesn't have support for them. + + +**** Passing kernel parameters from the EFI shell + +Arguments to the kernel can be passed after bzImage.efi, e.g. + + fs0:> bzImage.efi console=ttyS0 root=/dev/sda4 + + +**** The "initrd=" option + +Like most boot loaders, the EFI stub allows the user to specify +multiple initrd files using the "initrd=" option. This is the only EFI +stub-specific command line parameter, everything else is passed to the +kernel when it boots. + +The path to the initrd file must be an absolute path from the +beginning of the ESP, relative path names do not work. Also, the path +is an EFI-style path and directory elements must be separated with +backslashes (\). For example, given the following directory layout, + +fs0:> + Kernels\ + bzImage.efi + initrd-large.img + + Ramdisks\ + initrd-small.img + initrd-medium.img + +to boot with the initrd-large.img file if the current working +directory is fs0:\Kernels, the following command must be used, + + fs0:\Kernels> bzImage.efi initrd=\Kernels\initrd-large.img + +Notice how bzImage.efi can be specified with a relative path. That's +because the image we're executing is interpreted by the EFI shell, +which understands relative paths, whereas the rest of the command line +is passed to bzImage.efi. diff --git a/Documentation/x86/efi-stub.txt b/Documentation/x86/efi-stub.txt deleted file mode 100644 index 44e6bb6..0000000 --- a/Documentation/x86/efi-stub.txt +++ /dev/null @@ -1,65 +0,0 @@ - The EFI Boot Stub - --------------------------- - -On the x86 platform, a bzImage can masquerade as a PE/COFF image, -thereby convincing EFI firmware loaders to load it as an EFI -executable. The code that modifies the bzImage header, along with the -EFI-specific entry point that the firmware loader jumps to are -collectively known as the "EFI boot stub", and live in -arch/x86/boot/header.S and arch/x86/boot/compressed/eboot.c, -respectively. - -By using the EFI boot stub it's possible to boot a Linux kernel -without the use of a conventional EFI boot loader, such as grub or -elilo. Since the EFI boot stub performs the jobs of a boot loader, in -a certain sense it *IS* the boot loader. - -The EFI boot stub is enabled with the CONFIG_EFI_STUB kernel option. - - -**** How to install bzImage.efi - -The bzImage located in arch/x86/boot/bzImage must be copied to the EFI -System Partiion (ESP) and renamed with the extension ".efi". Without -the extension the EFI firmware loader will refuse to execute it. It's -not possible to execute bzImage.efi from the usual Linux file systems -because EFI firmware doesn't have support for them. - - -**** Passing kernel parameters from the EFI shell - -Arguments to the kernel can be passed after bzImage.efi, e.g. - - fs0:> bzImage.efi console=ttyS0 root=/dev/sda4 - - -**** The "initrd=" option - -Like most boot loaders, the EFI stub allows the user to specify -multiple initrd files using the "initrd=" option. This is the only EFI -stub-specific command line parameter, everything else is passed to the -kernel when it boots. - -The path to the initrd file must be an absolute path from the -beginning of the ESP, relative path names do not work. Also, the path -is an EFI-style path and directory elements must be separated with -backslashes (\). For example, given the following directory layout, - -fs0:> - Kernels\ - bzImage.efi - initrd-large.img - - Ramdisks\ - initrd-small.img - initrd-medium.img - -to boot with the initrd-large.img file if the current working -directory is fs0:\Kernels, the following command must be used, - - fs0:\Kernels> bzImage.efi initrd=\Kernels\initrd-large.img - -Notice how bzImage.efi can be specified with a relative path. That's -because the image we're executing is interpreted by the EFI shell, -which understands relative paths, whereas the rest of the command line -is passed to bzImage.efi. diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index b32ebf9..ec65b51 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1579,7 +1579,7 @@ config EFI_STUB This kernel feature allows a bzImage to be loaded directly by EFI firmware without the use of a bootloader. - See Documentation/x86/efi-stub.txt for more information. + See Documentation/efi-stub.txt for more information. config SECCOMP def_bool y -- cgit v0.10.2 From ed37ddffe201bfad7be3c45bc08bd65b5298adca Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:26 -0700 Subject: efi: Add proper definitions for some EFI function pointers. The x86/AMD64 EFI stubs must use a call wrapper to convert between the Linux and EFI ABIs, so void pointers are sufficient. For ARM, the ABIs are compatible, so we can directly invoke the function pointers. The functions that are used by the ARM stub are updated to match the EFI definitions. Also add some EFI types used by EFI functions. Signed-off-by: Roy Franz Acked-by: Mark Salter Reviewed-by: Grant Likely Signed-off-by: Matt Fleming diff --git a/arch/x86/boot/compressed/eboot.h b/arch/x86/boot/compressed/eboot.h index e5b0a8f..0226510 100644 --- a/arch/x86/boot/compressed/eboot.h +++ b/arch/x86/boot/compressed/eboot.h @@ -10,8 +10,6 @@ #define SEG_GRANULARITY_4KB (1 << 0) #define DESC_TYPE_CODE_DATA (1 << 0) - -#define EFI_PAGE_SIZE (1UL << EFI_PAGE_SHIFT) #define EFI_READ_CHUNK_SIZE (1024 * 1024) #define EFI_CONSOLE_OUT_DEVICE_GUID \ @@ -62,10 +60,4 @@ struct efi_uga_draw_protocol { void *blt; }; -struct efi_simple_text_output_protocol { - void *reset; - void *output_string; - void *test_string; -}; - #endif /* BOOT_COMPRESSED_EBOOT_H */ diff --git a/include/linux/efi.h b/include/linux/efi.h index c084b6d..bc5687d 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -39,6 +39,8 @@ typedef unsigned long efi_status_t; typedef u8 efi_bool_t; typedef u16 efi_char16_t; /* UNICODE character */ +typedef u64 efi_physical_addr_t; +typedef void *efi_handle_t; typedef struct { @@ -96,6 +98,7 @@ typedef struct { #define EFI_MEMORY_DESCRIPTOR_VERSION 1 #define EFI_PAGE_SHIFT 12 +#define EFI_PAGE_SIZE (1UL << EFI_PAGE_SHIFT) typedef struct { u32 type; @@ -157,11 +160,13 @@ typedef struct { efi_table_hdr_t hdr; void *raise_tpl; void *restore_tpl; - void *allocate_pages; - void *free_pages; - void *get_memory_map; - void *allocate_pool; - void *free_pool; + efi_status_t (*allocate_pages)(int, int, unsigned long, + efi_physical_addr_t *); + efi_status_t (*free_pages)(efi_physical_addr_t, unsigned long); + efi_status_t (*get_memory_map)(unsigned long *, void *, unsigned long *, + unsigned long *, u32 *); + efi_status_t (*allocate_pool)(int, unsigned long, void **); + efi_status_t (*free_pool)(void *); void *create_event; void *set_timer; void *wait_for_event; @@ -171,7 +176,7 @@ typedef struct { void *install_protocol_interface; void *reinstall_protocol_interface; void *uninstall_protocol_interface; - void *handle_protocol; + efi_status_t (*handle_protocol)(efi_handle_t, efi_guid_t *, void **); void *__reserved; void *register_protocol_notify; void *locate_handle; @@ -181,7 +186,7 @@ typedef struct { void *start_image; void *exit; void *unload_image; - void *exit_boot_services; + efi_status_t (*exit_boot_services)(efi_handle_t, unsigned long); void *get_next_monotonic_count; void *stall; void *set_watchdog_timer; @@ -494,10 +499,6 @@ typedef struct { unsigned long unload; } efi_loaded_image_t; -typedef struct { - u64 revision; - void *open_volume; -} efi_file_io_interface_t; typedef struct { u64 size; @@ -510,20 +511,30 @@ typedef struct { efi_char16_t filename[1]; } efi_file_info_t; -typedef struct { +typedef struct _efi_file_handle { u64 revision; - void *open; - void *close; + efi_status_t (*open)(struct _efi_file_handle *, + struct _efi_file_handle **, + efi_char16_t *, u64, u64); + efi_status_t (*close)(struct _efi_file_handle *); void *delete; - void *read; + efi_status_t (*read)(struct _efi_file_handle *, unsigned long *, + void *); void *write; void *get_position; void *set_position; - void *get_info; + efi_status_t (*get_info)(struct _efi_file_handle *, efi_guid_t *, + unsigned long *, void *); void *set_info; void *flush; } efi_file_handle_t; +typedef struct _efi_file_io_interface { + u64 revision; + int (*open_volume)(struct _efi_file_io_interface *, + efi_file_handle_t **); +} efi_file_io_interface_t; + #define EFI_FILE_MODE_READ 0x0000000000000001 #define EFI_FILE_MODE_WRITE 0x0000000000000002 #define EFI_FILE_MODE_CREATE 0x8000000000000000 @@ -792,6 +803,13 @@ struct efivar_entry { struct kobject kobj; }; + +struct efi_simple_text_output_protocol { + void *reset; + efi_status_t (*output_string)(void *, void *); + void *test_string; +}; + extern struct list_head efivar_sysfs_list; static inline void -- cgit v0.10.2 From 7721da4c1ebf96ff815987011c1a0edef596b50a Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:27 -0700 Subject: efi: Move common EFI stub code from x86 arch code to common location No code changes made, just moving functions and #define from x86 arch directory to common location. Code is shared using #include, similar to how decompression code is shared among architectures. Signed-off-by: Roy Franz Acked-by: Mark Salter Reviewed-by: Grant Likely Signed-off-by: Matt Fleming diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index b7388a4..ab0eefc 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -19,214 +19,10 @@ static efi_system_table_t *sys_table; -static void efi_char16_printk(efi_char16_t *str) -{ - struct efi_simple_text_output_protocol *out; - - out = (struct efi_simple_text_output_protocol *)sys_table->con_out; - efi_call_phys2(out->output_string, out, str); -} - -static void efi_printk(char *str) -{ - char *s8; - - for (s8 = str; *s8; s8++) { - efi_char16_t ch[2] = { 0 }; - - ch[0] = *s8; - if (*s8 == '\n') { - efi_char16_t nl[2] = { '\r', 0 }; - efi_char16_printk(nl); - } - - efi_char16_printk(ch); - } -} - -static efi_status_t __get_map(efi_memory_desc_t **map, unsigned long *map_size, - unsigned long *desc_size) -{ - efi_memory_desc_t *m = NULL; - efi_status_t status; - unsigned long key; - u32 desc_version; - - *map_size = sizeof(*m) * 32; -again: - /* - * Add an additional efi_memory_desc_t because we're doing an - * allocation which may be in a new descriptor region. - */ - *map_size += sizeof(*m); - status = efi_call_phys3(sys_table->boottime->allocate_pool, - EFI_LOADER_DATA, *map_size, (void **)&m); - if (status != EFI_SUCCESS) - goto fail; - - status = efi_call_phys5(sys_table->boottime->get_memory_map, map_size, - m, &key, desc_size, &desc_version); - if (status == EFI_BUFFER_TOO_SMALL) { - efi_call_phys1(sys_table->boottime->free_pool, m); - goto again; - } - - if (status != EFI_SUCCESS) - efi_call_phys1(sys_table->boottime->free_pool, m); - -fail: - *map = m; - return status; -} - -/* - * Allocate at the highest possible address that is not above 'max'. - */ -static efi_status_t high_alloc(unsigned long size, unsigned long align, - unsigned long *addr, unsigned long max) -{ - unsigned long map_size, desc_size; - efi_memory_desc_t *map; - efi_status_t status; - unsigned long nr_pages; - u64 max_addr = 0; - int i; - - status = __get_map(&map, &map_size, &desc_size); - if (status != EFI_SUCCESS) - goto fail; - - nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; -again: - for (i = 0; i < map_size / desc_size; i++) { - efi_memory_desc_t *desc; - unsigned long m = (unsigned long)map; - u64 start, end; - - desc = (efi_memory_desc_t *)(m + (i * desc_size)); - if (desc->type != EFI_CONVENTIONAL_MEMORY) - continue; - - if (desc->num_pages < nr_pages) - continue; - start = desc->phys_addr; - end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT); +#include "../../../../drivers/firmware/efi/efi-stub-helper.c" - if ((start + size) > end || (start + size) > max) - continue; - - if (end - size > max) - end = max; - - if (round_down(end - size, align) < start) - continue; - - start = round_down(end - size, align); - - /* - * Don't allocate at 0x0. It will confuse code that - * checks pointers against NULL. - */ - if (start == 0x0) - continue; - - if (start > max_addr) - max_addr = start; - } - - if (!max_addr) - status = EFI_NOT_FOUND; - else { - status = efi_call_phys4(sys_table->boottime->allocate_pages, - EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, - nr_pages, &max_addr); - if (status != EFI_SUCCESS) { - max = max_addr; - max_addr = 0; - goto again; - } - - *addr = max_addr; - } - -free_pool: - efi_call_phys1(sys_table->boottime->free_pool, map); - -fail: - return status; -} - -/* - * Allocate at the lowest possible address. - */ -static efi_status_t low_alloc(unsigned long size, unsigned long align, - unsigned long *addr) -{ - unsigned long map_size, desc_size; - efi_memory_desc_t *map; - efi_status_t status; - unsigned long nr_pages; - int i; - - status = __get_map(&map, &map_size, &desc_size); - if (status != EFI_SUCCESS) - goto fail; - - nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; - for (i = 0; i < map_size / desc_size; i++) { - efi_memory_desc_t *desc; - unsigned long m = (unsigned long)map; - u64 start, end; - - desc = (efi_memory_desc_t *)(m + (i * desc_size)); - - if (desc->type != EFI_CONVENTIONAL_MEMORY) - continue; - - if (desc->num_pages < nr_pages) - continue; - - start = desc->phys_addr; - end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT); - - /* - * Don't allocate at 0x0. It will confuse code that - * checks pointers against NULL. Skip the first 8 - * bytes so we start at a nice even number. - */ - if (start == 0x0) - start += 8; - - start = round_up(start, align); - if ((start + size) > end) - continue; - - status = efi_call_phys4(sys_table->boottime->allocate_pages, - EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, - nr_pages, &start); - if (status == EFI_SUCCESS) { - *addr = start; - break; - } - } - if (i == map_size / desc_size) - status = EFI_NOT_FOUND; - -free_pool: - efi_call_phys1(sys_table->boottime->free_pool, map); -fail: - return status; -} - -static void low_free(unsigned long size, unsigned long addr) -{ - unsigned long nr_pages; - - nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; - efi_call_phys2(sys_table->boottime->free_pages, addr, nr_pages); -} static void find_bits(unsigned long mask, u8 *pos, u8 *size) { @@ -624,242 +420,6 @@ void setup_graphics(struct boot_params *boot_params) } } -struct initrd { - efi_file_handle_t *handle; - u64 size; -}; - -/* - * Check the cmdline for a LILO-style initrd= arguments. - * - * We only support loading an initrd from the same filesystem as the - * kernel image. - */ -static efi_status_t handle_ramdisks(efi_loaded_image_t *image, - struct setup_header *hdr) -{ - struct initrd *initrds; - unsigned long initrd_addr; - efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; - u64 initrd_total; - efi_file_io_interface_t *io; - efi_file_handle_t *fh; - efi_status_t status; - int nr_initrds; - char *str; - int i, j, k; - - initrd_addr = 0; - initrd_total = 0; - - str = (char *)(unsigned long)hdr->cmd_line_ptr; - - j = 0; /* See close_handles */ - - if (!str || !*str) - return EFI_SUCCESS; - - for (nr_initrds = 0; *str; nr_initrds++) { - str = strstr(str, "initrd="); - if (!str) - break; - - str += 7; - - /* Skip any leading slashes */ - while (*str == '/' || *str == '\\') - str++; - - while (*str && *str != ' ' && *str != '\n') - str++; - } - - if (!nr_initrds) - return EFI_SUCCESS; - - status = efi_call_phys3(sys_table->boottime->allocate_pool, - EFI_LOADER_DATA, - nr_initrds * sizeof(*initrds), - &initrds); - if (status != EFI_SUCCESS) { - efi_printk("Failed to alloc mem for initrds\n"); - goto fail; - } - - str = (char *)(unsigned long)hdr->cmd_line_ptr; - for (i = 0; i < nr_initrds; i++) { - struct initrd *initrd; - efi_file_handle_t *h; - efi_file_info_t *info; - efi_char16_t filename_16[256]; - unsigned long info_sz; - efi_guid_t info_guid = EFI_FILE_INFO_ID; - efi_char16_t *p; - u64 file_sz; - - str = strstr(str, "initrd="); - if (!str) - break; - - str += 7; - - initrd = &initrds[i]; - p = filename_16; - - /* Skip any leading slashes */ - while (*str == '/' || *str == '\\') - str++; - - while (*str && *str != ' ' && *str != '\n') { - if ((u8 *)p >= (u8 *)filename_16 + sizeof(filename_16)) - break; - - if (*str == '/') { - *p++ = '\\'; - *str++; - } else { - *p++ = *str++; - } - } - - *p = '\0'; - - /* Only open the volume once. */ - if (!i) { - efi_boot_services_t *boottime; - - boottime = sys_table->boottime; - - status = efi_call_phys3(boottime->handle_protocol, - image->device_handle, &fs_proto, &io); - if (status != EFI_SUCCESS) { - efi_printk("Failed to handle fs_proto\n"); - goto free_initrds; - } - - status = efi_call_phys2(io->open_volume, io, &fh); - if (status != EFI_SUCCESS) { - efi_printk("Failed to open volume\n"); - goto free_initrds; - } - } - - status = efi_call_phys5(fh->open, fh, &h, filename_16, - EFI_FILE_MODE_READ, (u64)0); - if (status != EFI_SUCCESS) { - efi_printk("Failed to open initrd file: "); - efi_char16_printk(filename_16); - efi_printk("\n"); - goto close_handles; - } - - initrd->handle = h; - - info_sz = 0; - status = efi_call_phys4(h->get_info, h, &info_guid, - &info_sz, NULL); - if (status != EFI_BUFFER_TOO_SMALL) { - efi_printk("Failed to get initrd info size\n"); - goto close_handles; - } - -grow: - status = efi_call_phys3(sys_table->boottime->allocate_pool, - EFI_LOADER_DATA, info_sz, &info); - if (status != EFI_SUCCESS) { - efi_printk("Failed to alloc mem for initrd info\n"); - goto close_handles; - } - - status = efi_call_phys4(h->get_info, h, &info_guid, - &info_sz, info); - if (status == EFI_BUFFER_TOO_SMALL) { - efi_call_phys1(sys_table->boottime->free_pool, info); - goto grow; - } - - file_sz = info->file_size; - efi_call_phys1(sys_table->boottime->free_pool, info); - - if (status != EFI_SUCCESS) { - efi_printk("Failed to get initrd info\n"); - goto close_handles; - } - - initrd->size = file_sz; - initrd_total += file_sz; - } - - if (initrd_total) { - unsigned long addr; - - /* - * Multiple initrd's need to be at consecutive - * addresses in memory, so allocate enough memory for - * all the initrd's. - */ - status = high_alloc(initrd_total, 0x1000, - &initrd_addr, hdr->initrd_addr_max); - if (status != EFI_SUCCESS) { - efi_printk("Failed to alloc highmem for initrds\n"); - goto close_handles; - } - - /* We've run out of free low memory. */ - if (initrd_addr > hdr->initrd_addr_max) { - efi_printk("We've run out of free low memory\n"); - status = EFI_INVALID_PARAMETER; - goto free_initrd_total; - } - - addr = initrd_addr; - for (j = 0; j < nr_initrds; j++) { - u64 size; - - size = initrds[j].size; - while (size) { - u64 chunksize; - if (size > EFI_READ_CHUNK_SIZE) - chunksize = EFI_READ_CHUNK_SIZE; - else - chunksize = size; - status = efi_call_phys3(fh->read, - initrds[j].handle, - &chunksize, addr); - if (status != EFI_SUCCESS) { - efi_printk("Failed to read initrd\n"); - goto free_initrd_total; - } - addr += chunksize; - size -= chunksize; - } - - efi_call_phys1(fh->close, initrds[j].handle); - } - - } - - efi_call_phys1(sys_table->boottime->free_pool, initrds); - - hdr->ramdisk_image = initrd_addr; - hdr->ramdisk_size = initrd_total; - - return status; - -free_initrd_total: - low_free(initrd_total, initrd_addr); - -close_handles: - for (k = j; k < i; k++) - efi_call_phys1(fh->close, initrds[k].handle); -free_initrds: - efi_call_phys1(sys_table->boottime->free_pool, initrds); -fail: - hdr->ramdisk_image = 0; - hdr->ramdisk_size = 0; - - return status; -} /* * Because the x86 boot code expects to be passed a boot_params we diff --git a/arch/x86/boot/compressed/eboot.h b/arch/x86/boot/compressed/eboot.h index 0226510..81b6b65 100644 --- a/arch/x86/boot/compressed/eboot.h +++ b/arch/x86/boot/compressed/eboot.h @@ -10,7 +10,6 @@ #define SEG_GRANULARITY_4KB (1 << 0) #define DESC_TYPE_CODE_DATA (1 << 0) -#define EFI_READ_CHUNK_SIZE (1024 * 1024) #define EFI_CONSOLE_OUT_DEVICE_GUID \ EFI_GUID(0xd3b36f2c, 0xd551, 0x11d4, 0x9a, 0x46, 0x0, 0x90, 0x27, \ diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c new file mode 100644 index 0000000..8a83387 --- /dev/null +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -0,0 +1,463 @@ +/* + * Helper functions used by the EFI stub on multiple + * architectures. This should be #included by the EFI stub + * implementation files. + * + * Copyright 2011 Intel Corporation; author Matt Fleming + * + * This file is part of the Linux kernel, and is made available + * under the terms of the GNU General Public License version 2. + * + */ +#define EFI_READ_CHUNK_SIZE (1024 * 1024) + +struct initrd { + efi_file_handle_t *handle; + u64 size; +}; + + + + +static void efi_char16_printk(efi_char16_t *str) +{ + struct efi_simple_text_output_protocol *out; + + out = (struct efi_simple_text_output_protocol *)sys_table->con_out; + efi_call_phys2(out->output_string, out, str); +} + +static void efi_printk(char *str) +{ + char *s8; + + for (s8 = str; *s8; s8++) { + efi_char16_t ch[2] = { 0 }; + + ch[0] = *s8; + if (*s8 == '\n') { + efi_char16_t nl[2] = { '\r', 0 }; + efi_char16_printk(nl); + } + + efi_char16_printk(ch); + } +} + + +static efi_status_t __get_map(efi_memory_desc_t **map, unsigned long *map_size, + unsigned long *desc_size) +{ + efi_memory_desc_t *m = NULL; + efi_status_t status; + unsigned long key; + u32 desc_version; + + *map_size = sizeof(*m) * 32; +again: + /* + * Add an additional efi_memory_desc_t because we're doing an + * allocation which may be in a new descriptor region. + */ + *map_size += sizeof(*m); + status = efi_call_phys3(sys_table->boottime->allocate_pool, + EFI_LOADER_DATA, *map_size, (void **)&m); + if (status != EFI_SUCCESS) + goto fail; + + status = efi_call_phys5(sys_table->boottime->get_memory_map, map_size, + m, &key, desc_size, &desc_version); + if (status == EFI_BUFFER_TOO_SMALL) { + efi_call_phys1(sys_table->boottime->free_pool, m); + goto again; + } + + if (status != EFI_SUCCESS) + efi_call_phys1(sys_table->boottime->free_pool, m); + +fail: + *map = m; + return status; +} + +/* + * Allocate at the highest possible address that is not above 'max'. + */ +static efi_status_t high_alloc(unsigned long size, unsigned long align, + unsigned long *addr, unsigned long max) +{ + unsigned long map_size, desc_size; + efi_memory_desc_t *map; + efi_status_t status; + unsigned long nr_pages; + u64 max_addr = 0; + int i; + + status = __get_map(&map, &map_size, &desc_size); + if (status != EFI_SUCCESS) + goto fail; + + nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; +again: + for (i = 0; i < map_size / desc_size; i++) { + efi_memory_desc_t *desc; + unsigned long m = (unsigned long)map; + u64 start, end; + + desc = (efi_memory_desc_t *)(m + (i * desc_size)); + if (desc->type != EFI_CONVENTIONAL_MEMORY) + continue; + + if (desc->num_pages < nr_pages) + continue; + + start = desc->phys_addr; + end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT); + + if ((start + size) > end || (start + size) > max) + continue; + + if (end - size > max) + end = max; + + if (round_down(end - size, align) < start) + continue; + + start = round_down(end - size, align); + + /* + * Don't allocate at 0x0. It will confuse code that + * checks pointers against NULL. + */ + if (start == 0x0) + continue; + + if (start > max_addr) + max_addr = start; + } + + if (!max_addr) + status = EFI_NOT_FOUND; + else { + status = efi_call_phys4(sys_table->boottime->allocate_pages, + EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, + nr_pages, &max_addr); + if (status != EFI_SUCCESS) { + max = max_addr; + max_addr = 0; + goto again; + } + + *addr = max_addr; + } + +free_pool: + efi_call_phys1(sys_table->boottime->free_pool, map); + +fail: + return status; +} + +/* + * Allocate at the lowest possible address. + */ +static efi_status_t low_alloc(unsigned long size, unsigned long align, + unsigned long *addr) +{ + unsigned long map_size, desc_size; + efi_memory_desc_t *map; + efi_status_t status; + unsigned long nr_pages; + int i; + + status = __get_map(&map, &map_size, &desc_size); + if (status != EFI_SUCCESS) + goto fail; + + nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; + for (i = 0; i < map_size / desc_size; i++) { + efi_memory_desc_t *desc; + unsigned long m = (unsigned long)map; + u64 start, end; + + desc = (efi_memory_desc_t *)(m + (i * desc_size)); + + if (desc->type != EFI_CONVENTIONAL_MEMORY) + continue; + + if (desc->num_pages < nr_pages) + continue; + + start = desc->phys_addr; + end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT); + + /* + * Don't allocate at 0x0. It will confuse code that + * checks pointers against NULL. Skip the first 8 + * bytes so we start at a nice even number. + */ + if (start == 0x0) + start += 8; + + start = round_up(start, align); + if ((start + size) > end) + continue; + + status = efi_call_phys4(sys_table->boottime->allocate_pages, + EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, + nr_pages, &start); + if (status == EFI_SUCCESS) { + *addr = start; + break; + } + } + + if (i == map_size / desc_size) + status = EFI_NOT_FOUND; + +free_pool: + efi_call_phys1(sys_table->boottime->free_pool, map); +fail: + return status; +} + +static void low_free(unsigned long size, unsigned long addr) +{ + unsigned long nr_pages; + + nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; + efi_call_phys2(sys_table->boottime->free_pages, addr, nr_pages); +} + + +/* + * Check the cmdline for a LILO-style initrd= arguments. + * + * We only support loading an initrd from the same filesystem as the + * kernel image. + */ +static efi_status_t handle_ramdisks(efi_loaded_image_t *image, + struct setup_header *hdr) +{ + struct initrd *initrds; + unsigned long initrd_addr; + efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; + u64 initrd_total; + efi_file_io_interface_t *io; + efi_file_handle_t *fh; + efi_status_t status; + int nr_initrds; + char *str; + int i, j, k; + + initrd_addr = 0; + initrd_total = 0; + + str = (char *)(unsigned long)hdr->cmd_line_ptr; + + j = 0; /* See close_handles */ + + if (!str || !*str) + return EFI_SUCCESS; + + for (nr_initrds = 0; *str; nr_initrds++) { + str = strstr(str, "initrd="); + if (!str) + break; + + str += 7; + + /* Skip any leading slashes */ + while (*str == '/' || *str == '\\') + str++; + + while (*str && *str != ' ' && *str != '\n') + str++; + } + + if (!nr_initrds) + return EFI_SUCCESS; + + status = efi_call_phys3(sys_table->boottime->allocate_pool, + EFI_LOADER_DATA, + nr_initrds * sizeof(*initrds), + &initrds); + if (status != EFI_SUCCESS) { + efi_printk("Failed to alloc mem for initrds\n"); + goto fail; + } + + str = (char *)(unsigned long)hdr->cmd_line_ptr; + for (i = 0; i < nr_initrds; i++) { + struct initrd *initrd; + efi_file_handle_t *h; + efi_file_info_t *info; + efi_char16_t filename_16[256]; + unsigned long info_sz; + efi_guid_t info_guid = EFI_FILE_INFO_ID; + efi_char16_t *p; + u64 file_sz; + + str = strstr(str, "initrd="); + if (!str) + break; + + str += 7; + + initrd = &initrds[i]; + p = filename_16; + + /* Skip any leading slashes */ + while (*str == '/' || *str == '\\') + str++; + + while (*str && *str != ' ' && *str != '\n') { + if ((u8 *)p >= (u8 *)filename_16 + sizeof(filename_16)) + break; + + if (*str == '/') { + *p++ = '\\'; + *str++; + } else { + *p++ = *str++; + } + } + + *p = '\0'; + + /* Only open the volume once. */ + if (!i) { + efi_boot_services_t *boottime; + + boottime = sys_table->boottime; + + status = efi_call_phys3(boottime->handle_protocol, + image->device_handle, &fs_proto, &io); + if (status != EFI_SUCCESS) { + efi_printk("Failed to handle fs_proto\n"); + goto free_initrds; + } + + status = efi_call_phys2(io->open_volume, io, &fh); + if (status != EFI_SUCCESS) { + efi_printk("Failed to open volume\n"); + goto free_initrds; + } + } + + status = efi_call_phys5(fh->open, fh, &h, filename_16, + EFI_FILE_MODE_READ, (u64)0); + if (status != EFI_SUCCESS) { + efi_printk("Failed to open initrd file: "); + efi_char16_printk(filename_16); + efi_printk("\n"); + goto close_handles; + } + + initrd->handle = h; + + info_sz = 0; + status = efi_call_phys4(h->get_info, h, &info_guid, + &info_sz, NULL); + if (status != EFI_BUFFER_TOO_SMALL) { + efi_printk("Failed to get initrd info size\n"); + goto close_handles; + } + +grow: + status = efi_call_phys3(sys_table->boottime->allocate_pool, + EFI_LOADER_DATA, info_sz, &info); + if (status != EFI_SUCCESS) { + efi_printk("Failed to alloc mem for initrd info\n"); + goto close_handles; + } + + status = efi_call_phys4(h->get_info, h, &info_guid, + &info_sz, info); + if (status == EFI_BUFFER_TOO_SMALL) { + efi_call_phys1(sys_table->boottime->free_pool, info); + goto grow; + } + + file_sz = info->file_size; + efi_call_phys1(sys_table->boottime->free_pool, info); + + if (status != EFI_SUCCESS) { + efi_printk("Failed to get initrd info\n"); + goto close_handles; + } + + initrd->size = file_sz; + initrd_total += file_sz; + } + + if (initrd_total) { + unsigned long addr; + + /* + * Multiple initrd's need to be at consecutive + * addresses in memory, so allocate enough memory for + * all the initrd's. + */ + status = high_alloc(initrd_total, 0x1000, + &initrd_addr, hdr->initrd_addr_max); + if (status != EFI_SUCCESS) { + efi_printk("Failed to alloc highmem for initrds\n"); + goto close_handles; + } + + /* We've run out of free low memory. */ + if (initrd_addr > hdr->initrd_addr_max) { + efi_printk("We've run out of free low memory\n"); + status = EFI_INVALID_PARAMETER; + goto free_initrd_total; + } + + addr = initrd_addr; + for (j = 0; j < nr_initrds; j++) { + u64 size; + + size = initrds[j].size; + while (size) { + u64 chunksize; + if (size > EFI_READ_CHUNK_SIZE) + chunksize = EFI_READ_CHUNK_SIZE; + else + chunksize = size; + status = efi_call_phys3(fh->read, + initrds[j].handle, + &chunksize, addr); + if (status != EFI_SUCCESS) { + efi_printk("Failed to read initrd\n"); + goto free_initrd_total; + } + addr += chunksize; + size -= chunksize; + } + + efi_call_phys1(fh->close, initrds[j].handle); + } + + } + + efi_call_phys1(sys_table->boottime->free_pool, initrds); + + hdr->ramdisk_image = initrd_addr; + hdr->ramdisk_size = initrd_total; + + return status; + +free_initrd_total: + low_free(initrd_total, initrd_addr); + +close_handles: + for (k = j; k < i; k++) + efi_call_phys1(fh->close, initrds[k].handle); +free_initrds: + efi_call_phys1(sys_table->boottime->free_pool, initrds); +fail: + hdr->ramdisk_image = 0; + hdr->ramdisk_size = 0; + + return status; +} -- cgit v0.10.2 From 876dc36aceb1682cb01426f507f369aec4f68c76 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:28 -0700 Subject: efi: Add system table pointer argument to shared functions. Add system table pointer argument to shared EFI stub related functions so they no longer use a global system table pointer as they did when part of eboot.c. For the ARM EFI stub this allows us to avoid global variables completely and thereby not have to deal with GOT fixups. Not having the EFI stub fixup its GOT, which is shared with the decompressor, simplifies the relocating of the zImage to a bootable address. Signed-off-by: Roy Franz Signed-off-by: Matt Fleming diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index ab0eefc..65b6a34 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -453,13 +453,13 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table) status = efi_call_phys3(sys_table->boottime->handle_protocol, handle, &proto, (void *)&image); if (status != EFI_SUCCESS) { - efi_printk("Failed to get handle for LOADED_IMAGE_PROTOCOL\n"); + efi_printk(sys_table, "Failed to get handle for LOADED_IMAGE_PROTOCOL\n"); return NULL; } - status = low_alloc(0x4000, 1, (unsigned long *)&boot_params); + status = low_alloc(sys_table, 0x4000, 1, (unsigned long *)&boot_params); if (status != EFI_SUCCESS) { - efi_printk("Failed to alloc lowmem for boot params\n"); + efi_printk(sys_table, "Failed to alloc lowmem for boot params\n"); return NULL; } @@ -503,9 +503,10 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table) options_size++; /* NUL termination */ - status = low_alloc(options_size, 1, &cmdline); + status = low_alloc(sys_table, options_size, 1, + &cmdline); if (status != EFI_SUCCESS) { - efi_printk("Failed to alloc mem for cmdline\n"); + efi_printk(sys_table, "Failed to alloc mem for cmdline\n"); goto fail; } @@ -529,16 +530,16 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table) memset(sdt, 0, sizeof(*sdt)); - status = handle_ramdisks(image, hdr); + status = handle_ramdisks(sys_table, image, hdr); if (status != EFI_SUCCESS) goto fail2; return boot_params; fail2: if (options_size) - low_free(options_size, hdr->cmd_line_ptr); + low_free(sys_table, options_size, hdr->cmd_line_ptr); fail: - low_free(0x4000, (unsigned long)boot_params); + low_free(sys_table, 0x4000, (unsigned long)boot_params); return NULL; } @@ -561,7 +562,7 @@ static efi_status_t exit_boot(struct boot_params *boot_params, again: size += sizeof(*mem_map) * 2; _size = size; - status = low_alloc(size, 1, (unsigned long *)&mem_map); + status = low_alloc(sys_table, size, 1, (unsigned long *)&mem_map); if (status != EFI_SUCCESS) return status; @@ -569,7 +570,7 @@ get_map: status = efi_call_phys5(sys_table->boottime->get_memory_map, &size, mem_map, &key, &desc_size, &desc_version); if (status == EFI_BUFFER_TOO_SMALL) { - low_free(_size, (unsigned long)mem_map); + low_free(sys_table, _size, (unsigned long)mem_map); goto again; } @@ -671,7 +672,7 @@ get_map: return EFI_SUCCESS; free_mem_map: - low_free(_size, (unsigned long)mem_map); + low_free(sys_table, _size, (unsigned long)mem_map); return status; } @@ -694,10 +695,10 @@ static efi_status_t relocate_kernel(struct setup_header *hdr) EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, nr_pages, &start); if (status != EFI_SUCCESS) { - status = low_alloc(hdr->init_size, hdr->kernel_alignment, - &start); + status = low_alloc(sys_table, hdr->init_size, + hdr->kernel_alignment, &start); if (status != EFI_SUCCESS) - efi_printk("Failed to alloc mem for kernel\n"); + efi_printk(sys_table, "Failed to alloc mem for kernel\n"); } if (status == EFI_SUCCESS) @@ -737,14 +738,15 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table, EFI_LOADER_DATA, sizeof(*gdt), (void **)&gdt); if (status != EFI_SUCCESS) { - efi_printk("Failed to alloc mem for gdt structure\n"); + efi_printk(sys_table, "Failed to alloc mem for gdt structure\n"); goto fail; } gdt->size = 0x800; - status = low_alloc(gdt->size, 8, (unsigned long *)&gdt->address); + status = low_alloc(sys_table, gdt->size, 8, + (unsigned long *)&gdt->address); if (status != EFI_SUCCESS) { - efi_printk("Failed to alloc mem for gdt\n"); + efi_printk(sys_table, "Failed to alloc mem for gdt\n"); goto fail; } @@ -752,7 +754,7 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table, EFI_LOADER_DATA, sizeof(*idt), (void **)&idt); if (status != EFI_SUCCESS) { - efi_printk("Failed to alloc mem for idt structure\n"); + efi_printk(sys_table, "Failed to alloc mem for idt structure\n"); goto fail; } diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index 8a83387..05c539e 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -19,15 +19,16 @@ struct initrd { -static void efi_char16_printk(efi_char16_t *str) +static void efi_char16_printk(efi_system_table_t *sys_table_arg, + efi_char16_t *str) { struct efi_simple_text_output_protocol *out; - out = (struct efi_simple_text_output_protocol *)sys_table->con_out; + out = (struct efi_simple_text_output_protocol *)sys_table_arg->con_out; efi_call_phys2(out->output_string, out, str); } -static void efi_printk(char *str) +static void efi_printk(efi_system_table_t *sys_table_arg, char *str) { char *s8; @@ -37,15 +38,17 @@ static void efi_printk(char *str) ch[0] = *s8; if (*s8 == '\n') { efi_char16_t nl[2] = { '\r', 0 }; - efi_char16_printk(nl); + efi_char16_printk(sys_table_arg, nl); } - efi_char16_printk(ch); + efi_char16_printk(sys_table_arg, ch); } } -static efi_status_t __get_map(efi_memory_desc_t **map, unsigned long *map_size, +static efi_status_t __get_map(efi_system_table_t *sys_table_arg, + efi_memory_desc_t **map, + unsigned long *map_size, unsigned long *desc_size) { efi_memory_desc_t *m = NULL; @@ -60,20 +63,20 @@ again: * allocation which may be in a new descriptor region. */ *map_size += sizeof(*m); - status = efi_call_phys3(sys_table->boottime->allocate_pool, + status = efi_call_phys3(sys_table_arg->boottime->allocate_pool, EFI_LOADER_DATA, *map_size, (void **)&m); if (status != EFI_SUCCESS) goto fail; - status = efi_call_phys5(sys_table->boottime->get_memory_map, map_size, - m, &key, desc_size, &desc_version); + status = efi_call_phys5(sys_table_arg->boottime->get_memory_map, + map_size, m, &key, desc_size, &desc_version); if (status == EFI_BUFFER_TOO_SMALL) { - efi_call_phys1(sys_table->boottime->free_pool, m); + efi_call_phys1(sys_table_arg->boottime->free_pool, m); goto again; } if (status != EFI_SUCCESS) - efi_call_phys1(sys_table->boottime->free_pool, m); + efi_call_phys1(sys_table_arg->boottime->free_pool, m); fail: *map = m; @@ -83,8 +86,9 @@ fail: /* * Allocate at the highest possible address that is not above 'max'. */ -static efi_status_t high_alloc(unsigned long size, unsigned long align, - unsigned long *addr, unsigned long max) +static efi_status_t high_alloc(efi_system_table_t *sys_table_arg, + unsigned long size, unsigned long align, + unsigned long *addr, unsigned long max) { unsigned long map_size, desc_size; efi_memory_desc_t *map; @@ -93,7 +97,7 @@ static efi_status_t high_alloc(unsigned long size, unsigned long align, u64 max_addr = 0; int i; - status = __get_map(&map, &map_size, &desc_size); + status = __get_map(sys_table_arg, &map, &map_size, &desc_size); if (status != EFI_SUCCESS) goto fail; @@ -139,7 +143,7 @@ again: if (!max_addr) status = EFI_NOT_FOUND; else { - status = efi_call_phys4(sys_table->boottime->allocate_pages, + status = efi_call_phys4(sys_table_arg->boottime->allocate_pages, EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, nr_pages, &max_addr); if (status != EFI_SUCCESS) { @@ -152,7 +156,7 @@ again: } free_pool: - efi_call_phys1(sys_table->boottime->free_pool, map); + efi_call_phys1(sys_table_arg->boottime->free_pool, map); fail: return status; @@ -161,7 +165,8 @@ fail: /* * Allocate at the lowest possible address. */ -static efi_status_t low_alloc(unsigned long size, unsigned long align, +static efi_status_t low_alloc(efi_system_table_t *sys_table_arg, + unsigned long size, unsigned long align, unsigned long *addr) { unsigned long map_size, desc_size; @@ -170,7 +175,7 @@ static efi_status_t low_alloc(unsigned long size, unsigned long align, unsigned long nr_pages; int i; - status = __get_map(&map, &map_size, &desc_size); + status = __get_map(sys_table_arg, &map, &map_size, &desc_size); if (status != EFI_SUCCESS) goto fail; @@ -203,7 +208,7 @@ static efi_status_t low_alloc(unsigned long size, unsigned long align, if ((start + size) > end) continue; - status = efi_call_phys4(sys_table->boottime->allocate_pages, + status = efi_call_phys4(sys_table_arg->boottime->allocate_pages, EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, nr_pages, &start); if (status == EFI_SUCCESS) { @@ -216,17 +221,18 @@ static efi_status_t low_alloc(unsigned long size, unsigned long align, status = EFI_NOT_FOUND; free_pool: - efi_call_phys1(sys_table->boottime->free_pool, map); + efi_call_phys1(sys_table_arg->boottime->free_pool, map); fail: return status; } -static void low_free(unsigned long size, unsigned long addr) +static void low_free(efi_system_table_t *sys_table_arg, unsigned long size, + unsigned long addr) { unsigned long nr_pages; nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; - efi_call_phys2(sys_table->boottime->free_pages, addr, nr_pages); + efi_call_phys2(sys_table_arg->boottime->free_pages, addr, nr_pages); } @@ -236,7 +242,8 @@ static void low_free(unsigned long size, unsigned long addr) * We only support loading an initrd from the same filesystem as the * kernel image. */ -static efi_status_t handle_ramdisks(efi_loaded_image_t *image, +static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg, + efi_loaded_image_t *image, struct setup_header *hdr) { struct initrd *initrds; @@ -278,12 +285,12 @@ static efi_status_t handle_ramdisks(efi_loaded_image_t *image, if (!nr_initrds) return EFI_SUCCESS; - status = efi_call_phys3(sys_table->boottime->allocate_pool, + status = efi_call_phys3(sys_table_arg->boottime->allocate_pool, EFI_LOADER_DATA, nr_initrds * sizeof(*initrds), &initrds); if (status != EFI_SUCCESS) { - efi_printk("Failed to alloc mem for initrds\n"); + efi_printk(sys_table_arg, "Failed to alloc mem for initrds\n"); goto fail; } @@ -329,18 +336,18 @@ static efi_status_t handle_ramdisks(efi_loaded_image_t *image, if (!i) { efi_boot_services_t *boottime; - boottime = sys_table->boottime; + boottime = sys_table_arg->boottime; status = efi_call_phys3(boottime->handle_protocol, image->device_handle, &fs_proto, &io); if (status != EFI_SUCCESS) { - efi_printk("Failed to handle fs_proto\n"); + efi_printk(sys_table_arg, "Failed to handle fs_proto\n"); goto free_initrds; } status = efi_call_phys2(io->open_volume, io, &fh); if (status != EFI_SUCCESS) { - efi_printk("Failed to open volume\n"); + efi_printk(sys_table_arg, "Failed to open volume\n"); goto free_initrds; } } @@ -348,9 +355,9 @@ static efi_status_t handle_ramdisks(efi_loaded_image_t *image, status = efi_call_phys5(fh->open, fh, &h, filename_16, EFI_FILE_MODE_READ, (u64)0); if (status != EFI_SUCCESS) { - efi_printk("Failed to open initrd file: "); - efi_char16_printk(filename_16); - efi_printk("\n"); + efi_printk(sys_table_arg, "Failed to open initrd file: "); + efi_char16_printk(sys_table_arg, filename_16); + efi_printk(sys_table_arg, "\n"); goto close_handles; } @@ -360,30 +367,31 @@ static efi_status_t handle_ramdisks(efi_loaded_image_t *image, status = efi_call_phys4(h->get_info, h, &info_guid, &info_sz, NULL); if (status != EFI_BUFFER_TOO_SMALL) { - efi_printk("Failed to get initrd info size\n"); + efi_printk(sys_table_arg, "Failed to get initrd info size\n"); goto close_handles; } grow: - status = efi_call_phys3(sys_table->boottime->allocate_pool, + status = efi_call_phys3(sys_table_arg->boottime->allocate_pool, EFI_LOADER_DATA, info_sz, &info); if (status != EFI_SUCCESS) { - efi_printk("Failed to alloc mem for initrd info\n"); + efi_printk(sys_table_arg, "Failed to alloc mem for initrd info\n"); goto close_handles; } status = efi_call_phys4(h->get_info, h, &info_guid, &info_sz, info); if (status == EFI_BUFFER_TOO_SMALL) { - efi_call_phys1(sys_table->boottime->free_pool, info); + efi_call_phys1(sys_table_arg->boottime->free_pool, + info); goto grow; } file_sz = info->file_size; - efi_call_phys1(sys_table->boottime->free_pool, info); + efi_call_phys1(sys_table_arg->boottime->free_pool, info); if (status != EFI_SUCCESS) { - efi_printk("Failed to get initrd info\n"); + efi_printk(sys_table_arg, "Failed to get initrd info\n"); goto close_handles; } @@ -399,16 +407,16 @@ grow: * addresses in memory, so allocate enough memory for * all the initrd's. */ - status = high_alloc(initrd_total, 0x1000, + status = high_alloc(sys_table_arg, initrd_total, 0x1000, &initrd_addr, hdr->initrd_addr_max); if (status != EFI_SUCCESS) { - efi_printk("Failed to alloc highmem for initrds\n"); + efi_printk(sys_table_arg, "Failed to alloc highmem for initrds\n"); goto close_handles; } /* We've run out of free low memory. */ if (initrd_addr > hdr->initrd_addr_max) { - efi_printk("We've run out of free low memory\n"); + efi_printk(sys_table_arg, "We've run out of free low memory\n"); status = EFI_INVALID_PARAMETER; goto free_initrd_total; } @@ -428,7 +436,7 @@ grow: initrds[j].handle, &chunksize, addr); if (status != EFI_SUCCESS) { - efi_printk("Failed to read initrd\n"); + efi_printk(sys_table_arg, "Failed to read initrd\n"); goto free_initrd_total; } addr += chunksize; @@ -440,7 +448,7 @@ grow: } - efi_call_phys1(sys_table->boottime->free_pool, initrds); + efi_call_phys1(sys_table_arg->boottime->free_pool, initrds); hdr->ramdisk_image = initrd_addr; hdr->ramdisk_size = initrd_total; @@ -448,13 +456,13 @@ grow: return status; free_initrd_total: - low_free(initrd_total, initrd_addr); + low_free(sys_table_arg, initrd_total, initrd_addr); close_handles: for (k = j; k < i; k++) efi_call_phys1(fh->close, initrds[k].handle); free_initrds: - efi_call_phys1(sys_table->boottime->free_pool, initrds); + efi_call_phys1(sys_table_arg->boottime->free_pool, initrds); fail: hdr->ramdisk_image = 0; hdr->ramdisk_size = 0; -- cgit v0.10.2 From 40e4530a00b7d52332fe9a1361388fda8e5260da Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:29 -0700 Subject: efi: Rename memory allocation/free functions Rename them to be more similar, as low_free() could be used to free memory allocated by both high_alloc() and low_alloc(). high_alloc() -> efi_high_alloc() low_alloc() -> efi_low_alloc() low_free() -> efi_free() Signed-off-by: Roy Franz Acked-by: Mark Salter Reviewed-by: Grant Likely Signed-off-by: Matt Fleming diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index 65b6a34..2a4430a 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -457,7 +457,8 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table) return NULL; } - status = low_alloc(sys_table, 0x4000, 1, (unsigned long *)&boot_params); + status = efi_low_alloc(sys_table, 0x4000, 1, + (unsigned long *)&boot_params); if (status != EFI_SUCCESS) { efi_printk(sys_table, "Failed to alloc lowmem for boot params\n"); return NULL; @@ -503,7 +504,7 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table) options_size++; /* NUL termination */ - status = low_alloc(sys_table, options_size, 1, + status = efi_low_alloc(sys_table, options_size, 1, &cmdline); if (status != EFI_SUCCESS) { efi_printk(sys_table, "Failed to alloc mem for cmdline\n"); @@ -537,9 +538,9 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table) return boot_params; fail2: if (options_size) - low_free(sys_table, options_size, hdr->cmd_line_ptr); + efi_free(sys_table, options_size, hdr->cmd_line_ptr); fail: - low_free(sys_table, 0x4000, (unsigned long)boot_params); + efi_free(sys_table, 0x4000, (unsigned long)boot_params); return NULL; } @@ -562,7 +563,7 @@ static efi_status_t exit_boot(struct boot_params *boot_params, again: size += sizeof(*mem_map) * 2; _size = size; - status = low_alloc(sys_table, size, 1, (unsigned long *)&mem_map); + status = efi_low_alloc(sys_table, size, 1, (unsigned long *)&mem_map); if (status != EFI_SUCCESS) return status; @@ -570,7 +571,7 @@ get_map: status = efi_call_phys5(sys_table->boottime->get_memory_map, &size, mem_map, &key, &desc_size, &desc_version); if (status == EFI_BUFFER_TOO_SMALL) { - low_free(sys_table, _size, (unsigned long)mem_map); + efi_free(sys_table, _size, (unsigned long)mem_map); goto again; } @@ -672,7 +673,7 @@ get_map: return EFI_SUCCESS; free_mem_map: - low_free(sys_table, _size, (unsigned long)mem_map); + efi_free(sys_table, _size, (unsigned long)mem_map); return status; } @@ -695,7 +696,7 @@ static efi_status_t relocate_kernel(struct setup_header *hdr) EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, nr_pages, &start); if (status != EFI_SUCCESS) { - status = low_alloc(sys_table, hdr->init_size, + status = efi_low_alloc(sys_table, hdr->init_size, hdr->kernel_alignment, &start); if (status != EFI_SUCCESS) efi_printk(sys_table, "Failed to alloc mem for kernel\n"); @@ -743,7 +744,7 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table, } gdt->size = 0x800; - status = low_alloc(sys_table, gdt->size, 8, + status = efi_low_alloc(sys_table, gdt->size, 8, (unsigned long *)&gdt->address); if (status != EFI_SUCCESS) { efi_printk(sys_table, "Failed to alloc mem for gdt\n"); diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index 05c539e..2f528fb 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -86,7 +86,7 @@ fail: /* * Allocate at the highest possible address that is not above 'max'. */ -static efi_status_t high_alloc(efi_system_table_t *sys_table_arg, +static efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg, unsigned long size, unsigned long align, unsigned long *addr, unsigned long max) { @@ -165,8 +165,8 @@ fail: /* * Allocate at the lowest possible address. */ -static efi_status_t low_alloc(efi_system_table_t *sys_table_arg, - unsigned long size, unsigned long align, +static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg, + unsigned long size, unsigned long align, unsigned long *addr) { unsigned long map_size, desc_size; @@ -226,7 +226,7 @@ fail: return status; } -static void low_free(efi_system_table_t *sys_table_arg, unsigned long size, +static void efi_free(efi_system_table_t *sys_table_arg, unsigned long size, unsigned long addr) { unsigned long nr_pages; @@ -407,8 +407,8 @@ grow: * addresses in memory, so allocate enough memory for * all the initrd's. */ - status = high_alloc(sys_table_arg, initrd_total, 0x1000, - &initrd_addr, hdr->initrd_addr_max); + status = efi_high_alloc(sys_table_arg, initrd_total, 0x1000, + &initrd_addr, hdr->initrd_addr_max); if (status != EFI_SUCCESS) { efi_printk(sys_table_arg, "Failed to alloc highmem for initrds\n"); goto close_handles; @@ -456,7 +456,7 @@ grow: return status; free_initrd_total: - low_free(sys_table_arg, initrd_total, initrd_addr); + efi_free(sys_table_arg, initrd_total, initrd_addr); close_handles: for (k = j; k < i; k++) -- cgit v0.10.2 From 38dd9c02c3f2ed461165db22b93fae7f3ddde9ac Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:30 -0700 Subject: efi: Enforce minimum alignment of 1 page on allocations. The efi_high_alloc() and efi_low_alloc() functions use the EFI_ALLOCATE_ADDRESS option to the EFI function allocate_pages(), which requires a minimum of page alignment, and rejects all other requests. The existing code could fail to allocate depending on allocation size, as although repeated allocation attempts were made, none were guaranteed to be page aligned. Signed-off-by: Roy Franz Acked-by: Mark Salter Reviewed-by: Grant Likely Signed-off-by: Matt Fleming diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index 2f528fb..a256758 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -101,6 +101,14 @@ static efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg, if (status != EFI_SUCCESS) goto fail; + /* + * Enforce minimum alignment that EFI requires when requesting + * a specific address. We are doing page-based allocations, + * so we must be aligned to a page. + */ + if (align < EFI_PAGE_SIZE) + align = EFI_PAGE_SIZE; + nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; again: for (i = 0; i < map_size / desc_size; i++) { @@ -179,6 +187,14 @@ static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg, if (status != EFI_SUCCESS) goto fail; + /* + * Enforce minimum alignment that EFI requires when requesting + * a specific address. We are doing page-based allocations, + * so we must be aligned to a page. + */ + if (align < EFI_PAGE_SIZE) + align = EFI_PAGE_SIZE; + nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; for (i = 0; i < map_size / desc_size; i++) { efi_memory_desc_t *desc; -- cgit v0.10.2 From c6866d7238d4f26055de8db9c1d5a700e554b2d4 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:31 -0700 Subject: efi: Move relocate_kernel() to shared file. The relocate_kernel() function will be generalized and used by all architectures, as they all have similar requirements. Signed-off-by: Roy Franz Signed-off-by: Matt Fleming diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index 2a4430a..5bbba86 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -677,40 +677,6 @@ free_mem_map: return status; } -static efi_status_t relocate_kernel(struct setup_header *hdr) -{ - unsigned long start, nr_pages; - efi_status_t status; - - /* - * The EFI firmware loader could have placed the kernel image - * anywhere in memory, but the kernel has various restrictions - * on the max physical address it can run at. Attempt to move - * the kernel to boot_params.pref_address, or as low as - * possible. - */ - start = hdr->pref_address; - nr_pages = round_up(hdr->init_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; - - status = efi_call_phys4(sys_table->boottime->allocate_pages, - EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, - nr_pages, &start); - if (status != EFI_SUCCESS) { - status = efi_low_alloc(sys_table, hdr->init_size, - hdr->kernel_alignment, &start); - if (status != EFI_SUCCESS) - efi_printk(sys_table, "Failed to alloc mem for kernel\n"); - } - - if (status == EFI_SUCCESS) - memcpy((void *)start, (void *)(unsigned long)hdr->code32_start, - hdr->init_size); - - hdr->pref_address = hdr->code32_start; - hdr->code32_start = (__u32)start; - - return status; -} /* * On success we return a pointer to a boot_params structure, and NULL diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index a256758..333ed9c 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -485,3 +485,38 @@ fail: return status; } + +static efi_status_t relocate_kernel(struct setup_header *hdr) +{ + unsigned long start, nr_pages; + efi_status_t status; + + /* + * The EFI firmware loader could have placed the kernel image + * anywhere in memory, but the kernel has various restrictions + * on the max physical address it can run at. Attempt to move + * the kernel to boot_params.pref_address, or as low as + * possible. + */ + start = hdr->pref_address; + nr_pages = round_up(hdr->init_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; + + status = efi_call_phys4(sys_table->boottime->allocate_pages, + EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, + nr_pages, &start); + if (status != EFI_SUCCESS) { + status = efi_low_alloc(sys_table, hdr->init_size, + hdr->kernel_alignment, &start); + if (status != EFI_SUCCESS) + efi_printk(sys_table, "Failed to alloc mem for kernel\n"); + } + + if (status == EFI_SUCCESS) + memcpy((void *)start, (void *)(unsigned long)hdr->code32_start, + hdr->init_size); + + hdr->pref_address = hdr->code32_start; + hdr->code32_start = (__u32)start; + + return status; +} -- cgit v0.10.2 From 4a9f3a7c336a6b0ffeef2523bef93e67b0921163 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:32 -0700 Subject: efi: Generalize relocate_kernel() for use by other architectures. Rename relocate_kernel() to efi_relocate_kernel(), and take parameters rather than x86 specific structure. Add max_addr argument as for ARM we have some address constraints that we need to enforce when relocating the kernel. Add alloc_size parameter for use by ARM64 which uses an uncompressed kernel, and needs to allocate space for BSS. Signed-off-by: Roy Franz Signed-off-by: Matt Fleming diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index 5bbba86..2e997b6 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -733,10 +733,16 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table, * address, relocate it. */ if (hdr->pref_address != hdr->code32_start) { - status = relocate_kernel(hdr); - + unsigned long bzimage_addr = hdr->code32_start; + status = efi_relocate_kernel(sys_table, &bzimage_addr, + hdr->init_size, hdr->init_size, + hdr->pref_address, + hdr->kernel_alignment); if (status != EFI_SUCCESS) goto fail; + + hdr->pref_address = hdr->code32_start; + hdr->code32_start = bzimage_addr; } status = exit_boot(boot_params, handle); diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index 333ed9c..361e24d 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -485,38 +485,72 @@ fail: return status; } - -static efi_status_t relocate_kernel(struct setup_header *hdr) +/* + * Relocate a kernel image, either compressed or uncompressed. + * In the ARM64 case, all kernel images are currently + * uncompressed, and as such when we relocate it we need to + * allocate additional space for the BSS segment. Any low + * memory that this function should avoid needs to be + * unavailable in the EFI memory map, as if the preferred + * address is not available the lowest available address will + * be used. + */ +static efi_status_t efi_relocate_kernel(efi_system_table_t *sys_table_arg, + unsigned long *image_addr, + unsigned long image_size, + unsigned long alloc_size, + unsigned long preferred_addr, + unsigned long alignment) { - unsigned long start, nr_pages; + unsigned long cur_image_addr; + unsigned long new_addr = 0; efi_status_t status; + unsigned long nr_pages; + efi_physical_addr_t efi_addr = preferred_addr; + + if (!image_addr || !image_size || !alloc_size) + return EFI_INVALID_PARAMETER; + if (alloc_size < image_size) + return EFI_INVALID_PARAMETER; + + cur_image_addr = *image_addr; /* * The EFI firmware loader could have placed the kernel image - * anywhere in memory, but the kernel has various restrictions - * on the max physical address it can run at. Attempt to move - * the kernel to boot_params.pref_address, or as low as - * possible. + * anywhere in memory, but the kernel has restrictions on the + * max physical address it can run at. Some architectures + * also have a prefered address, so first try to relocate + * to the preferred address. If that fails, allocate as low + * as possible while respecting the required alignment. */ - start = hdr->pref_address; - nr_pages = round_up(hdr->init_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; - - status = efi_call_phys4(sys_table->boottime->allocate_pages, + nr_pages = round_up(alloc_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; + status = efi_call_phys4(sys_table_arg->boottime->allocate_pages, EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, - nr_pages, &start); + nr_pages, &efi_addr); + new_addr = efi_addr; + /* + * If preferred address allocation failed allocate as low as + * possible. + */ if (status != EFI_SUCCESS) { - status = efi_low_alloc(sys_table, hdr->init_size, - hdr->kernel_alignment, &start); - if (status != EFI_SUCCESS) - efi_printk(sys_table, "Failed to alloc mem for kernel\n"); + status = efi_low_alloc(sys_table_arg, alloc_size, alignment, + &new_addr); + } + if (status != EFI_SUCCESS) { + efi_printk(sys_table_arg, "ERROR: Failed to allocate usable memory for kernel.\n"); + return status; } - if (status == EFI_SUCCESS) - memcpy((void *)start, (void *)(unsigned long)hdr->code32_start, - hdr->init_size); + /* + * We know source/dest won't overlap since both memory ranges + * have been allocated by UEFI, so we can safely use memcpy. + */ + memcpy((void *)new_addr, (void *)cur_image_addr, image_size); + /* Zero any extra space we may have allocated for BSS. */ + memset((void *)(new_addr + image_size), alloc_size - image_size, 0); - hdr->pref_address = hdr->code32_start; - hdr->code32_start = (__u32)start; + /* Return the new address of the relocated image. */ + *image_addr = new_addr; return status; } -- cgit v0.10.2 From 5fef3870c572a30f9def07c4ded69dc37a9cf5d9 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:33 -0700 Subject: efi: Move unicode to ASCII conversion to shared function. Move the open-coded conversion to a shared function for use by all architectures. Change the allocation to prefer a high address for ARM, as this is required to avoid conflicts with reserved regions in low memory. We don't know the specifics of these regions until after we process the command line and device tree. Signed-off-by: Roy Franz Signed-off-by: Matt Fleming diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index 2e997b6..5e708c0 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -435,11 +435,10 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table) struct efi_info *efi; efi_loaded_image_t *image; void *options; - u32 load_options_size; efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID; int options_size = 0; efi_status_t status; - unsigned long cmdline; + char *cmdline_ptr; u16 *s2; u8 *s1; int i; @@ -487,41 +486,11 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table) hdr->type_of_loader = 0x21; /* Convert unicode cmdline to ascii */ - options = image->load_options; - load_options_size = image->load_options_size / 2; /* ASCII */ - cmdline = 0; - s2 = (u16 *)options; - - if (s2) { - while (*s2 && *s2 != '\n' && options_size < load_options_size) { - s2++; - options_size++; - } - - if (options_size) { - if (options_size > hdr->cmdline_size) - options_size = hdr->cmdline_size; - - options_size++; /* NUL termination */ - - status = efi_low_alloc(sys_table, options_size, 1, - &cmdline); - if (status != EFI_SUCCESS) { - efi_printk(sys_table, "Failed to alloc mem for cmdline\n"); - goto fail; - } - - s1 = (u8 *)(unsigned long)cmdline; - s2 = (u16 *)options; - - for (i = 0; i < options_size - 1; i++) - *s1++ = *s2++; - - *s1 = '\0'; - } - } - - hdr->cmd_line_ptr = cmdline; + cmdline_ptr = efi_convert_cmdline_to_ascii(sys_table, image, + &options_size); + if (!cmdline_ptr) + goto fail; + hdr->cmd_line_ptr = (unsigned long)cmdline_ptr; hdr->ramdisk_image = 0; hdr->ramdisk_size = 0; diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index 361e24d..96665b7 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -554,3 +554,64 @@ static efi_status_t efi_relocate_kernel(efi_system_table_t *sys_table_arg, return status; } + +/* + * Convert the unicode UEFI command line to ASCII to pass to kernel. + * Size of memory allocated return in *cmd_line_len. + * Returns NULL on error. + */ +static char *efi_convert_cmdline_to_ascii(efi_system_table_t *sys_table_arg, + efi_loaded_image_t *image, + int *cmd_line_len) +{ + u16 *s2; + u8 *s1 = NULL; + unsigned long cmdline_addr = 0; + int load_options_size = image->load_options_size / 2; /* ASCII */ + void *options = image->load_options; + int options_size = 0; + efi_status_t status; + int i; + u16 zero = 0; + + if (options) { + s2 = options; + while (*s2 && *s2 != '\n' && options_size < load_options_size) { + s2++; + options_size++; + } + } + + if (options_size == 0) { + /* No command line options, so return empty string*/ + options_size = 1; + options = &zero; + } + + options_size++; /* NUL termination */ +#ifdef CONFIG_ARM + /* + * For ARM, allocate at a high address to avoid reserved + * regions at low addresses that we don't know the specfics of + * at the time we are processing the command line. + */ + status = efi_high_alloc(sys_table_arg, options_size, 0, + &cmdline_addr, 0xfffff000); +#else + status = efi_low_alloc(sys_table_arg, options_size, 0, + &cmdline_addr); +#endif + if (status != EFI_SUCCESS) + return NULL; + + s1 = (u8 *)cmdline_addr; + s2 = (u16 *)options; + + for (i = 0; i < options_size - 1; i++) + *s1++ = *s2++; + + *s1 = '\0'; + + *cmd_line_len = options_size; + return (char *)cmdline_addr; +} -- cgit v0.10.2 From 86cc653b1955c0a47681d16457f9ee9009f2561a Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:35 -0700 Subject: efi: Rename __get_map() to efi_get_memory_map() Rename function in preparation for making it more flexible and sharing it. Signed-off-by: Roy Franz Signed-off-by: Matt Fleming diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index 96665b7..561b9cf 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -46,10 +46,10 @@ static void efi_printk(efi_system_table_t *sys_table_arg, char *str) } -static efi_status_t __get_map(efi_system_table_t *sys_table_arg, - efi_memory_desc_t **map, - unsigned long *map_size, - unsigned long *desc_size) +static efi_status_t efi_get_memory_map(efi_system_table_t *sys_table_arg, + efi_memory_desc_t **map, + unsigned long *map_size, + unsigned long *desc_size) { efi_memory_desc_t *m = NULL; efi_status_t status; @@ -97,7 +97,7 @@ static efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg, u64 max_addr = 0; int i; - status = __get_map(sys_table_arg, &map, &map_size, &desc_size); + status = efi_get_memory_map(sys_table_arg, &map, &map_size, &desc_size); if (status != EFI_SUCCESS) goto fail; @@ -183,7 +183,7 @@ static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg, unsigned long nr_pages; int i; - status = __get_map(sys_table_arg, &map, &map_size, &desc_size); + status = efi_get_memory_map(sys_table_arg, &map, &map_size, &desc_size); if (status != EFI_SUCCESS) goto fail; -- cgit v0.10.2 From 1c089c65f54b70261fdcb4c238986a979c11fad7 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:36 -0700 Subject: efi: generalize efi_get_memory_map() Add arguments for returning the descriptor version and also the memory map key. The key is required for calling exit_boot_services(). Signed-off-by: Roy Franz Signed-off-by: Matt Fleming diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index 561b9cf..b314bf7 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -49,7 +49,9 @@ static void efi_printk(efi_system_table_t *sys_table_arg, char *str) static efi_status_t efi_get_memory_map(efi_system_table_t *sys_table_arg, efi_memory_desc_t **map, unsigned long *map_size, - unsigned long *desc_size) + unsigned long *desc_size, + u32 *desc_ver, + unsigned long *key_ptr) { efi_memory_desc_t *m = NULL; efi_status_t status; @@ -77,6 +79,10 @@ again: if (status != EFI_SUCCESS) efi_call_phys1(sys_table_arg->boottime->free_pool, m); + if (key_ptr && status == EFI_SUCCESS) + *key_ptr = key; + if (desc_ver && status == EFI_SUCCESS) + *desc_ver = desc_version; fail: *map = m; @@ -97,7 +103,8 @@ static efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg, u64 max_addr = 0; int i; - status = efi_get_memory_map(sys_table_arg, &map, &map_size, &desc_size); + status = efi_get_memory_map(sys_table_arg, &map, &map_size, &desc_size, + NULL, NULL); if (status != EFI_SUCCESS) goto fail; @@ -183,7 +190,8 @@ static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg, unsigned long nr_pages; int i; - status = efi_get_memory_map(sys_table_arg, &map, &map_size, &desc_size); + status = efi_get_memory_map(sys_table_arg, &map, &map_size, &desc_size, + NULL, NULL); if (status != EFI_SUCCESS) goto fail; -- cgit v0.10.2 From ae8e9060a3ecfd22bd5059e39f81547613ae376c Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:37 -0700 Subject: efi: use efi_get_memory_map() to get final map for x86 Replace the open-coded memory map getting with the efi_get_memory_map() that is now general enough to use. Signed-off-by: Roy Franz Signed-off-by: Matt Fleming diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index 5e708c0..bf2c35d 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -527,25 +527,12 @@ static efi_status_t exit_boot(struct boot_params *boot_params, u8 nr_entries; int i; - size = sizeof(*mem_map) * 32; - -again: - size += sizeof(*mem_map) * 2; - _size = size; - status = efi_low_alloc(sys_table, size, 1, (unsigned long *)&mem_map); - if (status != EFI_SUCCESS) - return status; - get_map: - status = efi_call_phys5(sys_table->boottime->get_memory_map, &size, - mem_map, &key, &desc_size, &desc_version); - if (status == EFI_BUFFER_TOO_SMALL) { - efi_free(sys_table, _size, (unsigned long)mem_map); - goto again; - } + status = efi_get_memory_map(sys_table, &mem_map, &size, &desc_size, + &desc_version, &key); if (status != EFI_SUCCESS) - goto free_mem_map; + return status; memcpy(&efi->efi_loader_signature, EFI_LOADER_SIGNATURE, sizeof(__u32)); efi->efi_systab = (unsigned long)sys_table; @@ -574,6 +561,7 @@ get_map: goto free_mem_map; called_exit = true; + efi_call_phys1(sys_table->boottime->free_pool, mem_map); goto get_map; } @@ -642,7 +630,7 @@ get_map: return EFI_SUCCESS; free_mem_map: - efi_free(sys_table, _size, (unsigned long)mem_map); + efi_call_phys1(sys_table->boottime->free_pool, mem_map); return status; } -- cgit v0.10.2 From 0e1cadb05bba2293b4575c8cab275313d181d94f Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:38 -0700 Subject: efi: Allow efi_free() to be called with size of 0 Make efi_free() safely callable with size of 0, similar to free() being callable with NULL pointers, and do nothing in that case. Remove size checks that this makes redundant. This also avoids some size checks in the ARM EFI stub code that will be added as well. Signed-off-by: Roy Franz Signed-off-by: Matt Fleming diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index bf2c35d..ef2181a 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -506,8 +506,7 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table) return boot_params; fail2: - if (options_size) - efi_free(sys_table, options_size, hdr->cmd_line_ptr); + efi_free(sys_table, options_size, hdr->cmd_line_ptr); fail: efi_free(sys_table, 0x4000, (unsigned long)boot_params); return NULL; diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index b314bf7..fda510f 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -255,6 +255,9 @@ static void efi_free(efi_system_table_t *sys_table_arg, unsigned long size, { unsigned long nr_pages; + if (!size) + return; + nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; efi_call_phys2(sys_table_arg->boottime->free_pages, addr, nr_pages); } -- cgit v0.10.2 From 46f4582e7cbc5f30127183812d4da875782518f5 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:39 -0700 Subject: efi: Generalize handle_ramdisks() and rename to handle_cmdline_files(). The handle_cmdline_files now takes the option to handle as a string, and returns the loaded data through parameters, rather than taking an x86 specific setup_header structure. For ARM, this will be used to load a device tree blob in addition to initrd images. Signed-off-by: Roy Franz Acked-by: Mark Salter Reviewed-by: Grant Likely Signed-off-by: Matt Fleming diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index ef2181a..beb07a4 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -442,6 +442,8 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table) u16 *s2; u8 *s1; int i; + unsigned long ramdisk_addr; + unsigned long ramdisk_size; sys_table = _table; @@ -500,9 +502,14 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table) memset(sdt, 0, sizeof(*sdt)); - status = handle_ramdisks(sys_table, image, hdr); + status = handle_cmdline_files(sys_table, image, + (char *)(unsigned long)hdr->cmd_line_ptr, + "initrd=", hdr->initrd_addr_max, + &ramdisk_addr, &ramdisk_size); if (status != EFI_SUCCESS) goto fail2; + hdr->ramdisk_image = ramdisk_addr; + hdr->ramdisk_size = ramdisk_size; return boot_params; fail2: diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index fda510f..8c78394 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -269,9 +269,12 @@ static void efi_free(efi_system_table_t *sys_table_arg, unsigned long size, * We only support loading an initrd from the same filesystem as the * kernel image. */ -static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg, - efi_loaded_image_t *image, - struct setup_header *hdr) +static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, + efi_loaded_image_t *image, + char *cmd_line, char *option_string, + unsigned long max_addr, + unsigned long *load_addr, + unsigned long *load_size) { struct initrd *initrds; unsigned long initrd_addr; @@ -287,19 +290,25 @@ static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg, initrd_addr = 0; initrd_total = 0; - str = (char *)(unsigned long)hdr->cmd_line_ptr; + str = cmd_line; j = 0; /* See close_handles */ + if (!load_addr || !load_size) + return EFI_INVALID_PARAMETER; + + *load_addr = 0; + *load_size = 0; + if (!str || !*str) return EFI_SUCCESS; for (nr_initrds = 0; *str; nr_initrds++) { - str = strstr(str, "initrd="); + str = strstr(str, option_string); if (!str) break; - str += 7; + str += strlen(option_string); /* Skip any leading slashes */ while (*str == '/' || *str == '\\') @@ -317,11 +326,11 @@ static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg, nr_initrds * sizeof(*initrds), &initrds); if (status != EFI_SUCCESS) { - efi_printk(sys_table_arg, "Failed to alloc mem for initrds\n"); + efi_printk(sys_table_arg, "Failed to alloc mem for file load\n"); goto fail; } - str = (char *)(unsigned long)hdr->cmd_line_ptr; + str = cmd_line; for (i = 0; i < nr_initrds; i++) { struct initrd *initrd; efi_file_handle_t *h; @@ -332,11 +341,11 @@ static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg, efi_char16_t *p; u64 file_sz; - str = strstr(str, "initrd="); + str = strstr(str, option_string); if (!str) break; - str += 7; + str += strlen(option_string); initrd = &initrds[i]; p = filename_16; @@ -382,7 +391,7 @@ static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg, status = efi_call_phys5(fh->open, fh, &h, filename_16, EFI_FILE_MODE_READ, (u64)0); if (status != EFI_SUCCESS) { - efi_printk(sys_table_arg, "Failed to open initrd file: "); + efi_printk(sys_table_arg, "Failed to open file: "); efi_char16_printk(sys_table_arg, filename_16); efi_printk(sys_table_arg, "\n"); goto close_handles; @@ -394,7 +403,7 @@ static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg, status = efi_call_phys4(h->get_info, h, &info_guid, &info_sz, NULL); if (status != EFI_BUFFER_TOO_SMALL) { - efi_printk(sys_table_arg, "Failed to get initrd info size\n"); + efi_printk(sys_table_arg, "Failed to get file info size\n"); goto close_handles; } @@ -402,7 +411,7 @@ grow: status = efi_call_phys3(sys_table_arg->boottime->allocate_pool, EFI_LOADER_DATA, info_sz, &info); if (status != EFI_SUCCESS) { - efi_printk(sys_table_arg, "Failed to alloc mem for initrd info\n"); + efi_printk(sys_table_arg, "Failed to alloc mem for file info\n"); goto close_handles; } @@ -418,7 +427,7 @@ grow: efi_call_phys1(sys_table_arg->boottime->free_pool, info); if (status != EFI_SUCCESS) { - efi_printk(sys_table_arg, "Failed to get initrd info\n"); + efi_printk(sys_table_arg, "Failed to get file info\n"); goto close_handles; } @@ -435,14 +444,14 @@ grow: * all the initrd's. */ status = efi_high_alloc(sys_table_arg, initrd_total, 0x1000, - &initrd_addr, hdr->initrd_addr_max); + &initrd_addr, max_addr); if (status != EFI_SUCCESS) { efi_printk(sys_table_arg, "Failed to alloc highmem for initrds\n"); goto close_handles; } /* We've run out of free low memory. */ - if (initrd_addr > hdr->initrd_addr_max) { + if (initrd_addr > max_addr) { efi_printk(sys_table_arg, "We've run out of free low memory\n"); status = EFI_INVALID_PARAMETER; goto free_initrd_total; @@ -463,7 +472,7 @@ grow: initrds[j].handle, &chunksize, addr); if (status != EFI_SUCCESS) { - efi_printk(sys_table_arg, "Failed to read initrd\n"); + efi_printk(sys_table_arg, "Failed to read file\n"); goto free_initrd_total; } addr += chunksize; @@ -477,8 +486,8 @@ grow: efi_call_phys1(sys_table_arg->boottime->free_pool, initrds); - hdr->ramdisk_image = initrd_addr; - hdr->ramdisk_size = initrd_total; + *load_addr = initrd_addr; + *load_size = initrd_total; return status; @@ -491,8 +500,8 @@ close_handles: free_initrds: efi_call_phys1(sys_table_arg->boottime->free_pool, initrds); fail: - hdr->ramdisk_image = 0; - hdr->ramdisk_size = 0; + *load_addr = 0; + *load_size = 0; return status; } -- cgit v0.10.2 From 36f8961c963683ac12282e422dfc3db806ee0c7e Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:40 -0700 Subject: efi: Renames in handle_cmdline_files() to complete generalization. Rename variables to be not initrd specific, as now the function loads arbitrary files. This change is exclusively renames and comment changes to reflect the generalization. Signed-off-by: Roy Franz Acked-by: Mark Salter Reviewed-by: Grant Likely Signed-off-by: Matt Fleming diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index 8c78394..5cea5d5 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -11,7 +11,7 @@ */ #define EFI_READ_CHUNK_SIZE (1024 * 1024) -struct initrd { +struct file_info { efi_file_handle_t *handle; u64 size; }; @@ -264,10 +264,10 @@ static void efi_free(efi_system_table_t *sys_table_arg, unsigned long size, /* - * Check the cmdline for a LILO-style initrd= arguments. + * Check the cmdline for a LILO-style file= arguments. * - * We only support loading an initrd from the same filesystem as the - * kernel image. + * We only support loading a file from the same filesystem as + * the kernel image. */ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, efi_loaded_image_t *image, @@ -276,19 +276,19 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, unsigned long *load_addr, unsigned long *load_size) { - struct initrd *initrds; - unsigned long initrd_addr; + struct file_info *files; + unsigned long file_addr; efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; - u64 initrd_total; + u64 file_size_total; efi_file_io_interface_t *io; efi_file_handle_t *fh; efi_status_t status; - int nr_initrds; + int nr_files; char *str; int i, j, k; - initrd_addr = 0; - initrd_total = 0; + file_addr = 0; + file_size_total = 0; str = cmd_line; @@ -303,7 +303,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, if (!str || !*str) return EFI_SUCCESS; - for (nr_initrds = 0; *str; nr_initrds++) { + for (nr_files = 0; *str; nr_files++) { str = strstr(str, option_string); if (!str) break; @@ -318,21 +318,21 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, str++; } - if (!nr_initrds) + if (!nr_files) return EFI_SUCCESS; status = efi_call_phys3(sys_table_arg->boottime->allocate_pool, EFI_LOADER_DATA, - nr_initrds * sizeof(*initrds), - &initrds); + nr_files * sizeof(*files), + &files); if (status != EFI_SUCCESS) { - efi_printk(sys_table_arg, "Failed to alloc mem for file load\n"); + efi_printk(sys_table_arg, "Failed to alloc mem for file handle list\n"); goto fail; } str = cmd_line; - for (i = 0; i < nr_initrds; i++) { - struct initrd *initrd; + for (i = 0; i < nr_files; i++) { + struct file_info *file; efi_file_handle_t *h; efi_file_info_t *info; efi_char16_t filename_16[256]; @@ -347,7 +347,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, str += strlen(option_string); - initrd = &initrds[i]; + file = &files[i]; p = filename_16; /* Skip any leading slashes */ @@ -378,13 +378,13 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, image->device_handle, &fs_proto, &io); if (status != EFI_SUCCESS) { efi_printk(sys_table_arg, "Failed to handle fs_proto\n"); - goto free_initrds; + goto free_files; } status = efi_call_phys2(io->open_volume, io, &fh); if (status != EFI_SUCCESS) { efi_printk(sys_table_arg, "Failed to open volume\n"); - goto free_initrds; + goto free_files; } } @@ -397,7 +397,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, goto close_handles; } - initrd->handle = h; + file->handle = h; info_sz = 0; status = efi_call_phys4(h->get_info, h, &info_guid, @@ -431,37 +431,37 @@ grow: goto close_handles; } - initrd->size = file_sz; - initrd_total += file_sz; + file->size = file_sz; + file_size_total += file_sz; } - if (initrd_total) { + if (file_size_total) { unsigned long addr; /* - * Multiple initrd's need to be at consecutive - * addresses in memory, so allocate enough memory for - * all the initrd's. + * Multiple files need to be at consecutive addresses in memory, + * so allocate enough memory for all the files. This is used + * for loading multiple files. */ - status = efi_high_alloc(sys_table_arg, initrd_total, 0x1000, - &initrd_addr, max_addr); + status = efi_high_alloc(sys_table_arg, file_size_total, 0x1000, + &file_addr, max_addr); if (status != EFI_SUCCESS) { - efi_printk(sys_table_arg, "Failed to alloc highmem for initrds\n"); + efi_printk(sys_table_arg, "Failed to alloc highmem for files\n"); goto close_handles; } /* We've run out of free low memory. */ - if (initrd_addr > max_addr) { + if (file_addr > max_addr) { efi_printk(sys_table_arg, "We've run out of free low memory\n"); status = EFI_INVALID_PARAMETER; - goto free_initrd_total; + goto free_file_total; } - addr = initrd_addr; - for (j = 0; j < nr_initrds; j++) { + addr = file_addr; + for (j = 0; j < nr_files; j++) { u64 size; - size = initrds[j].size; + size = files[j].size; while (size) { u64 chunksize; if (size > EFI_READ_CHUNK_SIZE) @@ -469,36 +469,36 @@ grow: else chunksize = size; status = efi_call_phys3(fh->read, - initrds[j].handle, + files[j].handle, &chunksize, addr); if (status != EFI_SUCCESS) { efi_printk(sys_table_arg, "Failed to read file\n"); - goto free_initrd_total; + goto free_file_total; } addr += chunksize; size -= chunksize; } - efi_call_phys1(fh->close, initrds[j].handle); + efi_call_phys1(fh->close, files[j].handle); } } - efi_call_phys1(sys_table_arg->boottime->free_pool, initrds); + efi_call_phys1(sys_table_arg->boottime->free_pool, files); - *load_addr = initrd_addr; - *load_size = initrd_total; + *load_addr = file_addr; + *load_size = file_size_total; return status; -free_initrd_total: - efi_free(sys_table_arg, initrd_total, initrd_addr); +free_file_total: + efi_free(sys_table_arg, file_size_total, file_addr); close_handles: for (k = j; k < i; k++) - efi_call_phys1(fh->close, initrds[k].handle); -free_initrds: - efi_call_phys1(sys_table_arg->boottime->free_pool, initrds); + efi_call_phys1(fh->close, files[k].handle); +free_files: + efi_call_phys1(sys_table_arg->boottime->free_pool, files); fail: *load_addr = 0; *load_size = 0; -- cgit v0.10.2 From 6a5fe770d32811ffacefaa2a430cc067ecc7336c Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:41 -0700 Subject: efi: Fix types in EFI calls to match EFI function definitions. EFI calls can made directly on ARM, so the function pointers are directly invoked. This allows types to be checked at compile time, so here we ensure that the parameters match the function signature. The wrappers used by x86 prevent any type checking. Correct the type of chunksize to be based on native width as specified by the EFI_FILE_PROTOCOL read() function. Signed-off-by: Roy Franz Signed-off-by: Matt Fleming diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index 5cea5d5..4252d01 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -324,7 +324,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, status = efi_call_phys3(sys_table_arg->boottime->allocate_pool, EFI_LOADER_DATA, nr_files * sizeof(*files), - &files); + (void **)&files); if (status != EFI_SUCCESS) { efi_printk(sys_table_arg, "Failed to alloc mem for file handle list\n"); goto fail; @@ -375,7 +375,8 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, boottime = sys_table_arg->boottime; status = efi_call_phys3(boottime->handle_protocol, - image->device_handle, &fs_proto, &io); + image->device_handle, &fs_proto, + (void **)&io); if (status != EFI_SUCCESS) { efi_printk(sys_table_arg, "Failed to handle fs_proto\n"); goto free_files; @@ -409,7 +410,8 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, grow: status = efi_call_phys3(sys_table_arg->boottime->allocate_pool, - EFI_LOADER_DATA, info_sz, &info); + EFI_LOADER_DATA, info_sz, + (void **)&info); if (status != EFI_SUCCESS) { efi_printk(sys_table_arg, "Failed to alloc mem for file info\n"); goto close_handles; @@ -459,18 +461,19 @@ grow: addr = file_addr; for (j = 0; j < nr_files; j++) { - u64 size; + unsigned long size; size = files[j].size; while (size) { - u64 chunksize; + unsigned long chunksize; if (size > EFI_READ_CHUNK_SIZE) chunksize = EFI_READ_CHUNK_SIZE; else chunksize = size; status = efi_call_phys3(fh->read, files[j].handle, - &chunksize, addr); + &chunksize, + (void *)addr); if (status != EFI_SUCCESS) { efi_printk(sys_table_arg, "Failed to read file\n"); goto free_file_total; -- cgit v0.10.2 From 4e283088bd9af0ff119d6e15bca81a83d6e8e30c Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:42 -0700 Subject: efi: resolve warnings found on ARM compile warnings from gcc: warning: label 'free_pool' defined but not used [-Wunused-label] warning: value computed is not used [-Wunused-value] Signed-off-by: Roy Franz Signed-off-by: Matt Fleming diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index 4252d01..cc0581d 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -170,7 +170,6 @@ again: *addr = max_addr; } -free_pool: efi_call_phys1(sys_table_arg->boottime->free_pool, map); fail: @@ -244,7 +243,6 @@ static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg, if (i == map_size / desc_size) status = EFI_NOT_FOUND; -free_pool: efi_call_phys1(sys_table_arg->boottime->free_pool, map); fail: return status; @@ -360,7 +358,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, if (*str == '/') { *p++ = '\\'; - *str++; + str++; } else { *p++ = *str++; } -- cgit v0.10.2 From 4314895165623879937f46d767673654662b570c Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Sun, 22 Sep 2013 17:20:54 +0300 Subject: sched: Micro-optimize by dropping unnecessary task_rq() calls We always know the rq used, let's just pass it around. This seems to cut the size of scheduler core down a tiny bit: Before: [linux]$ size kernel/sched/core.o.orig text data bss dec hex filename 62760 16130 3876 82766 1434e kernel/sched/core.o.orig After: [linux]$ size kernel/sched/core.o.patched text data bss dec hex filename 62566 16130 3876 82572 1428c kernel/sched/core.o.patched Probably speeds it up as well. Signed-off-by: Michael S. Tsirkin Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20130922142054.GA11499@redhat.com Signed-off-by: Ingo Molnar diff --git a/kernel/sched/core.c b/kernel/sched/core.c index c2283c5..ac57967 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -767,14 +767,14 @@ static void set_load_weight(struct task_struct *p) static void enqueue_task(struct rq *rq, struct task_struct *p, int flags) { update_rq_clock(rq); - sched_info_queued(p); + sched_info_queued(rq, p); p->sched_class->enqueue_task(rq, p, flags); } static void dequeue_task(struct rq *rq, struct task_struct *p, int flags) { update_rq_clock(rq); - sched_info_dequeued(p); + sched_info_dequeued(rq, p); p->sched_class->dequeue_task(rq, p, flags); } @@ -1839,7 +1839,7 @@ prepare_task_switch(struct rq *rq, struct task_struct *prev, struct task_struct *next) { trace_sched_switch(prev, next); - sched_info_switch(prev, next); + sched_info_switch(rq, prev, next); perf_event_task_sched_out(prev, next); fire_sched_out_preempt_notifiers(prev, next); prepare_lock_switch(rq, next); diff --git a/kernel/sched/stats.h b/kernel/sched/stats.h index c7edee7..4ab7043 100644 --- a/kernel/sched/stats.h +++ b/kernel/sched/stats.h @@ -59,9 +59,9 @@ static inline void sched_info_reset_dequeued(struct task_struct *t) * from dequeue_task() to account for possible rq->clock skew across cpus. The * delta taken on each cpu would annul the skew. */ -static inline void sched_info_dequeued(struct task_struct *t) +static inline void sched_info_dequeued(struct rq *rq, struct task_struct *t) { - unsigned long long now = rq_clock(task_rq(t)), delta = 0; + unsigned long long now = rq_clock(rq), delta = 0; if (unlikely(sched_info_on())) if (t->sched_info.last_queued) @@ -69,7 +69,7 @@ static inline void sched_info_dequeued(struct task_struct *t) sched_info_reset_dequeued(t); t->sched_info.run_delay += delta; - rq_sched_info_dequeued(task_rq(t), delta); + rq_sched_info_dequeued(rq, delta); } /* @@ -77,9 +77,9 @@ static inline void sched_info_dequeued(struct task_struct *t) * long it was waiting to run. We also note when it began so that we * can keep stats on how long its timeslice is. */ -static void sched_info_arrive(struct task_struct *t) +static void sched_info_arrive(struct rq *rq, struct task_struct *t) { - unsigned long long now = rq_clock(task_rq(t)), delta = 0; + unsigned long long now = rq_clock(rq), delta = 0; if (t->sched_info.last_queued) delta = now - t->sched_info.last_queued; @@ -88,7 +88,7 @@ static void sched_info_arrive(struct task_struct *t) t->sched_info.last_arrival = now; t->sched_info.pcount++; - rq_sched_info_arrive(task_rq(t), delta); + rq_sched_info_arrive(rq, delta); } /* @@ -96,11 +96,11 @@ static void sched_info_arrive(struct task_struct *t) * the timestamp if it is already not set. It's assumed that * sched_info_dequeued() will clear that stamp when appropriate. */ -static inline void sched_info_queued(struct task_struct *t) +static inline void sched_info_queued(struct rq *rq, struct task_struct *t) { if (unlikely(sched_info_on())) if (!t->sched_info.last_queued) - t->sched_info.last_queued = rq_clock(task_rq(t)); + t->sched_info.last_queued = rq_clock(rq); } /* @@ -111,15 +111,15 @@ static inline void sched_info_queued(struct task_struct *t) * sched_info_queued() to mark that it has now again started waiting on * the runqueue. */ -static inline void sched_info_depart(struct task_struct *t) +static inline void sched_info_depart(struct rq *rq, struct task_struct *t) { - unsigned long long delta = rq_clock(task_rq(t)) - + unsigned long long delta = rq_clock(rq) - t->sched_info.last_arrival; - rq_sched_info_depart(task_rq(t), delta); + rq_sched_info_depart(rq, delta); if (t->state == TASK_RUNNING) - sched_info_queued(t); + sched_info_queued(rq, t); } /* @@ -128,32 +128,34 @@ static inline void sched_info_depart(struct task_struct *t) * the idle task.) We are only called when prev != next. */ static inline void -__sched_info_switch(struct task_struct *prev, struct task_struct *next) +__sched_info_switch(struct rq *rq, + struct task_struct *prev, struct task_struct *next) { - struct rq *rq = task_rq(prev); - /* * prev now departs the cpu. It's not interesting to record * stats about how efficient we were at scheduling the idle * process, however. */ if (prev != rq->idle) - sched_info_depart(prev); + sched_info_depart(rq, prev); if (next != rq->idle) - sched_info_arrive(next); + sched_info_arrive(rq, next); } static inline void -sched_info_switch(struct task_struct *prev, struct task_struct *next) +sched_info_switch(struct rq *rq, + struct task_struct *prev, struct task_struct *next) { if (unlikely(sched_info_on())) - __sched_info_switch(prev, next); + __sched_info_switch(rq, prev, next); } #else -#define sched_info_queued(t) do { } while (0) +#define sched_info_queued(rq, t) do { } while (0) #define sched_info_reset_dequeued(t) do { } while (0) -#define sched_info_dequeued(t) do { } while (0) -#define sched_info_switch(t, next) do { } while (0) +#define sched_info_dequeued(rq, t) do { } while (0) +#define sched_info_depart(rq, t) do { } while (0) +#define sched_info_arrive(rq, next) do { } while (0) +#define sched_info_switch(rq, t, next) do { } while (0) #endif /* CONFIG_SCHEDSTATS || CONFIG_TASK_DELAY_ACCT */ /* -- cgit v0.10.2 From 0c44c2d0f459cd7e275242b72f500137c4fa834d Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 11 Sep 2013 15:19:24 +0200 Subject: x86: Use asm goto to implement better modify_and_test() functions Linus suggested using asm goto to get rid of the typical SETcc + TEST instruction pair -- which also clobbers an extra register -- for our typical modify_and_test() functions. Because asm goto doesn't allow output fields it has to include an unconditinal memory clobber when it changes a memory variable to force a reload. Luckily all atomic ops already imply a compiler barrier to go along with their memory barrier semantics. Suggested-by: Linus Torvalds Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-0mtn9siwbeo1d33bap1422se@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/atomic.h b/arch/x86/include/asm/atomic.h index 722aa3b..da31c8b 100644 --- a/arch/x86/include/asm/atomic.h +++ b/arch/x86/include/asm/atomic.h @@ -6,6 +6,7 @@ #include #include #include +#include /* * Atomic operations that C can't guarantee us. Useful for @@ -76,12 +77,7 @@ static inline void atomic_sub(int i, atomic_t *v) */ static inline int atomic_sub_and_test(int i, atomic_t *v) { - unsigned char c; - - asm volatile(LOCK_PREFIX "subl %2,%0; sete %1" - : "+m" (v->counter), "=qm" (c) - : "ir" (i) : "memory"); - return c; + GEN_BINARY_RMWcc(LOCK_PREFIX "subl", v->counter, i, "%0", "e"); } /** @@ -118,12 +114,7 @@ static inline void atomic_dec(atomic_t *v) */ static inline int atomic_dec_and_test(atomic_t *v) { - unsigned char c; - - asm volatile(LOCK_PREFIX "decl %0; sete %1" - : "+m" (v->counter), "=qm" (c) - : : "memory"); - return c != 0; + GEN_UNARY_RMWcc(LOCK_PREFIX "decl", v->counter, "%0", "e"); } /** @@ -136,12 +127,7 @@ static inline int atomic_dec_and_test(atomic_t *v) */ static inline int atomic_inc_and_test(atomic_t *v) { - unsigned char c; - - asm volatile(LOCK_PREFIX "incl %0; sete %1" - : "+m" (v->counter), "=qm" (c) - : : "memory"); - return c != 0; + GEN_UNARY_RMWcc(LOCK_PREFIX "incl", v->counter, "%0", "e"); } /** @@ -155,12 +141,7 @@ static inline int atomic_inc_and_test(atomic_t *v) */ static inline int atomic_add_negative(int i, atomic_t *v) { - unsigned char c; - - asm volatile(LOCK_PREFIX "addl %2,%0; sets %1" - : "+m" (v->counter), "=qm" (c) - : "ir" (i) : "memory"); - return c; + GEN_BINARY_RMWcc(LOCK_PREFIX "addl", v->counter, i, "%0", "s"); } /** diff --git a/arch/x86/include/asm/atomic64_64.h b/arch/x86/include/asm/atomic64_64.h index 0e1cbfc..3f065c9 100644 --- a/arch/x86/include/asm/atomic64_64.h +++ b/arch/x86/include/asm/atomic64_64.h @@ -72,12 +72,7 @@ static inline void atomic64_sub(long i, atomic64_t *v) */ static inline int atomic64_sub_and_test(long i, atomic64_t *v) { - unsigned char c; - - asm volatile(LOCK_PREFIX "subq %2,%0; sete %1" - : "=m" (v->counter), "=qm" (c) - : "er" (i), "m" (v->counter) : "memory"); - return c; + GEN_BINARY_RMWcc(LOCK_PREFIX "subq", v->counter, i, "%0", "e"); } /** @@ -116,12 +111,7 @@ static inline void atomic64_dec(atomic64_t *v) */ static inline int atomic64_dec_and_test(atomic64_t *v) { - unsigned char c; - - asm volatile(LOCK_PREFIX "decq %0; sete %1" - : "=m" (v->counter), "=qm" (c) - : "m" (v->counter) : "memory"); - return c != 0; + GEN_UNARY_RMWcc(LOCK_PREFIX "decq", v->counter, "%0", "e"); } /** @@ -134,12 +124,7 @@ static inline int atomic64_dec_and_test(atomic64_t *v) */ static inline int atomic64_inc_and_test(atomic64_t *v) { - unsigned char c; - - asm volatile(LOCK_PREFIX "incq %0; sete %1" - : "=m" (v->counter), "=qm" (c) - : "m" (v->counter) : "memory"); - return c != 0; + GEN_UNARY_RMWcc(LOCK_PREFIX "incq", v->counter, "%0", "e"); } /** @@ -153,12 +138,7 @@ static inline int atomic64_inc_and_test(atomic64_t *v) */ static inline int atomic64_add_negative(long i, atomic64_t *v) { - unsigned char c; - - asm volatile(LOCK_PREFIX "addq %2,%0; sets %1" - : "=m" (v->counter), "=qm" (c) - : "er" (i), "m" (v->counter) : "memory"); - return c; + GEN_BINARY_RMWcc(LOCK_PREFIX "addq", v->counter, i, "%0", "s"); } /** diff --git a/arch/x86/include/asm/bitops.h b/arch/x86/include/asm/bitops.h index 41639ce..6d76d09 100644 --- a/arch/x86/include/asm/bitops.h +++ b/arch/x86/include/asm/bitops.h @@ -14,6 +14,7 @@ #include #include +#include #if BITS_PER_LONG == 32 # define _BITOPS_LONG_SHIFT 5 @@ -204,12 +205,7 @@ static inline void change_bit(long nr, volatile unsigned long *addr) */ static inline int test_and_set_bit(long nr, volatile unsigned long *addr) { - int oldbit; - - asm volatile(LOCK_PREFIX "bts %2,%1\n\t" - "sbb %0,%0" : "=r" (oldbit), ADDR : "Ir" (nr) : "memory"); - - return oldbit; + GEN_BINARY_RMWcc(LOCK_PREFIX "bts", *addr, nr, "%0", "c"); } /** @@ -255,13 +251,7 @@ static inline int __test_and_set_bit(long nr, volatile unsigned long *addr) */ static inline int test_and_clear_bit(long nr, volatile unsigned long *addr) { - int oldbit; - - asm volatile(LOCK_PREFIX "btr %2,%1\n\t" - "sbb %0,%0" - : "=r" (oldbit), ADDR : "Ir" (nr) : "memory"); - - return oldbit; + GEN_BINARY_RMWcc(LOCK_PREFIX "btr", *addr, nr, "%0", "c"); } /** @@ -314,13 +304,7 @@ static inline int __test_and_change_bit(long nr, volatile unsigned long *addr) */ static inline int test_and_change_bit(long nr, volatile unsigned long *addr) { - int oldbit; - - asm volatile(LOCK_PREFIX "btc %2,%1\n\t" - "sbb %0,%0" - : "=r" (oldbit), ADDR : "Ir" (nr) : "memory"); - - return oldbit; + GEN_BINARY_RMWcc(LOCK_PREFIX "btc", *addr, nr, "%0", "c"); } static __always_inline int constant_test_bit(long nr, const volatile unsigned long *addr) diff --git a/arch/x86/include/asm/local.h b/arch/x86/include/asm/local.h index 2d89e39..5b23e60 100644 --- a/arch/x86/include/asm/local.h +++ b/arch/x86/include/asm/local.h @@ -52,12 +52,7 @@ static inline void local_sub(long i, local_t *l) */ static inline int local_sub_and_test(long i, local_t *l) { - unsigned char c; - - asm volatile(_ASM_SUB "%2,%0; sete %1" - : "+m" (l->a.counter), "=qm" (c) - : "ir" (i) : "memory"); - return c; + GEN_BINARY_RMWcc(_ASM_SUB, l->a.counter, i, "%0", "e"); } /** @@ -70,12 +65,7 @@ static inline int local_sub_and_test(long i, local_t *l) */ static inline int local_dec_and_test(local_t *l) { - unsigned char c; - - asm volatile(_ASM_DEC "%0; sete %1" - : "+m" (l->a.counter), "=qm" (c) - : : "memory"); - return c != 0; + GEN_UNARY_RMWcc(_ASM_DEC, l->a.counter, "%0", "e"); } /** @@ -88,12 +78,7 @@ static inline int local_dec_and_test(local_t *l) */ static inline int local_inc_and_test(local_t *l) { - unsigned char c; - - asm volatile(_ASM_INC "%0; sete %1" - : "+m" (l->a.counter), "=qm" (c) - : : "memory"); - return c != 0; + GEN_UNARY_RMWcc(_ASM_INC, l->a.counter, "%0", "e"); } /** @@ -107,12 +92,7 @@ static inline int local_inc_and_test(local_t *l) */ static inline int local_add_negative(long i, local_t *l) { - unsigned char c; - - asm volatile(_ASM_ADD "%2,%0; sets %1" - : "+m" (l->a.counter), "=qm" (c) - : "ir" (i) : "memory"); - return c; + GEN_BINARY_RMWcc(_ASM_ADD, l->a.counter, i, "%0", "s"); } /** diff --git a/arch/x86/include/asm/rmwcc.h b/arch/x86/include/asm/rmwcc.h new file mode 100644 index 0000000..735f184 --- /dev/null +++ b/arch/x86/include/asm/rmwcc.h @@ -0,0 +1,41 @@ +#ifndef _ASM_X86_RMWcc +#define _ASM_X86_RMWcc + +#ifdef CC_HAVE_ASM_GOTO + +#define __GEN_RMWcc(fullop, var, cc, ...) \ +do { \ + asm volatile goto (fullop "; j" cc " %l[cc_label]" \ + : : "m" (var), ## __VA_ARGS__ \ + : "memory" : cc_label); \ + return 0; \ +cc_label: \ + return 1; \ +} while (0) + +#define GEN_UNARY_RMWcc(op, var, arg0, cc) \ + __GEN_RMWcc(op " " arg0, var, cc) + +#define GEN_BINARY_RMWcc(op, var, val, arg0, cc) \ + __GEN_RMWcc(op " %1, " arg0, var, cc, "er" (val)) + +#else /* !CC_HAVE_ASM_GOTO */ + +#define __GEN_RMWcc(fullop, var, cc, ...) \ +do { \ + char c; \ + asm volatile (fullop "; set" cc " %1" \ + : "+m" (var), "=qm" (c) \ + : __VA_ARGS__ : "memory"); \ + return c != 0; \ +} while (0) + +#define GEN_UNARY_RMWcc(op, var, arg0, cc) \ + __GEN_RMWcc(op " " arg0, var, cc) + +#define GEN_BINARY_RMWcc(op, var, val, arg0, cc) \ + __GEN_RMWcc(op " %2, " arg0, var, cc, "er" (val)) + +#endif /* CC_HAVE_ASM_GOTO */ + +#endif /* _ASM_X86_RMWcc */ -- cgit v0.10.2 From b021fe3e25094fbec22d0eff846d2adeee1b9736 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 17 Sep 2013 09:30:55 +0200 Subject: sched, rcu: Make RCU use resched_cpu() We're going to deprecate and remove set_need_resched() for it will do the wrong thing. Make an exception for RCU and allow it to use resched_cpu() which will do the right thing. Signed-off-by: Peter Zijlstra Cc: Paul McKenney Link: http://lkml.kernel.org/n/tip-2eywnacjl1nllctl1nszqa5w@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/kernel/rcutree.c b/kernel/rcutree.c index 32618b3..1dc9f36 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -898,6 +898,12 @@ static void print_other_cpu_stall(struct rcu_state *rsp) force_quiescent_state(rsp); /* Kick them all. */ } +/* + * This function really isn't for public consumption, but RCU is special in + * that context switches can allow the state machine to make progress. + */ +extern void resched_cpu(int cpu); + static void print_cpu_stall(struct rcu_state *rsp) { int cpu; @@ -927,7 +933,14 @@ static void print_cpu_stall(struct rcu_state *rsp) 3 * rcu_jiffies_till_stall_check() + 3; raw_spin_unlock_irqrestore(&rnp->lock, flags); - set_need_resched(); /* kick ourselves to get things going. */ + /* + * Attempt to revive the RCU machinery by forcing a context switch. + * + * A context switch would normally allow the RCU state machine to make + * progress and it could be we're stuck in kernel space without context + * switches for an entirely unreasonable amount of time. + */ + resched_cpu(smp_processor_id()); } static void check_cpu_stall(struct rcu_state *rsp, struct rcu_data *rdp) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index ac57967..242da0c 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -513,12 +513,11 @@ static inline void init_hrtick(void) * might also involve a cross-CPU call to trigger the scheduler on * the target CPU. */ -#ifdef CONFIG_SMP void resched_task(struct task_struct *p) { int cpu; - assert_raw_spin_locked(&task_rq(p)->lock); + lockdep_assert_held(&task_rq(p)->lock); if (test_tsk_need_resched(p)) return; @@ -546,6 +545,7 @@ void resched_cpu(int cpu) raw_spin_unlock_irqrestore(&rq->lock, flags); } +#ifdef CONFIG_SMP #ifdef CONFIG_NO_HZ_COMMON /* * In the semi idle case, use the nearest busy cpu for migrating timers @@ -693,12 +693,6 @@ void sched_avg_update(struct rq *rq) } } -#else /* !CONFIG_SMP */ -void resched_task(struct task_struct *p) -{ - assert_raw_spin_locked(&task_rq(p)->lock); - set_tsk_need_resched(p); -} #endif /* CONFIG_SMP */ #if defined(CONFIG_RT_GROUP_SCHED) || (defined(CONFIG_FAIR_GROUP_SCHED) && \ -- cgit v0.10.2 From 3150398626466c6cc626732f60bc901d58f40677 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 12 Sep 2013 15:10:31 +0200 Subject: sched: Remove {set,clear}_need_resched Preemption semantics are going to change which mandate a change. All DRM usage sites are already broken and will not be affected (much) by this change. DRM people are aware and will remove the last few stragglers. For now, leave an empty stub that generates a warning, once all users are gone we can remove this. Signed-off-by: Peter Zijlstra Cc: airlied@linux.ie Cc: daniel.vetter@ffwll.ch Cc: paulmck@linux.vnet.ibm.com Link: http://lkml.kernel.org/n/tip-qfc1el2zvhxiyut4ai99ij4n@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/include/linux/thread_info.h b/include/linux/thread_info.h index e7e0473..a629e4b 100644 --- a/include/linux/thread_info.h +++ b/include/linux/thread_info.h @@ -104,8 +104,19 @@ static inline int test_ti_thread_flag(struct thread_info *ti, int flag) #define test_thread_flag(flag) \ test_ti_thread_flag(current_thread_info(), flag) -#define set_need_resched() set_thread_flag(TIF_NEED_RESCHED) -#define clear_need_resched() clear_thread_flag(TIF_NEED_RESCHED) +static inline __deprecated void set_need_resched(void) +{ + /* + * Use of this function in deprecated. + * + * As of this writing there are only a few users in the DRM tree left + * all of which are wrong and can be removed without causing too much + * grief. + * + * The DRM people are aware and are working on removing the last few + * instances. + */ +} #if defined TIF_RESTORE_SIGMASK && !defined HAVE_SET_RESTORE_SIGMASK /* -- cgit v0.10.2 From ea8117478918a4734586d35ff530721b682425be Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 11 Sep 2013 12:43:13 +0200 Subject: sched, idle: Fix the idle polling state logic Mike reported that commit 7d1a9417 ("x86: Use generic idle loop") regressed several workloads and caused excessive reschedule interrupts. The patch in question failed to notice that the x86 code had an inverted sense of the polling state versus the new generic code (x86: default polling, generic: default !polling). Fix the two prominent x86 mwait based idle drivers and introduce a few new generic polling helpers (fixing the wrong smp_mb__after_clear_bit usage). Also switch the idle routines to using tif_need_resched() which is an immediate TIF_NEED_RESCHED test as opposed to need_resched which will end up being slightly different. Reported-by: Mike Galbraith Signed-off-by: Peter Zijlstra Cc: lenb@kernel.org Cc: tglx@linutronix.de Link: http://lkml.kernel.org/n/tip-nc03imb0etuefmzybzj7sprf@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index c83516b..3fb8d95 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -391,9 +391,9 @@ static void amd_e400_idle(void) * The switch back from broadcast mode needs to be * called with interrupts disabled. */ - local_irq_disable(); - clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu); - local_irq_enable(); + local_irq_disable(); + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu); + local_irq_enable(); } else default_idle(); } diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index f98dd00..c7414a5 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -119,17 +119,10 @@ static struct dmi_system_id processor_power_dmi_table[] = { */ static void acpi_safe_halt(void) { - current_thread_info()->status &= ~TS_POLLING; - /* - * TS_POLLING-cleared state must be visible before we - * test NEED_RESCHED: - */ - smp_mb(); - if (!need_resched()) { + if (!tif_need_resched()) { safe_halt(); local_irq_disable(); } - current_thread_info()->status |= TS_POLLING; } #ifdef ARCH_APICTIMER_STOPS_ON_C3 @@ -737,6 +730,11 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev, if (unlikely(!pr)) return -EINVAL; + if (cx->entry_method == ACPI_CSTATE_FFH) { + if (current_set_polling_and_test()) + return -EINVAL; + } + lapic_timer_state_broadcast(pr, cx, 1); acpi_idle_do_entry(cx); @@ -790,18 +788,9 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev, if (unlikely(!pr)) return -EINVAL; - if (cx->entry_method != ACPI_CSTATE_FFH) { - current_thread_info()->status &= ~TS_POLLING; - /* - * TS_POLLING-cleared state must be visible before we test - * NEED_RESCHED: - */ - smp_mb(); - - if (unlikely(need_resched())) { - current_thread_info()->status |= TS_POLLING; + if (cx->entry_method == ACPI_CSTATE_FFH) { + if (current_set_polling_and_test()) return -EINVAL; - } } /* @@ -819,9 +808,6 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev, sched_clock_idle_wakeup_event(0); - if (cx->entry_method != ACPI_CSTATE_FFH) - current_thread_info()->status |= TS_POLLING; - lapic_timer_state_broadcast(pr, cx, 0); return index; } @@ -858,18 +844,9 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, } } - if (cx->entry_method != ACPI_CSTATE_FFH) { - current_thread_info()->status &= ~TS_POLLING; - /* - * TS_POLLING-cleared state must be visible before we test - * NEED_RESCHED: - */ - smp_mb(); - - if (unlikely(need_resched())) { - current_thread_info()->status |= TS_POLLING; + if (cx->entry_method == ACPI_CSTATE_FFH) { + if (current_set_polling_and_test()) return -EINVAL; - } } acpi_unlazy_tlb(smp_processor_id()); @@ -915,9 +892,6 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, sched_clock_idle_wakeup_event(0); - if (cx->entry_method != ACPI_CSTATE_FFH) - current_thread_info()->status |= TS_POLLING; - lapic_timer_state_broadcast(pr, cx, 0); return index; } diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index fa6964d..f116d66 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -359,7 +359,7 @@ static int intel_idle(struct cpuidle_device *dev, if (!(lapic_timer_reliable_states & (1 << (cstate)))) clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu); - if (!need_resched()) { + if (!current_set_polling_and_test()) { __monitor((void *)¤t_thread_info()->flags, 0, 0); smp_mb(); diff --git a/include/linux/sched.h b/include/linux/sched.h index b5344de..e783ec5 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2479,34 +2479,98 @@ static inline int tsk_is_polling(struct task_struct *p) { return task_thread_info(p)->status & TS_POLLING; } -static inline void current_set_polling(void) +static inline void __current_set_polling(void) { current_thread_info()->status |= TS_POLLING; } -static inline void current_clr_polling(void) +static inline bool __must_check current_set_polling_and_test(void) +{ + __current_set_polling(); + + /* + * Polling state must be visible before we test NEED_RESCHED, + * paired by resched_task() + */ + smp_mb(); + + return unlikely(tif_need_resched()); +} + +static inline void __current_clr_polling(void) { current_thread_info()->status &= ~TS_POLLING; - smp_mb__after_clear_bit(); +} + +static inline bool __must_check current_clr_polling_and_test(void) +{ + __current_clr_polling(); + + /* + * Polling state must be visible before we test NEED_RESCHED, + * paired by resched_task() + */ + smp_mb(); + + return unlikely(tif_need_resched()); } #elif defined(TIF_POLLING_NRFLAG) static inline int tsk_is_polling(struct task_struct *p) { return test_tsk_thread_flag(p, TIF_POLLING_NRFLAG); } -static inline void current_set_polling(void) + +static inline void __current_set_polling(void) { set_thread_flag(TIF_POLLING_NRFLAG); } -static inline void current_clr_polling(void) +static inline bool __must_check current_set_polling_and_test(void) +{ + __current_set_polling(); + + /* + * Polling state must be visible before we test NEED_RESCHED, + * paired by resched_task() + * + * XXX: assumes set/clear bit are identical barrier wise. + */ + smp_mb__after_clear_bit(); + + return unlikely(tif_need_resched()); +} + +static inline void __current_clr_polling(void) { clear_thread_flag(TIF_POLLING_NRFLAG); } + +static inline bool __must_check current_clr_polling_and_test(void) +{ + __current_clr_polling(); + + /* + * Polling state must be visible before we test NEED_RESCHED, + * paired by resched_task() + */ + smp_mb__after_clear_bit(); + + return unlikely(tif_need_resched()); +} + #else static inline int tsk_is_polling(struct task_struct *p) { return 0; } -static inline void current_set_polling(void) { } -static inline void current_clr_polling(void) { } +static inline void __current_set_polling(void) { } +static inline void __current_clr_polling(void) { } + +static inline bool __must_check current_set_polling_and_test(void) +{ + return unlikely(tif_need_resched()); +} +static inline bool __must_check current_clr_polling_and_test(void) +{ + return unlikely(tif_need_resched()); +} #endif /* diff --git a/include/linux/thread_info.h b/include/linux/thread_info.h index a629e4b..fddbe20 100644 --- a/include/linux/thread_info.h +++ b/include/linux/thread_info.h @@ -118,6 +118,8 @@ static inline __deprecated void set_need_resched(void) */ } +#define tif_need_resched() test_thread_flag(TIF_NEED_RESCHED) + #if defined TIF_RESTORE_SIGMASK && !defined HAVE_SET_RESTORE_SIGMASK /* * An arch can define its own version of set_restore_sigmask() to get the diff --git a/kernel/cpu/idle.c b/kernel/cpu/idle.c index e695c0a..c261409 100644 --- a/kernel/cpu/idle.c +++ b/kernel/cpu/idle.c @@ -44,7 +44,7 @@ static inline int cpu_idle_poll(void) rcu_idle_enter(); trace_cpu_idle_rcuidle(0, smp_processor_id()); local_irq_enable(); - while (!need_resched()) + while (!tif_need_resched()) cpu_relax(); trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, smp_processor_id()); rcu_idle_exit(); @@ -92,8 +92,7 @@ static void cpu_idle_loop(void) if (cpu_idle_force_poll || tick_check_broadcast_expired()) { cpu_idle_poll(); } else { - current_clr_polling(); - if (!need_resched()) { + if (!current_clr_polling_and_test()) { stop_critical_timings(); rcu_idle_enter(); arch_cpu_idle(); @@ -103,7 +102,7 @@ static void cpu_idle_loop(void) } else { local_irq_enable(); } - current_set_polling(); + __current_set_polling(); } arch_cpu_idle_exit(); } @@ -129,7 +128,7 @@ void cpu_startup_entry(enum cpuhp_state state) */ boot_init_stack_canary(); #endif - current_set_polling(); + __current_set_polling(); arch_cpu_idle_prepare(); cpu_idle_loop(); } -- cgit v0.10.2 From 4a2b4b222743bb07fedf985b884550f2ca067ea9 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 14 Aug 2013 14:55:24 +0200 Subject: sched: Introduce preempt_count accessor functions Replace the single preempt_count() 'function' that's an lvalue with two proper functions: preempt_count() - returns the preempt_count value as rvalue preempt_count_set() - Allows setting the preempt-count value Also provide preempt_count_ptr() as a convenience wrapper to implement all modifying operations. Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-orxrbycjozopqfhb4dxdkdvb@git.kernel.org [ Fixed build failure. ] Signed-off-by: Ingo Molnar diff --git a/include/linux/preempt.h b/include/linux/preempt.h index f5d4723..eaac52a 100644 --- a/include/linux/preempt.h +++ b/include/linux/preempt.h @@ -10,19 +10,32 @@ #include #include +static __always_inline int preempt_count(void) +{ + return current_thread_info()->preempt_count; +} + +static __always_inline int *preempt_count_ptr(void) +{ + return ¤t_thread_info()->preempt_count; +} + +static __always_inline void preempt_count_set(int pc) +{ + *preempt_count_ptr() = pc; +} + #if defined(CONFIG_DEBUG_PREEMPT) || defined(CONFIG_PREEMPT_TRACER) extern void add_preempt_count(int val); extern void sub_preempt_count(int val); #else -# define add_preempt_count(val) do { preempt_count() += (val); } while (0) -# define sub_preempt_count(val) do { preempt_count() -= (val); } while (0) +# define add_preempt_count(val) do { *preempt_count_ptr() += (val); } while (0) +# define sub_preempt_count(val) do { *preempt_count_ptr() -= (val); } while (0) #endif #define inc_preempt_count() add_preempt_count(1) #define dec_preempt_count() sub_preempt_count(1) -#define preempt_count() (current_thread_info()->preempt_count) - #ifdef CONFIG_PREEMPT asmlinkage void preempt_schedule(void); @@ -81,9 +94,9 @@ do { \ /* For debugging and tracer internals only! */ #define add_preempt_count_notrace(val) \ - do { preempt_count() += (val); } while (0) + do { *preempt_count_ptr() += (val); } while (0) #define sub_preempt_count_notrace(val) \ - do { preempt_count() -= (val); } while (0) + do { *preempt_count_ptr() -= (val); } while (0) #define inc_preempt_count_notrace() add_preempt_count_notrace(1) #define dec_preempt_count_notrace() sub_preempt_count_notrace(1) diff --git a/init/main.c b/init/main.c index af310af..7cc4b78 100644 --- a/init/main.c +++ b/init/main.c @@ -692,7 +692,7 @@ int __init_or_module do_one_initcall(initcall_t fn) if (preempt_count() != count) { sprintf(msgbuf, "preemption imbalance "); - preempt_count() = count; + preempt_count_set(count); } if (irqs_disabled()) { strlcat(msgbuf, "disabled interrupts ", sizeof(msgbuf)); diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 242da0c..fe89afa 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2219,7 +2219,7 @@ void __kprobes add_preempt_count(int val) if (DEBUG_LOCKS_WARN_ON((preempt_count() < 0))) return; #endif - preempt_count() += val; + add_preempt_count_notrace(val); #ifdef CONFIG_DEBUG_PREEMPT /* * Spinlock count overflowing soon? @@ -2250,7 +2250,7 @@ void __kprobes sub_preempt_count(int val) if (preempt_count() == val) trace_preempt_on(CALLER_ADDR0, get_parent_ip(CALLER_ADDR1)); - preempt_count() -= val; + sub_preempt_count_notrace(val); } EXPORT_SYMBOL(sub_preempt_count); diff --git a/kernel/softirq.c b/kernel/softirq.c index 53cc09c..a90de70 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -106,7 +106,7 @@ static void __local_bh_disable(unsigned long ip, unsigned int cnt) * We must manually increment preempt_count here and manually * call the trace_preempt_off later. */ - preempt_count() += cnt; + add_preempt_count_notrace(cnt); /* * Were softirqs turned off above: */ @@ -256,7 +256,7 @@ restart: " exited with %08x?\n", vec_nr, softirq_to_name[vec_nr], h->action, prev_count, preempt_count()); - preempt_count() = prev_count; + preempt_count_set(prev_count); } rcu_bh_qs(cpu); diff --git a/kernel/timer.c b/kernel/timer.c index 4296d13..6582b82 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -1092,7 +1092,7 @@ static int cascade(struct tvec_base *base, struct tvec *tv, int index) static void call_timer_fn(struct timer_list *timer, void (*fn)(unsigned long), unsigned long data) { - int preempt_count = preempt_count(); + int count = preempt_count(); #ifdef CONFIG_LOCKDEP /* @@ -1119,16 +1119,16 @@ static void call_timer_fn(struct timer_list *timer, void (*fn)(unsigned long), lock_map_release(&lockdep_map); - if (preempt_count != preempt_count()) { + if (count != preempt_count()) { WARN_ONCE(1, "timer: %pF preempt leak: %08x -> %08x\n", - fn, preempt_count, preempt_count()); + fn, count, preempt_count()); /* * Restore the preempt count. That gives us a decent * chance to survive and extract information. If the * callback kept a lock held, bad luck, but not worse * than the BUG() we had. */ - preempt_count() = preempt_count; + preempt_count_set(count); } } diff --git a/lib/locking-selftest.c b/lib/locking-selftest.c index 6dc09d8..872a15a 100644 --- a/lib/locking-selftest.c +++ b/lib/locking-selftest.c @@ -1002,7 +1002,7 @@ static void dotest(void (*testcase_fn)(void), int expected, int lockclass_mask) * Some tests (e.g. double-unlock) might corrupt the preemption * count, so restore it: */ - preempt_count() = saved_preempt_count; + preempt_count_set(saved_preempt_count); #ifdef CONFIG_TRACE_IRQFLAGS if (softirq_count()) current->softirqs_enabled = 0; diff --git a/lib/smp_processor_id.c b/lib/smp_processor_id.c index 4c0d0e5..04abe53 100644 --- a/lib/smp_processor_id.c +++ b/lib/smp_processor_id.c @@ -9,10 +9,9 @@ notrace unsigned int debug_smp_processor_id(void) { - unsigned long preempt_count = preempt_count(); int this_cpu = raw_smp_processor_id(); - if (likely(preempt_count)) + if (likely(preempt_count())) goto out; if (irqs_disabled()) -- cgit v0.10.2 From f27dde8deef33c9e58027df11ceab2198601d6a6 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 14 Aug 2013 14:55:31 +0200 Subject: sched: Add NEED_RESCHED to the preempt_count In order to combine the preemption and need_resched test we need to fold the need_resched information into the preempt_count value. Since the NEED_RESCHED flag is set across CPUs this needs to be an atomic operation, however we very much want to avoid making preempt_count atomic, therefore we keep the existing TIF_NEED_RESCHED infrastructure in place but at 3 sites test it and fold its value into preempt_count; namely: - resched_task() when setting TIF_NEED_RESCHED on the current task - scheduler_ipi() when resched_task() sets TIF_NEED_RESCHED on a remote task it follows it up with a reschedule IPI and we can modify the cpu local preempt_count from there. - cpu_idle_loop() for when resched_task() found tsk_is_polling(). We use an inverted bitmask to indicate need_resched so that a 0 means both need_resched and !atomic. Also remove the barrier() in preempt_enable() between preempt_enable_no_resched() and preempt_check_resched() to avoid having to reload the preemption value and allow the compiler to use the flags of the previuos decrement. I couldn't come up with any sane reason for this barrier() to be there as preempt_enable_no_resched() already has a barrier() before doing the decrement. Suggested-by: Ingo Molnar Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-7a7m5qqbn5pmwnd4wko9u6da@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/include/linux/preempt.h b/include/linux/preempt.h index eaac52a..92e3418 100644 --- a/include/linux/preempt.h +++ b/include/linux/preempt.h @@ -10,9 +10,19 @@ #include #include +/* + * We use the MSB mostly because its available; see for + * the other bits -- can't include that header due to inclusion hell. + */ +#define PREEMPT_NEED_RESCHED 0x80000000 + +/* + * We mask the PREEMPT_NEED_RESCHED bit so as not to confuse all current users + * that think a non-zero value indicates we cannot preempt. + */ static __always_inline int preempt_count(void) { - return current_thread_info()->preempt_count; + return current_thread_info()->preempt_count & ~PREEMPT_NEED_RESCHED; } static __always_inline int *preempt_count_ptr(void) @@ -20,11 +30,40 @@ static __always_inline int *preempt_count_ptr(void) return ¤t_thread_info()->preempt_count; } +/* + * We now loose PREEMPT_NEED_RESCHED and cause an extra reschedule; however the + * alternative is loosing a reschedule. Better schedule too often -- also this + * should be a very rare operation. + */ static __always_inline void preempt_count_set(int pc) { *preempt_count_ptr() = pc; } +/* + * We fold the NEED_RESCHED bit into the preempt count such that + * preempt_enable() can decrement and test for needing to reschedule with a + * single instruction. + * + * We invert the actual bit, so that when the decrement hits 0 we know we both + * need to resched (the bit is cleared) and can resched (no preempt count). + */ + +static __always_inline void set_preempt_need_resched(void) +{ + *preempt_count_ptr() &= ~PREEMPT_NEED_RESCHED; +} + +static __always_inline void clear_preempt_need_resched(void) +{ + *preempt_count_ptr() |= PREEMPT_NEED_RESCHED; +} + +static __always_inline bool test_preempt_need_resched(void) +{ + return !(*preempt_count_ptr() & PREEMPT_NEED_RESCHED); +} + #if defined(CONFIG_DEBUG_PREEMPT) || defined(CONFIG_PREEMPT_TRACER) extern void add_preempt_count(int val); extern void sub_preempt_count(int val); @@ -42,7 +81,7 @@ asmlinkage void preempt_schedule(void); #define preempt_check_resched() \ do { \ - if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) \ + if (unlikely(!*preempt_count_ptr())) \ preempt_schedule(); \ } while (0) @@ -52,7 +91,7 @@ void preempt_schedule_context(void); #define preempt_check_resched_context() \ do { \ - if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) \ + if (unlikely(!*preempt_count_ptr())) \ preempt_schedule_context(); \ } while (0) #else @@ -88,7 +127,6 @@ do { \ #define preempt_enable() \ do { \ preempt_enable_no_resched(); \ - barrier(); \ preempt_check_resched(); \ } while (0) @@ -116,7 +154,6 @@ do { \ #define preempt_enable_notrace() \ do { \ preempt_enable_no_resched_notrace(); \ - barrier(); \ preempt_check_resched_context(); \ } while (0) diff --git a/include/linux/sched.h b/include/linux/sched.h index e783ec5..9fa151f 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -22,6 +22,7 @@ struct sched_param { #include #include #include +#include #include #include @@ -434,7 +435,9 @@ struct task_cputime { * We include PREEMPT_ACTIVE to avoid cond_resched() from working * before the scheduler is active -- see should_resched(). */ -#define INIT_PREEMPT_COUNT (1 + PREEMPT_ACTIVE) +#define INIT_PREEMPT_COUNT (1 + PREEMPT_ACTIVE + PREEMPT_NEED_RESCHED) +#define PREEMPT_ENABLED (PREEMPT_NEED_RESCHED) +#define PREEMPT_DISABLED (1 + PREEMPT_NEED_RESCHED) /** * struct thread_group_cputimer - thread group interval timer counts @@ -2408,7 +2411,7 @@ static inline int signal_pending_state(long state, struct task_struct *p) static inline int need_resched(void) { - return unlikely(test_thread_flag(TIF_NEED_RESCHED)); + return unlikely(test_preempt_need_resched()); } /* diff --git a/kernel/cpu/idle.c b/kernel/cpu/idle.c index c261409..988573a 100644 --- a/kernel/cpu/idle.c +++ b/kernel/cpu/idle.c @@ -105,6 +105,13 @@ static void cpu_idle_loop(void) __current_set_polling(); } arch_cpu_idle_exit(); + /* + * We need to test and propagate the TIF_NEED_RESCHED + * bit here because we might not have send the + * reschedule IPI to idle tasks. + */ + if (tif_need_resched()) + set_preempt_need_resched(); } tick_nohz_idle_exit(); schedule_preempt_disabled(); diff --git a/kernel/sched/core.c b/kernel/sched/core.c index fe89afa..ee61f5a 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -525,8 +525,10 @@ void resched_task(struct task_struct *p) set_tsk_need_resched(p); cpu = task_cpu(p); - if (cpu == smp_processor_id()) + if (cpu == smp_processor_id()) { + set_preempt_need_resched(); return; + } /* NEED_RESCHED must be visible before we test polling */ smp_mb(); @@ -1391,6 +1393,14 @@ static void sched_ttwu_pending(void) void scheduler_ipi(void) { + /* + * Fold TIF_NEED_RESCHED into the preempt_count; anybody setting + * TIF_NEED_RESCHED remotely (for the first time) will also send + * this IPI. + */ + if (tif_need_resched()) + set_preempt_need_resched(); + if (llist_empty(&this_rq()->wake_list) && !tick_nohz_full_cpu(smp_processor_id()) && !got_nohz_idle_kick()) @@ -1714,7 +1724,7 @@ void sched_fork(struct task_struct *p) #endif #ifdef CONFIG_PREEMPT_COUNT /* Want to start with kernel preemption disabled. */ - task_thread_info(p)->preempt_count = 1; + task_thread_info(p)->preempt_count = PREEMPT_DISABLED; #endif #ifdef CONFIG_SMP plist_node_init(&p->pushable_tasks, MAX_PRIO); @@ -2425,6 +2435,7 @@ need_resched: put_prev_task(rq, prev); next = pick_next_task(rq); clear_tsk_need_resched(prev); + clear_preempt_need_resched(); rq->skip_clock_update = 0; if (likely(prev != next)) { @@ -2536,11 +2547,10 @@ EXPORT_SYMBOL(preempt_schedule); */ asmlinkage void __sched preempt_schedule_irq(void) { - struct thread_info *ti = current_thread_info(); enum ctx_state prev_state; /* Catch callers which need to be fixed */ - BUG_ON(ti->preempt_count || !irqs_disabled()); + BUG_ON(preempt_count() || !irqs_disabled()); prev_state = exception_enter(); @@ -4207,7 +4217,7 @@ void init_idle(struct task_struct *idle, int cpu) raw_spin_unlock_irqrestore(&rq->lock, flags); /* Set the preempt count _outside_ the spinlocks! */ - task_thread_info(idle)->preempt_count = 0; + task_thread_info(idle)->preempt_count = PREEMPT_ENABLED; /* * The idle tasks have their own, simple scheduling class: -- cgit v0.10.2 From a787870924dbd6f321661e06d4ec1c7a408c9ccf Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 14 Aug 2013 14:55:40 +0200 Subject: sched, arch: Create asm/preempt.h In order to prepare to per-arch implementations of preempt_count move the required bits into an asm-generic header and use this for all archs. Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-h5j0c1r3e3fk015m30h8f1zx@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/arch/alpha/include/asm/Kbuild b/arch/alpha/include/asm/Kbuild index a6e85f44..f01fb50 100644 --- a/arch/alpha/include/asm/Kbuild +++ b/arch/alpha/include/asm/Kbuild @@ -3,3 +3,4 @@ generic-y += clkdev.h generic-y += exec.h generic-y += trace_clock.h +generic-y += preempt.h diff --git a/arch/arc/include/asm/Kbuild b/arch/arc/include/asm/Kbuild index d8dd660..5943f7f 100644 --- a/arch/arc/include/asm/Kbuild +++ b/arch/arc/include/asm/Kbuild @@ -46,3 +46,4 @@ generic-y += ucontext.h generic-y += user.h generic-y += vga.h generic-y += xor.h +generic-y += preempt.h diff --git a/arch/arm/include/asm/Kbuild b/arch/arm/include/asm/Kbuild index d3db398..4e6838d 100644 --- a/arch/arm/include/asm/Kbuild +++ b/arch/arm/include/asm/Kbuild @@ -33,3 +33,4 @@ generic-y += timex.h generic-y += trace_clock.h generic-y += types.h generic-y += unaligned.h +generic-y += preempt.h diff --git a/arch/arm64/include/asm/Kbuild b/arch/arm64/include/asm/Kbuild index 79a642d..519f89f 100644 --- a/arch/arm64/include/asm/Kbuild +++ b/arch/arm64/include/asm/Kbuild @@ -50,3 +50,4 @@ generic-y += unaligned.h generic-y += user.h generic-y += vga.h generic-y += xor.h +generic-y += preempt.h diff --git a/arch/avr32/include/asm/Kbuild b/arch/avr32/include/asm/Kbuild index d22af85..b946080 100644 --- a/arch/avr32/include/asm/Kbuild +++ b/arch/avr32/include/asm/Kbuild @@ -3,3 +3,4 @@ generic-y += clkdev.h generic-y += exec.h generic-y += trace_clock.h generic-y += param.h +generic-y += preempt.h diff --git a/arch/blackfin/include/asm/Kbuild b/arch/blackfin/include/asm/Kbuild index 127826f..f2b4347 100644 --- a/arch/blackfin/include/asm/Kbuild +++ b/arch/blackfin/include/asm/Kbuild @@ -44,3 +44,4 @@ generic-y += ucontext.h generic-y += unaligned.h generic-y += user.h generic-y += xor.h +generic-y += preempt.h diff --git a/arch/c6x/include/asm/Kbuild b/arch/c6x/include/asm/Kbuild index e49f918..fc0b3c3 100644 --- a/arch/c6x/include/asm/Kbuild +++ b/arch/c6x/include/asm/Kbuild @@ -56,3 +56,4 @@ generic-y += ucontext.h generic-y += user.h generic-y += vga.h generic-y += xor.h +generic-y += preempt.h diff --git a/arch/cris/include/asm/Kbuild b/arch/cris/include/asm/Kbuild index c832545..b06caf6 100644 --- a/arch/cris/include/asm/Kbuild +++ b/arch/cris/include/asm/Kbuild @@ -11,3 +11,4 @@ generic-y += module.h generic-y += trace_clock.h generic-y += vga.h generic-y += xor.h +generic-y += preempt.h diff --git a/arch/frv/include/asm/Kbuild b/arch/frv/include/asm/Kbuild index c5d7670..74742dc 100644 --- a/arch/frv/include/asm/Kbuild +++ b/arch/frv/include/asm/Kbuild @@ -2,3 +2,4 @@ generic-y += clkdev.h generic-y += exec.h generic-y += trace_clock.h +generic-y += preempt.h diff --git a/arch/h8300/include/asm/Kbuild b/arch/h8300/include/asm/Kbuild index 8ada3cf..7e0e721 100644 --- a/arch/h8300/include/asm/Kbuild +++ b/arch/h8300/include/asm/Kbuild @@ -6,3 +6,4 @@ generic-y += mmu.h generic-y += module.h generic-y += trace_clock.h generic-y += xor.h +generic-y += preempt.h diff --git a/arch/hexagon/include/asm/Kbuild b/arch/hexagon/include/asm/Kbuild index 1da17ca..67c3450 100644 --- a/arch/hexagon/include/asm/Kbuild +++ b/arch/hexagon/include/asm/Kbuild @@ -53,3 +53,4 @@ generic-y += types.h generic-y += ucontext.h generic-y += unaligned.h generic-y += xor.h +generic-y += preempt.h diff --git a/arch/ia64/include/asm/Kbuild b/arch/ia64/include/asm/Kbuild index a3456f3..f93ee08 100644 --- a/arch/ia64/include/asm/Kbuild +++ b/arch/ia64/include/asm/Kbuild @@ -3,4 +3,5 @@ generic-y += clkdev.h generic-y += exec.h generic-y += kvm_para.h generic-y += trace_clock.h +generic-y += preempt.h generic-y += vtime.h \ No newline at end of file diff --git a/arch/m32r/include/asm/Kbuild b/arch/m32r/include/asm/Kbuild index bebdc36..2b58c5f 100644 --- a/arch/m32r/include/asm/Kbuild +++ b/arch/m32r/include/asm/Kbuild @@ -3,3 +3,4 @@ generic-y += clkdev.h generic-y += exec.h generic-y += module.h generic-y += trace_clock.h +generic-y += preempt.h diff --git a/arch/m68k/include/asm/Kbuild b/arch/m68k/include/asm/Kbuild index 09d77a8..a5d27f2 100644 --- a/arch/m68k/include/asm/Kbuild +++ b/arch/m68k/include/asm/Kbuild @@ -31,3 +31,4 @@ generic-y += trace_clock.h generic-y += types.h generic-y += word-at-a-time.h generic-y += xor.h +generic-y += preempt.h diff --git a/arch/metag/include/asm/Kbuild b/arch/metag/include/asm/Kbuild index 6ae0ccb..84d0c1d 100644 --- a/arch/metag/include/asm/Kbuild +++ b/arch/metag/include/asm/Kbuild @@ -52,3 +52,4 @@ generic-y += unaligned.h generic-y += user.h generic-y += vga.h generic-y += xor.h +generic-y += preempt.h diff --git a/arch/microblaze/include/asm/Kbuild b/arch/microblaze/include/asm/Kbuild index d3c51a6..ce0bbf8 100644 --- a/arch/microblaze/include/asm/Kbuild +++ b/arch/microblaze/include/asm/Kbuild @@ -3,3 +3,4 @@ generic-y += clkdev.h generic-y += exec.h generic-y += trace_clock.h generic-y += syscalls.h +generic-y += preempt.h diff --git a/arch/mips/include/asm/Kbuild b/arch/mips/include/asm/Kbuild index 454ddf9..1acbb8b 100644 --- a/arch/mips/include/asm/Kbuild +++ b/arch/mips/include/asm/Kbuild @@ -11,5 +11,6 @@ generic-y += sections.h generic-y += segment.h generic-y += serial.h generic-y += trace_clock.h +generic-y += preempt.h generic-y += ucontext.h generic-y += xor.h diff --git a/arch/mn10300/include/asm/Kbuild b/arch/mn10300/include/asm/Kbuild index c5d7670..74742dc 100644 --- a/arch/mn10300/include/asm/Kbuild +++ b/arch/mn10300/include/asm/Kbuild @@ -2,3 +2,4 @@ generic-y += clkdev.h generic-y += exec.h generic-y += trace_clock.h +generic-y += preempt.h diff --git a/arch/openrisc/include/asm/Kbuild b/arch/openrisc/include/asm/Kbuild index 195653e..7840562 100644 --- a/arch/openrisc/include/asm/Kbuild +++ b/arch/openrisc/include/asm/Kbuild @@ -67,3 +67,4 @@ generic-y += ucontext.h generic-y += user.h generic-y += word-at-a-time.h generic-y += xor.h +generic-y += preempt.h diff --git a/arch/parisc/include/asm/Kbuild b/arch/parisc/include/asm/Kbuild index ff4c9fa..a603b9e 100644 --- a/arch/parisc/include/asm/Kbuild +++ b/arch/parisc/include/asm/Kbuild @@ -4,3 +4,4 @@ generic-y += word-at-a-time.h auxvec.h user.h cputime.h emergency-restart.h \ div64.h irq_regs.h kdebug.h kvm_para.h local64.h local.h param.h \ poll.h xor.h clkdev.h exec.h generic-y += trace_clock.h +generic-y += preempt.h diff --git a/arch/powerpc/include/asm/Kbuild b/arch/powerpc/include/asm/Kbuild index 704e6f1..d8f9d2f 100644 --- a/arch/powerpc/include/asm/Kbuild +++ b/arch/powerpc/include/asm/Kbuild @@ -2,4 +2,5 @@ generic-y += clkdev.h generic-y += rwsem.h generic-y += trace_clock.h +generic-y += preempt.h generic-y += vtime.h \ No newline at end of file diff --git a/arch/s390/include/asm/Kbuild b/arch/s390/include/asm/Kbuild index f313f9c..7a5288f 100644 --- a/arch/s390/include/asm/Kbuild +++ b/arch/s390/include/asm/Kbuild @@ -2,3 +2,4 @@ generic-y += clkdev.h generic-y += trace_clock.h +generic-y += preempt.h diff --git a/arch/score/include/asm/Kbuild b/arch/score/include/asm/Kbuild index e1c7bb9..f3414ad 100644 --- a/arch/score/include/asm/Kbuild +++ b/arch/score/include/asm/Kbuild @@ -4,3 +4,4 @@ header-y += generic-y += clkdev.h generic-y += trace_clock.h generic-y += xor.h +generic-y += preempt.h diff --git a/arch/sh/include/asm/Kbuild b/arch/sh/include/asm/Kbuild index 280bea9..231efbb 100644 --- a/arch/sh/include/asm/Kbuild +++ b/arch/sh/include/asm/Kbuild @@ -34,3 +34,4 @@ generic-y += termios.h generic-y += trace_clock.h generic-y += ucontext.h generic-y += xor.h +generic-y += preempt.h diff --git a/arch/sparc/include/asm/Kbuild b/arch/sparc/include/asm/Kbuild index 7e4a97f..bf39066 100644 --- a/arch/sparc/include/asm/Kbuild +++ b/arch/sparc/include/asm/Kbuild @@ -16,3 +16,4 @@ generic-y += serial.h generic-y += trace_clock.h generic-y += types.h generic-y += word-at-a-time.h +generic-y += preempt.h diff --git a/arch/tile/include/asm/Kbuild b/arch/tile/include/asm/Kbuild index 664d6ad..22f3bd1 100644 --- a/arch/tile/include/asm/Kbuild +++ b/arch/tile/include/asm/Kbuild @@ -38,3 +38,4 @@ generic-y += termios.h generic-y += trace_clock.h generic-y += types.h generic-y += xor.h +generic-y += preempt.h diff --git a/arch/um/include/asm/Kbuild b/arch/um/include/asm/Kbuild index b30f34a..fdde187 100644 --- a/arch/um/include/asm/Kbuild +++ b/arch/um/include/asm/Kbuild @@ -3,3 +3,4 @@ generic-y += hw_irq.h irq_regs.h kdebug.h percpu.h sections.h topology.h xor.h generic-y += ftrace.h pci.h io.h param.h delay.h mutex.h current.h exec.h generic-y += switch_to.h clkdev.h generic-y += trace_clock.h +generic-y += preempt.h diff --git a/arch/unicore32/include/asm/Kbuild b/arch/unicore32/include/asm/Kbuild index 89d8b6c..00045cb 100644 --- a/arch/unicore32/include/asm/Kbuild +++ b/arch/unicore32/include/asm/Kbuild @@ -60,3 +60,4 @@ generic-y += unaligned.h generic-y += user.h generic-y += vga.h generic-y += xor.h +generic-y += preempt.h diff --git a/arch/x86/include/asm/Kbuild b/arch/x86/include/asm/Kbuild index 7f66985..eca2028 100644 --- a/arch/x86/include/asm/Kbuild +++ b/arch/x86/include/asm/Kbuild @@ -5,3 +5,4 @@ genhdr-y += unistd_64.h genhdr-y += unistd_x32.h generic-y += clkdev.h +generic-y += preempt.h diff --git a/arch/xtensa/include/asm/Kbuild b/arch/xtensa/include/asm/Kbuild index 1b98264..228d6ae 100644 --- a/arch/xtensa/include/asm/Kbuild +++ b/arch/xtensa/include/asm/Kbuild @@ -28,3 +28,4 @@ generic-y += termios.h generic-y += topology.h generic-y += trace_clock.h generic-y += xor.h +generic-y += preempt.h diff --git a/include/asm-generic/preempt.h b/include/asm-generic/preempt.h new file mode 100644 index 0000000..a1fc659 --- /dev/null +++ b/include/asm-generic/preempt.h @@ -0,0 +1,54 @@ +#ifndef __ASM_PREEMPT_H +#define __ASM_PREEMPT_H + +#include + +/* + * We mask the PREEMPT_NEED_RESCHED bit so as not to confuse all current users + * that think a non-zero value indicates we cannot preempt. + */ +static __always_inline int preempt_count(void) +{ + return current_thread_info()->preempt_count & ~PREEMPT_NEED_RESCHED; +} + +static __always_inline int *preempt_count_ptr(void) +{ + return ¤t_thread_info()->preempt_count; +} + +/* + * We now loose PREEMPT_NEED_RESCHED and cause an extra reschedule; however the + * alternative is loosing a reschedule. Better schedule too often -- also this + * should be a very rare operation. + */ +static __always_inline void preempt_count_set(int pc) +{ + *preempt_count_ptr() = pc; +} + +/* + * We fold the NEED_RESCHED bit into the preempt count such that + * preempt_enable() can decrement and test for needing to reschedule with a + * single instruction. + * + * We invert the actual bit, so that when the decrement hits 0 we know we both + * need to resched (the bit is cleared) and can resched (no preempt count). + */ + +static __always_inline void set_preempt_need_resched(void) +{ + *preempt_count_ptr() &= ~PREEMPT_NEED_RESCHED; +} + +static __always_inline void clear_preempt_need_resched(void) +{ + *preempt_count_ptr() |= PREEMPT_NEED_RESCHED; +} + +static __always_inline bool test_preempt_need_resched(void) +{ + return !(*preempt_count_ptr() & PREEMPT_NEED_RESCHED); +} + +#endif /* __ASM_PREEMPT_H */ diff --git a/include/linux/preempt.h b/include/linux/preempt.h index 92e3418..df8e245 100644 --- a/include/linux/preempt.h +++ b/include/linux/preempt.h @@ -6,7 +6,6 @@ * preempt_count (used for kernel preemption, interrupt count, etc.) */ -#include #include #include @@ -16,53 +15,7 @@ */ #define PREEMPT_NEED_RESCHED 0x80000000 -/* - * We mask the PREEMPT_NEED_RESCHED bit so as not to confuse all current users - * that think a non-zero value indicates we cannot preempt. - */ -static __always_inline int preempt_count(void) -{ - return current_thread_info()->preempt_count & ~PREEMPT_NEED_RESCHED; -} - -static __always_inline int *preempt_count_ptr(void) -{ - return ¤t_thread_info()->preempt_count; -} - -/* - * We now loose PREEMPT_NEED_RESCHED and cause an extra reschedule; however the - * alternative is loosing a reschedule. Better schedule too often -- also this - * should be a very rare operation. - */ -static __always_inline void preempt_count_set(int pc) -{ - *preempt_count_ptr() = pc; -} - -/* - * We fold the NEED_RESCHED bit into the preempt count such that - * preempt_enable() can decrement and test for needing to reschedule with a - * single instruction. - * - * We invert the actual bit, so that when the decrement hits 0 we know we both - * need to resched (the bit is cleared) and can resched (no preempt count). - */ - -static __always_inline void set_preempt_need_resched(void) -{ - *preempt_count_ptr() &= ~PREEMPT_NEED_RESCHED; -} - -static __always_inline void clear_preempt_need_resched(void) -{ - *preempt_count_ptr() |= PREEMPT_NEED_RESCHED; -} - -static __always_inline bool test_preempt_need_resched(void) -{ - return !(*preempt_count_ptr() & PREEMPT_NEED_RESCHED); -} +#include #if defined(CONFIG_DEBUG_PREEMPT) || defined(CONFIG_PREEMPT_TRACER) extern void add_preempt_count(int val); -- cgit v0.10.2 From 01028747559ac6c6f642a7bbd2875cc4f66b2feb Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 14 Aug 2013 14:55:46 +0200 Subject: sched: Create more preempt_count accessors We need a few special preempt_count accessors: - task_preempt_count() for when we're interested in the preemption count of another (non-running) task. - init_task_preempt_count() for properly initializing the preemption count. - init_idle_preempt_count() a special case of the above for the idle threads. With these no generic code ever touches thread_info::preempt_count anymore and architectures could choose to remove it. Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-jf5swrio8l78j37d06fzmo4r@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/include/asm-generic/preempt.h b/include/asm-generic/preempt.h index a1fc659..8100b1e 100644 --- a/include/asm-generic/preempt.h +++ b/include/asm-generic/preempt.h @@ -28,6 +28,20 @@ static __always_inline void preempt_count_set(int pc) } /* + * must be macros to avoid header recursion hell + */ +#define task_preempt_count(p) \ + (task_thread_info(p)->preempt_count & ~PREEMPT_NEED_RESCHED) + +#define init_task_preempt_count(p) do { \ + task_thread_info(p)->preempt_count = PREEMPT_DISABLED; \ +} while (0) + +#define init_idle_preempt_count(p, cpu) do { \ + task_thread_info(p)->preempt_count = PREEMPT_ENABLED; \ +} while (0) + +/* * We fold the NEED_RESCHED bit into the preempt count such that * preempt_enable() can decrement and test for needing to reschedule with a * single instruction. diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index 2e7d994..613381b 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -100,7 +100,7 @@ static inline long __trace_sched_switch_state(struct task_struct *p) /* * For all intents and purposes a preempted task is a running task. */ - if (task_thread_info(p)->preempt_count & PREEMPT_ACTIVE) + if (task_preempt_count(p) & PREEMPT_ACTIVE) state = TASK_RUNNING | TASK_STATE_MAX; #endif diff --git a/kernel/sched/core.c b/kernel/sched/core.c index ee61f5a..0ba4e41 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -983,7 +983,7 @@ void set_task_cpu(struct task_struct *p, unsigned int new_cpu) * ttwu() will sort out the placement. */ WARN_ON_ONCE(p->state != TASK_RUNNING && p->state != TASK_WAKING && - !(task_thread_info(p)->preempt_count & PREEMPT_ACTIVE)); + !(task_preempt_count(p) & PREEMPT_ACTIVE)); #ifdef CONFIG_LOCKDEP /* @@ -1723,8 +1723,7 @@ void sched_fork(struct task_struct *p) p->on_cpu = 0; #endif #ifdef CONFIG_PREEMPT_COUNT - /* Want to start with kernel preemption disabled. */ - task_thread_info(p)->preempt_count = PREEMPT_DISABLED; + init_task_preempt_count(p); #endif #ifdef CONFIG_SMP plist_node_init(&p->pushable_tasks, MAX_PRIO); @@ -4217,7 +4216,7 @@ void init_idle(struct task_struct *idle, int cpu) raw_spin_unlock_irqrestore(&rq->lock, flags); /* Set the preempt count _outside_ the spinlocks! */ - task_thread_info(idle)->preempt_count = PREEMPT_ENABLED; + init_idle_preempt_count(idle, cpu); /* * The idle tasks have their own, simple scheduling class: -- cgit v0.10.2 From bdb43806589096ac4272fe1307e789846ac08d7c Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 10 Sep 2013 12:15:23 +0200 Subject: sched: Extract the basic add/sub preempt_count modifiers Rewrite the preempt_count macros in order to extract the 3 basic preempt_count value modifiers: __preempt_count_add() __preempt_count_sub() and the new: __preempt_count_dec_and_test() And since we're at it anyway, replace the unconventional $op_preempt_count names with the more conventional preempt_count_$op. Since these basic operators are equivalent to the previous _notrace() variants, do away with the _notrace() versions. Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-ewbpdbupy9xpsjhg960zwbv8@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c index e205ef5..1215617 100644 --- a/arch/mips/mm/init.c +++ b/arch/mips/mm/init.c @@ -124,7 +124,7 @@ void *kmap_coherent(struct page *page, unsigned long addr) BUG_ON(Page_dcache_dirty(page)); - inc_preempt_count(); + pagefault_disable(); idx = (addr >> PAGE_SHIFT) & (FIX_N_COLOURS - 1); #ifdef CONFIG_MIPS_MT_SMTC idx += FIX_N_COLOURS * smp_processor_id() + @@ -193,8 +193,7 @@ void kunmap_coherent(void) write_c0_entryhi(old_ctx); EXIT_CRITICAL(flags); #endif - dec_preempt_count(); - preempt_check_resched(); + pagefault_enable(); } void copy_user_highpage(struct page *to, struct page *from, diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 8c8093b..729aa77 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -88,7 +88,7 @@ static inline void conditional_sti(struct pt_regs *regs) static inline void preempt_conditional_sti(struct pt_regs *regs) { - inc_preempt_count(); + preempt_count_inc(); if (regs->flags & X86_EFLAGS_IF) local_irq_enable(); } @@ -103,7 +103,7 @@ static inline void preempt_conditional_cli(struct pt_regs *regs) { if (regs->flags & X86_EFLAGS_IF) local_irq_disable(); - dec_preempt_count(); + preempt_count_dec(); } static int __kprobes diff --git a/include/asm-generic/preempt.h b/include/asm-generic/preempt.h index 8100b1e..82d958f 100644 --- a/include/asm-generic/preempt.h +++ b/include/asm-generic/preempt.h @@ -65,4 +65,39 @@ static __always_inline bool test_preempt_need_resched(void) return !(*preempt_count_ptr() & PREEMPT_NEED_RESCHED); } +/* + * The various preempt_count add/sub methods + */ + +static __always_inline void __preempt_count_add(int val) +{ + *preempt_count_ptr() += val; +} + +static __always_inline void __preempt_count_sub(int val) +{ + *preempt_count_ptr() -= val; +} + +static __always_inline bool __preempt_count_dec_and_test(void) +{ + return !--*preempt_count_ptr(); +} + +/* + * Returns true when we need to resched -- even if we can not. + */ +static __always_inline bool need_resched(void) +{ + return unlikely(test_preempt_need_resched()); +} + +/* + * Returns true when we need to resched and can (barring IRQ state). + */ +static __always_inline bool should_resched(void) +{ + return unlikely(!*preempt_count_ptr()); +} + #endif /* __ASM_PREEMPT_H */ diff --git a/include/linux/hardirq.h b/include/linux/hardirq.h index 1e04106..d9cf963 100644 --- a/include/linux/hardirq.h +++ b/include/linux/hardirq.h @@ -33,7 +33,7 @@ extern void rcu_nmi_exit(void); #define __irq_enter() \ do { \ account_irq_enter_time(current); \ - add_preempt_count(HARDIRQ_OFFSET); \ + preempt_count_add(HARDIRQ_OFFSET); \ trace_hardirq_enter(); \ } while (0) @@ -49,7 +49,7 @@ extern void irq_enter(void); do { \ trace_hardirq_exit(); \ account_irq_exit_time(current); \ - sub_preempt_count(HARDIRQ_OFFSET); \ + preempt_count_sub(HARDIRQ_OFFSET); \ } while (0) /* @@ -62,7 +62,7 @@ extern void irq_exit(void); lockdep_off(); \ ftrace_nmi_enter(); \ BUG_ON(in_nmi()); \ - add_preempt_count(NMI_OFFSET + HARDIRQ_OFFSET); \ + preempt_count_add(NMI_OFFSET + HARDIRQ_OFFSET); \ rcu_nmi_enter(); \ trace_hardirq_enter(); \ } while (0) @@ -72,7 +72,7 @@ extern void irq_exit(void); trace_hardirq_exit(); \ rcu_nmi_exit(); \ BUG_ON(!in_nmi()); \ - sub_preempt_count(NMI_OFFSET + HARDIRQ_OFFSET); \ + preempt_count_sub(NMI_OFFSET + HARDIRQ_OFFSET); \ ftrace_nmi_exit(); \ lockdep_on(); \ } while (0) diff --git a/include/linux/preempt.h b/include/linux/preempt.h index df8e245..2343d87 100644 --- a/include/linux/preempt.h +++ b/include/linux/preempt.h @@ -18,97 +18,86 @@ #include #if defined(CONFIG_DEBUG_PREEMPT) || defined(CONFIG_PREEMPT_TRACER) - extern void add_preempt_count(int val); - extern void sub_preempt_count(int val); +extern void preempt_count_add(int val); +extern void preempt_count_sub(int val); +#define preempt_count_dec_and_test() ({ preempt_count_sub(1); should_resched(); }) #else -# define add_preempt_count(val) do { *preempt_count_ptr() += (val); } while (0) -# define sub_preempt_count(val) do { *preempt_count_ptr() -= (val); } while (0) +#define preempt_count_add(val) __preempt_count_add(val) +#define preempt_count_sub(val) __preempt_count_sub(val) +#define preempt_count_dec_and_test() __preempt_count_dec_and_test() #endif -#define inc_preempt_count() add_preempt_count(1) -#define dec_preempt_count() sub_preempt_count(1) - -#ifdef CONFIG_PREEMPT - -asmlinkage void preempt_schedule(void); - -#define preempt_check_resched() \ -do { \ - if (unlikely(!*preempt_count_ptr())) \ - preempt_schedule(); \ -} while (0) - -#ifdef CONFIG_CONTEXT_TRACKING - -void preempt_schedule_context(void); - -#define preempt_check_resched_context() \ -do { \ - if (unlikely(!*preempt_count_ptr())) \ - preempt_schedule_context(); \ -} while (0) -#else - -#define preempt_check_resched_context() preempt_check_resched() - -#endif /* CONFIG_CONTEXT_TRACKING */ - -#else /* !CONFIG_PREEMPT */ - -#define preempt_check_resched() do { } while (0) -#define preempt_check_resched_context() do { } while (0) - -#endif /* CONFIG_PREEMPT */ +#define __preempt_count_inc() __preempt_count_add(1) +#define __preempt_count_dec() __preempt_count_sub(1) +#define preempt_count_inc() preempt_count_add(1) +#define preempt_count_dec() preempt_count_sub(1) #ifdef CONFIG_PREEMPT_COUNT #define preempt_disable() \ do { \ - inc_preempt_count(); \ + preempt_count_inc(); \ barrier(); \ } while (0) #define sched_preempt_enable_no_resched() \ do { \ barrier(); \ - dec_preempt_count(); \ + preempt_count_dec(); \ } while (0) -#define preempt_enable_no_resched() sched_preempt_enable_no_resched() +#define preempt_enable_no_resched() sched_preempt_enable_no_resched() +#ifdef CONFIG_PREEMPT +asmlinkage void preempt_schedule(void); #define preempt_enable() \ do { \ - preempt_enable_no_resched(); \ - preempt_check_resched(); \ + barrier(); \ + if (unlikely(preempt_count_dec_and_test())) \ + preempt_schedule(); \ } while (0) -/* For debugging and tracer internals only! */ -#define add_preempt_count_notrace(val) \ - do { *preempt_count_ptr() += (val); } while (0) -#define sub_preempt_count_notrace(val) \ - do { *preempt_count_ptr() -= (val); } while (0) -#define inc_preempt_count_notrace() add_preempt_count_notrace(1) -#define dec_preempt_count_notrace() sub_preempt_count_notrace(1) +#define preempt_check_resched() \ +do { \ + if (should_resched()) \ + preempt_schedule(); \ +} while (0) + +#else +#define preempt_enable() preempt_enable_no_resched() +#define preempt_check_resched() do { } while (0) +#endif #define preempt_disable_notrace() \ do { \ - inc_preempt_count_notrace(); \ + __preempt_count_inc(); \ barrier(); \ } while (0) #define preempt_enable_no_resched_notrace() \ do { \ barrier(); \ - dec_preempt_count_notrace(); \ + __preempt_count_dec(); \ } while (0) -/* preempt_check_resched is OK to trace */ +#ifdef CONFIG_PREEMPT + +#ifdef CONFIG_CONTEXT_TRACKING +asmlinkage void preempt_schedule_context(void); +#else +#define preempt_schedule_context() preempt_schedule() +#endif + #define preempt_enable_notrace() \ do { \ - preempt_enable_no_resched_notrace(); \ - preempt_check_resched_context(); \ + barrier(); \ + if (unlikely(__preempt_count_dec_and_test())) \ + preempt_schedule_context(); \ } while (0) +#else +#define preempt_enable_notrace() preempt_enable_no_resched_notrace() +#endif #else /* !CONFIG_PREEMPT_COUNT */ @@ -118,10 +107,11 @@ do { \ * that can cause faults and scheduling migrate into our preempt-protected * region. */ -#define preempt_disable() barrier() +#define preempt_disable() barrier() #define sched_preempt_enable_no_resched() barrier() -#define preempt_enable_no_resched() barrier() -#define preempt_enable() barrier() +#define preempt_enable_no_resched() barrier() +#define preempt_enable() barrier() +#define preempt_check_resched() do { } while (0) #define preempt_disable_notrace() barrier() #define preempt_enable_no_resched_notrace() barrier() diff --git a/include/linux/sched.h b/include/linux/sched.h index 9fa151f..06ac17c 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2409,11 +2409,6 @@ static inline int signal_pending_state(long state, struct task_struct *p) return (state & TASK_INTERRUPTIBLE) || __fatal_signal_pending(p); } -static inline int need_resched(void) -{ - return unlikely(test_preempt_need_resched()); -} - /* * cond_resched() and cond_resched_lock(): latency reduction via * explicit rescheduling in places that are safe. The return diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h index 5ca0951..9d8cf05 100644 --- a/include/linux/uaccess.h +++ b/include/linux/uaccess.h @@ -15,7 +15,7 @@ */ static inline void pagefault_disable(void) { - inc_preempt_count(); + preempt_count_inc(); /* * make sure to have issued the store before a pagefault * can hit. @@ -30,11 +30,7 @@ static inline void pagefault_enable(void) * the pagefault handler again. */ barrier(); - dec_preempt_count(); - /* - * make sure we do.. - */ - barrier(); + preempt_count_dec(); preempt_check_resched(); } diff --git a/kernel/context_tracking.c b/kernel/context_tracking.c index 247091b..013161f 100644 --- a/kernel/context_tracking.c +++ b/kernel/context_tracking.c @@ -111,7 +111,7 @@ void context_tracking_user_enter(void) * instead of preempt_schedule() to exit user context if needed before * calling the scheduler. */ -void __sched notrace preempt_schedule_context(void) +asmlinkage void __sched notrace preempt_schedule_context(void) { enum ctx_state prev_ctx; diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 0ba4e41..9c84a9a 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2219,7 +2219,7 @@ notrace unsigned long get_parent_ip(unsigned long addr) #if defined(CONFIG_PREEMPT) && (defined(CONFIG_DEBUG_PREEMPT) || \ defined(CONFIG_PREEMPT_TRACER)) -void __kprobes add_preempt_count(int val) +void __kprobes preempt_count_add(int val) { #ifdef CONFIG_DEBUG_PREEMPT /* @@ -2228,7 +2228,7 @@ void __kprobes add_preempt_count(int val) if (DEBUG_LOCKS_WARN_ON((preempt_count() < 0))) return; #endif - add_preempt_count_notrace(val); + __preempt_count_add(val); #ifdef CONFIG_DEBUG_PREEMPT /* * Spinlock count overflowing soon? @@ -2239,9 +2239,9 @@ void __kprobes add_preempt_count(int val) if (preempt_count() == val) trace_preempt_off(CALLER_ADDR0, get_parent_ip(CALLER_ADDR1)); } -EXPORT_SYMBOL(add_preempt_count); +EXPORT_SYMBOL(preempt_count_add); -void __kprobes sub_preempt_count(int val) +void __kprobes preempt_count_sub(int val) { #ifdef CONFIG_DEBUG_PREEMPT /* @@ -2259,9 +2259,9 @@ void __kprobes sub_preempt_count(int val) if (preempt_count() == val) trace_preempt_on(CALLER_ADDR0, get_parent_ip(CALLER_ADDR1)); - sub_preempt_count_notrace(val); + __preempt_count_sub(val); } -EXPORT_SYMBOL(sub_preempt_count); +EXPORT_SYMBOL(preempt_count_sub); #endif @@ -2525,9 +2525,9 @@ asmlinkage void __sched notrace preempt_schedule(void) return; do { - add_preempt_count_notrace(PREEMPT_ACTIVE); + __preempt_count_add(PREEMPT_ACTIVE); __schedule(); - sub_preempt_count_notrace(PREEMPT_ACTIVE); + __preempt_count_sub(PREEMPT_ACTIVE); /* * Check again in case we missed a preemption opportunity @@ -2554,11 +2554,11 @@ asmlinkage void __sched preempt_schedule_irq(void) prev_state = exception_enter(); do { - add_preempt_count(PREEMPT_ACTIVE); + __preempt_count_add(PREEMPT_ACTIVE); local_irq_enable(); __schedule(); local_irq_disable(); - sub_preempt_count(PREEMPT_ACTIVE); + __preempt_count_sub(PREEMPT_ACTIVE); /* * Check again in case we missed a preemption opportunity @@ -3798,16 +3798,11 @@ SYSCALL_DEFINE0(sched_yield) return 0; } -static inline int should_resched(void) -{ - return need_resched() && !(preempt_count() & PREEMPT_ACTIVE); -} - static void __cond_resched(void) { - add_preempt_count(PREEMPT_ACTIVE); + __preempt_count_add(PREEMPT_ACTIVE); __schedule(); - sub_preempt_count(PREEMPT_ACTIVE); + __preempt_count_sub(PREEMPT_ACTIVE); } int __sched _cond_resched(void) diff --git a/kernel/softirq.c b/kernel/softirq.c index a90de70..3e88612 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -100,13 +100,13 @@ static void __local_bh_disable(unsigned long ip, unsigned int cnt) raw_local_irq_save(flags); /* - * The preempt tracer hooks into add_preempt_count and will break + * The preempt tracer hooks into preempt_count_add and will break * lockdep because it calls back into lockdep after SOFTIRQ_OFFSET * is set and before current->softirq_enabled is cleared. * We must manually increment preempt_count here and manually * call the trace_preempt_off later. */ - add_preempt_count_notrace(cnt); + __preempt_count_add(cnt); /* * Were softirqs turned off above: */ @@ -120,7 +120,7 @@ static void __local_bh_disable(unsigned long ip, unsigned int cnt) #else /* !CONFIG_TRACE_IRQFLAGS */ static inline void __local_bh_disable(unsigned long ip, unsigned int cnt) { - add_preempt_count(cnt); + preempt_count_add(cnt); barrier(); } #endif /* CONFIG_TRACE_IRQFLAGS */ @@ -139,7 +139,7 @@ static void __local_bh_enable(unsigned int cnt) if (softirq_count() == cnt) trace_softirqs_on(_RET_IP_); - sub_preempt_count(cnt); + preempt_count_sub(cnt); } /* @@ -169,12 +169,12 @@ static inline void _local_bh_enable_ip(unsigned long ip) * Keep preemption disabled until we are done with * softirq processing: */ - sub_preempt_count(SOFTIRQ_DISABLE_OFFSET - 1); + preempt_count_sub(SOFTIRQ_DISABLE_OFFSET - 1); if (unlikely(!in_interrupt() && local_softirq_pending())) do_softirq(); - dec_preempt_count(); + preempt_count_dec(); #ifdef CONFIG_TRACE_IRQFLAGS local_irq_enable(); #endif @@ -360,7 +360,7 @@ void irq_exit(void) account_irq_exit_time(current); trace_hardirq_exit(); - sub_preempt_count(HARDIRQ_OFFSET); + preempt_count_sub(HARDIRQ_OFFSET); if (!in_interrupt() && local_softirq_pending()) invoke_softirq(); -- cgit v0.10.2 From a233f1120c37724938f7201fe2353b2577adaaf9 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 23 Sep 2013 19:04:26 +0200 Subject: sched: Prepare for per-cpu preempt_count When using per-cpu preempt_count variables we need to save/restore the preempt_count on context switch (into per task storage; for instance the old thread_info::preempt_count variable) because of PREEMPT_ACTIVE. However, this means that on fork() the preempt_count value of the last context switch gets copied and if we had a PREEMPT_ACTIVE switch right before cloning a child task the child task will now too have PREEMPT_ACTIVE set and start its life with an extra PREEMPT_ACTIVE count. Therefore we need to make init_task_preempt_count() unconditional; this resets whatever preempt_count we inherited from our parent process. Doing so for !per-cpu implementations is harmless. For !PREEMPT_COUNT kernels we need to be careful not to start life with an increased preempt_count. Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-4k0b7oy1rcdyzochwiixuwi9@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/include/linux/sched.h b/include/linux/sched.h index 06ac17c..b09798b 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -428,6 +428,14 @@ struct task_cputime { .sum_exec_runtime = 0, \ } +#define PREEMPT_ENABLED (PREEMPT_NEED_RESCHED) + +#ifdef CONFIG_PREEMPT_COUNT +#define PREEMPT_DISABLED (1 + PREEMPT_ENABLED) +#else +#define PREEMPT_DISABLED PREEMPT_ENABLED +#endif + /* * Disable preemption until the scheduler is running. * Reset by start_kernel()->sched_init()->init_idle(). @@ -435,9 +443,7 @@ struct task_cputime { * We include PREEMPT_ACTIVE to avoid cond_resched() from working * before the scheduler is active -- see should_resched(). */ -#define INIT_PREEMPT_COUNT (1 + PREEMPT_ACTIVE + PREEMPT_NEED_RESCHED) -#define PREEMPT_ENABLED (PREEMPT_NEED_RESCHED) -#define PREEMPT_DISABLED (1 + PREEMPT_NEED_RESCHED) +#define INIT_PREEMPT_COUNT (PREEMPT_DISABLED + PREEMPT_ACTIVE) /** * struct thread_group_cputimer - thread group interval timer counts diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 9c84a9a..f575d5b 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -1722,9 +1722,7 @@ void sched_fork(struct task_struct *p) #if defined(CONFIG_SMP) p->on_cpu = 0; #endif -#ifdef CONFIG_PREEMPT_COUNT init_task_preempt_count(p); -#endif #ifdef CONFIG_SMP plist_node_init(&p->pushable_tasks, MAX_PRIO); #endif -- cgit v0.10.2 From c2daa3bed53a81171cf8c1a36db798e82b91afe8 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 14 Aug 2013 14:51:00 +0200 Subject: sched, x86: Provide a per-cpu preempt_count implementation Convert x86 to use a per-cpu preemption count. The reason for doing so is that accessing per-cpu variables is a lot cheaper than accessing thread_info variables. We still need to save/restore the actual preemption count due to PREEMPT_ACTIVE so we place the per-cpu __preempt_count variable in the same cache-line as the other hot __switch_to() variables such as current_task. NOTE: this save/restore is required even for !PREEMPT kernels as cond_resched() also relies on preempt_count's PREEMPT_ACTIVE to ignore task_struct::state. Also rename thread_info::preempt_count to ensure nobody is 'accidentally' still poking at it. Suggested-by: Linus Torvalds Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-gzn5rfsf8trgjoqx8hyayy3q@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/Kbuild b/arch/x86/include/asm/Kbuild index eca2028..7f66985 100644 --- a/arch/x86/include/asm/Kbuild +++ b/arch/x86/include/asm/Kbuild @@ -5,4 +5,3 @@ genhdr-y += unistd_64.h genhdr-y += unistd_x32.h generic-y += clkdev.h -generic-y += preempt.h diff --git a/arch/x86/include/asm/preempt.h b/arch/x86/include/asm/preempt.h new file mode 100644 index 0000000..1309942 --- /dev/null +++ b/arch/x86/include/asm/preempt.h @@ -0,0 +1,98 @@ +#ifndef __ASM_PREEMPT_H +#define __ASM_PREEMPT_H + +#include +#include +#include + +DECLARE_PER_CPU(int, __preempt_count); + +/* + * We mask the PREEMPT_NEED_RESCHED bit so as not to confuse all current users + * that think a non-zero value indicates we cannot preempt. + */ +static __always_inline int preempt_count(void) +{ + return __this_cpu_read_4(__preempt_count) & ~PREEMPT_NEED_RESCHED; +} + +static __always_inline void preempt_count_set(int pc) +{ + __this_cpu_write_4(__preempt_count, pc); +} + +/* + * must be macros to avoid header recursion hell + */ +#define task_preempt_count(p) \ + (task_thread_info(p)->saved_preempt_count & ~PREEMPT_NEED_RESCHED) + +#define init_task_preempt_count(p) do { \ + task_thread_info(p)->saved_preempt_count = PREEMPT_DISABLED; \ +} while (0) + +#define init_idle_preempt_count(p, cpu) do { \ + task_thread_info(p)->saved_preempt_count = PREEMPT_ENABLED; \ + per_cpu(__preempt_count, (cpu)) = PREEMPT_ENABLED; \ +} while (0) + +/* + * We fold the NEED_RESCHED bit into the preempt count such that + * preempt_enable() can decrement and test for needing to reschedule with a + * single instruction. + * + * We invert the actual bit, so that when the decrement hits 0 we know we both + * need to resched (the bit is cleared) and can resched (no preempt count). + */ + +static __always_inline void set_preempt_need_resched(void) +{ + __this_cpu_and_4(__preempt_count, ~PREEMPT_NEED_RESCHED); +} + +static __always_inline void clear_preempt_need_resched(void) +{ + __this_cpu_or_4(__preempt_count, PREEMPT_NEED_RESCHED); +} + +static __always_inline bool test_preempt_need_resched(void) +{ + return !(__this_cpu_read_4(__preempt_count) & PREEMPT_NEED_RESCHED); +} + +/* + * The various preempt_count add/sub methods + */ + +static __always_inline void __preempt_count_add(int val) +{ + __this_cpu_add_4(__preempt_count, val); +} + +static __always_inline void __preempt_count_sub(int val) +{ + __this_cpu_add_4(__preempt_count, -val); +} + +static __always_inline bool __preempt_count_dec_and_test(void) +{ + GEN_UNARY_RMWcc("decl", __preempt_count, __percpu_arg(0), "e"); +} + +/* + * Returns true when we need to resched -- even if we can not. + */ +static __always_inline bool need_resched(void) +{ + return unlikely(test_preempt_need_resched()); +} + +/* + * Returns true when we need to resched and can (barring IRQ state). + */ +static __always_inline bool should_resched(void) +{ + return unlikely(!__this_cpu_read_4(__preempt_count)); +} + +#endif /* __ASM_PREEMPT_H */ diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h index 2781119..c46a46b 100644 --- a/arch/x86/include/asm/thread_info.h +++ b/arch/x86/include/asm/thread_info.h @@ -28,8 +28,7 @@ struct thread_info { __u32 flags; /* low level flags */ __u32 status; /* thread synchronous flags */ __u32 cpu; /* current CPU */ - int preempt_count; /* 0 => preemptable, - <0 => BUG */ + int saved_preempt_count; mm_segment_t addr_limit; struct restart_block restart_block; void __user *sysenter_return; @@ -49,7 +48,7 @@ struct thread_info { .exec_domain = &default_exec_domain, \ .flags = 0, \ .cpu = 0, \ - .preempt_count = INIT_PREEMPT_COUNT, \ + .saved_preempt_count = INIT_PREEMPT_COUNT, \ .addr_limit = KERNEL_DS, \ .restart_block = { \ .fn = do_no_restart_syscall, \ diff --git a/arch/x86/kernel/asm-offsets.c b/arch/x86/kernel/asm-offsets.c index 2861082..9f6b934 100644 --- a/arch/x86/kernel/asm-offsets.c +++ b/arch/x86/kernel/asm-offsets.c @@ -32,7 +32,6 @@ void common(void) { OFFSET(TI_flags, thread_info, flags); OFFSET(TI_status, thread_info, status); OFFSET(TI_addr_limit, thread_info, addr_limit); - OFFSET(TI_preempt_count, thread_info, preempt_count); BLANK(); OFFSET(crypto_tfm_ctx_offset, crypto_tfm, __crt_ctx); diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 2793d1f..5223fe6 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -1095,6 +1095,9 @@ DEFINE_PER_CPU(char *, irq_stack_ptr) = DEFINE_PER_CPU(unsigned int, irq_count) __visible = -1; +DEFINE_PER_CPU(int, __preempt_count) = INIT_PREEMPT_COUNT; +EXPORT_PER_CPU_SYMBOL(__preempt_count); + DEFINE_PER_CPU(struct task_struct *, fpu_owner_task); /* @@ -1169,6 +1172,8 @@ void debug_stack_reset(void) DEFINE_PER_CPU(struct task_struct *, current_task) = &init_task; EXPORT_PER_CPU_SYMBOL(current_task); +DEFINE_PER_CPU(int, __preempt_count) = INIT_PREEMPT_COUNT; +EXPORT_PER_CPU_SYMBOL(__preempt_count); DEFINE_PER_CPU(struct task_struct *, fpu_owner_task); #ifdef CONFIG_CC_STACKPROTECTOR diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S index f0dcb0c..fd1bc1b 100644 --- a/arch/x86/kernel/entry_32.S +++ b/arch/x86/kernel/entry_32.S @@ -362,12 +362,9 @@ END(ret_from_exception) #ifdef CONFIG_PREEMPT ENTRY(resume_kernel) DISABLE_INTERRUPTS(CLBR_ANY) - cmpl $0,TI_preempt_count(%ebp) # non-zero preempt_count ? - jnz restore_all need_resched: - movl TI_flags(%ebp), %ecx # need_resched set ? - testb $_TIF_NEED_RESCHED, %cl - jz restore_all + cmpl $0,PER_CPU_VAR(__preempt_count) + jnz restore_all testl $X86_EFLAGS_IF,PT_EFLAGS(%esp) # interrupts off (exception path) ? jz restore_all call preempt_schedule_irq diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index 1b69951..6a43e7d 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S @@ -1118,10 +1118,8 @@ retint_signal: /* Returning to kernel space. Check if we need preemption */ /* rcx: threadinfo. interrupts off. */ ENTRY(retint_kernel) - cmpl $0,TI_preempt_count(%rcx) + cmpl $0,PER_CPU_VAR(__preempt_count) jnz retint_restore_args - bt $TIF_NEED_RESCHED,TI_flags(%rcx) - jnc retint_restore_args bt $9,EFLAGS-ARGOFFSET(%rsp) /* interrupts off? */ jnc retint_restore_args call preempt_schedule_irq diff --git a/arch/x86/kernel/irq_32.c b/arch/x86/kernel/irq_32.c index 4186755..3fe0663 100644 --- a/arch/x86/kernel/irq_32.c +++ b/arch/x86/kernel/irq_32.c @@ -100,9 +100,6 @@ execute_on_irq_stack(int overflow, struct irq_desc *desc, int irq) irqctx->tinfo.task = curctx->tinfo.task; irqctx->tinfo.previous_esp = current_stack_pointer; - /* Copy the preempt_count so that the [soft]irq checks work. */ - irqctx->tinfo.preempt_count = curctx->tinfo.preempt_count; - if (unlikely(overflow)) call_on_stack(print_stack_overflow, isp); @@ -131,7 +128,6 @@ void irq_ctx_init(int cpu) THREAD_SIZE_ORDER)); memset(&irqctx->tinfo, 0, sizeof(struct thread_info)); irqctx->tinfo.cpu = cpu; - irqctx->tinfo.preempt_count = HARDIRQ_OFFSET; irqctx->tinfo.addr_limit = MAKE_MM_SEG(0); per_cpu(hardirq_ctx, cpu) = irqctx; diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c index 884f98f..c2ec1aa 100644 --- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c @@ -292,6 +292,14 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p) set_iopl_mask(next->iopl); /* + * If it were not for PREEMPT_ACTIVE we could guarantee that the + * preempt_count of all tasks was equal here and this would not be + * needed. + */ + task_thread_info(prev_p)->saved_preempt_count = this_cpu_read(__preempt_count); + this_cpu_write(__preempt_count, task_thread_info(next_p)->saved_preempt_count); + + /* * Now maybe handle debug registers and/or IO bitmaps */ if (unlikely(task_thread_info(prev_p)->flags & _TIF_WORK_CTXSW_PREV || diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index bb1dc51..45ab4d6 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -363,6 +363,14 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p) this_cpu_write(old_rsp, next->usersp); this_cpu_write(current_task, next_p); + /* + * If it were not for PREEMPT_ACTIVE we could guarantee that the + * preempt_count of all tasks was equal here and this would not be + * needed. + */ + task_thread_info(prev_p)->saved_preempt_count = this_cpu_read(__preempt_count); + this_cpu_write(__preempt_count, task_thread_info(next_p)->saved_preempt_count); + this_cpu_write(kernel_stack, (unsigned long)task_stack_page(next_p) + THREAD_SIZE - KERNEL_STACK_OFFSET); -- cgit v0.10.2 From 1a338ac32ca630f67df25b4a16436cccc314e997 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 14 Aug 2013 14:51:00 +0200 Subject: sched, x86: Optimize the preempt_schedule() call Remove the bloat of the C calling convention out of the preempt_enable() sites by creating an ASM wrapper which allows us to do an asm("call ___preempt_schedule") instead. calling.h bits by Andi Kleen Suggested-by: Linus Torvalds Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-tk7xdi1cvvxewixzke8t8le1@git.kernel.org [ Fixed build error. ] Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/calling.h b/arch/x86/include/asm/calling.h index 0fa6750..cb4c73b 100644 --- a/arch/x86/include/asm/calling.h +++ b/arch/x86/include/asm/calling.h @@ -48,6 +48,8 @@ For 32-bit we have the following conventions - kernel is built with #include +#ifdef CONFIG_X86_64 + /* * 64-bit system call stack frame layout defines and helpers, * for assembly code: @@ -192,3 +194,51 @@ For 32-bit we have the following conventions - kernel is built with .macro icebp .byte 0xf1 .endm + +#else /* CONFIG_X86_64 */ + +/* + * For 32bit only simplified versions of SAVE_ALL/RESTORE_ALL. These + * are different from the entry_32.S versions in not changing the segment + * registers. So only suitable for in kernel use, not when transitioning + * from or to user space. The resulting stack frame is not a standard + * pt_regs frame. The main use case is calling C code from assembler + * when all the registers need to be preserved. + */ + + .macro SAVE_ALL + pushl_cfi %eax + CFI_REL_OFFSET eax, 0 + pushl_cfi %ebp + CFI_REL_OFFSET ebp, 0 + pushl_cfi %edi + CFI_REL_OFFSET edi, 0 + pushl_cfi %esi + CFI_REL_OFFSET esi, 0 + pushl_cfi %edx + CFI_REL_OFFSET edx, 0 + pushl_cfi %ecx + CFI_REL_OFFSET ecx, 0 + pushl_cfi %ebx + CFI_REL_OFFSET ebx, 0 + .endm + + .macro RESTORE_ALL + popl_cfi %ebx + CFI_RESTORE ebx + popl_cfi %ecx + CFI_RESTORE ecx + popl_cfi %edx + CFI_RESTORE edx + popl_cfi %esi + CFI_RESTORE esi + popl_cfi %edi + CFI_RESTORE edi + popl_cfi %ebp + CFI_RESTORE ebp + popl_cfi %eax + CFI_RESTORE eax + .endm + +#endif /* CONFIG_X86_64 */ + diff --git a/arch/x86/include/asm/preempt.h b/arch/x86/include/asm/preempt.h index 1309942..1de41690 100644 --- a/arch/x86/include/asm/preempt.h +++ b/arch/x86/include/asm/preempt.h @@ -95,4 +95,14 @@ static __always_inline bool should_resched(void) return unlikely(!__this_cpu_read_4(__preempt_count)); } +#ifdef CONFIG_PREEMPT + extern asmlinkage void ___preempt_schedule(void); +# define __preempt_schedule() asm ("call ___preempt_schedule") + extern asmlinkage void preempt_schedule(void); +# ifdef CONFIG_CONTEXT_TRACKING + extern asmlinkage void ___preempt_schedule_context(void); +# define __preempt_schedule_context() asm ("call ___preempt_schedule_context") +# endif +#endif + #endif /* __ASM_PREEMPT_H */ diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index a5408b9..9b0a34e 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -36,6 +36,8 @@ obj-y += tsc.o io_delay.o rtc.o obj-y += pci-iommu_table.o obj-y += resource.o +obj-$(CONFIG_PREEMPT) += preempt.o + obj-y += process.o obj-y += i387.o xsave.o obj-y += ptrace.o diff --git a/arch/x86/kernel/i386_ksyms_32.c b/arch/x86/kernel/i386_ksyms_32.c index 0fa6912..05fd74f 100644 --- a/arch/x86/kernel/i386_ksyms_32.c +++ b/arch/x86/kernel/i386_ksyms_32.c @@ -37,3 +37,10 @@ EXPORT_SYMBOL(strstr); EXPORT_SYMBOL(csum_partial); EXPORT_SYMBOL(empty_zero_page); + +#ifdef CONFIG_PREEMPT +EXPORT_SYMBOL(___preempt_schedule); +#ifdef CONFIG_CONTEXT_TRACKING +EXPORT_SYMBOL(___preempt_schedule_context); +#endif +#endif diff --git a/arch/x86/kernel/preempt.S b/arch/x86/kernel/preempt.S new file mode 100644 index 0000000..ca7f0d5 --- /dev/null +++ b/arch/x86/kernel/preempt.S @@ -0,0 +1,25 @@ + +#include +#include +#include +#include + +ENTRY(___preempt_schedule) + CFI_STARTPROC + SAVE_ALL + call preempt_schedule + RESTORE_ALL + ret + CFI_ENDPROC + +#ifdef CONFIG_CONTEXT_TRACKING + +ENTRY(___preempt_schedule_context) + CFI_STARTPROC + SAVE_ALL + call preempt_schedule_context + RESTORE_ALL + ret + CFI_ENDPROC + +#endif diff --git a/arch/x86/kernel/x8664_ksyms_64.c b/arch/x86/kernel/x8664_ksyms_64.c index b014d94..0406819 100644 --- a/arch/x86/kernel/x8664_ksyms_64.c +++ b/arch/x86/kernel/x8664_ksyms_64.c @@ -66,3 +66,10 @@ EXPORT_SYMBOL(empty_zero_page); #ifndef CONFIG_PARAVIRT EXPORT_SYMBOL(native_load_gs_index); #endif + +#ifdef CONFIG_PREEMPT +EXPORT_SYMBOL(___preempt_schedule); +#ifdef CONFIG_CONTEXT_TRACKING +EXPORT_SYMBOL(___preempt_schedule_context); +#endif +#endif diff --git a/include/asm-generic/preempt.h b/include/asm-generic/preempt.h index 82d958f..5dc14ed 100644 --- a/include/asm-generic/preempt.h +++ b/include/asm-generic/preempt.h @@ -100,4 +100,14 @@ static __always_inline bool should_resched(void) return unlikely(!*preempt_count_ptr()); } +#ifdef CONFIG_PREEMPT +extern asmlinkage void preempt_schedule(void); +#define __preempt_schedule() preempt_schedule() + +#ifdef CONFIG_CONTEXT_TRACKING +extern asmlinkage void preempt_schedule_context(void); +#define __preempt_schedule_context() preempt_schedule_context() +#endif +#endif /* CONFIG_PREEMPT */ + #endif /* __ASM_PREEMPT_H */ diff --git a/include/linux/preempt.h b/include/linux/preempt.h index 2343d87..a3d9dc8 100644 --- a/include/linux/preempt.h +++ b/include/linux/preempt.h @@ -50,18 +50,17 @@ do { \ #define preempt_enable_no_resched() sched_preempt_enable_no_resched() #ifdef CONFIG_PREEMPT -asmlinkage void preempt_schedule(void); #define preempt_enable() \ do { \ barrier(); \ if (unlikely(preempt_count_dec_and_test())) \ - preempt_schedule(); \ + __preempt_schedule(); \ } while (0) #define preempt_check_resched() \ do { \ if (should_resched()) \ - preempt_schedule(); \ + __preempt_schedule(); \ } while (0) #else @@ -83,17 +82,15 @@ do { \ #ifdef CONFIG_PREEMPT -#ifdef CONFIG_CONTEXT_TRACKING -asmlinkage void preempt_schedule_context(void); -#else -#define preempt_schedule_context() preempt_schedule() +#ifndef CONFIG_CONTEXT_TRACKING +#define __preempt_schedule_context() __preempt_schedule() #endif #define preempt_enable_notrace() \ do { \ barrier(); \ if (unlikely(__preempt_count_dec_and_test())) \ - preempt_schedule_context(); \ + __preempt_schedule_context(); \ } while (0) #else #define preempt_enable_notrace() preempt_enable_no_resched_notrace() -- cgit v0.10.2 From 806955dd9cf071ecd99acbaa8c73ae1f34dcf83d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 25 Sep 2013 13:10:33 +0100 Subject: ASoC: tlv320aic26: Convert to table based control init Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c index 7b8f3d9..32c6b07 100644 --- a/sound/soc/codecs/tlv320aic26.c +++ b/sound/soc/codecs/tlv320aic26.c @@ -377,7 +377,7 @@ static DEVICE_ATTR(keyclick, 0644, aic26_keyclick_show, aic26_keyclick_set); static int aic26_probe(struct snd_soc_codec *codec) { struct aic26 *aic26 = dev_get_drvdata(codec->dev); - int ret, err, i, reg; + int ret, i, reg; aic26->codec = codec; @@ -403,12 +403,6 @@ static int aic26_probe(struct snd_soc_codec *codec) if (ret) dev_info(codec->dev, "error creating sysfs files\n"); - /* register controls */ - dev_dbg(codec->dev, "Registering controls\n"); - err = snd_soc_add_codec_controls(codec, aic26_snd_controls, - ARRAY_SIZE(aic26_snd_controls)); - WARN_ON(err < 0); - return 0; } @@ -418,6 +412,8 @@ static struct snd_soc_codec_driver aic26_soc_codec_dev = { .write = aic26_reg_write, .reg_cache_size = AIC26_NUM_REGS, .reg_word_size = sizeof(u16), + .controls = aic26_snd_controls, + .num_controls = ARRAY_SIZE(aic26_snd_controls), .dapm_widgets = tlv320aic26_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(tlv320aic26_dapm_widgets), .dapm_routes = tlv320aic26_dapm_routes, -- cgit v0.10.2 From 5b0959d472c215e6d712ac47e64110bd125ddd07 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 25 Sep 2013 13:14:41 +0100 Subject: ASoC: tlv320aic26: Use snd_soc_update_bits() Use snd_soc_update_bits() rather than open coding. Since the register cache is currently only used where update_bits() is used this means the current register cache can be removed entirely. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c index 32c6b07..4d82447 100644 --- a/sound/soc/codecs/tlv320aic26.c +++ b/sound/soc/codecs/tlv320aic26.c @@ -74,19 +74,6 @@ static unsigned int aic26_reg_read(struct snd_soc_codec *codec, return value; } -static unsigned int aic26_reg_read_cache(struct snd_soc_codec *codec, - unsigned int reg) -{ - u16 *cache = codec->reg_cache; - - if (reg >= AIC26_NUM_REGS) { - WARN_ON_ONCE(1); - return 0; - } - - return cache[reg]; -} - static int aic26_reg_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { @@ -195,19 +182,15 @@ static int aic26_hw_params(struct snd_pcm_substream *substream, snd_soc_write(codec, AIC26_REG_PLL_PROG2, reg); /* Audio Control 3 (master mode, fsref rate) */ - reg = aic26_reg_read_cache(codec, AIC26_REG_AUDIO_CTRL3); - reg &= ~0xf800; if (aic26->master) - reg |= 0x0800; + reg = 0x0800; if (fsref == 48000) - reg |= 0x2000; - snd_soc_write(codec, AIC26_REG_AUDIO_CTRL3, reg); + reg = 0x2000; + snd_soc_update_bits(codec, AIC26_REG_AUDIO_CTRL3, 0xf800, reg); /* Audio Control 1 (FSref divisor) */ - reg = aic26_reg_read_cache(codec, AIC26_REG_AUDIO_CTRL1); - reg &= ~0x0fff; - reg |= wlen | aic26->datfm | (divisor << 3) | divisor; - snd_soc_write(codec, AIC26_REG_AUDIO_CTRL1, reg); + reg = wlen | aic26->datfm | (divisor << 3) | divisor; + snd_soc_update_bits(codec, AIC26_REG_AUDIO_CTRL1, 0xfff, reg); return 0; } @@ -219,16 +202,16 @@ static int aic26_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; struct aic26 *aic26 = snd_soc_codec_get_drvdata(codec); - u16 reg = aic26_reg_read_cache(codec, AIC26_REG_DAC_GAIN); + u16 reg; dev_dbg(&aic26->spi->dev, "aic26_mute(dai=%p, mute=%i)\n", dai, mute); if (mute) - reg |= 0x8080; + reg = 0x8080; else - reg &= ~0x8080; - snd_soc_write(codec, AIC26_REG_DAC_GAIN, reg); + reg = 0; + snd_soc_update_bits(codec, AIC26_REG_DAC_GAIN, 0x8000, reg); return 0; } @@ -346,7 +329,7 @@ static ssize_t aic26_keyclick_show(struct device *dev, struct aic26 *aic26 = dev_get_drvdata(dev); int val, amp, freq, len; - val = aic26_reg_read_cache(aic26->codec, AIC26_REG_AUDIO_CTRL2); + val = snd_soc_read(aic26->codec, AIC26_REG_AUDIO_CTRL2); amp = (val >> 12) & 0x7; freq = (125 << ((val >> 8) & 0x7)) >> 1; len = 2 * (1 + ((val >> 4) & 0xf)); @@ -360,11 +343,9 @@ static ssize_t aic26_keyclick_set(struct device *dev, const char *buf, size_t count) { struct aic26 *aic26 = dev_get_drvdata(dev); - int val; - val = aic26_reg_read_cache(aic26->codec, AIC26_REG_AUDIO_CTRL2); - val |= 0x8000; - snd_soc_write(aic26->codec, AIC26_REG_AUDIO_CTRL2, val); + snd_soc_update_bits(aic26->codec, AIC26_REG_AUDIO_CTRL2, + 0x8000, 0x800); return count; } @@ -377,7 +358,7 @@ static DEVICE_ATTR(keyclick, 0644, aic26_keyclick_show, aic26_keyclick_set); static int aic26_probe(struct snd_soc_codec *codec) { struct aic26 *aic26 = dev_get_drvdata(codec->dev); - int ret, i, reg; + int ret, reg; aic26->codec = codec; @@ -393,10 +374,6 @@ static int aic26_probe(struct snd_soc_codec *codec) reg |= 0x0800; /* set master mode */ snd_soc_write(codec, AIC26_REG_AUDIO_CTRL3, reg); - /* Fill register cache */ - for (i = 0; i < codec->driver->reg_cache_size; i++) - snd_soc_read(codec, i); - /* Register the sysfs files for debugging */ /* Create SysFS files */ ret = device_create_file(codec->dev, &dev_attr_keyclick); @@ -410,8 +387,6 @@ static struct snd_soc_codec_driver aic26_soc_codec_dev = { .probe = aic26_probe, .read = aic26_reg_read, .write = aic26_reg_write, - .reg_cache_size = AIC26_NUM_REGS, - .reg_word_size = sizeof(u16), .controls = aic26_snd_controls, .num_controls = ARRAY_SIZE(aic26_snd_controls), .dapm_widgets = tlv320aic26_dapm_widgets, -- cgit v0.10.2 From 7fbdeb809050cb958f3baa83dcc643f9a2f287f2 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 25 Sep 2013 13:29:44 +0100 Subject: ASoC: tlv320aic26: Convert to direct regmap API usage This moves us towards being able to remove the duplicated register I/O code in ASoC. The datasheet and the driver document the device as having a register map divided into pages but since the paging is actually done by sending the page address and the register address with each transaction this is no different to having a simple register address. The datasheet does also document the low five bits of the 16 bit "command" as unused which we could represent as padding but it seems simpler and less confusing to things that use block transfers or autoincrement to represent these as part of the register address. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c index 4d82447..94a658f 100644 --- a/sound/soc/codecs/tlv320aic26.c +++ b/sound/soc/codecs/tlv320aic26.c @@ -29,6 +29,7 @@ MODULE_LICENSE("GPL"); /* AIC26 driver private data */ struct aic26 { struct spi_device *spi; + struct regmap *regmap; struct snd_soc_codec *codec; int master; int datfm; @@ -40,72 +41,6 @@ struct aic26 { int keyclick_len; }; -/* --------------------------------------------------------------------- - * Register access routines - */ -static unsigned int aic26_reg_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - struct aic26 *aic26 = snd_soc_codec_get_drvdata(codec); - u16 *cache = codec->reg_cache; - u16 cmd, value; - u8 buffer[2]; - int rc; - - if (reg >= AIC26_NUM_REGS) { - WARN_ON_ONCE(1); - return 0; - } - - /* Do SPI transfer; first 16bits are command; remaining is - * register contents */ - cmd = AIC26_READ_COMMAND_WORD(reg); - buffer[0] = (cmd >> 8) & 0xff; - buffer[1] = cmd & 0xff; - rc = spi_write_then_read(aic26->spi, buffer, 2, buffer, 2); - if (rc) { - dev_err(&aic26->spi->dev, "AIC26 reg read error\n"); - return -EIO; - } - value = (buffer[0] << 8) | buffer[1]; - - /* Update the cache before returning with the value */ - cache[reg] = value; - return value; -} - -static int aic26_reg_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - struct aic26 *aic26 = snd_soc_codec_get_drvdata(codec); - u16 *cache = codec->reg_cache; - u16 cmd; - u8 buffer[4]; - int rc; - - if (reg >= AIC26_NUM_REGS) { - WARN_ON_ONCE(1); - return -EINVAL; - } - - /* Do SPI transfer; first 16bits are command; remaining is data - * to write into register */ - cmd = AIC26_WRITE_COMMAND_WORD(reg); - buffer[0] = (cmd >> 8) & 0xff; - buffer[1] = cmd & 0xff; - buffer[2] = value >> 8; - buffer[3] = value; - rc = spi_write(aic26->spi, buffer, 4); - if (rc) { - dev_err(&aic26->spi->dev, "AIC26 reg read error\n"); - return -EIO; - } - - /* update cache before returning */ - cache[reg] = value; - return 0; -} - static const struct snd_soc_dapm_widget tlv320aic26_dapm_widgets[] = { SND_SOC_DAPM_INPUT("MICIN"), SND_SOC_DAPM_INPUT("AUX"), @@ -360,6 +295,8 @@ static int aic26_probe(struct snd_soc_codec *codec) struct aic26 *aic26 = dev_get_drvdata(codec->dev); int ret, reg; + snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_REGMAP); + aic26->codec = codec; /* Reset the codec to power on defaults */ @@ -385,8 +322,6 @@ static int aic26_probe(struct snd_soc_codec *codec) static struct snd_soc_codec_driver aic26_soc_codec_dev = { .probe = aic26_probe, - .read = aic26_reg_read, - .write = aic26_reg_write, .controls = aic26_snd_controls, .num_controls = ARRAY_SIZE(aic26_snd_controls), .dapm_widgets = tlv320aic26_dapm_widgets, @@ -395,6 +330,11 @@ static struct snd_soc_codec_driver aic26_soc_codec_dev = { .num_dapm_routes = ARRAY_SIZE(tlv320aic26_dapm_routes), }; +static const struct regmap_config aic26_regmap = { + .reg_bits = 16, + .val_bits = 16, +}; + /* --------------------------------------------------------------------- * SPI device portion of driver: probe and release routines and SPI * driver registration. @@ -411,6 +351,10 @@ static int aic26_spi_probe(struct spi_device *spi) if (!aic26) return -ENOMEM; + aic26->regmap = devm_regmap_init_spi(spi, &aic26_regmap); + if (IS_ERR(aic26->regmap)) + return PTR_ERR(aic26->regmap); + /* Initialize the driver data */ aic26->spi = spi; dev_set_drvdata(&spi->dev, aic26); diff --git a/sound/soc/codecs/tlv320aic26.h b/sound/soc/codecs/tlv320aic26.h index 67f19c3..629b85e 100644 --- a/sound/soc/codecs/tlv320aic26.h +++ b/sound/soc/codecs/tlv320aic26.h @@ -9,10 +9,7 @@ #define _TLV320AIC16_H_ /* AIC26 Registers */ -#define AIC26_READ_COMMAND_WORD(addr) ((1 << 15) | (addr << 5)) -#define AIC26_WRITE_COMMAND_WORD(addr) ((0 << 15) | (addr << 5)) -#define AIC26_PAGE_ADDR(page, offset) ((page << 6) | offset) -#define AIC26_NUM_REGS AIC26_PAGE_ADDR(3, 0) +#define AIC26_PAGE_ADDR(page, offset) ((page << 11) | offset << 5) /* Page 0: Auxiliary data registers */ #define AIC26_REG_BAT1 AIC26_PAGE_ADDR(0, 0x05) -- cgit v0.10.2 From c29c2d4e6ba4f8f5868a4c6dd75a4c23e1993721 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 23 Sep 2013 19:14:31 +0100 Subject: mfd: mc13xxx: Don't require lock for simple register I/O Since the conversion to regmap there has been no need for device level locking for I/O as regmap provides locking so remove the locks. Signed-off-by: Mark Brown Signed-off-by: Lee Jones diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index 2a9b100..dbbf8ee 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -158,8 +158,6 @@ int mc13xxx_reg_read(struct mc13xxx *mc13xxx, unsigned int offset, u32 *val) { int ret; - BUG_ON(!mutex_is_locked(&mc13xxx->lock)); - if (offset > MC13XXX_NUMREGS) return -EINVAL; @@ -172,8 +170,6 @@ EXPORT_SYMBOL(mc13xxx_reg_read); int mc13xxx_reg_write(struct mc13xxx *mc13xxx, unsigned int offset, u32 val) { - BUG_ON(!mutex_is_locked(&mc13xxx->lock)); - dev_vdbg(mc13xxx->dev, "[0x%02x] <- 0x%06x\n", offset, val); if (offset > MC13XXX_NUMREGS || val > 0xffffff) @@ -186,7 +182,6 @@ EXPORT_SYMBOL(mc13xxx_reg_write); int mc13xxx_reg_rmw(struct mc13xxx *mc13xxx, unsigned int offset, u32 mask, u32 val) { - BUG_ON(!mutex_is_locked(&mc13xxx->lock)); BUG_ON(val & ~mask); dev_vdbg(mc13xxx->dev, "[0x%02x] <- 0x%06x (mask: 0x%06x)\n", offset, val, mask); -- cgit v0.10.2 From fd792f8fbcfa95674b6c417429f576ad1d808086 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 23 Sep 2013 19:14:32 +0100 Subject: mfd: mc13xxx: Move SPI erratum workaround into SPI I/O function Move the workaround for double sending AUDIO_CODEC and AUDIO_DAC writes into the SPI core, aiding refactoring to eliminate the ASoC custom I/O functions and avoiding the extra writes for I2C. Signed-off-by: Mark Brown Signed-off-by: Lee Jones diff --git a/drivers/mfd/mc13xxx-spi.c b/drivers/mfd/mc13xxx-spi.c index 77189da..5f14ef6 100644 --- a/drivers/mfd/mc13xxx-spi.c +++ b/drivers/mfd/mc13xxx-spi.c @@ -94,10 +94,15 @@ static int mc13xxx_spi_write(void *context, const void *data, size_t count) { struct device *dev = context; struct spi_device *spi = to_spi_device(dev); + const char *reg = data; if (count != 4) return -ENOTSUPP; + /* include errata fix for spi audio problems */ + if (*reg == MC13783_AUDIO_CODEC || *reg == MC13783_AUDIO_DAC) + spi_write(spi, data, count); + return spi_write(spi, data, count); } diff --git a/include/linux/mfd/mc13xxx.h b/include/linux/mfd/mc13xxx.h index 41ed592..67c17b5 100644 --- a/include/linux/mfd/mc13xxx.h +++ b/include/linux/mfd/mc13xxx.h @@ -41,6 +41,13 @@ int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode, unsigned int channel, u8 ato, bool atox, unsigned int *sample); +#define MC13783_AUDIO_RX0 36 +#define MC13783_AUDIO_RX1 37 +#define MC13783_AUDIO_TX 38 +#define MC13783_SSI_NETWORK 39 +#define MC13783_AUDIO_CODEC 40 +#define MC13783_AUDIO_DAC 41 + #define MC13XXX_IRQ_ADCDONE 0 #define MC13XXX_IRQ_ADCBISDONE 1 #define MC13XXX_IRQ_TS 2 diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c index ea141e1..4d3c8fd 100644 --- a/sound/soc/codecs/mc13783.c +++ b/sound/soc/codecs/mc13783.c @@ -125,10 +125,6 @@ static int mc13783_write(struct snd_soc_codec *codec, ret = mc13xxx_reg_write(priv->mc13xxx, reg, value); - /* include errata fix for spi audio problems */ - if (reg == MC13783_AUDIO_CODEC || reg == MC13783_AUDIO_DAC) - ret = mc13xxx_reg_write(priv->mc13xxx, reg, value); - mc13xxx_unlock(priv->mc13xxx); return ret; -- cgit v0.10.2 From 2d9215c1ecd6f133952bc081a288dbb180816290 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 18 Sep 2013 19:04:17 +0100 Subject: ASoC: mc13783: Use regmap directly from ASoC As part of a push to remove the register I/O functionality from ASoC (since it is now duplicated in the regmap API) convert the mc13783 driver to use regmap directly. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c index 4d3c8fd..eedbf05 100644 --- a/sound/soc/codecs/mc13783.c +++ b/sound/soc/codecs/mc13783.c @@ -30,16 +30,10 @@ #include #include #include +#include #include "mc13783.h" -#define MC13783_AUDIO_RX0 36 -#define MC13783_AUDIO_RX1 37 -#define MC13783_AUDIO_TX 38 -#define MC13783_SSI_NETWORK 39 -#define MC13783_AUDIO_CODEC 40 -#define MC13783_AUDIO_DAC 41 - #define AUDIO_RX0_ALSPEN (1 << 5) #define AUDIO_RX0_ALSPSEL (1 << 7) #define AUDIO_RX0_ADDCDC (1 << 21) @@ -95,41 +89,12 @@ struct mc13783_priv { struct mc13xxx *mc13xxx; + struct regmap *regmap; enum mc13783_ssi_port adc_ssi_port; enum mc13783_ssi_port dac_ssi_port; }; -static unsigned int mc13783_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec); - unsigned int value = 0; - - mc13xxx_lock(priv->mc13xxx); - - mc13xxx_reg_read(priv->mc13xxx, reg, &value); - - mc13xxx_unlock(priv->mc13xxx); - - return value; -} - -static int mc13783_write(struct snd_soc_codec *codec, - unsigned int reg, unsigned int value) -{ - struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec); - int ret; - - mc13xxx_lock(priv->mc13xxx); - - ret = mc13xxx_reg_write(priv->mc13xxx, reg, value); - - mc13xxx_unlock(priv->mc13xxx); - - return ret; -} - /* Mapping between sample rates and register value */ static unsigned int mc13783_rates[] = { 8000, 11025, 12000, 16000, @@ -583,8 +548,14 @@ static struct snd_kcontrol_new mc13783_control_list[] = { static int mc13783_probe(struct snd_soc_codec *codec) { struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec); + int ret; - mc13xxx_lock(priv->mc13xxx); + codec->control_data = dev_get_regmap(codec->dev->parent, NULL); + ret = snd_soc_codec_set_cache_io(codec, 8, 24, SND_SOC_REGMAP); + if (ret != 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } /* these are the reset values */ mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_RX0, 0x25893); @@ -608,8 +579,6 @@ static int mc13783_probe(struct snd_soc_codec *codec) mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_DAC, 0, AUDIO_SSI_SEL); - mc13xxx_unlock(priv->mc13xxx); - return 0; } @@ -617,13 +586,9 @@ static int mc13783_remove(struct snd_soc_codec *codec) { struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec); - mc13xxx_lock(priv->mc13xxx); - /* Make sure VAUDIOON is off */ mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_RX0, 0x3, 0); - mc13xxx_unlock(priv->mc13xxx); - return 0; } @@ -713,8 +678,6 @@ static struct snd_soc_dai_driver mc13783_dai_sync[] = { static struct snd_soc_codec_driver soc_codec_dev_mc13783 = { .probe = mc13783_probe, .remove = mc13783_remove, - .read = mc13783_read, - .write = mc13783_write, .controls = mc13783_control_list, .num_controls = ARRAY_SIZE(mc13783_control_list), .dapm_widgets = mc13783_dapm_widgets, -- cgit v0.10.2 From 7a497c963eceac42677ce1f5d7bb470abedd15f4 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 22 Aug 2013 18:16:16 -0700 Subject: rcu: Remove redundant code from rcu_cleanup_after_idle() The rcu_try_advance_all_cbs() function returns a bool saying whether or not there are callbacks ready to invoke, but rcu_cleanup_after_idle() rechecks this regardless. This commit therefore uses the value returned by rcu_try_advance_all_cbs() instead of making rcu_cleanup_after_idle() do this recheck. Reported-by: Tibor Billes Signed-off-by: Paul E. McKenney Tested-by: Tibor Billes Reviewed-by: Josh Triplett diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h index 130c97b..18d9c91 100644 --- a/kernel/rcutree_plugin.h +++ b/kernel/rcutree_plugin.h @@ -1768,17 +1768,11 @@ static void rcu_prepare_for_idle(int cpu) */ static void rcu_cleanup_after_idle(int cpu) { - struct rcu_data *rdp; - struct rcu_state *rsp; if (rcu_is_nocb_cpu(cpu)) return; - rcu_try_advance_all_cbs(); - for_each_rcu_flavor(rsp) { - rdp = per_cpu_ptr(rsp->rda, cpu); - if (cpu_has_callbacks_ready_to_invoke(rdp)) - invoke_rcu_core(); - } + if (rcu_try_advance_all_cbs()) + invoke_rcu_core(); } /* -- cgit v0.10.2 From c229828ca6bc62d6c654f64b1d1b8a9ebd8a56f3 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sun, 25 Aug 2013 21:20:47 -0700 Subject: rcu: Throttle rcu_try_advance_all_cbs() execution The rcu_try_advance_all_cbs() function is invoked on each attempted entry to and every exit from idle. If this function determines that there are callbacks ready to invoke, the caller will invoke the RCU core, which in turn will result in a pair of context switches. If a CPU enters and exits idle extremely frequently, this can result in an excessive number of context switches and high CPU overhead. This commit therefore causes rcu_try_advance_all_cbs() to throttle itself, refusing to do work more than once per jiffy. Reported-by: Tibor Billes Signed-off-by: Paul E. McKenney Tested-by: Tibor Billes Reviewed-by: Josh Triplett diff --git a/kernel/rcutree.h b/kernel/rcutree.h index 5f97eab..52be957 100644 --- a/kernel/rcutree.h +++ b/kernel/rcutree.h @@ -104,6 +104,8 @@ struct rcu_dynticks { /* idle-period nonlazy_posted snapshot. */ unsigned long last_accelerate; /* Last jiffy CBs were accelerated. */ + unsigned long last_advance_all; + /* Last jiffy CBs were all advanced. */ int tick_nohz_enabled_snap; /* Previously seen value from sysfs. */ #endif /* #ifdef CONFIG_RCU_FAST_NO_HZ */ }; diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h index 18d9c91..d81e385 100644 --- a/kernel/rcutree_plugin.h +++ b/kernel/rcutree_plugin.h @@ -1630,17 +1630,23 @@ module_param(rcu_idle_lazy_gp_delay, int, 0644); extern int tick_nohz_enabled; /* - * Try to advance callbacks for all flavors of RCU on the current CPU. - * Afterwards, if there are any callbacks ready for immediate invocation, - * return true. + * Try to advance callbacks for all flavors of RCU on the current CPU, but + * only if it has been awhile since the last time we did so. Afterwards, + * if there are any callbacks ready for immediate invocation, return true. */ static bool rcu_try_advance_all_cbs(void) { bool cbs_ready = false; struct rcu_data *rdp; + struct rcu_dynticks *rdtp = this_cpu_ptr(&rcu_dynticks); struct rcu_node *rnp; struct rcu_state *rsp; + /* Exit early if we advanced recently. */ + if (jiffies == rdtp->last_advance_all) + return 0; + rdtp->last_advance_all = jiffies; + for_each_rcu_flavor(rsp) { rdp = this_cpu_ptr(rsp->rda); rnp = rdp->mynode; -- cgit v0.10.2 From c337f8f58ed7cf150651d232af8222421a71463d Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 5 Sep 2013 17:02:11 -0700 Subject: rcu: Throttle invoke_rcu_core() invocations due to non-lazy callbacks If a non-lazy callback arrives on a CPU that has previously gone idle with no non-lazy callbacks, invoke_rcu_core() forces the RCU core to run. However, it does not update the conditions, which could result in several closely spaced invocations of the RCU core, which in turn could result in an excessively high context-switch rate and resulting high overhead. This commit therefore updates the ->all_lazy and ->nonlazy_posted_snap fields to prevent closely spaced invocations. Reported-by: Tibor Billes Signed-off-by: Paul E. McKenney Tested-by: Tibor Billes Reviewed-by: Josh Triplett diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h index d81e385..2c15d7c 100644 --- a/kernel/rcutree_plugin.h +++ b/kernel/rcutree_plugin.h @@ -1745,6 +1745,8 @@ static void rcu_prepare_for_idle(int cpu) */ if (rdtp->all_lazy && rdtp->nonlazy_posted != rdtp->nonlazy_posted_snap) { + rdtp->all_lazy = false; + rdtp->nonlazy_posted_snap = rdtp->nonlazy_posted; invoke_rcu_core(); return; } -- cgit v0.10.2 From cc6783f788d8fe8b23ec6fc2762f5e8c9a418eee Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 6 Sep 2013 17:39:49 -0700 Subject: rcu: Is it safe to enter an RCU read-side critical section? There is currently no way for kernel code to determine whether it is safe to enter an RCU read-side critical section, in other words, whether or not RCU is paying attention to the currently running CPU. Given the large and increasing quantity of code shared by the idle loop and non-idle code, the this shortcoming is becoming increasingly painful. This commit therefore adds __rcu_is_watching(), which returns true if it is safe to enter an RCU read-side critical section on the currently running CPU. This function is quite fast, using only a __this_cpu_read(). However, the caller must disable preemption. Reported-by: Steven Rostedt Signed-off-by: Paul E. McKenney Reviewed-by: Josh Triplett diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index f1f1bc3..a53a21a 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -261,6 +261,10 @@ static inline void rcu_user_hooks_switch(struct task_struct *prev, rcu_irq_exit(); \ } while (0) +#if defined(CONFIG_DEBUG_LOCK_ALLOC) || defined(CONFIG_RCU_TRACE) || defined(CONFIG_SMP) +extern int rcu_is_cpu_idle(void); +#endif /* #if defined(CONFIG_DEBUG_LOCK_ALLOC) || defined(CONFIG_RCU_TRACE) || defined(CONFIG_SMP) */ + /* * Infrastructure to implement the synchronize_() primitives in * TREE_RCU and rcu_barrier_() primitives in TINY_RCU. @@ -297,10 +301,6 @@ static inline void destroy_rcu_head_on_stack(struct rcu_head *head) } #endif /* #else !CONFIG_DEBUG_OBJECTS_RCU_HEAD */ -#if defined(CONFIG_DEBUG_LOCK_ALLOC) || defined(CONFIG_SMP) -extern int rcu_is_cpu_idle(void); -#endif /* #if defined(CONFIG_DEBUG_LOCK_ALLOC) || defined(CONFIG_SMP) */ - #if defined(CONFIG_HOTPLUG_CPU) && defined(CONFIG_PROVE_RCU) bool rcu_lockdep_current_cpu_online(void); #else /* #if defined(CONFIG_HOTPLUG_CPU) && defined(CONFIG_PROVE_RCU) */ diff --git a/include/linux/rcutiny.h b/include/linux/rcutiny.h index e31005e..bee6659 100644 --- a/include/linux/rcutiny.h +++ b/include/linux/rcutiny.h @@ -132,4 +132,13 @@ static inline void rcu_scheduler_starting(void) } #endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */ +#ifdef CONFIG_RCU_TRACE + +static inline bool __rcu_is_watching(void) +{ + return !rcu_is_cpu_idle(); +} + +#endif /* #ifdef CONFIG_RCU_TRACE */ + #endif /* __LINUX_RCUTINY_H */ diff --git a/include/linux/rcutree.h b/include/linux/rcutree.h index 226169d..293613d 100644 --- a/include/linux/rcutree.h +++ b/include/linux/rcutree.h @@ -90,4 +90,6 @@ extern void exit_rcu(void); extern void rcu_scheduler_starting(void); extern int rcu_scheduler_active __read_mostly; +extern bool __rcu_is_watching(void); + #endif /* __LINUX_RCUTREE_H */ diff --git a/kernel/rcutiny.c b/kernel/rcutiny.c index 9ed6075..b4bc618 100644 --- a/kernel/rcutiny.c +++ b/kernel/rcutiny.c @@ -174,7 +174,7 @@ void rcu_irq_enter(void) } EXPORT_SYMBOL_GPL(rcu_irq_enter); -#ifdef CONFIG_DEBUG_LOCK_ALLOC +#if defined(CONFIG_DEBUG_LOCK_ALLOC) || defined(CONFIG_RCU_TRACE) /* * Test whether RCU thinks that the current CPU is idle. @@ -185,7 +185,7 @@ int rcu_is_cpu_idle(void) } EXPORT_SYMBOL(rcu_is_cpu_idle); -#endif /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ +#endif /* defined(CONFIG_DEBUG_LOCK_ALLOC) || defined(CONFIG_RCU_TRACE) */ /* * Test whether the current CPU was interrupted from idle. Nested diff --git a/kernel/rcutree.c b/kernel/rcutree.c index 32618b3..910d868 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -671,6 +671,19 @@ int rcu_is_cpu_idle(void) } EXPORT_SYMBOL(rcu_is_cpu_idle); +/** + * __rcu_is_watching - are RCU read-side critical sections safe? + * + * Return true if RCU is watching the running CPU, which means that + * this CPU can safely enter RCU read-side critical sections. Unlike + * rcu_is_cpu_idle(), the caller of __rcu_is_watching() must have at + * least disabled preemption. + */ +bool __rcu_is_watching(void) +{ + return !!(atomic_read(this_cpu_ptr(&rcu_dynticks.dynticks)) & 0x1); +} + #if defined(CONFIG_PROVE_RCU) && defined(CONFIG_HOTPLUG_CPU) /* -- cgit v0.10.2 From f9ffc31ebd38d2d74dbfe9f0b67274e99ad668f5 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sun, 8 Sep 2013 11:51:06 -0700 Subject: rcu: Change EXPORT_SYMBOL() to EXPORT_SYMBOL_GPL() Commit e6b80a3b (rcu: Detect illegal rcu dereference in extended quiescent state) exported the pre-existing rcu_is_cpu_idle() function using EXPORT_SYMBOL(). However, this is inconsistent with the remaining exports from RCU, which are all EXPORT_SYMBOL_GPL(). The current state of affairs means that a non-GPL module could use rcu_is_cpu_idle(), but in a CONFIG_TREE_PREEMPT_RCU=y kernel would be unable to invoke rcu_read_lock() and rcu_read_unlock(). This commit therefore makes rcu_is_cpu_idle()'s export be consistent with the rest of RCU, namely EXPORT_SYMBOL_GPL(). Signed-off-by: Paul E. McKenney Cc: Frederic Weisbecker Reviewed-by: Josh Triplett diff --git a/kernel/rcutree.c b/kernel/rcutree.c index 910d868..1b123e1 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -669,7 +669,7 @@ int rcu_is_cpu_idle(void) preempt_enable(); return ret; } -EXPORT_SYMBOL(rcu_is_cpu_idle); +EXPORT_SYMBOL_GPL(rcu_is_cpu_idle); /** * __rcu_is_watching - are RCU read-side critical sections safe? -- cgit v0.10.2 From 5c173eb8bcb9c1aa888bd6d14a4cb746f3dd2420 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 13 Sep 2013 17:20:11 -0700 Subject: rcu: Consistent rcu_is_watching() naming The old rcu_is_cpu_idle() function is just __rcu_is_watching() with preemption disabled. This commit therefore renames rcu_is_cpu_idle() to rcu_is_watching. Signed-off-by: Paul E. McKenney Reviewed-by: Josh Triplett diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index a53a21a..39cbb88 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -262,7 +262,7 @@ static inline void rcu_user_hooks_switch(struct task_struct *prev, } while (0) #if defined(CONFIG_DEBUG_LOCK_ALLOC) || defined(CONFIG_RCU_TRACE) || defined(CONFIG_SMP) -extern int rcu_is_cpu_idle(void); +extern bool __rcu_is_watching(void); #endif /* #if defined(CONFIG_DEBUG_LOCK_ALLOC) || defined(CONFIG_RCU_TRACE) || defined(CONFIG_SMP) */ /* @@ -351,7 +351,7 @@ static inline int rcu_read_lock_held(void) { if (!debug_lockdep_rcu_enabled()) return 1; - if (rcu_is_cpu_idle()) + if (!rcu_is_watching()) return 0; if (!rcu_lockdep_current_cpu_online()) return 0; @@ -402,7 +402,7 @@ static inline int rcu_read_lock_sched_held(void) if (!debug_lockdep_rcu_enabled()) return 1; - if (rcu_is_cpu_idle()) + if (!rcu_is_watching()) return 0; if (!rcu_lockdep_current_cpu_online()) return 0; @@ -771,7 +771,7 @@ static inline void rcu_read_lock(void) __rcu_read_lock(); __acquire(RCU); rcu_lock_acquire(&rcu_lock_map); - rcu_lockdep_assert(!rcu_is_cpu_idle(), + rcu_lockdep_assert(rcu_is_watching(), "rcu_read_lock() used illegally while idle"); } @@ -792,7 +792,7 @@ static inline void rcu_read_lock(void) */ static inline void rcu_read_unlock(void) { - rcu_lockdep_assert(!rcu_is_cpu_idle(), + rcu_lockdep_assert(rcu_is_watching(), "rcu_read_unlock() used illegally while idle"); rcu_lock_release(&rcu_lock_map); __release(RCU); @@ -821,7 +821,7 @@ static inline void rcu_read_lock_bh(void) local_bh_disable(); __acquire(RCU_BH); rcu_lock_acquire(&rcu_bh_lock_map); - rcu_lockdep_assert(!rcu_is_cpu_idle(), + rcu_lockdep_assert(rcu_is_watching(), "rcu_read_lock_bh() used illegally while idle"); } @@ -832,7 +832,7 @@ static inline void rcu_read_lock_bh(void) */ static inline void rcu_read_unlock_bh(void) { - rcu_lockdep_assert(!rcu_is_cpu_idle(), + rcu_lockdep_assert(rcu_is_watching(), "rcu_read_unlock_bh() used illegally while idle"); rcu_lock_release(&rcu_bh_lock_map); __release(RCU_BH); @@ -857,7 +857,7 @@ static inline void rcu_read_lock_sched(void) preempt_disable(); __acquire(RCU_SCHED); rcu_lock_acquire(&rcu_sched_lock_map); - rcu_lockdep_assert(!rcu_is_cpu_idle(), + rcu_lockdep_assert(rcu_is_watching(), "rcu_read_lock_sched() used illegally while idle"); } @@ -875,7 +875,7 @@ static inline notrace void rcu_read_lock_sched_notrace(void) */ static inline void rcu_read_unlock_sched(void) { - rcu_lockdep_assert(!rcu_is_cpu_idle(), + rcu_lockdep_assert(rcu_is_watching(), "rcu_read_unlock_sched() used illegally while idle"); rcu_lock_release(&rcu_sched_lock_map); __release(RCU_SCHED); diff --git a/include/linux/rcutiny.h b/include/linux/rcutiny.h index bee6659..09ebcbe 100644 --- a/include/linux/rcutiny.h +++ b/include/linux/rcutiny.h @@ -132,13 +132,21 @@ static inline void rcu_scheduler_starting(void) } #endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */ -#ifdef CONFIG_RCU_TRACE +#if defined(CONFIG_DEBUG_LOCK_ALLOC) || defined(CONFIG_RCU_TRACE) -static inline bool __rcu_is_watching(void) +static inline bool rcu_is_watching(void) { - return !rcu_is_cpu_idle(); + return __rcu_is_watching(); } -#endif /* #ifdef CONFIG_RCU_TRACE */ +#else /* defined(CONFIG_DEBUG_LOCK_ALLOC) || defined(CONFIG_RCU_TRACE) */ + +static inline bool rcu_is_watching(void) +{ + return true; +} + + +#endif /* #else defined(CONFIG_DEBUG_LOCK_ALLOC) || defined(CONFIG_RCU_TRACE) */ #endif /* __LINUX_RCUTINY_H */ diff --git a/include/linux/rcutree.h b/include/linux/rcutree.h index 293613d..4b9c815 100644 --- a/include/linux/rcutree.h +++ b/include/linux/rcutree.h @@ -90,6 +90,6 @@ extern void exit_rcu(void); extern void rcu_scheduler_starting(void); extern int rcu_scheduler_active __read_mostly; -extern bool __rcu_is_watching(void); +extern bool rcu_is_watching(void); #endif /* __LINUX_RCUTREE_H */ diff --git a/kernel/lockdep.c b/kernel/lockdep.c index e16c45b..4e8e14c 100644 --- a/kernel/lockdep.c +++ b/kernel/lockdep.c @@ -4224,7 +4224,7 @@ void lockdep_rcu_suspicious(const char *file, const int line, const char *s) printk("\n%srcu_scheduler_active = %d, debug_locks = %d\n", !rcu_lockdep_current_cpu_online() ? "RCU used illegally from offline CPU!\n" - : rcu_is_cpu_idle() + : !rcu_is_watching() ? "RCU used illegally from idle CPU!\n" : "", rcu_scheduler_active, debug_locks); @@ -4247,7 +4247,7 @@ void lockdep_rcu_suspicious(const char *file, const int line, const char *s) * So complain bitterly if someone does call rcu_read_lock(), * rcu_read_lock_bh() and so on from extended quiescent states. */ - if (rcu_is_cpu_idle()) + if (!rcu_is_watching()) printk("RCU used illegally from extended quiescent state!\n"); lockdep_print_held_locks(curr); diff --git a/kernel/rcupdate.c b/kernel/rcupdate.c index b02a339..3b3c046 100644 --- a/kernel/rcupdate.c +++ b/kernel/rcupdate.c @@ -148,7 +148,7 @@ int rcu_read_lock_bh_held(void) { if (!debug_lockdep_rcu_enabled()) return 1; - if (rcu_is_cpu_idle()) + if (!rcu_is_watching()) return 0; if (!rcu_lockdep_current_cpu_online()) return 0; diff --git a/kernel/rcutiny.c b/kernel/rcutiny.c index b4bc618..0fa061d 100644 --- a/kernel/rcutiny.c +++ b/kernel/rcutiny.c @@ -179,11 +179,11 @@ EXPORT_SYMBOL_GPL(rcu_irq_enter); /* * Test whether RCU thinks that the current CPU is idle. */ -int rcu_is_cpu_idle(void) +bool __rcu_is_watching(void) { - return !rcu_dynticks_nesting; + return rcu_dynticks_nesting; } -EXPORT_SYMBOL(rcu_is_cpu_idle); +EXPORT_SYMBOL(__rcu_is_watching); #endif /* defined(CONFIG_DEBUG_LOCK_ALLOC) || defined(CONFIG_RCU_TRACE) */ diff --git a/kernel/rcutree.c b/kernel/rcutree.c index 1b123e1..981d0c1 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -655,34 +655,34 @@ void rcu_nmi_exit(void) } /** - * rcu_is_cpu_idle - see if RCU thinks that the current CPU is idle + * __rcu_is_watching - are RCU read-side critical sections safe? + * + * Return true if RCU is watching the running CPU, which means that + * this CPU can safely enter RCU read-side critical sections. Unlike + * rcu_is_watching(), the caller of __rcu_is_watching() must have at + * least disabled preemption. + */ +bool __rcu_is_watching(void) +{ + return atomic_read(this_cpu_ptr(&rcu_dynticks.dynticks)) & 0x1; +} + +/** + * rcu_is_watching - see if RCU thinks that the current CPU is idle * * If the current CPU is in its idle loop and is neither in an interrupt * or NMI handler, return true. */ -int rcu_is_cpu_idle(void) +bool rcu_is_watching(void) { int ret; preempt_disable(); - ret = (atomic_read(&__get_cpu_var(rcu_dynticks).dynticks) & 0x1) == 0; + ret = __rcu_is_watching(); preempt_enable(); return ret; } -EXPORT_SYMBOL_GPL(rcu_is_cpu_idle); - -/** - * __rcu_is_watching - are RCU read-side critical sections safe? - * - * Return true if RCU is watching the running CPU, which means that - * this CPU can safely enter RCU read-side critical sections. Unlike - * rcu_is_cpu_idle(), the caller of __rcu_is_watching() must have at - * least disabled preemption. - */ -bool __rcu_is_watching(void) -{ - return !!(atomic_read(this_cpu_ptr(&rcu_dynticks.dynticks)) & 0x1); -} +EXPORT_SYMBOL_GPL(rcu_is_watching); #if defined(CONFIG_PROVE_RCU) && defined(CONFIG_HOTPLUG_CPU) @@ -2268,7 +2268,7 @@ static void __call_rcu_core(struct rcu_state *rsp, struct rcu_data *rdp, * If called from an extended quiescent state, invoke the RCU * core in order to force a re-evaluation of RCU's idleness. */ - if (rcu_is_cpu_idle() && cpu_online(smp_processor_id())) + if (!rcu_is_watching() && cpu_online(smp_processor_id())) invoke_rcu_core(); /* If interrupts were disabled or CPU offline, don't invoke RCU core. */ -- cgit v0.10.2 From 64d3b7a1d5289486df2d8bce36e23ed5ebc80a3d Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 19 Aug 2013 11:59:43 -0700 Subject: rcu: Update stall-warning documentation Signed-off-by: Paul E. McKenney Reviewed-by: Josh Triplett diff --git a/Documentation/RCU/stallwarn.txt b/Documentation/RCU/stallwarn.txt index 8e9359d..6f3a005 100644 --- a/Documentation/RCU/stallwarn.txt +++ b/Documentation/RCU/stallwarn.txt @@ -12,12 +12,12 @@ CONFIG_RCU_CPU_STALL_TIMEOUT This kernel configuration parameter defines the period of time that RCU will wait from the beginning of a grace period until it issues an RCU CPU stall warning. This time period is normally - sixty seconds. + 21 seconds. This configuration parameter may be changed at runtime via the /sys/module/rcutree/parameters/rcu_cpu_stall_timeout, however this parameter is checked only at the beginning of a cycle. - So if you are 30 seconds into a 70-second stall, setting this + So if you are 10 seconds into a 40-second stall, setting this sysfs parameter to (say) five will shorten the timeout for the -next- stall, or the following warning for the current stall (assuming the stall lasts long enough). It will not affect the @@ -32,7 +32,7 @@ CONFIG_RCU_CPU_STALL_VERBOSE also dump the stacks of any tasks that are blocking the current RCU-preempt grace period. -RCU_CPU_STALL_INFO +CONFIG_RCU_CPU_STALL_INFO This kernel configuration parameter causes the stall warning to print out additional per-CPU diagnostic information, including @@ -43,7 +43,8 @@ RCU_STALL_DELAY_DELTA Although the lockdep facility is extremely useful, it does add some overhead. Therefore, under CONFIG_PROVE_RCU, the RCU_STALL_DELAY_DELTA macro allows five extra seconds before - giving an RCU CPU stall warning message. + giving an RCU CPU stall warning message. (This is a cpp + macro, not a kernel configuration parameter.) RCU_STALL_RAT_DELAY @@ -52,7 +53,8 @@ RCU_STALL_RAT_DELAY However, if the offending CPU does not detect its own stall in the number of jiffies specified by RCU_STALL_RAT_DELAY, then some other CPU will complain. This delay is normally set to - two jiffies. + two jiffies. (This is a cpp macro, not a kernel configuration + parameter.) When a CPU detects that it is stalling, it will print a message similar to the following: @@ -86,7 +88,12 @@ printing, there will be a spurious stall-warning message: INFO: rcu_bh_state detected stalls on CPUs/tasks: { } (detected by 4, 2502 jiffies) -This is rare, but does happen from time to time in real life. +This is rare, but does happen from time to time in real life. It is also +possible for a zero-jiffy stall to be flagged in this case, depending +on how the stall warning and the grace-period initialization happen to +interact. Please note that it is not possible to entirely eliminate this +sort of false positive without resorting to things like stop_machine(), +which is overkill for this sort of problem. If the CONFIG_RCU_CPU_STALL_INFO kernel configuration parameter is set, more information is printed with the stall-warning message, for example: @@ -216,4 +223,5 @@ that portion of the stack which remains the same from trace to trace. If you can reliably trigger the stall, ftrace can be quite helpful. RCU bugs can often be debugged with the help of CONFIG_RCU_TRACE -and with RCU's event tracing. +and with RCU's event tracing. For information on RCU's event tracing, +see include/trace/events/rcu.h. -- cgit v0.10.2 From 64f26e5c86af9ce8615721340c8282f2b148c9aa Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 10 Sep 2013 08:26:09 -0700 Subject: kthread: Add pointer to vmstat-avoidance patch Signed-off-by: Paul E. McKenney Reviewed-by: Josh Triplett diff --git a/Documentation/kernel-per-CPU-kthreads.txt b/Documentation/kernel-per-CPU-kthreads.txt index 32351bf..827104f 100644 --- a/Documentation/kernel-per-CPU-kthreads.txt +++ b/Documentation/kernel-per-CPU-kthreads.txt @@ -181,12 +181,17 @@ To reduce its OS jitter, do any of the following: make sure that this is safe on your particular system. d. It is not possible to entirely get rid of OS jitter from vmstat_update() on CONFIG_SMP=y systems, but you - can decrease its frequency by writing a large value to - /proc/sys/vm/stat_interval. The default value is HZ, - for an interval of one second. Of course, larger values - will make your virtual-memory statistics update more - slowly. Of course, you can also run your workload at - a real-time priority, thus preempting vmstat_update(). + can decrease its frequency by writing a large value + to /proc/sys/vm/stat_interval. The default value is + HZ, for an interval of one second. Of course, larger + values will make your virtual-memory statistics update + more slowly. Of course, you can also run your workload + at a real-time priority, thus preempting vmstat_update(), + but if your workload is CPU-bound, this is a bad idea. + However, there is an RFC patch from Christoph Lameter + (based on an earlier one from Gilad Ben-Yossef) that + reduces or even eliminates vmstat overhead for some + workloads at https://lkml.org/lkml/2013/9/4/379. e. If running on high-end powerpc servers, build with CONFIG_PPC_RTAS_DAEMON=n. This prevents the RTAS daemon from running on each CPU every second or so. -- cgit v0.10.2 From 4b0d3f0fde41a3c4454adb4d474618c23cfd4131 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Mon, 23 Sep 2013 12:40:41 -0700 Subject: rcu: Fix occurrence of "the the" in checklist.txt Signed-off-by: Michael Opdenacker Signed-off-by: Paul E. McKenney [ paulmck: Add "then" as suggested by Josh Triplett. ] Reviewed-by: Josh Triplett diff --git a/Documentation/RCU/checklist.txt b/Documentation/RCU/checklist.txt index 7703ec7..9126619 100644 --- a/Documentation/RCU/checklist.txt +++ b/Documentation/RCU/checklist.txt @@ -202,8 +202,8 @@ over a rather long period of time, but improvements are always welcome! updater uses call_rcu_sched() or synchronize_sched(), then the corresponding readers must disable preemption, possibly by calling rcu_read_lock_sched() and rcu_read_unlock_sched(). - If the updater uses synchronize_srcu() or call_srcu(), - the the corresponding readers must use srcu_read_lock() and + If the updater uses synchronize_srcu() or call_srcu(), then + the corresponding readers must use srcu_read_lock() and srcu_read_unlock(), and with the same srcu_struct. The rules for the expedited primitives are the same as for their non-expedited counterparts. Mixing things up will result in confusion and -- cgit v0.10.2 From 16c21ae5ca636cfd38e581ebcf709c49d78ea56d Mon Sep 17 00:00:00 2001 From: Li Fei Date: Wed, 21 Aug 2013 16:13:57 +0800 Subject: reboot: Allow specifying warm/cold reset for CF9 boot type In current implementation for reboot type CF9 and CF9_COND, warm and cold reset are not differentiated, and both are performed by writing 0x06 to port 0xCF9. This commit will differentiate warm and cold reset: For warm reset, write 0x06 to port 0xCF9; For cold reset, write 0x0E to port 0xCF9. [ hpa: This meaning of "cold" and "warm" reset is different from other reboot types use, where "warm" means "bypass BIOS POST". It is also not entirely clear that it actually solves any actual problem. However, it would seem fairly harmless to offer this additional option. Also note that we do not mask bit 3 in the "warm reset" case. This preserves the behavior on existing systems, including ones quirked to use CF9. It seems reasonable that on any system where the warm/cold distinction actually matters that bit 3 would be read as zero. ] From: Liu Chuansheng Signed-off-by: Li Fei Link: http://lkml.kernel.org/r/1377072837.24556.2.camel@fli24-HP-Compaq-8100-Elite-CMT-PC Signed-off-by: H. Peter Anvin Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index 563ed91..ca42f63 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -511,10 +511,13 @@ static void native_machine_emergency_restart(void) case BOOT_CF9_COND: if (port_cf9_safe) { - u8 cf9 = inb(0xcf9) & ~6; + u8 reboot_code = reboot_mode == REBOOT_WARM ? + 0x06 : 0x0E; + u8 cf9 = inb(0xcf9) & ~reboot_code; outb(cf9|2, 0xcf9); /* Request hard reset */ udelay(50); - outb(cf9|6, 0xcf9); /* Actually do the reset */ + /* Actually do the reset */ + outb(cf9|reboot_code, 0xcf9); udelay(50); } reboot_type = BOOT_KBD; -- cgit v0.10.2 From e0f17c75d9352c38926da1b4d8dbefc2d9942006 Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Sun, 22 Sep 2013 20:44:12 +0200 Subject: ALSA: Fix assignment of 0/1 to bool variables Convert 0 to false and 1 to true when assigning values to bool variables. Inspired by commit 3db1cd5c05f35fb43eb134df6f321de4e63141f2. The simplified semantic patch that find this problem is as follows (http://coccinelle.lip6.fr/): @@ bool b; @@ ( -b = 0 +b = false | -b = 1 +b = true ) Signed-off-by: Peter Senna Tschudin Signed-off-by: Takashi Iwai diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index c8e1216..1aef712 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -715,14 +715,14 @@ snd_azf3328_mixer_ac97_read(struct snd_ac97 *ac97, unsigned short reg_ac97) const struct snd_azf3328 *chip = ac97->private_data; unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97); unsigned short reg_val = 0; - bool unsupported = 0; + bool unsupported = false; snd_azf3328_dbgmixer( "snd_azf3328_mixer_ac97_read reg_ac97 %u\n", reg_ac97 ); if (reg_azf & AZF_AC97_REG_UNSUPPORTED) - unsupported = 1; + unsupported = true; else { if (reg_azf & AZF_AC97_REG_REAL_IO_READ) reg_val = snd_azf3328_mixer_inw(chip, @@ -759,7 +759,7 @@ snd_azf3328_mixer_ac97_read(struct snd_ac97 *ac97, unsigned short reg_ac97) reg_val = azf_emulated_ac97_vendor_id & 0xffff; break; default: - unsupported = 1; + unsupported = true; break; } } @@ -776,14 +776,14 @@ snd_azf3328_mixer_ac97_write(struct snd_ac97 *ac97, { const struct snd_azf3328 *chip = ac97->private_data; unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97); - bool unsupported = 0; + bool unsupported = false; snd_azf3328_dbgmixer( "snd_azf3328_mixer_ac97_write reg_ac97 %u val %u\n", reg_ac97, val ); if (reg_azf & AZF_AC97_REG_UNSUPPORTED) - unsupported = 1; + unsupported = true; else { if (reg_azf & AZF_AC97_REG_REAL_IO_WRITE) snd_azf3328_mixer_outw( @@ -808,7 +808,7 @@ snd_azf3328_mixer_ac97_write(struct snd_ac97 *ac97, */ break; default: - unsupported = 1; + unsupported = true; break; } } @@ -1559,7 +1559,7 @@ snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd) struct snd_azf3328_codec_data *codec = runtime->private_data; int result = 0; u16 flags1; - bool previously_muted = 0; + bool previously_muted = false; bool is_main_mixer_playback_codec = (AZF_CODEC_PLAYBACK == codec->type); snd_azf3328_dbgcalls("snd_azf3328_pcm_trigger cmd %d\n", cmd); diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 95558ef..44b0ba4 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1151,14 +1151,14 @@ static void check_no_speaker_on_headset(struct snd_kcontrol *kctl, const char *names_to_check[] = { "Headset", "headset", "Headphone", "headphone", NULL}; const char **s; - bool found = 0; + bool found = false; if (strcmp("Speaker", kctl->id.name)) return; for (s = names_to_check; *s; s++) if (strstr(card->shortname, *s)) { - found = 1; + found = true; break; } -- cgit v0.10.2 From 976b6c064a957445eb0573b270f2d0282630e9b9 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 24 Sep 2013 15:51:58 -0400 Subject: ALSA: improve buffer size computations for USB PCM audio This patch changes the way URBs are allocated and their sizes are determined for PCM playback in the snd-usb-audio driver. Currently the driver allocates too few URBs for endpoints that don't use implicit sync, making underruns more likely to occur. This may be a holdover from before I/O delays could be measured accurately; in any case, it is no longer necessary. The patch allocates as many URBs as possible, subject to four limitations: The total number of URBs for the endpoint is not allowed to exceed MAX_URBS (which the patch increases from 8 to 12). The total number of packets per URB is not allowed to exceed MAX_PACKS (or MAX_PACKS_HS for high-speed devices), which is decreased from 20 to 6. The total duration of queued data is not allowed to exceed MAX_QUEUE, which is decreased from 24 ms to 18 ms. The total number of ALSA frames in the output queue is not allowed to exceed the ALSA buffer size. The last requirement is the hardest to implement. Currently the number of URBs needed to fill a buffer cannot be determined in advance, because a buffer contains a fixed number of frames whereas the number of frames in an URB varies to match shifts in the device's clock rate. To solve this problem, the patch changes the logic for deciding how many packets an URB should contain. Rather than using as many as possible without exceeding an ALSA period boundary, now the driver uses only as many packets as needed to transfer a predetermined number of frames. As a result, unless the device's clock has an exceedingly variable rate, the number of URBs making up each period (and hence each buffer) will remain constant. The overall effect of the patch is that playback works better in low-latency settings. The user can still specify values for frames/period and periods/buffer that exceed the capabilities of the hardware, of course. But for values that are within those capabilities, the performance will be improved. For example, testing shows that a high-speed device can handle 32 frames/period and 3 periods/buffer at 48 KHz, whereas the current driver starts to get glitchy at 64 frames/period and 2 periods/buffer. A side effect of these changes is that the "nrpacks" module parameter is no longer used. The patch removes it. Signed-off-by: Alan Stern CC: Clemens Ladisch Tested-by: Daniel Mack Tested-by: Eldad Zack Signed-off-by: Takashi Iwai diff --git a/sound/usb/card.c b/sound/usb/card.c index 64952e2..d1f54df 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -79,7 +79,6 @@ static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card * /* Vendor/product IDs for this card */ static int vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; -static int nrpacks = 8; /* max. number of packets per urb */ static int device_setup[SNDRV_CARDS]; /* device parameter for this card */ static bool ignore_ctl_error; static bool autoclock = true; @@ -94,8 +93,6 @@ module_param_array(vid, int, NULL, 0444); MODULE_PARM_DESC(vid, "Vendor ID for the USB audio device."); module_param_array(pid, int, NULL, 0444); MODULE_PARM_DESC(pid, "Product ID for the USB audio device."); -module_param(nrpacks, int, 0644); -MODULE_PARM_DESC(nrpacks, "Max. number of packets per URB."); module_param_array(device_setup, int, NULL, 0444); MODULE_PARM_DESC(device_setup, "Specific device setup (if needed)."); module_param(ignore_ctl_error, bool, 0444); @@ -374,7 +371,6 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx, chip->dev = dev; chip->card = card; chip->setup = device_setup[idx]; - chip->nrpacks = nrpacks; chip->autoclock = autoclock; chip->probing = 1; @@ -756,10 +752,6 @@ static struct usb_driver usb_audio_driver = { static int __init snd_usb_audio_init(void) { - if (nrpacks < 1 || nrpacks > MAX_PACKS) { - printk(KERN_WARNING "invalid nrpacks value.\n"); - return -EINVAL; - } return usb_register(&usb_audio_driver); } diff --git a/sound/usb/card.h b/sound/usb/card.h index 5ecacaa..ca98a9b 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -2,11 +2,11 @@ #define __USBAUDIO_CARD_H #define MAX_NR_RATES 1024 -#define MAX_PACKS 20 +#define MAX_PACKS 6 /* per URB */ #define MAX_PACKS_HS (MAX_PACKS * 8) /* in high speed mode */ -#define MAX_URBS 8 +#define MAX_URBS 12 #define SYNC_URBS 4 /* always four urbs for sync */ -#define MAX_QUEUE 24 /* try not to exceed this queue length, in ms */ +#define MAX_QUEUE 18 /* try not to exceed this queue length, in ms */ struct audioformat { struct list_head list; @@ -87,6 +87,7 @@ struct snd_usb_endpoint { unsigned int phase; /* phase accumulator */ unsigned int maxpacksize; /* max packet size in bytes */ unsigned int maxframesize; /* max packet size in frames */ + unsigned int max_urb_frames; /* max URB size in frames */ unsigned int curpacksize; /* current packet size in bytes (for capture) */ unsigned int curframesize; /* current packet size in frames (for capture) */ unsigned int syncmaxsize; /* sync endpoint packet size */ @@ -116,6 +117,8 @@ struct snd_usb_substream { unsigned int channels_max; /* max channels in the all audiofmts */ unsigned int cur_rate; /* current rate (for hw_params callback) */ unsigned int period_bytes; /* current period bytes (for hw_params callback) */ + unsigned int period_frames; /* current frames per period */ + unsigned int buffer_periods; /* current periods per buffer */ unsigned int altset_idx; /* USB data format: index of alternate setting */ unsigned int txfr_quirk:1; /* allow sub-frame alignment */ unsigned int fmt_type; /* USB audio format type (1-3) */ @@ -125,6 +128,7 @@ struct snd_usb_substream { unsigned int hwptr_done; /* processed byte position in the buffer */ unsigned int transfer_done; /* processed frames since last period update */ + unsigned int frame_limit; /* limits number of packets in URB */ /* data and sync endpoints for this stream */ unsigned int ep_num; /* the endpoint number */ diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 93e970f..21dc642 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -574,11 +574,14 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep, snd_pcm_format_t pcm_format, unsigned int channels, unsigned int period_bytes, + unsigned int frames_per_period, + unsigned int periods_per_buffer, struct audioformat *fmt, struct snd_usb_endpoint *sync_ep) { - unsigned int maxsize, i, urb_packs, total_packs, packs_per_ms; - int is_playback = usb_pipeout(ep->pipe); + unsigned int maxsize, minsize, packs_per_ms, max_packs_per_urb; + unsigned int max_packs_per_period, urbs_per_period, urb_packs; + unsigned int max_urbs, i; int frame_bits = snd_pcm_format_physical_width(pcm_format) * channels; if (pcm_format == SNDRV_PCM_FORMAT_DSD_U16_LE && fmt->dsd_dop) { @@ -611,58 +614,67 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep, else ep->curpacksize = maxsize; - if (snd_usb_get_speed(ep->chip->dev) != USB_SPEED_FULL) + if (snd_usb_get_speed(ep->chip->dev) != USB_SPEED_FULL) { packs_per_ms = 8 >> ep->datainterval; - else - packs_per_ms = 1; - - if (is_playback && !snd_usb_endpoint_implicit_feedback_sink(ep)) { - urb_packs = max(ep->chip->nrpacks, 1); - urb_packs = min(urb_packs, (unsigned int) MAX_PACKS); + max_packs_per_urb = MAX_PACKS_HS; } else { - urb_packs = 1; + packs_per_ms = 1; + max_packs_per_urb = MAX_PACKS; } + if (sync_ep && !snd_usb_endpoint_implicit_feedback_sink(ep)) + max_packs_per_urb = min(max_packs_per_urb, + 1U << sync_ep->syncinterval); + max_packs_per_urb = max(1u, max_packs_per_urb >> ep->datainterval); - urb_packs *= packs_per_ms; + /* + * Capture endpoints need to use small URBs because there's no way + * to tell in advance where the next period will end, and we don't + * want the next URB to complete much after the period ends. + * + * Playback endpoints with implicit sync much use the same parameters + * as their corresponding capture endpoint. + */ + if (usb_pipein(ep->pipe) || + snd_usb_endpoint_implicit_feedback_sink(ep)) { - if (sync_ep && !snd_usb_endpoint_implicit_feedback_sink(ep)) - urb_packs = min(urb_packs, 1U << sync_ep->syncinterval); + /* make capture URBs <= 1 ms and smaller than a period */ + urb_packs = min(max_packs_per_urb, packs_per_ms); + while (urb_packs > 1 && urb_packs * maxsize >= period_bytes) + urb_packs >>= 1; + ep->nurbs = MAX_URBS; - /* decide how many packets to be used */ - if (is_playback && !snd_usb_endpoint_implicit_feedback_sink(ep)) { - unsigned int minsize, maxpacks; + /* + * Playback endpoints without implicit sync are adjusted so that + * a period fits as evenly as possible in the smallest number of + * URBs. The total number of URBs is adjusted to the size of the + * ALSA buffer, subject to the MAX_URBS and MAX_QUEUE limits. + */ + } else { /* determine how small a packet can be */ - minsize = (ep->freqn >> (16 - ep->datainterval)) - * (frame_bits >> 3); + minsize = (ep->freqn >> (16 - ep->datainterval)) * + (frame_bits >> 3); /* with sync from device, assume it can be 12% lower */ if (sync_ep) minsize -= minsize >> 3; minsize = max(minsize, 1u); - total_packs = (period_bytes + minsize - 1) / minsize; - /* we need at least two URBs for queueing */ - if (total_packs < 2) { - total_packs = 2; - } else { - /* and we don't want too long a queue either */ - maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2); - total_packs = min(total_packs, maxpacks); - } - } else { - while (urb_packs > 1 && urb_packs * maxsize >= period_bytes) - urb_packs >>= 1; - total_packs = MAX_URBS * urb_packs; - } - ep->nurbs = (total_packs + urb_packs - 1) / urb_packs; - if (ep->nurbs > MAX_URBS) { - /* too much... */ - ep->nurbs = MAX_URBS; - total_packs = MAX_URBS * urb_packs; - } else if (ep->nurbs < 2) { - /* too little - we need at least two packets - * to ensure contiguous playback/capture - */ - ep->nurbs = 2; + /* how many packets will contain an entire ALSA period? */ + max_packs_per_period = DIV_ROUND_UP(period_bytes, minsize); + + /* how many URBs will contain a period? */ + urbs_per_period = DIV_ROUND_UP(max_packs_per_period, + max_packs_per_urb); + /* how many packets are needed in each URB? */ + urb_packs = DIV_ROUND_UP(max_packs_per_period, urbs_per_period); + + /* limit the number of frames in a single URB */ + ep->max_urb_frames = DIV_ROUND_UP(frames_per_period, + urbs_per_period); + + /* try to use enough URBs to contain an entire ALSA buffer */ + max_urbs = min((unsigned) MAX_URBS, + MAX_QUEUE * packs_per_ms / urb_packs); + ep->nurbs = min(max_urbs, urbs_per_period * periods_per_buffer); } /* allocate and initialize data urbs */ @@ -670,8 +682,7 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep, struct snd_urb_ctx *u = &ep->urb[i]; u->index = i; u->ep = ep; - u->packets = (i + 1) * total_packs / ep->nurbs - - i * total_packs / ep->nurbs; + u->packets = urb_packs; u->buffer_size = maxsize * u->packets; if (fmt->fmt_type == UAC_FORMAT_TYPE_II) @@ -748,6 +759,8 @@ out_of_memory: * @pcm_format: the audio fomat. * @channels: the number of audio channels. * @period_bytes: the number of bytes in one alsa period. + * @period_frames: the number of frames in one alsa period. + * @buffer_periods: the number of periods in one alsa buffer. * @rate: the frame rate. * @fmt: the USB audio format information * @sync_ep: the sync endpoint to use, if any @@ -760,6 +773,8 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, snd_pcm_format_t pcm_format, unsigned int channels, unsigned int period_bytes, + unsigned int period_frames, + unsigned int buffer_periods, unsigned int rate, struct audioformat *fmt, struct snd_usb_endpoint *sync_ep) @@ -793,7 +808,8 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, switch (ep->type) { case SND_USB_ENDPOINT_TYPE_DATA: err = data_ep_set_params(ep, pcm_format, channels, - period_bytes, fmt, sync_ep); + period_bytes, period_frames, + buffer_periods, fmt, sync_ep); break; case SND_USB_ENDPOINT_TYPE_SYNC: err = sync_ep_set_params(ep, fmt); diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index 2287adf..3bd02f0 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -12,6 +12,8 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, snd_pcm_format_t pcm_format, unsigned int channels, unsigned int period_bytes, + unsigned int period_frames, + unsigned int buffer_periods, unsigned int rate, struct audioformat *fmt, struct snd_usb_endpoint *sync_ep); diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index b375d58..19e7995 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -595,6 +595,7 @@ static int configure_sync_endpoint(struct snd_usb_substream *subs) subs->pcm_format, subs->channels, subs->period_bytes, + 0, 0, subs->cur_rate, subs->cur_audiofmt, NULL); @@ -631,6 +632,7 @@ static int configure_sync_endpoint(struct snd_usb_substream *subs) subs->pcm_format, sync_fp->channels, sync_period_bytes, + 0, 0, subs->cur_rate, sync_fp, NULL); @@ -653,6 +655,8 @@ static int configure_endpoint(struct snd_usb_substream *subs) subs->pcm_format, subs->channels, subs->period_bytes, + subs->period_frames, + subs->buffer_periods, subs->cur_rate, subs->cur_audiofmt, subs->sync_endpoint); @@ -689,6 +693,8 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, subs->pcm_format = params_format(hw_params); subs->period_bytes = params_period_bytes(hw_params); + subs->period_frames = params_period_size(hw_params); + subs->buffer_periods = params_periods(hw_params); subs->channels = params_channels(hw_params); subs->cur_rate = params_rate(hw_params); @@ -1363,6 +1369,7 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, frames = 0; urb->number_of_packets = 0; spin_lock_irqsave(&subs->lock, flags); + subs->frame_limit += ep->max_urb_frames; for (i = 0; i < ctx->packets; i++) { if (ctx->packet_size[i]) counts = ctx->packet_size[i]; @@ -1377,6 +1384,7 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, subs->transfer_done += counts; if (subs->transfer_done >= runtime->period_size) { subs->transfer_done -= runtime->period_size; + subs->frame_limit = 0; period_elapsed = 1; if (subs->fmt_type == UAC_FORMAT_TYPE_II) { if (subs->transfer_done > 0) { @@ -1399,8 +1407,10 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, break; } } - if (period_elapsed && - !snd_usb_endpoint_implicit_feedback_sink(subs->data_endpoint)) /* finish at the period boundary */ + /* finish at the period boundary or after enough frames */ + if ((period_elapsed || + subs->transfer_done >= subs->frame_limit) && + !snd_usb_endpoint_implicit_feedback_sink(ep)) break; } bytes = frames * ep->stride; diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index caabe9b..5d2fe05 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -55,7 +55,6 @@ struct snd_usb_audio { struct list_head mixer_list; /* list of mixer interfaces */ int setup; /* from the 'device_setup' module param */ - int nrpacks; /* from the 'nrpacks' module param */ bool autoclock; /* from the 'autoclock' module param */ struct usb_host_interface *ctrl_intf; /* the audio control interface */ -- cgit v0.10.2 From e84841f9ba134d3aa4cad5c16d05712672583c76 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 14 Sep 2013 00:35:47 +0900 Subject: ALSA: firewire-lib: use inlune function to calculate frame bytes Calculating frame bytes can be replaced with inline function in include/sound/pcm.h. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index ea995af..4b08b25 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -268,7 +268,7 @@ static void amdtp_write_s32(struct amdtp_out_stream *s, channels = s->pcm_channels; src = (void *)runtime->dma_area + - s->pcm_buffer_pointer * (runtime->frame_bits / 8); + frames_to_bytes(runtime, s->pcm_buffer_pointer); remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; frame_step = s->data_block_quadlets - channels; @@ -294,7 +294,7 @@ static void amdtp_write_s16(struct amdtp_out_stream *s, channels = s->pcm_channels; src = (void *)runtime->dma_area + - s->pcm_buffer_pointer * (runtime->frame_bits / 8); + frames_to_bytes(runtime, s->pcm_buffer_pointer); remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; frame_step = s->data_block_quadlets - channels; -- cgit v0.10.2 From 28061758dc83df445a05af347b5ce55ccd968c03 Mon Sep 17 00:00:00 2001 From: Sudeep KarkadaNagesha Date: Tue, 13 Aug 2013 13:43:26 +0100 Subject: ARM/ARM64: arch_timer: add macros for bits in control register Add macros to describe the bitfields in the ARM architected timer control register to make code easy to understand. Reviewed-by: Lorenzo Pieralisi Reviewed-by: Will Deacon Acked-by: Catalin Marinas Acked-by: Olof Johansson Signed-off-by: Sudeep KarkadaNagesha diff --git a/arch/arm/include/asm/arch_timer.h b/arch/arm/include/asm/arch_timer.h index 5665134..d57e04d 100644 --- a/arch/arm/include/asm/arch_timer.h +++ b/arch/arm/include/asm/arch_timer.h @@ -93,8 +93,13 @@ static inline void arch_counter_set_user_access(void) asm volatile("mrc p15, 0, %0, c14, c1, 0" : "=r" (cntkctl)); - /* disable user access to everything */ - cntkctl &= ~((3 << 8) | (7 << 0)); + /* Disable user access to both physical/virtual counters/timers */ + /* Also disable virtual event stream */ + cntkctl &= ~(ARCH_TIMER_USR_PT_ACCESS_EN + | ARCH_TIMER_USR_VT_ACCESS_EN + | ARCH_TIMER_VIRT_EVT_EN + | ARCH_TIMER_USR_VCT_ACCESS_EN + | ARCH_TIMER_USR_PCT_ACCESS_EN); asm volatile("mcr p15, 0, %0, c14, c1, 0" : : "r" (cntkctl)); } diff --git a/arch/arm64/include/asm/arch_timer.h b/arch/arm64/include/asm/arch_timer.h index c9f1d28..2b9722f 100644 --- a/arch/arm64/include/asm/arch_timer.h +++ b/arch/arm64/include/asm/arch_timer.h @@ -96,12 +96,16 @@ static inline void arch_counter_set_user_access(void) { u32 cntkctl; - /* Disable user access to the timers and the physical counter. */ asm volatile("mrs %0, cntkctl_el1" : "=r" (cntkctl)); - cntkctl &= ~((3 << 8) | (1 << 0)); - /* Enable user access to the virtual counter and frequency. */ - cntkctl |= (1 << 1); + /* Disable user access to the timers and the physical counter */ + cntkctl &= ~(ARCH_TIMER_USR_PT_ACCESS_EN + | ARCH_TIMER_USR_VT_ACCESS_EN + | ARCH_TIMER_USR_PCT_ACCESS_EN); + + /* Enable user access to the virtual counter */ + cntkctl |= ARCH_TIMER_USR_VCT_ACCESS_EN; + asm volatile("msr cntkctl_el1, %0" : : "r" (cntkctl)); } diff --git a/include/clocksource/arm_arch_timer.h b/include/clocksource/arm_arch_timer.h index 93b7f96..8707dae 100644 --- a/include/clocksource/arm_arch_timer.h +++ b/include/clocksource/arm_arch_timer.h @@ -33,6 +33,14 @@ enum arch_timer_reg { #define ARCH_TIMER_MEM_PHYS_ACCESS 2 #define ARCH_TIMER_MEM_VIRT_ACCESS 3 +#define ARCH_TIMER_USR_PCT_ACCESS_EN (1 << 0) /* physical counter */ +#define ARCH_TIMER_USR_VCT_ACCESS_EN (1 << 1) /* virtual counter */ +#define ARCH_TIMER_VIRT_EVT_EN (1 << 2) +#define ARCH_TIMER_EVT_TRIGGER_SHIFT (4) +#define ARCH_TIMER_EVT_TRIGGER_MASK (0xF << ARCH_TIMER_EVT_TRIGGER_SHIFT) +#define ARCH_TIMER_USR_VT_ACCESS_EN (1 << 8) /* virtual timer registers */ +#define ARCH_TIMER_USR_PT_ACCESS_EN (1 << 9) /* physical timer registers */ + #ifdef CONFIG_ARM_ARCH_TIMER extern u32 arch_timer_get_rate(void); -- cgit v0.10.2 From e9faebc66ec74f1ab7f267d683b45e80faa69763 Mon Sep 17 00:00:00 2001 From: Sudeep KarkadaNagesha Date: Tue, 13 Aug 2013 14:30:32 +0100 Subject: ARM: arch_timer: add support to configure and enable event stream This patch adds support for configuring the event stream frequency and enabling it. It also adds the hwcaps definitions to the user to detect this event stream feature. Cc: Russell King Cc: Lorenzo Pieralisi Acked-by: Catalin Marinas Acked-by: Will Deacon Acked-by: Olof Johansson Signed-off-by: Sudeep KarkadaNagesha diff --git a/arch/arm/include/asm/arch_timer.h b/arch/arm/include/asm/arch_timer.h index d57e04d..0704e0c 100644 --- a/arch/arm/include/asm/arch_timer.h +++ b/arch/arm/include/asm/arch_timer.h @@ -87,11 +87,21 @@ static inline u64 arch_counter_get_cntvct(void) return cval; } -static inline void arch_counter_set_user_access(void) +static inline u32 arch_timer_get_cntkctl(void) { u32 cntkctl; - asm volatile("mrc p15, 0, %0, c14, c1, 0" : "=r" (cntkctl)); + return cntkctl; +} + +static inline void arch_timer_set_cntkctl(u32 cntkctl) +{ + asm volatile("mcr p15, 0, %0, c14, c1, 0" : : "r" (cntkctl)); +} + +static inline void arch_counter_set_user_access(void) +{ + u32 cntkctl = arch_timer_get_cntkctl(); /* Disable user access to both physical/virtual counters/timers */ /* Also disable virtual event stream */ @@ -100,9 +110,20 @@ static inline void arch_counter_set_user_access(void) | ARCH_TIMER_VIRT_EVT_EN | ARCH_TIMER_USR_VCT_ACCESS_EN | ARCH_TIMER_USR_PCT_ACCESS_EN); + arch_timer_set_cntkctl(cntkctl); +} - asm volatile("mcr p15, 0, %0, c14, c1, 0" : : "r" (cntkctl)); +static inline void arch_timer_evtstrm_enable(int divider) +{ + u32 cntkctl = arch_timer_get_cntkctl(); + cntkctl &= ~ARCH_TIMER_EVT_TRIGGER_MASK; + /* Set the divider and enable virtual event stream */ + cntkctl |= (divider << ARCH_TIMER_EVT_TRIGGER_SHIFT) + | ARCH_TIMER_VIRT_EVT_EN; + arch_timer_set_cntkctl(cntkctl); + elf_hwcap |= HWCAP_EVTSTRM; } + #endif #endif diff --git a/arch/arm/include/uapi/asm/hwcap.h b/arch/arm/include/uapi/asm/hwcap.h index 6d34d08..7dcc10d 100644 --- a/arch/arm/include/uapi/asm/hwcap.h +++ b/arch/arm/include/uapi/asm/hwcap.h @@ -26,5 +26,6 @@ #define HWCAP_VFPD32 (1 << 19) /* set if VFP has 32 regs (not 16) */ #define HWCAP_IDIV (HWCAP_IDIVA | HWCAP_IDIVT) #define HWCAP_LPAE (1 << 20) +#define HWCAP_EVTSTRM (1 << 21) #endif /* _UAPI__ASMARM_HWCAP_H */ diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index 0e1e2b3..5d65438 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -975,6 +975,7 @@ static const char *hwcap_str[] = { "idivt", "vfpd32", "lpae", + "evtstrm", NULL }; -- cgit v0.10.2 From 46efe547aca8498d51b64460c02366ae4032ca32 Mon Sep 17 00:00:00 2001 From: Sudeep KarkadaNagesha Date: Tue, 13 Aug 2013 15:57:53 +0100 Subject: ARM64: arch_timer: add support to configure and enable event stream This patch adds support for configuring the event stream frequency and enabling it. It also adds the hwcaps as well as compat-specific definitions to the user to detect this event stream feature. Cc: Lorenzo Pieralisi Cc: Will Deacon Acked-by: Catalin Marinas Acked-by: Olof Johansson Signed-off-by: Sudeep KarkadaNagesha diff --git a/arch/arm64/include/asm/arch_timer.h b/arch/arm64/include/asm/arch_timer.h index 2b9722f..9400596 100644 --- a/arch/arm64/include/asm/arch_timer.h +++ b/arch/arm64/include/asm/arch_timer.h @@ -92,21 +92,47 @@ static inline u32 arch_timer_get_cntfrq(void) return val; } -static inline void arch_counter_set_user_access(void) +static inline u32 arch_timer_get_cntkctl(void) { u32 cntkctl; - asm volatile("mrs %0, cntkctl_el1" : "=r" (cntkctl)); + return cntkctl; +} + +static inline void arch_timer_set_cntkctl(u32 cntkctl) +{ + asm volatile("msr cntkctl_el1, %0" : : "r" (cntkctl)); +} + +static inline void arch_counter_set_user_access(void) +{ + u32 cntkctl = arch_timer_get_cntkctl(); /* Disable user access to the timers and the physical counter */ + /* Also disable virtual event stream */ cntkctl &= ~(ARCH_TIMER_USR_PT_ACCESS_EN | ARCH_TIMER_USR_VT_ACCESS_EN + | ARCH_TIMER_VIRT_EVT_EN | ARCH_TIMER_USR_PCT_ACCESS_EN); /* Enable user access to the virtual counter */ cntkctl |= ARCH_TIMER_USR_VCT_ACCESS_EN; - asm volatile("msr cntkctl_el1, %0" : : "r" (cntkctl)); + arch_timer_set_cntkctl(cntkctl); +} + +static inline void arch_timer_evtstrm_enable(int divider) +{ + u32 cntkctl = arch_timer_get_cntkctl(); + cntkctl &= ~ARCH_TIMER_EVT_TRIGGER_MASK; + /* Set the divider and enable virtual event stream */ + cntkctl |= (divider << ARCH_TIMER_EVT_TRIGGER_SHIFT) + | ARCH_TIMER_VIRT_EVT_EN; + arch_timer_set_cntkctl(cntkctl); + elf_hwcap |= HWCAP_EVTSTRM; +#ifdef CONFIG_COMPAT + compat_elf_hwcap |= COMPAT_HWCAP_EVTSTRM; +#endif } static inline u64 arch_counter_get_cntvct(void) diff --git a/arch/arm64/include/asm/hwcap.h b/arch/arm64/include/asm/hwcap.h index e2950b0..6cddbb0 100644 --- a/arch/arm64/include/asm/hwcap.h +++ b/arch/arm64/include/asm/hwcap.h @@ -30,6 +30,7 @@ #define COMPAT_HWCAP_IDIVA (1 << 17) #define COMPAT_HWCAP_IDIVT (1 << 18) #define COMPAT_HWCAP_IDIV (COMPAT_HWCAP_IDIVA|COMPAT_HWCAP_IDIVT) +#define COMPAT_HWCAP_EVTSTRM (1 << 21) #ifndef __ASSEMBLY__ /* @@ -37,11 +38,11 @@ * instruction set this cpu supports. */ #define ELF_HWCAP (elf_hwcap) -#define COMPAT_ELF_HWCAP (COMPAT_HWCAP_HALF|COMPAT_HWCAP_THUMB|\ - COMPAT_HWCAP_FAST_MULT|COMPAT_HWCAP_EDSP|\ - COMPAT_HWCAP_TLS|COMPAT_HWCAP_VFP|\ - COMPAT_HWCAP_VFPv3|COMPAT_HWCAP_VFPv4|\ - COMPAT_HWCAP_NEON|COMPAT_HWCAP_IDIV) + +#ifdef CONFIG_COMPAT +#define COMPAT_ELF_HWCAP (compat_elf_hwcap) +extern unsigned int compat_elf_hwcap; +#endif extern unsigned long elf_hwcap; #endif diff --git a/arch/arm64/include/uapi/asm/hwcap.h b/arch/arm64/include/uapi/asm/hwcap.h index eea4975..9b12476 100644 --- a/arch/arm64/include/uapi/asm/hwcap.h +++ b/arch/arm64/include/uapi/asm/hwcap.h @@ -21,6 +21,7 @@ */ #define HWCAP_FP (1 << 0) #define HWCAP_ASIMD (1 << 1) +#define HWCAP_EVTSTRM (1 << 2) #endif /* _UAPI__ASM_HWCAP_H */ diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index 055cfb8..d355b7b 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -60,6 +60,16 @@ EXPORT_SYMBOL(processor_id); unsigned long elf_hwcap __read_mostly; EXPORT_SYMBOL_GPL(elf_hwcap); +#ifdef CONFIG_COMPAT +#define COMPAT_ELF_HWCAP_DEFAULT \ + (COMPAT_HWCAP_HALF|COMPAT_HWCAP_THUMB|\ + COMPAT_HWCAP_FAST_MULT|COMPAT_HWCAP_EDSP|\ + COMPAT_HWCAP_TLS|COMPAT_HWCAP_VFP|\ + COMPAT_HWCAP_VFPv3|COMPAT_HWCAP_VFPv4|\ + COMPAT_HWCAP_NEON|COMPAT_HWCAP_IDIV) +unsigned int compat_elf_hwcap __read_mostly = COMPAT_ELF_HWCAP_DEFAULT; +#endif + static const char *cpu_name; static const char *machine_name; phys_addr_t __fdt_pointer __initdata; @@ -304,6 +314,7 @@ subsys_initcall(topology_init); static const char *hwcap_str[] = { "fp", "asimd", + "evtstrm", NULL }; -- cgit v0.10.2 From 037f637767a82907efedda78d3ff405c34020075 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Fri, 23 Aug 2013 15:32:29 +0100 Subject: drivers: clocksource: add support for ARM architected timer event stream The ARM architected timer can generate events (used for waking up CPUs executing the wfe instruction) at a frequency represented as a power-of-2 divisor of the clock rate. An event stream might be used: - To implement wfe-based timeouts for userspace locking implementations. - To impose a timeout on a wfe for safeguarding against any programming error in case an expected event is not generated. This patch computes the event stream frequency aiming for a period of 100us between events. It uses ARM/ARM64 specific backends to configure and enable the event stream. Cc: Lorenzo Pieralisi Reviewed-by: Catalin Marinas Acked-by: Olof Johansson Signed-off-by: Will Deacon [sudeep: moving ARM/ARM64 changes into separate patches and adding Kconfig option] Signed-off-by: Sudeep KarkadaNagesha diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 41c6946..559d803 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -74,6 +74,21 @@ config ARM_ARCH_TIMER bool select CLKSRC_OF if OF +config ARM_ARCH_TIMER_EVTSTREAM + bool "Support for ARM architected timer event stream generation" + default y if ARM_ARCH_TIMER + help + This option enables support for event stream generation based on + the ARM architected timer. It is used for waking up CPUs executing + the wfe instruction at a frequency represented as a power-of-2 + divisor of the clock rate. + The main use of the event stream is wfe-based timeouts of userspace + locking implementations. It might also be useful for imposing timeout + on wfe to safeguard against any programming errors in case an expected + event is not generated. + This must be disabled for hardware validation purposes to detect any + hardware anomalies of missing events. + config ARM_GLOBAL_TIMER bool select CLKSRC_OF if OF diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index fbd9ccd..105f8ff 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -294,6 +294,19 @@ static void __arch_timer_setup(unsigned type, clockevents_config_and_register(clk, arch_timer_rate, 0xf, 0x7fffffff); } +static void arch_timer_configure_evtstream(void) +{ + int evt_stream_div, pos; + + /* Find the closest power of two to the divisor */ + evt_stream_div = arch_timer_rate / ARCH_TIMER_EVT_STREAM_FREQ; + pos = fls(evt_stream_div); + if (pos > 1 && !(evt_stream_div & (1 << (pos - 2)))) + pos--; + /* enable event stream */ + arch_timer_evtstrm_enable(min(pos, 15)); +} + static int arch_timer_setup(struct clock_event_device *clk) { __arch_timer_setup(ARCH_CP15_TIMER, clk); @@ -307,6 +320,8 @@ static int arch_timer_setup(struct clock_event_device *clk) } arch_counter_set_user_access(); + if (IS_ENABLED(CONFIG_ARM_ARCH_TIMER_EVTSTREAM)) + arch_timer_configure_evtstream(); return 0; } diff --git a/include/clocksource/arm_arch_timer.h b/include/clocksource/arm_arch_timer.h index 8707dae..6d26b40 100644 --- a/include/clocksource/arm_arch_timer.h +++ b/include/clocksource/arm_arch_timer.h @@ -41,6 +41,8 @@ enum arch_timer_reg { #define ARCH_TIMER_USR_VT_ACCESS_EN (1 << 8) /* virtual timer registers */ #define ARCH_TIMER_USR_PT_ACCESS_EN (1 << 9) /* physical timer registers */ +#define ARCH_TIMER_EVT_STREAM_FREQ 10000 /* 100us */ + #ifdef CONFIG_ARM_ARCH_TIMER extern u32 arch_timer_get_rate(void); -- cgit v0.10.2 From 346e7480f1d4740b3d798da60f83f087ea6488b4 Mon Sep 17 00:00:00 2001 From: Sudeep KarkadaNagesha Date: Fri, 23 Aug 2013 15:53:15 +0100 Subject: drivers: clocksource: add CPU PM notifier for ARM architected timer Few control settings done in architected timer as part of initialisation can be lost when CPU enters deeper power states. They need to be restored when the CPU is (warm)reset again. This patch adds CPU PM notifiers to save the counter control register when entering low power modes and restore it when CPU exits low power. Reviewed-by: Catalin Marinas Reviewed-by: Lorenzo Pieralisi Reviewed-by: Will Deacon Acked-by: Olof Johansson Signed-off-by: Sudeep KarkadaNagesha diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 105f8ff..9338f1a 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -475,6 +476,33 @@ static struct notifier_block arch_timer_cpu_nb = { .notifier_call = arch_timer_cpu_notify, }; +#ifdef CONFIG_CPU_PM +static unsigned int saved_cntkctl; +static int arch_timer_cpu_pm_notify(struct notifier_block *self, + unsigned long action, void *hcpu) +{ + if (action == CPU_PM_ENTER) + saved_cntkctl = arch_timer_get_cntkctl(); + else if (action == CPU_PM_ENTER_FAILED || action == CPU_PM_EXIT) + arch_timer_set_cntkctl(saved_cntkctl); + return NOTIFY_OK; +} + +static struct notifier_block arch_timer_cpu_pm_notifier = { + .notifier_call = arch_timer_cpu_pm_notify, +}; + +static int __init arch_timer_cpu_pm_init(void) +{ + return cpu_pm_register_notifier(&arch_timer_cpu_pm_notifier); +} +#else +static int __init arch_timer_cpu_pm_init(void) +{ + return 0; +} +#endif + static int __init arch_timer_register(void) { int err; @@ -514,11 +542,17 @@ static int __init arch_timer_register(void) if (err) goto out_free_irq; + err = arch_timer_cpu_pm_init(); + if (err) + goto out_unreg_notify; + /* Immediately configure the timer on the boot CPU */ arch_timer_setup(this_cpu_ptr(arch_timer_evt)); return 0; +out_unreg_notify: + unregister_cpu_notifier(&arch_timer_cpu_nb); out_free_irq: if (arch_timer_use_virtual) free_percpu_irq(arch_timer_ppi[VIRT_PPI], arch_timer_evt); -- cgit v0.10.2 From 83cbe35b874621a23ca468621c0d833b76a1b8de Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 25 Sep 2013 19:22:54 +0100 Subject: ASoC: sn95031: Convert to regmap This moves us towards being able to remove the duplicated register I/O functionality in ASoC. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c index dba26e63..13045f2 100644 --- a/sound/soc/codecs/sn95031.c +++ b/sound/soc/codecs/sn95031.c @@ -164,30 +164,28 @@ static unsigned int sn95031_get_mic_bias(struct snd_soc_codec *codec) } /*end - adc helper functions */ -static inline unsigned int sn95031_read(struct snd_soc_codec *codec, - unsigned int reg) +static int sn95031_read(void *ctx, unsigned int reg, unsigned int *val) { u8 value = 0; int ret; ret = intel_scu_ipc_ioread8(reg, &value); - if (ret) - pr_err("read of %x failed, err %d\n", reg, ret); - return value; + if (ret == 0) + *val = value; + return ret; } -static inline int sn95031_write(struct snd_soc_codec *codec, - unsigned int reg, unsigned int value) +static int sn95031_write(void *ctx, unsigned int reg, unsigned int value) { - int ret; - - ret = intel_scu_ipc_iowrite8(reg, value); - if (ret) - pr_err("write of %x failed, err %d\n", reg, ret); - return ret; + return intel_scu_ipc_iowrite8(reg, value); } +static const struct regmap_config sn95031_regmap = { + .reg_read = sn95031_read, + .reg_write = sn95031_write, +}; + static int sn95031_set_vaud_bias(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { @@ -827,6 +825,8 @@ static int sn95031_codec_probe(struct snd_soc_codec *codec) { pr_debug("codec_probe called\n"); + snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP); + /* PCM interface config * This sets the pcm rx slot conguration to max 6 slots * for max 4 dais (2 stereo and 2 mono) @@ -886,8 +886,6 @@ static int sn95031_codec_remove(struct snd_soc_codec *codec) static struct snd_soc_codec_driver sn95031_codec = { .probe = sn95031_codec_probe, .remove = sn95031_codec_remove, - .read = sn95031_read, - .write = sn95031_write, .set_bias_level = sn95031_set_vaud_bias, .idle_bias_off = true, .dapm_widgets = sn95031_dapm_widgets, @@ -898,7 +896,14 @@ static struct snd_soc_codec_driver sn95031_codec = { static int sn95031_device_probe(struct platform_device *pdev) { + struct regmap *regmap; + pr_debug("codec device probe called for %s\n", dev_name(&pdev->dev)); + + regmap = devm_regmap_init(&pdev->dev, NULL, NULL, &sn95031_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + return snd_soc_register_codec(&pdev->dev, &sn95031_codec, sn95031_dais, ARRAY_SIZE(sn95031_dais)); } -- cgit v0.10.2 From 752b776435cb35da27a0bbec8deecc33b3461288 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 25 Sep 2013 11:36:26 +0100 Subject: ASoC: tlv320aic32x4: Move GPIO acquisition to I2C probe This is more idiomatic and interacts better with deferred probe. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 2ed57d4..cf70bf8 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -617,16 +617,11 @@ static int aic32x4_probe(struct snd_soc_codec *codec) { struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); u32 tmp_reg; - int ret; codec->hw_write = (hw_write_t) i2c_master_send; codec->control_data = aic32x4->control_data; if (aic32x4->rstn_gpio >= 0) { - ret = devm_gpio_request_one(codec->dev, aic32x4->rstn_gpio, - GPIOF_OUT_INIT_LOW, "tlv320aic32x4 rstn"); - if (ret != 0) - return ret; ndelay(10); gpio_set_value(aic32x4->rstn_gpio, 1); } @@ -735,6 +730,13 @@ static int aic32x4_i2c_probe(struct i2c_client *i2c, aic32x4->rstn_gpio = -1; } + if (aic32x4->rstn_gpio >= 0) { + ret = devm_gpio_request_one(&i2c->dev, aic32x4->rstn_gpio, + GPIOF_OUT_INIT_LOW, "tlv320aic32x4 rstn"); + if (ret != 0) + return ret; + } + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_aic32x4, &aic32x4_dai, 1); return ret; -- cgit v0.10.2 From 4d208ca429ad424595fd08c0cca323605ebfc38b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 25 Sep 2013 11:37:53 +0100 Subject: ASoC: tlv320aic32x4: Convert to direct regmap API usage This moves us towards being able to remove the duplicate register I/O functionality in ASoC and saves some code. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index cf70bf8..18cdcca 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -60,9 +60,8 @@ struct aic32x4_rate_divs { }; struct aic32x4_priv { + struct regmap *regmap; u32 sysclk; - u8 page_no; - void *control_data; u32 power_cfg; u32 micpga_routing; bool swapdacs; @@ -262,67 +261,25 @@ static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = { {"Right ADC", NULL, "Right Input Mixer"}, }; -static inline int aic32x4_change_page(struct snd_soc_codec *codec, - unsigned int new_page) -{ - struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); - u8 data[2]; - int ret; - - data[0] = 0x00; - data[1] = new_page & 0xff; - - ret = codec->hw_write(codec->control_data, data, 2); - if (ret == 2) { - aic32x4->page_no = new_page; - return 0; - } else { - return ret; - } -} - -static int aic32x4_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int val) -{ - struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); - unsigned int page = reg / 128; - unsigned int fixed_reg = reg % 128; - u8 data[2]; - int ret; - - /* A write to AIC32X4_PSEL is really a non-explicit page change */ - if (reg == AIC32X4_PSEL) - return aic32x4_change_page(codec, val); - - if (aic32x4->page_no != page) { - ret = aic32x4_change_page(codec, page); - if (ret != 0) - return ret; - } +static const struct regmap_range_cfg aic32x4_regmap_pages[] = { + { + .selector_reg = 0, + .selector_mask = 0xff, + .window_start = 0, + .window_len = 128, + .range_min = AIC32X4_PAGE1, + .range_max = AIC32X4_PAGE1 + 127, + }, +}; - data[0] = fixed_reg & 0xff; - data[1] = val & 0xff; +static const struct regmap_config aic32x4_regmap = { + .reg_bits = 8, + .val_bits = 8, - if (codec->hw_write(codec->control_data, data, 2) == 2) - return 0; - else - return -EIO; -} - -static unsigned int aic32x4_read(struct snd_soc_codec *codec, unsigned int reg) -{ - struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); - unsigned int page = reg / 128; - unsigned int fixed_reg = reg % 128; - int ret; - - if (aic32x4->page_no != page) { - ret = aic32x4_change_page(codec, page); - if (ret != 0) - return ret; - } - return i2c_smbus_read_byte_data(codec->control_data, fixed_reg & 0xff); -} + .max_register = AIC32X4_RMICPGAVOL, + .ranges = aic32x4_regmap_pages, + .num_ranges = ARRAY_SIZE(aic32x4_regmap_pages), +}; static inline int aic32x4_get_divs(int mclk, int rate) { @@ -618,8 +575,7 @@ static int aic32x4_probe(struct snd_soc_codec *codec) struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); u32 tmp_reg; - codec->hw_write = (hw_write_t) i2c_master_send; - codec->control_data = aic32x4->control_data; + snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP); if (aic32x4->rstn_gpio >= 0) { ndelay(10); @@ -687,8 +643,6 @@ static int aic32x4_remove(struct snd_soc_codec *codec) } static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = { - .read = aic32x4_read, - .write = aic32x4_write, .probe = aic32x4_probe, .remove = aic32x4_remove, .suspend = aic32x4_suspend, @@ -715,7 +669,10 @@ static int aic32x4_i2c_probe(struct i2c_client *i2c, if (aic32x4 == NULL) return -ENOMEM; - aic32x4->control_data = i2c; + aic32x4->regmap = devm_regmap_init_i2c(i2c, &aic32x4_regmap); + if (IS_ERR(aic32x4->regmap)) + return PTR_ERR(aic32x4->regmap); + i2c_set_clientdata(i2c, aic32x4); if (pdata) { -- cgit v0.10.2 From 937433c2502f663e5a0e8804462bc38c41b9021f Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 12 Sep 2013 14:28:35 +0200 Subject: regulator: da9210: add Device Tree support This patch adds basic Device Tree support to the da9210 regulator driver - with no special properties, since also driver's platform data only contains standard regulator initialisation parameters. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/regulator/da9210.txt b/Documentation/devicetree/bindings/regulator/da9210.txt new file mode 100644 index 0000000..f120f22 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/da9210.txt @@ -0,0 +1,21 @@ +* Dialog Semiconductor DA9210 Voltage Regulator + +Required properties: + +- compatible: must be "diasemi,da9210" +- reg: the i2c slave address of the regulator. It should be 0x68. + +Any standard regulator properties can be used to configure the single da9210 +DCDC. + +Example: + + da9210@68 { + compatible = "diasemi,da9210"; + reg = <0x68>; + + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <1000000>; + regulator-boot-on; + regulator-always-on; + }; diff --git a/drivers/regulator/da9210-regulator.c b/drivers/regulator/da9210-regulator.c index f0fe54b..f7ccff1 100644 --- a/drivers/regulator/da9210-regulator.c +++ b/drivers/regulator/da9210-regulator.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "da9210-regulator.h" @@ -126,7 +127,8 @@ static int da9210_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct da9210 *chip; - struct da9210_pdata *pdata = i2c->dev.platform_data; + struct device *dev = &i2c->dev; + struct da9210_pdata *pdata = dev_get_platdata(dev); struct regulator_dev *rdev = NULL; struct regulator_config config = { }; int error; @@ -147,10 +149,11 @@ static int da9210_i2c_probe(struct i2c_client *i2c, } config.dev = &i2c->dev; - if (pdata) - config.init_data = &pdata->da9210_constraints; + config.init_data = pdata ? &pdata->da9210_constraints : + of_get_regulator_init_data(dev, dev->of_node); config.driver_data = chip; config.regmap = chip->regmap; + config.of_node = dev->of_node; rdev = regulator_register(&da9210_reg, &config); if (IS_ERR(rdev)) { -- cgit v0.10.2 From ce3d060990cd799cef4eeffc290090fb5da15e94 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Thu, 19 Sep 2013 11:20:43 +0200 Subject: ASoc: kirkwood: Extend the min and max number of bytes per period This patch extends the min and max number of bytes per period. It mainly permits to reduce the sound delay in MIDI real-time playing. Signed-off-by: Jean-Francois Moine Signed-off-by: Mark Brown diff --git a/sound/soc/kirkwood/kirkwood.h b/sound/soc/kirkwood/kirkwood.h index f8e1ccc..bf23afb 100644 --- a/sound/soc/kirkwood/kirkwood.h +++ b/sound/soc/kirkwood/kirkwood.h @@ -123,8 +123,8 @@ /* need to find where they come from */ #define KIRKWOOD_SND_MIN_PERIODS 8 #define KIRKWOOD_SND_MAX_PERIODS 16 -#define KIRKWOOD_SND_MIN_PERIOD_BYTES 0x4000 -#define KIRKWOOD_SND_MAX_PERIOD_BYTES 0x4000 +#define KIRKWOOD_SND_MIN_PERIOD_BYTES 0x800 +#define KIRKWOOD_SND_MAX_PERIOD_BYTES 0x8000 #define KIRKWOOD_SND_MAX_BUFFER_BYTES (KIRKWOOD_SND_MAX_PERIOD_BYTES \ * KIRKWOOD_SND_MAX_PERIODS) -- cgit v0.10.2 From 3a429eea10ded31d2ff088432d02072165a099f1 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 20 Sep 2013 15:54:54 -0700 Subject: ASoC: atmel-pcm: fix warning i386 allmodconfig: sound/soc/atmel/atmel-pcm.c: In function 'atmel_pcm_preallocate_dma_buffer': sound/soc/atmel/atmel-pcm.c:52: warning: cast to pointer from integer of different size Signed-off-by: Andrew Morton Signed-off-by: Mark Brown diff --git a/sound/soc/atmel/atmel-pcm.c b/sound/soc/atmel/atmel-pcm.c index 3109db7..612e580 100644 --- a/sound/soc/atmel/atmel-pcm.c +++ b/sound/soc/atmel/atmel-pcm.c @@ -50,7 +50,7 @@ static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, buf->area = dma_alloc_coherent(pcm->card->dev, size, &buf->addr, GFP_KERNEL); pr_debug("atmel-pcm: alloc dma buffer: area=%p, addr=%p, size=%zu\n", - (void *)buf->area, (void *)buf->addr, size); + (void *)buf->area, (void *)(long)buf->addr, size); if (!buf->area) return -ENOMEM; -- cgit v0.10.2 From d60336e2f136287de821901d4a1b56179a0f7b69 Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Mon, 23 Sep 2013 11:36:21 +0800 Subject: ASoC: fsl_ssi: let check zero instead of check NO_IRQ NO_IRQ may be defined as '(unsigned int) -1' in some architectures (arm, sh ...), and either may not be defined in some architectures which can enable SND_SOC_FSL_SSI (e.g. allmodconfig for arc). When irq_of_parse_and_map() fails, it will always return 0, so need check zero instead of NO_IRQ, or will cause compiling issue or run time bug in some architectures. Signed-off-by: Chen Gang Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index cdbb641..35e2773 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -929,7 +929,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) ssi_private->ssi_phys = res.start; ssi_private->irq = irq_of_parse_and_map(np, 0); - if (ssi_private->irq == NO_IRQ) { + if (!ssi_private->irq) { dev_err(&pdev->dev, "no irq for node %s\n", np->full_name); return -ENXIO; } -- cgit v0.10.2 From 247263dba208fab08c7f49900a9c79adaae1176e Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 24 Sep 2013 13:23:00 +0900 Subject: spi: bcm2835: use devm_spi_register_master() Use devm_spi_register_master() to make cleanup paths simpler, and remove a duplicate put. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 52c8148..4c33214 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -358,7 +358,7 @@ static int bcm2835_spi_probe(struct platform_device *pdev) bcm2835_wr(bs, BCM2835_SPI_CS, BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX); - err = spi_register_master(master); + err = devm_spi_register_master(&pdev->dev, master); if (err) { dev_err(&pdev->dev, "could not register SPI master: %d\n", err); goto out_free_irq; @@ -381,14 +381,12 @@ static int bcm2835_spi_remove(struct platform_device *pdev) struct bcm2835_spi *bs = spi_master_get_devdata(master); free_irq(bs->irq, master); - spi_unregister_master(master); /* Clear FIFOs, and disable the HW block */ bcm2835_wr(bs, BCM2835_SPI_CS, BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX); clk_disable_unprepare(bs->clk); - spi_master_put(master); return 0; } -- cgit v0.10.2 From bca76931b934ad323c56d9aab7a74139306d18bf Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 24 Sep 2013 13:24:57 +0900 Subject: spi: bcm63xx: use devm_spi_register_master() Use devm_spi_register_master() to make cleanup paths simpler, and remove a duplicate put. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index 536b0e3..80d56b2 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -412,7 +412,7 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS); /* register and we are done */ - ret = spi_register_master(master); + ret = devm_spi_register_master(dev, master); if (ret) { dev_err(dev, "spi register failed\n"); goto out_clk_disable; @@ -438,8 +438,6 @@ static int bcm63xx_spi_remove(struct platform_device *pdev) struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); struct bcm63xx_spi *bs = spi_master_get_devdata(master); - spi_unregister_master(master); - /* reset spi block */ bcm_spi_writeb(bs, 0, SPI_INT_MASK); @@ -447,8 +445,6 @@ static int bcm63xx_spi_remove(struct platform_device *pdev) clk_disable_unprepare(bs->clk); clk_put(bs->clk); - spi_master_put(master); - return 0; } -- cgit v0.10.2 From 6221df6d88b847da0803b2ced7826f005e62c837 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 24 Sep 2013 13:26:30 +0900 Subject: spi: bfin-v3: use devm_spi_register_master() Use devm_spi_register_master() to make cleanup paths simpler. Signed-off-by: Jingoo Han Acked-by: Scott Jiang Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-bfin-v3.c b/drivers/spi/spi-bfin-v3.c index f4bf813..8f85988 100644 --- a/drivers/spi/spi-bfin-v3.c +++ b/drivers/spi/spi-bfin-v3.c @@ -867,7 +867,7 @@ static int bfin_spi_probe(struct platform_device *pdev) tasklet_init(&drv_data->pump_transfers, bfin_spi_pump_transfers, (unsigned long)drv_data); /* register with the SPI framework */ - ret = spi_register_master(master); + ret = devm_spi_register_master(dev, master); if (ret) { dev_err(dev, "can not register spi master\n"); goto err_free_peripheral; @@ -898,7 +898,6 @@ static int bfin_spi_remove(struct platform_device *pdev) free_dma(drv_data->rx_dma); free_dma(drv_data->tx_dma); - spi_unregister_master(drv_data->master); return 0; } -- cgit v0.10.2 From c493fc4bbd4f27b1a8a141648df866c386441b70 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 24 Sep 2013 13:27:48 +0900 Subject: spi: clps711x: use devm_spi_register_master() Use devm_spi_register_master() to make cleanup paths simpler. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-clps711x.c b/drivers/spi/spi-clps711x.c index b597a92..f2b1a1c 100644 --- a/drivers/spi/spi-clps711x.c +++ b/drivers/spi/spi-clps711x.c @@ -230,7 +230,7 @@ static int spi_clps711x_probe(struct platform_device *pdev) goto clk_out; } - ret = spi_register_master(master); + ret = devm_spi_register_master(&pdev->dev, master); if (!ret) { dev_info(&pdev->dev, "SPI bus driver initialized. Master clock %u Hz\n", @@ -261,8 +261,6 @@ static int spi_clps711x_remove(struct platform_device *pdev) if (gpio_is_valid(hw->chipselect[i])) gpio_free(hw->chipselect[i]); - spi_unregister_master(master); - return 0; } -- cgit v0.10.2 From 434eaf3b231125c73450bbc6ebe2396b9b63aa2f Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 24 Sep 2013 13:30:41 +0900 Subject: spi: ep93xx: use devm_spi_register_master() Use devm_spi_register_master() to make cleanup paths simpler. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c index d22c00a..486fb6d 100644 --- a/drivers/spi/spi-ep93xx.c +++ b/drivers/spi/spi-ep93xx.c @@ -942,7 +942,7 @@ static int ep93xx_spi_probe(struct platform_device *pdev) /* make sure that the hardware is disabled */ ep93xx_spi_write_u8(espi, SSPCR1, 0); - error = spi_register_master(master); + error = devm_spi_register_master(&pdev->dev, master); if (error) { dev_err(&pdev->dev, "failed to register SPI master\n"); goto fail_free_dma; @@ -968,7 +968,6 @@ static int ep93xx_spi_remove(struct platform_device *pdev) ep93xx_spi_release_dma(espi); - spi_unregister_master(master); return 0; } -- cgit v0.10.2 From eaa24297846bcdd98cefd52937ed88046a121ebc Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 24 Sep 2013 13:31:50 +0900 Subject: spi: mpc512x: use devm_spi_register_master() Use devm_spi_register_master() to make cleanup paths simpler, and remove a duplicate put. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-mpc512x-psc.c b/drivers/spi/spi-mpc512x-psc.c index dbc5e99..b4426dd 100644 --- a/drivers/spi/spi-mpc512x-psc.c +++ b/drivers/spi/spi-mpc512x-psc.c @@ -534,7 +534,7 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr, if (ret < 0) goto free_clock; - ret = spi_register_master(master); + ret = devm_spi_register_master(dev, master); if (ret < 0) goto free_clock; @@ -557,12 +557,10 @@ static int mpc512x_psc_spi_do_remove(struct device *dev) struct spi_master *master = spi_master_get(dev_get_drvdata(dev)); struct mpc512x_psc_spi *mps = spi_master_get_devdata(master); - spi_unregister_master(master); clk_disable_unprepare(mps->clk_mclk); free_irq(mps->irq, mps); if (mps->psc) iounmap(mps->psc); - spi_master_put(master); return 0; } -- cgit v0.10.2 From 33e195acf2682627d9f4d9fb9c30a5dd5323bc5c Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 24 Sep 2013 13:32:56 +0900 Subject: spi: mxs: use devm_spi_register_master() Use devm_spi_register_master() to make cleanup paths simpler, and remove a duplicate put. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index de7b114..312c7f9 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -571,7 +571,7 @@ static int mxs_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, master); - ret = spi_register_master(master); + ret = devm_spi_register_master(&pdev->dev, master); if (ret) { dev_err(&pdev->dev, "Cannot register SPI master, %d\n", ret); goto out_disable_clk; @@ -598,10 +598,8 @@ static int mxs_spi_remove(struct platform_device *pdev) spi = spi_master_get_devdata(master); ssp = &spi->ssp; - spi_unregister_master(master); clk_disable_unprepare(ssp->clk); dma_release_channel(ssp->dmach); - spi_master_put(master); return 0; } -- cgit v0.10.2 From 22ad2d8df77d1e88996d0e8f1c04db65f235a778 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 24 Sep 2013 13:34:24 +0900 Subject: spi: octeon: use devm_spi_register_master() Use devm_spi_register_master() to make cleanup paths simpler. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-octeon.c b/drivers/spi/spi-octeon.c index 5f28ddb..67249a4 100644 --- a/drivers/spi/spi-octeon.c +++ b/drivers/spi/spi-octeon.c @@ -272,7 +272,7 @@ static int octeon_spi_probe(struct platform_device *pdev) master->bits_per_word_mask = SPI_BPW_MASK(8); master->dev.of_node = pdev->dev.of_node; - err = spi_register_master(master); + err = devm_spi_register_master(&pdev->dev, master); if (err) { dev_err(&pdev->dev, "register master failed: %d\n", err); goto fail; @@ -292,8 +292,6 @@ static int octeon_spi_remove(struct platform_device *pdev) struct octeon_spi *p = spi_master_get_devdata(master); u64 register_base = p->register_base; - spi_unregister_master(master); - /* Clear the CSENA* and put everything in a known state. */ cvmx_write_csr(register_base + OCTEON_SPI_CFG, 0); -- cgit v0.10.2 From 5c4c5c7be116b0cce2e71173158060afac4144d0 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 24 Sep 2013 13:37:23 +0900 Subject: spi: omap-100k: use devm_spi_register_master() Use devm_spi_register_master() to make cleanup paths simpler. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c index 69ecf05..b6ed82b 100644 --- a/drivers/spi/spi-omap-100k.c +++ b/drivers/spi/spi-omap-100k.c @@ -457,7 +457,7 @@ static int omap1_spi100k_probe(struct platform_device *pdev) goto err; } - status = spi_register_master(master); + status = devm_spi_register_master(&pdev->dev, master); if (status < 0) goto err; @@ -485,8 +485,6 @@ static int omap1_spi100k_remove(struct platform_device *pdev) r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - spi_unregister_master(master); - return 0; } -- cgit v0.10.2 From b95e02b748ce3674a81d5796b5ee398cbccc92df Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 24 Sep 2013 13:40:29 +0900 Subject: spi: omap2-mcspi: use devm_spi_register_master() Use devm_spi_register_master() to make cleanup paths simpler. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index ed4af47..2f7fd35 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1407,7 +1407,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev) if (status < 0) goto disable_pm; - status = spi_register_master(master); + status = devm_spi_register_master(&pdev->dev, master); if (status < 0) goto disable_pm; @@ -1435,7 +1435,6 @@ static int omap2_mcspi_remove(struct platform_device *pdev) pm_runtime_put_sync(mcspi->dev); pm_runtime_disable(&pdev->dev); - spi_unregister_master(master); kfree(dma_channels); return 0; -- cgit v0.10.2 From 4bd3d8e36b5571db8874ea95ba2b95de1192e387 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 24 Sep 2013 13:43:09 +0900 Subject: spi: orion: use devm_spi_register_master() Use devm_spi_register_master() to make cleanup paths simpler. Signed-off-by: Jingoo Han Acked-by: Jason Cooper Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index 1d1d321..d96d678 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -457,7 +457,7 @@ static int orion_spi_probe(struct platform_device *pdev) goto out_rel_clk; master->dev.of_node = pdev->dev.of_node; - status = spi_register_master(master); + status = devm_spi_register_master(&pdev->dev, master); if (status < 0) goto out_rel_clk; @@ -483,8 +483,6 @@ static int orion_spi_remove(struct platform_device *pdev) clk_disable_unprepare(spi->clk); clk_put(spi->clk); - spi_unregister_master(master); - return 0; } -- cgit v0.10.2 From 35794a77168b739bbc758b6c5b11c78572188d77 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 24 Sep 2013 13:44:59 +0900 Subject: spi: pl022: use devm_spi_register_master() Use devm_spi_register_master() to make cleanup paths simpler. Signed-off-by: Jingoo Han Acked-by: Linus Walleij Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index c13d523..5e30110 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -2225,7 +2225,7 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) /* Register with the SPI framework */ amba_set_drvdata(adev, pl022); - status = spi_register_master(master); + status = devm_spi_register_master(&adev->dev, master); if (status != 0) { dev_err(&adev->dev, "probe - problem registering spi master\n"); @@ -2285,7 +2285,6 @@ pl022_remove(struct amba_device *adev) clk_unprepare(pl022->clk); amba_release_regions(adev); tasklet_disable(&pl022->pump_transfers); - spi_unregister_master(pl022->master); return 0; } -- cgit v0.10.2 From a807fcd090d6e778717d0954fc6a58a72ee16ba0 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 24 Sep 2013 13:46:55 +0900 Subject: spi: pxa2xx: use devm_spi_register_master() Use devm_spi_register_master() to make cleanup paths simpler. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 2eb06ee..669306a 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1196,7 +1196,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) /* Register with the SPI framework */ platform_set_drvdata(pdev, drv_data); - status = spi_register_master(master); + status = devm_spi_register_master(&pdev->dev, master); if (status != 0) { dev_err(&pdev->dev, "problem registering spi master\n"); goto out_error_clock_enabled; @@ -1248,9 +1248,6 @@ static int pxa2xx_spi_remove(struct platform_device *pdev) /* Release SSP */ pxa_ssp_free(ssp); - /* Disconnect from the SPI framework */ - spi_unregister_master(drv_data->master); - return 0; } -- cgit v0.10.2 From 1c43f2ae1ee3b04f7466e31a59b19b7fa5fe4fc2 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 24 Sep 2013 13:47:59 +0900 Subject: spi: sh-hspi: use devm_spi_register_master() Use devm_spi_register_master() to make cleanup paths simpler. Signed-off-by: Jingoo Han Acked-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-sh-hspi.c b/drivers/spi/spi-sh-hspi.c index 0b68cb5..97e913d 100644 --- a/drivers/spi/spi-sh-hspi.c +++ b/drivers/spi/spi-sh-hspi.c @@ -303,7 +303,7 @@ static int hspi_probe(struct platform_device *pdev) master->mode_bits = SPI_CPOL | SPI_CPHA; master->auto_runtime_pm = true; master->transfer_one_message = hspi_transfer_one_message; - ret = spi_register_master(master); + ret = devm_spi_register_master(&pdev->dev, master); if (ret < 0) { dev_err(&pdev->dev, "spi_register_master error.\n"); goto error1; @@ -328,7 +328,6 @@ static int hspi_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); clk_put(hspi->clk); - spi_unregister_master(hspi->master); return 0; } -- cgit v0.10.2 From 5c8096439600de88a7ceded70cc86b817d4a548e Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 24 Sep 2013 13:49:24 +0900 Subject: spi: tegra114: use devm_spi_register_master() Use devm_spi_register_master() to make cleanup paths simpler. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index 145dd43..bb3a19f 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -1115,7 +1115,7 @@ static int tegra_spi_probe(struct platform_device *pdev) pm_runtime_put(&pdev->dev); master->dev.of_node = pdev->dev.of_node; - ret = spi_register_master(master); + ret = devm_spi_register_master(&pdev->dev, master); if (ret < 0) { dev_err(&pdev->dev, "can not register to master err %d\n", ret); goto exit_pm_disable; @@ -1142,7 +1142,6 @@ static int tegra_spi_remove(struct platform_device *pdev) struct tegra_spi_data *tspi = spi_master_get_devdata(master); free_irq(tspi->irq, tspi); - spi_unregister_master(master); if (tspi->tx_dma_chan) tegra_spi_deinit_dma_param(tspi, false); -- cgit v0.10.2 From f12f7318c44aa5ea1672f9a356de02a8739b6892 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 24 Sep 2013 13:50:25 +0900 Subject: spi: tegra20-sflash: use devm_spi_register_master() Use devm_spi_register_master() to make cleanup paths simpler. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c index 1d814dc..a68d779 100644 --- a/drivers/spi/spi-tegra20-sflash.c +++ b/drivers/spi/spi-tegra20-sflash.c @@ -529,7 +529,7 @@ static int tegra_sflash_probe(struct platform_device *pdev) pm_runtime_put(&pdev->dev); master->dev.of_node = pdev->dev.of_node; - ret = spi_register_master(master); + ret = devm_spi_register_master(&pdev->dev, master); if (ret < 0) { dev_err(&pdev->dev, "can not register to master err %d\n", ret); goto exit_pm_disable; @@ -553,7 +553,6 @@ static int tegra_sflash_remove(struct platform_device *pdev) struct tegra_sflash_data *tsd = spi_master_get_devdata(master); free_irq(tsd->irq, tsd); - spi_unregister_master(master); pm_runtime_disable(&pdev->dev); if (!pm_runtime_status_suspended(&pdev->dev)) -- cgit v0.10.2 From 716db5d64f5f9bd6600cd4f85c2986d34cd7f60e Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 24 Sep 2013 13:51:32 +0900 Subject: spi: tegra20-slink: use devm_spi_register_master() Use devm_spi_register_master() to make cleanup paths simpler. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index c703536..829283e 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -1164,7 +1164,7 @@ static int tegra_slink_probe(struct platform_device *pdev) pm_runtime_put(&pdev->dev); master->dev.of_node = pdev->dev.of_node; - ret = spi_register_master(master); + ret = devm_spi_register_master(&pdev->dev, master); if (ret < 0) { dev_err(&pdev->dev, "can not register to master err %d\n", ret); goto exit_pm_disable; @@ -1191,7 +1191,6 @@ static int tegra_slink_remove(struct platform_device *pdev) struct tegra_slink_data *tspi = spi_master_get_devdata(master); free_irq(tspi->irq, tspi); - spi_unregister_master(master); if (tspi->tx_dma_chan) tegra_slink_deinit_dma_param(tspi, false); -- cgit v0.10.2 From 7388c03bac1b4466864945233abf3ca8ba1cb061 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 24 Sep 2013 13:52:37 +0900 Subject: spi: ti-qspi: use devm_spi_register_master() Use devm_spi_register_master() to make cleanup paths simpler, and remove unnecessary remove(). Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c index e12d962..4e2109d 100644 --- a/drivers/spi/spi-ti-qspi.c +++ b/drivers/spi/spi-ti-qspi.c @@ -532,7 +532,7 @@ static int ti_qspi_probe(struct platform_device *pdev) if (!of_property_read_u32(np, "spi-max-frequency", &max_freq)) qspi->spi_max_frequency = max_freq; - ret = spi_register_master(master); + ret = devm_spi_register_master(&pdev->dev, master); if (ret) goto free_master; @@ -543,22 +543,12 @@ free_master: return ret; } -static int ti_qspi_remove(struct platform_device *pdev) -{ - struct ti_qspi *qspi = platform_get_drvdata(pdev); - - spi_unregister_master(qspi->master); - - return 0; -} - static const struct dev_pm_ops ti_qspi_pm_ops = { .runtime_resume = ti_qspi_runtime_resume, }; static struct platform_driver ti_qspi_driver = { .probe = ti_qspi_probe, - .remove = ti_qspi_remove, .driver = { .name = "ti,dra7xxx-qspi", .owner = THIS_MODULE, -- cgit v0.10.2 From 2fe7e4add3e53df7c1b97e32076f8390dd81c6b3 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 24 Sep 2013 13:53:37 +0900 Subject: spi: txx9: use devm_spi_register_master() Use devm_spi_register_master() to make cleanup paths simpler, and remove a duplicate put. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-txx9.c b/drivers/spi/spi-txx9.c index 7c6d157..69eb886 100644 --- a/drivers/spi/spi-txx9.c +++ b/drivers/spi/spi-txx9.c @@ -406,7 +406,7 @@ static int txx9spi_probe(struct platform_device *dev) master->num_chipselect = (u16)UINT_MAX; /* any GPIO numbers */ master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); - ret = spi_register_master(master); + ret = devm_spi_register_master(&dev->dev, master); if (ret) goto exit; return 0; @@ -428,11 +428,9 @@ static int txx9spi_remove(struct platform_device *dev) struct spi_master *master = spi_master_get(platform_get_drvdata(dev)); struct txx9spi *c = spi_master_get_devdata(master); - spi_unregister_master(master); destroy_workqueue(c->workqueue); clk_disable(c->clk); clk_put(c->clk); - spi_master_put(master); return 0; } -- cgit v0.10.2 From 7d5f880b46f222ff83a76fd36b01f5a59bf32122 Mon Sep 17 00:00:00 2001 From: Mateusz Krawczuk Date: Tue, 24 Sep 2013 18:07:46 +0200 Subject: spi: s3c64xx: Allow build on all Samsung platforms Replace all symbols by simple dependency PLAT_SAMSUNG. Signed-off-by: Mateusz Krawczuk Signed-off-by: Kyungmin Park Signed-off-by: Mark Brown diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index b9c53cc..d92d669 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -393,7 +393,7 @@ config SPI_S3C24XX_FIQ config SPI_S3C64XX tristate "Samsung S3C64XX series type SPI" - depends on (ARCH_S3C24XX || ARCH_S3C64XX || ARCH_S5P64X0 || ARCH_EXYNOS) + depends on PLAT_SAMSUNG select S3C64XX_DMA if ARCH_S3C64XX help SPI driver for Samsung S3C64XX and newer SoCs. -- cgit v0.10.2 From 633795b992ebae2a78890c0cfa5c17058eb93817 Mon Sep 17 00:00:00 2001 From: Sourav Poddar Date: Tue, 24 Sep 2013 20:41:23 +0530 Subject: spi/qspi: Add dual/quad read mode bit. Add dual/quad read mode bit flag for the master controller. These check will be used in the spi framework to determine whether the master controller can do dual/quad read respectively. Signed-off-by: Sourav Poddar Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c index e12d962..7a45c3e 100644 --- a/drivers/spi/spi-ti-qspi.c +++ b/drivers/spi/spi-ti-qspi.c @@ -472,7 +472,7 @@ static int ti_qspi_probe(struct platform_device *pdev) if (!master) return -ENOMEM; - master->mode_bits = SPI_CPOL | SPI_CPHA; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD; master->bus_num = -1; master->flags = SPI_MASTER_HALF_DUPLEX; -- cgit v0.10.2 From d6173df35f2dbd0e11f2361fc979ebf2e53cb6cc Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 26 Sep 2013 19:36:11 +0100 Subject: ASoC: si476x: Remove custom register I/O implementation The current si476x I/O implementation wraps the regmap for the core with functions that make the register map cache only when the device is powered down. This implementation appears to be incomplete since there is no code to synchronise the cache so writes done while the core is powered down will be ignored, the device will only be configured if it is powered. A better and more idiomatic approach would be to have the MFD manage the cache, making the device cache only when it powers things down. This also allows ASoC to use the standard regmap helpers for the device which helps remove the ASoC custom ones so do convert to do that. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/si476x.c b/sound/soc/codecs/si476x.c index 38f3b10..03645ce 100644 --- a/sound/soc/codecs/si476x.c +++ b/sound/soc/codecs/si476x.c @@ -60,48 +60,6 @@ enum si476x_pcm_format { SI476X_PCM_FORMAT_S24_LE = 6, }; -static unsigned int si476x_codec_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - int err; - unsigned int val; - struct si476x_core *core = codec->control_data; - - si476x_core_lock(core); - if (!si476x_core_is_powered_up(core)) - regcache_cache_only(core->regmap, true); - - err = regmap_read(core->regmap, reg, &val); - - if (!si476x_core_is_powered_up(core)) - regcache_cache_only(core->regmap, false); - si476x_core_unlock(core); - - if (err < 0) - return err; - - return val; -} - -static int si476x_codec_write(struct snd_soc_codec *codec, - unsigned int reg, unsigned int val) -{ - int err; - struct si476x_core *core = codec->control_data; - - si476x_core_lock(core); - if (!si476x_core_is_powered_up(core)) - regcache_cache_only(core->regmap, true); - - err = regmap_write(core->regmap, reg, val); - - if (!si476x_core_is_powered_up(core)) - regcache_cache_only(core->regmap, false); - si476x_core_unlock(core); - - return err; -} - static const struct snd_soc_dapm_widget si476x_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("LOUT"), SND_SOC_DAPM_OUTPUT("ROUT"), @@ -239,7 +197,7 @@ static int si476x_codec_hw_params(struct snd_pcm_substream *substream, static int si476x_codec_probe(struct snd_soc_codec *codec) { - codec->control_data = i2c_mfd_cell_to_core(codec->dev); + codec->control_data = dev_get_regmap(codec->dev->parent, NULL); return 0; } @@ -268,8 +226,6 @@ static struct snd_soc_dai_driver si476x_dai = { static struct snd_soc_codec_driver soc_codec_dev_si476x = { .probe = si476x_codec_probe, - .read = si476x_codec_read, - .write = si476x_codec_write, .dapm_widgets = si476x_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(si476x_dapm_widgets), .dapm_routes = si476x_dapm_routes, -- cgit v0.10.2 From b5eafc6f07c95e9f3dd047e72737449cb03c9956 Mon Sep 17 00:00:00 2001 From: Masoud Sharbiani Date: Thu, 26 Sep 2013 10:30:43 -0700 Subject: x86/reboot: Remove the duplicate C6100 entry in the reboot quirks list Two entries for the same system type were added, with two different vendor names: 'Dell' and 'Dell, Inc.'. Since a prefix match is being used by the DMI parsing code, we can eliminate the latter as redundant. Reported-by: "H. Peter Anvin" Signed-off-by: Masoud Sharbiani Cc: holt@sgi.com Link: http://lkml.kernel.org/r/1380216643-4683-1-git-send-email-masoud.sharbiani@gmail.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index f0783a6..d9333a4 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -362,14 +362,6 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = { .callback = set_pci_reboot, .ident = "Dell PowerEdge C6100", .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "C6100"), - }, - }, - { /* Some C6100 machines were shipped with vendor being 'Dell'. */ - .callback = set_pci_reboot, - .ident = "Dell PowerEdge C6100", - .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell"), DMI_MATCH(DMI_PRODUCT_NAME, "C6100"), }, -- cgit v0.10.2 From 809373e29a183e4d4f0e4f56ef8b211c6219123c Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 6 Sep 2013 11:15:32 -0700 Subject: x86, build: move build output statistics away from stderr When building on x86, the final image building step always emits stats to stderr, even though this information is neither a warning nor an error: BUILD arch/x86/boot/bzImage Setup is 16188 bytes (padded to 16384 bytes). System is 6368 kB CRC cbe50c61 Validating automated builds would be cleaner if stderr did not have to filter out these lines. Instead, change how tools/build is called, and make the zoffset header unconditional, and write to a specified file instead of to stdout, which can then be used for statistics, leaving stderr open for legitimate warnings and errors, like the output from die(). Signed-off-by: Kees Cook Link: http://lkml.kernel.org/r/20130906181532.GA31260@www.outflux.net Signed-off-by: H. Peter Anvin diff --git a/arch/x86/boot/Makefile b/arch/x86/boot/Makefile index 379814b..dce69a2 100644 --- a/arch/x86/boot/Makefile +++ b/arch/x86/boot/Makefile @@ -71,7 +71,8 @@ GCOV_PROFILE := n $(obj)/bzImage: asflags-y := $(SVGA_MODE) quiet_cmd_image = BUILD $@ -cmd_image = $(obj)/tools/build $(obj)/setup.bin $(obj)/vmlinux.bin $(obj)/zoffset.h > $@ +cmd_image = $(obj)/tools/build $(obj)/setup.bin $(obj)/vmlinux.bin \ + $(obj)/zoffset.h $@ $(obj)/bzImage: $(obj)/setup.bin $(obj)/vmlinux.bin $(obj)/tools/build FORCE $(call if_changed,image) diff --git a/arch/x86/boot/tools/build.c b/arch/x86/boot/tools/build.c index c941d6a..8e15b22 100644 --- a/arch/x86/boot/tools/build.c +++ b/arch/x86/boot/tools/build.c @@ -5,14 +5,15 @@ */ /* - * This file builds a disk-image from two different files: + * This file builds a disk-image from three different files: * * - setup: 8086 machine code, sets up system parm * - system: 80386 code for actual system + * - zoffset.h: header with ZO_* defines * - * It does some checking that all files are of the correct type, and - * just writes the result to stdout, removing headers and padding to - * the right amount. It also writes some system data to stderr. + * It does some checking that all files are of the correct type, and writes + * the result to the specified destination, removing headers and padding to + * the right amount. It also writes some system data to stdout. */ /* @@ -136,7 +137,7 @@ static void die(const char * str, ...) static void usage(void) { - die("Usage: build setup system [zoffset.h] [> image]"); + die("Usage: build setup system zoffset.h image"); } #ifdef CONFIG_EFI_STUB @@ -265,7 +266,7 @@ int main(int argc, char ** argv) int c; u32 sys_size; struct stat sb; - FILE *file; + FILE *file, *dest; int fd; void *kernel; u32 crc = 0xffffffffUL; @@ -280,10 +281,13 @@ int main(int argc, char ** argv) startup_64 = 0x200; #endif - if (argc == 4) - parse_zoffset(argv[3]); - else if (argc != 3) + if (argc != 5) usage(); + parse_zoffset(argv[3]); + + dest = fopen(argv[4], "w"); + if (!dest) + die("Unable to write `%s': %m", argv[4]); /* Copy the setup code */ file = fopen(argv[1], "r"); @@ -318,7 +322,7 @@ int main(int argc, char ** argv) /* Set the default root device */ put_unaligned_le16(DEFAULT_ROOT_DEV, &buf[508]); - fprintf(stderr, "Setup is %d bytes (padded to %d bytes).\n", c, i); + printf("Setup is %d bytes (padded to %d bytes).\n", c, i); /* Open and stat the kernel file */ fd = open(argv[2], O_RDONLY); @@ -327,7 +331,7 @@ int main(int argc, char ** argv) if (fstat(fd, &sb)) die("Unable to stat `%s': %m", argv[2]); sz = sb.st_size; - fprintf (stderr, "System is %d kB\n", (sz+1023)/1024); + printf("System is %d kB\n", (sz+1023)/1024); kernel = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0); if (kernel == MAP_FAILED) die("Unable to mmap '%s': %m", argv[2]); @@ -348,27 +352,31 @@ int main(int argc, char ** argv) #endif crc = partial_crc32(buf, i, crc); - if (fwrite(buf, 1, i, stdout) != i) + if (fwrite(buf, 1, i, dest) != i) die("Writing setup failed"); /* Copy the kernel code */ crc = partial_crc32(kernel, sz, crc); - if (fwrite(kernel, 1, sz, stdout) != sz) + if (fwrite(kernel, 1, sz, dest) != sz) die("Writing kernel failed"); /* Add padding leaving 4 bytes for the checksum */ while (sz++ < (sys_size*16) - 4) { crc = partial_crc32_one('\0', crc); - if (fwrite("\0", 1, 1, stdout) != 1) + if (fwrite("\0", 1, 1, dest) != 1) die("Writing padding failed"); } /* Write the CRC */ - fprintf(stderr, "CRC %x\n", crc); + printf("CRC %x\n", crc); put_unaligned_le32(crc, buf); - if (fwrite(buf, 1, 4, stdout) != 4) + if (fwrite(buf, 1, 4, dest) != 4) die("Writing CRC failed"); + /* Catch any delayed write failures */ + if (fclose(dest)) + die("Writing image failed"); + close(fd); /* Everything is OK */ -- cgit v0.10.2 From f6aaaac9995084e496d4ff800d092a8c0cb12641 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 26 Sep 2013 19:20:56 +0200 Subject: sh-pfc: r8a7790: add pin definitions for the I2C3 interface There are four I2C interfaces on r8a7790, each of them can be connected to one of the two respective I2C controllers, e.g. interface #0 can be configured to work with I2C0 or with IIC0. Additionally some of those interfaces can also use one of several pin sets. Interface #3 is special, because it can be used in automatic mode for DVFS. It only has one set of pins available and those pins cannot be used for anything else, they also lack the GPIO function. This patch uses the sh-pfc ability to configure pins, not associated with GPIOs and adds support for I2C3 to the r8a7790 PFC set up. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Laurent Pinchart diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7790.c b/drivers/pinctrl/sh-pfc/pfc-r8a7790.c index 5c2657b..72786fc 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7790.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7790.c @@ -781,6 +781,8 @@ enum { ADICS_SAMP_MARK, DU2_CDE_MARK, QPOLB_MARK, SCIFA2_RXD_B_MARK, USB1_PWEN_MARK, AUDIO_CLKOUT_D_MARK, USB1_OVC_MARK, TCLK1_B_MARK, + + I2C3_SCL_MARK, I2C3_SDA_MARK, PINMUX_MARK_END, }; @@ -1719,10 +1721,22 @@ static const u16 pinmux_data[] = { PINMUX_IPSR_DATA(IP16_6, AUDIO_CLKOUT_D), PINMUX_IPSR_DATA(IP16_7, USB1_OVC), PINMUX_IPSR_MODSEL_DATA(IP16_7, TCLK1_B, SEL_TMU1_1), + + PINMUX_DATA(I2C3_SCL_MARK, FN_SEL_IICDVFS_1), + PINMUX_DATA(I2C3_SDA_MARK, FN_SEL_IICDVFS_1), }; +/* R8A7790 has 6 banks with 32 GPIOs in each = 192 GPIOs */ +#define ROW_GROUP_A(r) ('Z' - 'A' + 1 + (r)) +#define PIN_NUMBER(r, c) (((r) - 'A') * 31 + (c) + 200) +#define PIN_A_NUMBER(r, c) PIN_NUMBER(ROW_GROUP_A(r), c) + static struct sh_pfc_pin pinmux_pins[] = { PINMUX_GPIO_GP_ALL(), + + /* Pins not associated with a GPIO port */ + SH_PFC_PIN_NAMED(ROW_GROUP_A('J'), 15, AJ15), + SH_PFC_PIN_NAMED(ROW_GROUP_A('H'), 15, AH15), }; /* - DU RGB ----------------------------------------------------------------- */ @@ -2048,6 +2062,14 @@ static const unsigned int i2c2_e_pins[] = { static const unsigned int i2c2_e_mux[] = { I2C2_SCL_E_MARK, I2C2_SDA_E_MARK, }; +/* - I2C3 ------------------------------------------------------------------- */ +static const unsigned int i2c3_pins[] = { + /* SCL, SDA */ + PIN_A_NUMBER('J', 15), PIN_A_NUMBER('H', 15), +}; +static const unsigned int i2c3_mux[] = { + I2C3_SCL_MARK, I2C3_SDA_MARK, +}; /* - INTC ------------------------------------------------------------------- */ static const unsigned int intc_irq0_pins[] = { /* IRQ */ @@ -3113,6 +3135,7 @@ static const struct sh_pfc_pin_group pinmux_groups[] = { SH_PFC_PIN_GROUP(i2c2_c), SH_PFC_PIN_GROUP(i2c2_d), SH_PFC_PIN_GROUP(i2c2_e), + SH_PFC_PIN_GROUP(i2c3), SH_PFC_PIN_GROUP(intc_irq0), SH_PFC_PIN_GROUP(intc_irq1), SH_PFC_PIN_GROUP(intc_irq2), @@ -3323,6 +3346,10 @@ static const char * const i2c2_groups[] = { "i2c2_e", }; +static const char * const i2c3_groups[] = { + "i2c3", +}; + static const char * const intc_groups[] = { "intc_irq0", "intc_irq1", @@ -3551,6 +3578,7 @@ static const struct sh_pfc_function pinmux_functions[] = { SH_PFC_FUNCTION(hscif1), SH_PFC_FUNCTION(i2c1), SH_PFC_FUNCTION(i2c2), + SH_PFC_FUNCTION(i2c3), SH_PFC_FUNCTION(intc), SH_PFC_FUNCTION(mmc0), SH_PFC_FUNCTION(mmc1), -- cgit v0.10.2 From ffde39ea61ea29fc57c15dbcfadbe545c2d3500f Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 25 Sep 2013 15:54:11 +0200 Subject: ARM: mmp: delete the custom GPIO header The MMP sub-architecture appears to have a custom for no reason whatsoever. The file became completely empty after Haojian removed the remaining content in commit 157d2644cb0c1e71a18baaffca56d2b1d0ebf10f "ARM: pxa: change gpio to platform device". Cc: Eric Miao Acked-by: Haojian Zhuang Signed-off-by: Linus Walleij diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 3f7714d..3950a12 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -559,7 +559,6 @@ config ARCH_MMP select GPIO_PXA select IRQ_DOMAIN select MULTI_IRQ_HANDLER - select NEED_MACH_GPIO_H select PINCTRL select PLAT_PXA select SPARSE_IRQ diff --git a/arch/arm/mach-mmp/include/mach/gpio.h b/arch/arm/mach-mmp/include/mach/gpio.h deleted file mode 100644 index 13219eb..0000000 --- a/arch/arm/mach-mmp/include/mach/gpio.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef __ASM_MACH_GPIO_H -#define __ASM_MACH_GPIO_H - -#include - -#include - -#endif /* __ASM_MACH_GPIO_H */ -- cgit v0.10.2 From 88f718e3fa4d67f3a8dbe79a2f97d722323e4051 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 26 Sep 2013 10:04:49 +0200 Subject: ARM: pxa: delete the custom GPIO header The PXA sub-architecture appears to have a custom for no reason whatsoever. The file became completely empty after Haojian removed the remaining content in commit 157d2644cb0c1e71a18baaffca56d2b1d0ebf10f "ARM: pxa: change gpio to platform device". That commit added these two lines: However it seems like deleting the file has no effect whatsoever on the kernel compilation. Cc: Eric Miao Cc: Russell King Acked-by: Haojian Zhuang Signed-off-by: Linus Walleij diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 3950a12..1ced444 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -621,7 +621,6 @@ config ARCH_PXA select GPIO_PXA select HAVE_IDE select MULTI_IRQ_HANDLER - select NEED_MACH_GPIO_H select PLAT_PXA select SPARSE_IRQ help diff --git a/arch/arm/mach-pxa/include/mach/gpio.h b/arch/arm/mach-pxa/include/mach/gpio.h deleted file mode 100644 index 0248e43..0000000 --- a/arch/arm/mach-pxa/include/mach/gpio.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * arch/arm/mach-pxa/include/mach/gpio.h - * - * PXA GPIO wrappers for arch-neutral GPIO calls - * - * Written by Philipp Zabel - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __ASM_ARCH_PXA_GPIO_H -#define __ASM_ARCH_PXA_GPIO_H - -#include - -#include -#include - -#endif -- cgit v0.10.2 From 77966ad7b68112b1a536f994d78d88a9eaca25bc Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 13 Sep 2013 09:45:33 +0200 Subject: pinctrl: at91: fix typos Fix AT91_PINCTRL_DEBOUNCE_VAL dt macro typo. Fix at91_pinctrl_mux_ops callback typos. Signed-off-by: Boris BREZILLON Acked-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index f350fd2..fae1b4d 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -144,11 +144,11 @@ struct at91_pinctrl_mux_ops { void (*mux_C_periph)(void __iomem *pio, unsigned mask); void (*mux_D_periph)(void __iomem *pio, unsigned mask); bool (*get_deglitch)(void __iomem *pio, unsigned pin); - void (*set_deglitch)(void __iomem *pio, unsigned mask, bool in_on); + void (*set_deglitch)(void __iomem *pio, unsigned mask, bool is_on); bool (*get_debounce)(void __iomem *pio, unsigned pin, u32 *div); - void (*set_debounce)(void __iomem *pio, unsigned mask, bool in_on, u32 div); + void (*set_debounce)(void __iomem *pio, unsigned mask, bool is_on, u32 div); bool (*get_pulldown)(void __iomem *pio, unsigned pin); - void (*set_pulldown)(void __iomem *pio, unsigned mask, bool in_on); + void (*set_pulldown)(void __iomem *pio, unsigned mask, bool is_on); bool (*get_schmitt_trig)(void __iomem *pio, unsigned pin); void (*disable_schmitt_trig)(void __iomem *pio, unsigned mask); /* irq */ diff --git a/include/dt-bindings/pinctrl/at91.h b/include/dt-bindings/pinctrl/at91.h index d7988b4..0fee6ff 100644 --- a/include/dt-bindings/pinctrl/at91.h +++ b/include/dt-bindings/pinctrl/at91.h @@ -16,7 +16,7 @@ #define AT91_PINCTRL_PULL_DOWN (1 << 3) #define AT91_PINCTRL_DIS_SCHMIT (1 << 4) #define AT91_PINCTRL_DEBOUNCE (1 << 16) -#define AT91_PINCTRL_DEBOUNCE_VA(x) (x << 17) +#define AT91_PINCTRL_DEBOUNCE_VAL(x) (x << 17) #define AT91_PINCTRL_PULL_UP_DEGLITCH (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_DEGLITCH) -- cgit v0.10.2 From c8dba02e7d5552a360ee5839f828856dd3a827cd Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 13 Sep 2013 09:47:22 +0200 Subject: pinctrl: at91: fix sam9x5 debounce/deglitch functions Replace at91_mux_get_deglitch with at91_mux_pio3_get_deglitch when using sam9x5 (pio3) IP. at91_mux_get_deglitch only test the activation of the "Input Filter" which may be overloaded by the activation of the "Input Filter Slow Clock" to use the input filter as a debounce filter instead of a deglitch filter. Fix at91_mux_pio3_get_debounce to test the activation of the Input Filter before testing the activation of the debounce filter (Input Filter Slow Clock depends on Input Filter). Fix at91_mux_pio3_set_debounce function to avoid disabling the deglitch filter ("Input Filter") when debounce filter is disabled. Signed-off-by: Boris BREZILLON Acked-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index fae1b4d..8ed9be8 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -417,6 +417,14 @@ static void at91_mux_set_deglitch(void __iomem *pio, unsigned mask, bool is_on) __raw_writel(mask, pio + (is_on ? PIO_IFER : PIO_IFDR)); } +static bool at91_mux_pio3_get_deglitch(void __iomem *pio, unsigned pin) +{ + if ((__raw_readl(pio + PIO_IFSR) >> pin) & 0x1) + return !((__raw_readl(pio + PIO_IFSCSR) >> pin) & 0x1); + + return false; +} + static void at91_mux_pio3_set_deglitch(void __iomem *pio, unsigned mask, bool is_on) { if (is_on) @@ -428,7 +436,8 @@ static bool at91_mux_pio3_get_debounce(void __iomem *pio, unsigned pin, u32 *div { *div = __raw_readl(pio + PIO_SCDR); - return (__raw_readl(pio + PIO_IFSCSR) >> pin) & 0x1; + return ((__raw_readl(pio + PIO_IFSR) >> pin) & 0x1) && + ((__raw_readl(pio + PIO_IFSCSR) >> pin) & 0x1); } static void at91_mux_pio3_set_debounce(void __iomem *pio, unsigned mask, @@ -438,9 +447,8 @@ static void at91_mux_pio3_set_debounce(void __iomem *pio, unsigned mask, __raw_writel(mask, pio + PIO_IFSCER); __raw_writel(div & PIO_SCDR_DIV, pio + PIO_SCDR); __raw_writel(mask, pio + PIO_IFER); - } else { - __raw_writel(mask, pio + PIO_IFDR); - } + } else + __raw_writel(mask, pio + PIO_IFSCDR); } static bool at91_mux_pio3_get_pulldown(void __iomem *pio, unsigned pin) @@ -478,7 +486,7 @@ static struct at91_pinctrl_mux_ops at91sam9x5_ops = { .mux_B_periph = at91_mux_pio3_set_B_periph, .mux_C_periph = at91_mux_pio3_set_C_periph, .mux_D_periph = at91_mux_pio3_set_D_periph, - .get_deglitch = at91_mux_get_deglitch, + .get_deglitch = at91_mux_pio3_get_deglitch, .set_deglitch = at91_mux_pio3_set_deglitch, .get_debounce = at91_mux_pio3_get_debounce, .set_debounce = at91_mux_pio3_set_debounce, -- cgit v0.10.2 From 8040dd09c2ca7e70daf84f040beb3ced9602fce5 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 10 Sep 2013 11:19:55 +0200 Subject: ARM: ixp4xx: convert remaining users to use gpiolib A few call sites inside mach-ixp4xx were still using the custom ixp4xx GPIO API with gpio_line_* accessors, convert all these to use the standard gpiolib functions instead. Also attempt to request and label all GPIOs before use. Move the GPIO requests to per-machine device_initcalls() so we are not dependent on the GPIO chip to be available at machine_init time. Cc: Imre Kaloz Cc: Alexandre Courbot Acked-by: Krzysztof Halasa Signed-off-by: Linus Walleij diff --git a/arch/arm/mach-ixp4xx/dsmg600-setup.c b/arch/arm/mach-ixp4xx/dsmg600-setup.c index 63de1b3..736dc69 100644 --- a/arch/arm/mach-ixp4xx/dsmg600-setup.c +++ b/arch/arm/mach-ixp4xx/dsmg600-setup.c @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -161,11 +162,8 @@ static struct platform_device *dsmg600_devices[] __initdata = { static void dsmg600_power_off(void) { - /* enable the pwr cntl gpio */ - gpio_line_config(DSMG600_PO_GPIO, IXP4XX_GPIO_OUT); - - /* poweroff */ - gpio_line_set(DSMG600_PO_GPIO, IXP4XX_GPIO_HIGH); + /* enable the pwr cntl and drive it high */ + gpio_direction_output(DSMG600_PO_GPIO, 1); } /* This is used to make sure the power-button pusher is serious. The button @@ -202,7 +200,7 @@ static void dsmg600_power_handler(unsigned long data) ctrl_alt_del(); /* Change the state of the power LED to "blink" */ - gpio_line_set(DSMG600_LED_PWR_GPIO, IXP4XX_GPIO_LOW); + gpio_set_value(DSMG600_LED_PWR_GPIO, 0); } else { power_button_countdown = PBUTTON_HOLDDOWN_COUNT; } @@ -228,6 +226,40 @@ static void __init dsmg600_timer_init(void) ixp4xx_timer_init(); } +static int __init dsmg600_gpio_init(void) +{ + if (!machine_is_dsmg600()) + return 0; + + gpio_request(DSMG600_RB_GPIO, "reset button"); + if (request_irq(gpio_to_irq(DSMG600_RB_GPIO), &dsmg600_reset_handler, + IRQF_DISABLED | IRQF_TRIGGER_LOW, + "DSM-G600 reset button", NULL) < 0) { + + printk(KERN_DEBUG "Reset Button IRQ %d not available\n", + gpio_to_irq(DSMG600_RB_GPIO)); + } + + /* + * The power button on the D-Link DSM-G600 is on GPIO 15, but + * it cannot handle interrupts on that GPIO line. So we'll + * have to poll it with a kernel timer. + */ + + /* Make sure that the power button GPIO is set up as an input */ + gpio_request(DSMG600_PB_GPIO, "power button"); + gpio_direction_input(DSMG600_PB_GPIO); + /* Request poweroff GPIO line */ + gpio_request(DSMG600_PO_GPIO, "power off button"); + + /* Set the initial value for the power button IRQ handler */ + power_button_countdown = PBUTTON_HOLDDOWN_COUNT; + + mod_timer(&dsmg600_power_timer, jiffies + msecs_to_jiffies(500)); + return 0; +} +device_initcall(dsmg600_gpio_init); + static void __init dsmg600_init(void) { ixp4xx_sys_init(); @@ -251,27 +283,6 @@ static void __init dsmg600_init(void) platform_add_devices(dsmg600_devices, ARRAY_SIZE(dsmg600_devices)); pm_power_off = dsmg600_power_off; - - if (request_irq(gpio_to_irq(DSMG600_RB_GPIO), &dsmg600_reset_handler, - IRQF_DISABLED | IRQF_TRIGGER_LOW, - "DSM-G600 reset button", NULL) < 0) { - - printk(KERN_DEBUG "Reset Button IRQ %d not available\n", - gpio_to_irq(DSMG600_RB_GPIO)); - } - - /* The power button on the D-Link DSM-G600 is on GPIO 15, but - * it cannot handle interrupts on that GPIO line. So we'll - * have to poll it with a kernel timer. - */ - - /* Make sure that the power button GPIO is set up as an input */ - gpio_line_config(DSMG600_PB_GPIO, IXP4XX_GPIO_IN); - - /* Set the initial value for the power button IRQ handler */ - power_button_countdown = PBUTTON_HOLDDOWN_COUNT; - - mod_timer(&dsmg600_power_timer, jiffies + msecs_to_jiffies(500)); } MACHINE_START(DSMG600, "D-Link DSM-G600 RevA") diff --git a/arch/arm/mach-ixp4xx/ixdp425-setup.c b/arch/arm/mach-ixp4xx/ixdp425-setup.c index 22d688b..e7b8bef 100644 --- a/arch/arm/mach-ixp4xx/ixdp425-setup.c +++ b/arch/arm/mach-ixp4xx/ixdp425-setup.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -80,10 +81,10 @@ ixdp425_flash_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) if (ctrl & NAND_CTRL_CHANGE) { if (ctrl & NAND_NCE) { - gpio_line_set(IXDP425_NAND_NCE_PIN, IXP4XX_GPIO_LOW); + gpio_set_value(IXDP425_NAND_NCE_PIN, 0); udelay(5); } else - gpio_line_set(IXDP425_NAND_NCE_PIN, IXP4XX_GPIO_HIGH); + gpio_set_value(IXDP425_NAND_NCE_PIN, 1); offset = (ctrl & NAND_CLE) ? IXDP425_NAND_CMD_BYTE : 0; offset |= (ctrl & NAND_ALE) ? IXDP425_NAND_ADDR_BYTE : 0; @@ -227,7 +228,8 @@ static void __init ixdp425_init(void) ixdp425_flash_nand_resource.start = IXP4XX_EXP_BUS_BASE(3), ixdp425_flash_nand_resource.end = IXP4XX_EXP_BUS_BASE(3) + 0x10 - 1; - gpio_line_config(IXDP425_NAND_NCE_PIN, IXP4XX_GPIO_OUT); + gpio_request(IXDP425_NAND_NCE_PIN, "NAND NCE pin"); + gpio_direction_output(IXDP425_NAND_NCE_PIN, 0); /* Configure expansion bus for NAND Flash */ *IXP4XX_EXP_CS3 = IXP4XX_EXP_BUS_CS_EN | diff --git a/arch/arm/mach-ixp4xx/nas100d-setup.c b/arch/arm/mach-ixp4xx/nas100d-setup.c index ed667ce..507cb52 100644 --- a/arch/arm/mach-ixp4xx/nas100d-setup.c +++ b/arch/arm/mach-ixp4xx/nas100d-setup.c @@ -184,11 +184,8 @@ static void nas100d_power_off(void) { /* This causes the box to drop the power and go dead. */ - /* enable the pwr cntl gpio */ - gpio_line_config(NAS100D_PO_GPIO, IXP4XX_GPIO_OUT); - - /* do the deed */ - gpio_line_set(NAS100D_PO_GPIO, IXP4XX_GPIO_HIGH); + /* enable the pwr cntl gpio and assert power off */ + gpio_direction_output(NAS100D_PO_GPIO, 1); } /* This is used to make sure the power-button pusher is serious. The button @@ -225,7 +222,7 @@ static void nas100d_power_handler(unsigned long data) ctrl_alt_del(); /* Change the state of the power LED to "blink" */ - gpio_line_set(NAS100D_LED_PWR_GPIO, IXP4XX_GPIO_LOW); + gpio_set_value(NAS100D_LED_PWR_GPIO, 0); } else { power_button_countdown = PBUTTON_HOLDDOWN_COUNT; } @@ -242,6 +239,33 @@ static irqreturn_t nas100d_reset_handler(int irq, void *dev_id) return IRQ_HANDLED; } +static int __init nas100d_gpio_init(void) +{ + if (!machine_is_nas100d()) + return 0; + + /* + * The power button on the Iomega NAS100d is on GPIO 14, but + * it cannot handle interrupts on that GPIO line. So we'll + * have to poll it with a kernel timer. + */ + + /* Request the power off GPIO */ + gpio_request(NAS100D_PO_GPIO, "power off"); + + /* Make sure that the power button GPIO is set up as an input */ + gpio_request(NAS100D_PB_GPIO, "power button"); + gpio_direction_input(NAS100D_PB_GPIO); + + /* Set the initial value for the power button IRQ handler */ + power_button_countdown = PBUTTON_HOLDDOWN_COUNT; + + mod_timer(&nas100d_power_timer, jiffies + msecs_to_jiffies(500)); + + return 0; +} +device_initcall(nas100d_gpio_init); + static void __init nas100d_init(void) { uint8_t __iomem *f; @@ -278,19 +302,6 @@ static void __init nas100d_init(void) gpio_to_irq(NAS100D_RB_GPIO)); } - /* The power button on the Iomega NAS100d is on GPIO 14, but - * it cannot handle interrupts on that GPIO line. So we'll - * have to poll it with a kernel timer. - */ - - /* Make sure that the power button GPIO is set up as an input */ - gpio_line_config(NAS100D_PB_GPIO, IXP4XX_GPIO_IN); - - /* Set the initial value for the power button IRQ handler */ - power_button_countdown = PBUTTON_HOLDDOWN_COUNT; - - mod_timer(&nas100d_power_timer, jiffies + msecs_to_jiffies(500)); - /* * Map in a portion of the flash and read the MAC address. * Since it is stored in BE in the flash itself, we need to diff --git a/arch/arm/mach-ixp4xx/nslu2-setup.c b/arch/arm/mach-ixp4xx/nslu2-setup.c index 7e55236..ba5f1cd 100644 --- a/arch/arm/mach-ixp4xx/nslu2-setup.c +++ b/arch/arm/mach-ixp4xx/nslu2-setup.c @@ -197,11 +197,8 @@ static void nslu2_power_off(void) { /* This causes the box to drop the power and go dead. */ - /* enable the pwr cntl gpio */ - gpio_line_config(NSLU2_PO_GPIO, IXP4XX_GPIO_OUT); - - /* do the deed */ - gpio_line_set(NSLU2_PO_GPIO, IXP4XX_GPIO_HIGH); + /* enable the pwr cntl gpio and assert power off */ + gpio_direction_output(NSLU2_PO_GPIO, 1); } static irqreturn_t nslu2_power_handler(int irq, void *dev_id) @@ -223,6 +220,16 @@ static irqreturn_t nslu2_reset_handler(int irq, void *dev_id) return IRQ_HANDLED; } +static int __init nslu2_gpio_init(void) +{ + if (!machine_is_nslu2()) + return 0; + + /* Request the power off GPIO */ + return gpio_request(NSLU2_PO_GPIO, "power off"); +} +device_initcall(nslu2_gpio_init); + static void __init nslu2_timer_init(void) { /* The xtal on this machine is non-standard. */ -- cgit v0.10.2 From e9c9fc2315ad7a57af1a5124ad911870b60d9bd0 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 10 Sep 2013 12:53:03 +0200 Subject: input: misc: ixp4-beeper: switch to use gpiolib The platform using this beeper has support for gpiolib, so there is no point to use the custom gpio_line* API. A strange ambiguity where a line was first set as input and then driven high was solved by first driving the line high as output and then switch it to input. Cc: Imre Kaloz Cc: Alexandre Courbot Acked-by: Dmitry Torokhov Acked-by: Arnd Bergmann Acked-by: Krzysztof Halasa Signed-off-by: Linus Walleij diff --git a/drivers/input/misc/ixp4xx-beeper.c b/drivers/input/misc/ixp4xx-beeper.c index f34beb2..f14afd0 100644 --- a/drivers/input/misc/ixp4xx-beeper.c +++ b/drivers/input/misc/ixp4xx-beeper.c @@ -20,6 +20,7 @@ #include #include #include +#include #include MODULE_AUTHOR("Alessandro Zummo "); @@ -35,15 +36,12 @@ static void ixp4xx_spkr_control(unsigned int pin, unsigned int count) spin_lock_irqsave(&beep_lock, flags); - if (count) { - gpio_line_config(pin, IXP4XX_GPIO_OUT); - gpio_line_set(pin, IXP4XX_GPIO_LOW); - + if (count) { + gpio_direction_output(pin, 0); *IXP4XX_OSRT2 = (count & ~IXP4XX_OST_RELOAD_MASK) | IXP4XX_OST_ENABLE; } else { - gpio_line_config(pin, IXP4XX_GPIO_IN); - gpio_line_set(pin, IXP4XX_GPIO_HIGH); - + gpio_direction_output(pin, 1); + gpio_direction_input(pin); *IXP4XX_OSRT2 = 0; } -- cgit v0.10.2 From b22973d0ecfcf499179870599b0f6e0712ff0a14 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 10 Sep 2013 13:06:57 +0200 Subject: input: misc: ixp4-beeper: use gpiolib strictly Request and free the GPIO line used for the beeper properly. Then use the gpiolib API to flip the output of the GPIO pin instead of relying on hacks to poke the register bits. Cc: Imre Kaloz Cc: Alexandre Courbot Acked-by: Dmitry Torokhov Acked-by: Arnd Bergmann Acked-by: Krzysztof Halasa Signed-off-by: Linus Walleij diff --git a/drivers/input/misc/ixp4xx-beeper.c b/drivers/input/misc/ixp4xx-beeper.c index f14afd0..17ccba8 100644 --- a/drivers/input/misc/ixp4xx-beeper.c +++ b/drivers/input/misc/ixp4xx-beeper.c @@ -76,11 +76,13 @@ static int ixp4xx_spkr_event(struct input_dev *dev, unsigned int type, unsigned static irqreturn_t ixp4xx_spkr_interrupt(int irq, void *dev_id) { + unsigned int pin = (unsigned int) dev_id; + /* clear interrupt */ *IXP4XX_OSST = IXP4XX_OSST_TIMER_2_PEND; /* flip the beeper output */ - *IXP4XX_GPIO_GPOUTR ^= (1 << (unsigned int) dev_id); + gpio_set_value(pin, !gpio_get_value(pin)); return IRQ_HANDLED; } @@ -108,11 +110,15 @@ static int ixp4xx_spkr_probe(struct platform_device *dev) input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); input_dev->event = ixp4xx_spkr_event; + err = gpio_request(dev->id, "ixp4-beeper"); + if (err) + goto err_free_device; + err = request_irq(IRQ_IXP4XX_TIMER2, &ixp4xx_spkr_interrupt, IRQF_NO_SUSPEND, "ixp4xx-beeper", (void *) dev->id); if (err) - goto err_free_device; + goto err_free_gpio; err = input_register_device(input_dev); if (err) @@ -124,6 +130,8 @@ static int ixp4xx_spkr_probe(struct platform_device *dev) err_free_irq: free_irq(IRQ_IXP4XX_TIMER2, (void *)dev->id); + err_free_gpio: + gpio_free(dev->id); err_free_device: input_free_device(input_dev); @@ -142,6 +150,7 @@ static int ixp4xx_spkr_remove(struct platform_device *dev) ixp4xx_spkr_control(pin, 0); free_irq(IRQ_IXP4XX_TIMER2, (void *)dev->id); + gpio_free(dev->id); return 0; } -- cgit v0.10.2 From dc6ab07d8f158b6c0a86fc412215692b28632c23 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 10 Sep 2013 13:15:18 +0200 Subject: ptp: switch to use gpiolib This platform supports gpiolib, so remove the custom API use and replace with calls to gpiolib. Also request the GPIO before starting to use it. Cc: Imre Kaloz Cc: Alexandre Courbot Cc: netdev@vger.kernel.org Acked-by: Richard Cochran Acked-by: Krzysztof Halasa Signed-off-by: Linus Walleij diff --git a/drivers/ptp/ptp_ixp46x.c b/drivers/ptp/ptp_ixp46x.c index d49b851..4a08727 100644 --- a/drivers/ptp/ptp_ixp46x.c +++ b/drivers/ptp/ptp_ixp46x.c @@ -259,8 +259,15 @@ static struct ixp_clock ixp_clock; static int setup_interrupt(int gpio) { int irq; + int err; - gpio_line_config(gpio, IXP4XX_GPIO_IN); + err = gpio_request(gpio, "ixp4-ptp"); + if (err) + return err; + + err = gpio_direction_input(gpio); + if (err) + return err; irq = gpio_to_irq(gpio); -- cgit v0.10.2 From b336cb29ae1b564d9368495f9d89e2e9bdc6023e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 10 Sep 2013 13:30:10 +0200 Subject: staging: media/lirc: switch to use gpiolib The lirc serial module has special hooks to work with NSLU2, switch these over to use gpiolib, as that is available on the ixp4 platform. Not even compile tested as there is no way to select this driver from menuconfig on the ixp4 platform. Cc: Imre Kaloz Cc: Alexandre Courbot Cc: Greg Kroah-Hartman Acked-by: Krzysztof Halasa Signed-off-by: Linus Walleij diff --git a/drivers/staging/media/lirc/lirc_serial.c b/drivers/staging/media/lirc/lirc_serial.c index af08e67..f6bc4c9 100644 --- a/drivers/staging/media/lirc/lirc_serial.c +++ b/drivers/staging/media/lirc/lirc_serial.c @@ -67,7 +67,7 @@ #include #include #include - +#include #include #include #include @@ -321,7 +321,7 @@ static void on(void) * status LED and ground */ if (type == LIRC_NSLU2) { - gpio_line_set(NSLU2_LED_GRN, IXP4XX_GPIO_LOW); + gpio_set_value(NSLU2_LED_GRN, 0); return; } #endif @@ -335,7 +335,7 @@ static void off(void) { #ifdef CONFIG_LIRC_SERIAL_NSLU2 if (type == LIRC_NSLU2) { - gpio_line_set(NSLU2_LED_GRN, IXP4XX_GPIO_HIGH); + gpio_set_value(NSLU2_LED_GRN, 1); return; } #endif @@ -839,6 +839,16 @@ static int lirc_serial_probe(struct platform_device *dev) { int i, nlow, nhigh, result; +#ifdef CONFIG_LIRC_SERIAL_NSLU2 + /* This GPIO is used for a LED on the NSLU2 */ + result = devm_gpio_request(dev, NSLU2_LED_GRN, "lirc-serial"); + if (result) + return result; + result = gpio_direction_output(NSLU2_LED_GRN, 0); + if (result) + return result; +#endif + result = request_irq(irq, irq_handler, (share_irq ? IRQF_SHARED : 0), LIRC_DRIVER_NAME, (void *)&hardware); -- cgit v0.10.2 From 81ded3c11eeb167c38406247914533872a82db8c Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 10 Sep 2013 13:39:03 +0200 Subject: ARM: ixp4: delete irq_to_gpio This dangerous function is not used in the kernel, so let's just delete it. Cc: Imre Kaloz Cc: Alexandre Courbot Acked-by: Krzysztof Halasa Signed-off-by: Linus Walleij diff --git a/arch/arm/mach-ixp4xx/common.c b/arch/arm/mach-ixp4xx/common.c index 5327dec..8708ac8 100644 --- a/arch/arm/mach-ixp4xx/common.c +++ b/arch/arm/mach-ixp4xx/common.c @@ -117,17 +117,6 @@ static int ixp4xx_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) return -EINVAL; } -int irq_to_gpio(unsigned int irq) -{ - int gpio = (irq < 32) ? irq2gpio[irq] : -EINVAL; - - if (gpio == -1) - return -EINVAL; - - return gpio; -} -EXPORT_SYMBOL(irq_to_gpio); - static int ixp4xx_set_irq_type(struct irq_data *d, unsigned int type) { int line = irq2gpio[d->irq]; -- cgit v0.10.2 From 098e30f6558f8950ed851e874d08bab91c9d4be7 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 10 Sep 2013 14:10:13 +0200 Subject: ARM: ixp4xx: stop broadcasting the custom GPIO API Now that these custom GPIO accessors are only used from the gpio chip in this machine, move the code out of the include file and right next to the gpiochip implementation. Cc: Imre Kaloz Cc: Alexandre Courbot Acked-by: Krzysztof Halasa Signed-off-by: Linus Walleij diff --git a/arch/arm/mach-ixp4xx/common.c b/arch/arm/mach-ixp4xx/common.c index 8708ac8..9edaf47 100644 --- a/arch/arm/mach-ixp4xx/common.c +++ b/arch/arm/mach-ixp4xx/common.c @@ -81,6 +81,44 @@ void __init ixp4xx_map_io(void) iotable_init(ixp4xx_io_desc, ARRAY_SIZE(ixp4xx_io_desc)); } +/* + * GPIO-functions + */ +/* + * The following converted to the real HW bits the gpio_line_config + */ +/* GPIO pin types */ +#define IXP4XX_GPIO_OUT 0x1 +#define IXP4XX_GPIO_IN 0x2 + +/* GPIO signal types */ +#define IXP4XX_GPIO_LOW 0 +#define IXP4XX_GPIO_HIGH 1 + +/* GPIO Clocks */ +#define IXP4XX_GPIO_CLK_0 14 +#define IXP4XX_GPIO_CLK_1 15 + +static void gpio_line_config(u8 line, u32 direction) +{ + if (direction == IXP4XX_GPIO_IN) + *IXP4XX_GPIO_GPOER |= (1 << line); + else + *IXP4XX_GPIO_GPOER &= ~(1 << line); +} + +static void gpio_line_get(u8 line, int *value) +{ + *value = (*IXP4XX_GPIO_GPINR >> line) & 0x1; +} + +static void gpio_line_set(u8 line, int value) +{ + if (value == IXP4XX_GPIO_HIGH) + *IXP4XX_GPIO_GPOUTR |= (1 << line); + else if (value == IXP4XX_GPIO_LOW) + *IXP4XX_GPIO_GPOUTR &= ~(1 << line); +} /************************************************************************* * IXP4xx chipset IRQ handling diff --git a/arch/arm/mach-ixp4xx/include/mach/platform.h b/arch/arm/mach-ixp4xx/include/mach/platform.h index 4c4c6a6..75c4c65 100644 --- a/arch/arm/mach-ixp4xx/include/mach/platform.h +++ b/arch/arm/mach-ixp4xx/include/mach/platform.h @@ -131,44 +131,5 @@ struct pci_sys_data; extern int ixp4xx_setup(int nr, struct pci_sys_data *sys); extern struct pci_ops ixp4xx_ops; -/* - * GPIO-functions - */ -/* - * The following converted to the real HW bits the gpio_line_config - */ -/* GPIO pin types */ -#define IXP4XX_GPIO_OUT 0x1 -#define IXP4XX_GPIO_IN 0x2 - -/* GPIO signal types */ -#define IXP4XX_GPIO_LOW 0 -#define IXP4XX_GPIO_HIGH 1 - -/* GPIO Clocks */ -#define IXP4XX_GPIO_CLK_0 14 -#define IXP4XX_GPIO_CLK_1 15 - -static inline void gpio_line_config(u8 line, u32 direction) -{ - if (direction == IXP4XX_GPIO_IN) - *IXP4XX_GPIO_GPOER |= (1 << line); - else - *IXP4XX_GPIO_GPOER &= ~(1 << line); -} - -static inline void gpio_line_get(u8 line, int *value) -{ - *value = (*IXP4XX_GPIO_GPINR >> line) & 0x1; -} - -static inline void gpio_line_set(u8 line, int value) -{ - if (value == IXP4XX_GPIO_HIGH) - *IXP4XX_GPIO_GPOUTR |= (1 << line); - else if (value == IXP4XX_GPIO_LOW) - *IXP4XX_GPIO_GPOUTR &= ~(1 << line); -} - #endif // __ASSEMBLY__ -- cgit v0.10.2 From 2467f1102dd117d4276498d30bb255796db7171c Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Tue, 27 Aug 2013 15:26:05 +0200 Subject: ARM: at91/at91-pinctrl documentation: add missing sam9x5 compatible string Add missing "atmel,at91sam9x5-pinctrl" compatible string to the documentation. Signed-off-by: Boris BREZILLON Acked-by: Nicolas Ferre Signed-off-by: Linus Walleij diff --git a/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt index 7ccae49..02ab5ab 100644 --- a/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt @@ -18,7 +18,7 @@ mode) this pin can work on and the 'config' configures various pad settings such as pull-up, multi drive, etc. Required properties for iomux controller: -- compatible: "atmel,at91rm9200-pinctrl" +- compatible: "atmel,at91rm9200-pinctrl" or "atmel,at91sam9x5-pinctrl" - atmel,mux-mask: array of mask (periph per bank) to describe if a pin can be configured in this periph mode. All the periph and bank need to be describe. -- cgit v0.10.2 From c8690d6d2957ed060c1cba4cde3ec41e3829651b Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 24 Sep 2013 15:46:11 +0800 Subject: pinctrl: adi2: Convert to devm_ioremap_resource Signed-off-by: Axel Lin Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/pinctrl-adi2.c b/drivers/pinctrl/pinctrl-adi2.c index 7a24e59..f2aa877 100644 --- a/drivers/pinctrl/pinctrl-adi2.c +++ b/drivers/pinctrl/pinctrl-adi2.c @@ -855,22 +855,9 @@ static int adi_gpio_pint_probe(struct platform_device *pdev) } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "Invalid mem resource\n"); - return -ENODEV; - } - - if (!devm_request_mem_region(dev, res->start, resource_size(res), - pdev->name)) { - dev_err(dev, "Region already claimed\n"); - return -EBUSY; - } - - pint->base = devm_ioremap(dev, res->start, resource_size(res)); - if (!pint->base) { - dev_err(dev, "Could not ioremap\n"); - return -ENOMEM; - } + pint->base = devm_ioremap_resource(dev, res); + if (IS_ERR(pint->base)) + return PTR_ERR(pint->base); pint->regs = (struct gpio_pint_regs *)pint->base; @@ -984,22 +971,9 @@ static int adi_gpio_probe(struct platform_device *pdev) } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "Invalid mem resource\n"); - return -ENODEV; - } - - if (!devm_request_mem_region(dev, res->start, resource_size(res), - pdev->name)) { - dev_err(dev, "Region already claimed\n"); - return -EBUSY; - } - - port->base = devm_ioremap(dev, res->start, resource_size(res)); - if (!port->base) { - dev_err(dev, "Could not ioremap\n"); - return -ENOMEM; - } + port->base = devm_ioremap_resource(dev, res); + if (IS_ERR(port->base)) + return PTR_ERR(port->base); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) -- cgit v0.10.2 From fe4315c3b0cbd4ae5a0b7fcf42c265ed03e27d7a Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 24 Sep 2013 15:47:41 +0800 Subject: pinctrl: adi2: Fix dead lock in adi_gpio_direction_output Current code hold port->lock spinlock and then try to grab the lock again in adi_gpio_set_value(). Fix it. Signed-off-by: Axel Lin Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/pinctrl-adi2.c b/drivers/pinctrl/pinctrl-adi2.c index f2aa877..8089fda 100644 --- a/drivers/pinctrl/pinctrl-adi2.c +++ b/drivers/pinctrl/pinctrl-adi2.c @@ -776,10 +776,11 @@ static int adi_gpio_direction_output(struct gpio_chip *chip, unsigned offset, struct gpio_port_t *regs = port->regs; unsigned long flags; + adi_gpio_set_value(chip, offset, value); + spin_lock_irqsave(&port->lock, flags); writew(readw(®s->inen) & ~(1 << offset), ®s->inen); - adi_gpio_set_value(chip, offset, value); writew(1 << offset, ®s->dir_set); spin_unlock_irqrestore(&port->lock, flags); -- cgit v0.10.2 From f4fade12d506e14867a2b0a5e2f7aaf227297d8b Mon Sep 17 00:00:00 2001 From: Rhyland Klein Date: Thu, 26 Sep 2013 13:01:43 -0400 Subject: spi/tegra114: Correct support for cs_change The tegra114 driver wasn't currently handling the cs_change functionality. cs_change is meant to invert the decisions of whether or not to deactivate CS after each transfer. Without cs_change, after every transfer (other than the last in the message) the normal behavior is to leave CS active. For the last transfer, normally CS is deactivated when the transfer is complete. With cs_change set on a transfer (other than last one) CS would be deactivated and the next transfer would need to activate it again. If cs_change was set on the last tranfer in a message, then CS would be left active when the message compeleted. Also, this builds in logic so that if a different device tries to start a transfer while CS is active from a different device, it will abort the previous transfer and start a new one for the new device. This splits tegra_spi_start_transfer_one into 2 functions, the new one being tegra_spi_setup_transfer_one. The setup function is safe to call on all transfers, sets up for the transfer, and handles the special case of the first transfer in a message. In this special case, it needs to know whether or not it needs to activate CS. This work was based on the spi-atmel driver. Signed-off-by: Rhyland Klein Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index 145dd43..64ca86c 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -182,6 +182,7 @@ struct tegra_spi_data { u32 cur_speed; struct spi_device *cur_spi; + struct spi_device *cs_control; unsigned cur_pos; unsigned cur_len; unsigned words_per_32bit; @@ -676,15 +677,12 @@ static void tegra_spi_deinit_dma_param(struct tegra_spi_data *tspi, dma_release_channel(dma_chan); } -static int tegra_spi_start_transfer_one(struct spi_device *spi, - struct spi_transfer *t, bool is_first_of_msg, - bool is_single_xfer) +static unsigned long tegra_spi_setup_transfer_one(struct spi_device *spi, + struct spi_transfer *t, bool is_first_of_msg) { struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master); u32 speed = t->speed_hz; u8 bits_per_word = t->bits_per_word; - unsigned total_fifo_words; - int ret; unsigned long command1; int req_mode; @@ -698,7 +696,6 @@ static int tegra_spi_start_transfer_one(struct spi_device *spi, tspi->cur_rx_pos = 0; tspi->cur_tx_pos = 0; tspi->curr_xfer = t; - total_fifo_words = tegra_spi_calculate_curr_xfer_param(spi, tspi, t); if (is_first_of_msg) { tegra_spi_clear_status(tspi); @@ -717,7 +714,12 @@ static int tegra_spi_start_transfer_one(struct spi_device *spi, else if (req_mode == SPI_MODE_3) command1 |= SPI_CONTROL_MODE_3; - tegra_spi_writel(tspi, command1, SPI_COMMAND1); + if (tspi->cs_control) { + if (tspi->cs_control != spi) + tegra_spi_writel(tspi, command1, SPI_COMMAND1); + tspi->cs_control = NULL; + } else + tegra_spi_writel(tspi, command1, SPI_COMMAND1); command1 |= SPI_CS_SW_HW; if (spi->mode & SPI_CS_HIGH) @@ -732,6 +734,18 @@ static int tegra_spi_start_transfer_one(struct spi_device *spi, command1 |= SPI_BIT_LENGTH(bits_per_word - 1); } + return command1; +} + +static int tegra_spi_start_transfer_one(struct spi_device *spi, + struct spi_transfer *t, unsigned long command1) +{ + struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master); + unsigned total_fifo_words; + int ret; + + total_fifo_words = tegra_spi_calculate_curr_xfer_param(spi, tspi, t); + if (tspi->is_packed) command1 |= SPI_PACKED; @@ -803,29 +817,50 @@ static int tegra_spi_setup(struct spi_device *spi) return 0; } +static void tegra_spi_transfer_delay(int delay) +{ + if (!delay) + return; + + if (delay >= 1000) + mdelay(delay / 1000); + + udelay(delay % 1000); +} + static int tegra_spi_transfer_one_message(struct spi_master *master, struct spi_message *msg) { bool is_first_msg = true; - int single_xfer; struct tegra_spi_data *tspi = spi_master_get_devdata(master); struct spi_transfer *xfer; struct spi_device *spi = msg->spi; int ret; + bool skip = false; msg->status = 0; msg->actual_length = 0; - single_xfer = list_is_singular(&msg->transfers); list_for_each_entry(xfer, &msg->transfers, transfer_list) { + unsigned long cmd1; + INIT_COMPLETION(tspi->xfer_completion); - ret = tegra_spi_start_transfer_one(spi, xfer, - is_first_msg, single_xfer); + + cmd1 = tegra_spi_setup_transfer_one(spi, xfer, is_first_msg); + + if (!xfer->len) { + ret = 0; + skip = true; + goto complete_xfer; + } + + ret = tegra_spi_start_transfer_one(spi, xfer, cmd1); if (ret < 0) { dev_err(tspi->dev, "spi can not start transfer, err %d\n", ret); - goto exit; + goto complete_xfer; } + is_first_msg = false; ret = wait_for_completion_timeout(&tspi->xfer_completion, SPI_DMA_TIMEOUT); @@ -833,24 +868,40 @@ static int tegra_spi_transfer_one_message(struct spi_master *master, dev_err(tspi->dev, "spi trasfer timeout, err %d\n", ret); ret = -EIO; - goto exit; + goto complete_xfer; } if (tspi->tx_status || tspi->rx_status) { dev_err(tspi->dev, "Error in Transfer\n"); ret = -EIO; - goto exit; + goto complete_xfer; } msg->actual_length += xfer->len; - if (xfer->cs_change && xfer->delay_usecs) { + +complete_xfer: + if (ret < 0 || skip) { tegra_spi_writel(tspi, tspi->def_command1_reg, SPI_COMMAND1); - udelay(xfer->delay_usecs); + tegra_spi_transfer_delay(xfer->delay_usecs); + goto exit; + } else if (msg->transfers.prev == &xfer->transfer_list) { + /* This is the last transfer in message */ + if (xfer->cs_change) + tspi->cs_control = spi; + else { + tegra_spi_writel(tspi, tspi->def_command1_reg, + SPI_COMMAND1); + tegra_spi_transfer_delay(xfer->delay_usecs); + } + } else if (xfer->cs_change) { + tegra_spi_writel(tspi, tspi->def_command1_reg, + SPI_COMMAND1); + tegra_spi_transfer_delay(xfer->delay_usecs); } + } ret = 0; exit: - tegra_spi_writel(tspi, tspi->def_command1_reg, SPI_COMMAND1); msg->status = ret; spi_finalize_current_message(master); return ret; -- cgit v0.10.2 From c3df37c9380d70f19a9cb2de4c7d58d7822a4b35 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 27 Sep 2013 13:47:08 +0200 Subject: ASoC: adau1373: Convert to direct regmap usage Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c index 1aa10dd..c57c1f8 100644 --- a/sound/soc/codecs/adau1373.c +++ b/sound/soc/codecs/adau1373.c @@ -32,6 +32,7 @@ struct adau1373_dai { }; struct adau1373 { + struct regmap *regmap; struct adau1373_dai dais[3]; }; @@ -152,37 +153,172 @@ struct adau1373 { #define ADAU1373_EP_CTRL_MICBIAS1_OFFSET 4 #define ADAU1373_EP_CTRL_MICBIAS2_OFFSET 2 -static const uint8_t adau1373_default_regs[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, /* 0x30 */ - 0x00, 0x00, 0x00, 0x80, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x00, /* 0x40 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x78, 0x18, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, /* 0x80 */ - 0x00, 0xc0, 0x88, 0x7a, 0xdf, 0x20, 0x00, 0x00, - 0x78, 0x18, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, /* 0x90 */ - 0x00, 0xc0, 0x88, 0x7a, 0xdf, 0x20, 0x00, 0x00, - 0x78, 0x18, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, /* 0xa0 */ - 0x00, 0xc0, 0x88, 0x7a, 0xdf, 0x20, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0 */ - 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, /* 0xe0 */ - 0x00, 0x1f, 0x0f, 0x00, 0x00, +static const struct reg_default adau1373_reg_defaults[] = { + { ADAU1373_INPUT_MODE, 0x00 }, + { ADAU1373_AINL_CTRL(0), 0x00 }, + { ADAU1373_AINR_CTRL(0), 0x00 }, + { ADAU1373_AINL_CTRL(1), 0x00 }, + { ADAU1373_AINR_CTRL(1), 0x00 }, + { ADAU1373_AINL_CTRL(2), 0x00 }, + { ADAU1373_AINR_CTRL(2), 0x00 }, + { ADAU1373_AINL_CTRL(3), 0x00 }, + { ADAU1373_AINR_CTRL(3), 0x00 }, + { ADAU1373_LLINE_OUT(0), 0x00 }, + { ADAU1373_RLINE_OUT(0), 0x00 }, + { ADAU1373_LLINE_OUT(1), 0x00 }, + { ADAU1373_RLINE_OUT(1), 0x00 }, + { ADAU1373_LSPK_OUT, 0x00 }, + { ADAU1373_RSPK_OUT, 0x00 }, + { ADAU1373_LHP_OUT, 0x00 }, + { ADAU1373_RHP_OUT, 0x00 }, + { ADAU1373_ADC_GAIN, 0x00 }, + { ADAU1373_LADC_MIXER, 0x00 }, + { ADAU1373_RADC_MIXER, 0x00 }, + { ADAU1373_LLINE1_MIX, 0x00 }, + { ADAU1373_RLINE1_MIX, 0x00 }, + { ADAU1373_LLINE2_MIX, 0x00 }, + { ADAU1373_RLINE2_MIX, 0x00 }, + { ADAU1373_LSPK_MIX, 0x00 }, + { ADAU1373_RSPK_MIX, 0x00 }, + { ADAU1373_LHP_MIX, 0x00 }, + { ADAU1373_RHP_MIX, 0x00 }, + { ADAU1373_EP_MIX, 0x00 }, + { ADAU1373_HP_CTRL, 0x00 }, + { ADAU1373_HP_CTRL2, 0x00 }, + { ADAU1373_LS_CTRL, 0x00 }, + { ADAU1373_EP_CTRL, 0x00 }, + { ADAU1373_MICBIAS_CTRL1, 0x00 }, + { ADAU1373_MICBIAS_CTRL2, 0x00 }, + { ADAU1373_OUTPUT_CTRL, 0x00 }, + { ADAU1373_PWDN_CTRL1, 0x00 }, + { ADAU1373_PWDN_CTRL2, 0x00 }, + { ADAU1373_PWDN_CTRL3, 0x00 }, + { ADAU1373_DPLL_CTRL(0), 0x00 }, + { ADAU1373_PLL_CTRL1(0), 0x00 }, + { ADAU1373_PLL_CTRL2(0), 0x00 }, + { ADAU1373_PLL_CTRL3(0), 0x00 }, + { ADAU1373_PLL_CTRL4(0), 0x00 }, + { ADAU1373_PLL_CTRL5(0), 0x00 }, + { ADAU1373_PLL_CTRL6(0), 0x02 }, + { ADAU1373_DPLL_CTRL(1), 0x00 }, + { ADAU1373_PLL_CTRL1(1), 0x00 }, + { ADAU1373_PLL_CTRL2(1), 0x00 }, + { ADAU1373_PLL_CTRL3(1), 0x00 }, + { ADAU1373_PLL_CTRL4(1), 0x00 }, + { ADAU1373_PLL_CTRL5(1), 0x00 }, + { ADAU1373_PLL_CTRL6(1), 0x02 }, + { ADAU1373_HEADDECT, 0x00 }, + { ADAU1373_ADC_CTRL, 0x00 }, + { ADAU1373_CLK_SRC_DIV(0), 0x00 }, + { ADAU1373_CLK_SRC_DIV(1), 0x00 }, + { ADAU1373_DAI(0), 0x0a }, + { ADAU1373_DAI(1), 0x0a }, + { ADAU1373_DAI(2), 0x0a }, + { ADAU1373_BCLKDIV(0), 0x00 }, + { ADAU1373_BCLKDIV(1), 0x00 }, + { ADAU1373_BCLKDIV(2), 0x00 }, + { ADAU1373_SRC_RATIOA(0), 0x00 }, + { ADAU1373_SRC_RATIOB(0), 0x00 }, + { ADAU1373_SRC_RATIOA(1), 0x00 }, + { ADAU1373_SRC_RATIOB(1), 0x00 }, + { ADAU1373_SRC_RATIOA(2), 0x00 }, + { ADAU1373_SRC_RATIOB(2), 0x00 }, + { ADAU1373_DEEMP_CTRL, 0x00 }, + { ADAU1373_SRC_DAI_CTRL(0), 0x08 }, + { ADAU1373_SRC_DAI_CTRL(1), 0x08 }, + { ADAU1373_SRC_DAI_CTRL(2), 0x08 }, + { ADAU1373_DIN_MIX_CTRL(0), 0x00 }, + { ADAU1373_DIN_MIX_CTRL(1), 0x00 }, + { ADAU1373_DIN_MIX_CTRL(2), 0x00 }, + { ADAU1373_DIN_MIX_CTRL(3), 0x00 }, + { ADAU1373_DIN_MIX_CTRL(4), 0x00 }, + { ADAU1373_DOUT_MIX_CTRL(0), 0x00 }, + { ADAU1373_DOUT_MIX_CTRL(1), 0x00 }, + { ADAU1373_DOUT_MIX_CTRL(2), 0x00 }, + { ADAU1373_DOUT_MIX_CTRL(3), 0x00 }, + { ADAU1373_DOUT_MIX_CTRL(4), 0x00 }, + { ADAU1373_DAI_PBL_VOL(0), 0x00 }, + { ADAU1373_DAI_PBR_VOL(0), 0x00 }, + { ADAU1373_DAI_PBL_VOL(1), 0x00 }, + { ADAU1373_DAI_PBR_VOL(1), 0x00 }, + { ADAU1373_DAI_PBL_VOL(2), 0x00 }, + { ADAU1373_DAI_PBR_VOL(2), 0x00 }, + { ADAU1373_DAI_RECL_VOL(0), 0x00 }, + { ADAU1373_DAI_RECR_VOL(0), 0x00 }, + { ADAU1373_DAI_RECL_VOL(1), 0x00 }, + { ADAU1373_DAI_RECR_VOL(1), 0x00 }, + { ADAU1373_DAI_RECL_VOL(2), 0x00 }, + { ADAU1373_DAI_RECR_VOL(2), 0x00 }, + { ADAU1373_DAC1_PBL_VOL, 0x00 }, + { ADAU1373_DAC1_PBR_VOL, 0x00 }, + { ADAU1373_DAC2_PBL_VOL, 0x00 }, + { ADAU1373_DAC2_PBR_VOL, 0x00 }, + { ADAU1373_ADC_RECL_VOL, 0x00 }, + { ADAU1373_ADC_RECR_VOL, 0x00 }, + { ADAU1373_DMIC_RECL_VOL, 0x00 }, + { ADAU1373_DMIC_RECR_VOL, 0x00 }, + { ADAU1373_VOL_GAIN1, 0x00 }, + { ADAU1373_VOL_GAIN2, 0x00 }, + { ADAU1373_VOL_GAIN3, 0x00 }, + { ADAU1373_HPF_CTRL, 0x00 }, + { ADAU1373_BASS1, 0x00 }, + { ADAU1373_BASS2, 0x00 }, + { ADAU1373_DRC(0) + 0x0, 0x78 }, + { ADAU1373_DRC(0) + 0x1, 0x18 }, + { ADAU1373_DRC(0) + 0x2, 0x00 }, + { ADAU1373_DRC(0) + 0x3, 0x00 }, + { ADAU1373_DRC(0) + 0x4, 0x00 }, + { ADAU1373_DRC(0) + 0x5, 0xc0 }, + { ADAU1373_DRC(0) + 0x6, 0x00 }, + { ADAU1373_DRC(0) + 0x7, 0x00 }, + { ADAU1373_DRC(0) + 0x8, 0x00 }, + { ADAU1373_DRC(0) + 0x9, 0xc0 }, + { ADAU1373_DRC(0) + 0xa, 0x88 }, + { ADAU1373_DRC(0) + 0xb, 0x7a }, + { ADAU1373_DRC(0) + 0xc, 0xdf }, + { ADAU1373_DRC(0) + 0xd, 0x20 }, + { ADAU1373_DRC(0) + 0xe, 0x00 }, + { ADAU1373_DRC(0) + 0xf, 0x00 }, + { ADAU1373_DRC(1) + 0x0, 0x78 }, + { ADAU1373_DRC(1) + 0x1, 0x18 }, + { ADAU1373_DRC(1) + 0x2, 0x00 }, + { ADAU1373_DRC(1) + 0x3, 0x00 }, + { ADAU1373_DRC(1) + 0x4, 0x00 }, + { ADAU1373_DRC(1) + 0x5, 0xc0 }, + { ADAU1373_DRC(1) + 0x6, 0x00 }, + { ADAU1373_DRC(1) + 0x7, 0x00 }, + { ADAU1373_DRC(1) + 0x8, 0x00 }, + { ADAU1373_DRC(1) + 0x9, 0xc0 }, + { ADAU1373_DRC(1) + 0xa, 0x88 }, + { ADAU1373_DRC(1) + 0xb, 0x7a }, + { ADAU1373_DRC(1) + 0xc, 0xdf }, + { ADAU1373_DRC(1) + 0xd, 0x20 }, + { ADAU1373_DRC(1) + 0xe, 0x00 }, + { ADAU1373_DRC(1) + 0xf, 0x00 }, + { ADAU1373_DRC(2) + 0x0, 0x78 }, + { ADAU1373_DRC(2) + 0x1, 0x18 }, + { ADAU1373_DRC(2) + 0x2, 0x00 }, + { ADAU1373_DRC(2) + 0x3, 0x00 }, + { ADAU1373_DRC(2) + 0x4, 0x00 }, + { ADAU1373_DRC(2) + 0x5, 0xc0 }, + { ADAU1373_DRC(2) + 0x6, 0x00 }, + { ADAU1373_DRC(2) + 0x7, 0x00 }, + { ADAU1373_DRC(2) + 0x8, 0x00 }, + { ADAU1373_DRC(2) + 0x9, 0xc0 }, + { ADAU1373_DRC(2) + 0xa, 0x88 }, + { ADAU1373_DRC(2) + 0xb, 0x7a }, + { ADAU1373_DRC(2) + 0xc, 0xdf }, + { ADAU1373_DRC(2) + 0xd, 0x20 }, + { ADAU1373_DRC(2) + 0xe, 0x00 }, + { ADAU1373_DRC(2) + 0xf, 0x00 }, + { ADAU1373_3D_CTRL1, 0x00 }, + { ADAU1373_3D_CTRL2, 0x00 }, + { ADAU1373_FDSP_SEL1, 0x00 }, + { ADAU1373_FDSP_SEL2, 0x00 }, + { ADAU1373_FDSP_SEL2, 0x00 }, + { ADAU1373_FDSP_SEL4, 0x00 }, + { ADAU1373_DIGMICCTRL, 0x00 }, + { ADAU1373_DIGEN, 0x00 }, }; static const unsigned int adau1373_out_tlv[] = { @@ -418,6 +554,7 @@ static int adau1373_pll_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = w->codec; + struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec); unsigned int pll_id = w->name[3] - '1'; unsigned int val; @@ -426,7 +563,7 @@ static int adau1373_pll_event(struct snd_soc_dapm_widget *w, else val = 0; - snd_soc_update_bits(codec, ADAU1373_PLL_CTRL6(pll_id), + regmap_update_bits(adau1373->regmap, ADAU1373_PLL_CTRL6(pll_id), ADAU1373_PLL_CTRL6_PLL_EN, val); if (SND_SOC_DAPM_EVENT_ON(event)) @@ -938,7 +1075,7 @@ static int adau1373_hw_params(struct snd_pcm_substream *substream, adau1373_dai->enable_src = (div != 0); - snd_soc_update_bits(codec, ADAU1373_BCLKDIV(dai->id), + regmap_update_bits(adau1373->regmap, ADAU1373_BCLKDIV(dai->id), ADAU1373_BCLKDIV_SR_MASK | ADAU1373_BCLKDIV_BCLK_MASK, (div << 2) | ADAU1373_BCLKDIV_64); @@ -959,7 +1096,7 @@ static int adau1373_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - return snd_soc_update_bits(codec, ADAU1373_DAI(dai->id), + return regmap_update_bits(adau1373->regmap, ADAU1373_DAI(dai->id), ADAU1373_DAI_WLEN_MASK, ctrl); } @@ -1016,7 +1153,7 @@ static int adau1373_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) return -EINVAL; } - snd_soc_update_bits(codec, ADAU1373_DAI(dai->id), + regmap_update_bits(adau1373->regmap, ADAU1373_DAI(dai->id), ~ADAU1373_DAI_WLEN_MASK, ctrl); return 0; @@ -1039,7 +1176,7 @@ static int adau1373_set_dai_sysclk(struct snd_soc_dai *dai, adau1373_dai->sysclk = freq; adau1373_dai->clk_src = clk_id; - snd_soc_update_bits(dai->codec, ADAU1373_BCLKDIV(dai->id), + regmap_update_bits(adau1373->regmap, ADAU1373_BCLKDIV(dai->id), ADAU1373_BCLKDIV_SOURCE, clk_id << 5); return 0; @@ -1120,6 +1257,7 @@ static struct snd_soc_dai_driver adau1373_dai_driver[] = { static int adau1373_set_pll(struct snd_soc_codec *codec, int pll_id, int source, unsigned int freq_in, unsigned int freq_out) { + struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec); unsigned int dpll_div = 0; unsigned int x, r, n, m, i, j, mode; @@ -1187,36 +1325,36 @@ static int adau1373_set_pll(struct snd_soc_codec *codec, int pll_id, if (dpll_div) { dpll_div = 11 - dpll_div; - snd_soc_update_bits(codec, ADAU1373_PLL_CTRL6(pll_id), + regmap_update_bits(adau1373->regmap, ADAU1373_PLL_CTRL6(pll_id), ADAU1373_PLL_CTRL6_DPLL_BYPASS, 0); } else { - snd_soc_update_bits(codec, ADAU1373_PLL_CTRL6(pll_id), + regmap_update_bits(adau1373->regmap, ADAU1373_PLL_CTRL6(pll_id), ADAU1373_PLL_CTRL6_DPLL_BYPASS, ADAU1373_PLL_CTRL6_DPLL_BYPASS); } - snd_soc_write(codec, ADAU1373_DPLL_CTRL(pll_id), + regmap_write(adau1373->regmap, ADAU1373_DPLL_CTRL(pll_id), (source << 4) | dpll_div); - snd_soc_write(codec, ADAU1373_PLL_CTRL1(pll_id), (m >> 8) & 0xff); - snd_soc_write(codec, ADAU1373_PLL_CTRL2(pll_id), m & 0xff); - snd_soc_write(codec, ADAU1373_PLL_CTRL3(pll_id), (n >> 8) & 0xff); - snd_soc_write(codec, ADAU1373_PLL_CTRL4(pll_id), n & 0xff); - snd_soc_write(codec, ADAU1373_PLL_CTRL5(pll_id), + regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL1(pll_id), (m >> 8) & 0xff); + regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL2(pll_id), m & 0xff); + regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL3(pll_id), (n >> 8) & 0xff); + regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL4(pll_id), n & 0xff); + regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL5(pll_id), (r << 3) | (x << 1) | mode); /* Set sysclk to pll_rate / 4 */ - snd_soc_update_bits(codec, ADAU1373_CLK_SRC_DIV(pll_id), 0x3f, 0x09); + regmap_update_bits(adau1373->regmap, ADAU1373_CLK_SRC_DIV(pll_id), 0x3f, 0x09); return 0; } -static void adau1373_load_drc_settings(struct snd_soc_codec *codec, +static void adau1373_load_drc_settings(struct adau1373 *adau1373, unsigned int nr, uint8_t *drc) { unsigned int i; for (i = 0; i < ADAU1373_DRC_SIZE; ++i) - snd_soc_write(codec, ADAU1373_DRC(nr) + i, drc[i]); + regmap_write(adau1373->regmap, ADAU1373_DRC(nr) + i, drc[i]); } static bool adau1373_valid_micbias(enum adau1373_micbias_voltage micbias) @@ -1235,13 +1373,14 @@ static bool adau1373_valid_micbias(enum adau1373_micbias_voltage micbias) static int adau1373_probe(struct snd_soc_codec *codec) { + struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec); struct adau1373_platform_data *pdata = codec->dev->platform_data; bool lineout_differential = false; unsigned int val; int ret; int i; - ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C); + ret = snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP); if (ret) { dev_err(codec->dev, "failed to set cache I/O: %d\n", ret); return ret; @@ -1256,7 +1395,7 @@ static int adau1373_probe(struct snd_soc_codec *codec) return -EINVAL; for (i = 0; i < pdata->num_drc; ++i) { - adau1373_load_drc_settings(codec, i, + adau1373_load_drc_settings(adau1373, i, pdata->drc_setting[i]); } @@ -1268,18 +1407,18 @@ static int adau1373_probe(struct snd_soc_codec *codec) if (pdata->input_differential[i]) val |= BIT(i); } - snd_soc_write(codec, ADAU1373_INPUT_MODE, val); + regmap_write(adau1373->regmap, ADAU1373_INPUT_MODE, val); val = 0; if (pdata->lineout_differential) val |= ADAU1373_OUTPUT_CTRL_LDIFF; if (pdata->lineout_ground_sense) val |= ADAU1373_OUTPUT_CTRL_LNFBEN; - snd_soc_write(codec, ADAU1373_OUTPUT_CTRL, val); + regmap_write(adau1373->regmap, ADAU1373_OUTPUT_CTRL, val); lineout_differential = pdata->lineout_differential; - snd_soc_write(codec, ADAU1373_EP_CTRL, + regmap_write(adau1373->regmap, ADAU1373_EP_CTRL, (pdata->micbias1 << ADAU1373_EP_CTRL_MICBIAS1_OFFSET) | (pdata->micbias2 << ADAU1373_EP_CTRL_MICBIAS2_OFFSET)); } @@ -1289,7 +1428,7 @@ static int adau1373_probe(struct snd_soc_codec *codec) ARRAY_SIZE(adau1373_lineout2_controls)); } - snd_soc_write(codec, ADAU1373_ADC_CTRL, + regmap_write(adau1373->regmap, ADAU1373_ADC_CTRL, ADAU1373_ADC_CTRL_RESET_FORCE | ADAU1373_ADC_CTRL_PEAK_DETECT); return 0; @@ -1298,17 +1437,19 @@ static int adau1373_probe(struct snd_soc_codec *codec) static int adau1373_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec); + switch (level) { case SND_SOC_BIAS_ON: break; case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - snd_soc_update_bits(codec, ADAU1373_PWDN_CTRL3, + regmap_update_bits(adau1373->regmap, ADAU1373_PWDN_CTRL3, ADAU1373_PWDN_CTRL3_PWR_EN, ADAU1373_PWDN_CTRL3_PWR_EN); break; case SND_SOC_BIAS_OFF: - snd_soc_update_bits(codec, ADAU1373_PWDN_CTRL3, + regmap_update_bits(adau1373->regmap, ADAU1373_PWDN_CTRL3, ADAU1373_PWDN_CTRL3_PWR_EN, 0); break; } @@ -1324,17 +1465,49 @@ static int adau1373_remove(struct snd_soc_codec *codec) static int adau1373_suspend(struct snd_soc_codec *codec) { - return adau1373_set_bias_level(codec, SND_SOC_BIAS_OFF); + struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec); + int ret; + + ret = adau1373_set_bias_level(codec, SND_SOC_BIAS_OFF); + regcache_cache_only(adau1373->regmap, true); + + return ret; } static int adau1373_resume(struct snd_soc_codec *codec) { + struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec); + + regcache_cache_only(adau1373->regmap, false); adau1373_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - snd_soc_cache_sync(codec); + regcache_sync(adau1373->regmap); return 0; } +static bool adau1373_register_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADAU1373_SOFT_RESET: + case ADAU1373_ADC_DAC_STATUS: + return true; + default: + return false; + } +} + +static const struct regmap_config adau1373_regmap_config = { + .val_bits = 8, + .reg_bits = 8, + + .volatile_reg = adau1373_register_volatile, + .max_register = ADAU1373_SOFT_RESET, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = adau1373_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(adau1373_reg_defaults), +}; + static struct snd_soc_codec_driver adau1373_codec_driver = { .probe = adau1373_probe, .remove = adau1373_remove, @@ -1342,9 +1515,6 @@ static struct snd_soc_codec_driver adau1373_codec_driver = { .resume = adau1373_resume, .set_bias_level = adau1373_set_bias_level, .idle_bias_off = true, - .reg_cache_size = ARRAY_SIZE(adau1373_default_regs), - .reg_cache_default = adau1373_default_regs, - .reg_word_size = sizeof(uint8_t), .set_pll = adau1373_set_pll, @@ -1366,6 +1536,11 @@ static int adau1373_i2c_probe(struct i2c_client *client, if (!adau1373) return -ENOMEM; + adau1373->regmap = devm_regmap_init_i2c(client, + &adau1373_regmap_config); + if (IS_ERR(adau1373->regmap)) + return PTR_ERR(adau1373->regmap); + dev_set_drvdata(&client->dev, adau1373); ret = snd_soc_register_codec(&client->dev, &adau1373_codec_driver, -- cgit v0.10.2 From 6fb04138a3068609fa0ef3a98b60e31e686b3160 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 27 Sep 2013 13:47:09 +0200 Subject: ASoC: adau1373: Remove ADAU1373_PLL_CTRL7 register definition There is no such register. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c index c57c1f8..2f84054 100644 --- a/sound/soc/codecs/adau1373.c +++ b/sound/soc/codecs/adau1373.c @@ -74,7 +74,6 @@ struct adau1373 { #define ADAU1373_PLL_CTRL4(x) (0x2c + (x) * 7) #define ADAU1373_PLL_CTRL5(x) (0x2d + (x) * 7) #define ADAU1373_PLL_CTRL6(x) (0x2e + (x) * 7) -#define ADAU1373_PLL_CTRL7(x) (0x2f + (x) * 7) #define ADAU1373_HEADDECT 0x36 #define ADAU1373_ADC_DAC_STATUS 0x37 #define ADAU1373_ADC_CTRL 0x3c -- cgit v0.10.2 From 729485f6adbf1c7e1e08a01d2c276da30a91b0a4 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 27 Sep 2013 13:47:10 +0200 Subject: ASoC: adau1373: Issue soft reset on probe Reset the device on probe to make sure that the register settings match the register cache defaults. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c index 2f84054..59654b1 100644 --- a/sound/soc/codecs/adau1373.c +++ b/sound/soc/codecs/adau1373.c @@ -1540,6 +1540,8 @@ static int adau1373_i2c_probe(struct i2c_client *client, if (IS_ERR(adau1373->regmap)) return PTR_ERR(adau1373->regmap); + regmap_write(adau1373->regmap, ADAU1373_SOFT_RESET, 0x00); + dev_set_drvdata(&client->dev, adau1373); ret = snd_soc_register_codec(&client->dev, &adau1373_codec_driver, -- cgit v0.10.2 From 2560b3d1bdf1344aa65bba1523a08e4db27a3c14 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 27 Sep 2013 15:18:25 +0200 Subject: ASoC: adav80x: Convert to direct regmap usage Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c index 15b012d0..14a7c16 100644 --- a/sound/soc/codecs/adav80x.c +++ b/sound/soc/codecs/adav80x.c @@ -115,22 +115,34 @@ #define ADAV80X_PLL_OUTE_SYSCLKPD(x) BIT(2 - (x)) -static u8 adav80x_default_regs[] = { - 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01, 0x80, 0x26, 0x00, 0x00, - 0x02, 0x40, 0x20, 0x00, 0x09, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd1, 0x92, 0xb1, 0x37, - 0x48, 0xd2, 0xfb, 0xca, 0xd2, 0x15, 0xe8, 0x29, 0xb9, 0x6a, 0xda, 0x2b, - 0xb7, 0xc0, 0x11, 0x65, 0x5c, 0xf6, 0xff, 0x8d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, - 0x00, 0xe8, 0x46, 0xe1, 0x5b, 0xd3, 0x43, 0x77, 0x93, 0xa7, 0x44, 0xee, - 0x32, 0x12, 0xc0, 0x11, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x3f, 0x3f, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, +static struct reg_default adav80x_reg_defaults[] = { + { ADAV80X_PLAYBACK_CTRL, 0x01 }, + { ADAV80X_AUX_IN_CTRL, 0x01 }, + { ADAV80X_REC_CTRL, 0x02 }, + { ADAV80X_AUX_OUT_CTRL, 0x01 }, + { ADAV80X_DPATH_CTRL1, 0xc0 }, + { ADAV80X_DPATH_CTRL2, 0x11 }, + { ADAV80X_DAC_CTRL1, 0x00 }, + { ADAV80X_DAC_CTRL2, 0x00 }, + { ADAV80X_DAC_CTRL3, 0x00 }, + { ADAV80X_DAC_L_VOL, 0xff }, + { ADAV80X_DAC_R_VOL, 0xff }, + { ADAV80X_PGA_L_VOL, 0x00 }, + { ADAV80X_PGA_R_VOL, 0x00 }, + { ADAV80X_ADC_CTRL1, 0x00 }, + { ADAV80X_ADC_CTRL2, 0x00 }, + { ADAV80X_ADC_L_VOL, 0xff }, + { ADAV80X_ADC_R_VOL, 0xff }, + { ADAV80X_PLL_CTRL1, 0x00 }, + { ADAV80X_PLL_CTRL2, 0x00 }, + { ADAV80X_ICLK_CTRL1, 0x00 }, + { ADAV80X_ICLK_CTRL2, 0x00 }, + { ADAV80X_PLL_CLK_SRC, 0x00 }, + { ADAV80X_PLL_OUTE, 0x00 }, }; struct adav80x { - enum snd_soc_control_type control_type; + struct regmap *regmap; enum adav80x_clk_src clk_src; unsigned int sysclk; @@ -298,7 +310,7 @@ static int adav80x_set_deemph(struct snd_soc_codec *codec) val = ADAV80X_DAC_CTRL2_DEEMPH_NONE; } - return snd_soc_update_bits(codec, ADAV80X_DAC_CTRL2, + return regmap_update_bits(adav80x->regmap, ADAV80X_DAC_CTRL2, ADAV80X_DAC_CTRL2_DEEMPH_MASK, val); } @@ -394,10 +406,11 @@ static int adav80x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) return -EINVAL; } - snd_soc_update_bits(codec, adav80x_port_ctrl_regs[dai->id][0], + regmap_update_bits(adav80x->regmap, adav80x_port_ctrl_regs[dai->id][0], ADAV80X_CAPTURE_MODE_MASK | ADAV80X_CAPTURE_MODE_MASTER, capture); - snd_soc_write(codec, adav80x_port_ctrl_regs[dai->id][1], playback); + regmap_write(adav80x->regmap, adav80x_port_ctrl_regs[dai->id][1], + playback); adav80x->dai_fmt[dai->id] = fmt & SND_SOC_DAIFMT_FORMAT_MASK; @@ -407,6 +420,7 @@ static int adav80x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) static int adav80x_set_adc_clock(struct snd_soc_codec *codec, unsigned int sample_rate) { + struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); unsigned int val; if (sample_rate <= 48000) @@ -414,7 +428,7 @@ static int adav80x_set_adc_clock(struct snd_soc_codec *codec, else val = ADAV80X_ADC_CTRL1_MODULATOR_64FS; - snd_soc_update_bits(codec, ADAV80X_ADC_CTRL1, + regmap_update_bits(adav80x->regmap, ADAV80X_ADC_CTRL1, ADAV80X_ADC_CTRL1_MODULATOR_MASK, val); return 0; @@ -423,6 +437,7 @@ static int adav80x_set_adc_clock(struct snd_soc_codec *codec, static int adav80x_set_dac_clock(struct snd_soc_codec *codec, unsigned int sample_rate) { + struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); unsigned int val; if (sample_rate <= 48000) @@ -430,7 +445,7 @@ static int adav80x_set_dac_clock(struct snd_soc_codec *codec, else val = ADAV80X_DAC_CTRL2_DIV2 | ADAV80X_DAC_CTRL2_INTERPOL_128FS; - snd_soc_update_bits(codec, ADAV80X_DAC_CTRL2, + regmap_update_bits(adav80x->regmap, ADAV80X_DAC_CTRL2, ADAV80X_DAC_CTRL2_DIV_MASK | ADAV80X_DAC_CTRL2_INTERPOL_MASK, val); @@ -440,6 +455,7 @@ static int adav80x_set_dac_clock(struct snd_soc_codec *codec, static int adav80x_set_capture_pcm_format(struct snd_soc_codec *codec, struct snd_soc_dai *dai, snd_pcm_format_t format) { + struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); unsigned int val; switch (format) { @@ -459,7 +475,7 @@ static int adav80x_set_capture_pcm_format(struct snd_soc_codec *codec, return -EINVAL; } - snd_soc_update_bits(codec, adav80x_port_ctrl_regs[dai->id][0], + regmap_update_bits(adav80x->regmap, adav80x_port_ctrl_regs[dai->id][0], ADAV80X_CAPTURE_WORD_LEN_MASK, val); return 0; @@ -491,7 +507,7 @@ static int adav80x_set_playback_pcm_format(struct snd_soc_codec *codec, return -EINVAL; } - snd_soc_update_bits(codec, adav80x_port_ctrl_regs[dai->id][1], + regmap_update_bits(adav80x->regmap, adav80x_port_ctrl_regs[dai->id][1], ADAV80X_PLAYBACK_MODE_MASK, val); return 0; @@ -554,8 +570,10 @@ static int adav80x_set_sysclk(struct snd_soc_codec *codec, ADAV80X_ICLK_CTRL1_ICLK2_SRC(clk_id); iclk_ctrl2 = ADAV80X_ICLK_CTRL2_ICLK1_SRC(clk_id); - snd_soc_write(codec, ADAV80X_ICLK_CTRL1, iclk_ctrl1); - snd_soc_write(codec, ADAV80X_ICLK_CTRL2, iclk_ctrl2); + regmap_write(adav80x->regmap, ADAV80X_ICLK_CTRL1, + iclk_ctrl1); + regmap_write(adav80x->regmap, ADAV80X_ICLK_CTRL2, + iclk_ctrl2); snd_soc_dapm_sync(&codec->dapm); } @@ -575,10 +593,12 @@ static int adav80x_set_sysclk(struct snd_soc_codec *codec, mask = ADAV80X_PLL_OUTE_SYSCLKPD(clk_id); if (freq == 0) { - snd_soc_update_bits(codec, ADAV80X_PLL_OUTE, mask, mask); + regmap_update_bits(adav80x->regmap, ADAV80X_PLL_OUTE, + mask, mask); adav80x->sysclk_pd[clk_id] = true; } else { - snd_soc_update_bits(codec, ADAV80X_PLL_OUTE, mask, 0); + regmap_update_bits(adav80x->regmap, ADAV80X_PLL_OUTE, + mask, 0); adav80x->sysclk_pd[clk_id] = false; } @@ -650,9 +670,9 @@ static int adav80x_set_pll(struct snd_soc_codec *codec, int pll_id, return -EINVAL; } - snd_soc_update_bits(codec, ADAV80X_PLL_CTRL1, ADAV80X_PLL_CTRL1_PLLDIV, - pll_ctrl1); - snd_soc_update_bits(codec, ADAV80X_PLL_CTRL2, + regmap_update_bits(adav80x->regmap, ADAV80X_PLL_CTRL1, + ADAV80X_PLL_CTRL1_PLLDIV, pll_ctrl1); + regmap_update_bits(adav80x->regmap, ADAV80X_PLL_CTRL2, ADAV80X_PLL_CTRL2_PLL_MASK(pll_id), pll_ctrl2); if (source != adav80x->pll_src) { @@ -661,7 +681,7 @@ static int adav80x_set_pll(struct snd_soc_codec *codec, int pll_id, else pll_src = ADAV80X_PLL_CLK_SRC_PLL_XIN(pll_id); - snd_soc_update_bits(codec, ADAV80X_PLL_CLK_SRC, + regmap_update_bits(adav80x->regmap, ADAV80X_PLL_CLK_SRC, ADAV80X_PLL_CLK_SRC_PLL_MASK(pll_id), pll_src); adav80x->pll_src = source; @@ -675,6 +695,7 @@ static int adav80x_set_pll(struct snd_soc_codec *codec, int pll_id, static int adav80x_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); unsigned int mask = ADAV80X_DAC_CTRL1_PD; switch (level) { @@ -683,10 +704,12 @@ static int adav80x_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - snd_soc_update_bits(codec, ADAV80X_DAC_CTRL1, mask, 0x00); + regmap_update_bits(adav80x->regmap, ADAV80X_DAC_CTRL1, mask, + 0x00); break; case SND_SOC_BIAS_OFF: - snd_soc_update_bits(codec, ADAV80X_DAC_CTRL1, mask, mask); + regmap_update_bits(adav80x->regmap, ADAV80X_DAC_CTRL1, mask, + mask); break; } @@ -780,7 +803,7 @@ static int adav80x_probe(struct snd_soc_codec *codec) int ret; struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); - ret = snd_soc_codec_set_cache_io(codec, 7, 9, adav80x->control_type); + ret = snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP); if (ret) { dev_err(codec->dev, "failed to set cache I/O: %d\n", ret); return ret; @@ -791,23 +814,31 @@ static int adav80x_probe(struct snd_soc_codec *codec) snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL2"); /* Power down S/PDIF receiver, since it is currently not supported */ - snd_soc_write(codec, ADAV80X_PLL_OUTE, 0x20); + regmap_write(adav80x->regmap, ADAV80X_PLL_OUTE, 0x20); /* Disable DAC zero flag */ - snd_soc_write(codec, ADAV80X_DAC_CTRL3, 0x6); + regmap_write(adav80x->regmap, ADAV80X_DAC_CTRL3, 0x6); return adav80x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); } static int adav80x_suspend(struct snd_soc_codec *codec) { - return adav80x_set_bias_level(codec, SND_SOC_BIAS_OFF); + struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); + int ret; + + ret = adav80x_set_bias_level(codec, SND_SOC_BIAS_OFF); + regcache_cache_only(adav80x->regmap, true); + + return ret; } static int adav80x_resume(struct snd_soc_codec *codec) { + struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); + + regcache_cache_only(adav80x->regmap, false); adav80x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - codec->cache_sync = 1; - snd_soc_cache_sync(codec); + regcache_sync(adav80x->regmap); return 0; } @@ -827,10 +858,6 @@ static struct snd_soc_codec_driver adav80x_codec_driver = { .set_pll = adav80x_set_pll, .set_sysclk = adav80x_set_sysclk, - .reg_word_size = sizeof(u8), - .reg_cache_size = ARRAY_SIZE(adav80x_default_regs), - .reg_cache_default = adav80x_default_regs, - .controls = adav80x_controls, .num_controls = ARRAY_SIZE(adav80x_controls), .dapm_widgets = adav80x_dapm_widgets, @@ -839,18 +866,21 @@ static struct snd_soc_codec_driver adav80x_codec_driver = { .num_dapm_routes = ARRAY_SIZE(adav80x_dapm_routes), }; -static int adav80x_bus_probe(struct device *dev, - enum snd_soc_control_type control_type) +static int adav80x_bus_probe(struct device *dev, struct regmap *regmap) { struct adav80x *adav80x; int ret; + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + adav80x = kzalloc(sizeof(*adav80x), GFP_KERNEL); if (!adav80x) return -ENOMEM; + dev_set_drvdata(dev, adav80x); - adav80x->control_type = control_type; + adav80x->regmap = regmap; ret = snd_soc_register_codec(dev, &adav80x_codec_driver, adav80x_dais, ARRAY_SIZE(adav80x_dais)); @@ -868,6 +898,19 @@ static int adav80x_bus_remove(struct device *dev) } #if defined(CONFIG_SPI_MASTER) +static const struct regmap_config adav80x_spi_regmap_config = { + .val_bits = 8, + .pad_bits = 1, + .reg_bits = 7, + .read_flag_mask = 0x01, + + .max_register = ADAV80X_PLL_OUTE, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = adav80x_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(adav80x_reg_defaults), +}; + static const struct spi_device_id adav80x_spi_id[] = { { "adav801", 0 }, { } @@ -876,7 +919,8 @@ MODULE_DEVICE_TABLE(spi, adav80x_spi_id); static int adav80x_spi_probe(struct spi_device *spi) { - return adav80x_bus_probe(&spi->dev, SND_SOC_SPI); + return adav80x_bus_probe(&spi->dev, + devm_regmap_init_spi(spi, &adav80x_spi_regmap_config)); } static int adav80x_spi_remove(struct spi_device *spi) @@ -896,6 +940,18 @@ static struct spi_driver adav80x_spi_driver = { #endif #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static const struct regmap_config adav80x_i2c_regmap_config = { + .val_bits = 8, + .pad_bits = 1, + .reg_bits = 7, + + .max_register = ADAV80X_PLL_OUTE, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = adav80x_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(adav80x_reg_defaults), +}; + static const struct i2c_device_id adav80x_i2c_id[] = { { "adav803", 0 }, { } @@ -905,7 +961,8 @@ MODULE_DEVICE_TABLE(i2c, adav80x_i2c_id); static int adav80x_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { - return adav80x_bus_probe(&client->dev, SND_SOC_I2C); + return adav80x_bus_probe(&client->dev, + devm_regmap_init_i2c(client, &adav80x_i2c_regmap_config)); } static int adav80x_i2c_remove(struct i2c_client *client) -- cgit v0.10.2 From 6090e9a6eaf0ecf38213f0cbaaa0b0147760432f Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Thu, 26 Sep 2013 18:18:04 +0530 Subject: pinctrl: palmas: remove non-require function Palmas pinmux and pin configuration support the single pin level configuration in place of pin group. Hence it is only require to pin_config_{set|get} and do not require pin_config_group_{set|get}. As core framework already check for require APIs availability, it is not require to implement as dummy for non-require ops and so removing it. Signed-off-by: Laxman Dewangan Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/pinctrl-palmas.c b/drivers/pinctrl/pinctrl-palmas.c index 82638fa..e4d61fa 100644 --- a/drivers/pinctrl/pinctrl-palmas.c +++ b/drivers/pinctrl/pinctrl-palmas.c @@ -961,26 +961,9 @@ static int palmas_pinconf_set(struct pinctrl_dev *pctldev, return 0; } -static int palmas_pinconf_group_get(struct pinctrl_dev *pctldev, - unsigned group, unsigned long *config) -{ - dev_err(pctldev->dev, "palmas_pinconf_group_get op not supported\n"); - return -ENOTSUPP; -} - -static int palmas_pinconf_group_set(struct pinctrl_dev *pctldev, - unsigned group, unsigned long *configs, - unsigned num_configs) -{ - dev_err(pctldev->dev, "palmas_pinconf_group_set op not supported\n"); - return -ENOTSUPP; -} - static const struct pinconf_ops palmas_pinconf_ops = { .pin_config_get = palmas_pinconf_get, .pin_config_set = palmas_pinconf_set, - .pin_config_group_get = palmas_pinconf_group_get, - .pin_config_group_set = palmas_pinconf_group_set, }; static struct pinctrl_desc palmas_pinctrl_desc = { -- cgit v0.10.2 From 70fac17cec347c4013cb8f380c6fe6554a13d884 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Sat, 31 Aug 2013 20:24:14 +0200 Subject: spi: simplify call to request_module() request_module() can handle format strings on its own, no need to create the full module name ourself. Signed-off-by: Mathias Krause Signed-off-by: Mark Brown diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 9e039c6..74a526c 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -839,7 +839,6 @@ static void of_register_spi_devices(struct spi_master *master) struct spi_device *spi; struct device_node *nc; const __be32 *prop; - char modalias[SPI_NAME_SIZE + 4]; int rc; int len; @@ -944,9 +943,7 @@ static void of_register_spi_devices(struct spi_master *master) spi->dev.of_node = nc; /* Register the new device */ - snprintf(modalias, sizeof(modalias), "%s%s", SPI_MODULE_PREFIX, - spi->modalias); - request_module(modalias); + request_module("%s%s", SPI_MODULE_PREFIX, spi->modalias); rc = spi_add_device(spi); if (rc) { dev_err(&master->dev, "spi_device register error %s\n", -- cgit v0.10.2 From 89da4293a7bb29ac42b7dd2c2573c8a5ebb0b6c7 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Fri, 27 Sep 2013 05:37:25 -0700 Subject: spi: Use of_property_read_u32 Instead of getting the raw property, checking the length, and doing endian conversion each time, use the OF function of_property_read_u32() that does all that. Error messages are slightly improved with error codes from of_property_read_u32() for different ways the property may be invalid (missing, too short, etc.) Signed-off-by: Trent Piepho Signed-off-by: Mark Brown diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 74a526c..1cd491f 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -838,9 +838,8 @@ static void of_register_spi_devices(struct spi_master *master) { struct spi_device *spi; struct device_node *nc; - const __be32 *prop; int rc; - int len; + u32 value; if (!master->dev.of_node) return; @@ -865,14 +864,14 @@ static void of_register_spi_devices(struct spi_master *master) } /* Device address */ - prop = of_get_property(nc, "reg", &len); - if (!prop || len < sizeof(*prop)) { - dev_err(&master->dev, "%s has no 'reg' property\n", - nc->full_name); + rc = of_property_read_u32(nc, "reg", &value); + if (rc) { + dev_err(&master->dev, "%s has no valid 'reg' property (%d)\n", + nc->full_name, rc); spi_dev_put(spi); continue; } - spi->chip_select = be32_to_cpup(prop); + spi->chip_select = value; /* Mode (clock phase/polarity/etc.) */ if (of_find_property(nc, "spi-cpha", NULL)) @@ -885,55 +884,53 @@ static void of_register_spi_devices(struct spi_master *master) spi->mode |= SPI_3WIRE; /* Device DUAL/QUAD mode */ - prop = of_get_property(nc, "spi-tx-bus-width", &len); - if (prop && len == sizeof(*prop)) { - switch (be32_to_cpup(prop)) { - case SPI_NBITS_SINGLE: + if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) { + switch (value) { + case 1: break; - case SPI_NBITS_DUAL: + case 2: spi->mode |= SPI_TX_DUAL; break; - case SPI_NBITS_QUAD: + case 4: spi->mode |= SPI_TX_QUAD; break; default: dev_err(&master->dev, "spi-tx-bus-width %d not supported\n", - be32_to_cpup(prop)); + value); spi_dev_put(spi); continue; } } - prop = of_get_property(nc, "spi-rx-bus-width", &len); - if (prop && len == sizeof(*prop)) { - switch (be32_to_cpup(prop)) { - case SPI_NBITS_SINGLE: + if (!of_property_read_u32(nc, "spi-rx-bus-width", &value)) { + switch (value) { + case 1: break; - case SPI_NBITS_DUAL: + case 2: spi->mode |= SPI_RX_DUAL; break; - case SPI_NBITS_QUAD: + case 4: spi->mode |= SPI_RX_QUAD; break; default: dev_err(&master->dev, "spi-rx-bus-width %d not supported\n", - be32_to_cpup(prop)); + value); spi_dev_put(spi); continue; } } /* Device speed */ - prop = of_get_property(nc, "spi-max-frequency", &len); - if (!prop || len < sizeof(*prop)) { - dev_err(&master->dev, "%s has no 'spi-max-frequency' property\n", - nc->full_name); + rc = of_property_read_u32(nc, "spi-max-frequency", &value); + if (rc) { + dev_err(&master->dev, "%s has no valid 'spi-max-frequency' property (%d)\n", + nc->full_name, rc); spi_dev_put(spi); continue; } - spi->max_speed_hz = be32_to_cpup(prop); + spi->max_speed_hz = value; /* IRQ */ spi->irq = irq_of_parse_and_map(nc, 0); -- cgit v0.10.2 From bde251a9a813bfbb8f300abef265ecb7028c428f Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Fri, 27 Sep 2013 08:25:13 -0500 Subject: regulator: ti-abb: skip optional parameter for ldo-address On platforms like OMAP4460, LDO override is never used. Even though efuse determines the ABB bias mode to operate at, ABB voltage is preconfigured in internal efuse registers without the need for LDO override for bias voltage. So skip optional parameter if property is not present. Signed-off-by: Nishanth Menon Signed-off-by: Mark Brown diff --git a/drivers/regulator/ti-abb-regulator.c b/drivers/regulator/ti-abb-regulator.c index d8e3e12..1e02805 100644 --- a/drivers/regulator/ti-abb-regulator.c +++ b/drivers/regulator/ti-abb-regulator.c @@ -765,6 +765,11 @@ static int ti_abb_probe(struct platform_device *pdev) pname = "ldo-address"; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); + if (!res) { + dev_dbg(dev, "Missing '%s' IO resource\n", pname); + ret = -ENODEV; + goto skip_opt; + } abb->ldo_base = devm_ioremap_resource(dev, res); if (IS_ERR(abb->ldo_base)) { ret = PTR_ERR(abb->ldo_base); -- cgit v0.10.2 From 75f93fed50c2abadbab6ef546b265f51ca975b27 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 27 Sep 2013 17:30:03 +0200 Subject: sched: Revert need_resched() to look at TIF_NEED_RESCHED Yuanhan reported a serious throughput regression in his pigz benchmark. Using the ftrace patch I found that several idle paths need more TLC before we can switch the generic need_resched() over to preempt_need_resched. The preemption paths benefit most from preempt_need_resched and do indeed use it; all other need_resched() users don't really care that much so reverting need_resched() back to tif_need_resched() is the simple and safe solution. Reported-by: Yuanhan Liu Signed-off-by: Peter Zijlstra Cc: Fengguang Wu Cc: Huang Ying Cc: lkp@linux.intel.com Cc: Linus Torvalds Link: http://lkml.kernel.org/r/20130927153003.GF15690@laptop.programming.kicks-ass.net Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/preempt.h b/arch/x86/include/asm/preempt.h index 1de41690..8729723 100644 --- a/arch/x86/include/asm/preempt.h +++ b/arch/x86/include/asm/preempt.h @@ -80,14 +80,6 @@ static __always_inline bool __preempt_count_dec_and_test(void) } /* - * Returns true when we need to resched -- even if we can not. - */ -static __always_inline bool need_resched(void) -{ - return unlikely(test_preempt_need_resched()); -} - -/* * Returns true when we need to resched and can (barring IRQ state). */ static __always_inline bool should_resched(void) diff --git a/include/asm-generic/preempt.h b/include/asm-generic/preempt.h index 5dc14ed..ddf2b42 100644 --- a/include/asm-generic/preempt.h +++ b/include/asm-generic/preempt.h @@ -85,14 +85,6 @@ static __always_inline bool __preempt_count_dec_and_test(void) } /* - * Returns true when we need to resched -- even if we can not. - */ -static __always_inline bool need_resched(void) -{ - return unlikely(test_preempt_need_resched()); -} - -/* * Returns true when we need to resched and can (barring IRQ state). */ static __always_inline bool should_resched(void) diff --git a/include/linux/sched.h b/include/linux/sched.h index b09798b..2ac5285 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2577,6 +2577,11 @@ static inline bool __must_check current_clr_polling_and_test(void) } #endif +static __always_inline bool need_resched(void) +{ + return unlikely(tif_need_resched()); +} + /* * Thread group CPU time accounting. */ -- cgit v0.10.2 From 646e29a1789a3a936871008c15199c50367bf291 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Fri, 27 Sep 2013 16:35:54 +0200 Subject: x86: Improve the printout of the SMP bootup CPU table As the new x86 CPU bootup printout format code maintainer, I am taking immediate action to improve and clean (and thus indulge my OCD) the reporting of the cores when coming up online. Fix padding to a right-hand alignment, cleanup code and bind reporting width to the max number of supported CPUs on the system, like this: [ 0.074509] smpboot: Booting Node 0, Processors: #1 #2 #3 #4 #5 #6 #7 OK [ 0.644008] smpboot: Booting Node 1, Processors: #8 #9 #10 #11 #12 #13 #14 #15 OK [ 1.245006] smpboot: Booting Node 2, Processors: #16 #17 #18 #19 #20 #21 #22 #23 OK [ 1.864005] smpboot: Booting Node 3, Processors: #24 #25 #26 #27 #28 #29 #30 #31 OK [ 2.489005] smpboot: Booting Node 4, Processors: #32 #33 #34 #35 #36 #37 #38 #39 OK [ 3.093005] smpboot: Booting Node 5, Processors: #40 #41 #42 #43 #44 #45 #46 #47 OK [ 3.698005] smpboot: Booting Node 6, Processors: #48 #49 #50 #51 #52 #53 #54 #55 OK [ 4.304005] smpboot: Booting Node 7, Processors: #56 #57 #58 #59 #60 #61 #62 #63 OK [ 4.961413] Brought up 64 CPUs and this: [ 0.072367] smpboot: Booting Node 0, Processors: #1 #2 #3 #4 #5 #6 #7 OK [ 0.686329] Brought up 8 CPUs Signed-off-by: Borislav Petkov Cc: Libin Cc: wangyijing@huawei.com Cc: fenghua.yu@intel.com Cc: guohanjun@huawei.com Cc: paul.gortmaker@windriver.com Link: http://lkml.kernel.org/r/20130927143554.GF4422@pd.tnic Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/misc.h b/arch/x86/include/asm/misc.h new file mode 100644 index 0000000..475f5bb --- /dev/null +++ b/arch/x86/include/asm/misc.h @@ -0,0 +1,6 @@ +#ifndef _ASM_X86_MISC_H +#define _ASM_X86_MISC_H + +int num_digits(int val); + +#endif /* _ASM_X86_MISC_H */ diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 6cacab6..d41f3ba 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -73,11 +73,10 @@ #include #include #include - #include #include - #include +#include /* State of each CPU */ DEFINE_PER_CPU(int, cpu_state) = { 0 }; @@ -653,17 +652,27 @@ static void announce_cpu(int cpu, int apicid) { static int current_node = -1; int node = early_cpu_to_node(cpu); - int max_cpu_present = find_last_bit(cpumask_bits(cpu_present_mask), NR_CPUS); + static int width; + + if (!width) + width = num_digits(num_possible_cpus()) + 1; /* + '#' sign */ if (system_state == SYSTEM_BOOTING) { if (node != current_node) { if (current_node > (-1)) pr_cont(" OK\n"); current_node = node; - pr_info("Booting Node %3d, Processors ", node); + pr_info("Booting Node %3d, Processors:", node); } - pr_cont(" #%4d%s", cpu, cpu == max_cpu_present ? " OK\n" : ""); - return; + + /* Add padding for the BSP */ + if (cpu == 1) + pr_cont("%*s", width + 1, " "); + + pr_cont("%*s#%d", width - num_digits(cpu), " ", cpu); + + if (cpu == num_present_cpus() - 1) + pr_cont(" OK\n"); } else pr_info("Booting Node %d Processor %d APIC 0x%x\n", node, cpu, apicid); diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile index 96b2c66..992d63b 100644 --- a/arch/x86/lib/Makefile +++ b/arch/x86/lib/Makefile @@ -16,7 +16,7 @@ clean-files := inat-tables.c obj-$(CONFIG_SMP) += msr-smp.o cache-smp.o -lib-y := delay.o +lib-y := delay.o misc.o lib-y += thunk_$(BITS).o lib-y += usercopy_$(BITS).o usercopy.o getuser.o putuser.o lib-y += memcpy_$(BITS).o diff --git a/arch/x86/lib/misc.c b/arch/x86/lib/misc.c new file mode 100644 index 0000000..bc35cde --- /dev/null +++ b/arch/x86/lib/misc.c @@ -0,0 +1,11 @@ +int num_digits(int val) +{ + int digits = 0; + + while (val) { + val /= 10; + digits++; + } + + return digits; +} -- cgit v0.10.2 From 594f88d19d751bd7a8eb967772922aae1e9de845 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Thu, 26 Sep 2013 10:42:02 +0100 Subject: Documentation: devicetree: arm: cpus/cpu nodes bindings updates In order to extend the current cpu nodes bindings to newer CPUs inclusive of AArch64 and to update support for older ARM CPUs this patch updates device tree documentation for the cpu nodes bindings. Main changes: - adds 64-bit bindings - define usage of #address-cells - defines behaviour on pre and post v7 uniprocessor systems - adds ARM 11MPcore specific reg property definition Signed-off-by: Lorenzo Pieralisi Acked-by: Mark Rutland Acked-by: Gregory CLEMENT Acked-by: Jason Cooper Signed-off-by: Rob Herring diff --git a/Documentation/devicetree/bindings/arm/cpus.txt b/Documentation/devicetree/bindings/arm/cpus.txt index f32494d..9130435 100644 --- a/Documentation/devicetree/bindings/arm/cpus.txt +++ b/Documentation/devicetree/bindings/arm/cpus.txt @@ -1,77 +1,384 @@ -* ARM CPUs binding description +================= +ARM CPUs bindings +================= The device tree allows to describe the layout of CPUs in a system through the "cpus" node, which in turn contains a number of subnodes (ie "cpu") defining properties for every cpu. -Bindings for CPU nodes follow the ePAPR standard, available from: - -http://devicetree.org - -For the ARM architecture every CPU node must contain the following properties: - -- device_type: must be "cpu" -- reg: property matching the CPU MPIDR[23:0] register bits - reg[31:24] bits must be set to 0 -- compatible: should be one of: - "arm,arm1020" - "arm,arm1020e" - "arm,arm1022" - "arm,arm1026" - "arm,arm720" - "arm,arm740" - "arm,arm7tdmi" - "arm,arm920" - "arm,arm922" - "arm,arm925" - "arm,arm926" - "arm,arm940" - "arm,arm946" - "arm,arm9tdmi" - "arm,cortex-a5" - "arm,cortex-a7" - "arm,cortex-a8" - "arm,cortex-a9" - "arm,cortex-a15" - "arm,arm1136" - "arm,arm1156" - "arm,arm1176" - "arm,arm11mpcore" - "faraday,fa526" - "intel,sa110" - "intel,sa1100" - "marvell,feroceon" - "marvell,mohawk" - "marvell,xsc3" - "marvell,xscale" - -Example: +Bindings for CPU nodes follow the ePAPR v1.1 standard, available from: + +https://www.power.org/documentation/epapr-version-1-1/ + +with updates for 32-bit and 64-bit ARM systems provided in this document. + +================================ +Convention used in this document +================================ + +This document follows the conventions described in the ePAPR v1.1, with +the addition: + +- square brackets define bitfields, eg reg[7:0] value of the bitfield in + the reg property contained in bits 7 down to 0 + +===================================== +cpus and cpu node bindings definition +===================================== + +The ARM architecture, in accordance with the ePAPR, requires the cpus and cpu +nodes to be present and contain the properties described below. + +- cpus node + + Description: Container of cpu nodes + + The node name must be "cpus". + + A cpus node must define the following properties: + + - #address-cells + Usage: required + Value type: + + Definition depends on ARM architecture version and + configuration: + + # On uniprocessor ARM architectures previous to v7 + value must be 1, to enable a simple enumeration + scheme for processors that do not have a HW CPU + identification register. + # On 32-bit ARM 11 MPcore, ARM v7 or later systems + value must be 1, that corresponds to CPUID/MPIDR + registers sizes. + # On ARM v8 64-bit systems value should be set to 2, + that corresponds to the MPIDR_EL1 register size. + If MPIDR_EL1[63:32] value is equal to 0 on all CPUs + in the system, #address-cells can be set to 1, since + MPIDR_EL1[63:32] bits are not used for CPUs + identification. + - #size-cells + Usage: required + Value type: + Definition: must be set to 0 + +- cpu node + + Description: Describes a CPU in an ARM based system + + PROPERTIES + + - device_type + Usage: required + Value type: + Definition: must be "cpu" + - reg + Usage and definition depend on ARM architecture version and + configuration: + + # On uniprocessor ARM architectures previous to v7 + this property is required and must be set to 0. + + # On ARM 11 MPcore based systems this property is + required and matches the CPUID[11:0] register bits. + + Bits [11:0] in the reg cell must be set to + bits [11:0] in CPU ID register. + + All other bits in the reg cell must be set to 0. + + # On 32-bit ARM v7 or later systems this property is + required and matches the CPU MPIDR[23:0] register + bits. + + Bits [23:0] in the reg cell must be set to + bits [23:0] in MPIDR. + + All other bits in the reg cell must be set to 0. + + # On ARM v8 64-bit systems this property is required + and matches the MPIDR_EL1 register affinity bits. + + * If cpus node's #address-cells property is set to 2 + + The first reg cell bits [7:0] must be set to + bits [39:32] of MPIDR_EL1. + + The second reg cell bits [23:0] must be set to + bits [23:0] of MPIDR_EL1. + + * If cpus node's #address-cells property is set to 1 + + The reg cell bits [23:0] must be set to bits [23:0] + of MPIDR_EL1. + + All other bits in the reg cells must be set to 0. + + - compatible: + Usage: required + Value type: + Definition: should be one of: + "arm,arm710t" + "arm,arm720t" + "arm,arm740t" + "arm,arm7ej-s" + "arm,arm7tdmi" + "arm,arm7tdmi-s" + "arm,arm9es" + "arm,arm9ej-s" + "arm,arm920t" + "arm,arm922t" + "arm,arm925" + "arm,arm926e-s" + "arm,arm926ej-s" + "arm,arm940t" + "arm,arm946e-s" + "arm,arm966e-s" + "arm,arm968e-s" + "arm,arm9tdmi" + "arm,arm1020e" + "arm,arm1020t" + "arm,arm1022e" + "arm,arm1026ej-s" + "arm,arm1136j-s" + "arm,arm1136jf-s" + "arm,arm1156t2-s" + "arm,arm1156t2f-s" + "arm,arm1176jzf" + "arm,arm1176jz-s" + "arm,arm1176jzf-s" + "arm,arm11mpcore" + "arm,cortex-a5" + "arm,cortex-a7" + "arm,cortex-a8" + "arm,cortex-a9" + "arm,cortex-a15" + "arm,cortex-a53" + "arm,cortex-a57" + "arm,cortex-m0" + "arm,cortex-m0+" + "arm,cortex-m1" + "arm,cortex-m3" + "arm,cortex-m4" + "arm,cortex-r4" + "arm,cortex-r5" + "arm,cortex-r7" + "faraday,fa526" + "intel,sa110" + "intel,sa1100" + "marvell,feroceon" + "marvell,mohawk" + "marvell,pj4a" + "marvell,pj4b" + "marvell,sheeva-v5" + "qcom,krait" + "qcom,scorpion" + - enable-method + Value type: + Usage and definition depend on ARM architecture version. + # On ARM v8 64-bit this property is required and must + be one of: + "spin-table" + "psci" + # On ARM 32-bit systems this property is optional. + + - cpu-release-addr + Usage: required for systems that have an "enable-method" + property value of "spin-table". + Value type: + Definition: + # On ARM v8 64-bit systems must be a two cell + property identifying a 64-bit zero-initialised + memory location. + +Example 1 (dual-cluster big.LITTLE system 32-bit): cpus { #size-cells = <0>; #address-cells = <1>; - CPU0: cpu@0 { + cpu@0 { device_type = "cpu"; compatible = "arm,cortex-a15"; reg = <0x0>; }; - CPU1: cpu@1 { + cpu@1 { device_type = "cpu"; compatible = "arm,cortex-a15"; reg = <0x1>; }; - CPU2: cpu@100 { + cpu@100 { device_type = "cpu"; compatible = "arm,cortex-a7"; reg = <0x100>; }; - CPU3: cpu@101 { + cpu@101 { device_type = "cpu"; compatible = "arm,cortex-a7"; reg = <0x101>; }; }; + +Example 2 (Cortex-A8 uniprocessor 32-bit system): + + cpus { + #size-cells = <0>; + #address-cells = <1>; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a8"; + reg = <0x0>; + }; + }; + +Example 3 (ARM 926EJ-S uniprocessor 32-bit system): + + cpus { + #size-cells = <0>; + #address-cells = <1>; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,arm926ej-s"; + reg = <0x0>; + }; + }; + +Example 4 (ARM Cortex-A57 64-bit system): + +cpus { + #size-cells = <0>; + #address-cells = <2>; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x0 0x0>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x0 0x1>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + cpu@100 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x0 0x100>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + cpu@101 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x0 0x101>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + cpu@10000 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x0 0x10000>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + cpu@10001 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x0 0x10001>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + cpu@10100 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x0 0x10100>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + cpu@10101 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x0 0x10101>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + cpu@100000000 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x1 0x0>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + cpu@100000001 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x1 0x1>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + cpu@100000100 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x1 0x100>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + cpu@100000101 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x1 0x101>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + cpu@100010000 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x1 0x10000>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + cpu@100010001 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x1 0x10001>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + cpu@100010100 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x1 0x10100>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + cpu@100010101 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x1 0x10101>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; +}; -- cgit v0.10.2 From deeea72860012324448c779b2f85371b2ebbf80b Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Thu, 26 Sep 2013 10:42:03 +0100 Subject: Documentation: DT: arm: define CPU topology bindings The advent of multi-cluster ARM systems requires a mechanism to describe how in hierarchical terms CPUs are connected in ARM SoCs so that the kernel can initialize and map resources like IRQs and memory space to specific group(s) of CPUs. The CPU topology is made up of multiple hierarchy levels whose bottom layers (aka leaf nodes in device tree syntax) contain links to the HW CPUs in the system. The topology bindings are generic for both 32-bit and 64-bit systems and lay the groundwork on top of which affinity schemes can be built. This patch provides the documentation in the kernel required to define the device tree bindings describing the CPU topology for ARM 32-bit and 64-bit systems. Signed-off-by: Lorenzo Pieralisi Signed-off-by: Rob Herring diff --git a/Documentation/devicetree/bindings/arm/topology.txt b/Documentation/devicetree/bindings/arm/topology.txt new file mode 100644 index 0000000..4aa20e7 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/topology.txt @@ -0,0 +1,474 @@ +=========================================== +ARM topology binding description +=========================================== + +=========================================== +1 - Introduction +=========================================== + +In an ARM system, the hierarchy of CPUs is defined through three entities that +are used to describe the layout of physical CPUs in the system: + +- cluster +- core +- thread + +The cpu nodes (bindings defined in [1]) represent the devices that +correspond to physical CPUs and are to be mapped to the hierarchy levels. + +The bottom hierarchy level sits at core or thread level depending on whether +symmetric multi-threading (SMT) is supported or not. + +For instance in a system where CPUs support SMT, "cpu" nodes represent all +threads existing in the system and map to the hierarchy level "thread" above. +In systems where SMT is not supported "cpu" nodes represent all cores present +in the system and map to the hierarchy level "core" above. + +ARM topology bindings allow one to associate cpu nodes with hierarchical groups +corresponding to the system hierarchy; syntactically they are defined as device +tree nodes. + +The remainder of this document provides the topology bindings for ARM, based +on the ePAPR standard, available from: + +http://www.power.org/documentation/epapr-version-1-1/ + +If not stated otherwise, whenever a reference to a cpu node phandle is made its +value must point to a cpu node compliant with the cpu node bindings as +documented in [1]. +A topology description containing phandles to cpu nodes that are not compliant +with bindings standardized in [1] is therefore considered invalid. + +=========================================== +2 - cpu-map node +=========================================== + +The ARM CPU topology is defined within the cpu-map node, which is a direct +child of the cpus node and provides a container where the actual topology +nodes are listed. + +- cpu-map node + + Usage: Optional - On ARM SMP systems provide CPUs topology to the OS. + ARM uniprocessor systems do not require a topology + description and therefore should not define a + cpu-map node. + + Description: The cpu-map node is just a container node where its + subnodes describe the CPU topology. + + Node name must be "cpu-map". + + The cpu-map node's parent node must be the cpus node. + + The cpu-map node's child nodes can be: + + - one or more cluster nodes + + Any other configuration is considered invalid. + +The cpu-map node can only contain three types of child nodes: + +- cluster node +- core node +- thread node + +whose bindings are described in paragraph 3. + +The nodes describing the CPU topology (cluster/core/thread) can only be +defined within the cpu-map node. +Any other configuration is consider invalid and therefore must be ignored. + +=========================================== +2.1 - cpu-map child nodes naming convention +=========================================== + +cpu-map child nodes must follow a naming convention where the node name +must be "clusterN", "coreN", "threadN" depending on the node type (ie +cluster/core/thread) (where N = {0, 1, ...} is the node number; nodes which +are siblings within a single common parent node must be given a unique and +sequential N value, starting from 0). +cpu-map child nodes which do not share a common parent node can have the same +name (ie same number N as other cpu-map child nodes at different device tree +levels) since name uniqueness will be guaranteed by the device tree hierarchy. + +=========================================== +3 - cluster/core/thread node bindings +=========================================== + +Bindings for cluster/cpu/thread nodes are defined as follows: + +- cluster node + + Description: must be declared within a cpu-map node, one node + per cluster. A system can contain several layers of + clustering and cluster nodes can be contained in parent + cluster nodes. + + The cluster node name must be "clusterN" as described in 2.1 above. + A cluster node can not be a leaf node. + + A cluster node's child nodes must be: + + - one or more cluster nodes; or + - one or more core nodes + + Any other configuration is considered invalid. + +- core node + + Description: must be declared in a cluster node, one node per core in + the cluster. If the system does not support SMT, core + nodes are leaf nodes, otherwise they become containers of + thread nodes. + + The core node name must be "coreN" as described in 2.1 above. + + A core node must be a leaf node if SMT is not supported. + + Properties for core nodes that are leaf nodes: + + - cpu + Usage: required + Value type: + Definition: a phandle to the cpu node that corresponds to the + core node. + + If a core node is not a leaf node (CPUs supporting SMT) a core node's + child nodes can be: + + - one or more thread nodes + + Any other configuration is considered invalid. + +- thread node + + Description: must be declared in a core node, one node per thread + in the core if the system supports SMT. Thread nodes are + always leaf nodes in the device tree. + + The thread node name must be "threadN" as described in 2.1 above. + + A thread node must be a leaf node. + + A thread node must contain the following property: + + - cpu + Usage: required + Value type: + Definition: a phandle to the cpu node that corresponds to + the thread node. + +=========================================== +4 - Example dts +=========================================== + +Example 1 (ARM 64-bit, 16-cpu system, two clusters of clusters): + +cpus { + #size-cells = <0>; + #address-cells = <2>; + + cpu-map { + cluster0 { + cluster0 { + core0 { + thread0 { + cpu = <&CPU0>; + }; + thread1 { + cpu = <&CPU1>; + }; + }; + + core1 { + thread0 { + cpu = <&CPU2>; + }; + thread1 { + cpu = <&CPU3>; + }; + }; + }; + + cluster1 { + core0 { + thread0 { + cpu = <&CPU4>; + }; + thread1 { + cpu = <&CPU5>; + }; + }; + + core1 { + thread0 { + cpu = <&CPU6>; + }; + thread1 { + cpu = <&CPU7>; + }; + }; + }; + }; + + cluster1 { + cluster0 { + core0 { + thread0 { + cpu = <&CPU8>; + }; + thread1 { + cpu = <&CPU9>; + }; + }; + core1 { + thread0 { + cpu = <&CPU10>; + }; + thread1 { + cpu = <&CPU11>; + }; + }; + }; + + cluster1 { + core0 { + thread0 { + cpu = <&CPU12>; + }; + thread1 { + cpu = <&CPU13>; + }; + }; + core1 { + thread0 { + cpu = <&CPU14>; + }; + thread1 { + cpu = <&CPU15>; + }; + }; + }; + }; + }; + + CPU0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x0 0x0>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + CPU1: cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x0 0x1>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + CPU2: cpu@100 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x0 0x100>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + CPU3: cpu@101 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x0 0x101>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + CPU4: cpu@10000 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x0 0x10000>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + CPU5: cpu@10001 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x0 0x10001>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + CPU6: cpu@10100 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x0 0x10100>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + CPU7: cpu@10101 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x0 0x10101>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + CPU8: cpu@100000000 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x1 0x0>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + CPU9: cpu@100000001 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x1 0x1>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + CPU10: cpu@100000100 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x1 0x100>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + CPU11: cpu@100000101 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x1 0x101>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + CPU12: cpu@100010000 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x1 0x10000>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + CPU13: cpu@100010001 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x1 0x10001>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + CPU14: cpu@100010100 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x1 0x10100>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; + + CPU15: cpu@100010101 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x1 0x10101>; + enable-method = "spin-table"; + cpu-release-addr = <0 0x20000000>; + }; +}; + +Example 2 (ARM 32-bit, dual-cluster, 8-cpu system, no SMT): + +cpus { + #size-cells = <0>; + #address-cells = <1>; + + cpu-map { + cluster0 { + core0 { + cpu = <&CPU0>; + }; + core1 { + cpu = <&CPU1>; + }; + core2 { + cpu = <&CPU2>; + }; + core3 { + cpu = <&CPU3>; + }; + }; + + cluster1 { + core0 { + cpu = <&CPU4>; + }; + core1 { + cpu = <&CPU5>; + }; + core2 { + cpu = <&CPU6>; + }; + core3 { + cpu = <&CPU7>; + }; + }; + }; + + CPU0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <0x0>; + }; + + CPU1: cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <0x1>; + }; + + CPU2: cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <0x2>; + }; + + CPU3: cpu@3 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <0x3>; + }; + + CPU4: cpu@100 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + reg = <0x100>; + }; + + CPU5: cpu@101 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + reg = <0x101>; + }; + + CPU6: cpu@102 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + reg = <0x102>; + }; + + CPU7: cpu@103 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + reg = <0x103>; + }; +}; + +=============================================================================== +[1] ARM Linux kernel documentation + Documentation/devicetree/bindings/arm/cpus.txt -- cgit v0.10.2 From d2724de1874917be2a12939657820616742bd8ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Gr=C3=A4uler?= Date: Sat, 28 Sep 2013 21:51:08 +0200 Subject: ALSA: snd-usb-caiaq: LED support for Maschine Controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds LED support for the Native Instruments Maschine Controller. It adds ALSA controls for dimming the LEDs of all buttons and the backlight of the two displays. Signed-off-by: Hannes Gräuler Acked-by: Daniel Mack Signed-off-by: Takashi Iwai diff --git a/sound/usb/caiaq/control.c b/sound/usb/caiaq/control.c index ae6b50f..f65fc09 100644 --- a/sound/usb/caiaq/control.c +++ b/sound/usb/caiaq/control.c @@ -28,6 +28,7 @@ #include "control.h" #define CNT_INTVAL 0x10000 +#define MASCHINE_BANK_SIZE 32 static int control_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) @@ -105,6 +106,10 @@ static int control_put(struct snd_kcontrol *kcontrol, USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1)) cmd = EP1_CMD_DIMM_LEDS; + if (cdev->chip.usb_id == + USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER)) + cmd = EP1_CMD_DIMM_LEDS; + if (pos & CNT_INTVAL) { int i = pos & ~CNT_INTVAL; @@ -121,6 +126,20 @@ static int control_put(struct snd_kcontrol *kcontrol, usb_sndbulkpipe(cdev->chip.dev, 8), cdev->ep8_out_buf, sizeof(cdev->ep8_out_buf), &actual_len, 200); + } else if (cdev->chip.usb_id == + USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER)) { + + int bank = 0; + int offset = 0; + + if (i >= MASCHINE_BANK_SIZE) { + bank = 0x1e; + offset = MASCHINE_BANK_SIZE; + } + + snd_usb_caiaq_send_command_bank(cdev, cmd, bank, + cdev->control_state + offset, + MASCHINE_BANK_SIZE); } else { snd_usb_caiaq_send_command(cdev, cmd, cdev->control_state, sizeof(cdev->control_state)); @@ -490,6 +509,74 @@ static struct caiaq_controller kontrols4_controller[] = { { "LED: FX2: Mode", 133 | CNT_INTVAL }, }; +static struct caiaq_controller maschine_controller[] = { + { "LED: Pad 1", 3 | CNT_INTVAL }, + { "LED: Pad 2", 2 | CNT_INTVAL }, + { "LED: Pad 3", 1 | CNT_INTVAL }, + { "LED: Pad 4", 0 | CNT_INTVAL }, + { "LED: Pad 5", 7 | CNT_INTVAL }, + { "LED: Pad 6", 6 | CNT_INTVAL }, + { "LED: Pad 7", 5 | CNT_INTVAL }, + { "LED: Pad 8", 4 | CNT_INTVAL }, + { "LED: Pad 9", 11 | CNT_INTVAL }, + { "LED: Pad 10", 10 | CNT_INTVAL }, + { "LED: Pad 11", 9 | CNT_INTVAL }, + { "LED: Pad 12", 8 | CNT_INTVAL }, + { "LED: Pad 13", 15 | CNT_INTVAL }, + { "LED: Pad 14", 14 | CNT_INTVAL }, + { "LED: Pad 15", 13 | CNT_INTVAL }, + { "LED: Pad 16", 12 | CNT_INTVAL }, + + { "LED: Mute", 16 | CNT_INTVAL }, + { "LED: Solo", 17 | CNT_INTVAL }, + { "LED: Select", 18 | CNT_INTVAL }, + { "LED: Duplicate", 19 | CNT_INTVAL }, + { "LED: Navigate", 20 | CNT_INTVAL }, + { "LED: Pad Mode", 21 | CNT_INTVAL }, + { "LED: Pattern", 22 | CNT_INTVAL }, + { "LED: Scene", 23 | CNT_INTVAL }, + + { "LED: Shift", 24 | CNT_INTVAL }, + { "LED: Erase", 25 | CNT_INTVAL }, + { "LED: Grid", 26 | CNT_INTVAL }, + { "LED: Right Bottom", 27 | CNT_INTVAL }, + { "LED: Rec", 28 | CNT_INTVAL }, + { "LED: Play", 29 | CNT_INTVAL }, + { "LED: Left Bottom", 32 | CNT_INTVAL }, + { "LED: Restart", 33 | CNT_INTVAL }, + + { "LED: Group A", 41 | CNT_INTVAL }, + { "LED: Group B", 40 | CNT_INTVAL }, + { "LED: Group C", 37 | CNT_INTVAL }, + { "LED: Group D", 36 | CNT_INTVAL }, + { "LED: Group E", 39 | CNT_INTVAL }, + { "LED: Group F", 38 | CNT_INTVAL }, + { "LED: Group G", 35 | CNT_INTVAL }, + { "LED: Group H", 34 | CNT_INTVAL }, + + { "LED: Auto Write", 42 | CNT_INTVAL }, + { "LED: Snap", 43 | CNT_INTVAL }, + { "LED: Right Top", 44 | CNT_INTVAL }, + { "LED: Left Top", 45 | CNT_INTVAL }, + { "LED: Sampling", 46 | CNT_INTVAL }, + { "LED: Browse", 47 | CNT_INTVAL }, + { "LED: Step", 48 | CNT_INTVAL }, + { "LED: Control", 49 | CNT_INTVAL }, + + { "LED: Top Button 1", 57 | CNT_INTVAL }, + { "LED: Top Button 2", 56 | CNT_INTVAL }, + { "LED: Top Button 3", 55 | CNT_INTVAL }, + { "LED: Top Button 4", 54 | CNT_INTVAL }, + { "LED: Top Button 5", 53 | CNT_INTVAL }, + { "LED: Top Button 6", 52 | CNT_INTVAL }, + { "LED: Top Button 7", 51 | CNT_INTVAL }, + { "LED: Top Button 8", 50 | CNT_INTVAL }, + + { "LED: Note Repeat", 58 | CNT_INTVAL }, + + { "Backlight Display", 59 | CNT_INTVAL } +}; + static int add_controls(struct caiaq_controller *c, int num, struct snd_usb_caiaqdev *cdev) { @@ -553,6 +640,11 @@ int snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *cdev) ret = add_controls(kontrols4_controller, ARRAY_SIZE(kontrols4_controller), cdev); break; + + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER): + ret = add_controls(maschine_controller, + ARRAY_SIZE(maschine_controller), cdev); + break; } return ret; diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index 1a61dd1..bc55f70 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -235,6 +235,31 @@ int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *cdev, cdev->ep1_out_buf, len+1, &actual_len, 200); } +int snd_usb_caiaq_send_command_bank(struct snd_usb_caiaqdev *cdev, + unsigned char command, + unsigned char bank, + const unsigned char *buffer, + int len) +{ + int actual_len; + struct usb_device *usb_dev = cdev->chip.dev; + + if (!usb_dev) + return -EIO; + + if (len > EP1_BUFSIZE - 2) + len = EP1_BUFSIZE - 2; + + if (buffer && len > 0) + memcpy(cdev->ep1_out_buf+2, buffer, len); + + cdev->ep1_out_buf[0] = command; + cdev->ep1_out_buf[1] = bank; + + return usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, 1), + cdev->ep1_out_buf, len+2, &actual_len, 200); +} + int snd_usb_caiaq_set_audio_params (struct snd_usb_caiaqdev *cdev, int rate, int depth, int bpp) { diff --git a/sound/usb/caiaq/device.h b/sound/usb/caiaq/device.h index ad102fa..ab0f752 100644 --- a/sound/usb/caiaq/device.h +++ b/sound/usb/caiaq/device.h @@ -128,5 +128,10 @@ int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *cdev, unsigned char command, const unsigned char *buffer, int len); +int snd_usb_caiaq_send_command_bank(struct snd_usb_caiaqdev *cdev, + unsigned char command, + unsigned char bank, + const unsigned char *buffer, + int len); #endif /* CAIAQ_DEVICE_H */ -- cgit v0.10.2 From d2078d5adbe227d64d7963d93f13479c890a9a16 Mon Sep 17 00:00:00 2001 From: Linn Crosetto Date: Sun, 22 Sep 2013 19:59:08 -0600 Subject: x86: EFI stub support for large memory maps This patch fixes a problem with EFI memory maps larger than 128 entries when booting using the EFI stub, which results in overflowing e820_map in boot_params and an eventual halt when checking the map size in sanitize_e820_map(). If the number of map entries is greater than what can fit in e820_map, add the extra entries to the setup_data list using type SETUP_E820_EXT. These extra entries are then picked up when the setup_data list is parsed in parse_e820_ext(). Signed-off-by: Linn Crosetto Signed-off-by: Matt Fleming diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index beb07a4..5c0dc55 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -519,71 +519,47 @@ fail: return NULL; } -static efi_status_t exit_boot(struct boot_params *boot_params, - void *handle) +static void add_e820ext(struct boot_params *params, + struct setup_data *e820ext, u32 nr_entries) { - struct efi_info *efi = &boot_params->efi_info; - struct e820entry *e820_map = &boot_params->e820_map[0]; - struct e820entry *prev = NULL; - unsigned long size, key, desc_size, _size; - efi_memory_desc_t *mem_map; + struct setup_data *data; efi_status_t status; - __u32 desc_version; - bool called_exit = false; - u8 nr_entries; - int i; - -get_map: - status = efi_get_memory_map(sys_table, &mem_map, &size, &desc_size, - &desc_version, &key); - - if (status != EFI_SUCCESS) - return status; + unsigned long size; - memcpy(&efi->efi_loader_signature, EFI_LOADER_SIGNATURE, sizeof(__u32)); - efi->efi_systab = (unsigned long)sys_table; - efi->efi_memdesc_size = desc_size; - efi->efi_memdesc_version = desc_version; - efi->efi_memmap = (unsigned long)mem_map; - efi->efi_memmap_size = size; + e820ext->type = SETUP_E820_EXT; + e820ext->len = nr_entries * sizeof(struct e820entry); + e820ext->next = 0; -#ifdef CONFIG_X86_64 - efi->efi_systab_hi = (unsigned long)sys_table >> 32; - efi->efi_memmap_hi = (unsigned long)mem_map >> 32; -#endif + data = (struct setup_data *)(unsigned long)params->hdr.setup_data; - /* Might as well exit boot services now */ - status = efi_call_phys2(sys_table->boottime->exit_boot_services, - handle, key); - if (status != EFI_SUCCESS) { - /* - * ExitBootServices() will fail if any of the event - * handlers change the memory map. In which case, we - * must be prepared to retry, but only once so that - * we're guaranteed to exit on repeated failures instead - * of spinning forever. - */ - if (called_exit) - goto free_mem_map; + while (data && data->next) + data = (struct setup_data *)(unsigned long)data->next; - called_exit = true; - efi_call_phys1(sys_table->boottime->free_pool, mem_map); - goto get_map; - } + if (data) + data->next = (unsigned long)e820ext; + else + params->hdr.setup_data = (unsigned long)e820ext; +} - /* Historic? */ - boot_params->alt_mem_k = 32 * 1024; +static efi_status_t setup_e820(struct boot_params *params, + struct setup_data *e820ext, u32 e820ext_size) +{ + struct e820entry *e820_map = ¶ms->e820_map[0]; + struct efi_info *efi = ¶ms->efi_info; + struct e820entry *prev = NULL; + u32 nr_entries; + u32 nr_desc; + int i; - /* - * Convert the EFI memory map to E820. - */ nr_entries = 0; - for (i = 0; i < size / desc_size; i++) { + nr_desc = efi->efi_memmap_size / efi->efi_memdesc_size; + + for (i = 0; i < nr_desc; i++) { efi_memory_desc_t *d; unsigned int e820_type = 0; - unsigned long m = (unsigned long)mem_map; + unsigned long m = efi->efi_memmap; - d = (efi_memory_desc_t *)(m + (i * desc_size)); + d = (efi_memory_desc_t *)(m + (i * efi->efi_memdesc_size)); switch (d->type) { case EFI_RESERVED_TYPE: case EFI_RUNTIME_SERVICES_CODE: @@ -620,18 +596,142 @@ get_map: /* Merge adjacent mappings */ if (prev && prev->type == e820_type && - (prev->addr + prev->size) == d->phys_addr) + (prev->addr + prev->size) == d->phys_addr) { prev->size += d->num_pages << 12; - else { - e820_map->addr = d->phys_addr; - e820_map->size = d->num_pages << 12; - e820_map->type = e820_type; - prev = e820_map++; - nr_entries++; + continue; } + + if (nr_entries == ARRAY_SIZE(params->e820_map)) { + u32 need = (nr_desc - i) * sizeof(struct e820entry) + + sizeof(struct setup_data); + + if (!e820ext || e820ext_size < need) + return EFI_BUFFER_TOO_SMALL; + + /* boot_params map full, switch to e820 extended */ + e820_map = (struct e820entry *)e820ext->data; + } + + e820_map->addr = d->phys_addr; + e820_map->size = d->num_pages << PAGE_SHIFT; + e820_map->type = e820_type; + prev = e820_map++; + nr_entries++; } - boot_params->e820_entries = nr_entries; + if (nr_entries > ARRAY_SIZE(params->e820_map)) { + u32 nr_e820ext = nr_entries - ARRAY_SIZE(params->e820_map); + + add_e820ext(params, e820ext, nr_e820ext); + nr_entries -= nr_e820ext; + } + + params->e820_entries = (u8)nr_entries; + + return EFI_SUCCESS; +} + +static efi_status_t alloc_e820ext(u32 nr_desc, struct setup_data **e820ext, + u32 *e820ext_size) +{ + efi_status_t status; + unsigned long size; + + size = sizeof(struct setup_data) + + sizeof(struct e820entry) * nr_desc; + + if (*e820ext) { + efi_call_phys1(sys_table->boottime->free_pool, *e820ext); + *e820ext = NULL; + *e820ext_size = 0; + } + + status = efi_call_phys3(sys_table->boottime->allocate_pool, + EFI_LOADER_DATA, size, e820ext); + + if (status == EFI_SUCCESS) + *e820ext_size = size; + + return status; +} + +static efi_status_t exit_boot(struct boot_params *boot_params, + void *handle) +{ + struct efi_info *efi = &boot_params->efi_info; + unsigned long map_sz, key, desc_size; + efi_memory_desc_t *mem_map; + struct setup_data *e820ext; + __u32 e820ext_size; + __u32 nr_desc, prev_nr_desc; + efi_status_t status; + __u32 desc_version; + bool called_exit = false; + u8 nr_entries; + int i; + + nr_desc = 0; + e820ext = NULL; + e820ext_size = 0; + +get_map: + status = efi_get_memory_map(sys_table, &mem_map, &map_sz, &desc_size, + &desc_version, &key); + + if (status != EFI_SUCCESS) + return status; + + prev_nr_desc = nr_desc; + nr_desc = map_sz / desc_size; + if (nr_desc > prev_nr_desc && + nr_desc > ARRAY_SIZE(boot_params->e820_map)) { + u32 nr_e820ext = nr_desc - ARRAY_SIZE(boot_params->e820_map); + + status = alloc_e820ext(nr_e820ext, &e820ext, &e820ext_size); + if (status != EFI_SUCCESS) + goto free_mem_map; + + efi_call_phys1(sys_table->boottime->free_pool, mem_map); + goto get_map; /* Allocated memory, get map again */ + } + + memcpy(&efi->efi_loader_signature, EFI_LOADER_SIGNATURE, sizeof(__u32)); + efi->efi_systab = (unsigned long)sys_table; + efi->efi_memdesc_size = desc_size; + efi->efi_memdesc_version = desc_version; + efi->efi_memmap = (unsigned long)mem_map; + efi->efi_memmap_size = map_sz; + +#ifdef CONFIG_X86_64 + efi->efi_systab_hi = (unsigned long)sys_table >> 32; + efi->efi_memmap_hi = (unsigned long)mem_map >> 32; +#endif + + /* Might as well exit boot services now */ + status = efi_call_phys2(sys_table->boottime->exit_boot_services, + handle, key); + if (status != EFI_SUCCESS) { + /* + * ExitBootServices() will fail if any of the event + * handlers change the memory map. In which case, we + * must be prepared to retry, but only once so that + * we're guaranteed to exit on repeated failures instead + * of spinning forever. + */ + if (called_exit) + goto free_mem_map; + + called_exit = true; + efi_call_phys1(sys_table->boottime->free_pool, mem_map); + goto get_map; + } + + /* Historic? */ + boot_params->alt_mem_k = 32 * 1024; + + status = setup_e820(boot_params, e820ext, e820ext_size); + if (status != EFI_SUCCESS) + return status; return EFI_SUCCESS; -- cgit v0.10.2 From 0ce6cda2c75d64175394341ef60e6e1d27dd9c10 Mon Sep 17 00:00:00 2001 From: Bart Kuivenhoven Date: Mon, 23 Sep 2013 11:45:28 +0200 Subject: x86 efi: bugfix interrupt disabling sequence The problem in efi_main was that the idt was cleared before the interrupts were disabled. The UEFI spec states that interrupts aren't used so this shouldn't be too much of a problem. Peripherals however don't necessarily know about this and thus might cause interrupts to happen anyway. Even if ExitBootServices() has been called. This means there is a risk of an interrupt being triggered while the IDT register is nullified and the interrupt bit hasn't been cleared, allowing for a triple fault. This patch disables the interrupt flag, while leaving the existing IDT in place. The CPU won't care about the IDT at all as long as the interrupt bit is off, so it's safe to leave it in place as nothing will ever happen to it. [ Removed the now unused 'idt' variable - Matt ] Signed-off-by: Bart Kuivenhoven Signed-off-by: Matt Fleming diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index 5c0dc55..a7677ba 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -748,7 +748,7 @@ free_mem_map: struct boot_params *efi_main(void *handle, efi_system_table_t *_table, struct boot_params *boot_params) { - struct desc_ptr *gdt, *idt; + struct desc_ptr *gdt; efi_loaded_image_t *image; struct setup_header *hdr = &boot_params->hdr; efi_status_t status; @@ -780,17 +780,6 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table, goto fail; } - status = efi_call_phys3(sys_table->boottime->allocate_pool, - EFI_LOADER_DATA, sizeof(*idt), - (void **)&idt); - if (status != EFI_SUCCESS) { - efi_printk(sys_table, "Failed to alloc mem for idt structure\n"); - goto fail; - } - - idt->size = 0; - idt->address = 0; - /* * If the kernel isn't already loaded at the preferred load * address, relocate it. @@ -865,10 +854,8 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table, desc->base2 = 0x00; #endif /* CONFIG_X86_64 */ - asm volatile ("lidt %0" : : "m" (*idt)); - asm volatile ("lgdt %0" : : "m" (*gdt)); - asm volatile("cli"); + asm volatile ("lgdt %0" : : "m" (*gdt)); return boot_params; fail: -- cgit v0.10.2 From 5df529d440aa4f0e67be9af3718e7edf05db7d02 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 20 Sep 2013 13:51:56 +0200 Subject: regulator: core: Reduce busy-wait looping Keep busy-wait looping to a minimum while waiting for a regulator to ramp-up to the target voltage. This follows the guidelines set forth in Documentation/timers/timers-howto.txt and assumes that regulators are never enabled in atomic context. Signed-off-by: Thierry Reding Signed-off-by: Mark Brown diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index ad15424..5075ed1 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1740,11 +1740,39 @@ static int _regulator_do_enable(struct regulator_dev *rdev) * together. */ trace_regulator_enable_delay(rdev_get_name(rdev)); - if (delay >= 1000) { - mdelay(delay / 1000); - udelay(delay % 1000); - } else if (delay) { - udelay(delay); + /* + * Delay for the requested amount of time as per the guidelines in: + * + * Documentation/timers/timers-howto.txt + * + * The assumption here is that regulators will never be enabled in + * atomic context and therefore sleeping functions can be used. + */ + if (delay) { + unsigned int ms = delay / 1000; + unsigned int us = delay % 1000; + + if (ms > 0) { + /* + * For small enough values, handle super-millisecond + * delays in the usleep_range() call below. + */ + if (ms < 20) + us += ms * 1000; + else + msleep(ms); + } + + /* + * Give the scheduler some room to coalesce with any other + * wakeup sources. For delays shorter than 10 us, don't even + * bother setting up high-resolution timers and just busy- + * loop. + */ + if (us >= 10) + usleep_range(us, us + 100); + else + udelay(us); } trace_regulator_enable_complete(rdev_get_name(rdev)); -- cgit v0.10.2 From 67651b29ef15e5ef9897ed8cb9072222c4cf3432 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 27 Sep 2013 20:10:26 +0100 Subject: spi/s3c64xx: Flush FIFOs prior to cleaning up transfer Ensure that the FIFOs are fully drained before we deassert /CS or do any delays that have been requested in order to ensure that the behaviour visible on the bus matches that which was requested by the caller. Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 229c6b9..2e267ce 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -960,6 +960,8 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master, goto out; } + flush_fifo(sdd); + if (xfer->delay_usecs) udelay(xfer->delay_usecs); @@ -972,8 +974,6 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master, } msg->actual_length += xfer->len; - - flush_fifo(sdd); } out: -- cgit v0.10.2 From 8b06d5b8576d1df4122fa9ca40578ec7f094cb3e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 27 Sep 2013 18:44:53 +0100 Subject: spi/s3c64xx: Check that clock enables succeed on runtime resume Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 2e267ce..dd5ed13 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1524,9 +1524,17 @@ static int s3c64xx_spi_runtime_resume(struct device *dev) { struct spi_master *master = dev_get_drvdata(dev); struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); + int ret; - clk_prepare_enable(sdd->src_clk); - clk_prepare_enable(sdd->clk); + ret = clk_prepare_enable(sdd->src_clk); + if (ret != 0) + return ret; + + ret = clk_prepare_enable(sdd->clk); + if (ret != 0) { + clk_disable_unprepare(sdd->src_clk); + return ret; + } return 0; } -- cgit v0.10.2 From 64d930ac116db25db814f76c31bfd4a7ea428f74 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 27 Sep 2013 18:45:23 +0100 Subject: spi/s3c64xx: Remove unused gpios field from driver data Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index dd5ed13..7f960db 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -205,7 +205,6 @@ struct s3c64xx_spi_driver_data { #endif struct s3c64xx_spi_port_config *port_conf; unsigned int port_id; - unsigned long gpios[4]; bool cs_gpio; }; -- cgit v0.10.2 From dd97e26849c16f484191f056b6e080cc30ebe98b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 27 Sep 2013 18:58:55 +0100 Subject: spi/s3c64xx: Use core cs_gpio field Rather than using the driver custom platform data to store the chip select GPIO use the cs_gpio field provided by the SPI core, supporting future refectoring. Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 7f960db..43caeee 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -558,22 +558,18 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, static inline void enable_cs(struct s3c64xx_spi_driver_data *sdd, struct spi_device *spi) { - struct s3c64xx_spi_csinfo *cs; - if (sdd->tgl_spi != NULL) { /* If last device toggled after mssg */ if (sdd->tgl_spi != spi) { /* if last mssg on diff device */ /* Deselect the last toggled device */ - cs = sdd->tgl_spi->controller_data; - if (sdd->cs_gpio) - gpio_set_value(cs->line, + if (spi->cs_gpio >= 0) + gpio_set_value(spi->cs_gpio, spi->mode & SPI_CS_HIGH ? 0 : 1); } sdd->tgl_spi = NULL; } - cs = spi->controller_data; - if (sdd->cs_gpio) - gpio_set_value(cs->line, spi->mode & SPI_CS_HIGH ? 1 : 0); + if (spi->cs_gpio >= 0) + gpio_set_value(spi->cs_gpio, spi->mode & SPI_CS_HIGH ? 1 : 0); /* Start the signals */ writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); @@ -701,13 +697,11 @@ static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd, static inline void disable_cs(struct s3c64xx_spi_driver_data *sdd, struct spi_device *spi) { - struct s3c64xx_spi_csinfo *cs = spi->controller_data; - if (sdd->tgl_spi == spi) sdd->tgl_spi = NULL; - if (sdd->cs_gpio) - gpio_set_value(cs->line, spi->mode & SPI_CS_HIGH ? 0 : 1); + if (spi->cs_gpio >= 0) + gpio_set_value(spi->cs_gpio, spi->mode & SPI_CS_HIGH ? 0 : 1); /* Quiese the signals */ writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL); @@ -1070,6 +1064,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi) cs->line, err); goto err_gpio_req; } + + spi->cs_gpio = cs->line; } spi_set_ctldata(spi, cs); @@ -1139,8 +1135,8 @@ static void s3c64xx_spi_cleanup(struct spi_device *spi) struct s3c64xx_spi_driver_data *sdd; sdd = spi_master_get_devdata(spi->master); - if (cs && sdd->cs_gpio) { - gpio_free(cs->line); + if (spi->cs_gpio) { + gpio_free(spi->cs_gpio); if (spi->dev.of_node) kfree(cs); } -- cgit v0.10.2 From 8c09daa1c963e2ab58029520dd2f3d54184d7258 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 27 Sep 2013 19:56:31 +0100 Subject: spi/s3c64xx: Factor transfer start out of enable/disable_cs() The hardware level /CS handling is tied to the start of the data path so is rolled into the same function as we use to manipulate GPIO /CS. In order to support factoring out the /CS handling into the core separate the two and explicitly start transfers separately to the /CS handling. Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 43caeee..2ab96fb 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -570,9 +570,6 @@ static inline void enable_cs(struct s3c64xx_spi_driver_data *sdd, if (spi->cs_gpio >= 0) gpio_set_value(spi->cs_gpio, spi->mode & SPI_CS_HIGH ? 1 : 0); - - /* Start the signals */ - writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); } static u32 s3c64xx_spi_wait_for_timeout(struct s3c64xx_spi_driver_data *sdd, @@ -702,9 +699,6 @@ static inline void disable_cs(struct s3c64xx_spi_driver_data *sdd, if (spi->cs_gpio >= 0) gpio_set_value(spi->cs_gpio, spi->mode & SPI_CS_HIGH ? 0 : 1); - - /* Quiese the signals */ - writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL); } static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) @@ -930,6 +924,9 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master, /* Slave Select */ enable_cs(sdd, spi); + /* Start the signals */ + writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); + spin_unlock_irqrestore(&sdd->lock, flags); status = wait_for_xfer(sdd, xfer, use_dma); @@ -970,10 +967,14 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master, } out: - if (!cs_toggle || status) + if (!cs_toggle || status) { + /* Quiese the signals */ + writel(S3C64XX_SPI_SLAVE_SIG_INACT, + sdd->regs + S3C64XX_SPI_SLAVE_SEL); disable_cs(sdd, spi); - else + } else { sdd->tgl_spi = spi; + } s3c64xx_spi_unmap_mssg(sdd, msg); @@ -1112,11 +1113,13 @@ static int s3c64xx_spi_setup(struct spi_device *spi) } pm_runtime_put(&sdd->pdev->dev); + writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL); disable_cs(sdd, spi); return 0; setup_exit: /* setup() returns with device de-selected */ + writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL); disable_cs(sdd, spi); gpio_free(cs->line); -- cgit v0.10.2 From 0f5a751ace250097d3ad7835df7d376f21f44509 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 27 Sep 2013 20:38:01 +0100 Subject: spi/s3c64xx: Enable GPIO /CS prior to starting hardware To help with bisection of future refactoring to share more of the code for handling a spi_message pull the enabling of GPIO based /CS prior to all the hardware setup for starting a transfer. Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 2ab96fb..e631232 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -906,6 +906,9 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master, s3c64xx_spi_config(sdd); } + /* Slave Select */ + enable_cs(sdd, spi); + /* Polling method for xfers not bigger than FIFO capacity */ use_dma = 0; if (!is_polling(sdd) && @@ -921,9 +924,6 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master, enable_datapath(sdd, spi, xfer, use_dma); - /* Slave Select */ - enable_cs(sdd, spi); - /* Start the signals */ writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); -- cgit v0.10.2 From 554b0004d0ec4fbd11e08668dfc400f211e8d5c5 Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Mon, 16 Sep 2013 15:28:21 -0700 Subject: vtime: Add HAVE_VIRT_CPU_ACCOUNTING_GEN Kconfig With VIRT_CPU_ACCOUNTING_GEN, cputime_t becomes 64-bit. In order to use that feature, arch code should be audited to ensure there are no races in concurrent read/write of cputime_t. For example, reading/writing 64-bit cputime_t on some 32-bit arches may require multiple accesses for low and high value parts, so proper locking is needed to protect against concurrent accesses. Therefore, add CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN which arches can enable after they've been audited for potential races. This option is automatically enabled on 64-bit platforms. Feature requested by Frederic Weisbecker. Signed-off-by: Kevin Hilman Cc: Ingo Molnar Cc: Russell King Cc: Paul E. McKenney Cc: Arm Linux Signed-off-by: Frederic Weisbecker diff --git a/arch/Kconfig b/arch/Kconfig index af2cc6e..185f8b0 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -353,6 +353,18 @@ config HAVE_CONTEXT_TRACKING config HAVE_VIRT_CPU_ACCOUNTING bool +config HAVE_VIRT_CPU_ACCOUNTING_GEN + bool + default y if 64BIT + help + With VIRT_CPU_ACCOUNTING_GEN, cputime_t becomes 64-bit. + Before enabling this option, arch code must be audited + to ensure there are no races in concurrent read/write of + cputime_t. For example, reading/writing 64-bit cputime_t on + some 32-bit arches may require multiple accesses, so proper + locking is needed to protect against concurrent accesses. + + config HAVE_IRQ_TIME_ACCOUNTING bool help diff --git a/init/Kconfig b/init/Kconfig index 3ecd8a1..68c1a0e 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -355,6 +355,7 @@ config VIRT_CPU_ACCOUNTING_NATIVE config VIRT_CPU_ACCOUNTING_GEN bool "Full dynticks CPU time accounting" depends on HAVE_CONTEXT_TRACKING && 64BIT + depends on HAVE_VIRT_CPU_ACCOUNTING_GEN select VIRT_CPU_ACCOUNTING select CONTEXT_TRACKING help diff --git a/kernel/time/Kconfig b/kernel/time/Kconfig index 2b62fe8..f148475 100644 --- a/kernel/time/Kconfig +++ b/kernel/time/Kconfig @@ -101,6 +101,7 @@ config NO_HZ_FULL depends on HAVE_CONTEXT_TRACKING # VIRT_CPU_ACCOUNTING_GEN dependency depends on 64BIT + depends on HAVE_VIRT_CPU_ACCOUNTING_GEN select NO_HZ_COMMON select RCU_USER_QS select RCU_NOCB_CPU -- cgit v0.10.2 From ff3fb2541246a83ce56c61d14df30b7a22e4302b Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Mon, 16 Sep 2013 15:28:19 -0700 Subject: nohz: Drop generic vtime obsolete dependency on CONFIG_64BIT The CONFIG_64BIT requirement on vtime can finally be removed since we now depend on HAVE_VIRT_CPU_ACCOUNTING_GEN which already takes care of the arch ability to handle nsecs based cputime_t safely. Signed-off-by: Kevin Hilman Cc: Ingo Molnar Cc: Russell King Cc: Paul E. McKenney Cc: Arm Linux Signed-off-by: Frederic Weisbecker diff --git a/init/Kconfig b/init/Kconfig index 68c1a0e..841e79c 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -354,7 +354,7 @@ config VIRT_CPU_ACCOUNTING_NATIVE config VIRT_CPU_ACCOUNTING_GEN bool "Full dynticks CPU time accounting" - depends on HAVE_CONTEXT_TRACKING && 64BIT + depends on HAVE_CONTEXT_TRACKING depends on HAVE_VIRT_CPU_ACCOUNTING_GEN select VIRT_CPU_ACCOUNTING select CONTEXT_TRACKING diff --git a/kernel/time/Kconfig b/kernel/time/Kconfig index f148475..3ce6e8c 100644 --- a/kernel/time/Kconfig +++ b/kernel/time/Kconfig @@ -100,7 +100,6 @@ config NO_HZ_FULL # RCU_USER_QS dependency depends on HAVE_CONTEXT_TRACKING # VIRT_CPU_ACCOUNTING_GEN dependency - depends on 64BIT depends on HAVE_VIRT_CPU_ACCOUNTING_GEN select NO_HZ_COMMON select RCU_USER_QS -- cgit v0.10.2 From 31c1fc8187158cb80ccd57c19e024c55af901797 Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Mon, 16 Sep 2013 15:28:22 -0700 Subject: ARM: Kconfig: allow full nohz CPU accounting With the 64-bit requirement removed from VIRT_CPU_ACCOUNTING_GEN, allow ARM platforms to enable it. Since VIRT_CPU_ACCOUNTING_GEN is a dependency for full NO_HZ, this allows ARM platforms to enable full NO_HZ as well. Signed-off-by: Kevin Hilman Cc: Ingo Molnar Cc: Russell King Cc: Paul E. McKenney Cc: Arm Linux Signed-off-by: Frederic Weisbecker diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 1ad6fb6..323baf0 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -54,6 +54,7 @@ config ARM select HAVE_REGS_AND_STACK_ACCESS_API select HAVE_SYSCALL_TRACEPOINTS select HAVE_UID16 + select HAVE_VIRT_CPU_ACCOUNTING_GEN select IRQ_FORCED_THREADING select KTIME_SCALAR select MODULES_USE_ELF_REL -- cgit v0.10.2 From 7cc302d231aae87a08909ae40cdf36dfe7bb5102 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Mon, 30 Sep 2013 17:08:15 +0300 Subject: ASoC: pcm: Remove extra spaces from dev_ prints dev_ prints are already prefixed by ": " before format string so there is no need for extra spaces. Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 330c9a6..d449872 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -721,7 +721,7 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe, list_add(&dpcm->list_be, &fe->dpcm[stream].be_clients); list_add(&dpcm->list_fe, &be->dpcm[stream].fe_clients); - dev_dbg(fe->dev, " connected new DPCM %s path %s %s %s\n", + dev_dbg(fe->dev, "connected new DPCM %s path %s %s %s\n", stream ? "capture" : "playback", fe->dai_link->name, stream ? "<-" : "->", be->dai_link->name); @@ -749,7 +749,7 @@ static void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe, if (dpcm->fe == fe) continue; - dev_dbg(fe->dev, " reparent %s path %s %s %s\n", + dev_dbg(fe->dev, "reparent %s path %s %s %s\n", stream ? "capture" : "playback", dpcm->fe->dai_link->name, stream ? "<-" : "->", dpcm->be->dai_link->name); @@ -773,7 +773,7 @@ static void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream) if (dpcm->state != SND_SOC_DPCM_LINK_STATE_FREE) continue; - dev_dbg(fe->dev, " freed DSP %s path %s %s %s\n", + dev_dbg(fe->dev, "freed DSP %s path %s %s %s\n", stream ? "capture" : "playback", fe->dai_link->name, stream ? "<-" : "->", dpcm->be->dai_link->name); @@ -2116,7 +2116,7 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) pcm->private_free = platform->driver->pcm_free; out: - dev_info(rtd->card->dev, " %s <-> %s mapping ok\n", codec_dai->name, + dev_info(rtd->card->dev, "%s <-> %s mapping ok\n", codec_dai->name, cpu_dai->name); return ret; } -- cgit v0.10.2 From fda5842cc62c36c5ed02b763f6045e720582b083 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 30 Sep 2013 09:50:14 +0900 Subject: regulator: ab3100: use devm_regulator_register() Use devm_regulator_register() to make cleanup paths simpler. Signed-off-by: Jingoo Han Acked-by: Linus Walleij Signed-off-by: Mark Brown diff --git a/drivers/regulator/ab3100.c b/drivers/regulator/ab3100.c index 7d5eaa8..77b46d0 100644 --- a/drivers/regulator/ab3100.c +++ b/drivers/regulator/ab3100.c @@ -535,7 +535,7 @@ static int ab3100_regulator_register(struct platform_device *pdev, config.dev = &pdev->dev; config.driver_data = reg; - rdev = regulator_register(desc, &config); + rdev = devm_regulator_register(&pdev->dev, desc, &config); if (IS_ERR(rdev)) { err = PTR_ERR(rdev); dev_err(&pdev->dev, @@ -616,7 +616,6 @@ static int ab3100_regulators_remove(struct platform_device *pdev) for (i = 0; i < AB3100_NUM_REGULATORS; i++) { struct ab3100_regulator *reg = &ab3100_regulators[i]; - regulator_unregister(reg->rdev); reg->rdev = NULL; } return 0; -- cgit v0.10.2 From a4a6b9de5c859ff953ce1b854cbfa112718a3f43 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 30 Sep 2013 09:52:37 +0900 Subject: regulator: ab8500-ext: use devm_regulator_register() Use devm_regulator_register() to make cleanup paths simpler, and remove unnecessary remove(). Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/regulator/ab8500-ext.c b/drivers/regulator/ab8500-ext.c index 02ff691..29c0faa 100644 --- a/drivers/regulator/ab8500-ext.c +++ b/drivers/regulator/ab8500-ext.c @@ -413,16 +413,12 @@ static int ab8500_ext_regulator_probe(struct platform_device *pdev) &pdata->ext_regulator[i]; /* register regulator with framework */ - info->rdev = regulator_register(&info->desc, &config); + info->rdev = devm_regulator_register(&pdev->dev, &info->desc, + &config); if (IS_ERR(info->rdev)) { err = PTR_ERR(info->rdev); dev_err(&pdev->dev, "failed to register regulator %s\n", info->desc.name); - /* when we fail, un-register all earlier regulators */ - while (--i >= 0) { - info = &ab8500_ext_regulator_info[i]; - regulator_unregister(info->rdev); - } return err; } @@ -433,26 +429,8 @@ static int ab8500_ext_regulator_probe(struct platform_device *pdev) return 0; } -static int ab8500_ext_regulator_remove(struct platform_device *pdev) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(ab8500_ext_regulator_info); i++) { - struct ab8500_ext_regulator_info *info = NULL; - info = &ab8500_ext_regulator_info[i]; - - dev_vdbg(rdev_get_dev(info->rdev), - "%s-remove\n", info->desc.name); - - regulator_unregister(info->rdev); - } - - return 0; -} - static struct platform_driver ab8500_ext_regulator_driver = { .probe = ab8500_ext_regulator_probe, - .remove = ab8500_ext_regulator_remove, .driver = { .name = "ab8500-ext-regulator", .owner = THIS_MODULE, -- cgit v0.10.2 From e97bba912eede5e813788bc9ead4a2c29130d377 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 30 Sep 2013 09:53:09 +0900 Subject: regulator: da9063: use devm_regulator_register() Use devm_regulator_register() to make cleanup paths simpler. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/regulator/da9063-regulator.c b/drivers/regulator/da9063-regulator.c index 1a78163..62b8469 100644 --- a/drivers/regulator/da9063-regulator.c +++ b/drivers/regulator/da9063-regulator.c @@ -847,13 +847,13 @@ static int da9063_regulator_probe(struct platform_device *pdev) if (da9063_reg_matches) config.of_node = da9063_reg_matches[id].of_node; config.regmap = da9063->regmap; - regl->rdev = regulator_register(®l->desc, &config); + regl->rdev = devm_regulator_register(&pdev->dev, ®l->desc, + &config); if (IS_ERR(regl->rdev)) { dev_err(&pdev->dev, "Failed to register %s regulator\n", regl->desc.name); - ret = PTR_ERR(regl->rdev); - goto err; + return PTR_ERR(regl->rdev); } id++; n++; @@ -862,9 +862,8 @@ static int da9063_regulator_probe(struct platform_device *pdev) /* LDOs overcurrent event support */ irq = platform_get_irq_byname(pdev, "LDO_LIM"); if (irq < 0) { - ret = irq; dev_err(&pdev->dev, "Failed to get IRQ.\n"); - goto err; + return irq; } regulators->irq_ldo_lim = regmap_irq_get_virq(da9063->regmap_irq, irq); @@ -881,27 +880,15 @@ static int da9063_regulator_probe(struct platform_device *pdev) } return 0; - -err: - /* Wind back regulators registeration */ - while (--n >= 0) - regulator_unregister(regulators->regulator[n].rdev); - - return ret; } static int da9063_regulator_remove(struct platform_device *pdev) { struct da9063_regulators *regulators = platform_get_drvdata(pdev); - struct da9063_regulator *regl; free_irq(regulators->irq_ldo_lim, regulators); free_irq(regulators->irq_uvov, regulators); - for (regl = ®ulators->regulator[regulators->n_regulators - 1]; - regl >= ®ulators->regulator[0]; regl--) - regulator_unregister(regl->rdev); - return 0; } -- cgit v0.10.2 From ed602534df5d07251c074bafe75feed643c588d1 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 30 Sep 2013 09:54:34 +0900 Subject: regulator: lp872x: use devm_regulator_register() Use devm_regulator_register() to make cleanup paths simpler, and remove unnecessary remove(). Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/regulator/lp872x.c b/drivers/regulator/lp872x.c index 2b84b72..2e4734f 100644 --- a/drivers/regulator/lp872x.c +++ b/drivers/regulator/lp872x.c @@ -785,7 +785,7 @@ static int lp872x_regulator_register(struct lp872x *lp) struct regulator_desc *desc; struct regulator_config cfg = { }; struct regulator_dev *rdev; - int i, ret; + int i; for (i = 0; i < lp->num_regulators; i++) { desc = (lp->chipid == LP8720) ? &lp8720_regulator_desc[i] : @@ -796,34 +796,16 @@ static int lp872x_regulator_register(struct lp872x *lp) cfg.driver_data = lp; cfg.regmap = lp->regmap; - rdev = regulator_register(desc, &cfg); + rdev = devm_regulator_register(lp->dev, desc, &cfg); if (IS_ERR(rdev)) { dev_err(lp->dev, "regulator register err"); - ret = PTR_ERR(rdev); - goto err; + return PTR_ERR(rdev); } *(lp->regulators + i) = rdev; } return 0; -err: - while (--i >= 0) { - rdev = *(lp->regulators + i); - regulator_unregister(rdev); - } - return ret; -} - -static void lp872x_regulator_unregister(struct lp872x *lp) -{ - struct regulator_dev *rdev; - int i; - - for (i = 0; i < lp->num_regulators; i++) { - rdev = *(lp->regulators + i); - regulator_unregister(rdev); - } } static const struct regmap_config lp872x_regmap_config = { @@ -979,14 +961,6 @@ err_dev: return ret; } -static int lp872x_remove(struct i2c_client *cl) -{ - struct lp872x *lp = i2c_get_clientdata(cl); - - lp872x_regulator_unregister(lp); - return 0; -} - static const struct of_device_id lp872x_dt_ids[] = { { .compatible = "ti,lp8720", }, { .compatible = "ti,lp8725", }, @@ -1008,7 +982,6 @@ static struct i2c_driver lp872x_driver = { .of_match_table = of_match_ptr(lp872x_dt_ids), }, .probe = lp872x_probe, - .remove = lp872x_remove, .id_table = lp872x_ids, }; -- cgit v0.10.2 From 3343fa174883b89c0151dac24df86e351eca85e4 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 30 Sep 2013 09:55:12 +0900 Subject: regulator: lp8788-buck: use devm_regulator_register() Use devm_regulator_register() to make cleanup paths simpler, and remove unnecessary remove(). Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/regulator/lp8788-buck.c b/drivers/regulator/lp8788-buck.c index 0b015f2..948afc2 100644 --- a/drivers/regulator/lp8788-buck.c +++ b/drivers/regulator/lp8788-buck.c @@ -515,7 +515,7 @@ static int lp8788_buck_probe(struct platform_device *pdev) cfg.driver_data = buck; cfg.regmap = lp->regmap; - rdev = regulator_register(&lp8788_buck_desc[id], &cfg); + rdev = devm_regulator_register(&pdev->dev, &lp8788_buck_desc[id], &cfg); if (IS_ERR(rdev)) { ret = PTR_ERR(rdev); dev_err(&pdev->dev, "BUCK%d regulator register err = %d\n", @@ -529,18 +529,8 @@ static int lp8788_buck_probe(struct platform_device *pdev) return 0; } -static int lp8788_buck_remove(struct platform_device *pdev) -{ - struct lp8788_buck *buck = platform_get_drvdata(pdev); - - regulator_unregister(buck->regulator); - - return 0; -} - static struct platform_driver lp8788_buck_driver = { .probe = lp8788_buck_probe, - .remove = lp8788_buck_remove, .driver = { .name = LP8788_DEV_BUCK, .owner = THIS_MODULE, -- cgit v0.10.2 From 0b7bb09054aebe66fd803c8f88015046b96cb47f Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 30 Sep 2013 09:55:51 +0900 Subject: regulator: lp8788-ldo: use devm_regulator_register() Use devm_regulator_register() to make cleanup paths simpler, and remove unnecessary remove(). Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/regulator/lp8788-ldo.c b/drivers/regulator/lp8788-ldo.c index 0527d87..b9a29a2 100644 --- a/drivers/regulator/lp8788-ldo.c +++ b/drivers/regulator/lp8788-ldo.c @@ -543,7 +543,7 @@ static int lp8788_dldo_probe(struct platform_device *pdev) cfg.driver_data = ldo; cfg.regmap = lp->regmap; - rdev = regulator_register(&lp8788_dldo_desc[id], &cfg); + rdev = devm_regulator_register(&pdev->dev, &lp8788_dldo_desc[id], &cfg); if (IS_ERR(rdev)) { ret = PTR_ERR(rdev); dev_err(&pdev->dev, "DLDO%d regulator register err = %d\n", @@ -557,18 +557,8 @@ static int lp8788_dldo_probe(struct platform_device *pdev) return 0; } -static int lp8788_dldo_remove(struct platform_device *pdev) -{ - struct lp8788_ldo *ldo = platform_get_drvdata(pdev); - - regulator_unregister(ldo->regulator); - - return 0; -} - static struct platform_driver lp8788_dldo_driver = { .probe = lp8788_dldo_probe, - .remove = lp8788_dldo_remove, .driver = { .name = LP8788_DEV_DLDO, .owner = THIS_MODULE, @@ -603,7 +593,7 @@ static int lp8788_aldo_probe(struct platform_device *pdev) cfg.driver_data = ldo; cfg.regmap = lp->regmap; - rdev = regulator_register(&lp8788_aldo_desc[id], &cfg); + rdev = devm_regulator_register(&pdev->dev, &lp8788_aldo_desc[id], &cfg); if (IS_ERR(rdev)) { ret = PTR_ERR(rdev); dev_err(&pdev->dev, "ALDO%d regulator register err = %d\n", @@ -617,18 +607,8 @@ static int lp8788_aldo_probe(struct platform_device *pdev) return 0; } -static int lp8788_aldo_remove(struct platform_device *pdev) -{ - struct lp8788_ldo *ldo = platform_get_drvdata(pdev); - - regulator_unregister(ldo->regulator); - - return 0; -} - static struct platform_driver lp8788_aldo_driver = { .probe = lp8788_aldo_probe, - .remove = lp8788_aldo_remove, .driver = { .name = LP8788_DEV_ALDO, .owner = THIS_MODULE, -- cgit v0.10.2 From 58765e24bec8940082068829a9d2c3a4431d60c0 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 30 Sep 2013 09:56:24 +0900 Subject: regulator: max8925: use devm_regulator_register() Use devm_regulator_register() to make cleanup paths simpler, and remove unnecessary remove(). Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/regulator/max8925-regulator.c b/drivers/regulator/max8925-regulator.c index d80b5fa..7595107 100644 --- a/drivers/regulator/max8925-regulator.c +++ b/drivers/regulator/max8925-regulator.c @@ -312,7 +312,7 @@ static int max8925_regulator_probe(struct platform_device *pdev) if (pdata) config.init_data = pdata; - rdev = regulator_register(&ri->desc, &config); + rdev = devm_regulator_register(&pdev->dev, &ri->desc, &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "failed to register regulator %s\n", ri->desc.name); @@ -323,22 +323,12 @@ static int max8925_regulator_probe(struct platform_device *pdev) return 0; } -static int max8925_regulator_remove(struct platform_device *pdev) -{ - struct regulator_dev *rdev = platform_get_drvdata(pdev); - - regulator_unregister(rdev); - - return 0; -} - static struct platform_driver max8925_regulator_driver = { .driver = { .name = "max8925-regulator", .owner = THIS_MODULE, }, .probe = max8925_regulator_probe, - .remove = max8925_regulator_remove, }; static int __init max8925_regulator_init(void) -- cgit v0.10.2 From 15dc006af830d85417c97b60a40b5a0b991ee32e Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 30 Sep 2013 09:56:55 +0900 Subject: regulator: pcap: use devm_regulator_register() Use devm_regulator_register() to make cleanup paths simpler, and remove unnecessary remove(). Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/regulator/pcap-regulator.c b/drivers/regulator/pcap-regulator.c index b49eaee..3727b7d 100644 --- a/drivers/regulator/pcap-regulator.c +++ b/drivers/regulator/pcap-regulator.c @@ -246,7 +246,8 @@ static int pcap_regulator_probe(struct platform_device *pdev) config.init_data = dev_get_platdata(&pdev->dev); config.driver_data = pcap; - rdev = regulator_register(&pcap_regulators[pdev->id], &config); + rdev = devm_regulator_register(&pdev->dev, &pcap_regulators[pdev->id], + &config); if (IS_ERR(rdev)) return PTR_ERR(rdev); @@ -255,22 +256,12 @@ static int pcap_regulator_probe(struct platform_device *pdev) return 0; } -static int pcap_regulator_remove(struct platform_device *pdev) -{ - struct regulator_dev *rdev = platform_get_drvdata(pdev); - - regulator_unregister(rdev); - - return 0; -} - static struct platform_driver pcap_regulator_driver = { .driver = { .name = "pcap-regulator", .owner = THIS_MODULE, }, .probe = pcap_regulator_probe, - .remove = pcap_regulator_remove, }; static int __init pcap_regulator_init(void) -- cgit v0.10.2 From 5e0165e5a91db2283e694023582c0fc23311ae59 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 30 Sep 2013 09:57:23 +0900 Subject: regulator: pcf50633: use devm_regulator_register() Use devm_regulator_register() to make cleanup paths simpler, and remove unnecessary remove(). Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/regulator/pcf50633-regulator.c b/drivers/regulator/pcf50633-regulator.c index 0f3576d..d7da1c1 100644 --- a/drivers/regulator/pcf50633-regulator.c +++ b/drivers/regulator/pcf50633-regulator.c @@ -90,7 +90,8 @@ static int pcf50633_regulator_probe(struct platform_device *pdev) config.driver_data = pcf; config.regmap = pcf->regmap; - rdev = regulator_register(®ulators[pdev->id], &config); + rdev = devm_regulator_register(&pdev->dev, ®ulators[pdev->id], + &config); if (IS_ERR(rdev)) return PTR_ERR(rdev); @@ -102,21 +103,11 @@ static int pcf50633_regulator_probe(struct platform_device *pdev) return 0; } -static int pcf50633_regulator_remove(struct platform_device *pdev) -{ - struct regulator_dev *rdev = platform_get_drvdata(pdev); - - regulator_unregister(rdev); - - return 0; -} - static struct platform_driver pcf50633_regulator_driver = { .driver = { .name = "pcf50633-regltr", }, .probe = pcf50633_regulator_probe, - .remove = pcf50633_regulator_remove, }; static int __init pcf50633_regulator_init(void) -- cgit v0.10.2 From 6a2b5a931f31c91cc2e261d99af7b3d881a41d86 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 30 Sep 2013 09:57:54 +0900 Subject: regulator: tps6105x: use devm_regulator_register() Use devm_regulator_register() to make cleanup paths simpler, and remove unnecessary remove(). Signed-off-by: Jingoo Han Acked-by: Linus Walleij Signed-off-by: Mark Brown diff --git a/drivers/regulator/tps6105x-regulator.c b/drivers/regulator/tps6105x-regulator.c index ec9453f..e0e818d 100644 --- a/drivers/regulator/tps6105x-regulator.c +++ b/drivers/regulator/tps6105x-regulator.c @@ -146,8 +146,9 @@ static int tps6105x_regulator_probe(struct platform_device *pdev) config.driver_data = tps6105x; /* Register regulator with framework */ - tps6105x->regulator = regulator_register(&tps6105x_regulator_desc, - &config); + tps6105x->regulator = devm_regulator_register(&pdev->dev, + &tps6105x_regulator_desc, + &config); if (IS_ERR(tps6105x->regulator)) { ret = PTR_ERR(tps6105x->regulator); dev_err(&tps6105x->client->dev, @@ -159,20 +160,12 @@ static int tps6105x_regulator_probe(struct platform_device *pdev) return 0; } -static int tps6105x_regulator_remove(struct platform_device *pdev) -{ - struct tps6105x *tps6105x = dev_get_platdata(&pdev->dev); - regulator_unregister(tps6105x->regulator); - return 0; -} - static struct platform_driver tps6105x_regulator_driver = { .driver = { .name = "tps6105x-regulator", .owner = THIS_MODULE, }, .probe = tps6105x_regulator_probe, - .remove = tps6105x_regulator_remove, }; static __init int tps6105x_regulator_init(void) -- cgit v0.10.2 From 00ce070e51f96ee4c942b44d63b7a15f5b777fd6 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 30 Sep 2013 09:59:04 +0900 Subject: regulator: twl: use devm_regulator_register() Use devm_regulator_register() to make cleanup paths simpler. Signed-off-by: Jingoo Han Acked-by: Nishanth Menon Signed-off-by: Mark Brown diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c index 78aae4c..8ebd785 100644 --- a/drivers/regulator/twl-regulator.c +++ b/drivers/regulator/twl-regulator.c @@ -1188,7 +1188,7 @@ static int twlreg_probe(struct platform_device *pdev) config.driver_data = info; config.of_node = pdev->dev.of_node; - rdev = regulator_register(&info->desc, &config); + rdev = devm_regulator_register(&pdev->dev, &info->desc, &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "can't register %s, %ld\n", info->desc.name, PTR_ERR(rdev)); @@ -1217,7 +1217,6 @@ static int twlreg_remove(struct platform_device *pdev) struct regulator_dev *rdev = platform_get_drvdata(pdev); struct twlreg_info *info = rdev->reg_data; - regulator_unregister(rdev); kfree(info); return 0; } -- cgit v0.10.2 From abf92a3d2e868fef808d4a205371b7e5417dd0f7 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 30 Sep 2013 09:59:31 +0900 Subject: regulator: vexpress: use devm_regulator_register() Use devm_regulator_register() to make cleanup paths simpler. Signed-off-by: Jingoo Han Acked-by: Pawel Moll Signed-off-by: Mark Brown diff --git a/drivers/regulator/vexpress.c b/drivers/regulator/vexpress.c index 4668c7f..f3ae28a 100644 --- a/drivers/regulator/vexpress.c +++ b/drivers/regulator/vexpress.c @@ -96,7 +96,7 @@ static int vexpress_regulator_probe(struct platform_device *pdev) config.driver_data = reg; config.of_node = pdev->dev.of_node; - reg->regdev = regulator_register(®->desc, &config); + reg->regdev = devm_regulator_register(&pdev->dev, ®->desc, &config); if (IS_ERR(reg->regdev)) { err = PTR_ERR(reg->regdev); goto error_regulator_register; @@ -119,7 +119,6 @@ static int vexpress_regulator_remove(struct platform_device *pdev) struct vexpress_regulator *reg = platform_get_drvdata(pdev); vexpress_config_func_put(reg->func); - regulator_unregister(reg->regdev); return 0; } -- cgit v0.10.2 From 11c0da7bb414505ff9a157af2b1301877a3726bd Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 30 Sep 2013 09:53:57 +0900 Subject: regulator: da9210: use devm_regulator_register() Use devm_regulator_register() to make cleanup paths simpler, and remove unnecessary remove(). Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/regulator/da9210-regulator.c b/drivers/regulator/da9210-regulator.c index f7ccff1..6f5ecbe 100644 --- a/drivers/regulator/da9210-regulator.c +++ b/drivers/regulator/da9210-regulator.c @@ -155,7 +155,7 @@ static int da9210_i2c_probe(struct i2c_client *i2c, config.regmap = chip->regmap; config.of_node = dev->of_node; - rdev = regulator_register(&da9210_reg, &config); + rdev = devm_regulator_register(&i2c->dev, &da9210_reg, &config); if (IS_ERR(rdev)) { dev_err(&i2c->dev, "Failed to register DA9210 regulator\n"); return PTR_ERR(rdev); @@ -168,13 +168,6 @@ static int da9210_i2c_probe(struct i2c_client *i2c, return 0; } -static int da9210_i2c_remove(struct i2c_client *i2c) -{ - struct da9210 *chip = i2c_get_clientdata(i2c); - regulator_unregister(chip->rdev); - return 0; -} - static const struct i2c_device_id da9210_i2c_id[] = { {"da9210", 0}, {}, @@ -188,7 +181,6 @@ static struct i2c_driver da9210_regulator_driver = { .owner = THIS_MODULE, }, .probe = da9210_i2c_probe, - .remove = da9210_i2c_remove, .id_table = da9210_i2c_id, }; -- cgit v0.10.2 From 5e0855759ca621bba454747fc1b5b76a7290f50f Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 30 Sep 2013 09:58:36 +0900 Subject: regulator: tps6524x: use devm_regulator_register() Use devm_regulator_register() to make cleanup paths simpler, and remove unnecessary remove(). Signed-off-by: Jingoo Han Reviewed-by: Axel Lin Signed-off-by: Mark Brown diff --git a/drivers/regulator/tps6524x-regulator.c b/drivers/regulator/tps6524x-regulator.c index 8b9ee39..9f6bfda 100644 --- a/drivers/regulator/tps6524x-regulator.c +++ b/drivers/regulator/tps6524x-regulator.c @@ -577,20 +577,6 @@ static struct regulator_ops regulator_ops = { .get_current_limit = get_current_limit, }; -static int pmic_remove(struct spi_device *spi) -{ - struct tps6524x *hw = spi_get_drvdata(spi); - int i; - - if (!hw) - return 0; - for (i = 0; i < N_REGULATORS; i++) { - regulator_unregister(hw->rdev[i]); - hw->rdev[i] = NULL; - } - return 0; -} - static int pmic_probe(struct spi_device *spi) { struct tps6524x *hw; @@ -598,7 +584,7 @@ static int pmic_probe(struct spi_device *spi) const struct supply_info *info = supply_info; struct regulator_init_data *init_data; struct regulator_config config = { }; - int ret = 0, i; + int i; init_data = dev_get_platdata(dev); if (!init_data) { @@ -631,24 +617,17 @@ static int pmic_probe(struct spi_device *spi) config.init_data = init_data; config.driver_data = hw; - hw->rdev[i] = regulator_register(&hw->desc[i], &config); - if (IS_ERR(hw->rdev[i])) { - ret = PTR_ERR(hw->rdev[i]); - hw->rdev[i] = NULL; - goto fail; - } + hw->rdev[i] = devm_regulator_register(dev, &hw->desc[i], + &config); + if (IS_ERR(hw->rdev[i])) + return PTR_ERR(hw->rdev[i]); } return 0; - -fail: - pmic_remove(spi); - return ret; } static struct spi_driver pmic_driver = { .probe = pmic_probe, - .remove = pmic_remove, .driver = { .name = "tps6524x", .owner = THIS_MODULE, -- cgit v0.10.2 From a72be8b89eb97655ea9b474bc7d6306672a922c2 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 1 Oct 2013 10:28:58 +0200 Subject: ARM: w90x900: delete The local file on the w90x900 is completely unused and the platform does not define NEED_MACH_GPIO_H. Cc: Wan ZongShun Signed-off-by: Linus Walleij diff --git a/arch/arm/mach-w90x900/include/mach/gpio.h b/arch/arm/mach-w90x900/include/mach/gpio.h deleted file mode 100644 index 5385a42..0000000 --- a/arch/arm/mach-w90x900/include/mach/gpio.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * linux/arch/arm/mach-w90p910/include/mach/gpio.h - * - * Generic w90p910 GPIO handling - * - * Wan ZongShun - * - * 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 __ASM_ARCH_W90P910_GPIO_H -#define __ASM_ARCH_W90P910_GPIO_H - -#include -#include - -static inline int gpio_to_irq(unsigned gpio) -{ - return gpio; -} -#define gpio_to_irq gpio_to_irq - -static inline int irq_to_gpio(unsigned irq) -{ - return irq; -} - -#endif -- cgit v0.10.2 From 85649711a8959f181b8b646bb985996736af97b8 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 1 Oct 2013 10:45:42 +0200 Subject: ARM: gemini: delete The Gemini has no need of a header as this is only used by the machine-local gpio.c gpiochip driver, which is also only using the irq_to_gpio() macro. Delete the file, move the single macro into the driver and remove the NEED_MACH_GPIO_H flag. Cc: Hans Ulli Kroll Signed-off-by: Linus Walleij diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index c0ed4b2..e4061de 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -388,7 +388,6 @@ config ARCH_GEMINI select ARCH_REQUIRE_GPIOLIB select ARCH_USES_GETTIMEOFFSET select CPU_FA526 - select NEED_MACH_GPIO_H help Support for the Cortina Systems Gemini family SoCs diff --git a/arch/arm/mach-gemini/gpio.c b/arch/arm/mach-gemini/gpio.c index 70bfa57..f8cb571 100644 --- a/arch/arm/mach-gemini/gpio.c +++ b/arch/arm/mach-gemini/gpio.c @@ -21,9 +21,9 @@ #include #include -#include #define GPIO_BASE(x) IO_ADDRESS(GEMINI_GPIO_BASE(x)) +#define irq_to_gpio(x) ((x) - GPIO_IRQ_BASE) /* GPIO registers definition */ #define GPIO_DATA_OUT 0x0 diff --git a/arch/arm/mach-gemini/include/mach/gpio.h b/arch/arm/mach-gemini/include/mach/gpio.h deleted file mode 100644 index 40a0527..0000000 --- a/arch/arm/mach-gemini/include/mach/gpio.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Gemini gpiolib specific defines - * - * Copyright (C) 2008-2009 Paulius Zaleckas - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#ifndef __MACH_GPIO_H__ -#define __MACH_GPIO_H__ - -#include - -#define gpio_to_irq(x) ((x) + GPIO_IRQ_BASE) -#define irq_to_gpio(x) ((x) - GPIO_IRQ_BASE) - -#endif /* __MACH_GPIO_H__ */ -- cgit v0.10.2 From a17bce4d1dce8f3cf714bc2e5d8e4bac009dc077 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 30 Sep 2013 11:56:24 +0200 Subject: x86/boot: Further compress CPUs bootup message Turn it into (for example): [ 0.073380] x86: Booting SMP configuration: [ 0.074005] .... node #0, CPUs: #1 #2 #3 #4 #5 #6 #7 [ 0.603005] .... node #1, CPUs: #8 #9 #10 #11 #12 #13 #14 #15 [ 1.200005] .... node #2, CPUs: #16 #17 #18 #19 #20 #21 #22 #23 [ 1.796005] .... node #3, CPUs: #24 #25 #26 #27 #28 #29 #30 #31 [ 2.393005] .... node #4, CPUs: #32 #33 #34 #35 #36 #37 #38 #39 [ 2.996005] .... node #5, CPUs: #40 #41 #42 #43 #44 #45 #46 #47 [ 3.600005] .... node #6, CPUs: #48 #49 #50 #51 #52 #53 #54 #55 [ 4.202005] .... node #7, CPUs: #56 #57 #58 #59 #60 #61 #62 #63 [ 4.811005] .... node #8, CPUs: #64 #65 #66 #67 #68 #69 #70 #71 [ 5.421006] .... node #9, CPUs: #72 #73 #74 #75 #76 #77 #78 #79 [ 6.032005] .... node #10, CPUs: #80 #81 #82 #83 #84 #85 #86 #87 [ 6.648006] .... node #11, CPUs: #88 #89 #90 #91 #92 #93 #94 #95 [ 7.262005] .... node #12, CPUs: #96 #97 #98 #99 #100 #101 #102 #103 [ 7.865005] .... node #13, CPUs: #104 #105 #106 #107 #108 #109 #110 #111 [ 8.466005] .... node #14, CPUs: #112 #113 #114 #115 #116 #117 #118 #119 [ 9.073006] .... node #15, CPUs: #120 #121 #122 #123 #124 #125 #126 #127 [ 9.679901] x86: Booted up 16 nodes, 128 CPUs and drop useless elements. Change num_digits() to hpa's division-avoiding, cell-phone-typed version which he went at great lengths and pains to submit on a Saturday evening. Signed-off-by: Borislav Petkov Cc: huawei.libin@huawei.com Cc: wangyijing@huawei.com Cc: fenghua.yu@intel.com Cc: guohanjun@huawei.com Cc: paul.gortmaker@windriver.com Cc: Linus Torvalds Cc: Andrew Morton Cc: Peter Zijlstra Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/20130930095624.GB16383@pd.tnic Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index d41f3ba..2a16558 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -647,22 +647,38 @@ wakeup_secondary_cpu_via_init(int phys_apicid, unsigned long start_eip) return (send_status | accept_status); } +void smp_announce(void) +{ + int num_nodes = num_online_nodes(); + + printk(KERN_INFO "x86: Booted up %d node%s, %d CPUs\n", + num_nodes, (num_nodes > 1 ? "s" : ""), num_online_cpus()); +} + /* reduce the number of lines printed when booting a large cpu count system */ static void announce_cpu(int cpu, int apicid) { static int current_node = -1; int node = early_cpu_to_node(cpu); - static int width; + static int width, node_width; if (!width) width = num_digits(num_possible_cpus()) + 1; /* + '#' sign */ + if (!node_width) + node_width = num_digits(num_possible_nodes()) + 1; /* + '#' */ + + if (cpu == 1) + printk(KERN_INFO "x86: Booting SMP configuration:\n"); + if (system_state == SYSTEM_BOOTING) { if (node != current_node) { if (current_node > (-1)) - pr_cont(" OK\n"); + pr_cont("\n"); current_node = node; - pr_info("Booting Node %3d, Processors:", node); + + printk(KERN_INFO ".... node %*s#%d, CPUs: ", + node_width - num_digits(node), " ", node); } /* Add padding for the BSP */ @@ -671,8 +687,6 @@ static void announce_cpu(int cpu, int apicid) pr_cont("%*s#%d", width - num_digits(cpu), " ", cpu); - if (cpu == num_present_cpus() - 1) - pr_cont(" OK\n"); } else pr_info("Booting Node %d Processor %d APIC 0x%x\n", node, cpu, apicid); diff --git a/arch/x86/lib/misc.c b/arch/x86/lib/misc.c index bc35cde..76b373a 100644 --- a/arch/x86/lib/misc.c +++ b/arch/x86/lib/misc.c @@ -1,11 +1,21 @@ +/* + * Count the digits of @val including a possible sign. + * + * (Typed on and submitted from hpa's mobile phone.) + */ int num_digits(int val) { - int digits = 0; + int m = 10; + int d = 1; - while (val) { - val /= 10; - digits++; + if (val < 0) { + d++; + val = -val; } - return digits; + while (val >= m) { + m *= 10; + d++; + } + return d; } diff --git a/kernel/smp.c b/kernel/smp.c index 0564571..f5768b0 100644 --- a/kernel/smp.c +++ b/kernel/smp.c @@ -524,6 +524,11 @@ void __init setup_nr_cpu_ids(void) nr_cpu_ids = find_last_bit(cpumask_bits(cpu_possible_mask),NR_CPUS) + 1; } +void __weak smp_announce(void) +{ + printk(KERN_INFO "Brought up %d CPUs\n", num_online_cpus()); +} + /* Called by boot processor to activate the rest. */ void __init smp_init(void) { @@ -540,7 +545,7 @@ void __init smp_init(void) } /* Any cleanup work */ - printk(KERN_INFO "Brought up %ld CPUs\n", (long)num_online_cpus()); + smp_announce(); smp_cpus_done(setup_max_cpus); } -- cgit v0.10.2 From 4fa8dbc18e8a57ea21c63103abdea042ab923202 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 30 Sep 2013 22:17:37 -0300 Subject: ASoC: fsl: imx-sgtl5000: Add .remove back Commit e8f00c1b01 (Merge remote-tracking branch 'asoc/fix/fsl' into asoc-devm) fixed a conflict, but missed to add the .remove function back,which causes the following build warning: sound/soc/fsl/imx-sgtl5000.c:185:12: warning: 'imx_sgtl5000_remove' defined but not used [-Wunused-function] Fix the warning by adding the .remove function back. Reported-by: Olof Johansson Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c index 6f4bdc8..ed6ba1e 100644 --- a/sound/soc/fsl/imx-sgtl5000.c +++ b/sound/soc/fsl/imx-sgtl5000.c @@ -204,6 +204,7 @@ static struct platform_driver imx_sgtl5000_driver = { .of_match_table = imx_sgtl5000_dt_ids, }, .probe = imx_sgtl5000_probe, + .remove = imx_sgtl5000_remove, }; module_platform_driver(imx_sgtl5000_driver); -- cgit v0.10.2 From e2c9917bfa4e104ba53819b37498eb45fbcd4e41 Mon Sep 17 00:00:00 2001 From: Vladimir Murzin Date: Sun, 29 Sep 2013 16:00:13 +0200 Subject: ASoC: kirkwood: fix compilation warning in kirkwood_dma_open writel() supposes the first argument of type unsigned int. This fix the warning: sound/soc/kirkwood/kirkwood-dma.c: In function 'kirkwood_dma_open': sound/soc/kirkwood/kirkwood-dma.c:164:3: warning: large integer implicitly truncated to unsigned type [-Woverflow] Signed-off-by: Vladimir Murzin Signed-off-by: Mark Brown diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c index 0c85c4e..55d0d9d3 100644 --- a/sound/soc/kirkwood/kirkwood-dma.c +++ b/sound/soc/kirkwood/kirkwood-dma.c @@ -159,7 +159,7 @@ static int kirkwood_dma_open(struct snd_pcm_substream *substream) * Enable Error interrupts. We're only ack'ing them but * it's useful for diagnostics */ - writel((unsigned long)-1, priv->io + KIRKWOOD_ERR_MASK); + writel((unsigned int)-1, priv->io + KIRKWOOD_ERR_MASK); } dram = mv_mbus_dram_info(); -- cgit v0.10.2 From 7d65f4a6553203da6a22097821d151fbbe7e4956 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Thu, 5 Sep 2013 15:49:45 +0200 Subject: irq: Consolidate do_softirq() arch overriden implementations All arch overriden implementations of do_softirq() share the following common code: disable irqs (to avoid races with the pending check), check if there are softirqs pending, then execute __do_softirq() on a specific stack. Consolidate the common parts such that archs only worry about the stack switch. Acked-by: Linus Torvalds Signed-off-by: Frederic Weisbecker Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: H. Peter Anvin Cc: Linus Torvalds Cc: Paul Mackerras Cc: James Hogan Cc: James E.J. Bottomley Cc: Helge Deller Cc: Martin Schwidefsky Cc: Heiko Carstens Cc: David S. Miller Cc: Andrew Morton diff --git a/arch/metag/kernel/irq.c b/arch/metag/kernel/irq.c index 2a2c9d5..3b4b7f6 100644 --- a/arch/metag/kernel/irq.c +++ b/arch/metag/kernel/irq.c @@ -159,44 +159,30 @@ void irq_ctx_exit(int cpu) extern asmlinkage void __do_softirq(void); -asmlinkage void do_softirq(void) +void do_softirq_own_stack(void) { - unsigned long flags; struct thread_info *curctx; union irq_ctx *irqctx; u32 *isp; - if (in_interrupt()) - return; - - local_irq_save(flags); - - if (local_softirq_pending()) { - curctx = current_thread_info(); - irqctx = softirq_ctx[smp_processor_id()]; - irqctx->tinfo.task = curctx->task; - - /* build the stack frame on the softirq stack */ - isp = (u32 *) ((char *)irqctx + sizeof(struct thread_info)); - - asm volatile ( - "MOV D0.5,%0\n" - "SWAP A0StP,D0.5\n" - "CALLR D1RtP,___do_softirq\n" - "MOV A0StP,D0.5\n" - : - : "r" (isp) - : "memory", "cc", "D1Ar1", "D0Ar2", "D1Ar3", "D0Ar4", - "D1Ar5", "D0Ar6", "D0Re0", "D1Re0", "D0.4", "D1RtP", - "D0.5" - ); - /* - * Shouldn't happen, we returned above if in_interrupt(): - */ - WARN_ON_ONCE(softirq_count()); - } - - local_irq_restore(flags); + curctx = current_thread_info(); + irqctx = softirq_ctx[smp_processor_id()]; + irqctx->tinfo.task = curctx->task; + + /* build the stack frame on the softirq stack */ + isp = (u32 *) ((char *)irqctx + sizeof(struct thread_info)); + + asm volatile ( + "MOV D0.5,%0\n" + "SWAP A0StP,D0.5\n" + "CALLR D1RtP,___do_softirq\n" + "MOV A0StP,D0.5\n" + : + : "r" (isp) + : "memory", "cc", "D1Ar1", "D0Ar2", "D1Ar3", "D0Ar4", + "D1Ar5", "D0Ar6", "D0Re0", "D1Re0", "D0.4", "D1RtP", + "D0.5" + ); } #endif diff --git a/arch/parisc/kernel/irq.c b/arch/parisc/kernel/irq.c index 2e6443b..ef59276 100644 --- a/arch/parisc/kernel/irq.c +++ b/arch/parisc/kernel/irq.c @@ -499,22 +499,9 @@ static void execute_on_irq_stack(void *func, unsigned long param1) *irq_stack_in_use = 1; } -asmlinkage void do_softirq(void) +void do_softirq_own_stack(void) { - __u32 pending; - unsigned long flags; - - if (in_interrupt()) - return; - - local_irq_save(flags); - - pending = local_softirq_pending(); - - if (pending) - execute_on_irq_stack(__do_softirq, 0); - - local_irq_restore(flags); + execute_on_irq_stack(__do_softirq, 0); } #endif /* CONFIG_IRQSTACKS */ diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index 57d286a..5c4adfc 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -593,7 +593,7 @@ void irq_ctx_init(void) } } -static inline void do_softirq_onstack(void) +void do_softirq_own_stack(void) { struct thread_info *curtp, *irqtp; @@ -611,21 +611,6 @@ static inline void do_softirq_onstack(void) set_bits(irqtp->flags, &curtp->flags); } -void do_softirq(void) -{ - unsigned long flags; - - if (in_interrupt()) - return; - - local_irq_save(flags); - - if (local_softirq_pending()) - do_softirq_onstack(); - - local_irq_restore(flags); -} - irq_hw_number_t virq_to_hw(unsigned int virq) { struct irq_data *irq_data = irq_get_irq_data(virq); diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index 8ac2097..bb27a26 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -157,39 +157,29 @@ int arch_show_interrupts(struct seq_file *p, int prec) /* * Switch to the asynchronous interrupt stack for softirq execution. */ -asmlinkage void do_softirq(void) +void do_softirq_own_stack(void) { - unsigned long flags, old, new; - - if (in_interrupt()) - return; - - local_irq_save(flags); - - if (local_softirq_pending()) { - /* Get current stack pointer. */ - asm volatile("la %0,0(15)" : "=a" (old)); - /* Check against async. stack address range. */ - new = S390_lowcore.async_stack; - if (((new - old) >> (PAGE_SHIFT + THREAD_ORDER)) != 0) { - /* Need to switch to the async. stack. */ - new -= STACK_FRAME_OVERHEAD; - ((struct stack_frame *) new)->back_chain = old; - - asm volatile(" la 15,0(%0)\n" - " basr 14,%2\n" - " la 15,0(%1)\n" - : : "a" (new), "a" (old), - "a" (__do_softirq) - : "0", "1", "2", "3", "4", "5", "14", - "cc", "memory" ); - } else { - /* We are already on the async stack. */ - __do_softirq(); - } + unsigned long old, new; + + /* Get current stack pointer. */ + asm volatile("la %0,0(15)" : "=a" (old)); + /* Check against async. stack address range. */ + new = S390_lowcore.async_stack; + if (((new - old) >> (PAGE_SHIFT + THREAD_ORDER)) != 0) { + /* Need to switch to the async. stack. */ + new -= STACK_FRAME_OVERHEAD; + ((struct stack_frame *) new)->back_chain = old; + asm volatile(" la 15,0(%0)\n" + " basr 14,%2\n" + " la 15,0(%1)\n" + : : "a" (new), "a" (old), + "a" (__do_softirq) + : "0", "1", "2", "3", "4", "5", "14", + "cc", "memory" ); + } else { + /* We are already on the async stack. */ + __do_softirq(); } - - local_irq_restore(flags); } /* diff --git a/arch/sh/kernel/irq.c b/arch/sh/kernel/irq.c index 063af10..0833736 100644 --- a/arch/sh/kernel/irq.c +++ b/arch/sh/kernel/irq.c @@ -149,47 +149,32 @@ void irq_ctx_exit(int cpu) hardirq_ctx[cpu] = NULL; } -asmlinkage void do_softirq(void) +void do_softirq_own_stack(void) { - unsigned long flags; struct thread_info *curctx; union irq_ctx *irqctx; u32 *isp; - if (in_interrupt()) - return; - - local_irq_save(flags); - - if (local_softirq_pending()) { - curctx = current_thread_info(); - irqctx = softirq_ctx[smp_processor_id()]; - irqctx->tinfo.task = curctx->task; - irqctx->tinfo.previous_sp = current_stack_pointer; - - /* build the stack frame on the softirq stack */ - isp = (u32 *)((char *)irqctx + sizeof(*irqctx)); - - __asm__ __volatile__ ( - "mov r15, r9 \n" - "jsr @%0 \n" - /* switch to the softirq stack */ - " mov %1, r15 \n" - /* restore the thread stack */ - "mov r9, r15 \n" - : /* no outputs */ - : "r" (__do_softirq), "r" (isp) - : "memory", "r0", "r1", "r2", "r3", "r4", - "r5", "r6", "r7", "r8", "r9", "r15", "t", "pr" - ); - - /* - * Shouldn't happen, we returned above if in_interrupt(): - */ - WARN_ON_ONCE(softirq_count()); - } - - local_irq_restore(flags); + curctx = current_thread_info(); + irqctx = softirq_ctx[smp_processor_id()]; + irqctx->tinfo.task = curctx->task; + irqctx->tinfo.previous_sp = current_stack_pointer; + + /* build the stack frame on the softirq stack */ + isp = (u32 *)((char *)irqctx + sizeof(*irqctx)); + + __asm__ __volatile__ ( + "mov r15, r9 \n" + "jsr @%0 \n" + /* switch to the softirq stack */ + " mov %1, r15 \n" + /* restore the thread stack */ + "mov r9, r15 \n" + : /* no outputs */ + : "r" (__do_softirq), "r" (isp) + : "memory", "r0", "r1", "r2", "r3", "r4", + "r5", "r6", "r7", "r8", "r9", "r15", "t", "pr" + ); } #else static inline void handle_one_irq(unsigned int irq) diff --git a/arch/sparc/kernel/irq_64.c b/arch/sparc/kernel/irq_64.c index d4840ce..666193f 100644 --- a/arch/sparc/kernel/irq_64.c +++ b/arch/sparc/kernel/irq_64.c @@ -698,30 +698,19 @@ void __irq_entry handler_irq(int pil, struct pt_regs *regs) set_irq_regs(old_regs); } -void do_softirq(void) +void do_softirq_own_stack(void) { - unsigned long flags; - - if (in_interrupt()) - return; - - local_irq_save(flags); + void *orig_sp, *sp = softirq_stack[smp_processor_id()]; - if (local_softirq_pending()) { - void *orig_sp, *sp = softirq_stack[smp_processor_id()]; - - sp += THREAD_SIZE - 192 - STACK_BIAS; - - __asm__ __volatile__("mov %%sp, %0\n\t" - "mov %1, %%sp" - : "=&r" (orig_sp) - : "r" (sp)); - __do_softirq(); - __asm__ __volatile__("mov %0, %%sp" - : : "r" (orig_sp)); - } + sp += THREAD_SIZE - 192 - STACK_BIAS; - local_irq_restore(flags); + __asm__ __volatile__("mov %%sp, %0\n\t" + "mov %1, %%sp" + : "=&r" (orig_sp) + : "r" (sp)); + __do_softirq(); + __asm__ __volatile__("mov %0, %%sp" + : : "r" (orig_sp)); } #ifdef CONFIG_HOTPLUG_CPU diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index b077f4c..083da7c 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S @@ -1342,7 +1342,7 @@ bad_gs: .previous /* Call softirq on interrupt stack. Interrupts are off. */ -ENTRY(call_softirq) +ENTRY(do_softirq_own_stack) CFI_STARTPROC pushq_cfi %rbp CFI_REL_OFFSET rbp,0 @@ -1359,7 +1359,7 @@ ENTRY(call_softirq) decl PER_CPU_VAR(irq_count) ret CFI_ENDPROC -END(call_softirq) +END(do_softirq_own_stack) #ifdef CONFIG_XEN zeroentry xen_hypervisor_callback xen_do_hypervisor_callback diff --git a/arch/x86/kernel/irq_32.c b/arch/x86/kernel/irq_32.c index 4186755..8a5bb01 100644 --- a/arch/x86/kernel/irq_32.c +++ b/arch/x86/kernel/irq_32.c @@ -149,35 +149,21 @@ void irq_ctx_init(int cpu) cpu, per_cpu(hardirq_ctx, cpu), per_cpu(softirq_ctx, cpu)); } -asmlinkage void do_softirq(void) +void do_softirq_own_stack(void) { - unsigned long flags; struct thread_info *curctx; union irq_ctx *irqctx; u32 *isp; - if (in_interrupt()) - return; - - local_irq_save(flags); - - if (local_softirq_pending()) { - curctx = current_thread_info(); - irqctx = __this_cpu_read(softirq_ctx); - irqctx->tinfo.task = curctx->task; - irqctx->tinfo.previous_esp = current_stack_pointer; - - /* build the stack frame on the softirq stack */ - isp = (u32 *) ((char *)irqctx + sizeof(*irqctx)); + curctx = current_thread_info(); + irqctx = __this_cpu_read(softirq_ctx); + irqctx->tinfo.task = curctx->task; + irqctx->tinfo.previous_esp = current_stack_pointer; - call_on_stack(__do_softirq, isp); - /* - * Shouldn't happen, we returned above if in_interrupt(): - */ - WARN_ON_ONCE(softirq_count()); - } + /* build the stack frame on the softirq stack */ + isp = (u32 *) ((char *)irqctx + sizeof(*irqctx)); - local_irq_restore(flags); + call_on_stack(__do_softirq, isp); } bool handle_irq(unsigned irq, struct pt_regs *regs) diff --git a/arch/x86/kernel/irq_64.c b/arch/x86/kernel/irq_64.c index d04d3ec..4d1c746 100644 --- a/arch/x86/kernel/irq_64.c +++ b/arch/x86/kernel/irq_64.c @@ -87,24 +87,3 @@ bool handle_irq(unsigned irq, struct pt_regs *regs) generic_handle_irq_desc(irq, desc); return true; } - - -extern void call_softirq(void); - -asmlinkage void do_softirq(void) -{ - __u32 pending; - unsigned long flags; - - if (in_interrupt()) - return; - - local_irq_save(flags); - pending = local_softirq_pending(); - /* Switch to interrupt stack */ - if (pending) { - call_softirq(); - WARN_ON_ONCE(softirq_count()); - } - local_irq_restore(flags); -} diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 5e865b5..c9e831d 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -19,6 +19,7 @@ #include #include +#include /* * These correspond to the IORESOURCE_IRQ_* defines in @@ -374,6 +375,16 @@ struct softirq_action asmlinkage void do_softirq(void); asmlinkage void __do_softirq(void); + +#ifdef __ARCH_HAS_DO_SOFTIRQ +void do_softirq_own_stack(void); +#else +static inline void do_softirq_own_stack(void) +{ + __do_softirq(); +} +#endif + extern void open_softirq(int nr, void (*action)(struct softirq_action *)); extern void softirq_init(void); extern void __raise_softirq_irqoff(unsigned int nr); diff --git a/kernel/softirq.c b/kernel/softirq.c index d7d498d..26ee727 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -29,7 +29,6 @@ #define CREATE_TRACE_POINTS #include -#include /* - No shared variables, all the data are CPU local. - If a softirq needs serialization, let it serialize itself @@ -283,7 +282,7 @@ restart: tsk_restore_flags(current, old_flags, PF_MEMALLOC); } -#ifndef __ARCH_HAS_DO_SOFTIRQ + asmlinkage void do_softirq(void) { @@ -298,13 +297,12 @@ asmlinkage void do_softirq(void) pending = local_softirq_pending(); if (pending) - __do_softirq(); + do_softirq_own_stack(); + WARN_ON_ONCE(softirq_count()); local_irq_restore(flags); } -#endif - /* * Enter an interrupt context. */ -- cgit v0.10.2 From be6e1016440860fc4ec098b2d0aed3d0397b5d6e Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 24 Sep 2013 16:39:41 +0200 Subject: irq: Optimize call to softirq on hardirq exit Before processing softirqs on hardirq exit, we already do the check for pending softirqs while hardirqs are guaranteed to be disabled. So we can take a shortcut and safely jump to the arch specific implementation directly. Acked-by: Linus Torvalds Signed-off-by: Frederic Weisbecker Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: H. Peter Anvin Cc: Linus Torvalds Cc: Paul Mackerras Cc: James Hogan Cc: James E.J. Bottomley Cc: Helge Deller Cc: Martin Schwidefsky Cc: Heiko Carstens Cc: David S. Miller Cc: Andrew Morton diff --git a/kernel/softirq.c b/kernel/softirq.c index 26ee727..17c5cd2 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -335,7 +335,7 @@ static inline void invoke_softirq(void) * in its own stack to prevent from any overrun on top * of a potentially deep task stack. */ - do_softirq(); + do_softirq_own_stack(); } else { wakeup_softirqd(); } -- cgit v0.10.2 From 5d60d3e7c08a46643e902e39d9743cf394382151 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 24 Sep 2013 04:11:35 +0200 Subject: irq: Improve a bit softirq debugging do_softirq() has a debug check that verifies that it is not nesting on softirqs processing, nor miscounting the softirq part of the preempt count. But making sure that softirqs processing don't nest is actually a more generic concern that applies to any caller of __do_softirq(). Do take it one step further and generalize that debug check to any softirq processing. Acked-by: Linus Torvalds Signed-off-by: Frederic Weisbecker Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: H. Peter Anvin Cc: Linus Torvalds Cc: Paul Mackerras Cc: James Hogan Cc: James E.J. Bottomley Cc: Helge Deller Cc: Martin Schwidefsky Cc: Heiko Carstens Cc: David S. Miller Cc: Andrew Morton diff --git a/kernel/softirq.c b/kernel/softirq.c index 17c5cd2..9f8092b 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -133,7 +133,6 @@ EXPORT_SYMBOL(local_bh_disable); static void __local_bh_enable(unsigned int cnt) { - WARN_ON_ONCE(in_irq()); WARN_ON_ONCE(!irqs_disabled()); if (softirq_count() == cnt) @@ -148,6 +147,7 @@ static void __local_bh_enable(unsigned int cnt) */ void _local_bh_enable(void) { + WARN_ON_ONCE(in_irq()); __local_bh_enable(SOFTIRQ_DISABLE_OFFSET); } @@ -279,6 +279,7 @@ restart: account_irq_exit_time(current); __local_bh_enable(SOFTIRQ_OFFSET); + WARN_ON_ONCE(in_interrupt()); tsk_restore_flags(current, old_flags, PF_MEMALLOC); } @@ -299,7 +300,6 @@ asmlinkage void do_softirq(void) if (pending) do_softirq_own_stack(); - WARN_ON_ONCE(softirq_count()); local_irq_restore(flags); } -- cgit v0.10.2 From 0bed698a334766ed07bacd6cb33f0228003a7f61 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Thu, 5 Sep 2013 16:14:00 +0200 Subject: irq: Justify the various softirq stack choices For clarity, comment the various stack choices for softirqs processing, whether we execute them from ksoftirqd or local_irq_enable() calls. Their use on irq_exit() is already commented. Acked-by: Linus Torvalds Signed-off-by: Frederic Weisbecker Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: H. Peter Anvin Cc: Linus Torvalds Cc: Paul Mackerras Cc: James Hogan Cc: James E.J. Bottomley Cc: Helge Deller Cc: Martin Schwidefsky Cc: Heiko Carstens Cc: David S. Miller Cc: Andrew Morton diff --git a/kernel/softirq.c b/kernel/softirq.c index 9f8092b..2b4328e 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -170,8 +170,13 @@ static inline void _local_bh_enable_ip(unsigned long ip) */ sub_preempt_count(SOFTIRQ_DISABLE_OFFSET - 1); - if (unlikely(!in_interrupt() && local_softirq_pending())) + if (unlikely(!in_interrupt() && local_softirq_pending())) { + /* + * Run softirq if any pending. And do it in its own stack + * as we may be calling this deep in a task call stack already. + */ do_softirq(); + } dec_preempt_count(); #ifdef CONFIG_TRACE_IRQFLAGS @@ -769,6 +774,10 @@ static void run_ksoftirqd(unsigned int cpu) { local_irq_disable(); if (local_softirq_pending()) { + /* + * We can safely run softirq on inline stack, as we are not deep + * in the task stack here. + */ __do_softirq(); rcu_note_context_switch(cpu); local_irq_enable(); -- cgit v0.10.2 From cc1f027454929924471bea2f362431072e3c71be Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 24 Sep 2013 17:17:47 +0200 Subject: irq: Optimize softirq stack selection in irq exit If irq_exit() is called on the arch's specified irq stack, it should be safe to run softirqs inline under that same irq stack as it is near empty by the time we call irq_exit(). For example if we use the same stack for both hard and soft irqs here, the worst case scenario is: hardirq -> softirq -> hardirq. But then the softirq supersedes the first hardirq as the stack user since irq_exit() is called in a mostly empty stack. So the stack merge in this case looks acceptable. Stack overrun still have a chance to happen if hardirqs have more opportunities to nest, but then it's another problem to solve. So lets adapt the irq exit's softirq stack on top of a new Kconfig symbol that can be defined when irq_exit() runs on the irq stack. That way we can spare some stack switch on irq processing and all the cache issues that come along. Acked-by: Linus Torvalds Signed-off-by: Frederic Weisbecker Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: H. Peter Anvin Cc: Linus Torvalds Cc: Paul Mackerras Cc: James Hogan Cc: James E.J. Bottomley Cc: Helge Deller Cc: Martin Schwidefsky Cc: Heiko Carstens Cc: David S. Miller Cc: Andrew Morton diff --git a/arch/Kconfig b/arch/Kconfig index af2cc6e..ad95133 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -390,6 +390,16 @@ config HAVE_UNDERSCORE_SYMBOL_PREFIX Some architectures generate an _ in front of C symbols; things like module loading and assembly files need to know about this. +config HAVE_IRQ_EXIT_ON_IRQ_STACK + bool + help + Architecture doesn't only execute the irq handler on the irq stack + but also irq_exit(). This way we can process softirqs on this irq + stack instead of switching to a new one when we call __do_softirq() + in the end of an hardirq. + This spares a stack switch and improves cache usage on softirq + processing. + # # ABI hall of shame # diff --git a/kernel/softirq.c b/kernel/softirq.c index 2b4328e..dacd0ab 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -332,15 +332,21 @@ void irq_enter(void) static inline void invoke_softirq(void) { if (!force_irqthreads) { +#ifdef CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK /* * We can safely execute softirq on the current stack if * it is the irq stack, because it should be near empty - * at this stage. But we have no way to know if the arch - * calls irq_exit() on the irq stack. So call softirq - * in its own stack to prevent from any overrun on top - * of a potentially deep task stack. + * at this stage. + */ + __do_softirq(); +#else + /* + * Otherwise, irq_exit() is called on the task stack that can + * be potentially deep already. So call softirq in its own stack + * to prevent from any overrun. */ do_softirq_own_stack(); +#endif } else { wakeup_softirqd(); } -- cgit v0.10.2 From a2cd11f7d58a3a5390ee4cbcd8cff634a4d59173 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 24 Sep 2013 17:18:36 +0200 Subject: x86: Tell about irq stack coverage x86-64 runs irq_exit() under the irq stack. So it can afford to run softirqs in hardirq exit without the need to switch the stacks. The hardirq stack is good enough for that. Now x86-64 runs softirqs in the hardirq stack anyway, so what we mostly skip is some needless per cpu refcounting updates there. x86-32 is not concerned because it only runs the irq handler on the irq stack. Acked-by: Linus Torvalds Signed-off-by: Frederic Weisbecker Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: H. Peter Anvin Cc: Linus Torvalds Cc: Paul Mackerras Cc: James Hogan Cc: James E.J. Bottomley Cc: Helge Deller Cc: Martin Schwidefsky Cc: Heiko Carstens Cc: David S. Miller Cc: Andrew Morton diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index ee2fb9d..99eb1cc 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -123,6 +123,7 @@ config X86 select COMPAT_OLD_SIGACTION if IA32_EMULATION select RTC_LIB select HAVE_DEBUG_STACKOVERFLOW + select HAVE_IRQ_EXIT_ON_IRQ_STACK if X86_64 config INSTRUCTION_DECODER def_bool y -- cgit v0.10.2 From 62d26c8200a8382e1c67419ca3aff78d37898cc5 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 24 Sep 2013 17:18:36 +0200 Subject: powerpc: Tell about irq stack coverage Now that powerpc runs irq_exit() under the irq stack, let the softirq core know about that so that we spare the needless stack switch on irq exit's softirq processing. Acked-by: Benjamin Herrenschmidt Acked-by: Linus Torvalds Signed-off-by: Frederic Weisbecker Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: H. Peter Anvin Cc: Linus Torvalds Cc: Paul Mackerras Cc: James Hogan Cc: James E.J. Bottomley Cc: Helge Deller Cc: Martin Schwidefsky Cc: Heiko Carstens Cc: David S. Miller Cc: Andrew Morton diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 38f3b7e..b365d5c 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -138,6 +138,7 @@ config PPC select OLD_SIGSUSPEND select OLD_SIGACTION if PPC32 select HAVE_DEBUG_STACKOVERFLOW + select HAVE_IRQ_EXIT_ON_IRQ_STACK config EARLY_PRINTK bool -- cgit v0.10.2 From a6d30e0fffb32ab9e48fa7fc8f463805d5b0bddb Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 30 Sep 2013 15:21:15 +0200 Subject: x86/geode: Fix incorrect placement of __initdata tag __initdata tag should be placed between the variable name and equal sign for the variable to be placed in the intended .init.data section. Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Link: http://lkml.kernel.org/r/18427893.G5JGWn465D@amdc1032 Signed-off-by: Ingo Molnar diff --git a/arch/x86/platform/geode/alix.c b/arch/x86/platform/geode/alix.c index 90e23e7..76b6632 100644 --- a/arch/x86/platform/geode/alix.c +++ b/arch/x86/platform/geode/alix.c @@ -98,7 +98,7 @@ static struct platform_device alix_leds_dev = { .dev.platform_data = &alix_leds_data, }; -static struct __initdata platform_device *alix_devs[] = { +static struct platform_device *alix_devs[] __initdata = { &alix_buttons_dev, &alix_leds_dev, }; diff --git a/arch/x86/platform/geode/geos.c b/arch/x86/platform/geode/geos.c index c2e6d53..aa733fb 100644 --- a/arch/x86/platform/geode/geos.c +++ b/arch/x86/platform/geode/geos.c @@ -87,7 +87,7 @@ static struct platform_device geos_leds_dev = { .dev.platform_data = &geos_leds_data, }; -static struct __initdata platform_device *geos_devs[] = { +static struct platform_device *geos_devs[] __initdata = { &geos_buttons_dev, &geos_leds_dev, }; diff --git a/arch/x86/platform/geode/net5501.c b/arch/x86/platform/geode/net5501.c index 646e3b5..927e38c 100644 --- a/arch/x86/platform/geode/net5501.c +++ b/arch/x86/platform/geode/net5501.c @@ -78,7 +78,7 @@ static struct platform_device net5501_leds_dev = { .dev.platform_data = &net5501_leds_data, }; -static struct __initdata platform_device *net5501_devs[] = { +static struct platform_device *net5501_devs[] __initdata = { &net5501_buttons_dev, &net5501_leds_dev, }; -- cgit v0.10.2 From f1c8b2edf916b5be9dc29e98989a5eaff3c6e75b Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Sun, 29 Sep 2013 02:37:14 +0200 Subject: clk: Add error handling to clk_fetch_parent_index() There are at least two different error cases that can happen in clk_fetch_parent_index() function: - allocation failure, - parent clock lookup failure, however it returns only an u8, which is supposed to contain parent clock index. This patch modified the function to return full int instead allowing positive clock indices and negative error codes to be returned. All users of this function are adjusted as well to handle the return value correctly. Signed-off-by: Tomasz Figa Signed-off-by: Mike Turquette diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index a004769..9e0a837 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1080,13 +1080,16 @@ unsigned long clk_get_rate(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_get_rate); -static u8 clk_fetch_parent_index(struct clk *clk, struct clk *parent) +static int clk_fetch_parent_index(struct clk *clk, struct clk *parent) { - u8 i; + int i; - if (!clk->parents) + if (!clk->parents) { clk->parents = kzalloc((sizeof(struct clk*) * clk->num_parents), GFP_KERNEL); + if (!clk->parents) + return -ENOMEM; + } /* * find index of new parent clock using cached parent ptrs, @@ -1095,15 +1098,15 @@ static u8 clk_fetch_parent_index(struct clk *clk, struct clk *parent) */ for (i = 0; i < clk->num_parents; i++) { if (clk->parents && clk->parents[i] == parent) - break; + return i; else if (!strcmp(clk->parent_names[i], parent->name)) { if (clk->parents) clk->parents[i] = __clk_lookup(parent->name); - break; + return i; } } - return i; + return -EINVAL; } static void clk_reparent(struct clk *clk, struct clk *new_parent) @@ -1265,7 +1268,7 @@ static struct clk *clk_calc_new_rates(struct clk *clk, unsigned long rate) struct clk *old_parent, *parent; unsigned long best_parent_rate = 0; unsigned long new_rate; - u8 p_index = 0; + int p_index = 0; /* sanity */ if (IS_ERR_OR_NULL(clk)) @@ -1306,7 +1309,7 @@ static struct clk *clk_calc_new_rates(struct clk *clk, unsigned long rate) /* try finding the new parent index */ if (parent) { p_index = clk_fetch_parent_index(clk, parent); - if (p_index == clk->num_parents) { + if (p_index < 0) { pr_debug("%s: clk %s can not be parent of clk %s\n", __func__, parent->name, clk->name); return NULL; @@ -1568,7 +1571,7 @@ void __clk_reparent(struct clk *clk, struct clk *new_parent) int clk_set_parent(struct clk *clk, struct clk *parent) { int ret = 0; - u8 p_index = 0; + int p_index = 0; unsigned long p_rate = 0; if (!clk) @@ -1597,10 +1600,10 @@ int clk_set_parent(struct clk *clk, struct clk *parent) if (parent) { p_index = clk_fetch_parent_index(clk, parent); p_rate = parent->rate; - if (p_index == clk->num_parents) { + if (p_index < 0) { pr_debug("%s: clk %s can not be parent of clk %s\n", __func__, parent->name, clk->name); - ret = -EINVAL; + ret = p_index; goto out; } } -- cgit v0.10.2 From 96a7ed9079a3483c5681b17f4713c37c1cf2b1c9 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Sun, 29 Sep 2013 02:37:15 +0200 Subject: clk: Use kcalloc() to allocate arrays Instead of calculating sizes of arrays manually, kcalloc() can be used to allocate arrays of elements with defined size. This is just a cleanup patch without any functional changes. Signed-off-by: Tomasz Figa Signed-off-by: Mike Turquette diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 9e0a837..63f9ac1 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1085,8 +1085,8 @@ static int clk_fetch_parent_index(struct clk *clk, struct clk *parent) int i; if (!clk->parents) { - clk->parents = kzalloc((sizeof(struct clk*) * clk->num_parents), - GFP_KERNEL); + clk->parents = kcalloc(clk->num_parents, + sizeof(struct clk *), GFP_KERNEL); if (!clk->parents) return -ENOMEM; } @@ -1535,7 +1535,7 @@ static struct clk *__clk_init_parent(struct clk *clk) if (!clk->parents) clk->parents = - kzalloc((sizeof(struct clk*) * clk->num_parents), + kcalloc(clk->num_parents, sizeof(struct clk *), GFP_KERNEL); ret = clk_get_parent_by_index(clk, index); @@ -1692,8 +1692,8 @@ int __clk_init(struct device *dev, struct clk *clk) * for clock drivers to statically initialize clk->parents. */ if (clk->num_parents > 1 && !clk->parents) { - clk->parents = kzalloc((sizeof(struct clk*) * clk->num_parents), - GFP_KERNEL); + clk->parents = kcalloc(clk->num_parents, sizeof(struct clk *), + GFP_KERNEL); /* * __clk_lookup returns NULL for parents that have not been * clk_init'd; thus any access to clk->parents[] must check @@ -1833,8 +1833,8 @@ static int _clk_register(struct device *dev, struct clk_hw *hw, struct clk *clk) hw->clk = clk; /* allocate local copy in case parent_names is __initdata */ - clk->parent_names = kzalloc((sizeof(char*) * clk->num_parents), - GFP_KERNEL); + clk->parent_names = kcalloc(clk->num_parents, sizeof(char *), + GFP_KERNEL); if (!clk->parent_names) { pr_err("%s: could not allocate clk->parent_names\n", __func__); -- cgit v0.10.2 From da0f0b2c3ad2ad9533c8c5cae84ad88d57a5e8dc Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Sun, 29 Sep 2013 02:37:16 +0200 Subject: clk: Correct lookup logic in clk_fetch_parent_index() This function is supposed to iterate over all parents of given child clock to find the index of given parent clock in its parent list, using parent cache if possible and falling back to string compare otherwise. However currently the logic falls back to string compare in every iteration in which clock cache entry does not match given parent, due to wrong check conditions. This patch corrects the logic to continue the loop if parent cache entry is present and does not match requested parent clock. In addition, redundant checks for parent cache array presence are removed, because it is always allocated in the beginning of the function. Signed-off-by: Tomasz Figa Signed-off-by: Mike Turquette diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 63f9ac1..32e2fed 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1097,11 +1097,14 @@ static int clk_fetch_parent_index(struct clk *clk, struct clk *parent) * them now to avoid future calls to __clk_lookup. */ for (i = 0; i < clk->num_parents; i++) { - if (clk->parents && clk->parents[i] == parent) + if (clk->parents[i] == parent) return i; - else if (!strcmp(clk->parent_names[i], parent->name)) { - if (clk->parents) - clk->parents[i] = __clk_lookup(parent->name); + + if (clk->parents[i]) + continue; + + if (!strcmp(clk->parent_names[i], parent->name)) { + clk->parents[i] = __clk_lookup(parent->name); return i; } } -- cgit v0.10.2 From e56e57f6613d5ed5c3127419341d1aa989a11971 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Tue, 1 Oct 2013 16:36:55 -0400 Subject: x86/reboot: Sort reboot DMI quirks by vendor Grouping them by vendor should make it easier to spot duplicates. Signed-off-by: Dave Jones Link: http://lkml.kernel.org/r/20131001203655.GA10719@redhat.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index d9333a4..7692520 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -136,236 +136,248 @@ static int __init set_kbd_reboot(const struct dmi_system_id *d) * This is a single dmi_table handling all reboot quirks. */ static struct dmi_system_id __initdata reboot_dmi_table[] = { - { /* Handle problems with rebooting on Dell E520's */ - .callback = set_bios_reboot, - .ident = "Dell E520", + + /* Acer */ + { /* Handle reboot issue on Acer Aspire one */ + .callback = set_kbd_reboot, + .ident = "Acer Aspire One A110", .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Dell DM061"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "AOA110"), }, }, - { /* Handle problems with rebooting on Dell 1300's */ - .callback = set_bios_reboot, - .ident = "Dell PowerEdge 1300", + + /* Apple */ + { /* Handle problems with rebooting on Apple MacBook5 */ + .callback = set_pci_reboot, + .ident = "Apple MacBook5", .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 1300/"), + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5"), }, }, - { /* Handle problems with rebooting on Dell 300's */ - .callback = set_bios_reboot, - .ident = "Dell PowerEdge 300", + { /* Handle problems with rebooting on Apple MacBookPro5 */ + .callback = set_pci_reboot, + .ident = "Apple MacBookPro5", .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 300/"), + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5"), }, }, - { /* Handle problems with rebooting on Dell Optiplex 745's SFF */ - .callback = set_bios_reboot, - .ident = "Dell OptiPlex 745", + { /* Handle problems with rebooting on Apple Macmini3,1 */ + .callback = set_pci_reboot, + .ident = "Apple Macmini3,1", .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 745"), + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Macmini3,1"), }, }, - { /* Handle problems with rebooting on Dell Optiplex 745's DFF */ - .callback = set_bios_reboot, - .ident = "Dell OptiPlex 745", + { /* Handle problems with rebooting on the iMac9,1. */ + .callback = set_pci_reboot, + .ident = "Apple iMac9,1", .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 745"), - DMI_MATCH(DMI_BOARD_NAME, "0MM599"), + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "iMac9,1"), }, }, - { /* Handle problems with rebooting on Dell Optiplex 745 with 0KW626 */ + + /* ASUS */ + { /* Handle problems with rebooting on ASUS P4S800 */ .callback = set_bios_reboot, - .ident = "Dell OptiPlex 745", + .ident = "ASUS P4S800", .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 745"), - DMI_MATCH(DMI_BOARD_NAME, "0KW626"), + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), + DMI_MATCH(DMI_BOARD_NAME, "P4S800"), }, }, - { /* Handle problems with rebooting on Dell Optiplex 330 with 0KP561 */ + + /* Dell */ + { /* Handle problems with rebooting on Dell DXP061 */ .callback = set_bios_reboot, - .ident = "Dell OptiPlex 330", + .ident = "Dell DXP061", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 330"), - DMI_MATCH(DMI_BOARD_NAME, "0KP561"), + DMI_MATCH(DMI_PRODUCT_NAME, "Dell DXP061"), }, }, - { /* Handle problems with rebooting on Dell Optiplex 360 with 0T656F */ + { /* Handle problems with rebooting on Dell E520's */ .callback = set_bios_reboot, - .ident = "Dell OptiPlex 360", + .ident = "Dell E520", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 360"), - DMI_MATCH(DMI_BOARD_NAME, "0T656F"), + DMI_MATCH(DMI_PRODUCT_NAME, "Dell DM061"), }, }, - { /* Handle problems with rebooting on Dell OptiPlex 760 with 0G919G */ - .callback = set_bios_reboot, - .ident = "Dell OptiPlex 760", + { /* Handle problems with rebooting on the Latitude E5420. */ + .callback = set_pci_reboot, + .ident = "Dell Latitude E5420", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 760"), - DMI_MATCH(DMI_BOARD_NAME, "0G919G"), + DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E5420"), }, }, - { /* Handle problems with rebooting on Dell 2400's */ - .callback = set_bios_reboot, - .ident = "Dell PowerEdge 2400", + { /* Handle problems with rebooting on the Latitude E6320. */ + .callback = set_pci_reboot, + .ident = "Dell Latitude E6320", .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 2400"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E6320"), }, }, - { /* Handle problems with rebooting on Dell T5400's */ - .callback = set_bios_reboot, - .ident = "Dell Precision T5400", + { /* Handle problems with rebooting on the Latitude E6420. */ + .callback = set_pci_reboot, + .ident = "Dell Latitude E6420", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Precision WorkStation T5400"), + DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E6420"), }, }, - { /* Handle problems with rebooting on Dell T7400's */ + { /* Handle problems with rebooting on Dell Optiplex 330 with 0KP561 */ .callback = set_bios_reboot, - .ident = "Dell Precision T7400", + .ident = "Dell OptiPlex 330", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Precision WorkStation T7400"), + DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 330"), + DMI_MATCH(DMI_BOARD_NAME, "0KP561"), }, }, - { /* Handle problems with rebooting on HP laptops */ + { /* Handle problems with rebooting on Dell Optiplex 360 with 0T656F */ .callback = set_bios_reboot, - .ident = "HP Compaq Laptop", + .ident = "Dell OptiPlex 360", .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 360"), + DMI_MATCH(DMI_BOARD_NAME, "0T656F"), }, }, - { /* Handle problems with rebooting on Dell XPS710 */ + { /* Handle problems with rebooting on Dell Optiplex 745's SFF */ .callback = set_bios_reboot, - .ident = "Dell XPS710", + .ident = "Dell OptiPlex 745", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Dell XPS710"), + DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 745"), }, }, - { /* Handle problems with rebooting on Dell DXP061 */ + { /* Handle problems with rebooting on Dell Optiplex 745's DFF */ .callback = set_bios_reboot, - .ident = "Dell DXP061", + .ident = "Dell OptiPlex 745", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Dell DXP061"), + DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 745"), + DMI_MATCH(DMI_BOARD_NAME, "0MM599"), }, }, - { /* Handle problems with rebooting on Sony VGN-Z540N */ + { /* Handle problems with rebooting on Dell Optiplex 745 with 0KW626 */ .callback = set_bios_reboot, - .ident = "Sony VGN-Z540N", + .ident = "Dell OptiPlex 745", .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "VGN-Z540N"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 745"), + DMI_MATCH(DMI_BOARD_NAME, "0KW626"), }, }, - { /* Handle problems with rebooting on ASUS P4S800 */ + { /* Handle problems with rebooting on Dell OptiPlex 760 with 0G919G */ .callback = set_bios_reboot, - .ident = "ASUS P4S800", + .ident = "Dell OptiPlex 760", .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), - DMI_MATCH(DMI_BOARD_NAME, "P4S800"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 760"), + DMI_MATCH(DMI_BOARD_NAME, "0G919G"), }, }, - - { /* Handle reboot issue on Acer Aspire one */ - .callback = set_kbd_reboot, - .ident = "Acer Aspire One A110", + { /* Handle problems with rebooting on the OptiPlex 990. */ + .callback = set_pci_reboot, + .ident = "Dell OptiPlex 990", .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "AOA110"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 990"), }, }, - { /* Handle problems with rebooting on Apple MacBook5 */ - .callback = set_pci_reboot, - .ident = "Apple MacBook5", + { /* Handle problems with rebooting on Dell 300's */ + .callback = set_bios_reboot, + .ident = "Dell PowerEdge 300", .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 300/"), }, }, - { /* Handle problems with rebooting on Apple MacBookPro5 */ - .callback = set_pci_reboot, - .ident = "Apple MacBookPro5", + { /* Handle problems with rebooting on Dell 1300's */ + .callback = set_bios_reboot, + .ident = "Dell PowerEdge 1300", .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 1300/"), }, }, - { /* Handle problems with rebooting on Apple Macmini3,1 */ - .callback = set_pci_reboot, - .ident = "Apple Macmini3,1", + { /* Handle problems with rebooting on Dell 2400's */ + .callback = set_bios_reboot, + .ident = "Dell PowerEdge 2400", .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Macmini3,1"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 2400"), }, }, - { /* Handle problems with rebooting on the iMac9,1. */ + { /* Handle problems with rebooting on the Dell PowerEdge C6100. */ .callback = set_pci_reboot, - .ident = "Apple iMac9,1", + .ident = "Dell PowerEdge C6100", .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "iMac9,1"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell"), + DMI_MATCH(DMI_PRODUCT_NAME, "C6100"), }, }, - { /* Handle problems with rebooting on the Latitude E6320. */ + { /* Handle problems with rebooting on the Precision M6600. */ .callback = set_pci_reboot, - .ident = "Dell Latitude E6320", + .ident = "Dell Precision M6600", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E6320"), + DMI_MATCH(DMI_PRODUCT_NAME, "Precision M6600"), }, }, - { /* Handle problems with rebooting on the Latitude E5420. */ - .callback = set_pci_reboot, - .ident = "Dell Latitude E5420", + { /* Handle problems with rebooting on Dell T5400's */ + .callback = set_bios_reboot, + .ident = "Dell Precision T5400", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E5420"), + DMI_MATCH(DMI_PRODUCT_NAME, "Precision WorkStation T5400"), }, }, - { /* Handle problems with rebooting on the Latitude E6420. */ - .callback = set_pci_reboot, - .ident = "Dell Latitude E6420", + { /* Handle problems with rebooting on Dell T7400's */ + .callback = set_bios_reboot, + .ident = "Dell Precision T7400", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E6420"), + DMI_MATCH(DMI_PRODUCT_NAME, "Precision WorkStation T7400"), }, }, - { /* Handle problems with rebooting on the OptiPlex 990. */ - .callback = set_pci_reboot, - .ident = "Dell OptiPlex 990", + { /* Handle problems with rebooting on Dell XPS710 */ + .callback = set_bios_reboot, + .ident = "Dell XPS710", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 990"), + DMI_MATCH(DMI_PRODUCT_NAME, "Dell XPS710"), }, }, - { /* Handle problems with rebooting on the Precision M6600. */ - .callback = set_pci_reboot, - .ident = "Dell Precision M6600", + + /* Hewlett-Packard */ + { /* Handle problems with rebooting on HP laptops */ + .callback = set_bios_reboot, + .ident = "HP Compaq Laptop", .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Precision M6600"), + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq"), }, }, - { /* Handle problems with rebooting on the Dell PowerEdge C6100. */ - .callback = set_pci_reboot, - .ident = "Dell PowerEdge C6100", + + /* Sony */ + { /* Handle problems with rebooting on Sony VGN-Z540N */ + .callback = set_bios_reboot, + .ident = "Sony VGN-Z540N", .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell"), - DMI_MATCH(DMI_PRODUCT_NAME, "C6100"), + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-Z540N"), }, }, + { } }; -- cgit v0.10.2 From 92947789815f9ec808ad3de177af99a5d02ef6b2 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Thu, 26 Sep 2013 19:18:15 +0530 Subject: clk: wm831x: get rid of the implementation of remove function The remove function implemented for platform driver's remove callback just return 0 as part of its implementation. Remove this APIs and do not pass the valid .remove for platform driver. Signed-off-by: Laxman Dewangan Acked-by: Mark Brown Signed-off-by: Mike Turquette diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c index 805b4c3..b131041 100644 --- a/drivers/clk/clk-wm831x.c +++ b/drivers/clk/clk-wm831x.c @@ -391,14 +391,8 @@ static int wm831x_clk_probe(struct platform_device *pdev) return 0; } -static int wm831x_clk_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver wm831x_clk_driver = { .probe = wm831x_clk_probe, - .remove = wm831x_clk_remove, .driver = { .name = "wm831x-clk", .owner = THIS_MODULE, -- cgit v0.10.2 From 3713c0cfd06fa49729a12929a7ee8b7ad48f3c02 Mon Sep 17 00:00:00 2001 From: Soren Brinkmann Date: Wed, 18 Sep 2013 11:48:35 -0700 Subject: clockchips: Add FEAT_PERCPU clockevent flag Add the flag CLOCK_EVT_FEAT_PERCPU which is supposed to be set for per cpu clockevent devices. Signed-off-by: Soren Brinkmann Signed-off-by: Daniel Lezcano Acked-by: Michal Simek diff --git a/include/linux/clockchips.h b/include/linux/clockchips.h index 0857922..493aa02 100644 --- a/include/linux/clockchips.h +++ b/include/linux/clockchips.h @@ -60,6 +60,7 @@ enum clock_event_mode { * Core shall set the interrupt affinity dynamically in broadcast mode */ #define CLOCK_EVT_FEAT_DYNIRQ 0x000020 +#define CLOCK_EVT_FEAT_PERCPU 0x000040 /** * struct clock_event_device - clock event device descriptor -- cgit v0.10.2 From 6661039dc906bce5d532477f26c7c965f25e5d02 Mon Sep 17 00:00:00 2001 From: Soren Brinkmann Date: Wed, 18 Sep 2013 11:48:36 -0700 Subject: clocksource/arm_global_timer: Set FEAT_PERCPU flag The arm_global_timer is a per cpu device. Set the appropriate flag. Signed-off-by: Soren Brinkmann Signed-off-by: Daniel Lezcano Acked-by: Michal Simek Acked-by: Srinivas Kandagatla diff --git a/drivers/clocksource/arm_global_timer.c b/drivers/clocksource/arm_global_timer.c index b66c1f3..c639b1a 100644 --- a/drivers/clocksource/arm_global_timer.c +++ b/drivers/clocksource/arm_global_timer.c @@ -169,7 +169,8 @@ static int gt_clockevents_init(struct clock_event_device *clk) int cpu = smp_processor_id(); clk->name = "arm_global_timer"; - clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; + clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | + CLOCK_EVT_FEAT_PERCPU; clk->set_mode = gt_clockevent_set_mode; clk->set_next_event = gt_clockevent_set_next_event; clk->cpumask = cpumask_of(cpu); -- cgit v0.10.2 From 245a34962661cd2ce7b4dd6c4aa65d870a589c50 Mon Sep 17 00:00:00 2001 From: Soren Brinkmann Date: Wed, 18 Sep 2013 11:48:37 -0700 Subject: tick: broadcast: Deny per-cpu clockevents from being broadcast sources On most ARM systems the per-cpu clockevents are truly per-cpu in the sense that they can't be controlled on any other CPU besides the CPU that they interrupt. If one of these clockevents were to become a broadcast source we will run into a lot of trouble because the broadcast source is enabled on the first CPU to go into deep idle (if that CPU suffers from FEAT_C3_STOP) and that could be a different CPU than what the clockevent is interrupting (or even worse the CPU that the clockevent interrupts could be offline). Theoretically it's possible to support per-cpu clockevents as the broadcast source but so far we haven't needed this and supporting it is rather complicated. Let's just deny the possibility for now until this becomes a reality (let's hope it never does!). Signed-off-by: Soren Brinkmann Signed-off-by: Daniel Lezcano Acked-by: Michal Simek diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index 218bcb5..9532690 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -70,6 +70,7 @@ static bool tick_check_broadcast_device(struct clock_event_device *curdev, struct clock_event_device *newdev) { if ((newdev->features & CLOCK_EVT_FEAT_DUMMY) || + (newdev->features & CLOCK_EVT_FEAT_PERCPU) || (newdev->features & CLOCK_EVT_FEAT_C3STOP)) return false; -- cgit v0.10.2 From fa94bd57b5a5b2206e5fdd0ed2dbacff199121f2 Mon Sep 17 00:00:00 2001 From: Soren Brinkmann Date: Wed, 18 Sep 2013 11:48:38 -0700 Subject: arm: zynq: Enable arm_global_timer Zynq is based on an ARM Cortex-A9 MPCore, which features the arm_global_timer in its SCU. Therefore enable the timer for Zynq. Signed-off-by: Soren Brinkmann Acked-by: Daniel Lezcano Signed-off-by: Daniel Lezcano Acked-by: Michal Simek diff --git a/arch/arm/boot/dts/zynq-7000.dtsi b/arch/arm/boot/dts/zynq-7000.dtsi index e32b92b..e7f73b2 100644 --- a/arch/arm/boot/dts/zynq-7000.dtsi +++ b/arch/arm/boot/dts/zynq-7000.dtsi @@ -92,6 +92,14 @@ }; }; + global_timer: timer@f8f00200 { + compatible = "arm,cortex-a9-global-timer"; + reg = <0xf8f00200 0x20>; + interrupts = <1 11 0x301>; + interrupt-parent = <&intc>; + clocks = <&clkc 4>; + }; + ttc0: ttc0@f8001000 { interrupt-parent = <&intc>; interrupts = < 0 10 4 0 11 4 0 12 4 >; diff --git a/arch/arm/mach-zynq/Kconfig b/arch/arm/mach-zynq/Kconfig index 04f8a4a..6b04260 100644 --- a/arch/arm/mach-zynq/Kconfig +++ b/arch/arm/mach-zynq/Kconfig @@ -13,5 +13,6 @@ config ARCH_ZYNQ select HAVE_SMP select SPARSE_IRQ select CADENCE_TTC_TIMER + select ARM_GLOBAL_TIMER help Support for Xilinx Zynq ARM Cortex A9 Platform -- cgit v0.10.2 From 326e31eebe61dc838e031ea16968b2cfb43443e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 1 Oct 2013 11:00:53 +0200 Subject: clocksource: Put nodes passed to CLOCKSOURCE_OF_DECLARE callbacks centrally MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of letting each driver call of_node_put do it centrally in the loop that also calls the CLOCKSOURCE_OF_DECLARE callbacks. This is less prone to error and also moves getting and putting the references into the same function. Consequently all respective of_node_put calls in drivers are removed. Signed-off-by: Uwe Kleine-König Signed-off-by: Daniel Lezcano Acked-by: David Brown diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c index 696fb73..1e9c338 100644 --- a/arch/arm/mach-msm/timer.c +++ b/arch/arm/mach-msm/timer.c @@ -274,7 +274,6 @@ static void __init msm_dt_timer_init(struct device_node *np) pr_err("Unknown frequency\n"); return; } - of_node_put(np); event_base = base + 0x4; sts_base = base + 0x88; diff --git a/drivers/clocksource/clksrc-of.c b/drivers/clocksource/clksrc-of.c index 37f5325..8b2ed14 100644 --- a/drivers/clocksource/clksrc-of.c +++ b/drivers/clocksource/clksrc-of.c @@ -32,5 +32,6 @@ void __init clocksource_of_init(void) for_each_matching_node_and_match(np, __clksrc_of_table, &match) { init_func = match->data; init_func(np); + of_node_put(np); } } diff --git a/drivers/clocksource/dw_apb_timer_of.c b/drivers/clocksource/dw_apb_timer_of.c index 003b230..482618b 100644 --- a/drivers/clocksource/dw_apb_timer_of.c +++ b/drivers/clocksource/dw_apb_timer_of.c @@ -138,12 +138,10 @@ static void __init dw_apb_timer_init(struct device_node *timer) case 0: pr_debug("%s: found clockevent timer\n", __func__); add_clockevent(timer); - of_node_put(timer); break; case 1: pr_debug("%s: found clocksource timer\n", __func__); add_clocksource(timer); - of_node_put(timer); init_sched_clock(); break; default: diff --git a/drivers/clocksource/tegra20_timer.c b/drivers/clocksource/tegra20_timer.c index 5cff616..6428492 100644 --- a/drivers/clocksource/tegra20_timer.c +++ b/drivers/clocksource/tegra20_timer.c @@ -181,8 +181,6 @@ static void __init tegra20_init_timer(struct device_node *np) rate = clk_get_rate(clk); } - of_node_put(np); - switch (rate) { case 12000000: timer_writel(0x000b, TIMERUS_USEC_CFG); @@ -241,8 +239,6 @@ static void __init tegra20_init_rtc(struct device_node *np) else clk_prepare_enable(clk); - of_node_put(np); - register_persistent_clock(NULL, tegra_read_persistent_clock); } CLOCKSOURCE_OF_DECLARE(tegra20_rtc, "nvidia,tegra20-rtc", tegra20_init_rtc); diff --git a/drivers/clocksource/vt8500_timer.c b/drivers/clocksource/vt8500_timer.c index 64f553f..ad3c0e8 100644 --- a/drivers/clocksource/vt8500_timer.c +++ b/drivers/clocksource/vt8500_timer.c @@ -137,14 +137,12 @@ static void __init vt8500_timer_init(struct device_node *np) if (!regbase) { pr_err("%s: Missing iobase description in Device Tree\n", __func__); - of_node_put(np); return; } timer_irq = irq_of_parse_and_map(np, 0); if (!timer_irq) { pr_err("%s: Missing irq description in Device Tree\n", __func__); - of_node_put(np); return; } -- cgit v0.10.2 From 1cf0203ac9e3d7abed67196db494469b24fe09e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 1 Oct 2013 10:38:12 +0200 Subject: clocksource: dw_apb_timer_of: Mark a few more functions as __init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are all only called by dw_apb_timer_init which is an __init function, too Signed-off-by: Uwe Kleine-König Signed-off-by: Daniel Lezcano diff --git a/drivers/clocksource/dw_apb_timer_of.c b/drivers/clocksource/dw_apb_timer_of.c index 482618b..45ba8ae 100644 --- a/drivers/clocksource/dw_apb_timer_of.c +++ b/drivers/clocksource/dw_apb_timer_of.c @@ -23,7 +23,7 @@ #include #include -static void timer_get_base_and_rate(struct device_node *np, +static void __init timer_get_base_and_rate(struct device_node *np, void __iomem **base, u32 *rate) { struct clk *timer_clk; @@ -55,11 +55,11 @@ static void timer_get_base_and_rate(struct device_node *np, try_clock_freq: if (of_property_read_u32(np, "clock-freq", rate) && - of_property_read_u32(np, "clock-frequency", rate)) + of_property_read_u32(np, "clock-frequency", rate)) panic("No clock nor clock-frequency property for %s", np->name); } -static void add_clockevent(struct device_node *event_timer) +static void __init add_clockevent(struct device_node *event_timer) { void __iomem *iobase; struct dw_apb_clock_event_device *ced; @@ -82,7 +82,7 @@ static void add_clockevent(struct device_node *event_timer) static void __iomem *sched_io_base; static u32 sched_rate; -static void add_clocksource(struct device_node *source_timer) +static void __init add_clocksource(struct device_node *source_timer) { void __iomem *iobase; struct dw_apb_clocksource *cs; @@ -117,7 +117,7 @@ static const struct of_device_id sptimer_ids[] __initconst = { { /* Sentinel */ }, }; -static void init_sched_clock(void) +static void __init init_sched_clock(void) { struct device_node *sched_timer; -- cgit v0.10.2 From 4fbcdc813fb9c0324fcff4c75414e717569d965e Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 27 Sep 2013 13:13:12 -0700 Subject: clocksource: arm_arch_timer: Use clocksource for suspend timekeeping The ARM architected timers keep counting during suspend so we can mark this clocksource with the CLOCK_SOURCE_SUSPEND_NONSTOP flag. This flag will indicate that this clocksource can be used for calculating suspend time and injecting sleep time into the timekeeping core. This should be more accurate than using an external RTC or architecture specific persistent clock. Cc: Mark Rutland Signed-off-by: Stephen Boyd Signed-off-by: Daniel Lezcano diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index fbd9ccd..ce98d5e 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -389,7 +389,7 @@ static struct clocksource clocksource_counter = { .rating = 400, .read = arch_counter_read, .mask = CLOCKSOURCE_MASK(56), - .flags = CLOCK_SOURCE_IS_CONTINUOUS, + .flags = CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_SUSPEND_NONSTOP, }; static struct cyclecounter cyclecounter = { -- cgit v0.10.2 From 83924fb30038a6b425fe9d9d499e7d8b79d31d21 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sat, 28 Sep 2013 17:34:07 +0530 Subject: gpio: adnp: Remove redundant of_match_ptr The data structure of_match_ptr() protects is always compiled in. Hence of_match_ptr() is not needed. Signed-off-by: Sachin Kamat Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c index c0f3fc4..6439e0e 100644 --- a/drivers/gpio/gpio-adnp.c +++ b/drivers/gpio/gpio-adnp.c @@ -594,7 +594,7 @@ static struct i2c_driver adnp_i2c_driver = { .driver = { .name = "gpio-adnp", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(adnp_of_match), + .of_match_table = adnp_of_match, }, .probe = adnp_i2c_probe, .remove = adnp_i2c_remove, -- cgit v0.10.2 From 8675de924064481c15c895cba6f887d2b160613a Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sat, 28 Sep 2013 17:34:08 +0530 Subject: gpio: clps711x: Remove redundant of_match_ptr The data structure of_match_ptr() protects is always compiled in. Hence of_match_ptr() is not needed. Signed-off-by: Sachin Kamat Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-clps711x.c b/drivers/gpio/gpio-clps711x.c index 0edaf2c..0924f20 100644 --- a/drivers/gpio/gpio-clps711x.c +++ b/drivers/gpio/gpio-clps711x.c @@ -87,7 +87,7 @@ static struct platform_driver clps711x_gpio_driver = { .driver = { .name = "clps711x-gpio", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(clps711x_gpio_ids), + .of_match_table = clps711x_gpio_ids, }, .probe = clps711x_gpio_probe, .remove = clps711x_gpio_remove, -- cgit v0.10.2 From e5560af49b2d0148c293cffaa53823227b0585b3 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sat, 28 Sep 2013 17:34:09 +0530 Subject: gpio: twl4030: Remove redundant of_match_ptr The data structure of_match_ptr() protects is always compiled in. Hence of_match_ptr() is not needed. Signed-off-by: Sachin Kamat Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index d8e4f6e..0c7e891 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -594,7 +594,7 @@ static struct platform_driver gpio_twl4030_driver = { .driver = { .name = "twl4030_gpio", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(twl_gpio_match), + .of_match_table = twl_gpio_match, }, .probe = gpio_twl4030_probe, .remove = gpio_twl4030_remove, -- cgit v0.10.2 From 4bbd6f2e4902b18fcf5861d36588729c93ecda40 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Sun, 29 Sep 2013 21:00:37 +0100 Subject: gpio: arizona: Use the of_node from the Arizona device We need to use the of_node from the main Arizona device as that holds our configuration. Signed-off-by: Charles Keepax Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-arizona.c b/drivers/gpio/gpio-arizona.c index a2bec3c..dceb5dc 100644 --- a/drivers/gpio/gpio-arizona.c +++ b/drivers/gpio/gpio-arizona.c @@ -109,6 +109,9 @@ static int arizona_gpio_probe(struct platform_device *pdev) arizona_gpio->arizona = arizona; arizona_gpio->gpio_chip = template_chip; arizona_gpio->gpio_chip.dev = &pdev->dev; +#ifdef CONFIG_OF_GPIO + arizona_gpio->gpio_chip.of_node = arizona->dev->of_node; +#endif switch (arizona->type) { case WM5102: -- cgit v0.10.2 From 05071aa864e84000759191438a4a9ff7ba2c360e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 27 Sep 2013 16:34:27 +0200 Subject: spi: Add a spi_w8r16be() helper This patch adds a new spi_w8r16be() helper, which is similar to spi_w8r16() except that it converts the read data word from big endian to native endianness before returning it. The reason for introducing this new helper is that for SPI slave devices it is quite common that the read 16 bit data word is in big endian. So users of spi_w8r16() have to convert the result to native endianness manually. A second reason is that in this case the endianness of the return value of spi_w8r16() depends on its sign. If it is negative (i.e. a error code) it is already in native endianness, if it is positive it is in big endian. The sparse code checker doesn't like this kind of mixed endianness and special annotations are necessary to keep it quiet (E.g. casting to be16 using __force). Doing the conversion to native endianness in the helper function does not require such annotations since we are not mixing different endiannesses in the same variable. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 887116d..0e0aebd 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -823,6 +823,33 @@ static inline ssize_t spi_w8r16(struct spi_device *spi, u8 cmd) return (status < 0) ? status : result; } +/** + * spi_w8r16be - SPI synchronous 8 bit write followed by 16 bit big-endian read + * @spi: device with which data will be exchanged + * @cmd: command to be written before data is read back + * Context: can sleep + * + * This returns the (unsigned) sixteen bit number returned by the device in cpu + * endianness, or else a negative error code. Callable only from contexts that + * can sleep. + * + * This function is similar to spi_w8r16, with the exception that it will + * convert the read 16 bit data word from big-endian to native endianness. + * + */ +static inline ssize_t spi_w8r16be(struct spi_device *spi, u8 cmd) + +{ + ssize_t status; + __be16 result; + + status = spi_write_then_read(spi, &cmd, 1, &result, 2); + if (status < 0) + return status; + + return be16_to_cpu(result); +} + /*---------------------------------------------------------------------------*/ /* -- cgit v0.10.2 From 600ba98bff438510dc81fc1ba9048420479bbded Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 27 Sep 2013 16:34:28 +0200 Subject: hwmon: (adt7310) Use spi_w8r16be() instead spi_w8r16() Using spi_w8r16be() instead of spi_w8r16() in this driver makes a code a bit shorter and cleaner. Signed-off-by: Lars-Peter Clausen Acked-by: Guenter Roeck Signed-off-by: Mark Brown diff --git a/drivers/hwmon/adt7310.c b/drivers/hwmon/adt7310.c index da5f078..5994cf6 100644 --- a/drivers/hwmon/adt7310.c +++ b/drivers/hwmon/adt7310.c @@ -42,13 +42,8 @@ static const u8 adt7310_reg_table[] = { static int adt7310_spi_read_word(struct device *dev, u8 reg) { struct spi_device *spi = to_spi_device(dev); - int ret; - ret = spi_w8r16(spi, AD7310_COMMAND(reg) | ADT7310_CMD_READ); - if (ret < 0) - return ret; - - return be16_to_cpu((__force __be16)ret); + return spi_w8r16be(spi, AD7310_COMMAND(reg) | ADT7310_CMD_READ); } static int adt7310_spi_write_word(struct device *dev, u8 reg, u16 data) -- cgit v0.10.2 From 235907422548aae38d03a545f20fceb728b70ddd Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 27 Sep 2013 16:34:29 +0200 Subject: staging:iio:ade7753/ade7754/ade7759: Use spi_w8r16be() instead of spi_w8r16() Using spi_w8r16be() will do the conversion of the result from big endian to native endian in the helper function. This makes the code a bit smaller and also keeps sparse happy. Fixes the following sparse warnings: drivers/staging/iio/meter/ade7753.c:97:29: warning: incorrect type in argument 1 (different base types) drivers/staging/iio/meter/ade7753.c:97:29: expected restricted __be16 const [usertype] *p drivers/staging/iio/meter/ade7753.c:97:29: got unsigned short [usertype] *val drivers/staging/iio/meter/ade7754.c:97:29: warning: incorrect type in argument 1 (different base types) drivers/staging/iio/meter/ade7754.c:97:29: expected restricted __be16 const [usertype] *p drivers/staging/iio/meter/ade7754.c:97:29: got unsigned short [usertype] *val drivers/staging/iio/meter/ade7759.c:97:29: warning: incorrect type in argument 1 (different base types) drivers/staging/iio/meter/ade7759.c:97:29: expected restricted __be16 const [usertype] *p drivers/staging/iio/meter/ade7759.c:97:29: got unsigned short [usertype] *val Signed-off-by: Lars-Peter Clausen Acked-by: Jonathan Cameron Signed-off-by: Mark Brown diff --git a/drivers/staging/iio/meter/ade7753.c b/drivers/staging/iio/meter/ade7753.c index 74025fb..abfc8bd 100644 --- a/drivers/staging/iio/meter/ade7753.c +++ b/drivers/staging/iio/meter/ade7753.c @@ -86,7 +86,7 @@ static int ade7753_spi_read_reg_16(struct device *dev, struct ade7753_state *st = iio_priv(indio_dev); ssize_t ret; - ret = spi_w8r16(st->us, ADE7753_READ_REG(reg_address)); + ret = spi_w8r16be(st->us, ADE7753_READ_REG(reg_address)); if (ret < 0) { dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X", reg_address); @@ -94,7 +94,6 @@ static int ade7753_spi_read_reg_16(struct device *dev, } *val = ret; - *val = be16_to_cpup(val); return 0; } diff --git a/drivers/staging/iio/meter/ade7754.c b/drivers/staging/iio/meter/ade7754.c index f649ebe..3d1c02c 100644 --- a/drivers/staging/iio/meter/ade7754.c +++ b/drivers/staging/iio/meter/ade7754.c @@ -86,7 +86,7 @@ static int ade7754_spi_read_reg_16(struct device *dev, struct ade7754_state *st = iio_priv(indio_dev); int ret; - ret = spi_w8r16(st->us, ADE7754_READ_REG(reg_address)); + ret = spi_w8r16be(st->us, ADE7754_READ_REG(reg_address)); if (ret < 0) { dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X", reg_address); @@ -94,7 +94,6 @@ static int ade7754_spi_read_reg_16(struct device *dev, } *val = ret; - *val = be16_to_cpup(val); return 0; } diff --git a/drivers/staging/iio/meter/ade7759.c b/drivers/staging/iio/meter/ade7759.c index d214ac4..7467e51 100644 --- a/drivers/staging/iio/meter/ade7759.c +++ b/drivers/staging/iio/meter/ade7759.c @@ -86,7 +86,7 @@ static int ade7759_spi_read_reg_16(struct device *dev, struct ade7759_state *st = iio_priv(indio_dev); int ret; - ret = spi_w8r16(st->us, ADE7759_READ_REG(reg_address)); + ret = spi_w8r16be(st->us, ADE7759_READ_REG(reg_address)); if (ret < 0) { dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X", reg_address); @@ -94,7 +94,6 @@ static int ade7759_spi_read_reg_16(struct device *dev, } *val = ret; - *val = be16_to_cpup(val); return 0; } -- cgit v0.10.2 From 648c538204c23370c734d72921155cc24aff928d Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Tue, 1 Oct 2013 14:48:24 +0200 Subject: ASoC: tas5086: move two variables into private struct We need to access the charge_period and start_mid_z values from other places later, so move them to the private struct. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c index 6d31d88..31b5868 100644 --- a/sound/soc/codecs/tas5086.c +++ b/sound/soc/codecs/tas5086.c @@ -244,6 +244,8 @@ struct tas5086_private { unsigned int mclk, sclk; unsigned int format; bool deemph; + unsigned int charge_period; + unsigned int pwm_start_mid_z; /* Current sample rate for de-emphasis control */ int rate; /* GPIO driving Reset pin, if any */ @@ -720,13 +722,15 @@ static const int tas5086_charge_period[] = { static int tas5086_probe(struct snd_soc_codec *codec) { struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec); - int charge_period = 1300000; /* hardware default is 1300 ms */ - u8 pwm_start_mid_z = 0; int i, ret; + priv->pwm_start_mid_z = 0; + priv->charge_period = 1300000; /* hardware default is 1300 ms */ + if (of_match_device(of_match_ptr(tas5086_dt_ids), codec->dev)) { struct device_node *of_node = codec->dev->of_node; - of_property_read_u32(of_node, "ti,charge-period", &charge_period); + of_property_read_u32(of_node, "ti,charge-period", + &priv->charge_period); for (i = 0; i < 6; i++) { char name[25]; @@ -735,7 +739,7 @@ static int tas5086_probe(struct snd_soc_codec *codec) "ti,mid-z-channel-%d", i + 1); if (of_get_property(of_node, name, NULL) != NULL) - pwm_start_mid_z |= 1 << i; + priv->pwm_start_mid_z |= 1 << i; } } @@ -744,25 +748,25 @@ static int tas5086_probe(struct snd_soc_codec *codec) * configure 'part 1' of the PWM starts to use Mid-Z, and tell * all configured mid-z channels to start start under 'part 1'. */ - if (pwm_start_mid_z) + if (priv->pwm_start_mid_z) regmap_write(priv->regmap, TAS5086_PWM_START, TAS5086_PWM_START_MIDZ_FOR_START_1 | - pwm_start_mid_z); + priv->pwm_start_mid_z); /* lookup and set split-capacitor charge period */ - if (charge_period == 0) { + if (priv->charge_period == 0) { regmap_write(priv->regmap, TAS5086_SPLIT_CAP_CHARGE, 0); } else { i = index_in_array(tas5086_charge_period, ARRAY_SIZE(tas5086_charge_period), - charge_period); + priv->charge_period); if (i >= 0) regmap_write(priv->regmap, TAS5086_SPLIT_CAP_CHARGE, i + 0x08); else dev_warn(codec->dev, "Invalid split-cap charge period of %d ns.\n", - charge_period); + priv->charge_period); } /* enable factory trim */ -- cgit v0.10.2 From d5fd3ccc2d9df493ad6f1eaf7aba72f690e98937 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Tue, 1 Oct 2013 14:48:25 +0200 Subject: ASoC: tas5086: move initialization code to own functions We'll need to call code to initialize and reset the codec again at resume time, so factor it out first. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c index 31b5868..3a88c68 100644 --- a/sound/soc/codecs/tas5086.c +++ b/sound/soc/codecs/tas5086.c @@ -458,6 +458,75 @@ static int tas5086_mute_stream(struct snd_soc_dai *dai, int mute, int stream) return regmap_write(priv->regmap, TAS5086_SOFT_MUTE, val); } +static void tas5086_reset(struct tas5086_private *priv) +{ + if (gpio_is_valid(priv->gpio_nreset)) { + /* Reset codec - minimum assertion time is 400ns */ + gpio_direction_output(priv->gpio_nreset, 0); + udelay(1); + gpio_set_value(priv->gpio_nreset, 1); + + /* Codec needs ~15ms to wake up */ + msleep(15); + } +} + +/* charge period values in microseconds */ +static const int tas5086_charge_period[] = { + 13000, 16900, 23400, 31200, 41600, 54600, 72800, 96200, + 130000, 156000, 234000, 312000, 416000, 546000, 728000, 962000, + 1300000, 169000, 2340000, 3120000, 4160000, 5460000, 7280000, 9620000, +}; + +static int tas5086_init(struct device *dev, struct tas5086_private *priv) +{ + int ret, i; + + /* + * If any of the channels is configured to start in Mid-Z mode, + * configure 'part 1' of the PWM starts to use Mid-Z, and tell + * all configured mid-z channels to start start under 'part 1'. + */ + if (priv->pwm_start_mid_z) + regmap_write(priv->regmap, TAS5086_PWM_START, + TAS5086_PWM_START_MIDZ_FOR_START_1 | + priv->pwm_start_mid_z); + + /* lookup and set split-capacitor charge period */ + if (priv->charge_period == 0) { + regmap_write(priv->regmap, TAS5086_SPLIT_CAP_CHARGE, 0); + } else { + i = index_in_array(tas5086_charge_period, + ARRAY_SIZE(tas5086_charge_period), + priv->charge_period); + if (i >= 0) + regmap_write(priv->regmap, TAS5086_SPLIT_CAP_CHARGE, + i + 0x08); + else + dev_warn(dev, + "Invalid split-cap charge period of %d ns.\n", + priv->charge_period); + } + + /* enable factory trim */ + ret = regmap_write(priv->regmap, TAS5086_OSC_TRIM, 0x00); + if (ret < 0) + return ret; + + /* start all channels */ + ret = regmap_write(priv->regmap, TAS5086_SYS_CONTROL_2, 0x20); + if (ret < 0) + return ret; + + /* mute all channels for now */ + ret = regmap_write(priv->regmap, TAS5086_SOFT_MUTE, + TAS5086_SOFT_MUTE_ALL); + if (ret < 0) + return ret; + + return 0; +} + /* TAS5086 controls */ static const DECLARE_TLV_DB_SCALE(tas5086_dac_tlv, -10350, 50, 1); @@ -712,13 +781,6 @@ static const struct of_device_id tas5086_dt_ids[] = { MODULE_DEVICE_TABLE(of, tas5086_dt_ids); #endif -/* charge period values in microseconds */ -static const int tas5086_charge_period[] = { - 13000, 16900, 23400, 31200, 41600, 54600, 72800, 96200, - 130000, 156000, 234000, 312000, 416000, 546000, 728000, 962000, - 1300000, 169000, 2340000, 3120000, 4160000, 5460000, 7280000, 9620000, -}; - static int tas5086_probe(struct snd_soc_codec *codec) { struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec); @@ -729,6 +791,7 @@ static int tas5086_probe(struct snd_soc_codec *codec) if (of_match_device(of_match_ptr(tas5086_dt_ids), codec->dev)) { struct device_node *of_node = codec->dev->of_node; + of_property_read_u32(of_node, "ti,charge-period", &priv->charge_period); @@ -743,39 +806,7 @@ static int tas5086_probe(struct snd_soc_codec *codec) } } - /* - * If any of the channels is configured to start in Mid-Z mode, - * configure 'part 1' of the PWM starts to use Mid-Z, and tell - * all configured mid-z channels to start start under 'part 1'. - */ - if (priv->pwm_start_mid_z) - regmap_write(priv->regmap, TAS5086_PWM_START, - TAS5086_PWM_START_MIDZ_FOR_START_1 | - priv->pwm_start_mid_z); - - /* lookup and set split-capacitor charge period */ - if (priv->charge_period == 0) { - regmap_write(priv->regmap, TAS5086_SPLIT_CAP_CHARGE, 0); - } else { - i = index_in_array(tas5086_charge_period, - ARRAY_SIZE(tas5086_charge_period), - priv->charge_period); - if (i >= 0) - regmap_write(priv->regmap, TAS5086_SPLIT_CAP_CHARGE, - i + 0x08); - else - dev_warn(codec->dev, - "Invalid split-cap charge period of %d ns.\n", - priv->charge_period); - } - - /* enable factory trim */ - ret = regmap_write(priv->regmap, TAS5086_OSC_TRIM, 0x00); - if (ret < 0) - return ret; - - /* start all channels */ - ret = regmap_write(priv->regmap, TAS5086_SYS_CONTROL_2, 0x20); + ret = tas5086_init(codec->dev, priv); if (ret < 0) return ret; @@ -784,12 +815,6 @@ static int tas5086_probe(struct snd_soc_codec *codec) if (ret < 0) return ret; - /* mute all channels for now */ - ret = regmap_write(priv->regmap, TAS5086_SOFT_MUTE, - TAS5086_SOFT_MUTE_ALL); - if (ret < 0) - return ret; - return 0; } @@ -866,17 +891,8 @@ static int tas5086_i2c_probe(struct i2c_client *i2c, if (devm_gpio_request(dev, gpio_nreset, "TAS5086 Reset")) gpio_nreset = -EINVAL; - if (gpio_is_valid(gpio_nreset)) { - /* Reset codec - minimum assertion time is 400ns */ - gpio_direction_output(gpio_nreset, 0); - udelay(1); - gpio_set_value(gpio_nreset, 1); - - /* Codec needs ~15ms to wake up */ - msleep(15); - } - priv->gpio_nreset = gpio_nreset; + tas5086_reset(priv); /* The TAS5086 always returns 0x03 in its TAS5086_DEV_ID register */ ret = regmap_read(priv->regmap, TAS5086_DEV_ID, &i); -- cgit v0.10.2 From 25c84cc1ace56421fa9a676a387a1919e7bc4e62 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Tue, 1 Oct 2013 14:48:26 +0200 Subject: ASoC: tas5086: add suspend callback When going to suspend, shut down all channels and re-do the init procedure at resume time. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c index 3a88c68..2996d2e 100644 --- a/sound/soc/codecs/tas5086.c +++ b/sound/soc/codecs/tas5086.c @@ -762,14 +762,39 @@ static struct snd_soc_dai_driver tas5086_dai = { }; #ifdef CONFIG_PM +static int tas5086_soc_suspend(struct snd_soc_codec *codec) +{ + struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec); + int ret; + + /* Shut down all channels */ + ret = regmap_write(priv->regmap, TAS5086_SYS_CONTROL_2, 0x60); + if (ret < 0) + return ret; + + return 0; +} + static int tas5086_soc_resume(struct snd_soc_codec *codec) { struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec); + int ret; - /* Restore codec state */ - return regcache_sync(priv->regmap); + tas5086_reset(priv); + regcache_mark_dirty(priv->regmap); + + ret = tas5086_init(codec->dev, priv); + if (ret < 0) + return ret; + + ret = regcache_sync(priv->regmap); + if (ret < 0) + return ret; + + return 0; } #else +#define tas5086_soc_suspend NULL #define tas5086_soc_resume NULL #endif /* CONFIG_PM */ @@ -832,6 +857,7 @@ static int tas5086_remove(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_dev_tas5086 = { .probe = tas5086_probe, .remove = tas5086_remove, + .suspend = tas5086_soc_suspend, .resume = tas5086_soc_resume, .controls = tas5086_controls, .num_controls = ARRAY_SIZE(tas5086_controls), -- cgit v0.10.2 From a85e419edee73ec458354388e1ba9b8b58bdcbba Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Tue, 1 Oct 2013 14:50:02 +0200 Subject: ASoC: davinci-mcasp: add support for suspend and resume When the system returns from suspend, it looses its configuration. Most of it is restored by running a normal audio stream startup, but the DAI format is left unset as that's configured on the audio device creation. Hence, it suffices here to care for the registers which are touched by davinci_mcasp_set_dai_fmt() and restore them when the system is resumed. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 32ddb7f..cdfe959 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -1251,12 +1251,51 @@ static int davinci_mcasp_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int davinci_mcasp_suspend(struct device *dev) +{ + struct davinci_audio_dev *a = dev_get_drvdata(dev); + void __iomem *base = a->base; + + a->context.txfmtctl = mcasp_get_reg(base + DAVINCI_MCASP_TXFMCTL_REG); + a->context.rxfmtctl = mcasp_get_reg(base + DAVINCI_MCASP_RXFMCTL_REG); + a->context.txfmt = mcasp_get_reg(base + DAVINCI_MCASP_TXFMT_REG); + a->context.rxfmt = mcasp_get_reg(base + DAVINCI_MCASP_RXFMT_REG); + a->context.aclkxctl = mcasp_get_reg(base + DAVINCI_MCASP_ACLKXCTL_REG); + a->context.aclkrctl = mcasp_get_reg(base + DAVINCI_MCASP_ACLKRCTL_REG); + a->context.pdir = mcasp_get_reg(base + DAVINCI_MCASP_PDIR_REG); + + return 0; +} + +static int davinci_mcasp_resume(struct device *dev) +{ + struct davinci_audio_dev *a = dev_get_drvdata(dev); + void __iomem *base = a->base; + + mcasp_set_reg(base + DAVINCI_MCASP_TXFMCTL_REG, a->context.txfmtctl); + mcasp_set_reg(base + DAVINCI_MCASP_RXFMCTL_REG, a->context.rxfmtctl); + mcasp_set_reg(base + DAVINCI_MCASP_TXFMT_REG, a->context.txfmt); + mcasp_set_reg(base + DAVINCI_MCASP_RXFMT_REG, a->context.rxfmt); + mcasp_set_reg(base + DAVINCI_MCASP_ACLKXCTL_REG, a->context.aclkxctl); + mcasp_set_reg(base + DAVINCI_MCASP_ACLKRCTL_REG, a->context.aclkrctl); + mcasp_set_reg(base + DAVINCI_MCASP_PDIR_REG, a->context.pdir); + + return 0; +} +#endif + +SIMPLE_DEV_PM_OPS(davinci_mcasp_pm_ops, + davinci_mcasp_suspend, + davinci_mcasp_resume); + static struct platform_driver davinci_mcasp_driver = { .probe = davinci_mcasp_probe, .remove = davinci_mcasp_remove, .driver = { .name = "davinci-mcasp", .owner = THIS_MODULE, + .pm = &davinci_mcasp_pm_ops, .of_match_table = mcasp_dt_ids, }, }; diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h index a9ac0c1..a2e27e1 100644 --- a/sound/soc/davinci/davinci-mcasp.h +++ b/sound/soc/davinci/davinci-mcasp.h @@ -43,6 +43,18 @@ struct davinci_audio_dev { /* McASP FIFO related */ u8 txnumevt; u8 rxnumevt; + +#ifdef CONFIG_PM_SLEEP + struct { + u32 txfmtctl; + u32 rxfmtctl; + u32 txfmt; + u32 rxfmt; + u32 aclkxctl; + u32 aclkrctl; + u32 pdir; + } context; +#endif }; #endif /* DAVINCI_MCASP_H */ -- cgit v0.10.2 From 18dca93eb7faa8d90fbc54bfb08560caebbf92cd Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 2 Oct 2013 20:54:20 +0800 Subject: regulator: stw481x-vmmc: Set missing .of_match_table to stw481x_vmmc_match Signed-off-by: Axel Lin Signed-off-by: Mark Brown diff --git a/drivers/regulator/stw481x-vmmc.c b/drivers/regulator/stw481x-vmmc.c index 5ae72a8..f78857b 100644 --- a/drivers/regulator/stw481x-vmmc.c +++ b/drivers/regulator/stw481x-vmmc.c @@ -102,6 +102,7 @@ static struct platform_driver stw481x_vmmc_regulator_driver = { .driver = { .name = "stw481x-vmmc-regulator", .owner = THIS_MODULE, + .of_match_table = stw481x_vmmc_match, }, .probe = stw481x_vmmc_regulator_probe, .remove = stw481x_vmmc_regulator_remove, -- cgit v0.10.2 From 0e746ec55812a2f8f5ab986aeedd5291e3b6fad4 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Wed, 2 Oct 2013 14:35:20 +0200 Subject: clocksource: tcb_clksrc: Replace clk_enable/disable with clk_prepare_enable/disable_unprepare Replace clk_enable/disable with clk_prepare_enable/disable_unprepare to avoid common clk framework warnings. Signed-off-by: Boris BREZILLON Acked-by: Nicolas Ferre Signed-off-by: Daniel Lezcano diff --git a/drivers/clocksource/tcb_clksrc.c b/drivers/clocksource/tcb_clksrc.c index 8a61872..0481562 100644 --- a/drivers/clocksource/tcb_clksrc.c +++ b/drivers/clocksource/tcb_clksrc.c @@ -100,7 +100,7 @@ static void tc_mode(enum clock_event_mode m, struct clock_event_device *d) || tcd->clkevt.mode == CLOCK_EVT_MODE_ONESHOT) { __raw_writel(0xff, regs + ATMEL_TC_REG(2, IDR)); __raw_writel(ATMEL_TC_CLKDIS, regs + ATMEL_TC_REG(2, CCR)); - clk_disable(tcd->clk); + clk_disable_unprepare(tcd->clk); } switch (m) { @@ -109,7 +109,7 @@ static void tc_mode(enum clock_event_mode m, struct clock_event_device *d) * of oneshot, we get lower overhead and improved accuracy. */ case CLOCK_EVT_MODE_PERIODIC: - clk_enable(tcd->clk); + clk_prepare_enable(tcd->clk); /* slow clock, count up to RC, then irq and restart */ __raw_writel(timer_clock @@ -126,7 +126,7 @@ static void tc_mode(enum clock_event_mode m, struct clock_event_device *d) break; case CLOCK_EVT_MODE_ONESHOT: - clk_enable(tcd->clk); + clk_prepare_enable(tcd->clk); /* slow clock, count up to RC, then irq and stop */ __raw_writel(timer_clock | ATMEL_TC_CPCSTOP @@ -265,6 +265,7 @@ static int __init tcb_clksrc_init(void) int best_divisor_idx = -1; int clk32k_divisor_idx = -1; int i; + int ret; tc = atmel_tc_alloc(CONFIG_ATMEL_TCB_CLKSRC_BLOCK, clksrc.name); if (!tc) { @@ -275,7 +276,11 @@ static int __init tcb_clksrc_init(void) pdev = tc->pdev; t0_clk = tc->clk[0]; - clk_enable(t0_clk); + ret = clk_prepare_enable(t0_clk); + if (ret) { + pr_debug("can't enable T0 clk\n"); + goto err_free_tc; + } /* How fast will we be counting? Pick something over 5 MHz. */ rate = (u32) clk_get_rate(t0_clk); @@ -313,7 +318,11 @@ static int __init tcb_clksrc_init(void) /* tclib will give us three clocks no matter what the * underlying platform supports. */ - clk_enable(tc->clk[1]); + ret = clk_prepare_enable(tc->clk[1]); + if (ret) { + pr_debug("can't enable T1 clk\n"); + goto err_disable_t0; + } /* setup both channel 0 & 1 */ tcb_setup_dual_chan(tc, best_divisor_idx); } @@ -325,5 +334,12 @@ static int __init tcb_clksrc_init(void) setup_clkevents(tc, clk32k_divisor_idx); return 0; + +err_disable_t0: + clk_disable_unprepare(t0_clk); + +err_free_tc: + atmel_tc_free(tc); + return ret; } arch_initcall(tcb_clksrc_init); -- cgit v0.10.2 From 5b3c11da141c27df6f8dd8acf90c0046ebab3a07 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Wed, 2 Oct 2013 14:35:41 +0200 Subject: clocksource: tcb_clksrc: Improve driver robustness Check function return values to avoid false positive driver init. Signed-off-by: Boris BREZILLON Signed-off-by: Daniel Lezcano diff --git a/drivers/clocksource/tcb_clksrc.c b/drivers/clocksource/tcb_clksrc.c index 0481562..10a5d9e 100644 --- a/drivers/clocksource/tcb_clksrc.c +++ b/drivers/clocksource/tcb_clksrc.c @@ -184,11 +184,18 @@ static struct irqaction tc_irqaction = { .handler = ch2_irq, }; -static void __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) +static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) { + int ret; struct clk *t2_clk = tc->clk[2]; int irq = tc->irq[2]; + /* try to enable t2 clk to avoid future errors in mode change */ + ret = clk_prepare_enable(t2_clk); + if (ret) + return ret; + clk_disable_unprepare(t2_clk); + clkevt.regs = tc->regs; clkevt.clk = t2_clk; tc_irqaction.dev_id = &clkevt; @@ -197,16 +204,21 @@ static void __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) clkevt.clkevt.cpumask = cpumask_of(0); + ret = setup_irq(irq, &tc_irqaction); + if (ret) + return ret; + clockevents_config_and_register(&clkevt.clkevt, 32768, 1, 0xffff); - setup_irq(irq, &tc_irqaction); + return ret; } #else /* !CONFIG_GENERIC_CLOCKEVENTS */ -static void __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) +static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) { /* NOTHING */ + return 0; } #endif @@ -328,13 +340,24 @@ static int __init tcb_clksrc_init(void) } /* and away we go! */ - clocksource_register_hz(&clksrc, divided_rate); + ret = clocksource_register_hz(&clksrc, divided_rate); + if (ret) + goto err_disable_t1; /* channel 2: periodic and oneshot timer support */ - setup_clkevents(tc, clk32k_divisor_idx); + ret = setup_clkevents(tc, clk32k_divisor_idx); + if (ret) + goto err_unregister_clksrc; return 0; +err_unregister_clksrc: + clocksource_unregister(&clksrc); + +err_disable_t1: + if (!tc->tcb_config || tc->tcb_config->counter_width != 32) + clk_disable_unprepare(tc->clk[1]); + err_disable_t0: clk_disable_unprepare(t0_clk); -- cgit v0.10.2 From f51380a75652a4600b34ce384c4ff89ce0a15132 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Wed, 2 Oct 2013 14:35:48 +0200 Subject: clocksource: tcb_clksrc: Remove IRQF_DISABLED Remove the deprecated IRQF_DISABLED flag. Signed-off-by: Boris BREZILLON Signed-off-by: Daniel Lezcano diff --git a/drivers/clocksource/tcb_clksrc.c b/drivers/clocksource/tcb_clksrc.c index 10a5d9e..00fdd11 100644 --- a/drivers/clocksource/tcb_clksrc.c +++ b/drivers/clocksource/tcb_clksrc.c @@ -180,7 +180,7 @@ static irqreturn_t ch2_irq(int irq, void *handle) static struct irqaction tc_irqaction = { .name = "tc_clkevt", - .flags = IRQF_TIMER | IRQF_DISABLED, + .flags = IRQF_TIMER, .handler = ch2_irq, }; -- cgit v0.10.2 From 43f6fc9542d6c42cc2e5783536ed31d90516c999 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 2 Oct 2013 20:47:56 +0800 Subject: regulator: as3722: Fix equation to calculate max_uV in regulator_lin_range macro Fix off-by-one in the equation to calculate max_uV and also adjust the _min_uV setting accordingly. For LDOs: The voltage select bits set the LDO output voltage 0.825V...3.3V, 25mV steps ....00h : LDO off 01h-24h : V_LDO4 = 0.8V + ldo4_vsel * 25mV = 0.825V + (ldo4_vsel - 1h) * 25mV 25h-3Fh : do not use 40h-7Fh : V_LDO4 = 1.725V + (ldo4_vsel - 40h) * 25mV For SD2345: The voltage select bits set the DC/DC output voltage level and power the DC/DC converter down. ....00h : DC/DC powered down 01h-40h : V_SD2 = 0.6V + sd2_vsel * 12.5mV = 0.6125V + (sd2_vsel - 1h) * 12.5mV 41h-70h : V_SD2 = 1.4V + (sd2_vsel - 40h) * 25mV = 1.425V + (sd2_vsel - 41h) * 25mV 71h-7Fh : V_SD2 = 2.6V + (sd2_vsel - 70h) * 50mV = 2.65V + (sd2_vsel - 71h) * 50mV Note, the third entry in as3722_sd2345_ranges is wrong in current code. Fix it based on the datasheet. Signed-off-by: Axel Lin Acked-by: Laxman Dewangan Signed-off-by: Mark Brown diff --git a/drivers/regulator/as3722-regulator.c b/drivers/regulator/as3722-regulator.c index 16a5d26..01a8b17 100644 --- a/drivers/regulator/as3722-regulator.c +++ b/drivers/regulator/as3722-regulator.c @@ -441,11 +441,11 @@ static struct regulator_ops as3722_ldo3_extcntrl_ops = { .max_sel = _max_sel, \ .uV_step = _step_uV, \ .min_uV = _min_uV, \ - .max_uV = _min_uV + (_max_sel - _min_sel + 1) * _step_uV, \ + .max_uV = _min_uV + (_max_sel - _min_sel) * _step_uV, \ } static const struct regulator_linear_range as3722_ldo_ranges[] = { - regulator_lin_range(0x01, 0x24, 800000, 25000), + regulator_lin_range(0x01, 0x24, 825000, 25000), regulator_lin_range(0x40, 0x7F, 1725000, 25000), }; @@ -605,9 +605,9 @@ static int as3722_sd016_set_current_limit(struct regulator_dev *rdev, } static const struct regulator_linear_range as3722_sd2345_ranges[] = { - regulator_lin_range(0x01, 0x40, 600000, 12500), - regulator_lin_range(0x41, 0x70, 1400000, 25000), - regulator_lin_range(0x71, 0x7F, 1725000, 50000), + regulator_lin_range(0x01, 0x40, 612500, 12500), + regulator_lin_range(0x41, 0x70, 1425000, 25000), + regulator_lin_range(0x71, 0x7F, 2650000, 50000), }; static struct regulator_ops as3722_sd016_ops = { -- cgit v0.10.2 From 5e965704803dcbcbfbf97e8a976b3778b245834b Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 2 Oct 2013 20:49:29 +0800 Subject: regulator: as3722: Fix off-by-one n_voltages setting for SDx AS3722_SDx_VSEL_MAX means the maximum selecter, the n_voltages should be AS3722_SDx_VSEL_MAX + 1. Signed-off-by: Axel Lin Acked-by: Laxman Dewangan Signed-off-by: Mark Brown diff --git a/drivers/regulator/as3722-regulator.c b/drivers/regulator/as3722-regulator.c index 01a8b17..d7b71a9 100644 --- a/drivers/regulator/as3722-regulator.c +++ b/drivers/regulator/as3722-regulator.c @@ -99,7 +99,7 @@ static const struct as3722_register_mapping as3722_reg_lookup[] = { .sleep_ctrl_mask = AS3722_SD0_EXT_ENABLE_MASK, .control_reg = AS3722_SD0_CONTROL_REG, .mode_mask = AS3722_SD0_MODE_FAST, - .n_voltages = AS3722_SD0_VSEL_MAX, + .n_voltages = AS3722_SD0_VSEL_MAX + 1, }, { .regulator_id = AS3722_REGULATOR_ID_SD1, @@ -112,7 +112,7 @@ static const struct as3722_register_mapping as3722_reg_lookup[] = { .sleep_ctrl_mask = AS3722_SD1_EXT_ENABLE_MASK, .control_reg = AS3722_SD1_CONTROL_REG, .mode_mask = AS3722_SD1_MODE_FAST, - .n_voltages = AS3722_SD0_VSEL_MAX, + .n_voltages = AS3722_SD0_VSEL_MAX + 1, }, { .regulator_id = AS3722_REGULATOR_ID_SD2, @@ -126,7 +126,7 @@ static const struct as3722_register_mapping as3722_reg_lookup[] = { .sleep_ctrl_mask = AS3722_SD2_EXT_ENABLE_MASK, .control_reg = AS3722_SD23_CONTROL_REG, .mode_mask = AS3722_SD2_MODE_FAST, - .n_voltages = AS3722_SD2_VSEL_MAX, + .n_voltages = AS3722_SD2_VSEL_MAX + 1, }, { .regulator_id = AS3722_REGULATOR_ID_SD3, @@ -140,7 +140,7 @@ static const struct as3722_register_mapping as3722_reg_lookup[] = { .sleep_ctrl_mask = AS3722_SD3_EXT_ENABLE_MASK, .control_reg = AS3722_SD23_CONTROL_REG, .mode_mask = AS3722_SD3_MODE_FAST, - .n_voltages = AS3722_SD2_VSEL_MAX, + .n_voltages = AS3722_SD2_VSEL_MAX + 1, }, { .regulator_id = AS3722_REGULATOR_ID_SD4, @@ -154,7 +154,7 @@ static const struct as3722_register_mapping as3722_reg_lookup[] = { .sleep_ctrl_mask = AS3722_SD4_EXT_ENABLE_MASK, .control_reg = AS3722_SD4_CONTROL_REG, .mode_mask = AS3722_SD4_MODE_FAST, - .n_voltages = AS3722_SD2_VSEL_MAX, + .n_voltages = AS3722_SD2_VSEL_MAX + 1, }, { .regulator_id = AS3722_REGULATOR_ID_SD5, @@ -168,7 +168,7 @@ static const struct as3722_register_mapping as3722_reg_lookup[] = { .sleep_ctrl_mask = AS3722_SD5_EXT_ENABLE_MASK, .control_reg = AS3722_SD5_CONTROL_REG, .mode_mask = AS3722_SD5_MODE_FAST, - .n_voltages = AS3722_SD2_VSEL_MAX, + .n_voltages = AS3722_SD2_VSEL_MAX + 1, }, { .regulator_id = AS3722_REGULATOR_ID_SD6, @@ -181,7 +181,7 @@ static const struct as3722_register_mapping as3722_reg_lookup[] = { .sleep_ctrl_mask = AS3722_SD6_EXT_ENABLE_MASK, .control_reg = AS3722_SD6_CONTROL_REG, .mode_mask = AS3722_SD6_MODE_FAST, - .n_voltages = AS3722_SD0_VSEL_MAX, + .n_voltages = AS3722_SD0_VSEL_MAX + 1, }, { .regulator_id = AS3722_REGULATOR_ID_LDO0, -- cgit v0.10.2 From c70837908dc1b63298125e1b80064c4639d92fd2 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 27 Sep 2013 15:32:53 +0530 Subject: spi: clps711x: Remove redundant label Remove empty label. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-clps711x.c b/drivers/spi/spi-clps711x.c index 5655acf..1a78683 100644 --- a/drivers/spi/spi-clps711x.c +++ b/drivers/spi/spi-clps711x.c @@ -227,7 +227,7 @@ static int spi_clps711x_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "Can't request IRQ\n"); clk_put(hw->spi_clk); - goto clk_out; + goto err_out; } ret = spi_register_master(master); @@ -240,7 +240,6 @@ static int spi_clps711x_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to register master\n"); -clk_out: err_out: while (--i >= 0) if (gpio_is_valid(hw->chipselect[i])) -- cgit v0.10.2 From 8daaa5f8261bffd2f6217a960f9182d0503a5c44 Mon Sep 17 00:00:00 2001 From: Mike Travis Date: Wed, 2 Oct 2013 10:14:18 -0500 Subject: kdb: Add support for external NMI handler to call KGDB/KDB This patch adds a kgdb_nmicallin() interface that can be used by external NMI handlers to call the KGDB/KDB handler. The primary need for this is for those types of NMI interrupts where all the CPUs have already received the NMI signal. Therefore no send_IPI(NMI) is required, and in fact it will cause a 2nd unhandled NMI to occur. This generates the "Dazed and Confuzed" messages. Since all the CPUs are getting the NMI at roughly the same time, it's not guaranteed that the first CPU that hits the NMI handler will manage to enter KGDB and set the dbg_master_lock before the slaves start entering. The new argument "send_ready" was added for KGDB to signal the NMI handler to release the slave CPUs for entry into KGDB. Signed-off-by: Mike Travis Acked-by: Jason Wessel Reviewed-by: Dimitri Sivanich Reviewed-by: Hedi Berriche Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo Link: http://lkml.kernel.org/r/20131002151417.928886849@asylum.americas.sgi.com Signed-off-by: Ingo Molnar diff --git a/include/linux/kdb.h b/include/linux/kdb.h index 7f6fe6e..290db12 100644 --- a/include/linux/kdb.h +++ b/include/linux/kdb.h @@ -109,6 +109,7 @@ typedef enum { KDB_REASON_RECURSE, /* Recursive entry to kdb; * regs probably valid */ KDB_REASON_SSTEP, /* Single Step trap. - regs valid */ + KDB_REASON_SYSTEM_NMI, /* In NMI due to SYSTEM cmd; regs valid */ } kdb_reason_t; extern int kdb_trap_printk; diff --git a/include/linux/kgdb.h b/include/linux/kgdb.h index c6e091b..dfb4f2f 100644 --- a/include/linux/kgdb.h +++ b/include/linux/kgdb.h @@ -310,6 +310,7 @@ extern int kgdb_handle_exception(int ex_vector, int signo, int err_code, struct pt_regs *regs); extern int kgdb_nmicallback(int cpu, void *regs); +extern int kgdb_nmicallin(int cpu, int trapnr, void *regs, atomic_t *snd_rdy); extern void gdbstub_exit(int status); extern int kgdb_single_step; diff --git a/kernel/debug/debug_core.c b/kernel/debug/debug_core.c index 0506d44..7d2f35e 100644 --- a/kernel/debug/debug_core.c +++ b/kernel/debug/debug_core.c @@ -575,8 +575,12 @@ return_normal: raw_spin_lock(&dbg_slave_lock); #ifdef CONFIG_SMP + /* If send_ready set, slaves are already waiting */ + if (ks->send_ready) + atomic_set(ks->send_ready, 1); + /* Signal the other CPUs to enter kgdb_wait() */ - if ((!kgdb_single_step) && kgdb_do_roundup) + else if ((!kgdb_single_step) && kgdb_do_roundup) kgdb_roundup_cpus(flags); #endif @@ -678,11 +682,11 @@ kgdb_handle_exception(int evector, int signo, int ecode, struct pt_regs *regs) if (arch_kgdb_ops.enable_nmi) arch_kgdb_ops.enable_nmi(0); + memset(ks, 0, sizeof(struct kgdb_state)); ks->cpu = raw_smp_processor_id(); ks->ex_vector = evector; ks->signo = signo; ks->err_code = ecode; - ks->kgdb_usethreadid = 0; ks->linux_regs = regs; if (kgdb_reenter_check(ks)) @@ -732,6 +736,30 @@ int kgdb_nmicallback(int cpu, void *regs) return 1; } +int kgdb_nmicallin(int cpu, int trapnr, void *regs, atomic_t *send_ready) +{ +#ifdef CONFIG_SMP + if (!kgdb_io_ready(0) || !send_ready) + return 1; + + if (kgdb_info[cpu].enter_kgdb == 0) { + struct kgdb_state kgdb_var; + struct kgdb_state *ks = &kgdb_var; + + memset(ks, 0, sizeof(struct kgdb_state)); + ks->cpu = cpu; + ks->ex_vector = trapnr; + ks->signo = SIGTRAP; + ks->err_code = KGDB_KDB_REASON_SYSTEM_NMI; + ks->linux_regs = regs; + ks->send_ready = send_ready; + kgdb_cpu_enter(ks, regs, DCPU_WANT_MASTER); + return 0; + } +#endif + return 1; +} + static void kgdb_console_write(struct console *co, const char *s, unsigned count) { diff --git a/kernel/debug/debug_core.h b/kernel/debug/debug_core.h index 2235967..572aa4f 100644 --- a/kernel/debug/debug_core.h +++ b/kernel/debug/debug_core.h @@ -26,6 +26,7 @@ struct kgdb_state { unsigned long threadid; long kgdb_usethreadid; struct pt_regs *linux_regs; + atomic_t *send_ready; }; /* Exception state values */ @@ -74,11 +75,13 @@ extern int kdb_stub(struct kgdb_state *ks); extern int kdb_parse(const char *cmdstr); extern int kdb_common_init_state(struct kgdb_state *ks); extern int kdb_common_deinit_state(void); +#define KGDB_KDB_REASON_SYSTEM_NMI KDB_REASON_SYSTEM_NMI #else /* ! CONFIG_KGDB_KDB */ static inline int kdb_stub(struct kgdb_state *ks) { return DBG_PASS_EVENT; } +#define KGDB_KDB_REASON_SYSTEM_NMI 0 #endif /* CONFIG_KGDB_KDB */ #endif /* _DEBUG_CORE_H_ */ diff --git a/kernel/debug/kdb/kdb_debugger.c b/kernel/debug/kdb/kdb_debugger.c index 328d18e..8859ca3 100644 --- a/kernel/debug/kdb/kdb_debugger.c +++ b/kernel/debug/kdb/kdb_debugger.c @@ -69,7 +69,10 @@ int kdb_stub(struct kgdb_state *ks) if (atomic_read(&kgdb_setting_breakpoint)) reason = KDB_REASON_KEYBOARD; - if (in_nmi()) + if (ks->err_code == KDB_REASON_SYSTEM_NMI && ks->signo == SIGTRAP) + reason = KDB_REASON_SYSTEM_NMI; + + else if (in_nmi()) reason = KDB_REASON_NMI; for (i = 0, bp = kdb_breakpoints; i < KDB_MAXBPT; i++, bp++) { diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c index 00eb8f7..0b097c8 100644 --- a/kernel/debug/kdb/kdb_main.c +++ b/kernel/debug/kdb/kdb_main.c @@ -1200,6 +1200,9 @@ static int kdb_local(kdb_reason_t reason, int error, struct pt_regs *regs, instruction_pointer(regs)); kdb_dumpregs(regs); break; + case KDB_REASON_SYSTEM_NMI: + kdb_printf("due to System NonMaskable Interrupt\n"); + break; case KDB_REASON_NMI: kdb_printf("due to NonMaskable Interrupt @ " kdb_machreg_fmt "\n", -- cgit v0.10.2 From e379ea82dd53a5cc8e3ac0b7899a8012006c712c Mon Sep 17 00:00:00 2001 From: Mike Travis Date: Wed, 2 Oct 2013 10:14:19 -0500 Subject: x86/UV: Add call to KGDB/KDB from NMI handler This patch restores the capability to enter KDB (and KGDB) from the UV NMI handler. This is needed because the UV system console is not capable of sending the 'break' signal to the serial console port. It is also useful when the kernel is hung in such a way that it isn't responding to normal external I/O, so sending 'g' to sysreq-trigger does not work either. Another benefit of the external NMI command is that all the cpus receive the NMI signal at roughly the same time so they are more closely aligned timewise. It utilizes the newly added kgdb_nmicallin function to gain entry to KGDB/KDB by the master. The slaves still enter via the standard kgdb_nmicallback function. It also uses the new 'send_ready' pointer to tell KGDB/KDB to signal the slaves when to proceed into the KGDB slave loop. It is enabled when the nmi action is set to "kdb" and the kernel is built with CONFIG_KDB enabled. Note that if kgdb is connected that interface will be used instead. Signed-off-by: Mike Travis Reviewed-by: Dimitri Sivanich Reviewed-by: Hedi Berriche Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo Cc: Jason Wessel Link: http://lkml.kernel.org/r/20131002151418.089692683@asylum.americas.sgi.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/platform/uv/uv_nmi.c b/arch/x86/platform/uv/uv_nmi.c index 9126dfb..9b8ac60 100644 --- a/arch/x86/platform/uv/uv_nmi.c +++ b/arch/x86/platform/uv/uv_nmi.c @@ -21,7 +21,9 @@ #include #include +#include #include +#include #include #include #include @@ -32,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -153,8 +156,9 @@ module_param_named(retry_count, uv_nmi_retry_count, int, 0644); * "dump" - dump process stack for each cpu * "ips" - dump IP info for each cpu * "kdump" - do crash dump + * "kdb" - enter KDB/KGDB (default) */ -static char uv_nmi_action[8] = "dump"; +static char uv_nmi_action[8] = "kdb"; module_param_string(action, uv_nmi_action, sizeof(uv_nmi_action), 0644); static inline bool uv_nmi_action_is(const char *action) @@ -540,6 +544,43 @@ static inline void uv_nmi_kdump(int cpu, int master, struct pt_regs *regs) } #endif /* !CONFIG_KEXEC */ +#ifdef CONFIG_KGDB_KDB +/* Call KDB from NMI handler */ +static void uv_call_kdb(int cpu, struct pt_regs *regs, int master) +{ + int ret; + + if (master) { + /* call KGDB NMI handler as MASTER */ + ret = kgdb_nmicallin(cpu, X86_TRAP_NMI, regs, + &uv_nmi_slave_continue); + if (ret) { + pr_alert("KDB returned error, is kgdboc set?\n"); + atomic_set(&uv_nmi_slave_continue, SLAVE_EXIT); + } + } else { + /* wait for KGDB signal that it's ready for slaves to enter */ + int sig; + + do { + cpu_relax(); + sig = atomic_read(&uv_nmi_slave_continue); + } while (!sig); + + /* call KGDB as slave */ + if (sig == SLAVE_CONTINUE) + kgdb_nmicallback(cpu, regs); + } + uv_nmi_sync_exit(master); +} + +#else /* !CONFIG_KGDB_KDB */ +static inline void uv_call_kdb(int cpu, struct pt_regs *regs, int master) +{ + pr_err("UV: NMI error: KGDB/KDB is not enabled in this kernel\n"); +} +#endif /* !CONFIG_KGDB_KDB */ + /* * UV NMI handler */ @@ -576,6 +617,10 @@ int uv_handle_nmi(unsigned int reason, struct pt_regs *regs) if (uv_nmi_action_is("ips") || uv_nmi_action_is("dump")) uv_nmi_dump_state(cpu, regs, master); + /* Call KDB if enabled */ + else if (uv_nmi_action_is("kdb")) + uv_call_kdb(cpu, regs, master); + /* Clear per_cpu "in nmi" flag */ atomic_set(&uv_cpu_nmi.state, UV_NMI_STATE_OUT); -- cgit v0.10.2 From 354cc40e3b0981c38ba2ce2964954480e6c03c37 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Tue, 1 Oct 2013 07:22:15 -0700 Subject: tools/perf: Fix sorting for 64bit entries Some of the node comparisons in hist.c dropped the upper 32bit by using an int variable to store the compare result. This broke various 64bit fields, causing incorrect collapsing (found for the TSX transaction field) Just use int64_t always. Acked-by: Namhyung Kim Signed-off-by: Andi Kleen Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1380637335-30110-1-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 9ff6cf3..97dc280 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -346,7 +346,7 @@ static struct hist_entry *add_hist_entry(struct hists *hists, struct rb_node **p; struct rb_node *parent = NULL; struct hist_entry *he; - int cmp; + int64_t cmp; p = &hists->entries_in->rb_node; @@ -884,7 +884,7 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists, struct rb_node **p; struct rb_node *parent = NULL; struct hist_entry *he; - int cmp; + int64_t cmp; if (sort__need_collapse) root = &hists->entries_collapsed; -- cgit v0.10.2 From 723478c8a471403c53cf144999701f6e0c4bbd11 Mon Sep 17 00:00:00 2001 From: Knut Petersen Date: Wed, 25 Sep 2013 14:29:37 +0200 Subject: perf: Enforce 1 as lower limit for perf_event_max_sample_rate /proc/sys/kernel/perf_event_max_sample_rate will accept negative values as well as 0. Negative values are unreasonable, and 0 causes a divide by zero exception in perf_proc_update_handler. This patch enforces a lower limit of 1. Signed-off-by: Knut Petersen Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/5242DB0C.4070005@t-online.de Signed-off-by: Ingo Molnar diff --git a/kernel/events/core.c b/kernel/events/core.c index d49a9d2..b25d65c 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -193,7 +193,7 @@ int perf_proc_update_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { - int ret = proc_dointvec(table, write, buffer, lenp, ppos); + int ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); if (ret || !write) return ret; diff --git a/kernel/sysctl.c b/kernel/sysctl.c index b2f06f3..2a9db91 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1049,6 +1049,7 @@ static struct ctl_table kern_table[] = { .maxlen = sizeof(sysctl_perf_event_sample_rate), .mode = 0644, .proc_handler = perf_proc_update_handler, + .extra1 = &one, }, { .procname = "perf_cpu_time_max_percent", -- cgit v0.10.2 From 4cabc3d1cb6a46f581a2628d1d11c483d5f300e5 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 21 Aug 2013 16:47:26 -0700 Subject: tools/perf/stat: Add perf stat --transaction Add support to perf stat to print the basic transactional execution statistics: Total cycles, Cycles in Transaction, Cycles in aborted transsactions using the in_tx and in_tx_checkpoint qualifiers. Transaction Starts and Elision Starts, to compute the average transaction length. This is a reasonable overview over the success of the transactions. Also support architectures that have a transaction aborted cycles counter like POWER8. Since that is awkward to handle in the kernel abstract handle both cases here. Enable with a new --transaction / -T option. This requires measuring these events in a group, since they depend on each other. This is implemented by using TM sysfs events exported by the kernel Signed-off-by: Andi Kleen Acked-by: Arnaldo Carvalho de Melo Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1377128846-977-5-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 73c9759..80c7da6 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -137,6 +137,11 @@ core number and the number of online logical processors on that physical process After starting the program, wait msecs before measuring. This is useful to filter out the startup phase of the program, which is often very different. +-T:: +--transaction:: + +Print statistics of transactional execution if supported. + EXAMPLES -------- diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index f686d5f..cc7efee 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -46,6 +46,7 @@ #include "util/util.h" #include "util/parse-options.h" #include "util/parse-events.h" +#include "util/pmu.h" #include "util/event.h" #include "util/evlist.h" #include "util/evsel.h" @@ -70,6 +71,41 @@ static void print_counter_aggr(struct perf_evsel *counter, char *prefix); static void print_counter(struct perf_evsel *counter, char *prefix); static void print_aggr(char *prefix); +/* Default events used for perf stat -T */ +static const char * const transaction_attrs[] = { + "task-clock", + "{" + "instructions," + "cycles," + "cpu/cycles-t/," + "cpu/tx-start/," + "cpu/el-start/," + "cpu/cycles-ct/" + "}" +}; + +/* More limited version when the CPU does not have all events. */ +static const char * const transaction_limited_attrs[] = { + "task-clock", + "{" + "instructions," + "cycles," + "cpu/cycles-t/," + "cpu/tx-start/" + "}" +}; + +/* must match transaction_attrs and the beginning limited_attrs */ +enum { + T_TASK_CLOCK, + T_INSTRUCTIONS, + T_CYCLES, + T_CYCLES_IN_TX, + T_TRANSACTION_START, + T_ELISION_START, + T_CYCLES_IN_TX_CP, +}; + static struct perf_evlist *evsel_list; static struct perf_target target = { @@ -90,6 +126,7 @@ static enum aggr_mode aggr_mode = AGGR_GLOBAL; static volatile pid_t child_pid = -1; static bool null_run = false; static int detailed_run = 0; +static bool transaction_run; static bool big_num = true; static int big_num_opt = -1; static const char *csv_sep = NULL; @@ -214,7 +251,10 @@ static struct stats runtime_l1_icache_stats[MAX_NR_CPUS]; static struct stats runtime_ll_cache_stats[MAX_NR_CPUS]; static struct stats runtime_itlb_cache_stats[MAX_NR_CPUS]; static struct stats runtime_dtlb_cache_stats[MAX_NR_CPUS]; +static struct stats runtime_cycles_in_tx_stats[MAX_NR_CPUS]; static struct stats walltime_nsecs_stats; +static struct stats runtime_transaction_stats[MAX_NR_CPUS]; +static struct stats runtime_elision_stats[MAX_NR_CPUS]; static void perf_stat__reset_stats(struct perf_evlist *evlist) { @@ -236,6 +276,11 @@ static void perf_stat__reset_stats(struct perf_evlist *evlist) memset(runtime_ll_cache_stats, 0, sizeof(runtime_ll_cache_stats)); memset(runtime_itlb_cache_stats, 0, sizeof(runtime_itlb_cache_stats)); memset(runtime_dtlb_cache_stats, 0, sizeof(runtime_dtlb_cache_stats)); + memset(runtime_cycles_in_tx_stats, 0, + sizeof(runtime_cycles_in_tx_stats)); + memset(runtime_transaction_stats, 0, + sizeof(runtime_transaction_stats)); + memset(runtime_elision_stats, 0, sizeof(runtime_elision_stats)); memset(&walltime_nsecs_stats, 0, sizeof(walltime_nsecs_stats)); } @@ -274,6 +319,29 @@ static inline int nsec_counter(struct perf_evsel *evsel) return 0; } +static struct perf_evsel *nth_evsel(int n) +{ + static struct perf_evsel **array; + static int array_len; + struct perf_evsel *ev; + int j; + + /* Assumes this only called when evsel_list does not change anymore. */ + if (!array) { + list_for_each_entry(ev, &evsel_list->entries, node) + array_len++; + array = malloc(array_len * sizeof(void *)); + if (!array) + exit(ENOMEM); + j = 0; + list_for_each_entry(ev, &evsel_list->entries, node) + array[j++] = ev; + } + if (n < array_len) + return array[n]; + return NULL; +} + /* * Update various tracking values we maintain to print * more semantic information such as miss/hit ratios, @@ -285,6 +353,15 @@ static void update_shadow_stats(struct perf_evsel *counter, u64 *count) update_stats(&runtime_nsecs_stats[0], count[0]); else if (perf_evsel__match(counter, HARDWARE, HW_CPU_CYCLES)) update_stats(&runtime_cycles_stats[0], count[0]); + else if (transaction_run && + perf_evsel__cmp(counter, nth_evsel(T_CYCLES_IN_TX))) + update_stats(&runtime_cycles_in_tx_stats[0], count[0]); + else if (transaction_run && + perf_evsel__cmp(counter, nth_evsel(T_TRANSACTION_START))) + update_stats(&runtime_transaction_stats[0], count[0]); + else if (transaction_run && + perf_evsel__cmp(counter, nth_evsel(T_ELISION_START))) + update_stats(&runtime_elision_stats[0], count[0]); else if (perf_evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_FRONTEND)) update_stats(&runtime_stalled_cycles_front_stats[0], count[0]); else if (perf_evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_BACKEND)) @@ -827,7 +904,7 @@ static void print_ll_cache_misses(int cpu, static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) { - double total, ratio = 0.0; + double total, ratio = 0.0, total2; const char *fmt; if (csv_output) @@ -923,6 +1000,43 @@ static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) ratio = 1.0 * avg / total; fprintf(output, " # %8.3f GHz ", ratio); + } else if (transaction_run && + perf_evsel__cmp(evsel, nth_evsel(T_CYCLES_IN_TX))) { + total = avg_stats(&runtime_cycles_stats[cpu]); + if (total) + fprintf(output, + " # %5.2f%% transactional cycles ", + 100.0 * (avg / total)); + } else if (transaction_run && + perf_evsel__cmp(evsel, nth_evsel(T_CYCLES_IN_TX_CP))) { + total = avg_stats(&runtime_cycles_stats[cpu]); + total2 = avg_stats(&runtime_cycles_in_tx_stats[cpu]); + if (total2 < avg) + total2 = avg; + if (total) + fprintf(output, + " # %5.2f%% aborted cycles ", + 100.0 * ((total2-avg) / total)); + } else if (transaction_run && + perf_evsel__cmp(evsel, nth_evsel(T_TRANSACTION_START)) && + avg > 0 && + runtime_cycles_in_tx_stats[cpu].n != 0) { + total = avg_stats(&runtime_cycles_in_tx_stats[cpu]); + + if (total) + ratio = total / avg; + + fprintf(output, " # %8.0f cycles / transaction ", ratio); + } else if (transaction_run && + perf_evsel__cmp(evsel, nth_evsel(T_ELISION_START)) && + avg > 0 && + runtime_cycles_in_tx_stats[cpu].n != 0) { + total = avg_stats(&runtime_cycles_in_tx_stats[cpu]); + + if (total) + ratio = total / avg; + + fprintf(output, " # %8.0f cycles / elision ", ratio); } else if (runtime_nsecs_stats[cpu].n != 0) { char unit = 'M'; @@ -1236,6 +1350,16 @@ static int perf_stat_init_aggr_mode(void) return 0; } +static int setup_events(const char * const *attrs, unsigned len) +{ + unsigned i; + + for (i = 0; i < len; i++) { + if (parse_events(evsel_list, attrs[i])) + return -1; + } + return 0; +} /* * Add default attributes, if there were no attributes specified or @@ -1354,6 +1478,22 @@ static int add_default_attributes(void) if (null_run) return 0; + if (transaction_run) { + int err; + if (pmu_have_event("cpu", "cycles-ct") && + pmu_have_event("cpu", "el-start")) + err = setup_events(transaction_attrs, + ARRAY_SIZE(transaction_attrs)); + else + err = setup_events(transaction_limited_attrs, + ARRAY_SIZE(transaction_limited_attrs)); + if (err < 0) { + fprintf(stderr, "Cannot set up transaction events\n"); + return -1; + } + return 0; + } + if (!evsel_list->nr_entries) { if (perf_evlist__add_default_attrs(evsel_list, default_attrs) < 0) return -1; @@ -1388,6 +1528,8 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) int output_fd = 0; const char *output_name = NULL; const struct option options[] = { + OPT_BOOLEAN('T', "transaction", &transaction_run, + "hardware transaction statistics"), OPT_CALLBACK('e', "event", &evsel_list, "event", "event selector. use 'perf list' to list available events", parse_events_option), diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 4a7bdc7..5aa68cd 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -197,6 +197,12 @@ static inline bool perf_evsel__match2(struct perf_evsel *e1, (e1->attr.config == e2->attr.config); } +#define perf_evsel__cmp(a, b) \ + ((a) && \ + (b) && \ + (a)->attr.type == (b)->attr.type && \ + (a)->attr.config == (b)->attr.config) + int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, int cpu, int thread, bool scale); diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index bc9d806..64362fe 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -637,3 +637,19 @@ void print_pmu_events(const char *event_glob, bool name_only) printf("\n"); free(aliases); } + +bool pmu_have_event(const char *pname, const char *name) +{ + struct perf_pmu *pmu; + struct perf_pmu_alias *alias; + + pmu = NULL; + while ((pmu = perf_pmu__scan(pmu)) != NULL) { + if (strcmp(pname, pmu->name)) + continue; + list_for_each_entry(alias, &pmu->aliases, list) + if (!strcmp(alias->name, name)) + return true; + } + return false; +} diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 6b2cbe2..1179b26 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -42,6 +42,7 @@ int perf_pmu__format_parse(char *dir, struct list_head *head); struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); void print_pmu_events(const char *event_glob, bool name_only); +bool pmu_have_event(const char *pname, const char *name); int perf_pmu__test(void); #endif /* __PMU_H */ -- cgit v0.10.2 From fdfbbd07e91f8fe387140776f3fd94605f0c89e5 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Fri, 20 Sep 2013 07:40:39 -0700 Subject: perf: Add generic transaction flags Add a generic qualifier for transaction events, as a new sample type that returns a flag word. This is particularly useful for qualifying aborts: to distinguish aborts which happen due to asynchronous events (like conflicts caused by another CPU) versus instructions that lead to an abort. The tuning strategies are very different for those cases, so it's important to distinguish them easily and early. Since it's inconvenient and inflexible to filter for this in the kernel we report all the events out and allow some post processing in user space. The flags are based on the Intel TSX events, but should be fairly generic and mostly applicable to other HTM architectures too. In addition to various flag words there's also reserved space to report an program supplied abort code. For TSX this is used to distinguish specific classes of aborts, like a lock busy abort when doing lock elision. Flags: Elision and generic transactions (ELISION vs TRANSACTION) (HLE vs RTM on TSX; IBM etc. would likely only use TRANSACTION) Aborts caused by current thread vs aborts caused by others (SYNC vs ASYNC) Retryable transaction (RETRY) Conflicts with other threads (CONFLICT) Transaction write capacity overflow (CAPACITY WRITE) Transaction read capacity overflow (CAPACITY READ) Transactions implicitely aborted can also return an abort code. This can be used to signal specific events to the profiler. A common case is abort on lock busy in a RTM eliding library (code 0xff) To handle this case we include the TSX abort code Common example aborts in TSX would be: - Data conflict with another thread on memory read. Flags: TRANSACTION|ASYNC|CONFLICT - executing a WRMSR in a transaction. Flags: TRANSACTION|SYNC - HLE transaction in user space is too large Flags: ELISION|SYNC|CAPACITY-WRITE The only flag that is somewhat TSX specific is ELISION. This adds the perf core glue needed for reporting the new flag word out. v2: Add MEM/MISC v3: Move transaction to the end v4: Separate capacity-read/write and remove misc v5: Remove _SAMPLE. Move abort flags to 32bit. Rename transaction to txn Signed-off-by: Andi Kleen Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1379688044-14173-2-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index c8ba627..2e069d1 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -584,6 +584,10 @@ struct perf_sample_data { struct perf_regs_user regs_user; u64 stack_user_size; u64 weight; + /* + * Transaction flags for abort events: + */ + u64 txn; }; static inline void perf_sample_data_init(struct perf_sample_data *data, @@ -599,6 +603,7 @@ static inline void perf_sample_data_init(struct perf_sample_data *data, data->stack_user_size = 0; data->weight = 0; data->data_src.val = 0; + data->txn = 0; } extern void perf_output_sample(struct perf_output_handle *handle, diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 009a655..da48837 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -136,8 +136,9 @@ enum perf_event_sample_format { PERF_SAMPLE_WEIGHT = 1U << 14, PERF_SAMPLE_DATA_SRC = 1U << 15, PERF_SAMPLE_IDENTIFIER = 1U << 16, + PERF_SAMPLE_TRANSACTION = 1U << 17, - PERF_SAMPLE_MAX = 1U << 17, /* non-ABI */ + PERF_SAMPLE_MAX = 1U << 18, /* non-ABI */ }; /* @@ -181,6 +182,28 @@ enum perf_sample_regs_abi { }; /* + * Values for the memory transaction event qualifier, mostly for + * abort events. Multiple bits can be set. + */ +enum { + PERF_TXN_ELISION = (1 << 0), /* From elision */ + PERF_TXN_TRANSACTION = (1 << 1), /* From transaction */ + PERF_TXN_SYNC = (1 << 2), /* Instruction is related */ + PERF_TXN_ASYNC = (1 << 3), /* Instruction not related */ + PERF_TXN_RETRY = (1 << 4), /* Retry possible */ + PERF_TXN_CONFLICT = (1 << 5), /* Conflict abort */ + PERF_TXN_CAPACITY_WRITE = (1 << 6), /* Capacity write abort */ + PERF_TXN_CAPACITY_READ = (1 << 7), /* Capacity read abort */ + + PERF_TXN_MAX = (1 << 8), /* non-ABI */ + + /* bits 32..63 are reserved for the abort code */ + + PERF_TXN_ABORT_MASK = (0xffffffffULL << 32), + PERF_TXN_ABORT_SHIFT = 32, +}; + +/* * The format of the data returned by read() on a perf event fd, * as specified by attr.read_format: * diff --git a/kernel/events/core.c b/kernel/events/core.c index b25d65c..c716385 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -1201,6 +1201,9 @@ static void perf_event__header_size(struct perf_event *event) if (sample_type & PERF_SAMPLE_DATA_SRC) size += sizeof(data->data_src.val); + if (sample_type & PERF_SAMPLE_TRANSACTION) + size += sizeof(data->txn); + event->header_size = size; } @@ -4572,6 +4575,9 @@ void perf_output_sample(struct perf_output_handle *handle, if (sample_type & PERF_SAMPLE_DATA_SRC) perf_output_put(handle, data->data_src.val); + if (sample_type & PERF_SAMPLE_TRANSACTION) + perf_output_put(handle, data->txn); + if (!event->attr.watermark) { int wakeup_events = event->attr.wakeup_events; -- cgit v0.10.2 From a405bad5ad2086766ce320b16a56952e013327f8 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Fri, 20 Sep 2013 07:40:40 -0700 Subject: perf/x86: Add Haswell specific transaction flag reporting In the PEBS handler report the transaction flags using the new generic transaction flags facility. Most of them come from the "tsx_tuning" field in PEBSv2, but the abort code is derived from the RAX register reported in the PEBS record. Signed-off-by: Andi Kleen Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1379688044-14173-3-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index 07d9a05..32e9ed8 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -206,6 +206,8 @@ union hsw_tsx_tuning { u64 value; }; +#define PEBS_HSW_TSX_FLAGS 0xff00000000ULL + void init_debug_store_on_cpu(int cpu) { struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds; @@ -807,6 +809,16 @@ static inline u64 intel_hsw_weight(struct pebs_record_hsw *pebs) return 0; } +static inline u64 intel_hsw_transaction(struct pebs_record_hsw *pebs) +{ + u64 txn = (pebs->tsx_tuning & PEBS_HSW_TSX_FLAGS) >> 32; + + /* For RTM XABORTs also log the abort code from AX */ + if ((txn & PERF_TXN_TRANSACTION) && (pebs->ax & 1)) + txn |= ((pebs->ax >> 24) & 0xff) << PERF_TXN_ABORT_SHIFT; + return txn; +} + static void __intel_pmu_pebs_event(struct perf_event *event, struct pt_regs *iregs, void *__pebs) { @@ -885,10 +897,14 @@ static void __intel_pmu_pebs_event(struct perf_event *event, x86_pmu.intel_cap.pebs_format >= 1) data.addr = pebs->dla; - /* Only set the TSX weight when no memory weight was requested. */ - if ((event->attr.sample_type & PERF_SAMPLE_WEIGHT) && !fll && - (x86_pmu.intel_cap.pebs_format >= 2)) - data.weight = intel_hsw_weight(pebs); + if (x86_pmu.intel_cap.pebs_format >= 2) { + /* Only set the TSX weight when no memory weight. */ + if ((event->attr.sample_type & PERF_SAMPLE_WEIGHT) && !fll) + data.weight = intel_hsw_weight(pebs); + + if (event->attr.sample_type & PERF_SAMPLE_TRANSACTION) + data.txn = intel_hsw_transaction(pebs); + } if (has_branch_stack(event)) data.br_stack = &cpuc->lbr_stack; -- cgit v0.10.2 From f5d05bcec409aec2c41727077ad818f7c4db005b Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Fri, 20 Sep 2013 07:40:41 -0700 Subject: tools/perf: Support sorting by in_tx or abort branch flags Extend the perf branch sorting code to support sorting by in_tx or abort_tx qualifiers. Also print out those qualifiers. This also fixes up some of the existing sort key documentation. We do not support no_tx here, because it's simply not showing the in_tx flag. Signed-off-by: Andi Kleen Acked-by: Jiri Olsa Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1379688044-14173-4-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 2b8097e..ae337e3 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -71,7 +71,7 @@ OPTIONS entries are displayed as "[other]". - cpu: cpu number the task ran at the time of sample - srcline: filename and line number executed at the time of sample. The - DWARF debuggin info must be provided. + DWARF debugging info must be provided. By default, comm, dso and symbol keys are used. (i.e. --sort comm,dso,symbol) @@ -85,6 +85,8 @@ OPTIONS - symbol_from: name of function branched from - symbol_to: name of function branched to - mispredict: "N" for predicted branch, "Y" for mispredicted branch + - in_tx: branch in TSX transaction + - abort: TSX transaction abort. And default sort keys are changed to comm, dso_from, symbol_from, dso_to and symbol_to, see '--branch-stack'. diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index 58d6598..f852eb5 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt @@ -112,7 +112,8 @@ Default is to monitor all CPUS. -s:: --sort:: - Sort by key(s): pid, comm, dso, symbol, parent, srcline, weight, local_weight. + Sort by key(s): pid, comm, dso, symbol, parent, srcline, weight, + local_weight, abort, in_tx -n:: --show-nr-samples:: diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 72eae74..89b188d 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -787,7 +787,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline," " dso_to, dso_from, symbol_to, symbol_from, mispredict," " weight, local_weight, mem, symbol_daddr, dso_daddr, tlb, " - "snoop, locked"), + "snoop, locked, abort, in_tx"), OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, "Show sample percentage for different cpu modes"), OPT_STRING('p', "parent", &parent_pattern, "regex", diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 2122141..6534a37 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1103,7 +1103,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) OPT_INCR('v', "verbose", &verbose, "be more verbose (show counter open errors, etc)"), OPT_STRING('s', "sort", &sort_order, "key[,key2...]", - "sort by key(s): pid, comm, dso, symbol, parent, weight, local_weight"), + "sort by key(s): pid, comm, dso, symbol, parent, weight, local_weight," + " abort, in_tx"), OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, "Show a column with the number of samples"), OPT_CALLBACK_DEFAULT('G', "call-graph", &top.record_opts, diff --git a/tools/perf/perf.h b/tools/perf/perf.h index cf20187..acf3d66 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -182,7 +182,9 @@ struct ip_callchain { struct branch_flags { u64 mispred:1; u64 predicted:1; - u64 reserved:62; + u64 in_tx:1; + u64 abort:1; + u64 reserved:60; }; struct branch_entry { diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 1329b6b..f743e96 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -45,6 +45,8 @@ enum hist_column { HISTC_CPU, HISTC_SRCLINE, HISTC_MISPREDICT, + HISTC_IN_TX, + HISTC_ABORT, HISTC_SYMBOL_FROM, HISTC_SYMBOL_TO, HISTC_DSO_FROM, diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 5f118a0..1771566 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -858,6 +858,55 @@ struct sort_entry sort_mem_snoop = { .se_width_idx = HISTC_MEM_SNOOP, }; +static int64_t +sort__abort_cmp(struct hist_entry *left, struct hist_entry *right) +{ + return left->branch_info->flags.abort != + right->branch_info->flags.abort; +} + +static int hist_entry__abort_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) +{ + static const char *out = "."; + + if (self->branch_info->flags.abort) + out = "A"; + return repsep_snprintf(bf, size, "%-*s", width, out); +} + +struct sort_entry sort_abort = { + .se_header = "Transaction abort", + .se_cmp = sort__abort_cmp, + .se_snprintf = hist_entry__abort_snprintf, + .se_width_idx = HISTC_ABORT, +}; + +static int64_t +sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right) +{ + return left->branch_info->flags.in_tx != + right->branch_info->flags.in_tx; +} + +static int hist_entry__in_tx_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) +{ + static const char *out = "."; + + if (self->branch_info->flags.in_tx) + out = "T"; + + return repsep_snprintf(bf, size, "%-*s", width, out); +} + +struct sort_entry sort_in_tx = { + .se_header = "Branch in transaction", + .se_cmp = sort__in_tx_cmp, + .se_snprintf = hist_entry__in_tx_snprintf, + .se_width_idx = HISTC_IN_TX, +}; + struct sort_dimension { const char *name; struct sort_entry *entry; @@ -888,6 +937,8 @@ static struct sort_dimension bstack_sort_dimensions[] = { DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from), DIM(SORT_SYM_TO, "symbol_to", sort_sym_to), DIM(SORT_MISPREDICT, "mispredict", sort_mispredict), + DIM(SORT_IN_TX, "in_tx", sort_in_tx), + DIM(SORT_ABORT, "abort", sort_abort), }; #undef DIM diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 4e80dbd..9dad3a0 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -153,6 +153,8 @@ enum sort_type { SORT_SYM_FROM, SORT_SYM_TO, SORT_MISPREDICT, + SORT_ABORT, + SORT_IN_TX, /* memory mode specific sort keys */ __SORT_MEMORY_MODE, -- cgit v0.10.2 From 0126d493b62e1306db09e1019c05e0bfe84ae8e7 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Fri, 20 Sep 2013 07:40:42 -0700 Subject: tools/perf/record: Add abort_tx,no_tx,in_tx branch filter options to perf record -j Make perf record -j aware of the new in_tx,no_tx,abort_tx branch qualifiers. Signed-off-by: Andi Kleen Acked-by: Jiri Olsa Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1379688044-14173-5-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index e297b74..6bec1c9 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -166,6 +166,9 @@ following filters are defined: - u: only when the branch target is at the user level - k: only when the branch target is in the kernel - hv: only when the target is at the hypervisor level + - in_tx: only when the target is in a hardware transaction + - no_tx: only when the target is not in a hardware transaction + - abort_tx: only when the target is a hardware transaction abort + The option requires at least one branch type among any, any_call, any_ret, ind_call. diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index a41ac415..8384b54 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -618,6 +618,9 @@ static const struct branch_mode branch_modes[] = { BRANCH_OPT("any_call", PERF_SAMPLE_BRANCH_ANY_CALL), BRANCH_OPT("any_ret", PERF_SAMPLE_BRANCH_ANY_RETURN), BRANCH_OPT("ind_call", PERF_SAMPLE_BRANCH_IND_CALL), + BRANCH_OPT("abort_tx", PERF_SAMPLE_BRANCH_ABORT_TX), + BRANCH_OPT("in_tx", PERF_SAMPLE_BRANCH_IN_TX), + BRANCH_OPT("no_tx", PERF_SAMPLE_BRANCH_NO_TX), BRANCH_END }; -- cgit v0.10.2 From 475eeab9f3c1579c8da89667496084db4867bf7c Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Fri, 20 Sep 2013 07:40:43 -0700 Subject: tools/perf: Add support for record transaction flags Add support for recording and displaying the transaction flags. They are essentially a new sort key. Also display them in a nice way to the user. Signed-off-by: Andi Kleen Acked-by: Jiri Olsa Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1379688044-14173-6-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 6bec1c9..f732eaa 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -179,12 +179,14 @@ is enabled for all the sampling events. The sampled branch type is the same for The various filters must be specified as a comma separated list: --branch-filter any_ret,u,k Note that this feature may not be available on all processors. --W:: --weight:: Enable weightened sampling. An additional weight is recorded per sample and can be displayed with the weight and local_weight sort keys. This currently works for TSX abort events and some memory events in precise mode on modern Intel CPUs. +--transaction:: +Record transaction flags for transaction related events. + SEE ALSO -------- linkperf:perf-stat[1], linkperf:perf-list[1] diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index ae337e3..be5ad87 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -72,6 +72,10 @@ OPTIONS - cpu: cpu number the task ran at the time of sample - srcline: filename and line number executed at the time of sample. The DWARF debugging info must be provided. + - weight: Event specific weight, e.g. memory latency or transaction + abort cost. This is the global weight. + - local_weight: Local weight version of the weight above. + - transaction: Transaction abort flags. By default, comm, dso and symbol keys are used. (i.e. --sort comm,dso,symbol) diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index f852eb5..6d70fbf 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt @@ -113,7 +113,7 @@ Default is to monitor all CPUS. -s:: --sort:: Sort by key(s): pid, comm, dso, symbol, parent, srcline, weight, - local_weight, abort, in_tx + local_weight, abort, in_tx, transaction -n:: --show-nr-samples:: diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 5ebd0c3..0393d98 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -63,7 +63,7 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel, return 0; } - he = __hists__add_entry(&evsel->hists, al, NULL, 1, 1); + he = __hists__add_entry(&evsel->hists, al, NULL, 1, 1, 0); if (he == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index f28799e..2a78dc8 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -304,9 +304,10 @@ static int formula_fprintf(struct hist_entry *he, struct hist_entry *pair, static int hists__add_entry(struct hists *self, struct addr_location *al, u64 period, - u64 weight) + u64 weight, u64 transaction) { - if (__hists__add_entry(self, al, NULL, period, weight) != NULL) + if (__hists__add_entry(self, al, NULL, period, weight, transaction) + != NULL) return 0; return -ENOMEM; } @@ -328,7 +329,8 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused, if (al.filtered) return 0; - if (hists__add_entry(&evsel->hists, &al, sample->period, sample->weight)) { + if (hists__add_entry(&evsel->hists, &al, sample->period, + sample->weight, sample->transaction)) { pr_warning("problem incrementing symbol period, skipping event\n"); return -1; } diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 8384b54..a78db3f 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -894,6 +894,8 @@ const struct option record_options[] = { parse_branch_stack), OPT_BOOLEAN('W', "weight", &record.opts.sample_weight, "sample by weight (on special events only)"), + OPT_BOOLEAN(0, "transaction", &record.opts.sample_transaction, + "sample transaction flags (special events only)"), OPT_END() }; diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 89b188d..06e1abe 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -259,7 +259,7 @@ static int perf_evsel__add_hist_entry(struct perf_evsel *evsel, } he = __hists__add_entry(&evsel->hists, al, parent, sample->period, - sample->weight); + sample->weight, sample->transaction); if (he == NULL) return -ENOMEM; @@ -787,7 +787,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline," " dso_to, dso_from, symbol_to, symbol_from, mispredict," " weight, local_weight, mem, symbol_daddr, dso_daddr, tlb, " - "snoop, locked, abort, in_tx"), + "snoop, locked, abort, in_tx, transaction"), OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, "Show sample percentage for different cpu modes"), OPT_STRING('p', "parent", &parent_pattern, "regex", diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 6534a37..b3e0229 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -247,9 +247,8 @@ static struct hist_entry *perf_evsel__add_hist_entry(struct perf_evsel *evsel, pthread_mutex_lock(&evsel->hists.lock); he = __hists__add_entry(&evsel->hists, al, NULL, sample->period, - sample->weight); + sample->weight, sample->transaction); pthread_mutex_unlock(&evsel->hists.lock); - if (he == NULL) return NULL; @@ -1104,7 +1103,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) "be more verbose (show counter open errors, etc)"), OPT_STRING('s', "sort", &sort_order, "key[,key2...]", "sort by key(s): pid, comm, dso, symbol, parent, weight, local_weight," - " abort, in_tx"), + " abort, in_tx, transaction"), OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, "Show a column with the number of samples"), OPT_CALLBACK_DEFAULT('G', "call-graph", &top.record_opts, diff --git a/tools/perf/perf.h b/tools/perf/perf.h index acf3d66..84502e8 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -233,6 +233,7 @@ struct perf_record_opts { u64 default_interval; u64 user_interval; u16 stack_dump_size; + bool sample_transaction; }; #endif diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index 4228ffc..025503a 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c @@ -222,7 +222,8 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) &sample) < 0) goto out; - he = __hists__add_entry(&evsel->hists, &al, NULL, 1, 1); + he = __hists__add_entry(&evsel->hists, &al, NULL, + 1, 1, 0); if (he == NULL) goto out; @@ -244,7 +245,8 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) &sample) < 0) goto out; - he = __hists__add_entry(&evsel->hists, &al, NULL, 1, 1); + he = __hists__add_entry(&evsel->hists, &al, NULL, 1, 1, + 0); if (he == NULL) goto out; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index c67ecc4..17d9e16 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -111,6 +111,7 @@ struct perf_sample { u64 stream_id; u64 period; u64 weight; + u64 transaction; u32 cpu; u32 raw_size; u64 data_src; diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 0ce9feb..abe69af 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -681,6 +681,9 @@ void perf_evsel__config(struct perf_evsel *evsel, attr->mmap2 = track && !perf_missing_features.mmap2; attr->comm = track; + if (opts->sample_transaction) + attr->sample_type |= PERF_SAMPLE_TRANSACTION; + /* * XXX see the function comment above * @@ -1470,6 +1473,12 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, array++; } + data->transaction = 0; + if (type & PERF_SAMPLE_TRANSACTION) { + data->transaction = *array; + array++; + } + return 0; } diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 97dc280..f3278a3 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -160,6 +160,10 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) hists__new_col_len(hists, HISTC_MEM_LVL, 21 + 3); hists__new_col_len(hists, HISTC_LOCAL_WEIGHT, 12); hists__new_col_len(hists, HISTC_GLOBAL_WEIGHT, 12); + + if (h->transaction) + hists__new_col_len(hists, HISTC_TRANSACTION, + hist_entry__transaction_len()); } void hists__output_recalc_col_len(struct hists *hists, int max_rows) @@ -466,7 +470,7 @@ struct hist_entry *__hists__add_branch_entry(struct hists *self, struct hist_entry *__hists__add_entry(struct hists *self, struct addr_location *al, struct symbol *sym_parent, u64 period, - u64 weight) + u64 weight, u64 transaction) { struct hist_entry entry = { .thread = al->thread, @@ -487,6 +491,7 @@ struct hist_entry *__hists__add_entry(struct hists *self, .hists = self, .branch_info = NULL, .mem_info = NULL, + .transaction = transaction, }; return add_hist_entry(self, &entry, al, period, weight); diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index f743e96..6a048c0 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -59,6 +59,7 @@ enum hist_column { HISTC_MEM_TLB, HISTC_MEM_LVL, HISTC_MEM_SNOOP, + HISTC_TRANSACTION, HISTC_NR_COLS, /* Last entry */ }; @@ -84,9 +85,10 @@ struct hists { struct hist_entry *__hists__add_entry(struct hists *self, struct addr_location *al, struct symbol *parent, u64 period, - u64 weight); + u64 weight, u64 transaction); int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right); int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); +int hist_entry__transaction_len(void); int hist_entry__sort_snprintf(struct hist_entry *self, char *bf, size_t size, struct hists *hists); void hist_entry__free(struct hist_entry *); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 70ffa41..211b325 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -858,6 +858,9 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event, if (sample_type & PERF_SAMPLE_DATA_SRC) printf(" . data_src: 0x%"PRIx64"\n", sample->data_src); + if (sample_type & PERF_SAMPLE_TRANSACTION) + printf("... transaction: %" PRIx64 "\n", sample->transaction); + if (sample_type & PERF_SAMPLE_READ) sample_read__printf(sample, evsel->attr.read_format); } diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 1771566..b4ecc0e 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -907,6 +907,78 @@ struct sort_entry sort_in_tx = { .se_width_idx = HISTC_IN_TX, }; +static int64_t +sort__transaction_cmp(struct hist_entry *left, struct hist_entry *right) +{ + return left->transaction - right->transaction; +} + +static inline char *add_str(char *p, const char *str) +{ + strcpy(p, str); + return p + strlen(str); +} + +static struct txbit { + unsigned flag; + const char *name; + int skip_for_len; +} txbits[] = { + { PERF_TXN_ELISION, "EL ", 0 }, + { PERF_TXN_TRANSACTION, "TX ", 1 }, + { PERF_TXN_SYNC, "SYNC ", 1 }, + { PERF_TXN_ASYNC, "ASYNC ", 0 }, + { PERF_TXN_RETRY, "RETRY ", 0 }, + { PERF_TXN_CONFLICT, "CON ", 0 }, + { PERF_TXN_CAPACITY_WRITE, "CAP-WRITE ", 1 }, + { PERF_TXN_CAPACITY_READ, "CAP-READ ", 0 }, + { 0, NULL, 0 } +}; + +int hist_entry__transaction_len(void) +{ + int i; + int len = 0; + + for (i = 0; txbits[i].name; i++) { + if (!txbits[i].skip_for_len) + len += strlen(txbits[i].name); + } + len += 4; /* :XX */ + return len; +} + +static int hist_entry__transaction_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) +{ + u64 t = self->transaction; + char buf[128]; + char *p = buf; + int i; + + buf[0] = 0; + for (i = 0; txbits[i].name; i++) + if (txbits[i].flag & t) + p = add_str(p, txbits[i].name); + if (t && !(t & (PERF_TXN_SYNC|PERF_TXN_ASYNC))) + p = add_str(p, "NEITHER "); + if (t & PERF_TXN_ABORT_MASK) { + sprintf(p, ":%" PRIx64, + (t & PERF_TXN_ABORT_MASK) >> + PERF_TXN_ABORT_SHIFT); + p += strlen(p); + } + + return repsep_snprintf(bf, size, "%-*s", width, buf); +} + +struct sort_entry sort_transaction = { + .se_header = "Transaction ", + .se_cmp = sort__transaction_cmp, + .se_snprintf = hist_entry__transaction_snprintf, + .se_width_idx = HISTC_TRANSACTION, +}; + struct sort_dimension { const char *name; struct sort_entry *entry; @@ -925,6 +997,7 @@ static struct sort_dimension common_sort_dimensions[] = { DIM(SORT_SRCLINE, "srcline", sort_srcline), DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight), DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight), + DIM(SORT_TRANSACTION, "transaction", sort_transaction), }; #undef DIM diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 9dad3a0..bf43336 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -85,6 +85,7 @@ struct hist_entry { struct map_symbol ms; struct thread *thread; u64 ip; + u64 transaction; s32 cpu; struct hist_entry_diff diff; @@ -145,6 +146,7 @@ enum sort_type { SORT_SRCLINE, SORT_LOCAL_WEIGHT, SORT_GLOBAL_WEIGHT, + SORT_TRANSACTION, /* branch stack specific sort keys */ __SORT_BRANCH_STACK, -- cgit v0.10.2 From b7af41a1bc255c0098c37a4bcf5c7e5e168ce875 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Fri, 20 Sep 2013 07:40:44 -0700 Subject: perf/x86: Suppress duplicated abort LBR records Haswell always give an extra LBR record after every TSX abort. Suppress the extra record. This only works when the abort is visible in the LBR If the original abort has already left the 16 LBR entries the extra entry will will stay. Signed-off-by: Andi Kleen Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1379688044-14173-7-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h index ce84ede..fd00bb2 100644 --- a/arch/x86/kernel/cpu/perf_event.h +++ b/arch/x86/kernel/cpu/perf_event.h @@ -445,6 +445,7 @@ struct x86_pmu { int lbr_nr; /* hardware stack size */ u64 lbr_sel_mask; /* LBR_SELECT valid bits */ const int *lbr_sel_map; /* lbr_select mappings */ + bool lbr_double_abort; /* duplicated lbr aborts */ /* * Extra registers for events diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 36b5ab8..0fa4f24 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -2519,6 +2519,7 @@ __init int intel_pmu_init(void) x86_pmu.hw_config = hsw_hw_config; x86_pmu.get_event_constraints = hsw_get_event_constraints; x86_pmu.cpu_events = hsw_events_attrs; + x86_pmu.lbr_double_abort = true; pr_cont("Haswell events, "); break; diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/arch/x86/kernel/cpu/perf_event_intel_lbr.c index d5be06a..90ee6c1 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_lbr.c +++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c @@ -284,6 +284,7 @@ static void intel_pmu_lbr_read_64(struct cpu_hw_events *cpuc) int lbr_format = x86_pmu.intel_cap.lbr_format; u64 tos = intel_pmu_lbr_tos(); int i; + int out = 0; for (i = 0; i < x86_pmu.lbr_nr; i++) { unsigned long lbr_idx = (tos - i) & mask; @@ -306,15 +307,27 @@ static void intel_pmu_lbr_read_64(struct cpu_hw_events *cpuc) } from = (u64)((((s64)from) << skip) >> skip); - cpuc->lbr_entries[i].from = from; - cpuc->lbr_entries[i].to = to; - cpuc->lbr_entries[i].mispred = mis; - cpuc->lbr_entries[i].predicted = pred; - cpuc->lbr_entries[i].in_tx = in_tx; - cpuc->lbr_entries[i].abort = abort; - cpuc->lbr_entries[i].reserved = 0; + /* + * Some CPUs report duplicated abort records, + * with the second entry not having an abort bit set. + * Skip them here. This loop runs backwards, + * so we need to undo the previous record. + * If the abort just happened outside the window + * the extra entry cannot be removed. + */ + if (abort && x86_pmu.lbr_double_abort && out > 0) + out--; + + cpuc->lbr_entries[out].from = from; + cpuc->lbr_entries[out].to = to; + cpuc->lbr_entries[out].mispred = mis; + cpuc->lbr_entries[out].predicted = pred; + cpuc->lbr_entries[out].in_tx = in_tx; + cpuc->lbr_entries[out].abort = abort; + cpuc->lbr_entries[out].reserved = 0; + out++; } - cpuc->lbr_stack.nr = i; + cpuc->lbr_stack.nr = out; } void intel_pmu_lbr_read(void) -- cgit v0.10.2 From 2f2a2b60adf368bacd6acd2116c01e32caf936c4 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 2 Oct 2013 11:22:18 +0200 Subject: sched/wait: Make the signal_pending() checks consistent There's two patterns to check signals in the __wait_event*() macros: if (!signal_pending(current)) { schedule(); continue; } ret = -ERESTARTSYS; break; And the more natural: if (signal_pending(current)) { ret = -ERESTARTSYS; break; } schedule(); Change them all into the latter form. Reviewed-by: Oleg Nesterov Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20131002092527.956416254@infradead.org Signed-off-by: Ingo Molnar diff --git a/include/linux/tty.h b/include/linux/tty.h index 64f8646..0503729 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -686,14 +686,13 @@ do { \ prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \ if (condition) \ break; \ - if (!signal_pending(current)) { \ - tty_unlock(tty); \ - schedule(); \ - tty_lock(tty); \ - continue; \ + if (signal_pending(current)) { \ + ret = -ERESTARTSYS; \ + break; \ } \ - ret = -ERESTARTSYS; \ - break; \ + tty_unlock(tty); \ + schedule(); \ + tty_lock(tty); \ } \ finish_wait(&wq, &__wait); \ } while (0) diff --git a/include/linux/wait.h b/include/linux/wait.h index a67fc16..ccf0c52 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -261,12 +261,11 @@ do { \ prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \ if (condition) \ break; \ - if (!signal_pending(current)) { \ - schedule(); \ - continue; \ + if (signal_pending(current)) { \ + ret = -ERESTARTSYS; \ + break; \ } \ - ret = -ERESTARTSYS; \ - break; \ + schedule(); \ } \ finish_wait(&wq, &__wait); \ } while (0) @@ -302,14 +301,13 @@ do { \ prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \ if (condition) \ break; \ - if (!signal_pending(current)) { \ - ret = schedule_timeout(ret); \ - if (!ret) \ - break; \ - continue; \ + if (signal_pending(current)) { \ + ret = -ERESTARTSYS; \ + break; \ } \ - ret = -ERESTARTSYS; \ - break; \ + ret = schedule_timeout(ret); \ + if (!ret) \ + break; \ } \ if (!ret && (condition)) \ ret = 1; \ @@ -439,14 +437,13 @@ do { \ finish_wait(&wq, &__wait); \ break; \ } \ - if (!signal_pending(current)) { \ - schedule(); \ - continue; \ - } \ - ret = -ERESTARTSYS; \ - abort_exclusive_wait(&wq, &__wait, \ + if (signal_pending(current)) { \ + ret = -ERESTARTSYS; \ + abort_exclusive_wait(&wq, &__wait, \ TASK_INTERRUPTIBLE, NULL); \ - break; \ + break; \ + } \ + schedule(); \ } \ } while (0) -- cgit v0.10.2 From 2953ef246b058989657e1e77b36b67566ac06f7b Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 2 Oct 2013 11:22:19 +0200 Subject: sched/wait: Change timeout logic Commit 4c663cf ("wait: fix false timeouts when using wait_event_timeout()") introduced an additional condition check after a timeout but there's a few issues; - it forgot one site - it put the check after the main loop; not at the actual timeout check. Cure both; by wrapping the condition (as suggested by Oleg), this avoids double evaluation of 'condition' which could be quite big. Reviewed-by: Oleg Nesterov Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20131002092528.028892896@infradead.org Signed-off-by: Ingo Molnar diff --git a/include/linux/wait.h b/include/linux/wait.h index ccf0c52..b2afd66 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -179,6 +179,14 @@ wait_queue_head_t *bit_waitqueue(void *, int); #define wake_up_interruptible_sync_poll(x, m) \ __wake_up_sync_key((x), TASK_INTERRUPTIBLE, 1, (void *) (m)) +#define ___wait_cond_timeout(condition, ret) \ +({ \ + bool __cond = (condition); \ + if (__cond && !ret) \ + ret = 1; \ + __cond || !ret; \ +}) + #define __wait_event(wq, condition) \ do { \ DEFINE_WAIT(__wait); \ @@ -217,14 +225,10 @@ do { \ \ for (;;) { \ prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE); \ - if (condition) \ + if (___wait_cond_timeout(condition, ret)) \ break; \ ret = schedule_timeout(ret); \ - if (!ret) \ - break; \ } \ - if (!ret && (condition)) \ - ret = 1; \ finish_wait(&wq, &__wait); \ } while (0) @@ -299,18 +303,14 @@ do { \ \ for (;;) { \ prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \ - if (condition) \ + if (___wait_cond_timeout(condition, ret)) \ break; \ if (signal_pending(current)) { \ ret = -ERESTARTSYS; \ break; \ } \ ret = schedule_timeout(ret); \ - if (!ret) \ - break; \ } \ - if (!ret && (condition)) \ - ret = 1; \ finish_wait(&wq, &__wait); \ } while (0) @@ -815,7 +815,7 @@ do { \ \ for (;;) { \ prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \ - if (condition) \ + if (___wait_cond_timeout(condition, ret)) \ break; \ if (signal_pending(current)) { \ ret = -ERESTARTSYS; \ @@ -824,8 +824,6 @@ do { \ spin_unlock_irq(&lock); \ ret = schedule_timeout(ret); \ spin_lock_irq(&lock); \ - if (!ret) \ - break; \ } \ finish_wait(&wq, &__wait); \ } while (0) -- cgit v0.10.2 From bb632bc44970f75b66df102e831a4fc0692e9159 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 2 Oct 2013 11:22:20 +0200 Subject: sched/wait: Change the wait_exclusive control flow Purely a preparatory patch; it changes the control flow to match what will soon be generated by generic code so that that patch can be a unity transform. Reviewed-by: Oleg Nesterov Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20131002092528.107994763@infradead.org Signed-off-by: Ingo Molnar diff --git a/include/linux/wait.h b/include/linux/wait.h index b2afd66..7d7819d 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -428,23 +428,24 @@ do { \ #define __wait_event_interruptible_exclusive(wq, condition, ret) \ do { \ + __label__ __out; \ DEFINE_WAIT(__wait); \ \ for (;;) { \ prepare_to_wait_exclusive(&wq, &__wait, \ TASK_INTERRUPTIBLE); \ - if (condition) { \ - finish_wait(&wq, &__wait); \ + if (condition) \ break; \ - } \ if (signal_pending(current)) { \ ret = -ERESTARTSYS; \ abort_exclusive_wait(&wq, &__wait, \ TASK_INTERRUPTIBLE, NULL); \ - break; \ + goto __out; \ } \ schedule(); \ } \ + finish_wait(&wq, &__wait); \ +__out: ; \ } while (0) #define wait_event_interruptible_exclusive(wq, condition) \ -- cgit v0.10.2 From 41a1431b178c3b731d6dfc40b987528b333dd93e Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 2 Oct 2013 11:22:21 +0200 Subject: sched/wait: Introduce ___wait_event() There's far too much duplication in the __wait_event macros; in order to fix this introduce ___wait_event() a macro with the capability to replace most other macros. With the previous patches changing the various __wait_event*() implementations to be more uniform; we can now collapse the lot without also changing generated code. Reviewed-by: Oleg Nesterov Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20131002092528.181897111@infradead.org Signed-off-by: Ingo Molnar diff --git a/include/linux/wait.h b/include/linux/wait.h index 7d7819d..29d0249 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -187,6 +187,42 @@ wait_queue_head_t *bit_waitqueue(void *, int); __cond || !ret; \ }) +#define ___wait_signal_pending(state) \ + ((state == TASK_INTERRUPTIBLE && signal_pending(current)) || \ + (state == TASK_KILLABLE && fatal_signal_pending(current))) + +#define ___wait_nop_ret int ret __always_unused + +#define ___wait_event(wq, condition, state, exclusive, ret, cmd) \ +do { \ + __label__ __out; \ + DEFINE_WAIT(__wait); \ + \ + for (;;) { \ + if (exclusive) \ + prepare_to_wait_exclusive(&wq, &__wait, state); \ + else \ + prepare_to_wait(&wq, &__wait, state); \ + \ + if (condition) \ + break; \ + \ + if (___wait_signal_pending(state)) { \ + ret = -ERESTARTSYS; \ + if (exclusive) { \ + abort_exclusive_wait(&wq, &__wait, \ + state, NULL); \ + goto __out; \ + } \ + break; \ + } \ + \ + cmd; \ + } \ + finish_wait(&wq, &__wait); \ +__out: ; \ +} while (0) + #define __wait_event(wq, condition) \ do { \ DEFINE_WAIT(__wait); \ -- cgit v0.10.2 From 854267f4384243b19c03a2942e84f06f2beb0952 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 2 Oct 2013 11:22:22 +0200 Subject: sched/wait: Collapse __wait_event() Reduce macro complexity by using the new ___wait_event() helper. No change in behaviour, identical generated code. Reviewed-by: Oleg Nesterov Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20131002092528.254863348@infradead.org Signed-off-by: Ingo Molnar diff --git a/include/linux/wait.h b/include/linux/wait.h index 29d0249..68e3a62 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -224,17 +224,8 @@ __out: ; \ } while (0) #define __wait_event(wq, condition) \ -do { \ - DEFINE_WAIT(__wait); \ - \ - for (;;) { \ - prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE); \ - if (condition) \ - break; \ - schedule(); \ - } \ - finish_wait(&wq, &__wait); \ -} while (0) + ___wait_event(wq, condition, TASK_UNINTERRUPTIBLE, 0, \ + ___wait_nop_ret, schedule()) /** * wait_event - sleep until a condition gets true -- cgit v0.10.2 From ddc1994b8217527e1818f690f17597fc9cedf81b Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 2 Oct 2013 11:22:23 +0200 Subject: sched/wait: Collapse __wait_event_timeout() Reduce macro complexity by using the new ___wait_event() helper. No change in behaviour, identical generated code. Reviewed-by: Oleg Nesterov Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20131002092528.325264677@infradead.org Signed-off-by: Ingo Molnar diff --git a/include/linux/wait.h b/include/linux/wait.h index 68e3a62..546b94e 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -247,17 +247,9 @@ do { \ } while (0) #define __wait_event_timeout(wq, condition, ret) \ -do { \ - DEFINE_WAIT(__wait); \ - \ - for (;;) { \ - prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE); \ - if (___wait_cond_timeout(condition, ret)) \ - break; \ - ret = schedule_timeout(ret); \ - } \ - finish_wait(&wq, &__wait); \ -} while (0) + ___wait_event(wq, ___wait_cond_timeout(condition, ret), \ + TASK_UNINTERRUPTIBLE, 0, ret, \ + ret = schedule_timeout(ret)) /** * wait_event_timeout - sleep until a condition gets true or a timeout elapses -- cgit v0.10.2 From f13f4c41c9cf9cd61c896e46e4e7ba2687e2af9c Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 2 Oct 2013 11:22:24 +0200 Subject: sched/wait: Collapse __wait_event_interruptible() Reduce macro complexity by using the new ___wait_event() helper. No change in behaviour, identical generated code. Reviewed-by: Oleg Nesterov Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20131002092528.396949919@infradead.org Signed-off-by: Ingo Molnar diff --git a/include/linux/wait.h b/include/linux/wait.h index 546b94e..39e4bbd 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -277,21 +277,8 @@ do { \ }) #define __wait_event_interruptible(wq, condition, ret) \ -do { \ - DEFINE_WAIT(__wait); \ - \ - for (;;) { \ - prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \ - if (condition) \ - break; \ - if (signal_pending(current)) { \ - ret = -ERESTARTSYS; \ - break; \ - } \ - schedule(); \ - } \ - finish_wait(&wq, &__wait); \ -} while (0) + ___wait_event(wq, condition, TASK_INTERRUPTIBLE, 0, ret, \ + schedule()) /** * wait_event_interruptible - sleep until a condition gets true -- cgit v0.10.2 From c2ebb1fb4eddf3d1d66fe31d1e89e83ee211b81c Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 2 Oct 2013 11:22:25 +0200 Subject: sched/wait: Collapse __wait_event_interruptible_timeout() Reduce macro complexity by using the new ___wait_event() helper. No change in behaviour, identical generated code. Reviewed-by: Oleg Nesterov Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20131002092528.469616907@infradead.org Signed-off-by: Ingo Molnar diff --git a/include/linux/wait.h b/include/linux/wait.h index 39e4bbd..a79fb15 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -304,21 +304,9 @@ do { \ }) #define __wait_event_interruptible_timeout(wq, condition, ret) \ -do { \ - DEFINE_WAIT(__wait); \ - \ - for (;;) { \ - prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \ - if (___wait_cond_timeout(condition, ret)) \ - break; \ - if (signal_pending(current)) { \ - ret = -ERESTARTSYS; \ - break; \ - } \ - ret = schedule_timeout(ret); \ - } \ - finish_wait(&wq, &__wait); \ -} while (0) + ___wait_event(wq, ___wait_cond_timeout(condition, ret), \ + TASK_INTERRUPTIBLE, 0, ret, \ + ret = schedule_timeout(ret)) /** * wait_event_interruptible_timeout - sleep until a condition gets true or a timeout elapses -- cgit v0.10.2 From 48c2521717b39cb6904941ec2847d9775669207a Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 2 Oct 2013 11:22:26 +0200 Subject: sched/wait: Collapse __wait_event_interruptible_exclusive() Reduce macro complexity by using the new ___wait_event() helper. No change in behaviour, identical generated code. Reviewed-by: Oleg Nesterov Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20131002092528.541716442@infradead.org Signed-off-by: Ingo Molnar diff --git a/include/linux/wait.h b/include/linux/wait.h index a79fb15..c4ab172 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -421,26 +421,8 @@ do { \ }) #define __wait_event_interruptible_exclusive(wq, condition, ret) \ -do { \ - __label__ __out; \ - DEFINE_WAIT(__wait); \ - \ - for (;;) { \ - prepare_to_wait_exclusive(&wq, &__wait, \ - TASK_INTERRUPTIBLE); \ - if (condition) \ - break; \ - if (signal_pending(current)) { \ - ret = -ERESTARTSYS; \ - abort_exclusive_wait(&wq, &__wait, \ - TASK_INTERRUPTIBLE, NULL); \ - goto __out; \ - } \ - schedule(); \ - } \ - finish_wait(&wq, &__wait); \ -__out: ; \ -} while (0) + ___wait_event(wq, condition, TASK_INTERRUPTIBLE, 1, ret, \ + schedule()) #define wait_event_interruptible_exclusive(wq, condition) \ ({ \ -- cgit v0.10.2 From 13cb5042a4b80396f77cf5d599d2c002c57b89dc Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 2 Oct 2013 11:22:27 +0200 Subject: sched/wait: Collapse __wait_event_lock_irq() Reduce macro complexity by using the new ___wait_event() helper. No change in behaviour, identical generated code. Reviewed-by: Oleg Nesterov Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20131002092528.612813379@infradead.org Signed-off-by: Ingo Molnar diff --git a/include/linux/wait.h b/include/linux/wait.h index c4ab172..d64918e 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -624,20 +624,12 @@ do { \ #define __wait_event_lock_irq(wq, condition, lock, cmd) \ -do { \ - DEFINE_WAIT(__wait); \ - \ - for (;;) { \ - prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE); \ - if (condition) \ - break; \ - spin_unlock_irq(&lock); \ - cmd; \ - schedule(); \ - spin_lock_irq(&lock); \ - } \ - finish_wait(&wq, &__wait); \ -} while (0) + ___wait_event(wq, condition, TASK_UNINTERRUPTIBLE, 0, \ + ___wait_nop_ret, \ + spin_unlock_irq(&lock); \ + cmd; \ + schedule(); \ + spin_lock_irq(&lock)) /** * wait_event_lock_irq_cmd - sleep until a condition gets true. The -- cgit v0.10.2 From 8fbd88fa1717601ef91ced49a32f24786b167065 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 2 Oct 2013 11:22:28 +0200 Subject: sched/wait: Collapse __wait_event_interruptible_lock_irq() Reduce macro complexity by using the new ___wait_event() helper. No change in behaviour, identical generated code. Reviewed-by: Oleg Nesterov Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20131002092528.686006009@infradead.org Signed-off-by: Ingo Molnar diff --git a/include/linux/wait.h b/include/linux/wait.h index d64918e..a577a85 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -689,26 +689,12 @@ do { \ } while (0) -#define __wait_event_interruptible_lock_irq(wq, condition, \ - lock, ret, cmd) \ -do { \ - DEFINE_WAIT(__wait); \ - \ - for (;;) { \ - prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \ - if (condition) \ - break; \ - if (signal_pending(current)) { \ - ret = -ERESTARTSYS; \ - break; \ - } \ - spin_unlock_irq(&lock); \ - cmd; \ - schedule(); \ - spin_lock_irq(&lock); \ - } \ - finish_wait(&wq, &__wait); \ -} while (0) +#define __wait_event_interruptible_lock_irq(wq, condition, lock, ret, cmd) \ + ___wait_event(wq, condition, TASK_INTERRUPTIBLE, 0, ret, \ + spin_unlock_irq(&lock); \ + cmd; \ + schedule(); \ + spin_lock_irq(&lock)) /** * wait_event_interruptible_lock_irq_cmd - sleep until a condition gets true. -- cgit v0.10.2 From a1dc6852ac5eecdcd3122ae01703183a3e88e979 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 2 Oct 2013 11:22:29 +0200 Subject: sched/wait: Collapse __wait_event_interruptible_lock_irq_timeout() Reduce macro complexity by using the new ___wait_event() helper. No change in behaviour, identical generated code. Reviewed-by: Oleg Nesterov Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20131002092528.759956109@infradead.org Signed-off-by: Ingo Molnar diff --git a/include/linux/wait.h b/include/linux/wait.h index a577a85..5d5408b 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -763,25 +763,12 @@ do { \ __ret; \ }) -#define __wait_event_interruptible_lock_irq_timeout(wq, condition, \ - lock, ret) \ -do { \ - DEFINE_WAIT(__wait); \ - \ - for (;;) { \ - prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \ - if (___wait_cond_timeout(condition, ret)) \ - break; \ - if (signal_pending(current)) { \ - ret = -ERESTARTSYS; \ - break; \ - } \ - spin_unlock_irq(&lock); \ - ret = schedule_timeout(ret); \ - spin_lock_irq(&lock); \ - } \ - finish_wait(&wq, &__wait); \ -} while (0) +#define __wait_event_interruptible_lock_irq_timeout(wq, condition, lock, ret) \ + ___wait_event(wq, ___wait_cond_timeout(condition, ret), \ + TASK_INTERRUPTIBLE, 0, ret, \ + spin_unlock_irq(&lock); \ + ret = schedule_timeout(ret); \ + spin_lock_irq(&lock)); /** * wait_event_interruptible_lock_irq_timeout - sleep until a condition gets true or a timeout elapses. -- cgit v0.10.2 From 0d1e1c8a430450a3ce61a842cec64f9e2a9f3b05 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 2 Oct 2013 11:22:30 +0200 Subject: sched/wait: Collapse __wait_event_interruptible_tty() Reduce macro complexity by using the new ___wait_event() helper. No change in behaviour, identical generated code. Reviewed-by: Oleg Nesterov Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20131002092528.831085521@infradead.org Signed-off-by: Ingo Molnar diff --git a/include/linux/tty.h b/include/linux/tty.h index 0503729..6e80329 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -679,23 +679,10 @@ static inline void tty_wait_until_sent_from_close(struct tty_struct *tty, }) #define __wait_event_interruptible_tty(tty, wq, condition, ret) \ -do { \ - DEFINE_WAIT(__wait); \ - \ - for (;;) { \ - prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \ - if (condition) \ - break; \ - if (signal_pending(current)) { \ - ret = -ERESTARTSYS; \ - break; \ - } \ - tty_unlock(tty); \ - schedule(); \ - tty_lock(tty); \ - } \ - finish_wait(&wq, &__wait); \ -} while (0) + ___wait_event(wq, condition, TASK_INTERRUPTIBLE, 0, ret, \ + tty_unlock(tty); \ + schedule(); \ + tty_lock(tty)) #ifdef CONFIG_PROC_FS extern void proc_tty_register_driver(struct tty_driver *); -- cgit v0.10.2 From cf7361fd961b6f0510572af6cf8ca3ffba07018b Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 2 Oct 2013 11:22:31 +0200 Subject: sched/wait: Collapse __wait_event_killable() Reduce macro complexity by using the new ___wait_event() helper. No change in behaviour, identical generated code. Reviewed-by: Oleg Nesterov Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20131002092528.898691966@infradead.org Signed-off-by: Ingo Molnar diff --git a/include/linux/wait.h b/include/linux/wait.h index 5d5408b..ec3683e 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -582,22 +582,7 @@ do { \ #define __wait_event_killable(wq, condition, ret) \ -do { \ - DEFINE_WAIT(__wait); \ - \ - for (;;) { \ - prepare_to_wait(&wq, &__wait, TASK_KILLABLE); \ - if (condition) \ - break; \ - if (!fatal_signal_pending(current)) { \ - schedule(); \ - continue; \ - } \ - ret = -ERESTARTSYS; \ - break; \ - } \ - finish_wait(&wq, &__wait); \ -} while (0) + ___wait_event(wq, condition, TASK_KILLABLE, 0, ret, schedule()) /** * wait_event_killable - sleep until a condition gets true -- cgit v0.10.2 From ebdc195f2ec68576876216081035293e37318e86 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 2 Oct 2013 11:22:32 +0200 Subject: sched/wait: Collapse __wait_event_hrtimeout() While not a whole-sale replacement like the others we can still reduce the size of __wait_event_hrtimeout() considerably by noting that the actual core of __wait_event_hrtimeout() is identical to what ___wait_event() generates. Reviewed-by: Oleg Nesterov Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20131002092528.972793648@infradead.org Signed-off-by: Ingo Molnar diff --git a/include/linux/wait.h b/include/linux/wait.h index ec3683e..c065e8a 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -337,7 +337,6 @@ do { \ #define __wait_event_hrtimeout(wq, condition, timeout, state) \ ({ \ int __ret = 0; \ - DEFINE_WAIT(__wait); \ struct hrtimer_sleeper __t; \ \ hrtimer_init_on_stack(&__t.timer, CLOCK_MONOTONIC, \ @@ -348,25 +347,15 @@ do { \ current->timer_slack_ns, \ HRTIMER_MODE_REL); \ \ - for (;;) { \ - prepare_to_wait(&wq, &__wait, state); \ - if (condition) \ - break; \ - if (state == TASK_INTERRUPTIBLE && \ - signal_pending(current)) { \ - __ret = -ERESTARTSYS; \ - break; \ - } \ + ___wait_event(wq, condition, state, 0, __ret, \ if (!__t.task) { \ __ret = -ETIME; \ break; \ } \ - schedule(); \ - } \ + schedule()); \ \ hrtimer_cancel(&__t.timer); \ destroy_hrtimer_on_stack(&__t.timer); \ - finish_wait(&wq, &__wait); \ __ret; \ }) -- cgit v0.10.2 From 35a2af94c7ce7130ca292c68b1d27fcfdb648f6b Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 2 Oct 2013 11:22:33 +0200 Subject: sched/wait: Make the __wait_event*() interface more friendly Change all __wait_event*() implementations to match the corresponding wait_event*() signature for convenience. In particular this does away with the weird 'ret' logic. Since there are __wait_event*() users this requires we update them too. Reviewed-by: Oleg Nesterov Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20131002092529.042563462@infradead.org Signed-off-by: Ingo Molnar diff --git a/arch/mips/kernel/rtlx.c b/arch/mips/kernel/rtlx.c index d763f11..2c12ea1 100644 --- a/arch/mips/kernel/rtlx.c +++ b/arch/mips/kernel/rtlx.c @@ -172,8 +172,9 @@ int rtlx_open(int index, int can_sleep) if (rtlx == NULL) { if( (p = vpe_get_shared(tclimit)) == NULL) { if (can_sleep) { - __wait_event_interruptible(channel_wqs[index].lx_queue, - (p = vpe_get_shared(tclimit)), ret); + ret = __wait_event_interruptible( + channel_wqs[index].lx_queue, + (p = vpe_get_shared(tclimit))); if (ret) goto out_fail; } else { @@ -263,11 +264,10 @@ unsigned int rtlx_read_poll(int index, int can_sleep) /* data available to read? */ if (chan->lx_read == chan->lx_write) { if (can_sleep) { - int ret = 0; - - __wait_event_interruptible(channel_wqs[index].lx_queue, + int ret = __wait_event_interruptible( + channel_wqs[index].lx_queue, (chan->lx_read != chan->lx_write) || - sp_stopping, ret); + sp_stopping); if (ret) return ret; @@ -440,14 +440,13 @@ static ssize_t file_write(struct file *file, const char __user * buffer, /* any space left... */ if (!rtlx_write_poll(minor)) { - int ret = 0; + int ret; if (file->f_flags & O_NONBLOCK) return -EAGAIN; - __wait_event_interruptible(channel_wqs[minor].rt_queue, - rtlx_write_poll(minor), - ret); + ret = __wait_event_interruptible(channel_wqs[minor].rt_queue, + rtlx_write_poll(minor)); if (ret) return ret; } diff --git a/include/linux/tty.h b/include/linux/tty.h index 6e80329..633cac7 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -672,14 +672,14 @@ static inline void tty_wait_until_sent_from_close(struct tty_struct *tty, #define wait_event_interruptible_tty(tty, wq, condition) \ ({ \ int __ret = 0; \ - if (!(condition)) { \ - __wait_event_interruptible_tty(tty, wq, condition, __ret); \ - } \ + if (!(condition)) \ + __ret = __wait_event_interruptible_tty(tty, wq, \ + condition); \ __ret; \ }) -#define __wait_event_interruptible_tty(tty, wq, condition, ret) \ - ___wait_event(wq, condition, TASK_INTERRUPTIBLE, 0, ret, \ +#define __wait_event_interruptible_tty(tty, wq, condition) \ + ___wait_event(wq, condition, TASK_INTERRUPTIBLE, 0, 0, \ tty_unlock(tty); \ schedule(); \ tty_lock(tty)) diff --git a/include/linux/wait.h b/include/linux/wait.h index c065e8a..bd4bd7b 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -179,24 +179,23 @@ wait_queue_head_t *bit_waitqueue(void *, int); #define wake_up_interruptible_sync_poll(x, m) \ __wake_up_sync_key((x), TASK_INTERRUPTIBLE, 1, (void *) (m)) -#define ___wait_cond_timeout(condition, ret) \ +#define ___wait_cond_timeout(condition) \ ({ \ bool __cond = (condition); \ - if (__cond && !ret) \ - ret = 1; \ - __cond || !ret; \ + if (__cond && !__ret) \ + __ret = 1; \ + __cond || !__ret; \ }) #define ___wait_signal_pending(state) \ ((state == TASK_INTERRUPTIBLE && signal_pending(current)) || \ (state == TASK_KILLABLE && fatal_signal_pending(current))) -#define ___wait_nop_ret int ret __always_unused - #define ___wait_event(wq, condition, state, exclusive, ret, cmd) \ -do { \ +({ \ __label__ __out; \ DEFINE_WAIT(__wait); \ + long __ret = ret; \ \ for (;;) { \ if (exclusive) \ @@ -208,7 +207,7 @@ do { \ break; \ \ if (___wait_signal_pending(state)) { \ - ret = -ERESTARTSYS; \ + __ret = -ERESTARTSYS; \ if (exclusive) { \ abort_exclusive_wait(&wq, &__wait, \ state, NULL); \ @@ -220,12 +219,12 @@ do { \ cmd; \ } \ finish_wait(&wq, &__wait); \ -__out: ; \ -} while (0) +__out: __ret; \ +}) #define __wait_event(wq, condition) \ - ___wait_event(wq, condition, TASK_UNINTERRUPTIBLE, 0, \ - ___wait_nop_ret, schedule()) + (void)___wait_event(wq, condition, TASK_UNINTERRUPTIBLE, 0, 0, \ + schedule()) /** * wait_event - sleep until a condition gets true @@ -246,10 +245,10 @@ do { \ __wait_event(wq, condition); \ } while (0) -#define __wait_event_timeout(wq, condition, ret) \ - ___wait_event(wq, ___wait_cond_timeout(condition, ret), \ - TASK_UNINTERRUPTIBLE, 0, ret, \ - ret = schedule_timeout(ret)) +#define __wait_event_timeout(wq, condition, timeout) \ + ___wait_event(wq, ___wait_cond_timeout(condition), \ + TASK_UNINTERRUPTIBLE, 0, timeout, \ + __ret = schedule_timeout(__ret)) /** * wait_event_timeout - sleep until a condition gets true or a timeout elapses @@ -272,12 +271,12 @@ do { \ ({ \ long __ret = timeout; \ if (!(condition)) \ - __wait_event_timeout(wq, condition, __ret); \ + __ret = __wait_event_timeout(wq, condition, timeout); \ __ret; \ }) -#define __wait_event_interruptible(wq, condition, ret) \ - ___wait_event(wq, condition, TASK_INTERRUPTIBLE, 0, ret, \ +#define __wait_event_interruptible(wq, condition) \ + ___wait_event(wq, condition, TASK_INTERRUPTIBLE, 0, 0, \ schedule()) /** @@ -299,14 +298,14 @@ do { \ ({ \ int __ret = 0; \ if (!(condition)) \ - __wait_event_interruptible(wq, condition, __ret); \ + __ret = __wait_event_interruptible(wq, condition); \ __ret; \ }) -#define __wait_event_interruptible_timeout(wq, condition, ret) \ - ___wait_event(wq, ___wait_cond_timeout(condition, ret), \ - TASK_INTERRUPTIBLE, 0, ret, \ - ret = schedule_timeout(ret)) +#define __wait_event_interruptible_timeout(wq, condition, timeout) \ + ___wait_event(wq, ___wait_cond_timeout(condition), \ + TASK_INTERRUPTIBLE, 0, timeout, \ + __ret = schedule_timeout(__ret)) /** * wait_event_interruptible_timeout - sleep until a condition gets true or a timeout elapses @@ -330,7 +329,8 @@ do { \ ({ \ long __ret = timeout; \ if (!(condition)) \ - __wait_event_interruptible_timeout(wq, condition, __ret); \ + __ret = __wait_event_interruptible_timeout(wq, \ + condition, timeout); \ __ret; \ }) @@ -347,7 +347,7 @@ do { \ current->timer_slack_ns, \ HRTIMER_MODE_REL); \ \ - ___wait_event(wq, condition, state, 0, __ret, \ + __ret = ___wait_event(wq, condition, state, 0, 0, \ if (!__t.task) { \ __ret = -ETIME; \ break; \ @@ -409,15 +409,15 @@ do { \ __ret; \ }) -#define __wait_event_interruptible_exclusive(wq, condition, ret) \ - ___wait_event(wq, condition, TASK_INTERRUPTIBLE, 1, ret, \ +#define __wait_event_interruptible_exclusive(wq, condition) \ + ___wait_event(wq, condition, TASK_INTERRUPTIBLE, 1, 0, \ schedule()) #define wait_event_interruptible_exclusive(wq, condition) \ ({ \ int __ret = 0; \ if (!(condition)) \ - __wait_event_interruptible_exclusive(wq, condition, __ret);\ + __ret = __wait_event_interruptible_exclusive(wq, condition);\ __ret; \ }) @@ -570,8 +570,8 @@ do { \ -#define __wait_event_killable(wq, condition, ret) \ - ___wait_event(wq, condition, TASK_KILLABLE, 0, ret, schedule()) +#define __wait_event_killable(wq, condition) \ + ___wait_event(wq, condition, TASK_KILLABLE, 0, 0, schedule()) /** * wait_event_killable - sleep until a condition gets true @@ -592,18 +592,17 @@ do { \ ({ \ int __ret = 0; \ if (!(condition)) \ - __wait_event_killable(wq, condition, __ret); \ + __ret = __wait_event_killable(wq, condition); \ __ret; \ }) #define __wait_event_lock_irq(wq, condition, lock, cmd) \ - ___wait_event(wq, condition, TASK_UNINTERRUPTIBLE, 0, \ - ___wait_nop_ret, \ - spin_unlock_irq(&lock); \ - cmd; \ - schedule(); \ - spin_lock_irq(&lock)) + (void)___wait_event(wq, condition, TASK_UNINTERRUPTIBLE, 0, 0, \ + spin_unlock_irq(&lock); \ + cmd; \ + schedule(); \ + spin_lock_irq(&lock)) /** * wait_event_lock_irq_cmd - sleep until a condition gets true. The @@ -663,11 +662,11 @@ do { \ } while (0) -#define __wait_event_interruptible_lock_irq(wq, condition, lock, ret, cmd) \ - ___wait_event(wq, condition, TASK_INTERRUPTIBLE, 0, ret, \ - spin_unlock_irq(&lock); \ - cmd; \ - schedule(); \ +#define __wait_event_interruptible_lock_irq(wq, condition, lock, cmd) \ + ___wait_event(wq, condition, TASK_INTERRUPTIBLE, 0, 0, \ + spin_unlock_irq(&lock); \ + cmd; \ + schedule(); \ spin_lock_irq(&lock)) /** @@ -698,10 +697,9 @@ do { \ #define wait_event_interruptible_lock_irq_cmd(wq, condition, lock, cmd) \ ({ \ int __ret = 0; \ - \ if (!(condition)) \ - __wait_event_interruptible_lock_irq(wq, condition, \ - lock, __ret, cmd); \ + __ret = __wait_event_interruptible_lock_irq(wq, \ + condition, lock, cmd); \ __ret; \ }) @@ -730,18 +728,18 @@ do { \ #define wait_event_interruptible_lock_irq(wq, condition, lock) \ ({ \ int __ret = 0; \ - \ if (!(condition)) \ - __wait_event_interruptible_lock_irq(wq, condition, \ - lock, __ret, ); \ + __ret = __wait_event_interruptible_lock_irq(wq, \ + condition, lock,) \ __ret; \ }) -#define __wait_event_interruptible_lock_irq_timeout(wq, condition, lock, ret) \ - ___wait_event(wq, ___wait_cond_timeout(condition, ret), \ - TASK_INTERRUPTIBLE, 0, ret, \ - spin_unlock_irq(&lock); \ - ret = schedule_timeout(ret); \ +#define __wait_event_interruptible_lock_irq_timeout(wq, condition, \ + lock, timeout) \ + ___wait_event(wq, ___wait_cond_timeout(condition), \ + TASK_INTERRUPTIBLE, 0, ret, \ + spin_unlock_irq(&lock); \ + __ret = schedule_timeout(__ret); \ spin_lock_irq(&lock)); /** @@ -771,11 +769,10 @@ do { \ #define wait_event_interruptible_lock_irq_timeout(wq, condition, lock, \ timeout) \ ({ \ - int __ret = timeout; \ - \ + long __ret = timeout; \ if (!(condition)) \ - __wait_event_interruptible_lock_irq_timeout( \ - wq, condition, lock, __ret); \ + __ret = __wait_event_interruptible_lock_irq_timeout( \ + wq, condition, lock, timeout); \ __ret; \ }) diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c index 0578d4f..0f67690 100644 --- a/net/irda/af_irda.c +++ b/net/irda/af_irda.c @@ -2563,9 +2563,8 @@ bed: jiffies + msecs_to_jiffies(val)); /* Wait for IR-LMP to call us back */ - __wait_event_interruptible(self->query_wait, - (self->cachedaddr != 0 || self->errno == -ETIME), - err); + err = __wait_event_interruptible(self->query_wait, + (self->cachedaddr != 0 || self->errno == -ETIME)); /* If watchdog is still activated, kill it! */ del_timer(&(self->watchdog)); diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index f448471..f63c238 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -1637,12 +1637,9 @@ static int sync_thread_master(void *data) continue; } while (ip_vs_send_sync_msg(tinfo->sock, sb->mesg) < 0) { - int ret = 0; - - __wait_event_interruptible(*sk_sleep(sk), + int ret = __wait_event_interruptible(*sk_sleep(sk), sock_writeable(sk) || - kthread_should_stop(), - ret); + kthread_should_stop()); if (unlikely(kthread_should_stop())) goto done; } -- cgit v0.10.2 From c2eb505b9b24d5e912798c033814ff429b1a8823 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 2 Oct 2013 11:49:47 +0200 Subject: MAINTAINERS, sched: Update file pattern Took a while to sort out these bits and we'd like to be Cc:-ed on future modifications to the waitqueue APIs and all that. Signed-off-by: Peter Zijlstra Cc: Linus Torvalds Cc: Andrew Morton Link: http://lkml.kernel.org/n/tip-0ix315c7qcz88slmnrpshvmf@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/MAINTAINERS b/MAINTAINERS index e61c2e8..efdfcb2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7256,6 +7256,8 @@ S: Maintained F: kernel/sched/ F: include/linux/sched.h F: include/uapi/linux/sched.h +F: kernel/wait.c +F: include/linux/wait.h SCORE ARCHITECTURE M: Chen Liqin -- cgit v0.10.2 From 4040394e12cb1eed21d1306cacdc8a6f0464c8e2 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 4 Oct 2013 11:42:53 +0100 Subject: regulator: core: Always warn when using a dummy regulator This helps people spot if they have missed a supply from a device tree or equivalent data structure. Suggested-by: Stephen Warren Signed-off-by: Mark Brown diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index a40055e..82805e2 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1281,13 +1281,8 @@ static struct regulator *_regulator_get(struct device *dev, const char *id, * even if it isn't hooked up and just provide a dummy. */ if (has_full_constraints && allow_dummy) { - /* - * Log the substitution if regulator configuration is - * not complete to help development. - */ - if (!has_full_constraints) - pr_warn("%s supply %s not found, using dummy regulator\n", - devname, id); + pr_warn("%s supply %s not found, using dummy regulator\n", + devname, id); rdev = dummy_regulator_rdev; goto found; -- cgit v0.10.2 From 6653efb75dcac8fc61191cba9bd6dda99137bc44 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 4 Oct 2013 15:18:18 +0530 Subject: regulator: gpio: Remove redundant break 'break' after goto has no effect. Remove it. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c index 98a98ff..04406a9 100644 --- a/drivers/regulator/gpio-regulator.c +++ b/drivers/regulator/gpio-regulator.c @@ -283,7 +283,6 @@ static int gpio_regulator_probe(struct platform_device *pdev) dev_err(&pdev->dev, "No regulator type set\n"); ret = -EINVAL; goto err_memgpio; - break; } drvdata->nr_gpios = config->nr_gpios; -- cgit v0.10.2 From fb869b6e91a3ac235f237f73305ecf34cdc4969b Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Fri, 4 Oct 2013 10:24:49 +0200 Subject: sched/wait: Clean up wait.h details a bit Since we are changing wait.h profoundly, use the opportunity to: - add a sentence to explain what this file is about - remove whitespace noise - prettify weird looking line break fixup attempts - standardize type definition and initialization sequences - use consistent style details No code is changed. Acked-by: Peter Zijlstra Cc: Oleg Nesterov Cc: Linus Torvalds Cc: Andrew Morton Link: http://lkml.kernel.org/n/tip-O8dIie5swnctqpupakatvqyq@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/include/linux/wait.h b/include/linux/wait.h index bd4bd7b..a2726c7 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -1,7 +1,8 @@ #ifndef _LINUX_WAIT_H #define _LINUX_WAIT_H - - +/* + * Linux wait queue related types and methods + */ #include #include #include @@ -13,27 +14,27 @@ typedef int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int flags, v int default_wake_function(wait_queue_t *wait, unsigned mode, int flags, void *key); struct __wait_queue { - unsigned int flags; + unsigned int flags; #define WQ_FLAG_EXCLUSIVE 0x01 - void *private; - wait_queue_func_t func; - struct list_head task_list; + void *private; + wait_queue_func_t func; + struct list_head task_list; }; struct wait_bit_key { - void *flags; - int bit_nr; -#define WAIT_ATOMIC_T_BIT_NR -1 + void *flags; + int bit_nr; +#define WAIT_ATOMIC_T_BIT_NR -1 }; struct wait_bit_queue { - struct wait_bit_key key; - wait_queue_t wait; + struct wait_bit_key key; + wait_queue_t wait; }; struct __wait_queue_head { - spinlock_t lock; - struct list_head task_list; + spinlock_t lock; + struct list_head task_list; }; typedef struct __wait_queue_head wait_queue_head_t; @@ -84,17 +85,17 @@ extern void __init_waitqueue_head(wait_queue_head_t *q, const char *name, struct static inline void init_waitqueue_entry(wait_queue_t *q, struct task_struct *p) { - q->flags = 0; - q->private = p; - q->func = default_wake_function; + q->flags = 0; + q->private = p; + q->func = default_wake_function; } -static inline void init_waitqueue_func_entry(wait_queue_t *q, - wait_queue_func_t func) +static inline void +init_waitqueue_func_entry(wait_queue_t *q, wait_queue_func_t func) { - q->flags = 0; - q->private = NULL; - q->func = func; + q->flags = 0; + q->private = NULL; + q->func = func; } static inline int waitqueue_active(wait_queue_head_t *q) @@ -114,8 +115,8 @@ static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_t *new) /* * Used for wake-one threads: */ -static inline void __add_wait_queue_exclusive(wait_queue_head_t *q, - wait_queue_t *wait) +static inline void +__add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t *wait) { wait->flags |= WQ_FLAG_EXCLUSIVE; __add_wait_queue(q, wait); @@ -127,23 +128,22 @@ static inline void __add_wait_queue_tail(wait_queue_head_t *head, list_add_tail(&new->task_list, &head->task_list); } -static inline void __add_wait_queue_tail_exclusive(wait_queue_head_t *q, - wait_queue_t *wait) +static inline void +__add_wait_queue_tail_exclusive(wait_queue_head_t *q, wait_queue_t *wait) { wait->flags |= WQ_FLAG_EXCLUSIVE; __add_wait_queue_tail(q, wait); } -static inline void __remove_wait_queue(wait_queue_head_t *head, - wait_queue_t *old) +static inline void +__remove_wait_queue(wait_queue_head_t *head, wait_queue_t *old) { list_del(&old->task_list); } void __wake_up(wait_queue_head_t *q, unsigned int mode, int nr, void *key); void __wake_up_locked_key(wait_queue_head_t *q, unsigned int mode, void *key); -void __wake_up_sync_key(wait_queue_head_t *q, unsigned int mode, int nr, - void *key); +void __wake_up_sync_key(wait_queue_head_t *q, unsigned int mode, int nr, void *key); void __wake_up_locked(wait_queue_head_t *q, unsigned int mode, int nr); void __wake_up_sync(wait_queue_head_t *q, unsigned int mode, int nr); void __wake_up_bit(wait_queue_head_t *, void *, int); @@ -170,21 +170,21 @@ wait_queue_head_t *bit_waitqueue(void *, int); /* * Wakeup macros to be used to report events to the targets. */ -#define wake_up_poll(x, m) \ +#define wake_up_poll(x, m) \ __wake_up(x, TASK_NORMAL, 1, (void *) (m)) -#define wake_up_locked_poll(x, m) \ +#define wake_up_locked_poll(x, m) \ __wake_up_locked_key((x), TASK_NORMAL, (void *) (m)) -#define wake_up_interruptible_poll(x, m) \ +#define wake_up_interruptible_poll(x, m) \ __wake_up(x, TASK_INTERRUPTIBLE, 1, (void *) (m)) #define wake_up_interruptible_sync_poll(x, m) \ __wake_up_sync_key((x), TASK_INTERRUPTIBLE, 1, (void *) (m)) #define ___wait_cond_timeout(condition) \ ({ \ - bool __cond = (condition); \ - if (__cond && !__ret) \ - __ret = 1; \ - __cond || !__ret; \ + bool __cond = (condition); \ + if (__cond && !__ret) \ + __ret = 1; \ + __cond || !__ret; \ }) #define ___wait_signal_pending(state) \ @@ -209,8 +209,8 @@ wait_queue_head_t *bit_waitqueue(void *, int); if (___wait_signal_pending(state)) { \ __ret = -ERESTARTSYS; \ if (exclusive) { \ - abort_exclusive_wait(&wq, &__wait, \ - state, NULL); \ + abort_exclusive_wait(&wq, &__wait, \ + state, NULL); \ goto __out; \ } \ break; \ @@ -222,7 +222,7 @@ wait_queue_head_t *bit_waitqueue(void *, int); __out: __ret; \ }) -#define __wait_event(wq, condition) \ +#define __wait_event(wq, condition) \ (void)___wait_event(wq, condition, TASK_UNINTERRUPTIBLE, 0, 0, \ schedule()) @@ -238,9 +238,9 @@ __out: __ret; \ * wake_up() has to be called after changing any variable that could * change the result of the wait condition. */ -#define wait_event(wq, condition) \ +#define wait_event(wq, condition) \ do { \ - if (condition) \ + if (condition) \ break; \ __wait_event(wq, condition); \ } while (0) @@ -270,7 +270,7 @@ do { \ #define wait_event_timeout(wq, condition, timeout) \ ({ \ long __ret = timeout; \ - if (!(condition)) \ + if (!(condition)) \ __ret = __wait_event_timeout(wq, condition, timeout); \ __ret; \ }) @@ -329,7 +329,7 @@ do { \ ({ \ long __ret = timeout; \ if (!(condition)) \ - __ret = __wait_event_interruptible_timeout(wq, \ + __ret = __wait_event_interruptible_timeout(wq, \ condition, timeout); \ __ret; \ }) @@ -569,7 +569,6 @@ do { \ ? 0 : __wait_event_interruptible_locked(wq, condition, 1, 1)) - #define __wait_event_killable(wq, condition) \ ___wait_event(wq, condition, TASK_KILLABLE, 0, 0, schedule()) @@ -663,7 +662,7 @@ do { \ #define __wait_event_interruptible_lock_irq(wq, condition, lock, cmd) \ - ___wait_event(wq, condition, TASK_INTERRUPTIBLE, 0, 0, \ + ___wait_event(wq, condition, TASK_INTERRUPTIBLE, 0, 0, \ spin_unlock_irq(&lock); \ cmd; \ schedule(); \ @@ -698,7 +697,7 @@ do { \ ({ \ int __ret = 0; \ if (!(condition)) \ - __ret = __wait_event_interruptible_lock_irq(wq, \ + __ret = __wait_event_interruptible_lock_irq(wq, \ condition, lock, cmd); \ __ret; \ }) @@ -734,18 +733,18 @@ do { \ __ret; \ }) -#define __wait_event_interruptible_lock_irq_timeout(wq, condition, \ - lock, timeout) \ +#define __wait_event_interruptible_lock_irq_timeout(wq, condition, \ + lock, timeout) \ ___wait_event(wq, ___wait_cond_timeout(condition), \ - TASK_INTERRUPTIBLE, 0, ret, \ + TASK_INTERRUPTIBLE, 0, ret, \ spin_unlock_irq(&lock); \ __ret = schedule_timeout(__ret); \ spin_lock_irq(&lock)); /** - * wait_event_interruptible_lock_irq_timeout - sleep until a condition gets true or a timeout elapses. - * The condition is checked under the lock. This is expected - * to be called with the lock taken. + * wait_event_interruptible_lock_irq_timeout - sleep until a condition gets + * true or a timeout elapses. The condition is checked under + * the lock. This is expected to be called with the lock taken. * @wq: the waitqueue to wait on * @condition: a C expression for the event to wait for * @lock: a locked spinlock_t, which will be released before schedule() @@ -783,11 +782,9 @@ do { \ * We plan to remove these interfaces. */ extern void sleep_on(wait_queue_head_t *q); -extern long sleep_on_timeout(wait_queue_head_t *q, - signed long timeout); +extern long sleep_on_timeout(wait_queue_head_t *q, signed long timeout); extern void interruptible_sleep_on(wait_queue_head_t *q); -extern long interruptible_sleep_on_timeout(wait_queue_head_t *q, - signed long timeout); +extern long interruptible_sleep_on_timeout(wait_queue_head_t *q, signed long timeout); /* * Waitqueues which are removed from the waitqueue_head at wakeup time @@ -795,8 +792,7 @@ extern long interruptible_sleep_on_timeout(wait_queue_head_t *q, void prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state); void prepare_to_wait_exclusive(wait_queue_head_t *q, wait_queue_t *wait, int state); void finish_wait(wait_queue_head_t *q, wait_queue_t *wait); -void abort_exclusive_wait(wait_queue_head_t *q, wait_queue_t *wait, - unsigned int mode, void *key); +void abort_exclusive_wait(wait_queue_head_t *q, wait_queue_t *wait, unsigned int mode, void *key); int autoremove_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key); int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *key); @@ -842,8 +838,8 @@ int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *key); * One uses wait_on_bit() where one is waiting for the bit to clear, * but has no intention of setting it. */ -static inline int wait_on_bit(void *word, int bit, - int (*action)(void *), unsigned mode) +static inline int +wait_on_bit(void *word, int bit, int (*action)(void *), unsigned mode) { if (!test_bit(bit, word)) return 0; @@ -866,8 +862,8 @@ static inline int wait_on_bit(void *word, int bit, * One uses wait_on_bit_lock() where one is waiting for the bit to * clear with the intention of setting it, and when done, clearing it. */ -static inline int wait_on_bit_lock(void *word, int bit, - int (*action)(void *), unsigned mode) +static inline int +wait_on_bit_lock(void *word, int bit, int (*action)(void *), unsigned mode) { if (!test_and_set_bit(bit, word)) return 0; @@ -891,5 +887,5 @@ int wait_on_atomic_t(atomic_t *val, int (*action)(atomic_t *), unsigned mode) return 0; return out_of_line_wait_on_atomic_t(val, action, mode); } - -#endif + +#endif /* _LINUX_WAIT_H */ -- cgit v0.10.2 From 722da9d20e7f203ddbb54aba32f95a020d41fc42 Mon Sep 17 00:00:00 2001 From: Leif Lindholm Date: Thu, 3 Oct 2013 15:42:37 +0100 Subject: x86/efi: Fix config_table_type array termination Incorrect use of 0 in terminating entry of arch_tables[] causes the following sparse warning, arch/x86/platform/efi/efi.c:74:27: sparse: Using plain integer as NULL pointer Replace with NULL. Signed-off-by: Leif Lindholm [ Included sparse warning in commit message. ] Signed-off-by: Matt Fleming diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index fbc1d70..1d3372a 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -71,7 +71,7 @@ static __initdata efi_config_table_type_t arch_tables[] = { #ifdef CONFIG_X86_UV {UV_SYSTEM_TABLE_GUID, "UVsystab", &efi.uv_systab}, #endif - {NULL_GUID, NULL, 0}, + {NULL_GUID, NULL, NULL}, }; /* -- cgit v0.10.2 From c158c3bf59951bbb44bd7ccca9e6665dfd1617c5 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Fri, 4 Oct 2013 09:02:46 -0700 Subject: boot, efi: Remove redundant memset() Remove a redundant memset() call from efi_relocate_kernel() that was clearing memory that would be used by BSS in non-compressed images loaded with this function. This clear was redundant with the clearing done in the image itself, and also implemented incorrectly with a 0 length. Signed-off-by: Roy Franz Acked-by: Mark Salter Signed-off-by: Matt Fleming diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index cc0581d..b6bffbf 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -567,8 +567,6 @@ static efi_status_t efi_relocate_kernel(efi_system_table_t *sys_table_arg, * have been allocated by UEFI, so we can safely use memcpy. */ memcpy((void *)new_addr, (void *)cur_image_addr, image_size); - /* Zero any extra space we may have allocated for BSS. */ - memset((void *)(new_addr + image_size), alloc_size - image_size, 0); /* Return the new address of the relocated image. */ *image_addr = new_addr; -- cgit v0.10.2 From 6bfa687c19b7ab8adee03f0d43c197c2945dd869 Mon Sep 17 00:00:00 2001 From: Shawn Bohrer Date: Fri, 4 Oct 2013 14:24:53 -0500 Subject: sched/rt: Remove redundant nr_cpus_allowed test In 76854c7e8f3f4172fef091e78d88b3b751463ac6 ("sched: Use rt.nr_cpus_allowed to recover select_task_rq() cycles") an optimization was added to select_task_rq_rt() that immediately returns when p->nr_cpus_allowed == 1 at the beginning of the function. This makes the latter p->nr_cpus_allowed > 1 check redundant, which can now be removed. Signed-off-by: Shawn Bohrer Reviewed-by: Steven Rostedt Cc: Mike Galbraith Cc: tomk@rgmadvisors.com Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1380914693-24634-1-git-send-email-shawn.bohrer@gmail.com Signed-off-by: Ingo Molnar diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index 01970c8..ceebfba 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -1213,8 +1213,7 @@ select_task_rq_rt(struct task_struct *p, int sd_flag, int flags) */ if (curr && unlikely(rt_task(curr)) && (curr->nr_cpus_allowed < 2 || - curr->prio <= p->prio) && - (p->nr_cpus_allowed > 1)) { + curr->prio <= p->prio)) { int target = find_lowest_rq(p); if (target != -1) -- cgit v0.10.2 From 38901f1c1cae241fd4e3c5e63fcc690b921734b1 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Fri, 4 Oct 2013 14:37:56 -0700 Subject: x86/iommu: Don't make AMD_GART depend on EXPERT and default y The AMD_GART driver was made EXPERT/EMBEDDED a long time ago to avoid unbootable 64bit systems with 32bit only devices. This was before swiotlb was there, which does the job of this fallback today. SWIOTLB is always on, so systems should always boot. The drawback is that every system has to compile that driver in (it cannot be a module). Also: - Newer AMD CPUs (the APUs) don't seem to have AMD_GART support at all anymore. - Newer AMD platforms have a much better real IOMMU - The AMD GART driver was never very good (lots of overhead, e.g. in flushing due to some workarounds) and it's doubtful it's really better than SWIOTLB. - On older K8 systems it didn't even work with all chipsets. - The 32bit device bounce buffer case should be rare/ non performance critical these days anyways. - On non AMD systems it is not needed at all. So drop the EXPERT dependency on AMD_GART and remove the default y. The driver can be still compiled in, just it's an explicit decision now, and people who don't want it can unselect it. I also clarified the description a bit. This allows to save ~8K text on most modern x86-64 systems. Signed-off-by: Andi Kleen Acked-by: Borislav Petkov Link: http://lkml.kernel.org/r/1380922676-23007-1-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index ee2fb9d..c9d2b81 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -756,17 +756,16 @@ config DMI BIOS code. config GART_IOMMU - bool "GART IOMMU support" if EXPERT - default y + bool "Old AMD GART IOMMU support" select SWIOTLB depends on X86_64 && PCI && AMD_NB ---help--- Support for full DMA access of devices with 32bit memory access only on systems with more than 3GB. This is usually needed for USB, sound, many IDE/SATA chipsets and some other devices. - Provides a driver for the AMD Athlon64/Opteron/Turion/Sempron GART - based hardware IOMMU and a software bounce buffer based IOMMU used - on Intel systems and as fallback. + Provides a driver for the older AMD Athlon64/Opteron/Turion/Sempron GART + based hardware IOMMU. + Newer systems typically have a better AMD IOMMU. The code is only active when needed (enough memory and limited device) unless CONFIG_IOMMU_DEBUG or iommu=force is specified too. -- cgit v0.10.2 From ced3c42c9fcba049ec9c76c8461ac194cafb20ba Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Sun, 6 Oct 2013 11:45:20 +0200 Subject: x86/iommu: Clean up the CONFIG_GART_IOMMU config option a bit Improve the explanation of this config option. Cc: Andi Kleen Cc: Borislav Petkov Link: http://lkml.kernel.org/n/tip-gUMmysvsbl3mccbyf6olmxqg@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index c9d2b81..7081958 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -760,15 +760,21 @@ config GART_IOMMU select SWIOTLB depends on X86_64 && PCI && AMD_NB ---help--- - Support for full DMA access of devices with 32bit memory access only - on systems with more than 3GB. This is usually needed for USB, - sound, many IDE/SATA chipsets and some other devices. - Provides a driver for the older AMD Athlon64/Opteron/Turion/Sempron GART - based hardware IOMMU. - Newer systems typically have a better AMD IOMMU. - The code is only active when needed (enough memory and limited - device) unless CONFIG_IOMMU_DEBUG or iommu=force is specified - too. + Provides a driver for older AMD Athlon64/Opteron/Turion/Sempron + GART based hardware IOMMUs. + + The GART supports full DMA access for devices with 32-bit access + limitations, on systems with more than 3 GB. This is usually needed + for USB, sound, many IDE/SATA chipsets and some other devices. + + Newer systems typically have a modern AMD IOMMU, supported via + the CONFIG_AMD_IOMMU=y config option. + + In normal configurations this driver is only active when needed: + there's more than 3 GB of memory and the system contains a + 32-bit limited device. + + If unsure, say Y. config CALGARY_IOMMU bool "IBM Calgary IOMMU support" -- cgit v0.10.2 From 93721039903eab4a3d406e6fb095e2598093bc9c Mon Sep 17 00:00:00 2001 From: Eldad Zack Date: Sun, 6 Oct 2013 22:31:06 +0200 Subject: ALSA: usb-audio: remove unused parameter from sync_ep_set_params Since the format is not actually used in sync_ep_set_params(), there is no need to pass it down. Signed-off-by: Eldad Zack Signed-off-by: Takashi Iwai diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 21dc642..5dd51af 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -714,8 +714,7 @@ out_of_memory: /* * configure a sync endpoint */ -static int sync_ep_set_params(struct snd_usb_endpoint *ep, - struct audioformat *fmt) +static int sync_ep_set_params(struct snd_usb_endpoint *ep) { int i; @@ -812,7 +811,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, buffer_periods, fmt, sync_ep); break; case SND_USB_ENDPOINT_TYPE_SYNC: - err = sync_ep_set_params(ep, fmt); + err = sync_ep_set_params(ep); break; default: err = -EINVAL; -- cgit v0.10.2 From 26de5d0a8df0817a58422f6ad43019c47716ce1f Mon Sep 17 00:00:00 2001 From: Eldad Zack Date: Sun, 6 Oct 2013 22:31:07 +0200 Subject: ALSA: usb-audio: remove deactivate_endpoints() The only call site for deactivate_endpoints() at snd_usb_hw_free(). The return value is not checked there, as it is irrelevant if it fails on hw_free. This patch moves the deactivation of the endpoints directly into snd_usb_hw_free(). Signed-off-by: Eldad Zack Signed-off-by: Takashi Iwai diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 19e7995..1a9a018 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -282,22 +282,6 @@ static void stop_endpoints(struct snd_usb_substream *subs, bool wait) } } -static int deactivate_endpoints(struct snd_usb_substream *subs) -{ - int reta, retb; - - reta = snd_usb_endpoint_deactivate(subs->sync_endpoint); - retb = snd_usb_endpoint_deactivate(subs->data_endpoint); - - if (reta < 0) - return reta; - - if (retb < 0) - return retb; - - return 0; -} - static int search_roland_implicit_fb(struct usb_device *dev, int ifnum, unsigned int altsetting, struct usb_host_interface **alts, @@ -736,7 +720,8 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream) down_read(&subs->stream->chip->shutdown_rwsem); if (!subs->stream->chip->shutdown) { stop_endpoints(subs, true); - deactivate_endpoints(subs); + snd_usb_endpoint_deactivate(subs->sync_endpoint); + snd_usb_endpoint_deactivate(subs->data_endpoint); } up_read(&subs->stream->chip->shutdown_rwsem); return snd_pcm_lib_free_vmalloc_buffer(substream); -- cgit v0.10.2 From 239b9f7990873d851335ed23fa091ce1484a5bcc Mon Sep 17 00:00:00 2001 From: Eldad Zack Date: Sun, 6 Oct 2013 22:31:09 +0200 Subject: ALSA: usb-audio: don't deactivate URBs on in-use EP If an endpoint in use, its associated URBs should not be deactivated. Signed-off-by: Eldad Zack Signed-off-by: Takashi Iwai diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 5dd51af..e84732c 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -959,12 +959,12 @@ int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep) if (!ep) return -EINVAL; - deactivate_urbs(ep, true); - wait_clear_urbs(ep); - if (ep->use_count != 0) return 0; + deactivate_urbs(ep, true); + wait_clear_urbs(ep); + clear_bit(EP_FLAG_ACTIVATED, &ep->flags); return 0; -- cgit v0.10.2 From 9b7c552bba88748001574925b80ba520691b0e4d Mon Sep 17 00:00:00 2001 From: Eldad Zack Date: Sun, 6 Oct 2013 22:31:10 +0200 Subject: ALSA: usb-audio: void return type of snd_usb_endpoint_deactivate() The return value of snd_usb_endpoint_deactivate() is not used, make the function have no return value. Update the documentation to reflect what the function is actually doing. Signed-off-by: Eldad Zack Signed-off-by: Takashi Iwai diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index e84732c..2685660 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -946,28 +946,23 @@ void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep) * * @ep: the endpoint to deactivate * - * If the endpoint is not currently in use, this functions will select the - * alternate interface setting 0 for the interface of this endpoint. + * If the endpoint is not currently in use, this functions will + * deactivate its associated URBs. * * In case of any active users, this functions does nothing. - * - * Returns an error if usb_set_interface() failed, 0 in all other - * cases. */ -int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep) +void snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep) { if (!ep) - return -EINVAL; + return; if (ep->use_count != 0) - return 0; + return; deactivate_urbs(ep, true); wait_clear_urbs(ep); clear_bit(EP_FLAG_ACTIVATED, &ep->flags); - - return 0; } /** diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index 3bd02f0..1c7e8ee 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -22,7 +22,7 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, bool can_sleep); void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep); void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep); int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep); -int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep); +void snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep); void snd_usb_endpoint_free(struct list_head *head); int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep); -- cgit v0.10.2 From 06613f547a4f62c2201f70b24807d9edd4d733b0 Mon Sep 17 00:00:00 2001 From: Eldad Zack Date: Sun, 6 Oct 2013 22:31:11 +0200 Subject: ALSA: usb-audio: clear SUBSTREAM_FLAG_SYNC_EP_STARTED on error If setting the interface fails, the SUBSTREAM_FLAG_SYNC_EP_STARTED should be cleared. Signed-off-by: Eldad Zack Signed-off-by: Takashi Iwai diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 1a9a018..0c1a95d 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -246,6 +246,7 @@ static int start_endpoints(struct snd_usb_substream *subs, bool can_sleep) subs->sync_endpoint->iface, subs->sync_endpoint->alt_idx); if (err < 0) { + clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags); snd_printk(KERN_ERR "%d:%d:%d: cannot set interface (%d)\n", subs->dev->devnum, -- cgit v0.10.2 From df23a2466a4961092625b487ead60f526e401062 Mon Sep 17 00:00:00 2001 From: Eldad Zack Date: Sun, 6 Oct 2013 22:31:13 +0200 Subject: ALSA: usb-audio: rename alt_idx to altsetting As Clemens Ladisch kindly explained: "Please note that there are two methods to identify alternate settings: the number, which is the value in bAlternateSetting, and the index, which is the index in the descriptor array. There might be some wording in the USB spec that these two values must be the same, but in reality, [insert standard rant about firmware writers], bAlternateSetting must be treated as a random ID value." This patch changes the name to express the correct usage semantics. No functional change. Signed-off-by: Eldad Zack Signed-off-by: Takashi Iwai diff --git a/sound/usb/card.h b/sound/usb/card.h index ca98a9b..9867ab8 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -96,7 +96,7 @@ struct snd_usb_endpoint { unsigned int syncinterval; /* P for adaptive mode, 0 otherwise */ unsigned char silence_value; unsigned int stride; - int iface, alt_idx; + int iface, altsetting; int skip_packets; /* quirks for devices to ignore the first n packets in a stream */ diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 2685660..e25e32a 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -426,9 +426,9 @@ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip, list_for_each_entry(ep, &chip->ep_list, list) { if (ep->ep_num == ep_num && ep->iface == alts->desc.bInterfaceNumber && - ep->alt_idx == alts->desc.bAlternateSetting) { + ep->altsetting == alts->desc.bAlternateSetting) { snd_printdd(KERN_DEBUG "Re-using EP %x in iface %d,%d @%p\n", - ep_num, ep->iface, ep->alt_idx, ep); + ep_num, ep->iface, ep->altsetting, ep); goto __exit_unlock; } } @@ -447,7 +447,7 @@ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip, ep->type = type; ep->ep_num = ep_num; ep->iface = alts->desc.bInterfaceNumber; - ep->alt_idx = alts->desc.bAlternateSetting; + ep->altsetting = alts->desc.bAlternateSetting; INIT_LIST_HEAD(&ep->ready_playback_urbs); ep_num &= USB_ENDPOINT_NUMBER_MASK; diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 0c1a95d..ca3256d 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -241,17 +241,17 @@ static int start_endpoints(struct snd_usb_substream *subs, bool can_sleep) struct snd_usb_endpoint *ep = subs->sync_endpoint; if (subs->data_endpoint->iface != subs->sync_endpoint->iface || - subs->data_endpoint->alt_idx != subs->sync_endpoint->alt_idx) { + subs->data_endpoint->altsetting != subs->sync_endpoint->altsetting) { err = usb_set_interface(subs->dev, subs->sync_endpoint->iface, - subs->sync_endpoint->alt_idx); + subs->sync_endpoint->altsetting); if (err < 0) { clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags); snd_printk(KERN_ERR "%d:%d:%d: cannot set interface (%d)\n", subs->dev->devnum, subs->sync_endpoint->iface, - subs->sync_endpoint->alt_idx, err); + subs->sync_endpoint->altsetting, err); return -EIO; } } -- cgit v0.10.2 From 05c79b772f0236b857abf8034d9b0af2ecae901c Mon Sep 17 00:00:00 2001 From: Eldad Zack Date: Sun, 6 Oct 2013 22:31:17 +0200 Subject: ALSA: usb-audio: remove unused endpoint flag EP_FLAG_ACTIVATED EP_FLAG_ACTIVATED is never tested for, remove it. Signed-off-by: Eldad Zack Signed-off-by: Takashi Iwai diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index e25e32a..b9ba0fc 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -33,7 +33,6 @@ #include "pcm.h" #include "quirks.h" -#define EP_FLAG_ACTIVATED 0 #define EP_FLAG_RUNNING 1 #define EP_FLAG_STOPPING 2 @@ -961,8 +960,6 @@ void snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep) deactivate_urbs(ep, true); wait_clear_urbs(ep); - - clear_bit(EP_FLAG_ACTIVATED, &ep->flags); } /** -- cgit v0.10.2 From 3b3a80019ff194e86e740ec2f013a8915efd1ccf Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 2 Oct 2013 16:45:25 +0200 Subject: spi: ti-qspi: one only one interrupt handler The here used irq and threaded irq handler is a complete non-sense. After the status register is read and the source disabled it schedules a thread (the irq thread) to read the status from the variable, invoke complete() and then renable the interrupt. Again: schedule a thread which invokes _only_ complete(). This patch removes this non-sense and we remain with one handler which invokes complete() if needed. The device remove path should now disable the interupts. This has been compile time tested. Signed-off-by: Sebastian Andrzej Siewior Tested-by: Sourav Poddar Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c index 7a45c3e..a61aeb9 100644 --- a/drivers/spi/spi-ti-qspi.c +++ b/drivers/spi/spi-ti-qspi.c @@ -41,9 +41,6 @@ struct ti_qspi_regs { struct ti_qspi { struct completion transfer_complete; - /* IRQ synchronization */ - spinlock_t lock; - /* list synchronization */ struct mutex list_lock; @@ -57,7 +54,6 @@ struct ti_qspi { u32 spi_max_frequency; u32 cmd; u32 dc; - u32 stat; }; #define QSPI_PID (0x0) @@ -397,13 +393,12 @@ static irqreturn_t ti_qspi_isr(int irq, void *dev_id) { struct ti_qspi *qspi = dev_id; u16 int_stat; + u32 stat; irqreturn_t ret = IRQ_HANDLED; - spin_lock(&qspi->lock); - int_stat = ti_qspi_read(qspi, QSPI_INTR_STATUS_ENABLED_CLEAR); - qspi->stat = ti_qspi_read(qspi, QSPI_SPI_STATUS_REG); + stat = ti_qspi_read(qspi, QSPI_SPI_STATUS_REG); if (!int_stat) { dev_dbg(qspi->dev, "No IRQ triggered\n"); @@ -411,35 +406,14 @@ static irqreturn_t ti_qspi_isr(int irq, void *dev_id) goto out; } - ret = IRQ_WAKE_THREAD; - - ti_qspi_write(qspi, QSPI_WC_INT_DISABLE, QSPI_INTR_ENABLE_CLEAR_REG); ti_qspi_write(qspi, QSPI_WC_INT_DISABLE, QSPI_INTR_STATUS_ENABLED_CLEAR); - + if (stat & WC) + complete(&qspi->transfer_complete); out: - spin_unlock(&qspi->lock); - return ret; } -static irqreturn_t ti_qspi_threaded_isr(int this_irq, void *dev_id) -{ - struct ti_qspi *qspi = dev_id; - unsigned long flags; - - spin_lock_irqsave(&qspi->lock, flags); - - if (qspi->stat & WC) - complete(&qspi->transfer_complete); - - spin_unlock_irqrestore(&qspi->lock, flags); - - ti_qspi_write(qspi, QSPI_WC_INT_EN, QSPI_INTR_ENABLE_SET_REG); - - return IRQ_HANDLED; -} - static int ti_qspi_runtime_resume(struct device *dev) { struct ti_qspi *qspi; @@ -499,7 +473,6 @@ static int ti_qspi_probe(struct platform_device *pdev) return irq; } - spin_lock_init(&qspi->lock); mutex_init(&qspi->list_lock); qspi->base = devm_ioremap_resource(&pdev->dev, r); @@ -508,8 +481,7 @@ static int ti_qspi_probe(struct platform_device *pdev) goto free_master; } - ret = devm_request_threaded_irq(&pdev->dev, irq, ti_qspi_isr, - ti_qspi_threaded_isr, 0, + ret = devm_request_irq(&pdev->dev, irq, ti_qspi_isr, 0, dev_name(&pdev->dev), qspi); if (ret < 0) { dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n", @@ -547,6 +519,7 @@ static int ti_qspi_remove(struct platform_device *pdev) { struct ti_qspi *qspi = platform_get_drvdata(pdev); + ti_qspi_write(qspi, QSPI_WC_INT_DISABLE, QSPI_INTR_ENABLE_CLEAR_REG); spi_unregister_master(qspi->master); return 0; -- cgit v0.10.2 From da83fea6122ea637be5f960b95bb599561617319 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 5 Oct 2013 19:26:17 +0200 Subject: ASoC: dapm: Ignore VMID widgets for target bias VMID widgets behave very similar to signal generator widgets. Both are always considered to be powered up. This means that we need to ignore the VMID widgets in the same way as signal generator widgets when calculating the DAPM context's target bias level. Otherwise the presence of a VMID widget, regardless whether it is on an active path or not, will cause the DAPM context to be powered up. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index c17c14c..177f8a1 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1840,6 +1840,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) */ switch (w->id) { case snd_soc_dapm_siggen: + case snd_soc_dapm_vmid: break; case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: -- cgit v0.10.2 From 56cac413dd6d43af8355f5d1f90a199b540f73fc Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sat, 5 Oct 2013 02:25:38 +0300 Subject: ALSA: hda - hdmi: Fix reported channel map on common default layouts hdmi_setup_fake_chmap() is supposed to set the reported channel map when the channel map is not specified by the user. However, the function indexes channel_allocations[] with a wrong value and extracts the wrong nibble from hdmi_channel_mapping[], causing wrong channel maps to be shown. Fix those issues. Tested on Intel HDMI to correctly generate various channel maps, for example 3,4,14,15,7,8,5,6 (instead of incorrect 3,4,8,7,5,6,14,0) for standard 7.1 channel audio. (Note that the side and rear channels are reported as RL/RR and RLC/RRC, respectively, as per the CEA-861 standard, instead of the more traditional SL/SR and RL/RR.) Note that this only fixes the layouts that only contain traditional 7.1 speakers (2.0, 2.1, 4.0, 5.1, 7.1, etc.). E.g. the rear center of 6.1 is still being shown wrongly due to an issue with from_cea_slot() which will be fixed in a later patch. Signed-off-by: Anssi Hannula Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 7ea0245..dcc4c1c 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -740,9 +740,10 @@ static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec, static void hdmi_setup_fake_chmap(unsigned char *map, int ca) { int i; + int ordered_ca = get_channel_allocation_order(ca); for (i = 0; i < 8; i++) { - if (i < channel_allocations[ca].channels) - map[i] = from_cea_slot((hdmi_channel_mapping[ca][i] >> 4) & 0x0f); + if (i < channel_allocations[ordered_ca].channels) + map[i] = from_cea_slot(hdmi_channel_mapping[ca][i] & 0x0f); else map[i] = 0; } -- cgit v0.10.2 From 249ce1387b7739dbea2ac1a697e4bf1e37ec06b7 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 6 Oct 2013 13:43:49 +0200 Subject: ASoC: dapm: Add support for virtual mixer controls This patch adds support for virtual DAPM mixer controls. They are similar to virtual DAPM enums. There is no hardware register backing the control, so changing the control's value wont have any direct effect on the hardware. But it still influences the DAPM graph by causing the path it sits on to be connected or disconnected. This in turn can cause power changes for some of the widgets on the DAPM graph, which will then modify the hardware state. Signed-off-by: Lars-Peter Clausen Tested-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 27a72d5..2037c45 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -286,6 +286,8 @@ struct device; .info = snd_soc_info_volsw, \ .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 1) } +#define SOC_DAPM_SINGLE_VIRT(xname, max) \ + SOC_DAPM_SINGLE(xname, SND_SOC_NOPM, 0, max, 0) #define SOC_DAPM_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_volsw, \ @@ -300,6 +302,8 @@ struct device; .tlv.p = (tlv_array), \ .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } +#define SOC_DAPM_SINGLE_TLV_VIRT(xname, max, tlv_array) \ + SOC_DAPM_SINGLE(xname, SND_SOC_NOPM, 0, max, 0, tlv_array) #define SOC_DAPM_ENUM(xname, xenum) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_enum_double, \ diff --git a/include/sound/soc.h b/include/sound/soc.h index d22cb0a..b429dba 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1088,7 +1088,8 @@ struct snd_soc_pcm_runtime { /* mixer control */ struct soc_mixer_control { int min, max, platform_max; - unsigned int reg, rreg, shift, rshift; + int reg, rreg; + unsigned int shift, rshift; unsigned int invert:1; unsigned int autodisable:1; }; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 177f8a1..9273216 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -499,18 +499,22 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, int val; struct soc_mixer_control *mc = (struct soc_mixer_control *) w->kcontrol_news[i].private_value; - unsigned int reg = mc->reg; + int reg = mc->reg; unsigned int shift = mc->shift; int max = mc->max; unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; - val = soc_widget_read(w, reg); - val = (val >> shift) & mask; - if (invert) - val = max - val; + if (reg != SND_SOC_NOPM) { + val = soc_widget_read(w, reg); + val = (val >> shift) & mask; + if (invert) + val = max - val; + p->connect = !!val; + } else { + p->connect = 0; + } - p->connect = !!val; } break; case snd_soc_dapm_mux: { @@ -2792,7 +2796,7 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, struct snd_soc_card *card = codec->card; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; + int reg = mc->reg; unsigned int shift = mc->shift; int max = mc->max; unsigned int mask = (1 << fls(max)) - 1; @@ -2805,7 +2809,7 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, kcontrol->id.name); mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - if (dapm_kcontrol_is_powered(kcontrol)) + if (dapm_kcontrol_is_powered(kcontrol) && reg != SND_SOC_NOPM) val = (snd_soc_read(codec, reg) >> shift) & mask; else val = dapm_kcontrol_get_value(kcontrol); @@ -2836,7 +2840,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, struct snd_soc_card *card = codec->card; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; + int reg = mc->reg; unsigned int shift = mc->shift; int max = mc->max; unsigned int mask = (1 << fls(max)) - 1; @@ -2858,19 +2862,24 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - dapm_kcontrol_set_value(kcontrol, val); + change = dapm_kcontrol_set_value(kcontrol, val); - mask = mask << shift; - val = val << shift; + if (reg != SND_SOC_NOPM) { + mask = mask << shift; + val = val << shift; + + change = snd_soc_test_bits(codec, reg, mask, val); + } - change = snd_soc_test_bits(codec, reg, mask, val); if (change) { - update.kcontrol = kcontrol; - update.reg = reg; - update.mask = mask; - update.val = val; + if (reg != SND_SOC_NOPM) { + update.kcontrol = kcontrol; + update.reg = reg; + update.mask = mask; + update.val = val; - card->update = &update; + card->update = &update; + } soc_dapm_mixer_update_power(card, kcontrol, connect); -- cgit v0.10.2 From 90f28002110d783f49639f0db2ccdc0b58302cbd Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sat, 5 Oct 2013 02:25:39 +0300 Subject: ALSA: hda - hdmi: Fix incorrect default channel mapping for unusual CAs hdmi_std_setup_channel_mapping() selects a Channel Allocation according to the sink reported speaker mask, preferring the ALSA standard layouts. If the channel allocation is not one of the ALSA standard layouts, the ALSA channels are mapped directly to HDMI channels in order. However, the function does not take into account that there a holes in the HDMI channel map. Additionally, the function tries to disable a slot by using AC_VERB_SET_CHAN_SLOT with parameter ((alsa_ch << 8) | 0xf), while the correct parameter is ((0xf << 8) | hdmi_slot), i.e. the slot should be unassigned, not the ALSA channel. Fix both of the issues for non-ALSA-default layouts. Tested on Intel HDMI with a speaker mask of FL | FR | FC | RC, which causes CA 0x06 to be selected for 4-channel audio, which causes incorrect output (sound destined to RC goes to FC and FC goes nowhere) without the patch. Signed-off-by: Anssi Hannula Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index dcc4c1c..b187cce 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -595,22 +595,32 @@ static void hdmi_std_setup_channel_mapping(struct hda_codec *codec, bool non_pcm, int ca) { + struct cea_channel_speaker_allocation *ch_alloc; int i; int err; int order; int non_pcm_mapping[8]; order = get_channel_allocation_order(ca); + ch_alloc = &channel_allocations[order]; if (hdmi_channel_mapping[ca][1] == 0) { - for (i = 0; i < channel_allocations[order].channels; i++) - hdmi_channel_mapping[ca][i] = i | (i << 4); - for (; i < 8; i++) - hdmi_channel_mapping[ca][i] = 0xf | (i << 4); + int hdmi_slot = 0; + /* fill actual channel mappings in ALSA channel (i) order */ + for (i = 0; i < ch_alloc->channels; i++) { + while (!ch_alloc->speakers[7 - hdmi_slot] && !WARN_ON(hdmi_slot >= 8)) + hdmi_slot++; /* skip zero slots */ + + hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++; + } + /* fill the rest of the slots with ALSA channel 0xf */ + for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) + if (!ch_alloc->speakers[7 - hdmi_slot]) + hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot; } if (non_pcm) { - for (i = 0; i < channel_allocations[order].channels; i++) + for (i = 0; i < ch_alloc->channels; i++) non_pcm_mapping[i] = i | (i << 4); for (; i < 8; i++) non_pcm_mapping[i] = 0xf | (i << 4); -- cgit v0.10.2 From 1df5a06abbaa876ecc01ea84064cdffb4f52a1a1 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sat, 5 Oct 2013 02:25:40 +0300 Subject: ALSA: hda - hdmi: Fix programmed active channel count Currently the converter channel count is set to the number of actual input channels. The audio infoframe channel count field is set similarly. However, sometimes the used channel map does not map all input channels to outputs. Notably, 3 channel modes (e.g. 2.1) require a dummy input channel so there are 4 input channels. According to the HDA specification, converter channel count should be programmed according to the number of _active_ channels. On Intel HDMI codecs (but not on NVIDIA), setting the converter channel to a higher value than there are actually mapped channels to HDMI slots will cause no audio to be output at all. Note that the effects of this issue are currently partially masked by other bugs that prevent the driver from actually unmapping channels in certain cases. For example, if a 4 channel stream is first created and prepared, it gets a FL,FR,RL,RR mapping (ALSA->HDMI slot mapping 0->0, 1->1, 2->4, 3->5). If one thereafter assigns a FR,FL,FC mapping to it, the driver will remap 2->3 but fail to unmap 2->4 and 3->5, so there are still 4 active channels and the issue will not trigger in this case. These bugs will be fixed separately. Fix the channel counts in the converter channel count field and in the audio infoframe channel count field to match the actual number of active channels. Signed-off-by: Anssi Hannula Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index b187cce..4efaca8 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -63,6 +63,7 @@ struct hdmi_spec_per_pin { hda_nid_t pin_nid; int num_mux_nids; hda_nid_t mux_nids[HDA_MAX_CONNECTIONS]; + hda_nid_t cvt_nid; struct hda_codec *codec; struct hdmi_eld sink_eld; @@ -900,8 +901,9 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, { hda_nid_t pin_nid = per_pin->pin_nid; int channels = per_pin->channels; + int active_channels; struct hdmi_eld *eld; - int ca; + int ca, ordered_ca; union audio_infoframe ai; if (!channels) @@ -923,6 +925,11 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, if (ca < 0) ca = 0; + ordered_ca = get_channel_allocation_order(ca); + active_channels = channel_allocations[ordered_ca].channels; + + hdmi_set_channel_count(codec, per_pin->cvt_nid, active_channels); + memset(&ai, 0, sizeof(ai)); if (eld->info.conn_type == 0) { /* HDMI */ struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi; @@ -930,7 +937,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hdmi_ai->type = 0x84; hdmi_ai->ver = 0x01; hdmi_ai->len = 0x0a; - hdmi_ai->CC02_CT47 = channels - 1; + hdmi_ai->CC02_CT47 = active_channels - 1; hdmi_ai->CA = ca; hdmi_checksum_audio_infoframe(hdmi_ai); } else if (eld->info.conn_type == 1) { /* DisplayPort */ @@ -939,7 +946,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, dp_ai->type = 0x84; dp_ai->len = 0x1b; dp_ai->ver = 0x11 << 2; - dp_ai->CC02_CT47 = channels - 1; + dp_ai->CC02_CT47 = active_channels - 1; dp_ai->CA = ca; } else { snd_printd("HDMI: unknown connection type at pin %d\n", @@ -957,7 +964,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, snd_printdd("hdmi_setup_audio_infoframe: " "pin=%d channels=%d\n", pin_nid, - channels); + active_channels); hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca, channels, per_pin->chmap, per_pin->chmap_set); @@ -1230,6 +1237,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, per_cvt = get_cvt(spec, cvt_idx); /* Claim converter */ per_cvt->assigned = 1; + per_pin->cvt_nid = per_cvt->cvt_nid; hinfo->nid = per_cvt->cvt_nid; snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0, @@ -1552,8 +1560,6 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, per_pin->channels = substream->runtime->channels; per_pin->setup = true; - hdmi_set_channel_count(codec, cvt_nid, substream->runtime->channels); - hdmi_setup_audio_infoframe(codec, per_pin, non_pcm); return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); -- cgit v0.10.2 From 290c348ee5522a5682c2011fa4d51f232404e8a4 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 6 Oct 2013 13:43:51 +0200 Subject: ASoC: twl6040: Use virtual DAPM mixer controls By using the new virtual DAPM mixer controls it is possible to remove the twl6040 specific implementation of virtual controls. Signed-off-by: Lars-Peter Clausen Acked-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 35059a2..f2f4bcb 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -54,12 +54,7 @@ enum twl6040_dai_id { #define TWL6040_OUTHF_0dB 0x03 #define TWL6040_OUTHF_M52dB 0x1D -/* Shadow register used by the driver */ -#define TWL6040_REG_SW_SHADOW 0x2F -#define TWL6040_CACHEREGNUM (TWL6040_REG_SW_SHADOW + 1) - -/* TWL6040_REG_SW_SHADOW (0x2F) fields */ -#define TWL6040_EAR_PATH_ENABLE 0x01 +#define TWL6040_CACHEREGNUM (TWL6040_REG_STATUS + 1) struct twl6040_jack_data { struct snd_soc_jack *jack; @@ -135,8 +130,6 @@ static const u8 twl6040_reg[TWL6040_CACHEREGNUM] = { 0x00, /* REG_HFOTRIM 0x2C */ 0x09, /* REG_ACCCTL 0x2D */ 0x00, /* REG_STATUS 0x2E (ro) */ - - 0x00, /* REG_SW_SHADOW 0x2F - Shadow, non HW register */ }; /* List of registers to be restored after power up */ @@ -220,12 +213,8 @@ static int twl6040_read_reg_volatile(struct snd_soc_codec *codec, if (reg >= TWL6040_CACHEREGNUM) return -EIO; - if (likely(reg < TWL6040_REG_SW_SHADOW)) { - value = twl6040_reg_read(twl6040, reg); - twl6040_write_reg_cache(codec, reg, value); - } else { - value = twl6040_read_reg_cache(codec, reg); - } + value = twl6040_reg_read(twl6040, reg); + twl6040_write_reg_cache(codec, reg, value); return value; } @@ -261,8 +250,7 @@ static int twl6040_write(struct snd_soc_codec *codec, return -EIO; twl6040_write_reg_cache(codec, reg, value); - if (likely(reg < TWL6040_REG_SW_SHADOW) && - twl6040_is_path_unmuted(codec, reg)) + if (twl6040_is_path_unmuted(codec, reg)) return twl6040_reg_write(twl6040, reg, value); else return 0; @@ -555,7 +543,7 @@ static const struct snd_kcontrol_new hfr_mux_controls = SOC_DAPM_ENUM("Route", twl6040_hf_enum[1]); static const struct snd_kcontrol_new ep_path_enable_control = - SOC_DAPM_SINGLE("Switch", TWL6040_REG_SW_SHADOW, 0, 1, 0); + SOC_DAPM_SINGLE_VIRT("Switch", 1); static const struct snd_kcontrol_new auxl_switch_control = SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFLCTL, 6, 1, 0); -- cgit v0.10.2 From 11f7c52d90b21a51b0bc6a8b642c6ed150bdc219 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sat, 5 Oct 2013 02:25:41 +0300 Subject: ALSA: hda - hdmi: Fix unused slots being enabled in manual and non-PCM mappings hdmi_manual_setup_channel_mapping() and hdmi_std_setup_channel_mapping try to assign ALSA channels to HDMI channel slots and disable (i.e. silence) other slots. However, they try to disable a slot by using AC_VERB_SET_CHAN_SLOT with parameter ((alsa_ch << 8) | 0xf), while the correct parameter is ((0xf << 8) | hdmi_slot), i.e. the slot should be unassigned, not the ALSA channel. Fix that by actually disabling the unused slots. Note that this bug did not cause any (reported) issues because slots incorrectly having audio are normally ignored by a receiver if the CEA channel allocation used does not map that slot to any speaker. Additionally, the converter channel count configuration limits the number of actually active channels in any case. Signed-off-by: Anssi Hannula Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 4efaca8..8bf526e 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -622,9 +622,9 @@ static void hdmi_std_setup_channel_mapping(struct hda_codec *codec, if (non_pcm) { for (i = 0; i < ch_alloc->channels; i++) - non_pcm_mapping[i] = i | (i << 4); + non_pcm_mapping[i] = (i << 4) | i; for (; i < 8; i++) - non_pcm_mapping[i] = 0xf | (i << 4); + non_pcm_mapping[i] = (0xf << 4) | i; } for (i = 0; i < 8; i++) { @@ -678,7 +678,7 @@ static int to_cea_slot(unsigned char c) if (t->map == c) return t->cea_slot; } - return 0x0f; + return -1; } /* from CEA slot to ALSA API channel position */ @@ -731,14 +731,23 @@ static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec, hda_nid_t pin_nid, int chs, unsigned char *map) { - int i; - for (i = 0; i < 8; i++) { + int alsa_pos, hdmi_slot; + int assignments[8] = {[0 ... 7] = 0xf}; + + for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) { + + hdmi_slot = to_cea_slot(map[alsa_pos]); + + if (hdmi_slot < 0) + continue; /* unassigned channel */ + + assignments[hdmi_slot] = alsa_pos; + } + + for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) { int val, err; - if (i < chs) - val = to_cea_slot(map[i]); - else - val = 0xf; - val |= (i << 4); + + val = (assignments[hdmi_slot] << 4) | hdmi_slot; err = snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_CHAN_SLOT, val); if (err) -- cgit v0.10.2 From a5b7d510b2220cccbcaeb1b87a6d8c47efeb154c Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sat, 5 Oct 2013 02:25:42 +0300 Subject: ALSA: hda - hdmi: Fix channel maps with less common speakers For some speakers and slots the CEA slot <-> speaker assignment depends on the used CEA Channel Allocation value. Therefore the from_cea_slot() and to_cea_slot() helpers currently only work correctly for the regular 7.1 speakers. Fix them to work with all speakers, taking the re-ordered CA index as input and adapting use sites accordingly. This change allows manual channel mapping to actually work for all CEA allocated speakers. Additionally, this fixes incorrect channel map reporting in automatic channel mapping mode when an affected speaker position is used (e.g. 6.1 map which contains an RC speaker). Signed-off-by: Anssi Hannula Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 8bf526e..dab2af7 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -643,19 +643,27 @@ static void hdmi_std_setup_channel_mapping(struct hda_codec *codec, struct channel_map_table { unsigned char map; /* ALSA API channel map position */ - unsigned char cea_slot; /* CEA slot value */ int spk_mask; /* speaker position bit mask */ }; static struct channel_map_table map_tables[] = { - { SNDRV_CHMAP_FL, 0x00, FL }, - { SNDRV_CHMAP_FR, 0x01, FR }, - { SNDRV_CHMAP_RL, 0x04, RL }, - { SNDRV_CHMAP_RR, 0x05, RR }, - { SNDRV_CHMAP_LFE, 0x02, LFE }, - { SNDRV_CHMAP_FC, 0x03, FC }, - { SNDRV_CHMAP_RLC, 0x06, RLC }, - { SNDRV_CHMAP_RRC, 0x07, RRC }, + { SNDRV_CHMAP_FL, FL }, + { SNDRV_CHMAP_FR, FR }, + { SNDRV_CHMAP_RL, RL }, + { SNDRV_CHMAP_RR, RR }, + { SNDRV_CHMAP_LFE, LFE }, + { SNDRV_CHMAP_FC, FC }, + { SNDRV_CHMAP_RLC, RLC }, + { SNDRV_CHMAP_RRC, RRC }, + { SNDRV_CHMAP_RC, RC }, + { SNDRV_CHMAP_FLC, FLC }, + { SNDRV_CHMAP_FRC, FRC }, + { SNDRV_CHMAP_FLH, FLH }, + { SNDRV_CHMAP_FRH, FRH }, + { SNDRV_CHMAP_FLW, FLW }, + { SNDRV_CHMAP_FRW, FRW }, + { SNDRV_CHMAP_TC, TC }, + { SNDRV_CHMAP_FCH, FCH }, {} /* terminator */ }; @@ -671,25 +679,19 @@ static int to_spk_mask(unsigned char c) } /* from ALSA API channel position to CEA slot */ -static int to_cea_slot(unsigned char c) +static int to_cea_slot(int ordered_ca, unsigned char pos) { - struct channel_map_table *t = map_tables; - for (; t->map; t++) { - if (t->map == c) - return t->cea_slot; - } - return -1; -} + int mask = to_spk_mask(pos); + int i; -/* from CEA slot to ALSA API channel position */ -static int from_cea_slot(unsigned char c) -{ - struct channel_map_table *t = map_tables; - for (; t->map; t++) { - if (t->cea_slot == c) - return t->map; + if (mask) { + for (i = 0; i < 8; i++) { + if (channel_allocations[ordered_ca].speakers[7 - i] == mask) + return i; + } } - return 0; + + return -1; } /* from speaker bit mask to ALSA API channel position */ @@ -703,6 +705,14 @@ static int spk_to_chmap(int spk) return 0; } +/* from CEA slot to ALSA API channel position */ +static int from_cea_slot(int ordered_ca, unsigned char slot) +{ + int mask = channel_allocations[ordered_ca].speakers[7 - slot]; + + return spk_to_chmap(mask); +} + /* get the CA index corresponding to the given ALSA API channel map */ static int hdmi_manual_channel_allocation(int chs, unsigned char *map) { @@ -729,14 +739,16 @@ static int hdmi_manual_channel_allocation(int chs, unsigned char *map) /* set up the channel slots for the given ALSA API channel map */ static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec, hda_nid_t pin_nid, - int chs, unsigned char *map) + int chs, unsigned char *map, + int ca) { + int ordered_ca = get_channel_allocation_order(ca); int alsa_pos, hdmi_slot; int assignments[8] = {[0 ... 7] = 0xf}; for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) { - hdmi_slot = to_cea_slot(map[alsa_pos]); + hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]); if (hdmi_slot < 0) continue; /* unassigned channel */ @@ -763,7 +775,7 @@ static void hdmi_setup_fake_chmap(unsigned char *map, int ca) int ordered_ca = get_channel_allocation_order(ca); for (i = 0; i < 8; i++) { if (i < channel_allocations[ordered_ca].channels) - map[i] = from_cea_slot(hdmi_channel_mapping[ca][i] & 0x0f); + map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f); else map[i] = 0; } @@ -776,7 +788,7 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec, { if (!non_pcm && chmap_set) { hdmi_manual_setup_channel_mapping(codec, pin_nid, - channels, map); + channels, map, ca); } else { hdmi_std_setup_channel_mapping(codec, pin_nid, non_pcm, ca); hdmi_setup_fake_chmap(map, ca); -- cgit v0.10.2 From bb731f2100e614a8d7c5965d3663aed893859733 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sat, 5 Oct 2013 02:25:43 +0300 Subject: ALSA: hda - hdmi: Fix available channel maps missing from TLV Currently the available channel maps TLV only contains channel maps that are limited to the traditional 7.1 speakers. Since the other HDMI channel mapping functions have been fixed to properly handle all CEA-861-E specified speakers, allow them to be listed. Signed-off-by: Anssi Hannula Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index dab2af7..62b1e85 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1658,8 +1658,6 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = info->private_data; struct hdmi_spec *spec = codec->spec; - const unsigned int valid_mask = - FL | FR | RL | RR | LFE | FC | RLC | RRC; unsigned int __user *dst; int chs, count = 0; @@ -1677,8 +1675,6 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, int chs_bytes = chs * 4; if (cap->channels != chs) continue; - if (cap->spk_mask & ~valid_mask) - continue; if (size < 8) return -ENOMEM; if (put_user(SNDRV_CTL_TLVT_CHMAP_VAR, dst) || -- cgit v0.10.2 From 980b24958f0c615fd003d37f0fce4ab1ecd01784 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sat, 5 Oct 2013 02:25:44 +0300 Subject: ALSA: hda - hdmi: Tweak debug messages to be more useful Allow channel map debugging for both automatic and manual channel maps, and print CA always when updating infoframe. Signed-off-by: Anssi Hannula Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 62b1e85..00e0413 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -637,8 +637,6 @@ static void hdmi_std_setup_channel_mapping(struct hda_codec *codec, break; } } - - hdmi_debug_channel_mapping(codec, pin_nid); } struct channel_map_table { @@ -793,6 +791,8 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec, hdmi_std_setup_channel_mapping(codec, pin_nid, non_pcm, ca); hdmi_setup_fake_chmap(map, ca); } + + hdmi_debug_channel_mapping(codec, pin_nid); } /* @@ -983,9 +983,9 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes, sizeof(ai))) { snd_printdd("hdmi_setup_audio_infoframe: " - "pin=%d channels=%d\n", + "pin=%d channels=%d ca=0x%02x\n", pin_nid, - active_channels); + active_channels, ca); hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca, channels, per_pin->chmap, per_pin->chmap_set); -- cgit v0.10.2 From 052901f42f360062f36cc5c0aa6e5ae372fe0895 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 6 Oct 2013 13:43:50 +0200 Subject: ASoC: twl4030: Use virtual DAPM mixer controls By using the new virtual DAPM mixer controls it is possible to remove the twl4030 specific implementation of virtual controls. Signed-off-by: Lars-Peter Clausen Acked-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 1e3884d..dfc51bb 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -46,13 +46,7 @@ /* TWL4030 PMBR1 Register GPIO6 mux bits */ #define TWL4030_GPIO6_PWM0_MUTE(value) ((value & 0x03) << 2) -/* Shadow register used by the audio driver */ -#define TWL4030_REG_SW_SHADOW 0x4A -#define TWL4030_CACHEREGNUM (TWL4030_REG_SW_SHADOW + 1) - -/* TWL4030_REG_SW_SHADOW (0x4A) Fields */ -#define TWL4030_HFL_EN 0x01 -#define TWL4030_HFR_EN 0x02 +#define TWL4030_CACHEREGNUM (TWL4030_REG_MISC_SET_2 + 1) /* * twl4030 register cache & default register settings @@ -132,7 +126,6 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { 0x00, /* REG_VIBRA_PWM_SET (0x47) */ 0x00, /* REG_ANAMIC_GAIN (0x48) */ 0x00, /* REG_MISC_SET_2 (0x49) */ - 0x00, /* REG_SW_SHADOW (0x4A) - Shadow, non HW register */ }; /* codec private data */ @@ -198,42 +191,41 @@ static int twl4030_write(struct snd_soc_codec *codec, int write_to_reg = 0; twl4030_write_reg_cache(codec, reg, value); - if (likely(reg < TWL4030_REG_SW_SHADOW)) { - /* Decide if the given register can be written */ - switch (reg) { - case TWL4030_REG_EAR_CTL: - if (twl4030->earpiece_enabled) - write_to_reg = 1; - break; - case TWL4030_REG_PREDL_CTL: - if (twl4030->predrivel_enabled) - write_to_reg = 1; - break; - case TWL4030_REG_PREDR_CTL: - if (twl4030->predriver_enabled) - write_to_reg = 1; - break; - case TWL4030_REG_PRECKL_CTL: - if (twl4030->carkitl_enabled) - write_to_reg = 1; - break; - case TWL4030_REG_PRECKR_CTL: - if (twl4030->carkitr_enabled) - write_to_reg = 1; - break; - case TWL4030_REG_HS_GAIN_SET: - if (twl4030->hsl_enabled || twl4030->hsr_enabled) - write_to_reg = 1; - break; - default: - /* All other register can be written */ + /* Decide if the given register can be written */ + switch (reg) { + case TWL4030_REG_EAR_CTL: + if (twl4030->earpiece_enabled) write_to_reg = 1; - break; - } - if (write_to_reg) - return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, - value, reg); + break; + case TWL4030_REG_PREDL_CTL: + if (twl4030->predrivel_enabled) + write_to_reg = 1; + break; + case TWL4030_REG_PREDR_CTL: + if (twl4030->predriver_enabled) + write_to_reg = 1; + break; + case TWL4030_REG_PRECKL_CTL: + if (twl4030->carkitl_enabled) + write_to_reg = 1; + break; + case TWL4030_REG_PRECKR_CTL: + if (twl4030->carkitr_enabled) + write_to_reg = 1; + break; + case TWL4030_REG_HS_GAIN_SET: + if (twl4030->hsl_enabled || twl4030->hsr_enabled) + write_to_reg = 1; + break; + default: + /* All other register can be written */ + write_to_reg = 1; + break; } + if (write_to_reg) + return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, + value, reg); + return 0; } @@ -532,7 +524,7 @@ SOC_DAPM_ENUM("Route", twl4030_handsfreel_enum); /* Handsfree Left virtual mute */ static const struct snd_kcontrol_new twl4030_dapm_handsfreelmute_control = - SOC_DAPM_SINGLE("Switch", TWL4030_REG_SW_SHADOW, 0, 1, 0); + SOC_DAPM_SINGLE_VIRT("Switch", 1); /* Handsfree Right */ static const char *twl4030_handsfreer_texts[] = @@ -548,7 +540,7 @@ SOC_DAPM_ENUM("Route", twl4030_handsfreer_enum); /* Handsfree Right virtual mute */ static const struct snd_kcontrol_new twl4030_dapm_handsfreermute_control = - SOC_DAPM_SINGLE("Switch", TWL4030_REG_SW_SHADOW, 1, 1, 0); + SOC_DAPM_SINGLE_VIRT("Switch", 1); /* Vibra */ /* Vibra audio path selection */ -- cgit v0.10.2 From df3774c5c53305eda4053d5c941bb17a2145e3c6 Mon Sep 17 00:00:00 2001 From: Thomas Pugliese Date: Tue, 1 Oct 2013 14:32:15 -0500 Subject: ALSA: usb-audio: add support for wireless USB devices This patch updates snd_usb_audio_create also support devices whose speed == USB_SPEED_WIRELESS. Signed-off-by: Thomas Pugliese Acked-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/sound/usb/card.c b/sound/usb/card.c index d1f54df..9d9de8d 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -346,6 +346,7 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx, case USB_SPEED_LOW: case USB_SPEED_FULL: case USB_SPEED_HIGH: + case USB_SPEED_WIRELESS: case USB_SPEED_SUPER: break; default: -- cgit v0.10.2 From 6d5eba5aac4ec9bba3d6b3e6425d857706d12ccb Mon Sep 17 00:00:00 2001 From: Thomas Pugliese Date: Tue, 1 Oct 2013 14:32:57 -0500 Subject: ALSA: usb-audio: support wireless devices in snd_usb_parse_datainterval This patch adds support for dev speed USB_SPEED_WIRELESS in snd_usb_parse_datainterval which allows the usb sound core to create ISO urbs with the correct number and size of buffers. Signed-off-by: Thomas Pugliese Acked-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai diff --git a/sound/usb/helper.c b/sound/usb/helper.c index 6209024..51ed1ac 100644 --- a/sound/usb/helper.c +++ b/sound/usb/helper.c @@ -118,6 +118,7 @@ unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip, { switch (snd_usb_get_speed(chip->dev)) { case USB_SPEED_HIGH: + case USB_SPEED_WIRELESS: case USB_SPEED_SUPER: if (get_endpoint(alts, 0)->bInterval >= 1 && get_endpoint(alts, 0)->bInterval <= 4) -- cgit v0.10.2 From acc1ccadb85fc47238e9d92b2336c04c9fc64d53 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Thu, 3 Oct 2013 19:36:37 +0530 Subject: regulator: palmas: get regulators node from parent node only The device tree binding of Palmas regulator driver says as: palmas_pmis { compatible = "ti,palmas-pmic"; ... regulators { ... } }; In this "regulators" subnode is expected to be part of parent node, not the outside of parent node. Hence to get the regulator node, the correct call is of_get_child_by_name() rather than of_find_node_by_name() which actually searches the "regulators" node from the parent node to end of DTS file. Signed-off-by: Laxman Dewangan Reviewed-by: Stephen Warren Signed-off-by: Mark Brown diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c index 1b31e41..d68f05b 100644 --- a/drivers/regulator/palmas-regulator.c +++ b/drivers/regulator/palmas-regulator.c @@ -826,7 +826,7 @@ static void palmas_dt_to_pdata(struct device *dev, int idx, ret; node = of_node_get(node); - regulators = of_find_node_by_name(node, "regulators"); + regulators = of_get_child_by_name(node, "regulators"); if (!regulators) { dev_info(dev, "regulator node not found\n"); return; -- cgit v0.10.2 From 702a4879ec337463f858c8ab467482cce260bf18 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 10 Sep 2013 15:43:41 +0800 Subject: spi: bitbang: Let spi_bitbang_start() take a reference to master Many drivers that use bitbang library have a leak on probe error paths. This is because once a spi_master_get() call succeeds, we need an additional spi_master_put() call to free the memory. Fix this issue by moving the code taking a reference to master to spi_bitbang_start(), so spi_bitbang_start() will take a reference to master on success. With this change, the caller is responsible for calling spi_bitbang_stop() to decrement the reference and spi_master_put() as counterpart of spi_alloc_master() to prevent a memory leak. So now we have below patten for drivers using bitbang library: probe: spi_alloc_master -> Init reference count to 1 spi_bitbang_start -> Increment reference count remove: spi_bitbang_stop -> Decrement reference count spi_master_put -> Decrement reference count (reference count reaches 0) Fixup all users accordingly. Signed-off-by: Axel Lin Suggested-by: Uwe Kleine-Koenig Acked-by: Uwe Kleine-Koenig Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-altera.c b/drivers/spi/spi-altera.c index 9a64c3f..595b62c 100644 --- a/drivers/spi/spi-altera.c +++ b/drivers/spi/spi-altera.c @@ -219,7 +219,7 @@ static int altera_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, hw); /* setup the state for the bitbang driver */ - hw->bitbang.master = spi_master_get(master); + hw->bitbang.master = master; if (!hw->bitbang.master) return err; hw->bitbang.chipselect = altera_spi_chipsel; diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index 37bad95..821bf7a 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -231,7 +231,7 @@ static int ath79_spi_probe(struct platform_device *pdev) master->num_chipselect = pdata->num_chipselect; } - sp->bitbang.master = spi_master_get(master); + sp->bitbang.master = master; sp->bitbang.chipselect = ath79_spi_chipselect; sp->bitbang.txrx_word[SPI_MODE_0] = ath79_spi_txrx_mode0; sp->bitbang.setup_transfer = spi_bitbang_setup_transfer; diff --git a/drivers/spi/spi-au1550.c b/drivers/spi/spi-au1550.c index 1d00d9b3..e06400a 100644 --- a/drivers/spi/spi-au1550.c +++ b/drivers/spi/spi-au1550.c @@ -775,7 +775,7 @@ static int au1550_spi_probe(struct platform_device *pdev) hw = spi_master_get_devdata(master); - hw->master = spi_master_get(master); + hw->master = master; hw->pdata = dev_get_platdata(&pdev->dev); hw->dev = &pdev->dev; diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c index 8c11355..0056623 100644 --- a/drivers/spi/spi-bitbang.c +++ b/drivers/spi/spi-bitbang.c @@ -414,10 +414,16 @@ static int spi_bitbang_unprepare_hardware(struct spi_master *spi) * This routine registers the spi_master, which will process requests in a * dedicated task, keeping IRQs unblocked most of the time. To stop * processing those requests, call spi_bitbang_stop(). + * + * On success, this routine will take a reference to master. The caller is + * responsible for calling spi_bitbang_stop() to decrement the reference and + * spi_master_put() as counterpart of spi_alloc_master() to prevent a memory + * leak. */ int spi_bitbang_start(struct spi_bitbang *bitbang) { struct spi_master *master = bitbang->master; + int ret; if (!master || !bitbang->chipselect) return -EINVAL; @@ -449,7 +455,11 @@ int spi_bitbang_start(struct spi_bitbang *bitbang) /* driver may get busy before register() returns, especially * if someone registered boardinfo for devices */ - return spi_register_master(master); + ret = spi_register_master(spi_master_get(master)); + if (ret) + spi_master_put(master); + + return 0; } EXPORT_SYMBOL_GPL(spi_bitbang_start); diff --git a/drivers/spi/spi-butterfly.c b/drivers/spi/spi-butterfly.c index 5ed08e5..b1ecea2 100644 --- a/drivers/spi/spi-butterfly.c +++ b/drivers/spi/spi-butterfly.c @@ -225,7 +225,7 @@ static void butterfly_attach(struct parport *p) master->bus_num = 42; master->num_chipselect = 2; - pp->bitbang.master = spi_master_get(master); + pp->bitbang.master = master; pp->bitbang.chipselect = butterfly_chipselect; pp->bitbang.txrx_word[SPI_MODE_0] = butterfly_txrx_word_mode0; diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index 8fbfe24..af2bb73 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -916,7 +916,7 @@ static int davinci_spi_probe(struct platform_device *pdev) if (ret) goto unmap_io; - dspi->bitbang.master = spi_master_get(master); + dspi->bitbang.master = master; if (dspi->bitbang.master == NULL) { ret = -ENODEV; goto irq_free; @@ -925,7 +925,7 @@ static int davinci_spi_probe(struct platform_device *pdev) dspi->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(dspi->clk)) { ret = -ENODEV; - goto put_master; + goto irq_free; } clk_prepare_enable(dspi->clk); @@ -1015,8 +1015,6 @@ free_dma: free_clk: clk_disable_unprepare(dspi->clk); clk_put(dspi->clk); -put_master: - spi_master_put(master); irq_free: free_irq(dspi->irq, dspi); unmap_io: @@ -1024,7 +1022,7 @@ unmap_io: release_region: release_mem_region(dspi->pbase, resource_size(r)); free_master: - kfree(master); + spi_master_put(master); err: return ret; } @@ -1051,11 +1049,11 @@ static int davinci_spi_remove(struct platform_device *pdev) clk_disable_unprepare(dspi->clk); clk_put(dspi->clk); - spi_master_put(master); free_irq(dspi->irq, dspi); iounmap(dspi->base); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(dspi->pbase, resource_size(r)); + spi_master_put(master); return 0; } diff --git a/drivers/spi/spi-efm32.c b/drivers/spi/spi-efm32.c index 7d84418..d428a40 100644 --- a/drivers/spi/spi-efm32.c +++ b/drivers/spi/spi-efm32.c @@ -347,7 +347,7 @@ static int efm32_spi_probe(struct platform_device *pdev) ddata = spi_master_get_devdata(master); - ddata->bitbang.master = spi_master_get(master); + ddata->bitbang.master = master; ddata->bitbang.chipselect = efm32_spi_chipselect; ddata->bitbang.setup_transfer = efm32_spi_setup_transfer; ddata->bitbang.txrx_bufs = efm32_spi_txrx_bufs; diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 6cd07d1..d338b67 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -450,7 +450,7 @@ static int dspi_probe(struct platform_device *pdev) dspi = spi_master_get_devdata(master); dspi->pdev = pdev; - dspi->bitbang.master = spi_master_get(master); + dspi->bitbang.master = master; dspi->bitbang.chipselect = dspi_chipselect; dspi->bitbang.setup_transfer = dspi_setup_transfer; dspi->bitbang.txrx_bufs = dspi_txrx_transfer; diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index 68b69fe..14c01b4 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -467,7 +467,7 @@ static int spi_gpio_probe(struct platform_device *pdev) } #endif - spi_gpio->bitbang.master = spi_master_get(master); + spi_gpio->bitbang.master = master; spi_gpio->bitbang.chipselect = spi_gpio_chipselect; if ((master_flags & (SPI_MASTER_NO_TX | SPI_MASTER_NO_RX)) == 0) { @@ -486,7 +486,6 @@ static int spi_gpio_probe(struct platform_device *pdev) status = spi_bitbang_start(&spi_gpio->bitbang); if (status < 0) { - spi_master_put(spi_gpio->bitbang.master); gpio_free: if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO) gpio_free(SPI_MISO_GPIO); @@ -510,13 +509,13 @@ static int spi_gpio_remove(struct platform_device *pdev) /* stop() unregisters child devices too */ status = spi_bitbang_stop(&spi_gpio->bitbang); - spi_master_put(spi_gpio->bitbang.master); if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO) gpio_free(SPI_MISO_GPIO); if (SPI_MOSI_GPIO != SPI_GPIO_NO_MOSI) gpio_free(SPI_MOSI_GPIO); gpio_free(SPI_SCK_GPIO); + spi_master_put(spi_gpio->bitbang.master); return status; } diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 15323d8..02d9f46 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -786,7 +786,7 @@ static int spi_imx_probe(struct platform_device *pdev) master->num_chipselect = num_cs; spi_imx = spi_master_get_devdata(master); - spi_imx->bitbang.master = spi_master_get(master); + spi_imx->bitbang.master = master; for (i = 0; i < master->num_chipselect; i++) { int cs_gpio = of_get_named_gpio(np, "cs-gpios", i); diff --git a/drivers/spi/spi-lm70llp.c b/drivers/spi/spi-lm70llp.c index 0759b5d..41c5765 100644 --- a/drivers/spi/spi-lm70llp.c +++ b/drivers/spi/spi-lm70llp.c @@ -222,7 +222,7 @@ static void spi_lm70llp_attach(struct parport *p) /* * SPI and bitbang hookup. */ - pp->bitbang.master = spi_master_get(master); + pp->bitbang.master = master; pp->bitbang.chipselect = lm70_chipselect; pp->bitbang.txrx_word[SPI_MODE_0] = lm70_txrx; pp->bitbang.flags = SPI_3WIRE; diff --git a/drivers/spi/spi-nuc900.c b/drivers/spi/spi-nuc900.c index 47a68b4..e0c32bc 100644 --- a/drivers/spi/spi-nuc900.c +++ b/drivers/spi/spi-nuc900.c @@ -349,7 +349,7 @@ static int nuc900_spi_probe(struct platform_device *pdev) } hw = spi_master_get_devdata(master); - hw->master = spi_master_get(master); + hw->master = master; hw->pdata = dev_get_platdata(&pdev->dev); hw->dev = &pdev->dev; @@ -435,7 +435,6 @@ err_iomap: kfree(hw->ioarea); err_pdata: spi_master_put(hw->master); - err_nomem: return err; } diff --git a/drivers/spi/spi-oc-tiny.c b/drivers/spi/spi-oc-tiny.c index 333cb1b..91c6685 100644 --- a/drivers/spi/spi-oc-tiny.c +++ b/drivers/spi/spi-oc-tiny.c @@ -306,7 +306,7 @@ static int tiny_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, hw); /* setup the state for the bitbang driver */ - hw->bitbang.master = spi_master_get(master); + hw->bitbang.master = master; if (!hw->bitbang.master) return err; hw->bitbang.setup_transfer = tiny_spi_setup_transfer; diff --git a/drivers/spi/spi-ppc4xx.c b/drivers/spi/spi-ppc4xx.c index 0ee53c2..c57740bb7 100644 --- a/drivers/spi/spi-ppc4xx.c +++ b/drivers/spi/spi-ppc4xx.c @@ -396,7 +396,7 @@ static int spi_ppc4xx_of_probe(struct platform_device *op) master->dev.of_node = np; platform_set_drvdata(op, master); hw = spi_master_get_devdata(master); - hw->master = spi_master_get(master); + hw->master = master; hw->dev = dev; init_completion(&hw->done); @@ -558,6 +558,7 @@ static int spi_ppc4xx_of_remove(struct platform_device *op) free_irq(hw->irqnum, hw); iounmap(hw->regs); free_gpios(hw); + spi_master_put(master); return 0; } diff --git a/drivers/spi/spi-s3c24xx.c b/drivers/spi/spi-s3c24xx.c index ce318d9..c9cfdfa 100644 --- a/drivers/spi/spi-s3c24xx.c +++ b/drivers/spi/spi-s3c24xx.c @@ -524,7 +524,7 @@ static int s3c24xx_spi_probe(struct platform_device *pdev) hw = spi_master_get_devdata(master); memset(hw, 0, sizeof(struct s3c24xx_spi)); - hw->master = spi_master_get(master); + hw->master = master; hw->pdata = pdata = dev_get_platdata(&pdev->dev); hw->dev = &pdev->dev; diff --git a/drivers/spi/spi-sh-sci.c b/drivers/spi/spi-sh-sci.c index 8eefeb6..38eb24d 100644 --- a/drivers/spi/spi-sh-sci.c +++ b/drivers/spi/spi-sh-sci.c @@ -133,7 +133,7 @@ static int sh_sci_spi_probe(struct platform_device *dev) sp->info = dev_get_platdata(&dev->dev); /* setup spi bitbang adaptor */ - sp->bitbang.master = spi_master_get(master); + sp->bitbang.master = master; sp->bitbang.master->bus_num = sp->info->bus_num; sp->bitbang.master->num_chipselect = sp->info->num_chipselect; sp->bitbang.chipselect = sh_sci_spi_chipselect; diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index a1f21b7..592b4af 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -632,7 +632,7 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) if (ret) goto free_master; - sspi->bitbang.master = spi_master_get(master); + sspi->bitbang.master = master; sspi->bitbang.chipselect = spi_sirfsoc_chipselect; sspi->bitbang.setup_transfer = spi_sirfsoc_setup_transfer; sspi->bitbang.txrx_bufs = spi_sirfsoc_transfer; diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c index 0bf1b2c..ec3a83f 100644 --- a/drivers/spi/spi-xilinx.c +++ b/drivers/spi/spi-xilinx.c @@ -372,7 +372,7 @@ static int xilinx_spi_probe(struct platform_device *pdev) master->mode_bits = SPI_CPOL | SPI_CPHA; xspi = spi_master_get_devdata(master); - xspi->bitbang.master = spi_master_get(master); + xspi->bitbang.master = master; xspi->bitbang.chipselect = xilinx_spi_chipselect; xspi->bitbang.setup_transfer = xilinx_spi_setup_transfer; xspi->bitbang.txrx_bufs = xilinx_spi_txrx_bufs; -- cgit v0.10.2 From e6f7563b7354c2eb26d89b8622b3582a4c6510d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 10 Sep 2013 09:51:30 +0200 Subject: spi: efm32: add spi_bitbang_stop to device remove callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This call is needed to cleanup the resources requested by spi_bitbang_start in the probe callback. Noticed-by: Axel Lin Signed-off-by: Uwe Kleine-König Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-efm32.c b/drivers/spi/spi-efm32.c index 34ef44c..5ce61e5 100644 --- a/drivers/spi/spi-efm32.c +++ b/drivers/spi/spi-efm32.c @@ -477,6 +477,8 @@ static int efm32_spi_remove(struct platform_device *pdev) struct spi_master *master = platform_get_drvdata(pdev); struct efm32_spi_ddata *ddata = spi_master_get_devdata(master); + spi_bitbang_stop(&ddata->bitbang); + efm32_spi_write32(ddata, 0, REG_IEN); free_irq(ddata->txirq, ddata); -- cgit v0.10.2 From 308964caeebc45eb7723c87818076f61fa1a2e1b Mon Sep 17 00:00:00 2001 From: Loc Ho Date: Wed, 26 Jun 2013 11:56:09 -0600 Subject: clk: Add APM X-Gene SoC clock driver clk: Add APM X-Gene SoC clock driver for reference, PLL, and device clocks. Signed-off-by: Loc Ho Signed-off-by: Kumar Sankaran Signed-off-by: Vinayak Kale Signed-off-by: Feng Kan Signed-off-by: Mike Turquette diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 279407a..dd37f91 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -93,6 +93,13 @@ config CLK_PPC_CORENET This adds the clock driver support for Freescale PowerPC corenet platforms using common clock framework. +config COMMON_CLK_XGENE + bool "Clock driver for APM XGene SoC" + default y + depends on ARM64 + ---help--- + Sypport for the APM X-Gene SoC reference, PLL, and device clocks. + endmenu source "drivers/clk/mvebu/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 7b11106..270f3fd 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o obj-$(CONFIG_ARCH_ZYNQ) += zynq/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_PLAT_SAMSUNG) += samsung/ +obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o obj-$(CONFIG_X86) += x86/ diff --git a/drivers/clk/clk-xgene.c b/drivers/clk/clk-xgene.c new file mode 100644 index 0000000..dd8a62d --- /dev/null +++ b/drivers/clk/clk-xgene.c @@ -0,0 +1,521 @@ +/* + * clk-xgene.c - AppliedMicro X-Gene Clock Interface + * + * Copyright (c) 2013, Applied Micro Circuits Corporation + * Author: Loc Ho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register SCU_PCPPLL bit fields */ +#define N_DIV_RD(src) (((src) & 0x000001ff)) + +/* Register SCU_SOCPLL bit fields */ +#define CLKR_RD(src) (((src) & 0x07000000)>>24) +#define CLKOD_RD(src) (((src) & 0x00300000)>>20) +#define REGSPEC_RESET_F1_MASK 0x00010000 +#define CLKF_RD(src) (((src) & 0x000001ff)) + +#define XGENE_CLK_DRIVER_VER "0.1" + +static DEFINE_SPINLOCK(clk_lock); + +static inline u32 xgene_clk_read(void *csr) +{ + return readl_relaxed(csr); +} + +static inline void xgene_clk_write(u32 data, void *csr) +{ + return writel_relaxed(data, csr); +} + +/* PLL Clock */ +enum xgene_pll_type { + PLL_TYPE_PCP = 0, + PLL_TYPE_SOC = 1, +}; + +struct xgene_clk_pll { + struct clk_hw hw; + const char *name; + void __iomem *reg; + spinlock_t *lock; + u32 pll_offset; + enum xgene_pll_type type; +}; + +#define to_xgene_clk_pll(_hw) container_of(_hw, struct xgene_clk_pll, hw) + +static int xgene_clk_pll_is_enabled(struct clk_hw *hw) +{ + struct xgene_clk_pll *pllclk = to_xgene_clk_pll(hw); + u32 data; + + data = xgene_clk_read(pllclk->reg + pllclk->pll_offset); + pr_debug("%s pll %s\n", pllclk->name, + data & REGSPEC_RESET_F1_MASK ? "disabled" : "enabled"); + + return data & REGSPEC_RESET_F1_MASK ? 0 : 1; +} + +static unsigned long xgene_clk_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct xgene_clk_pll *pllclk = to_xgene_clk_pll(hw); + unsigned long fref; + unsigned long fvco; + u32 pll; + u32 nref; + u32 nout; + u32 nfb; + + pll = xgene_clk_read(pllclk->reg + pllclk->pll_offset); + + if (pllclk->type == PLL_TYPE_PCP) { + /* + * PLL VCO = Reference clock * NF + * PCP PLL = PLL_VCO / 2 + */ + nout = 2; + fvco = parent_rate * (N_DIV_RD(pll) + 4); + } else { + /* + * Fref = Reference Clock / NREF; + * Fvco = Fref * NFB; + * Fout = Fvco / NOUT; + */ + nref = CLKR_RD(pll) + 1; + nout = CLKOD_RD(pll) + 1; + nfb = CLKF_RD(pll); + fref = parent_rate / nref; + fvco = fref * nfb; + } + pr_debug("%s pll recalc rate %ld parent %ld\n", pllclk->name, + fvco / nout, parent_rate); + + return fvco / nout; +} + +const struct clk_ops xgene_clk_pll_ops = { + .is_enabled = xgene_clk_pll_is_enabled, + .recalc_rate = xgene_clk_pll_recalc_rate, +}; + +static struct clk *xgene_register_clk_pll(struct device *dev, + const char *name, const char *parent_name, + unsigned long flags, void __iomem *reg, u32 pll_offset, + u32 type, spinlock_t *lock) +{ + struct xgene_clk_pll *apmclk; + struct clk *clk; + struct clk_init_data init; + + /* allocate the APM clock structure */ + apmclk = kzalloc(sizeof(*apmclk), GFP_KERNEL); + if (!apmclk) { + pr_err("%s: could not allocate APM clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + init.name = name; + init.ops = &xgene_clk_pll_ops; + init.flags = flags; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + + apmclk->name = name; + apmclk->reg = reg; + apmclk->lock = lock; + apmclk->pll_offset = pll_offset; + apmclk->type = type; + apmclk->hw.init = &init; + + /* Register the clock */ + clk = clk_register(dev, &apmclk->hw); + if (IS_ERR(clk)) { + pr_err("%s: could not register clk %s\n", __func__, name); + kfree(apmclk); + return NULL; + } + return clk; +} + +static void xgene_pllclk_init(struct device_node *np, enum xgene_pll_type pll_type) +{ + const char *clk_name = np->full_name; + struct clk *clk; + void *reg; + + reg = of_iomap(np, 0); + if (reg == NULL) { + pr_err("Unable to map CSR register for %s\n", np->full_name); + return; + } + of_property_read_string(np, "clock-output-names", &clk_name); + clk = xgene_register_clk_pll(NULL, + clk_name, of_clk_get_parent_name(np, 0), + CLK_IS_ROOT, reg, 0, pll_type, &clk_lock); + if (!IS_ERR(clk)) { + of_clk_add_provider(np, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + pr_debug("Add %s clock PLL\n", clk_name); + } +} + +static void xgene_socpllclk_init(struct device_node *np) +{ + xgene_pllclk_init(np, PLL_TYPE_SOC); +} + +static void xgene_pcppllclk_init(struct device_node *np) +{ + xgene_pllclk_init(np, PLL_TYPE_PCP); +} + +/* IP Clock */ +struct xgene_dev_parameters { + void __iomem *csr_reg; /* CSR for IP clock */ + u32 reg_clk_offset; /* Offset to clock enable CSR */ + u32 reg_clk_mask; /* Mask bit for clock enable */ + u32 reg_csr_offset; /* Offset to CSR reset */ + u32 reg_csr_mask; /* Mask bit for disable CSR reset */ + void __iomem *divider_reg; /* CSR for divider */ + u32 reg_divider_offset; /* Offset to divider register */ + u32 reg_divider_shift; /* Bit shift to divider field */ + u32 reg_divider_width; /* Width of the bit to divider field */ +}; + +struct xgene_clk { + struct clk_hw hw; + const char *name; + spinlock_t *lock; + struct xgene_dev_parameters param; +}; + +#define to_xgene_clk(_hw) container_of(_hw, struct xgene_clk, hw) + +static int xgene_clk_enable(struct clk_hw *hw) +{ + struct xgene_clk *pclk = to_xgene_clk(hw); + unsigned long flags = 0; + u32 data; + + if (pclk->lock) + spin_lock_irqsave(pclk->lock, flags); + + if (pclk->param.csr_reg != NULL) { + pr_debug("%s clock enabled\n", pclk->name); + /* First enable the clock */ + data = xgene_clk_read(pclk->param.csr_reg + + pclk->param.reg_clk_offset); + data |= pclk->param.reg_clk_mask; + xgene_clk_write(data, pclk->param.csr_reg + + pclk->param.reg_clk_offset); + pr_debug("%s clock PADDR base 0x%016LX clk offset 0x%08X mask 0x%08X value 0x%08X\n", + pclk->name, __pa(pclk->param.csr_reg), + pclk->param.reg_clk_offset, pclk->param.reg_clk_mask, + data); + + /* Second enable the CSR */ + data = xgene_clk_read(pclk->param.csr_reg + + pclk->param.reg_csr_offset); + data &= ~pclk->param.reg_csr_mask; + xgene_clk_write(data, pclk->param.csr_reg + + pclk->param.reg_csr_offset); + pr_debug("%s CSR RESET PADDR base 0x%016LX csr offset 0x%08X mask 0x%08X value 0x%08X\n", + pclk->name, __pa(pclk->param.csr_reg), + pclk->param.reg_csr_offset, pclk->param.reg_csr_mask, + data); + } + + if (pclk->lock) + spin_unlock_irqrestore(pclk->lock, flags); + + return 0; +} + +static void xgene_clk_disable(struct clk_hw *hw) +{ + struct xgene_clk *pclk = to_xgene_clk(hw); + unsigned long flags = 0; + u32 data; + + if (pclk->lock) + spin_lock_irqsave(pclk->lock, flags); + + if (pclk->param.csr_reg != NULL) { + pr_debug("%s clock disabled\n", pclk->name); + /* First put the CSR in reset */ + data = xgene_clk_read(pclk->param.csr_reg + + pclk->param.reg_csr_offset); + data |= pclk->param.reg_csr_mask; + xgene_clk_write(data, pclk->param.csr_reg + + pclk->param.reg_csr_offset); + + /* Second disable the clock */ + data = xgene_clk_read(pclk->param.csr_reg + + pclk->param.reg_clk_offset); + data &= ~pclk->param.reg_clk_mask; + xgene_clk_write(data, pclk->param.csr_reg + + pclk->param.reg_clk_offset); + } + + if (pclk->lock) + spin_unlock_irqrestore(pclk->lock, flags); +} + +static int xgene_clk_is_enabled(struct clk_hw *hw) +{ + struct xgene_clk *pclk = to_xgene_clk(hw); + u32 data = 0; + + if (pclk->param.csr_reg != NULL) { + pr_debug("%s clock checking\n", pclk->name); + data = xgene_clk_read(pclk->param.csr_reg + + pclk->param.reg_clk_offset); + pr_debug("%s clock is %s\n", pclk->name, + data & pclk->param.reg_clk_mask ? "enabled" : + "disabled"); + } + + if (pclk->param.csr_reg == NULL) + return 1; + return data & pclk->param.reg_clk_mask ? 1 : 0; +} + +static unsigned long xgene_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct xgene_clk *pclk = to_xgene_clk(hw); + u32 data; + + if (pclk->param.divider_reg) { + data = xgene_clk_read(pclk->param.divider_reg + + pclk->param.reg_divider_offset); + data >>= pclk->param.reg_divider_shift; + data &= (1 << pclk->param.reg_divider_width) - 1; + + pr_debug("%s clock recalc rate %ld parent %ld\n", + pclk->name, parent_rate / data, parent_rate); + return parent_rate / data; + } else { + pr_debug("%s clock recalc rate %ld parent %ld\n", + pclk->name, parent_rate, parent_rate); + return parent_rate; + } +} + +static int xgene_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct xgene_clk *pclk = to_xgene_clk(hw); + unsigned long flags = 0; + u32 data; + u32 divider; + u32 divider_save; + + if (pclk->lock) + spin_lock_irqsave(pclk->lock, flags); + + if (pclk->param.divider_reg) { + /* Let's compute the divider */ + if (rate > parent_rate) + rate = parent_rate; + divider_save = divider = parent_rate / rate; /* Rounded down */ + divider &= (1 << pclk->param.reg_divider_width) - 1; + divider <<= pclk->param.reg_divider_shift; + + /* Set new divider */ + data = xgene_clk_read(pclk->param.divider_reg + + pclk->param.reg_divider_offset); + data &= ~((1 << pclk->param.reg_divider_width) - 1); + data |= divider; + xgene_clk_write(data, pclk->param.divider_reg + + pclk->param.reg_divider_offset); + pr_debug("%s clock set rate %ld\n", pclk->name, + parent_rate / divider_save); + } else { + divider_save = 1; + } + + if (pclk->lock) + spin_unlock_irqrestore(pclk->lock, flags); + + return parent_rate / divider_save; +} + +static long xgene_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct xgene_clk *pclk = to_xgene_clk(hw); + unsigned long parent_rate = *prate; + u32 divider; + + if (pclk->param.divider_reg) { + /* Let's compute the divider */ + if (rate > parent_rate) + rate = parent_rate; + divider = parent_rate / rate; /* Rounded down */ + } else { + divider = 1; + } + + return parent_rate / divider; +} + +const struct clk_ops xgene_clk_ops = { + .enable = xgene_clk_enable, + .disable = xgene_clk_disable, + .is_enabled = xgene_clk_is_enabled, + .recalc_rate = xgene_clk_recalc_rate, + .set_rate = xgene_clk_set_rate, + .round_rate = xgene_clk_round_rate, +}; + +static struct clk *xgene_register_clk(struct device *dev, + const char *name, const char *parent_name, + struct xgene_dev_parameters *parameters, spinlock_t *lock) +{ + struct xgene_clk *apmclk; + struct clk *clk; + struct clk_init_data init; + int rc; + + /* allocate the APM clock structure */ + apmclk = kzalloc(sizeof(*apmclk), GFP_KERNEL); + if (!apmclk) { + pr_err("%s: could not allocate APM clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + init.name = name; + init.ops = &xgene_clk_ops; + init.flags = 0; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + + apmclk->name = name; + apmclk->lock = lock; + apmclk->hw.init = &init; + apmclk->param = *parameters; + + /* Register the clock */ + clk = clk_register(dev, &apmclk->hw); + if (IS_ERR(clk)) { + pr_err("%s: could not register clk %s\n", __func__, name); + kfree(apmclk); + return clk; + } + + /* Register the clock for lookup */ + rc = clk_register_clkdev(clk, name, NULL); + if (rc != 0) { + pr_err("%s: could not register lookup clk %s\n", + __func__, name); + } + return clk; +} + +static void __init xgene_devclk_init(struct device_node *np) +{ + const char *clk_name = np->full_name; + struct clk *clk; + struct resource res; + int rc; + struct xgene_dev_parameters parameters; + int i; + + /* Check if the entry is disabled */ + if (!of_device_is_available(np)) + return; + + /* Parse the DTS register for resource */ + parameters.csr_reg = NULL; + parameters.divider_reg = NULL; + for (i = 0; i < 2; i++) { + void *map_res; + rc = of_address_to_resource(np, i, &res); + if (rc != 0) { + if (i == 0) { + pr_err("no DTS register for %s\n", + np->full_name); + return; + } + break; + } + map_res = of_iomap(np, i); + if (map_res == NULL) { + pr_err("Unable to map resource %d for %s\n", + i, np->full_name); + goto err; + } + if (strcmp(res.name, "div-reg") == 0) + parameters.divider_reg = map_res; + else /* if (strcmp(res->name, "csr-reg") == 0) */ + parameters.csr_reg = map_res; + } + if (of_property_read_u32(np, "csr-offset", ¶meters.reg_csr_offset)) + parameters.reg_csr_offset = 0; + if (of_property_read_u32(np, "csr-mask", ¶meters.reg_csr_mask)) + parameters.reg_csr_mask = 0xF; + if (of_property_read_u32(np, "enable-offset", + ¶meters.reg_clk_offset)) + parameters.reg_clk_offset = 0x8; + if (of_property_read_u32(np, "enable-mask", ¶meters.reg_clk_mask)) + parameters.reg_clk_mask = 0xF; + if (of_property_read_u32(np, "divider-offset", + ¶meters.reg_divider_offset)) + parameters.reg_divider_offset = 0; + if (of_property_read_u32(np, "divider-width", + ¶meters.reg_divider_width)) + parameters.reg_divider_width = 0; + if (of_property_read_u32(np, "divider-shift", + ¶meters.reg_divider_shift)) + parameters.reg_divider_shift = 0; + of_property_read_string(np, "clock-output-names", &clk_name); + + clk = xgene_register_clk(NULL, clk_name, + of_clk_get_parent_name(np, 0), ¶meters, &clk_lock); + if (IS_ERR(clk)) + goto err; + pr_debug("Add %s clock\n", clk_name); + rc = of_clk_add_provider(np, of_clk_src_simple_get, clk); + if (rc != 0) + pr_err("%s: could register provider clk %s\n", __func__, + np->full_name); + + return; + +err: + if (parameters.csr_reg) + iounmap(parameters.csr_reg); + if (parameters.divider_reg) + iounmap(parameters.divider_reg); +} + +CLK_OF_DECLARE(xgene_socpll_clock, "apm,xgene-socpll-clock", xgene_socpllclk_init); +CLK_OF_DECLARE(xgene_pcppll_clock, "apm,xgene-pcppll-clock", xgene_pcppllclk_init); +CLK_OF_DECLARE(xgene_dev_clock, "apm,xgene-device-clock", xgene_devclk_init); -- cgit v0.10.2 From 3eb15d84e355f4ea1acf0eaba11ca173de342107 Mon Sep 17 00:00:00 2001 From: Loc Ho Date: Wed, 26 Jun 2013 11:56:10 -0600 Subject: clk: arm64: Add DTS clock entry for APM X-Gene Storm SoC clk: arm64: Add DTS clock entry for APM X-Gene Storm SoC with reference to reference, PCP PLL, SoC PLL, and Ethernet clocks. Signed-off-by: Loc Ho Signed-off-by: Kumar Sankaran Signed-off-by: Vinayak Kale Signed-off-by: Feng Kan Signed-off-by: Mike Turquette diff --git a/arch/arm64/boot/dts/apm-storm.dtsi b/arch/arm64/boot/dts/apm-storm.dtsi index bfdc578..d37d736 100644 --- a/arch/arm64/boot/dts/apm-storm.dtsi +++ b/arch/arm64/boot/dts/apm-storm.dtsi @@ -103,6 +103,81 @@ #size-cells = <2>; ranges; + clocks { + #address-cells = <2>; + #size-cells = <2>; + ranges; + refclk: refclk { + compatible = "fixed-clock"; + #clock-cells = <1>; + clock-frequency = <100000000>; + clock-output-names = "refclk"; + }; + + pcppll: pcppll@17000100 { + compatible = "apm,xgene-pcppll-clock"; + #clock-cells = <1>; + clocks = <&refclk 0>; + clock-names = "pcppll"; + reg = <0x0 0x17000100 0x0 0x1000>; + clock-output-names = "pcppll"; + type = <0>; + }; + + socpll: socpll@17000120 { + compatible = "apm,xgene-socpll-clock"; + #clock-cells = <1>; + clocks = <&refclk 0>; + clock-names = "socpll"; + reg = <0x0 0x17000120 0x0 0x1000>; + clock-output-names = "socpll"; + type = <1>; + }; + + socplldiv2: socplldiv2 { + compatible = "fixed-factor-clock"; + #clock-cells = <1>; + clocks = <&socpll 0>; + clock-names = "socplldiv2"; + clock-mult = <1>; + clock-div = <2>; + clock-output-names = "socplldiv2"; + }; + + qmlclk: qmlclk { + compatible = "apm,xgene-device-clock"; + #clock-cells = <1>; + clocks = <&socplldiv2 0>; + clock-names = "qmlclk"; + reg = <0x0 0x1703C000 0x0 0x1000>; + reg-names = "csr-reg"; + clock-output-names = "qmlclk"; + }; + + ethclk: ethclk { + compatible = "apm,xgene-device-clock"; + #clock-cells = <1>; + clocks = <&socplldiv2 0>; + clock-names = "ethclk"; + reg = <0x0 0x17000000 0x0 0x1000>; + reg-names = "div-reg"; + divider-offset = <0x238>; + divider-width = <0x9>; + divider-shift = <0x0>; + clock-output-names = "ethclk"; + }; + + eth8clk: eth8clk { + compatible = "apm,xgene-device-clock"; + #clock-cells = <1>; + clocks = <ðclk 0>; + clock-names = "eth8clk"; + reg = <0x0 0x1702C000 0x0 0x1000>; + reg-names = "csr-reg"; + clock-output-names = "eth8clk"; + }; + }; + serial0: serial@1c020000 { device_type = "serial"; compatible = "ns16550"; -- cgit v0.10.2 From 938cc3a14ca0d921165c741fb10d8defba203dde Mon Sep 17 00:00:00 2001 From: Loc Ho Date: Wed, 26 Jun 2013 11:56:11 -0600 Subject: Documentation: Add documentation for APM X-Gene clock binding Documentation: Add documentation for APM X-Gene clock binding with PLL and device clocks. Signed-off-by: Loc Ho Signed-off-by: Kumar Sankaran Signed-off-by: Vinayak Kale Signed-off-by: Feng Kan Signed-off-by: Mike Turquette diff --git a/Documentation/devicetree/bindings/clock/xgene.txt b/Documentation/devicetree/bindings/clock/xgene.txt new file mode 100644 index 0000000..1c4ef77 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/xgene.txt @@ -0,0 +1,111 @@ +Device Tree Clock bindings for APM X-Gene + +This binding uses the common clock binding[1]. + +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt + +Required properties: +- compatible : shall be one of the following: + "apm,xgene-socpll-clock" - for a X-Gene SoC PLL clock + "apm,xgene-pcppll-clock" - for a X-Gene PCP PLL clock + "apm,xgene-device-clock" - for a X-Gene device clock + +Required properties for SoC or PCP PLL clocks: +- reg : shall be the physical PLL register address for the pll clock. +- clocks : shall be the input parent clock phandle for the clock. This should + be the reference clock. +- #clock-cells : shall be set to 1. +- clock-output-names : shall be the name of the PLL referenced by derive + clock. +Optional properties for PLL clocks: +- clock-names : shall be the name of the PLL. If missing, use the device name. + +Required properties for device clocks: +- reg : shall be a list of address and length pairs describing the CSR + reset and/or the divider. Either may be omitted, but at least + one must be present. + - reg-names : shall be a string list describing the reg resource. This + may include "csr-reg" and/or "div-reg". If this property + is not present, the reg property is assumed to describe + only "csr-reg". +- clocks : shall be the input parent clock phandle for the clock. +- #clock-cells : shall be set to 1. +- clock-output-names : shall be the name of the device referenced. +Optional properties for device clocks: +- clock-names : shall be the name of the device clock. If missing, use the + device name. +- csr-offset : Offset to the CSR reset register from the reset address base. + Default is 0. +- csr-mask : CSR reset mask bit. Default is 0xF. +- enable-offset : Offset to the enable register from the reset address base. + Default is 0x8. +- enable-mask : CSR enable mask bit. Default is 0xF. +- divider-offset : Offset to the divider CSR register from the divider base. + Default is 0x0. +- divider-width : Width of the divider register. Default is 0. +- divider-shift : Bit shift of the divider register. Default is 0. + +For example: + + pcppll: pcppll@17000100 { + compatible = "apm,xgene-pcppll-clock"; + #clock-cells = <1>; + clocks = <&refclk 0>; + clock-names = "pcppll"; + reg = <0x0 0x17000100 0x0 0x1000>; + clock-output-names = "pcppll"; + type = <0>; + }; + + socpll: socpll@17000120 { + compatible = "apm,xgene-socpll-clock"; + #clock-cells = <1>; + clocks = <&refclk 0>; + clock-names = "socpll"; + reg = <0x0 0x17000120 0x0 0x1000>; + clock-output-names = "socpll"; + type = <1>; + }; + + qmlclk: qmlclk { + compatible = "apm,xgene-device-clock"; + #clock-cells = <1>; + clocks = <&socplldiv2 0>; + clock-names = "qmlclk"; + reg = <0x0 0x1703C000 0x0 0x1000>; + reg-name = "csr-reg"; + clock-output-names = "qmlclk"; + }; + + ethclk: ethclk { + compatible = "apm,xgene-device-clock"; + #clock-cells = <1>; + clocks = <&socplldiv2 0>; + clock-names = "ethclk"; + reg = <0x0 0x17000000 0x0 0x1000>; + reg-names = "div-reg"; + divider-offset = <0x238>; + divider-width = <0x9>; + divider-shift = <0x0>; + clock-output-names = "ethclk"; + }; + + apbclk: apbclk { + compatible = "apm,xgene-device-clock"; + #clock-cells = <1>; + clocks = <&ahbclk 0>; + clock-names = "apbclk"; + reg = <0x0 0x1F2AC000 0x0 0x1000 + 0x0 0x1F2AC000 0x0 0x1000>; + reg-names = "csr-reg", "div-reg"; + csr-offset = <0x0>; + csr-mask = <0x200>; + enable-offset = <0x8>; + enable-mask = <0x200>; + divider-offset = <0x10>; + divider-width = <0x2>; + divider-shift = <0x0>; + flags = <0x8>; + clock-output-names = "apbclk"; + }; + -- cgit v0.10.2 From fb30a1819a4315091cb731403ebf74ff1a490f43 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 4 Oct 2013 16:39:22 +0100 Subject: spi/tegra20-slink: Remove unused is_single_xfer check Currently transfer_one_message() checks to see if the message consists of a single spi_transfer and tells _start_transfer_one() but it just ignores this. Don't bother. Signed-off-by: Mark Brown Acked-by: Laxman Dewangan diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index c703536..36d4e66 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -707,8 +707,7 @@ static void tegra_slink_deinit_dma_param(struct tegra_slink_data *tspi, } static int tegra_slink_start_transfer_one(struct spi_device *spi, - struct spi_transfer *t, bool is_first_of_msg, - bool is_single_xfer) + struct spi_transfer *t, bool is_first_of_msg) { struct tegra_slink_data *tspi = spi_master_get_devdata(spi->master); u32 speed; @@ -828,7 +827,6 @@ static int tegra_slink_transfer_one_message(struct spi_master *master, struct spi_message *msg) { bool is_first_msg = true; - int single_xfer; struct tegra_slink_data *tspi = spi_master_get_devdata(master); struct spi_transfer *xfer; struct spi_device *spi = msg->spi; @@ -837,11 +835,9 @@ static int tegra_slink_transfer_one_message(struct spi_master *master, msg->status = 0; msg->actual_length = 0; - single_xfer = list_is_singular(&msg->transfers); list_for_each_entry(xfer, &msg->transfers, transfer_list) { INIT_COMPLETION(tspi->xfer_completion); - ret = tegra_slink_start_transfer_one(spi, xfer, - is_first_msg, single_xfer); + ret = tegra_slink_start_transfer_one(spi, xfer, is_first_msg); if (ret < 0) { dev_err(tspi->dev, "spi can not start transfer, err %d\n", ret); -- cgit v0.10.2 From 56ec1978ff07380bbdc0a942c8779ec9fd9e02ee Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 7 Oct 2013 19:33:53 +0100 Subject: spi: Provide trace points for message processing Provide tracepoints for the lifecycle of a message from submission to completion and for the active time for masters to help with performance analysis of SPI I/O. Signed-off-by: Mark Brown diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 9e039c6..8bef0c9 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -39,6 +39,9 @@ #include #include +#define CREATE_TRACE_POINTS +#include + static void spidev_release(struct device *dev) { struct spi_device *spi = to_spi_device(dev); @@ -557,6 +560,7 @@ static void spi_pump_messages(struct kthread_work *work) pm_runtime_mark_last_busy(master->dev.parent); pm_runtime_put_autosuspend(master->dev.parent); } + trace_spi_master_idle(master); return; } @@ -585,6 +589,9 @@ static void spi_pump_messages(struct kthread_work *work) } } + if (!was_busy) + trace_spi_master_busy(master); + if (!was_busy && master->prepare_transfer_hardware) { ret = master->prepare_transfer_hardware(master); if (ret) { @@ -597,6 +604,8 @@ static void spi_pump_messages(struct kthread_work *work) } } + trace_spi_message_start(master->cur_msg); + ret = master->transfer_one_message(master, master->cur_msg); if (ret) { dev_err(&master->dev, @@ -689,6 +698,8 @@ void spi_finalize_current_message(struct spi_master *master) mesg->state = NULL; if (mesg->complete) mesg->complete(mesg->context); + + trace_spi_message_done(mesg); } EXPORT_SYMBOL_GPL(spi_finalize_current_message); @@ -1421,6 +1432,10 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) struct spi_master *master = spi->master; struct spi_transfer *xfer; + message->spi = spi; + + trace_spi_message_submit(message); + if (list_empty(&message->transfers)) return -EINVAL; if (!message->complete) @@ -1520,7 +1535,6 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) } } - message->spi = spi; message->status = -EINPROGRESS; return master->transfer(spi, message); } diff --git a/include/trace/events/spi.h b/include/trace/events/spi.h new file mode 100644 index 0000000..a7b0907 --- /dev/null +++ b/include/trace/events/spi.h @@ -0,0 +1,94 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM spi + +#if !defined(_TRACE_SPI_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_SPI_H + +#include +#include + +DECLARE_EVENT_CLASS(spi_master, + + TP_PROTO(struct spi_master *master), + + TP_ARGS(master), + + TP_STRUCT__entry( + __field( int, bus_num ) + ), + + TP_fast_assign( + __entry->bus_num = master->bus_num; + ), + + TP_printk("spi%d", (int)__entry->bus_num) + +); + +DEFINE_EVENT(spi_master, spi_master_idle, + + TP_PROTO(struct spi_master *master), + + TP_ARGS(master) + +); + +DEFINE_EVENT(spi_master, spi_master_busy, + + TP_PROTO(struct spi_master *master), + + TP_ARGS(master) + +); + +DECLARE_EVENT_CLASS(spi_message, + + TP_PROTO(struct spi_message *msg), + + TP_ARGS(msg), + + TP_STRUCT__entry( + __field( int, bus_num ) + __field( int, chip_select ) + __field( struct spi_message *, msg ) + ), + + TP_fast_assign( + __entry->bus_num = msg->spi->master->bus_num; + __entry->chip_select = msg->spi->chip_select; + __entry->msg = msg; + ), + + TP_printk("spi%d.%d %p", (int)__entry->bus_num, + (int)__entry->chip_select, + (struct spi_message *)__entry->msg) +); + +DEFINE_EVENT(spi_message, spi_message_submit, + + TP_PROTO(struct spi_message *msg), + + TP_ARGS(msg) + +); + +DEFINE_EVENT(spi_message, spi_message_start, + + TP_PROTO(struct spi_message *msg), + + TP_ARGS(msg) + +); + +DEFINE_EVENT(spi_message, spi_message_done, + + TP_PROTO(struct spi_message *msg), + + TP_ARGS(msg) + +); + +#endif /* _TRACE_POWER_H */ + +/* This part must be outside protection */ +#include -- cgit v0.10.2 From ebd805cc14bec607e74795b7933570f240508cb4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 27 Sep 2013 19:56:31 +0100 Subject: spi/s3c64xx: Factor transfer start out of enable/disable_cs() The hardware level /CS handling is tied to the start of the data path so is rolled into the same function as we use to manipulate GPIO /CS. In order to support factoring out the /CS handling into the core separate the two and explicitly start transfers separately to the /CS handling. Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index e631232..8e732a1 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -927,6 +927,9 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master, /* Start the signals */ writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); + /* Start the signals */ + writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); + spin_unlock_irqrestore(&sdd->lock, flags); status = wait_for_xfer(sdd, xfer, use_dma); -- cgit v0.10.2 From 7e09a979404ed07b8f05d09a0e87a87c7891f472 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 7 Oct 2013 23:00:24 +0100 Subject: regmap: Cache async work structures Rather than allocating and deallocating the structures used to manage async transfers each time we do one keep the structures around as long as the regmap is around. This should provide a small performance improvement. Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 57f7778..793ebe2 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -44,7 +44,6 @@ struct regmap_format { struct regmap_async { struct list_head list; - struct work_struct cleanup; struct regmap *map; void *work_buf; }; @@ -67,6 +66,7 @@ struct regmap { spinlock_t async_lock; wait_queue_head_t async_waitq; struct list_head async_list; + struct list_head async_free; int async_ret; #ifdef CONFIG_DEBUG_FS diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7d689a1..742f300 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -42,15 +42,6 @@ static int _regmap_bus_formatted_write(void *context, unsigned int reg, static int _regmap_bus_raw_write(void *context, unsigned int reg, unsigned int val); -static void async_cleanup(struct work_struct *work) -{ - struct regmap_async *async = container_of(work, struct regmap_async, - cleanup); - - kfree(async->work_buf); - kfree(async); -} - bool regmap_reg_in_ranges(unsigned int reg, const struct regmap_range *ranges, unsigned int nranges) @@ -465,6 +456,7 @@ struct regmap *regmap_init(struct device *dev, spin_lock_init(&map->async_lock); INIT_LIST_HEAD(&map->async_list); + INIT_LIST_HEAD(&map->async_free); init_waitqueue_head(&map->async_waitq); if (config->read_flag_mask || config->write_flag_mask) { @@ -942,12 +934,22 @@ EXPORT_SYMBOL_GPL(regmap_reinit_cache); */ void regmap_exit(struct regmap *map) { + struct regmap_async *async; + regcache_exit(map); regmap_debugfs_exit(map); regmap_range_exit(map); if (map->bus && map->bus->free_context) map->bus->free_context(map->bus_context); kfree(map->work_buf); + while (!list_empty(&map->async_free)) { + async = list_first_entry_or_null(&map->async_free, + struct regmap_async, + list); + list_del(&async->list); + kfree(async->work_buf); + kfree(async); + } kfree(map); } EXPORT_SYMBOL_GPL(regmap_exit); @@ -1115,20 +1117,31 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, u8[0] |= map->write_flag_mask; if (async && map->bus->async_write) { - struct regmap_async *async = map->bus->async_alloc(); - if (!async) - return -ENOMEM; + struct regmap_async *async; trace_regmap_async_write_start(map->dev, reg, val_len); - async->work_buf = kzalloc(map->format.buf_size, - GFP_KERNEL | GFP_DMA); - if (!async->work_buf) { - kfree(async); - return -ENOMEM; + spin_lock_irqsave(&map->async_lock, flags); + async = list_first_entry_or_null(&map->async_free, + struct regmap_async, + list); + if (async) + list_del(&async->list); + spin_unlock_irqrestore(&map->async_lock, flags); + + if (!async) { + async = map->bus->async_alloc(); + if (!async) + return -ENOMEM; + + async->work_buf = kzalloc(map->format.buf_size, + GFP_KERNEL | GFP_DMA); + if (!async->work_buf) { + kfree(async); + return -ENOMEM; + } } - INIT_WORK(&async->cleanup, async_cleanup); async->map = map; /* If the caller supplied the value we can use it safely. */ @@ -1152,11 +1165,8 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, ret); spin_lock_irqsave(&map->async_lock, flags); - list_del(&async->list); + list_move(&async->list, &map->async_free); spin_unlock_irqrestore(&map->async_lock, flags); - - kfree(async->work_buf); - kfree(async); } return ret; @@ -1820,8 +1830,7 @@ void regmap_async_complete_cb(struct regmap_async *async, int ret) trace_regmap_async_io_complete(map->dev); spin_lock(&map->async_lock); - - list_del(&async->list); + list_move(&async->list, &map->async_free); wake = list_empty(&map->async_list); if (ret != 0) @@ -1829,8 +1838,6 @@ void regmap_async_complete_cb(struct regmap_async *async, int ret) spin_unlock(&map->async_lock); - schedule_work(&async->cleanup); - if (wake) wake_up(&map->async_waitq); } -- cgit v0.10.2 From b9e0d40c0d83805bc6feb86d602e73f2cdcb17f9 Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Wed, 25 Sep 2013 21:18:13 -0400 Subject: clk: keystone: add Keystone PLL clock driver Add the driver for the PLL IPs found on Keystone 2 devices. The PLL IP typically has a multiplier, a divider and a post-divider. The PLL IPs like ARMPLL, DDRPLL and PAPLL are controlled by the memory mapped register where as the Main PLL is controlled by a PLL controller and memory map registers. Signed-off-by: Santosh Shilimkar Signed-off-by: Mike Turquette diff --git a/Documentation/devicetree/bindings/clock/keystone-pll.txt b/Documentation/devicetree/bindings/clock/keystone-pll.txt new file mode 100644 index 0000000..12bd726 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/keystone-pll.txt @@ -0,0 +1,84 @@ +Status: Unstable - ABI compatibility may be broken in the future + +Binding for keystone PLLs. The main PLL IP typically has a multiplier, +a divider and a post divider. The additional PLL IPs like ARMPLL, DDRPLL +and PAPLL are controlled by the memory mapped register where as the Main +PLL is controlled by a PLL controller registers along with memory mapped +registers. + +This binding uses the common clock binding[1]. + +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt + +Required properties: +- #clock-cells : from common clock binding; shall be set to 0. +- compatible : shall be "ti,keystone,main-pll-clock" or "ti,keystone,pll-clock" +- clocks : parent clock phandle +- reg - pll control0 and pll multipler registers +- reg-names : control and multiplier. The multiplier is applicable only for + main pll clock +- fixed-postdiv : fixed post divider value + +Example: + mainpllclk: mainpllclk@2310110 { + #clock-cells = <0>; + compatible = "ti,keystone,main-pll-clock"; + clocks = <&refclkmain>; + reg = <0x02620350 4>, <0x02310110 4>; + reg-names = "control", "multiplier"; + fixed-postdiv = <2>; + }; + + papllclk: papllclk@2620358 { + #clock-cells = <0>; + compatible = "ti,keystone,pll-clock"; + clocks = <&refclkmain>; + clock-output-names = "pa-pll-clk"; + reg = <0x02620358 4>; + reg-names = "control"; + fixed-postdiv = <6>; + }; + +Required properties: +- #clock-cells : from common clock binding; shall be set to 0. +- compatible : shall be "ti,keystone,pll-mux-clock" +- clocks : link phandles of parent clocks +- reg - pll mux register +- bit-shift : number of bits to shift the bit-mask +- bit-mask : arbitrary bitmask for programming the mux + +Optional properties: +- clock-output-names : From common clock binding. + +Example: + mainmuxclk: mainmuxclk@2310108 { + #clock-cells = <0>; + compatible = "ti,keystone,pll-mux-clock"; + clocks = <&mainpllclk>, <&refclkmain>; + reg = <0x02310108 4>; + bit-shift = <23>; + bit-mask = <1>; + clock-output-names = "mainmuxclk"; + }; + +Required properties: +- #clock-cells : from common clock binding; shall be set to 0. +- compatible : shall be "ti,keystone,pll-divider-clock" +- clocks : parent clock phandle +- reg - pll mux register +- bit-shift : number of bits to shift the bit-mask +- bit-mask : arbitrary bitmask for programming the divider + +Optional properties: +- clock-output-names : From common clock binding. + +Example: + gemtraceclk: gemtraceclk@2310120 { + #clock-cells = <0>; + compatible = "ti,keystone,pll-divider-clock"; + clocks = <&mainmuxclk>; + reg = <0x02310120 4>; + bit-shift = <0>; + bit-mask = <8>; + clock-output-names = "gemtraceclk"; + }; diff --git a/drivers/clk/keystone/pll.c b/drivers/clk/keystone/pll.c new file mode 100644 index 0000000..47a1bd9 --- /dev/null +++ b/drivers/clk/keystone/pll.c @@ -0,0 +1,305 @@ +/* + * PLL clock driver for Keystone devices + * + * Copyright (C) 2013 Texas Instruments Inc. + * Murali Karicheri + * Santosh Shilimkar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define PLLM_LOW_MASK 0x3f +#define PLLM_HIGH_MASK 0x7ffc0 +#define MAIN_PLLM_HIGH_MASK 0x7f000 +#define PLLM_HIGH_SHIFT 6 +#define PLLD_MASK 0x3f + +/** + * struct clk_pll_data - pll data structure + * @has_pllctrl: If set to non zero, lower 6 bits of multiplier is in pllm + * register of pll controller, else it is in the pll_ctrl0((bit 11-6) + * @phy_pllm: Physical address of PLLM in pll controller. Used when + * has_pllctrl is non zero. + * @phy_pll_ctl0: Physical address of PLL ctrl0. This could be that of + * Main PLL or any other PLLs in the device such as ARM PLL, DDR PLL + * or PA PLL available on keystone2. These PLLs are controlled by + * this register. Main PLL is controlled by a PLL controller. + * @pllm: PLL register map address + * @pll_ctl0: PLL controller map address + * @pllm_lower_mask: multiplier lower mask + * @pllm_upper_mask: multiplier upper mask + * @pllm_upper_shift: multiplier upper shift + * @plld_mask: divider mask + * @postdiv: Post divider + */ +struct clk_pll_data { + bool has_pllctrl; + u32 phy_pllm; + u32 phy_pll_ctl0; + void __iomem *pllm; + void __iomem *pll_ctl0; + u32 pllm_lower_mask; + u32 pllm_upper_mask; + u32 pllm_upper_shift; + u32 plld_mask; + u32 postdiv; +}; + +/** + * struct clk_pll - Main pll clock + * @hw: clk_hw for the pll + * @pll_data: PLL driver specific data + */ +struct clk_pll { + struct clk_hw hw; + struct clk_pll_data *pll_data; +}; + +#define to_clk_pll(_hw) container_of(_hw, struct clk_pll, hw) + +static unsigned long clk_pllclk_recalc(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_pll *pll = to_clk_pll(hw); + struct clk_pll_data *pll_data = pll->pll_data; + unsigned long rate = parent_rate; + u32 mult = 0, prediv, postdiv, val; + + /* + * get bits 0-5 of multiplier from pllctrl PLLM register + * if has_pllctrl is non zero + */ + if (pll_data->has_pllctrl) { + val = readl(pll_data->pllm); + mult = (val & pll_data->pllm_lower_mask); + } + + /* bit6-12 of PLLM is in Main PLL control register */ + val = readl(pll_data->pll_ctl0); + mult |= ((val & pll_data->pllm_upper_mask) + >> pll_data->pllm_upper_shift); + prediv = (val & pll_data->plld_mask); + postdiv = pll_data->postdiv; + + rate /= (prediv + 1); + rate = (rate * (mult + 1)); + rate /= postdiv; + + return rate; +} + +static const struct clk_ops clk_pll_ops = { + .recalc_rate = clk_pllclk_recalc, +}; + +static struct clk *clk_register_pll(struct device *dev, + const char *name, + const char *parent_name, + struct clk_pll_data *pll_data) +{ + struct clk_init_data init; + struct clk_pll *pll; + struct clk *clk; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clk_pll_ops; + init.flags = 0; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + + pll->pll_data = pll_data; + pll->hw.init = &init; + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) + goto out; + + return clk; +out: + kfree(pll); + return NULL; +} + +/** + * _of_clk_init - PLL initialisation via DT + * @node: device tree node for this clock + * @pllctrl: If true, lower 6 bits of multiplier is in pllm register of + * pll controller, else it is in the control regsiter0(bit 11-6) + */ +static void __init _of_pll_clk_init(struct device_node *node, bool pllctrl) +{ + struct clk_pll_data *pll_data; + const char *parent_name; + struct clk *clk; + int i; + + pll_data = kzalloc(sizeof(*pll_data), GFP_KERNEL); + if (!pll_data) { + pr_err("%s: Out of memory\n", __func__); + return; + } + + parent_name = of_clk_get_parent_name(node, 0); + if (of_property_read_u32(node, "fixed-postdiv", &pll_data->postdiv)) + goto out; + + i = of_property_match_string(node, "reg-names", "control"); + pll_data->pll_ctl0 = of_iomap(node, i); + if (!pll_data->pll_ctl0) { + pr_err("%s: ioremap failed\n", __func__); + goto out; + } + + pll_data->pllm_lower_mask = PLLM_LOW_MASK; + pll_data->pllm_upper_shift = PLLM_HIGH_SHIFT; + pll_data->plld_mask = PLLD_MASK; + pll_data->has_pllctrl = pllctrl; + if (!pll_data->has_pllctrl) { + pll_data->pllm_upper_mask = PLLM_HIGH_MASK; + } else { + pll_data->pllm_upper_mask = MAIN_PLLM_HIGH_MASK; + i = of_property_match_string(node, "reg-names", "multiplier"); + pll_data->pllm = of_iomap(node, i); + if (!pll_data->pllm) { + iounmap(pll_data->pll_ctl0); + goto out; + } + } + + clk = clk_register_pll(NULL, node->name, parent_name, pll_data); + if (clk) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + return; + } + +out: + pr_err("%s: error initializing pll %s\n", __func__, node->name); + kfree(pll_data); +} + +/** + * of_keystone_pll_clk_init - PLL initialisation DT wrapper + * @node: device tree node for this clock + */ +static void __init of_keystone_pll_clk_init(struct device_node *node) +{ + _of_pll_clk_init(node, false); +} +CLK_OF_DECLARE(keystone_pll_clock, "ti,keystone,pll-clock", + of_keystone_pll_clk_init); + +/** + * of_keystone_pll_main_clk_init - Main PLL initialisation DT wrapper + * @node: device tree node for this clock + */ +static void __init of_keystone_main_pll_clk_init(struct device_node *node) +{ + _of_pll_clk_init(node, true); +} +CLK_OF_DECLARE(keystone_main_pll_clock, "ti,keystone,main-pll-clock", + of_keystone_main_pll_clk_init); + +/** + * of_pll_div_clk_init - PLL divider setup function + * @node: device tree node for this clock + */ +static void __init of_pll_div_clk_init(struct device_node *node) +{ + const char *parent_name; + void __iomem *reg; + u32 shift, mask; + struct clk *clk; + const char *clk_name = node->name; + + of_property_read_string(node, "clock-output-names", &clk_name); + reg = of_iomap(node, 0); + if (!reg) { + pr_err("%s: ioremap failed\n", __func__); + return; + } + + parent_name = of_clk_get_parent_name(node, 0); + if (!parent_name) { + pr_err("%s: missing parent clock\n", __func__); + return; + } + + if (of_property_read_u32(node, "bit-shift", &shift)) { + pr_err("%s: missing 'shift' property\n", __func__); + return; + } + + if (of_property_read_u32(node, "bit-mask", &mask)) { + pr_err("%s: missing 'bit-mask' property\n", __func__); + return; + } + + clk = clk_register_divider(NULL, clk_name, parent_name, 0, reg, shift, + mask, 0, NULL); + if (clk) + of_clk_add_provider(node, of_clk_src_simple_get, clk); + else + pr_err("%s: error registering divider %s\n", __func__, clk_name); +} +CLK_OF_DECLARE(pll_divider_clock, "ti,keystone,pll-divider-clock", of_pll_div_clk_init); + +/** + * of_pll_mux_clk_init - PLL mux setup function + * @node: device tree node for this clock + */ +static void __init of_pll_mux_clk_init(struct device_node *node) +{ + void __iomem *reg; + u32 shift, mask; + struct clk *clk; + const char *parents[2]; + const char *clk_name = node->name; + + of_property_read_string(node, "clock-output-names", &clk_name); + reg = of_iomap(node, 0); + if (!reg) { + pr_err("%s: ioremap failed\n", __func__); + return; + } + + parents[0] = of_clk_get_parent_name(node, 0); + parents[1] = of_clk_get_parent_name(node, 1); + if (!parents[0] || !parents[1]) { + pr_err("%s: missing parent clocks\n", __func__); + return; + } + + if (of_property_read_u32(node, "bit-shift", &shift)) { + pr_err("%s: missing 'shift' property\n", __func__); + return; + } + + if (of_property_read_u32(node, "bit-mask", &mask)) { + pr_err("%s: missing 'bit-mask' property\n", __func__); + return; + } + + clk = clk_register_mux(NULL, clk_name, (const char **)&parents, + ARRAY_SIZE(parents) , 0, reg, shift, mask, + 0, NULL); + if (clk) + of_clk_add_provider(node, of_clk_src_simple_get, clk); + else + pr_err("%s: error registering mux %s\n", __func__, clk_name); +} +CLK_OF_DECLARE(pll_mux_clock, "ti,keystone,pll-mux-clock", of_pll_mux_clk_init); -- cgit v0.10.2 From 7affe5685c962ed0bc0fadf307400484b2276c89 Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Wed, 25 Sep 2013 21:18:14 -0400 Subject: clk: keystone: Add gate control clock driver Add the driver for the clock gate control which uses PSC (Power Sleep Controller) IP on Keystone 2 based SOCs. It is responsible for enabling and disabling of the clocks for different IPs present in the SoC. Signed-off-by: Santosh Shilimkar Signed-off-by: Mike Turquette diff --git a/Documentation/devicetree/bindings/clock/keystone-gate.txt b/Documentation/devicetree/bindings/clock/keystone-gate.txt new file mode 100644 index 0000000..c5aa187 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/keystone-gate.txt @@ -0,0 +1,29 @@ +Status: Unstable - ABI compatibility may be broken in the future + +Binding for Keystone gate control driver which uses PSC controller IP. + +This binding uses the common clock binding[1]. + +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt + +Required properties: +- compatible : shall be "ti,keystone,psc-clock". +- #clock-cells : from common clock binding; shall be set to 0. +- clocks : parent clock phandle +- reg : psc control and domain address address space +- reg-names : psc control and domain registers +- domain-id : psc domain id needed to check the transition state register + +Optional properties: +- clock-output-names : From common clock binding to override the + default output clock name +Example: + clkusb: clkusb { + #clock-cells = <0>; + compatible = "ti,keystone,psc-clock"; + clocks = <&chipclk16>; + clock-output-names = "usb"; + reg = <0x02350008 0xb00>, <0x02350000 0x400>; + reg-names = "control", "domain"; + domain-id = <0>; + }; diff --git a/drivers/clk/keystone/gate.c b/drivers/clk/keystone/gate.c new file mode 100644 index 0000000..1f333bc --- /dev/null +++ b/drivers/clk/keystone/gate.c @@ -0,0 +1,264 @@ +/* + * Clock driver for Keystone 2 based devices + * + * Copyright (C) 2013 Texas Instruments. + * Murali Karicheri + * Santosh Shilimkar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* PSC register offsets */ +#define PTCMD 0x120 +#define PTSTAT 0x128 +#define PDSTAT 0x200 +#define PDCTL 0x300 +#define MDSTAT 0x800 +#define MDCTL 0xa00 + +/* PSC module states */ +#define PSC_STATE_SWRSTDISABLE 0 +#define PSC_STATE_SYNCRST 1 +#define PSC_STATE_DISABLE 2 +#define PSC_STATE_ENABLE 3 + +#define MDSTAT_STATE_MASK 0x3f +#define MDSTAT_MCKOUT BIT(12) +#define PDSTAT_STATE_MASK 0x1f +#define MDCTL_FORCE BIT(31) +#define MDCTL_LRESET BIT(8) +#define PDCTL_NEXT BIT(0) + +/* Maximum timeout to bail out state transition for module */ +#define STATE_TRANS_MAX_COUNT 0xffff + +static void __iomem *domain_transition_base; + +/** + * struct clk_psc_data - PSC data + * @control_base: Base address for a PSC control + * @domain_base: Base address for a PSC domain + * @domain_id: PSC domain id number + */ +struct clk_psc_data { + void __iomem *control_base; + void __iomem *domain_base; + u32 domain_id; +}; + +/** + * struct clk_psc - PSC clock structure + * @hw: clk_hw for the psc + * @psc_data: PSC driver specific data + * @lock: Spinlock used by the driver + */ +struct clk_psc { + struct clk_hw hw; + struct clk_psc_data *psc_data; + spinlock_t *lock; +}; + +static DEFINE_SPINLOCK(psc_lock); + +#define to_clk_psc(_hw) container_of(_hw, struct clk_psc, hw) + +static void psc_config(void __iomem *control_base, void __iomem *domain_base, + u32 next_state, u32 domain_id) +{ + u32 ptcmd, pdstat, pdctl, mdstat, mdctl, ptstat; + u32 count = STATE_TRANS_MAX_COUNT; + + mdctl = readl(control_base + MDCTL); + mdctl &= ~MDSTAT_STATE_MASK; + mdctl |= next_state; + /* For disable, we always put the module in local reset */ + if (next_state == PSC_STATE_DISABLE) + mdctl &= ~MDCTL_LRESET; + writel(mdctl, control_base + MDCTL); + + pdstat = readl(domain_base + PDSTAT); + if (!(pdstat & PDSTAT_STATE_MASK)) { + pdctl = readl(domain_base + PDCTL); + pdctl |= PDCTL_NEXT; + writel(pdctl, domain_base + PDCTL); + } + + ptcmd = 1 << domain_id; + writel(ptcmd, domain_transition_base + PTCMD); + do { + ptstat = readl(domain_transition_base + PTSTAT); + } while (((ptstat >> domain_id) & 1) && count--); + + count = STATE_TRANS_MAX_COUNT; + do { + mdstat = readl(control_base + MDSTAT); + } while (!((mdstat & MDSTAT_STATE_MASK) == next_state) && count--); +} + +static int keystone_clk_is_enabled(struct clk_hw *hw) +{ + struct clk_psc *psc = to_clk_psc(hw); + struct clk_psc_data *data = psc->psc_data; + u32 mdstat = readl(data->control_base + MDSTAT); + + return (mdstat & MDSTAT_MCKOUT) ? 1 : 0; +} + +static int keystone_clk_enable(struct clk_hw *hw) +{ + struct clk_psc *psc = to_clk_psc(hw); + struct clk_psc_data *data = psc->psc_data; + unsigned long flags = 0; + + if (psc->lock) + spin_lock_irqsave(psc->lock, flags); + + psc_config(data->control_base, data->domain_base, + PSC_STATE_ENABLE, data->domain_id); + + if (psc->lock) + spin_unlock_irqrestore(psc->lock, flags); + + return 0; +} + +static void keystone_clk_disable(struct clk_hw *hw) +{ + struct clk_psc *psc = to_clk_psc(hw); + struct clk_psc_data *data = psc->psc_data; + unsigned long flags = 0; + + if (psc->lock) + spin_lock_irqsave(psc->lock, flags); + + psc_config(data->control_base, data->domain_base, + PSC_STATE_DISABLE, data->domain_id); + + if (psc->lock) + spin_unlock_irqrestore(psc->lock, flags); +} + +static const struct clk_ops clk_psc_ops = { + .enable = keystone_clk_enable, + .disable = keystone_clk_disable, + .is_enabled = keystone_clk_is_enabled, +}; + +/** + * clk_register_psc - register psc clock + * @dev: device that is registering this clock + * @name: name of this clock + * @parent_name: name of clock's parent + * @psc_data: platform data to configure this clock + * @lock: spinlock used by this clock + */ +static struct clk *clk_register_psc(struct device *dev, + const char *name, + const char *parent_name, + struct clk_psc_data *psc_data, + spinlock_t *lock) +{ + struct clk_init_data init; + struct clk_psc *psc; + struct clk *clk; + + psc = kzalloc(sizeof(*psc), GFP_KERNEL); + if (!psc) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clk_psc_ops; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + + psc->psc_data = psc_data; + psc->lock = lock; + psc->hw.init = &init; + + clk = clk_register(NULL, &psc->hw); + if (IS_ERR(clk)) + kfree(psc); + + return clk; +} + +/** + * of_psc_clk_init - initialize psc clock through DT + * @node: device tree node for this clock + * @lock: spinlock used by this clock + */ +static void __init of_psc_clk_init(struct device_node *node, spinlock_t *lock) +{ + const char *clk_name = node->name; + const char *parent_name; + struct clk_psc_data *data; + struct clk *clk; + int i; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + pr_err("%s: Out of memory\n", __func__); + return; + } + + i = of_property_match_string(node, "reg-names", "control"); + data->control_base = of_iomap(node, i); + if (!data->control_base) { + pr_err("%s: control ioremap failed\n", __func__); + goto out; + } + + i = of_property_match_string(node, "reg-names", "domain"); + data->domain_base = of_iomap(node, i); + if (!data->domain_base) { + pr_err("%s: domain ioremap failed\n", __func__); + iounmap(data->control_base); + goto out; + } + + of_property_read_u32(node, "domain-id", &data->domain_id); + + /* Domain transition registers at fixed address space of domain_id 0 */ + if (!domain_transition_base && !data->domain_id) + domain_transition_base = data->domain_base; + + of_property_read_string(node, "clock-output-names", &clk_name); + parent_name = of_clk_get_parent_name(node, 0); + if (!parent_name) { + pr_err("%s: Parent clock not found\n", __func__); + goto out; + } + + clk = clk_register_psc(NULL, clk_name, parent_name, data, lock); + if (clk) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + return; + } + + pr_err("%s: error registering clk %s\n", __func__, node->name); +out: + kfree(data); + return; +} + +/** + * of_keystone_psc_clk_init - initialize psc clock through DT + * @node: device tree node for this clock + */ +static void __init of_keystone_psc_clk_init(struct device_node *node) +{ + of_psc_clk_init(node, &psc_lock); +} +CLK_OF_DECLARE(keystone_gate_clk, "ti,keystone,psc-clock", + of_keystone_psc_clk_init); -- cgit v0.10.2 From 6cfc229d6f967041b5e1ee56a5bb87a500f31311 Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Wed, 25 Sep 2013 21:18:15 -0400 Subject: clk: keystone: Build Keystone clock drivers Now build the keystone common clock drivers. The build is made conditional based on COMMON_CLK_KEYSTONE Signed-off-by: Santosh Shilimkar Signed-off-by: Mike Turquette diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index dd37f91..5c51115 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -100,6 +100,13 @@ config COMMON_CLK_XGENE ---help--- Sypport for the APM X-Gene SoC reference, PLL, and device clocks. +config COMMON_CLK_KEYSTONE + tristate "Clock drivers for Keystone based SOCs" + depends on ARCH_KEYSTONE && OF + ---help--- + Supports clock drivers for Keystone based SOCs. These SOCs have local + a power sleep control module that gate the clock to the IPs and PLLs. + endmenu source "drivers/clk/mvebu/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 270f3fd..df33820 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_ARCH_ZYNQ) += zynq/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_PLAT_SAMSUNG) += samsung/ obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o +obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/ obj-$(CONFIG_X86) += x86/ diff --git a/drivers/clk/keystone/Makefile b/drivers/clk/keystone/Makefile new file mode 100644 index 0000000..0477cf6 --- /dev/null +++ b/drivers/clk/keystone/Makefile @@ -0,0 +1 @@ +obj-y += pll.o gate.o -- cgit v0.10.2 From f8fe36f6083a70270a7305f7740b124ff1e8aea7 Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Mon, 7 Oct 2013 23:25:44 -0300 Subject: clk/zynq: Fix possible memory leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The zynq_clk_register_fclk function can leak memory (fclk_lock) when unable to alloc memory for fclk_gate_lock Signed-off-by: Felipe Pena Acked-by: Sören Brinkmann Signed-off-by: Mike Turquette diff --git a/drivers/clk/zynq/clkc.c b/drivers/clk/zynq/clkc.c index cc40fe6..10772aa 100644 --- a/drivers/clk/zynq/clkc.c +++ b/drivers/clk/zynq/clkc.c @@ -117,13 +117,19 @@ static void __init zynq_clk_register_fclk(enum zynq_clk fclk, goto err; fclk_gate_lock = kmalloc(sizeof(*fclk_gate_lock), GFP_KERNEL); if (!fclk_gate_lock) - goto err; + goto err_fclk_gate_lock; spin_lock_init(fclk_lock); spin_lock_init(fclk_gate_lock); mux_name = kasprintf(GFP_KERNEL, "%s_mux", clk_name); + if (!mux_name) + goto err_mux_name; div0_name = kasprintf(GFP_KERNEL, "%s_div0", clk_name); + if (!div0_name) + goto err_div0_name; div1_name = kasprintf(GFP_KERNEL, "%s_div1", clk_name); + if (!div1_name) + goto err_div1_name; clk = clk_register_mux(NULL, mux_name, parents, 4, CLK_SET_RATE_NO_REPARENT, fclk_ctrl_reg, 4, 2, 0, @@ -147,6 +153,14 @@ static void __init zynq_clk_register_fclk(enum zynq_clk fclk, return; +err_div1_name: + kfree(div0_name); +err_div0_name: + kfree(mux_name); +err_mux_name: + kfree(fclk_gate_lock); +err_fclk_gate_lock: + kfree(fclk_lock); err: clks[fclk] = ERR_PTR(-ENOMEM); } -- cgit v0.10.2 From f61027426a5bc7093aa8359a411b053a35bb4b68 Mon Sep 17 00:00:00 2001 From: Mike Turquette Date: Mon, 7 Oct 2013 23:12:13 -0700 Subject: clk: of: helper for determining number of parent clocks Walks the "clocks" array of parent clock phandles and returns the number. Signed-off-by: Mike Turquette diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 32e2fed..2cf2ea6 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2202,6 +2202,12 @@ struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec) return clk; } +int of_clk_get_parent_count(struct device_node *np) +{ + return of_count_phandle_with_args(np, "clocks", "#clock-cells"); +} +EXPORT_SYMBOL_GPL(of_clk_get_parent_count); + const char *of_clk_get_parent_name(struct device_node *np, int index) { struct of_phandle_args clkspec; diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 73bdb69..7e59253 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -472,6 +472,7 @@ void of_clk_del_provider(struct device_node *np); struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec, void *data); struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data); +int of_clk_get_parent_count(struct device_node *np); const char *of_clk_get_parent_name(struct device_node *np, int index); void of_clk_init(const struct of_device_id *matches); -- cgit v0.10.2 From 606fca94f7bd156d37be1b38b3aded3ede5994b1 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sat, 28 Sep 2013 17:38:48 +0530 Subject: pinctrl: remove redundant of_match_ptr The data structure of_match_ptr() protects is always compiled in. Hence of_match_ptr() is not needed. This is a squash commit of: pinctrl: at91: Remove redundant of_match_ptr pinctrl: exynos5440: Remove redundant of_match_ptr pinctrl: imx35: Remove redundant of_match_ptr pinctrl: imx51: Remove redundant of_match_ptr pinctrl: imx53: Remove redundant of_match_ptr pinctrl: imx6dl: Remove redundant of_match_ptr pinctrl: imx6q: Remove redundant of_match_ptr pinctrl: samsung: Remove redundant of_match_ptr pinctrl: vf610: Remove redundant of_match_ptr pinctrl: imx6sl: Remove redundant of_match_ptr pinctrl: plgpio: Remove redundant of_match_ptr Acked-by: Viresh Kumar Signed-off-by: Sachin Kamat Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index 8ed9be8..0c8bf93 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -1679,7 +1679,7 @@ static struct platform_driver at91_gpio_driver = { .driver = { .name = "gpio-at91", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(at91_gpio_of_match), + .of_match_table = at91_gpio_of_match, }, .probe = at91_gpio_probe, }; @@ -1688,7 +1688,7 @@ static struct platform_driver at91_pinctrl_driver = { .driver = { .name = "pinctrl-at91", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(at91_pinctrl_of_match), + .of_match_table = at91_pinctrl_of_match, }, .probe = at91_pinctrl_probe, .remove = at91_pinctrl_remove, diff --git a/drivers/pinctrl/pinctrl-exynos5440.c b/drivers/pinctrl/pinctrl-exynos5440.c index 544d469..8fe2ab0 100644 --- a/drivers/pinctrl/pinctrl-exynos5440.c +++ b/drivers/pinctrl/pinctrl-exynos5440.c @@ -1048,7 +1048,7 @@ static struct platform_driver exynos5440_pinctrl_driver = { .driver = { .name = "exynos5440-pinctrl", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(exynos5440_pinctrl_dt_match), + .of_match_table = exynos5440_pinctrl_dt_match, }, }; diff --git a/drivers/pinctrl/pinctrl-imx35.c b/drivers/pinctrl/pinctrl-imx35.c index c454982..278a04a 100644 --- a/drivers/pinctrl/pinctrl-imx35.c +++ b/drivers/pinctrl/pinctrl-imx35.c @@ -1019,7 +1019,7 @@ static struct platform_driver imx35_pinctrl_driver = { .driver = { .name = "imx35-pinctrl", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(imx35_pinctrl_of_match), + .of_match_table = imx35_pinctrl_of_match, }, .probe = imx35_pinctrl_probe, .remove = imx_pinctrl_remove, diff --git a/drivers/pinctrl/pinctrl-imx51.c b/drivers/pinctrl/pinctrl-imx51.c index db268b9..19ab182 100644 --- a/drivers/pinctrl/pinctrl-imx51.c +++ b/drivers/pinctrl/pinctrl-imx51.c @@ -782,7 +782,7 @@ static struct platform_driver imx51_pinctrl_driver = { .driver = { .name = "imx51-pinctrl", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(imx51_pinctrl_of_match), + .of_match_table = imx51_pinctrl_of_match, }, .probe = imx51_pinctrl_probe, .remove = imx_pinctrl_remove, diff --git a/drivers/pinctrl/pinctrl-imx53.c b/drivers/pinctrl/pinctrl-imx53.c index 17562ae..f8d45c4 100644 --- a/drivers/pinctrl/pinctrl-imx53.c +++ b/drivers/pinctrl/pinctrl-imx53.c @@ -468,7 +468,7 @@ static struct platform_driver imx53_pinctrl_driver = { .driver = { .name = "imx53-pinctrl", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(imx53_pinctrl_of_match), + .of_match_table = imx53_pinctrl_of_match, }, .probe = imx53_pinctrl_probe, .remove = imx_pinctrl_remove, diff --git a/drivers/pinctrl/pinctrl-imx6dl.c b/drivers/pinctrl/pinctrl-imx6dl.c index a76b724..db2a148 100644 --- a/drivers/pinctrl/pinctrl-imx6dl.c +++ b/drivers/pinctrl/pinctrl-imx6dl.c @@ -474,7 +474,7 @@ static struct platform_driver imx6dl_pinctrl_driver = { .driver = { .name = "imx6dl-pinctrl", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(imx6dl_pinctrl_of_match), + .of_match_table = imx6dl_pinctrl_of_match, }, .probe = imx6dl_pinctrl_probe, .remove = imx_pinctrl_remove, diff --git a/drivers/pinctrl/pinctrl-imx6q.c b/drivers/pinctrl/pinctrl-imx6q.c index 76dd9c4..8eb5ac1 100644 --- a/drivers/pinctrl/pinctrl-imx6q.c +++ b/drivers/pinctrl/pinctrl-imx6q.c @@ -480,7 +480,7 @@ static struct platform_driver imx6q_pinctrl_driver = { .driver = { .name = "imx6q-pinctrl", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(imx6q_pinctrl_of_match), + .of_match_table = imx6q_pinctrl_of_match, }, .probe = imx6q_pinctrl_probe, .remove = imx_pinctrl_remove, diff --git a/drivers/pinctrl/pinctrl-imx6sl.c b/drivers/pinctrl/pinctrl-imx6sl.c index 4eb7cca..f21b738 100644 --- a/drivers/pinctrl/pinctrl-imx6sl.c +++ b/drivers/pinctrl/pinctrl-imx6sl.c @@ -380,7 +380,7 @@ static struct platform_driver imx6sl_pinctrl_driver = { .driver = { .name = "imx6sl-pinctrl", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(imx6sl_pinctrl_of_match), + .of_match_table = imx6sl_pinctrl_of_match, }, .probe = imx6sl_pinctrl_probe, .remove = imx_pinctrl_remove, diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index 92a9d6c..47ec2e8 100644 --- a/drivers/pinctrl/pinctrl-samsung.c +++ b/drivers/pinctrl/pinctrl-samsung.c @@ -1148,7 +1148,7 @@ static struct platform_driver samsung_pinctrl_driver = { .driver = { .name = "samsung-pinctrl", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(samsung_pinctrl_dt_match), + .of_match_table = samsung_pinctrl_dt_match, }, }; diff --git a/drivers/pinctrl/pinctrl-vf610.c b/drivers/pinctrl/pinctrl-vf610.c index 68a970b..bddd913 100644 --- a/drivers/pinctrl/pinctrl-vf610.c +++ b/drivers/pinctrl/pinctrl-vf610.c @@ -316,7 +316,7 @@ static struct platform_driver vf610_pinctrl_driver = { .driver = { .name = "vf610-pinctrl", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(vf610_pinctrl_of_match), + .of_match_table = vf610_pinctrl_of_match, }, .probe = vf610_pinctrl_probe, .remove = imx_pinctrl_remove, diff --git a/drivers/pinctrl/spear/pinctrl-plgpio.c b/drivers/pinctrl/spear/pinctrl-plgpio.c index 0a7f0bdb..ff2940e 100644 --- a/drivers/pinctrl/spear/pinctrl-plgpio.c +++ b/drivers/pinctrl/spear/pinctrl-plgpio.c @@ -735,7 +735,7 @@ static struct platform_driver plgpio_driver = { .owner = THIS_MODULE, .name = "spear-plgpio", .pm = &plgpio_dev_pm_ops, - .of_match_table = of_match_ptr(plgpio_of_match), + .of_match_table = plgpio_of_match, }, }; -- cgit v0.10.2 From fb85f4290ddf646548ac30848863f3211c3d2e54 Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Sun, 29 Sep 2013 22:27:57 +0800 Subject: pinctrl: sirf: add lost uart0-no-stream-control pingroup for prima2 the old codes defined uart0_nostreamctrl_pins, but missed pingroup and padmux definition for it. this patch fixes it. Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Linus Walleij diff --git a/arch/arm/boot/dts/prima2.dtsi b/arch/arm/boot/dts/prima2.dtsi index bbeb623..766aa39 100644 --- a/arch/arm/boot/dts/prima2.dtsi +++ b/arch/arm/boot/dts/prima2.dtsi @@ -341,6 +341,12 @@ sirf,function = "uart0"; }; }; + uart0_noflow_pins_a: uart0@1 { + uart { + sirf,pins = "uart0_nostreamctrlgrp"; + sirf,function = "uart0_nostreamctrl"; + }; + }; uart1_pins_a: uart1@0 { uart { sirf,pins = "uart1grp"; diff --git a/drivers/pinctrl/sirf/pinctrl-prima2.c b/drivers/pinctrl/sirf/pinctrl-prima2.c index 1f0ad1e..29fc842 100644 --- a/drivers/pinctrl/sirf/pinctrl-prima2.c +++ b/drivers/pinctrl/sirf/pinctrl-prima2.c @@ -764,6 +764,7 @@ static const struct sirfsoc_pin_group sirfsoc_pin_groups[] = { SIRFSOC_PIN_GROUP("lcd_24bitsgrp", lcd_24bits_pins), SIRFSOC_PIN_GROUP("lcdrom_grp", lcdrom_pins), SIRFSOC_PIN_GROUP("uart0grp", uart0_pins), + SIRFSOC_PIN_GROUP("uart0_nostreamctrlgrp", uart0_nostreamctrl_pins), SIRFSOC_PIN_GROUP("uart1grp", uart1_pins), SIRFSOC_PIN_GROUP("uart2grp", uart2_pins), SIRFSOC_PIN_GROUP("uart2_nostreamctrlgrp", uart2_nostreamctrl_pins), @@ -803,6 +804,7 @@ static const char * const lcd_18bitsgrp[] = { "lcd_18bitsgrp" }; static const char * const lcd_24bitsgrp[] = { "lcd_24bitsgrp" }; static const char * const lcdromgrp[] = { "lcdromgrp" }; static const char * const uart0grp[] = { "uart0grp" }; +static const char * const uart0_nostreamctrlgrp[] = { "uart0_nostreamctrlgrp" }; static const char * const uart1grp[] = { "uart1grp" }; static const char * const uart2grp[] = { "uart2grp" }; static const char * const uart2_nostreamctrlgrp[] = { "uart2_nostreamctrlgrp" }; @@ -842,6 +844,7 @@ static const struct sirfsoc_pmx_func sirfsoc_pmx_functions[] = { SIRFSOC_PMX_FUNCTION("lcd_24bits", lcd_24bitsgrp, lcd_24bits_padmux), SIRFSOC_PMX_FUNCTION("lcdrom", lcdromgrp, lcdrom_padmux), SIRFSOC_PMX_FUNCTION("uart0", uart0grp, uart0_padmux), + SIRFSOC_PMX_FUNCTION("uart0_nostreamctrl", uart0_nostreamctrlgrp, uart0_nostreamctrl_padmux), SIRFSOC_PMX_FUNCTION("uart1", uart1grp, uart1_padmux), SIRFSOC_PMX_FUNCTION("uart2", uart2grp, uart2_padmux), SIRFSOC_PMX_FUNCTION("uart2_nostreamctrl", uart2_nostreamctrlgrp, uart2_nostreamctrl_padmux), -- cgit v0.10.2 From af614b2301f0e30423240a754ec2812a4c793201 Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Sun, 29 Sep 2013 22:27:58 +0800 Subject: pinctrl: sirf: add lost USP-based UART pin groups for prima2 USP(Universal Serial Ports) can be UART as commit 5df831117b85a08e7aa, this patch defines the USP-based UART function pin groups for prima2. Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Linus Walleij diff --git a/arch/arm/boot/dts/prima2.dtsi b/arch/arm/boot/dts/prima2.dtsi index 766aa39..569763a 100644 --- a/arch/arm/boot/dts/prima2.dtsi +++ b/arch/arm/boot/dts/prima2.dtsi @@ -485,18 +485,42 @@ sirf,function = "usp0"; }; }; + usp0_uart_nostreamctrl_pins_a: usp0@1 { + usp0 { + sirf,pins = + "usp0_uart_nostreamctrl_grp"; + sirf,function = + "usp0_uart_nostreamctrl"; + }; + }; usp1_pins_a: usp1@0 { usp1 { sirf,pins = "usp1grp"; sirf,function = "usp1"; }; }; + usp1_uart_nostreamctrl_pins_a: usp1@1 { + usp1 { + sirf,pins = + "usp1_uart_nostreamctrl_grp"; + sirf,function = + "usp1_uart_nostreamctrl"; + }; + }; usp2_pins_a: usp2@0 { usp2 { sirf,pins = "usp2grp"; sirf,function = "usp2"; }; }; + usp2_uart_nostreamctrl_pins_a: usp2@1 { + usp2 { + sirf,pins = + "usp2_uart_nostreamctrl_grp"; + sirf,function = + "usp2_uart_nostreamctrl"; + }; + }; usb0_utmi_drvbus_pins_a: usb0_utmi_drvbus@0 { usb0_utmi_drvbus { sirf,pins = "usb0_utmi_drvbusgrp"; diff --git a/drivers/pinctrl/sirf/pinctrl-prima2.c b/drivers/pinctrl/sirf/pinctrl-prima2.c index 29fc842..241d839 100644 --- a/drivers/pinctrl/sirf/pinctrl-prima2.c +++ b/drivers/pinctrl/sirf/pinctrl-prima2.c @@ -485,6 +485,20 @@ static const struct sirfsoc_padmux usp0_padmux = { static const unsigned usp0_pins[] = { 51, 52, 53, 54, 55 }; +static const struct sirfsoc_muxmask usp0_uart_nostreamctrl_muxmask[] = { + { + .group = 1, + .mask = BIT(20) | BIT(21), + }, +}; + +static const struct sirfsoc_padmux usp0_uart_nostreamctrl_padmux = { + .muxmask_counts = ARRAY_SIZE(usp0_uart_nostreamctrl_muxmask), + .muxmask = usp0_uart_nostreamctrl_muxmask, +}; + +static const unsigned usp0_uart_nostreamctrl_pins[] = { 52, 53 }; + static const struct sirfsoc_muxmask usp1_muxmask[] = { { .group = 1, @@ -501,6 +515,20 @@ static const struct sirfsoc_padmux usp1_padmux = { static const unsigned usp1_pins[] = { 56, 57, 58, 59, 60 }; +static const struct sirfsoc_muxmask usp1_uart_nostreamctrl_muxmask[] = { + { + .group = 1, + .mask = BIT(25) | BIT(26), + }, +}; + +static const struct sirfsoc_padmux usp1_uart_nostreamctrl_padmux = { + .muxmask_counts = ARRAY_SIZE(usp1_uart_nostreamctrl_muxmask), + .muxmask = usp1_uart_nostreamctrl_muxmask, +}; + +static const unsigned usp1_uart_nostreamctrl_pins[] = { 57, 58 }; + static const struct sirfsoc_muxmask usp2_muxmask[] = { { .group = 1, @@ -520,6 +548,20 @@ static const struct sirfsoc_padmux usp2_padmux = { static const unsigned usp2_pins[] = { 61, 62, 63, 64, 65 }; +static const struct sirfsoc_muxmask usp2_uart_nostreamctrl_muxmask[] = { + { + .group = 1, + .mask = BIT(30) | BIT(31), + }, +}; + +static const struct sirfsoc_padmux usp2_uart_nostreamctrl_padmux = { + .muxmask_counts = ARRAY_SIZE(usp2_uart_nostreamctrl_muxmask), + .muxmask = usp2_uart_nostreamctrl_muxmask, +}; + +static const unsigned usp2_uart_nostreamctrl_pins[] = { 62, 63 }; + static const struct sirfsoc_muxmask nand_muxmask[] = { { .group = 2, @@ -769,8 +811,14 @@ static const struct sirfsoc_pin_group sirfsoc_pin_groups[] = { SIRFSOC_PIN_GROUP("uart2grp", uart2_pins), SIRFSOC_PIN_GROUP("uart2_nostreamctrlgrp", uart2_nostreamctrl_pins), SIRFSOC_PIN_GROUP("usp0grp", usp0_pins), + SIRFSOC_PIN_GROUP("usp0_uart_nostreamctrl_grp", + usp0_uart_nostreamctrl_pins), SIRFSOC_PIN_GROUP("usp1grp", usp1_pins), + SIRFSOC_PIN_GROUP("usp1_uart_nostreamctrl_grp", + usp1_uart_nostreamctrl_pins), SIRFSOC_PIN_GROUP("usp2grp", usp2_pins), + SIRFSOC_PIN_GROUP("usp2_uart_nostreamctrl_grp", + usp2_uart_nostreamctrl_pins), SIRFSOC_PIN_GROUP("i2c0grp", i2c0_pins), SIRFSOC_PIN_GROUP("i2c1grp", i2c1_pins), SIRFSOC_PIN_GROUP("pwm0grp", pwm0_pins), @@ -809,8 +857,14 @@ static const char * const uart1grp[] = { "uart1grp" }; static const char * const uart2grp[] = { "uart2grp" }; static const char * const uart2_nostreamctrlgrp[] = { "uart2_nostreamctrlgrp" }; static const char * const usp0grp[] = { "usp0grp" }; +static const char * const usp0_uart_nostreamctrl_grp[] = + { "usp0_uart_nostreamctrl_grp" }; static const char * const usp1grp[] = { "usp1grp" }; +static const char * const usp1_uart_nostreamctrl_grp[] = + { "usp1_uart_nostreamctrl_grp" }; static const char * const usp2grp[] = { "usp2grp" }; +static const char * const usp2_uart_nostreamctrl_grp[] = + { "usp2_uart_nostreamctrl_grp" }; static const char * const i2c0grp[] = { "i2c0grp" }; static const char * const i2c1grp[] = { "i2c1grp" }; static const char * const pwm0grp[] = { "pwm0grp" }; @@ -849,8 +903,14 @@ static const struct sirfsoc_pmx_func sirfsoc_pmx_functions[] = { SIRFSOC_PMX_FUNCTION("uart2", uart2grp, uart2_padmux), SIRFSOC_PMX_FUNCTION("uart2_nostreamctrl", uart2_nostreamctrlgrp, uart2_nostreamctrl_padmux), SIRFSOC_PMX_FUNCTION("usp0", usp0grp, usp0_padmux), + SIRFSOC_PMX_FUNCTION("usp0_uart_nostreamctrl", + usp0_uart_nostreamctrl_grp, usp0_uart_nostreamctrl_padmux), SIRFSOC_PMX_FUNCTION("usp1", usp1grp, usp1_padmux), + SIRFSOC_PMX_FUNCTION("usp1_uart_nostreamctrl", + usp1_uart_nostreamctrl_grp, usp1_uart_nostreamctrl_padmux), SIRFSOC_PMX_FUNCTION("usp2", usp2grp, usp2_padmux), + SIRFSOC_PMX_FUNCTION("usp2_uart_nostreamctrl", + usp2_uart_nostreamctrl_grp, usp2_uart_nostreamctrl_padmux), SIRFSOC_PMX_FUNCTION("i2c0", i2c0grp, i2c0_padmux), SIRFSOC_PMX_FUNCTION("i2c1", i2c1grp, i2c1_padmux), SIRFSOC_PMX_FUNCTION("pwm0", pwm0grp, pwm0_padmux), -- cgit v0.10.2 From 6a08a92ec45782e5543addf5f8785e2560a078f6 Mon Sep 17 00:00:00 2001 From: Rong Wang Date: Sun, 29 Sep 2013 22:27:59 +0800 Subject: pinctrl: sirf: add USB1/UART1 pinmux usb/uart share dn and dp of USB1 can share with UART1(UART1 can route rx,tx to dn and dp pins of USB1). here we add this pinmux capability. USB1/UART1 mode selection has dedicated control register in RSC module, here we attach the register offset of private data of related pin groups. Signed-off-by: Rong Wang Signed-off-by: Barry Song Signed-off-by: Linus Walleij diff --git a/arch/arm/boot/dts/atlas6.dtsi b/arch/arm/boot/dts/atlas6.dtsi index 8678e0c..378d411 100644 --- a/arch/arm/boot/dts/atlas6.dtsi +++ b/arch/arm/boot/dts/atlas6.dtsi @@ -515,6 +515,18 @@ sirf,function = "usb1_utmi_drvbus"; }; }; + usb1_dp_dn_pins_a: usb1_dp_dn@0 { + usb1_dp_dn { + sirf,pins = "usb1_dp_dngrp"; + sirf,function = "usb1_dp_dn"; + }; + }; + uart1_route_io_usb1_pins_a: uart1_route_io_usb1@0 { + uart1_route_io_usb1 { + sirf,pins = "uart1_route_io_usb1grp"; + sirf,function = "uart1_route_io_usb1"; + }; + }; warm_rst_pins_a: warm_rst@0 { warm_rst { sirf,pins = "warm_rstgrp"; diff --git a/arch/arm/boot/dts/prima2.dtsi b/arch/arm/boot/dts/prima2.dtsi index 569763a..fb2ffae 100644 --- a/arch/arm/boot/dts/prima2.dtsi +++ b/arch/arm/boot/dts/prima2.dtsi @@ -533,6 +533,18 @@ sirf,function = "usb1_utmi_drvbus"; }; }; + usb1_dp_dn_pins_a: usb1_dp_dn@0 { + usb1_dp_dn { + sirf,pins = "usb1_dp_dngrp"; + sirf,function = "usb1_dp_dn"; + }; + }; + uart1_route_io_usb1_pins_a: uart1_route_io_usb1@0 { + uart1_route_io_usb1 { + sirf,pins = "uart1_route_io_usb1grp"; + sirf,function = "uart1_route_io_usb1"; + }; + }; warm_rst_pins_a: warm_rst@0 { warm_rst { sirf,pins = "warm_rstgrp"; diff --git a/drivers/pinctrl/sirf/pinctrl-atlas6.c b/drivers/pinctrl/sirf/pinctrl-atlas6.c index edf45a6..8ab7898 100644 --- a/drivers/pinctrl/sirf/pinctrl-atlas6.c +++ b/drivers/pinctrl/sirf/pinctrl-atlas6.c @@ -122,6 +122,9 @@ static const struct pinctrl_pin_desc sirfsoc_pads[] = { PINCTRL_PIN(100, "ac97_dout"), PINCTRL_PIN(101, "ac97_din"), PINCTRL_PIN(102, "x_rtc_io"), + + PINCTRL_PIN(103, "x_usb1_dp"), + PINCTRL_PIN(104, "x_usb1_dn"), }; static const struct sirfsoc_muxmask lcd_16bits_sirfsoc_muxmask[] = { @@ -139,6 +142,7 @@ static const struct sirfsoc_muxmask lcd_16bits_sirfsoc_muxmask[] = { static const struct sirfsoc_padmux lcd_16bits_padmux = { .muxmask_counts = ARRAY_SIZE(lcd_16bits_sirfsoc_muxmask), .muxmask = lcd_16bits_sirfsoc_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(4), .funcval = 0, }; @@ -164,6 +168,7 @@ static const struct sirfsoc_muxmask lcd_18bits_muxmask[] = { static const struct sirfsoc_padmux lcd_18bits_padmux = { .muxmask_counts = ARRAY_SIZE(lcd_18bits_muxmask), .muxmask = lcd_18bits_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(4) | BIT(15), .funcval = 0, }; @@ -189,6 +194,7 @@ static const struct sirfsoc_muxmask lcd_24bits_muxmask[] = { static const struct sirfsoc_padmux lcd_24bits_padmux = { .muxmask_counts = ARRAY_SIZE(lcd_24bits_muxmask), .muxmask = lcd_24bits_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(4) | BIT(15), .funcval = 0, }; @@ -214,6 +220,7 @@ static const struct sirfsoc_muxmask lcdrom_muxmask[] = { static const struct sirfsoc_padmux lcdrom_padmux = { .muxmask_counts = ARRAY_SIZE(lcdrom_muxmask), .muxmask = lcdrom_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(4), .funcval = BIT(4), }; @@ -237,6 +244,7 @@ static const struct sirfsoc_muxmask uart0_muxmask[] = { static const struct sirfsoc_padmux uart0_padmux = { .muxmask_counts = ARRAY_SIZE(uart0_muxmask), .muxmask = uart0_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(9), .funcval = BIT(9), }; @@ -284,6 +292,7 @@ static const struct sirfsoc_muxmask uart2_muxmask[] = { static const struct sirfsoc_padmux uart2_padmux = { .muxmask_counts = ARRAY_SIZE(uart2_muxmask), .muxmask = uart2_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(10), .funcval = BIT(10), }; @@ -317,6 +326,7 @@ static const struct sirfsoc_muxmask sdmmc3_muxmask[] = { static const struct sirfsoc_padmux sdmmc3_padmux = { .muxmask_counts = ARRAY_SIZE(sdmmc3_muxmask), .muxmask = sdmmc3_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(7), .funcval = 0, }; @@ -336,6 +346,7 @@ static const struct sirfsoc_muxmask spi0_muxmask[] = { static const struct sirfsoc_padmux spi0_padmux = { .muxmask_counts = ARRAY_SIZE(spi0_muxmask), .muxmask = spi0_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(7), .funcval = BIT(7), }; @@ -352,6 +363,7 @@ static const struct sirfsoc_muxmask cko1_muxmask[] = { static const struct sirfsoc_padmux cko1_padmux = { .muxmask_counts = ARRAY_SIZE(cko1_muxmask), .muxmask = cko1_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(3), .funcval = 0, }; @@ -371,6 +383,7 @@ static const struct sirfsoc_muxmask i2s_muxmask[] = { static const struct sirfsoc_padmux i2s_padmux = { .muxmask_counts = ARRAY_SIZE(i2s_muxmask), .muxmask = i2s_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(3), .funcval = BIT(3), }; @@ -390,6 +403,7 @@ static const struct sirfsoc_muxmask i2s_no_din_muxmask[] = { static const struct sirfsoc_padmux i2s_no_din_padmux = { .muxmask_counts = ARRAY_SIZE(i2s_no_din_muxmask), .muxmask = i2s_no_din_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(3), .funcval = BIT(3), }; @@ -409,6 +423,7 @@ static const struct sirfsoc_muxmask i2s_6chn_muxmask[] = { static const struct sirfsoc_padmux i2s_6chn_padmux = { .muxmask_counts = ARRAY_SIZE(i2s_6chn_muxmask), .muxmask = i2s_6chn_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(1) | BIT(3) | BIT(9), .funcval = BIT(1) | BIT(3) | BIT(9), }; @@ -439,6 +454,7 @@ static const struct sirfsoc_muxmask spi1_muxmask[] = { static const struct sirfsoc_padmux spi1_padmux = { .muxmask_counts = ARRAY_SIZE(spi1_muxmask), .muxmask = spi1_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(16), .funcval = 0, }; @@ -455,6 +471,7 @@ static const struct sirfsoc_muxmask sdmmc1_muxmask[] = { static const struct sirfsoc_padmux sdmmc1_padmux = { .muxmask_counts = ARRAY_SIZE(sdmmc1_muxmask), .muxmask = sdmmc1_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(5), .funcval = BIT(5), }; @@ -471,6 +488,7 @@ static const struct sirfsoc_muxmask gps_muxmask[] = { static const struct sirfsoc_padmux gps_padmux = { .muxmask_counts = ARRAY_SIZE(gps_muxmask), .muxmask = gps_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(13), .funcval = 0, }; @@ -487,6 +505,7 @@ static const struct sirfsoc_muxmask sdmmc5_muxmask[] = { static const struct sirfsoc_padmux sdmmc5_padmux = { .muxmask_counts = ARRAY_SIZE(sdmmc5_muxmask), .muxmask = sdmmc5_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(13), .funcval = BIT(13), }; @@ -503,6 +522,7 @@ static const struct sirfsoc_muxmask usp0_muxmask[] = { static const struct sirfsoc_padmux usp0_padmux = { .muxmask_counts = ARRAY_SIZE(usp0_muxmask), .muxmask = usp0_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(1) | BIT(2) | BIT(9), .funcval = 0, }; @@ -535,6 +555,7 @@ static const struct sirfsoc_muxmask usp1_muxmask[] = { static const struct sirfsoc_padmux usp1_padmux = { .muxmask_counts = ARRAY_SIZE(usp1_muxmask), .muxmask = usp1_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(16), .funcval = BIT(16), }; @@ -554,6 +575,7 @@ static const struct sirfsoc_muxmask nand_muxmask[] = { static const struct sirfsoc_padmux nand_padmux = { .muxmask_counts = ARRAY_SIZE(nand_muxmask), .muxmask = nand_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(5) | BIT(19), .funcval = 0, }; @@ -570,6 +592,7 @@ static const struct sirfsoc_muxmask sdmmc0_muxmask[] = { static const struct sirfsoc_padmux sdmmc0_padmux = { .muxmask_counts = ARRAY_SIZE(sdmmc0_muxmask), .muxmask = sdmmc0_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(5) | BIT(19), .funcval = BIT(19), }; @@ -586,6 +609,7 @@ static const struct sirfsoc_muxmask sdmmc2_muxmask[] = { static const struct sirfsoc_padmux sdmmc2_padmux = { .muxmask_counts = ARRAY_SIZE(sdmmc2_muxmask), .muxmask = sdmmc2_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(11), .funcval = 0, }; @@ -602,6 +626,7 @@ static const struct sirfsoc_muxmask sdmmc2_nowp_muxmask[] = { static const struct sirfsoc_padmux sdmmc2_nowp_padmux = { .muxmask_counts = ARRAY_SIZE(sdmmc2_nowp_muxmask), .muxmask = sdmmc2_nowp_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(11), .funcval = 0, }; @@ -634,6 +659,7 @@ static const struct sirfsoc_muxmask vip_muxmask[] = { static const struct sirfsoc_padmux vip_padmux = { .muxmask_counts = ARRAY_SIZE(vip_muxmask), .muxmask = vip_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(18), .funcval = BIT(18), }; @@ -654,6 +680,7 @@ static const struct sirfsoc_muxmask vip_noupli_muxmask[] = { static const struct sirfsoc_padmux vip_noupli_padmux = { .muxmask_counts = ARRAY_SIZE(vip_noupli_muxmask), .muxmask = vip_noupli_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(15), .funcval = BIT(15), }; @@ -684,6 +711,7 @@ static const struct sirfsoc_muxmask i2c1_muxmask[] = { static const struct sirfsoc_padmux i2c1_padmux = { .muxmask_counts = ARRAY_SIZE(i2c1_muxmask), .muxmask = i2c1_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(16), .funcval = 0, }; @@ -700,6 +728,7 @@ static const struct sirfsoc_muxmask pwm0_muxmask[] = { static const struct sirfsoc_padmux pwm0_padmux = { .muxmask_counts = ARRAY_SIZE(pwm0_muxmask), .muxmask = pwm0_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(12), .funcval = 0, }; @@ -772,6 +801,7 @@ static const struct sirfsoc_muxmask warm_rst_muxmask[] = { static const struct sirfsoc_padmux warm_rst_padmux = { .muxmask_counts = ARRAY_SIZE(warm_rst_muxmask), .muxmask = warm_rst_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(4), .funcval = 0, }; @@ -789,6 +819,7 @@ static const struct sirfsoc_muxmask usb0_upli_drvbus_muxmask[] = { static const struct sirfsoc_padmux usb0_upli_drvbus_padmux = { .muxmask_counts = ARRAY_SIZE(usb0_upli_drvbus_muxmask), .muxmask = usb0_upli_drvbus_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(18), .funcval = 0, }; @@ -805,12 +836,31 @@ static const struct sirfsoc_muxmask usb1_utmi_drvbus_muxmask[] = { static const struct sirfsoc_padmux usb1_utmi_drvbus_padmux = { .muxmask_counts = ARRAY_SIZE(usb1_utmi_drvbus_muxmask), .muxmask = usb1_utmi_drvbus_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(11), .funcval = BIT(11), /* refer to PAD_UTMI_DRVVBUS1_ENABLE */ }; static const unsigned usb1_utmi_drvbus_pins[] = { 28 }; +static const struct sirfsoc_padmux usb1_dp_dn_padmux = { + .muxmask_counts = 0, + .ctrlreg = SIRFSOC_RSC_USB_UART_SHARE, + .funcmask = BIT(2), + .funcval = BIT(2), +}; + +static const unsigned usb1_dp_dn_pins[] = { 103, 104 }; + +static const struct sirfsoc_padmux uart1_route_io_usb1_padmux = { + .muxmask_counts = 0, + .ctrlreg = SIRFSOC_RSC_USB_UART_SHARE, + .funcmask = BIT(2), + .funcval = 0, +}; + +static const unsigned uart1_route_io_usb1_pins[] = { 103, 104 }; + static const struct sirfsoc_muxmask pulse_count_muxmask[] = { { .group = 0, @@ -859,6 +909,8 @@ static const struct sirfsoc_pin_group sirfsoc_pin_groups[] = { SIRFSOC_PIN_GROUP("sdmmc5grp", sdmmc5_pins), SIRFSOC_PIN_GROUP("usb0_upli_drvbusgrp", usb0_upli_drvbus_pins), SIRFSOC_PIN_GROUP("usb1_utmi_drvbusgrp", usb1_utmi_drvbus_pins), + SIRFSOC_PIN_GROUP("usb1_dp_dngrp", usb1_dp_dn_pins), + SIRFSOC_PIN_GROUP("uart1_route_io_usb1grp", uart1_route_io_usb1_pins), SIRFSOC_PIN_GROUP("pulse_countgrp", pulse_count_pins), SIRFSOC_PIN_GROUP("i2sgrp", i2s_pins), SIRFSOC_PIN_GROUP("i2s_no_dingrp", i2s_no_din_pins), @@ -903,6 +955,8 @@ static const char * const sdmmc5grp[] = { "sdmmc5grp" }; static const char * const sdmmc2_nowpgrp[] = { "sdmmc2_nowpgrp" }; static const char * const usb0_upli_drvbusgrp[] = { "usb0_upli_drvbusgrp" }; static const char * const usb1_utmi_drvbusgrp[] = { "usb1_utmi_drvbusgrp" }; +static const char * const usb1_dp_dngrp[] = { "usb1_dp_dngrp" }; +static const char * const uart1_route_io_usb1grp[] = { "uart1_route_io_usb1grp" }; static const char * const pulse_countgrp[] = { "pulse_countgrp" }; static const char * const i2sgrp[] = { "i2sgrp" }; static const char * const i2s_no_dingrp[] = { "i2s_no_dingrp" }; @@ -949,6 +1003,8 @@ static const struct sirfsoc_pmx_func sirfsoc_pmx_functions[] = { SIRFSOC_PMX_FUNCTION("sdmmc2_nowp", sdmmc2_nowpgrp, sdmmc2_nowp_padmux), SIRFSOC_PMX_FUNCTION("usb0_upli_drvbus", usb0_upli_drvbusgrp, usb0_upli_drvbus_padmux), SIRFSOC_PMX_FUNCTION("usb1_utmi_drvbus", usb1_utmi_drvbusgrp, usb1_utmi_drvbus_padmux), + SIRFSOC_PMX_FUNCTION("usb1_dp_dn", usb1_dp_dngrp, usb1_dp_dn_padmux), + SIRFSOC_PMX_FUNCTION("uart1_route_io_usb1", uart1_route_io_usb1grp, uart1_route_io_usb1_padmux), SIRFSOC_PMX_FUNCTION("pulse_count", pulse_countgrp, pulse_count_padmux), SIRFSOC_PMX_FUNCTION("i2s", i2sgrp, i2s_padmux), SIRFSOC_PMX_FUNCTION("i2s_no_din", i2s_no_dingrp, i2s_no_din_padmux), diff --git a/drivers/pinctrl/sirf/pinctrl-prima2.c b/drivers/pinctrl/sirf/pinctrl-prima2.c index 241d839..050777b 100644 --- a/drivers/pinctrl/sirf/pinctrl-prima2.c +++ b/drivers/pinctrl/sirf/pinctrl-prima2.c @@ -126,6 +126,9 @@ static const struct pinctrl_pin_desc sirfsoc_pads[] = { PINCTRL_PIN(112, "x_ldd[13]"), PINCTRL_PIN(113, "x_ldd[14]"), PINCTRL_PIN(114, "x_ldd[15]"), + + PINCTRL_PIN(115, "x_usb1_dp"), + PINCTRL_PIN(116, "x_usb1_dn"), }; static const struct sirfsoc_muxmask lcd_16bits_sirfsoc_muxmask[] = { @@ -143,6 +146,7 @@ static const struct sirfsoc_muxmask lcd_16bits_sirfsoc_muxmask[] = { static const struct sirfsoc_padmux lcd_16bits_padmux = { .muxmask_counts = ARRAY_SIZE(lcd_16bits_sirfsoc_muxmask), .muxmask = lcd_16bits_sirfsoc_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(4), .funcval = 0, }; @@ -168,6 +172,7 @@ static const struct sirfsoc_muxmask lcd_18bits_muxmask[] = { static const struct sirfsoc_padmux lcd_18bits_padmux = { .muxmask_counts = ARRAY_SIZE(lcd_18bits_muxmask), .muxmask = lcd_18bits_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(4), .funcval = 0, }; @@ -193,6 +198,7 @@ static const struct sirfsoc_muxmask lcd_24bits_muxmask[] = { static const struct sirfsoc_padmux lcd_24bits_padmux = { .muxmask_counts = ARRAY_SIZE(lcd_24bits_muxmask), .muxmask = lcd_24bits_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(4), .funcval = 0, }; @@ -218,6 +224,7 @@ static const struct sirfsoc_muxmask lcdrom_muxmask[] = { static const struct sirfsoc_padmux lcdrom_padmux = { .muxmask_counts = ARRAY_SIZE(lcdrom_muxmask), .muxmask = lcdrom_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(4), .funcval = BIT(4), }; @@ -238,6 +245,7 @@ static const struct sirfsoc_muxmask uart0_muxmask[] = { static const struct sirfsoc_padmux uart0_padmux = { .muxmask_counts = ARRAY_SIZE(uart0_muxmask), .muxmask = uart0_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(9), .funcval = BIT(9), }; @@ -282,6 +290,7 @@ static const struct sirfsoc_muxmask uart2_muxmask[] = { static const struct sirfsoc_padmux uart2_padmux = { .muxmask_counts = ARRAY_SIZE(uart2_muxmask), .muxmask = uart2_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(10), .funcval = BIT(10), }; @@ -315,6 +324,7 @@ static const struct sirfsoc_muxmask sdmmc3_muxmask[] = { static const struct sirfsoc_padmux sdmmc3_padmux = { .muxmask_counts = ARRAY_SIZE(sdmmc3_muxmask), .muxmask = sdmmc3_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(7), .funcval = 0, }; @@ -331,6 +341,7 @@ static const struct sirfsoc_muxmask spi0_muxmask[] = { static const struct sirfsoc_padmux spi0_padmux = { .muxmask_counts = ARRAY_SIZE(spi0_muxmask), .muxmask = spi0_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(7), .funcval = BIT(7), }; @@ -361,6 +372,7 @@ static const struct sirfsoc_muxmask cko1_muxmask[] = { static const struct sirfsoc_padmux cko1_padmux = { .muxmask_counts = ARRAY_SIZE(cko1_muxmask), .muxmask = cko1_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(3), .funcval = 0, }; @@ -379,6 +391,7 @@ static const struct sirfsoc_muxmask i2s_muxmask[] = { static const struct sirfsoc_padmux i2s_padmux = { .muxmask_counts = ARRAY_SIZE(i2s_muxmask), .muxmask = i2s_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(3) | BIT(9), .funcval = BIT(3), }; @@ -395,6 +408,7 @@ static const struct sirfsoc_muxmask ac97_muxmask[] = { static const struct sirfsoc_padmux ac97_padmux = { .muxmask_counts = ARRAY_SIZE(ac97_muxmask), .muxmask = ac97_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(8), .funcval = 0, }; @@ -411,6 +425,7 @@ static const struct sirfsoc_muxmask spi1_muxmask[] = { static const struct sirfsoc_padmux spi1_padmux = { .muxmask_counts = ARRAY_SIZE(spi1_muxmask), .muxmask = spi1_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(8), .funcval = BIT(8), }; @@ -441,6 +456,7 @@ static const struct sirfsoc_muxmask gps_muxmask[] = { static const struct sirfsoc_padmux gps_padmux = { .muxmask_counts = ARRAY_SIZE(gps_muxmask), .muxmask = gps_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(12) | BIT(13) | BIT(14), .funcval = BIT(12), }; @@ -463,6 +479,7 @@ static const struct sirfsoc_muxmask sdmmc5_muxmask[] = { static const struct sirfsoc_padmux sdmmc5_padmux = { .muxmask_counts = ARRAY_SIZE(sdmmc5_muxmask), .muxmask = sdmmc5_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(13) | BIT(14), .funcval = BIT(13) | BIT(14), }; @@ -479,6 +496,7 @@ static const struct sirfsoc_muxmask usp0_muxmask[] = { static const struct sirfsoc_padmux usp0_padmux = { .muxmask_counts = ARRAY_SIZE(usp0_muxmask), .muxmask = usp0_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(1) | BIT(2) | BIT(6) | BIT(9), .funcval = 0, }; @@ -509,6 +527,7 @@ static const struct sirfsoc_muxmask usp1_muxmask[] = { static const struct sirfsoc_padmux usp1_padmux = { .muxmask_counts = ARRAY_SIZE(usp1_muxmask), .muxmask = usp1_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(1) | BIT(9) | BIT(10) | BIT(11), .funcval = 0, }; @@ -542,6 +561,7 @@ static const struct sirfsoc_muxmask usp2_muxmask[] = { static const struct sirfsoc_padmux usp2_padmux = { .muxmask_counts = ARRAY_SIZE(usp2_muxmask), .muxmask = usp2_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(13) | BIT(14), .funcval = 0, }; @@ -572,6 +592,7 @@ static const struct sirfsoc_muxmask nand_muxmask[] = { static const struct sirfsoc_padmux nand_padmux = { .muxmask_counts = ARRAY_SIZE(nand_muxmask), .muxmask = nand_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(5), .funcval = 0, }; @@ -580,6 +601,7 @@ static const unsigned nand_pins[] = { 64, 65, 92, 93, 94 }; static const struct sirfsoc_padmux sdmmc0_padmux = { .muxmask_counts = 0, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(5), .funcval = 0, }; @@ -596,6 +618,7 @@ static const struct sirfsoc_muxmask sdmmc2_muxmask[] = { static const struct sirfsoc_padmux sdmmc2_padmux = { .muxmask_counts = ARRAY_SIZE(sdmmc2_muxmask), .muxmask = sdmmc2_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(5), .funcval = BIT(5), }; @@ -628,6 +651,7 @@ static const struct sirfsoc_muxmask vip_muxmask[] = { static const struct sirfsoc_padmux vip_padmux = { .muxmask_counts = ARRAY_SIZE(vip_muxmask), .muxmask = vip_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(0), .funcval = 0, }; @@ -677,6 +701,7 @@ static const struct sirfsoc_muxmask viprom_muxmask[] = { static const struct sirfsoc_padmux viprom_padmux = { .muxmask_counts = ARRAY_SIZE(viprom_muxmask), .muxmask = viprom_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(0), .funcval = BIT(0), }; @@ -693,6 +718,7 @@ static const struct sirfsoc_muxmask pwm0_muxmask[] = { static const struct sirfsoc_padmux pwm0_padmux = { .muxmask_counts = ARRAY_SIZE(pwm0_muxmask), .muxmask = pwm0_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(12), .funcval = 0, }; @@ -764,6 +790,7 @@ static const struct sirfsoc_muxmask usb0_utmi_drvbus_muxmask[] = { static const struct sirfsoc_padmux usb0_utmi_drvbus_padmux = { .muxmask_counts = ARRAY_SIZE(usb0_utmi_drvbus_muxmask), .muxmask = usb0_utmi_drvbus_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(6), .funcval = BIT(6), /* refer to PAD_UTMI_DRVVBUS0_ENABLE */ }; @@ -780,12 +807,31 @@ static const struct sirfsoc_muxmask usb1_utmi_drvbus_muxmask[] = { static const struct sirfsoc_padmux usb1_utmi_drvbus_padmux = { .muxmask_counts = ARRAY_SIZE(usb1_utmi_drvbus_muxmask), .muxmask = usb1_utmi_drvbus_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, .funcmask = BIT(11), .funcval = BIT(11), /* refer to PAD_UTMI_DRVVBUS1_ENABLE */ }; static const unsigned usb1_utmi_drvbus_pins[] = { 59 }; +static const struct sirfsoc_padmux usb1_dp_dn_padmux = { + .muxmask_counts = 0, + .ctrlreg = SIRFSOC_RSC_USB_UART_SHARE, + .funcmask = BIT(2), + .funcval = BIT(2), +}; + +static const unsigned usb1_dp_dn_pins[] = { 115, 116 }; + +static const struct sirfsoc_padmux uart1_route_io_usb1_padmux = { + .muxmask_counts = 0, + .ctrlreg = SIRFSOC_RSC_USB_UART_SHARE, + .funcmask = BIT(2), + .funcval = 0, +}; + +static const unsigned uart1_route_io_usb1_pins[] = { 115, 116 }; + static const struct sirfsoc_muxmask pulse_count_muxmask[] = { { .group = 0, @@ -838,6 +884,8 @@ static const struct sirfsoc_pin_group sirfsoc_pin_groups[] = { SIRFSOC_PIN_GROUP("sdmmc5grp", sdmmc5_pins), SIRFSOC_PIN_GROUP("usb0_utmi_drvbusgrp", usb0_utmi_drvbus_pins), SIRFSOC_PIN_GROUP("usb1_utmi_drvbusgrp", usb1_utmi_drvbus_pins), + SIRFSOC_PIN_GROUP("usb1_dp_dngrp", usb1_dp_dn_pins), + SIRFSOC_PIN_GROUP("uart1_route_io_usb1grp", uart1_route_io_usb1_pins), SIRFSOC_PIN_GROUP("pulse_countgrp", pulse_count_pins), SIRFSOC_PIN_GROUP("i2sgrp", i2s_pins), SIRFSOC_PIN_GROUP("ac97grp", ac97_pins), @@ -884,6 +932,8 @@ static const char * const sdmmc4grp[] = { "sdmmc4grp" }; static const char * const sdmmc5grp[] = { "sdmmc5grp" }; static const char * const usb0_utmi_drvbusgrp[] = { "usb0_utmi_drvbusgrp" }; static const char * const usb1_utmi_drvbusgrp[] = { "usb1_utmi_drvbusgrp" }; +static const char * const usb1_dp_dngrp[] = { "usb1_dp_dngrp" }; +static const char * const uart1_route_io_usb1grp[] = { "uart1_route_io_usb1grp" }; static const char * const pulse_countgrp[] = { "pulse_countgrp" }; static const char * const i2sgrp[] = { "i2sgrp" }; static const char * const ac97grp[] = { "ac97grp" }; @@ -930,6 +980,8 @@ static const struct sirfsoc_pmx_func sirfsoc_pmx_functions[] = { SIRFSOC_PMX_FUNCTION("sdmmc5", sdmmc5grp, sdmmc5_padmux), SIRFSOC_PMX_FUNCTION("usb0_utmi_drvbus", usb0_utmi_drvbusgrp, usb0_utmi_drvbus_padmux), SIRFSOC_PMX_FUNCTION("usb1_utmi_drvbus", usb1_utmi_drvbusgrp, usb1_utmi_drvbus_padmux), + SIRFSOC_PMX_FUNCTION("usb1_dp_dn", usb1_dp_dngrp, usb1_dp_dn_padmux), + SIRFSOC_PMX_FUNCTION("uart1_route_io_usb1", uart1_route_io_usb1grp, uart1_route_io_usb1_padmux), SIRFSOC_PMX_FUNCTION("pulse_count", pulse_countgrp, pulse_count_padmux), SIRFSOC_PMX_FUNCTION("i2s", i2sgrp, i2s_padmux), SIRFSOC_PMX_FUNCTION("ac97", ac97grp, ac97_padmux), diff --git a/drivers/pinctrl/sirf/pinctrl-sirf.c b/drivers/pinctrl/sirf/pinctrl-sirf.c index 26f946a..b81e388 100644 --- a/drivers/pinctrl/sirf/pinctrl-sirf.c +++ b/drivers/pinctrl/sirf/pinctrl-sirf.c @@ -166,12 +166,12 @@ static void sirfsoc_pinmux_endisable(struct sirfsoc_pmx *spmx, unsigned selector if (mux->funcmask && enable) { u32 func_en_val; + func_en_val = - readl(spmx->rsc_virtbase + SIRFSOC_RSC_PIN_MUX); + readl(spmx->rsc_virtbase + mux->ctrlreg); func_en_val = - (func_en_val & ~mux->funcmask) | (mux-> - funcval); - writel(func_en_val, spmx->rsc_virtbase + SIRFSOC_RSC_PIN_MUX); + (func_en_val & ~mux->funcmask) | (mux->funcval); + writel(func_en_val, spmx->rsc_virtbase + mux->ctrlreg); } } diff --git a/drivers/pinctrl/sirf/pinctrl-sirf.h b/drivers/pinctrl/sirf/pinctrl-sirf.h index 17cc108..d7f16b4 100644 --- a/drivers/pinctrl/sirf/pinctrl-sirf.h +++ b/drivers/pinctrl/sirf/pinctrl-sirf.h @@ -9,8 +9,9 @@ #ifndef __PINMUX_SIRF_H__ #define __PINMUX_SIRF_H__ -#define SIRFSOC_NUM_PADS 622 -#define SIRFSOC_RSC_PIN_MUX 0x4 +#define SIRFSOC_NUM_PADS 622 +#define SIRFSOC_RSC_USB_UART_SHARE 0 +#define SIRFSOC_RSC_PIN_MUX 0x4 #define SIRFSOC_GPIO_PAD_EN(g) ((g)*0x100 + 0x84) #define SIRFSOC_GPIO_PAD_EN_CLR(g) ((g)*0x100 + 0x90) @@ -61,6 +62,7 @@ struct sirfsoc_padmux { unsigned long muxmask_counts; const struct sirfsoc_muxmask *muxmask; /* RSC_PIN_MUX set */ + unsigned long ctrlreg; unsigned long funcmask; unsigned long funcval; }; -- cgit v0.10.2 From aa7da564cab718d04c77e3a3c73926473e7a94f7 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 7 Oct 2013 18:27:38 -0700 Subject: spi: convert bus code to use dev_groups The dev_attrs field of struct bus_type is going away soon, dev_groups should be used instead. This converts the spi bus code to use the correct field. Signed-off-by: Greg Kroah-Hartman Signed-off-by: Mark Brown diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 1cd491f..7f8057f 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -58,11 +58,13 @@ modalias_show(struct device *dev, struct device_attribute *a, char *buf) return sprintf(buf, "%s%s\n", SPI_MODULE_PREFIX, spi->modalias); } +static DEVICE_ATTR_RO(modalias); -static struct device_attribute spi_dev_attrs[] = { - __ATTR_RO(modalias), - __ATTR_NULL, +static struct attribute *spi_dev_attrs[] = { + &dev_attr_modalias.attr, + NULL, }; +ATTRIBUTE_GROUPS(spi_dev); /* modalias support makes "modprobe $MODALIAS" new-style hotplug work, * and the sysfs version makes coldplug work too. @@ -229,7 +231,7 @@ static const struct dev_pm_ops spi_pm = { struct bus_type spi_bus_type = { .name = "spi", - .dev_attrs = spi_dev_attrs, + .dev_groups = spi_dev_groups, .match = spi_match_device, .uevent = spi_uevent, .pm = &spi_pm, -- cgit v0.10.2 From a047914e7f123074bc8ee6f40e4502111a1b4cc4 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 2 Oct 2013 17:07:12 +0530 Subject: pinctrl: palmas: remove pin config BIAS_PULL_PIN_DEFAULT support Palmas devices do not support the default bias configuration and hence removing this option from valid pin config parameters. Acked-by: Stephen Warren Signed-off-by: Laxman Dewangan Signed-off-by: Linus Walleij diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-palmas.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-palmas.txt index 734d9b0..caf297b 100644 --- a/Documentation/devicetree/bindings/pinctrl/pinctrl-palmas.txt +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-palmas.txt @@ -41,7 +41,7 @@ pinctrl-bindings.txt: Required: pins Options: function, bias-disable, bias-pull-up, bias-pull-down, - bias-pin-default, drive-open-drain. + drive-open-drain. Note that many of these properties are only valid for certain specific pins. See the Palmas device datasheet for complete details regarding which pins diff --git a/drivers/pinctrl/pinctrl-palmas.c b/drivers/pinctrl/pinctrl-palmas.c index 6164381..f13d0e7 100644 --- a/drivers/pinctrl/pinctrl-palmas.c +++ b/drivers/pinctrl/pinctrl-palmas.c @@ -891,9 +891,6 @@ static int palmas_pinconf_set(struct pinctrl_dev *pctldev, param = pinconf_to_config_param(configs[i]); param_val = pinconf_to_config_argument(configs[i]); - if (param == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT) - continue; - switch (param) { case PIN_CONFIG_BIAS_DISABLE: case PIN_CONFIG_BIAS_PULL_UP: -- cgit v0.10.2 From 31a2c46cd94c6463b2b57b476e5a0fd154fee439 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 7 Oct 2013 23:36:56 +0100 Subject: spi/trace: Trace length of SPI messages on completion Signed-off-by: Mark Brown diff --git a/include/trace/events/spi.h b/include/trace/events/spi.h index a7b0907..5e77e21 100644 --- a/include/trace/events/spi.h +++ b/include/trace/events/spi.h @@ -80,12 +80,32 @@ DEFINE_EVENT(spi_message, spi_message_start, ); -DEFINE_EVENT(spi_message, spi_message_done, +TRACE_EVENT(spi_message_done, TP_PROTO(struct spi_message *msg), - TP_ARGS(msg) + TP_ARGS(msg), + + TP_STRUCT__entry( + __field( int, bus_num ) + __field( int, chip_select ) + __field( struct spi_message *, msg ) + __field( unsigned, frame ) + __field( unsigned, actual ) + ), + TP_fast_assign( + __entry->bus_num = msg->spi->master->bus_num; + __entry->chip_select = msg->spi->chip_select; + __entry->msg = msg; + __entry->frame = msg->frame_length; + __entry->actual = msg->actual_length; + ), + + TP_printk("spi%d.%d %p len=%u/%u", (int)__entry->bus_num, + (int)__entry->chip_select, + (struct spi_message *)__entry->msg, + (unsigned)__entry->actual, (unsigned)__entry->frame) ); #endif /* _TRACE_POWER_H */ -- cgit v0.10.2 From 49449c30c4d1514486364d1e0dbea6938914b86f Mon Sep 17 00:00:00 2001 From: "Geyslan G. Bem" Date: Mon, 7 Oct 2013 19:16:59 -0300 Subject: x86: mkpiggy.c: Explicitly close the output file Even though the resource is released when the application is closed or when returned from main function, modify the code to make it obvious, and to keep static analysis tools from complaining. Signed-off-by: Geyslan G. Bem Link: http://lkml.kernel.org/r/1381184219-10985-1-git-send-email-geyslan@gmail.com Signed-off-by: H. Peter Anvin diff --git a/arch/x86/boot/compressed/mkpiggy.c b/arch/x86/boot/compressed/mkpiggy.c index 958a641..b669ab6 100644 --- a/arch/x86/boot/compressed/mkpiggy.c +++ b/arch/x86/boot/compressed/mkpiggy.c @@ -36,11 +36,12 @@ int main(int argc, char *argv[]) uint32_t olen; long ilen; unsigned long offs; - FILE *f; + FILE *f = NULL; + int retval = 1; if (argc < 2) { fprintf(stderr, "Usage: %s compressed_file\n", argv[0]); - return 1; + goto bail; } /* Get the information for the compressed kernel image first */ @@ -48,7 +49,7 @@ int main(int argc, char *argv[]) f = fopen(argv[1], "r"); if (!f) { perror(argv[1]); - return 1; + goto bail; } @@ -58,12 +59,11 @@ int main(int argc, char *argv[]) if (fread(&olen, sizeof(olen), 1, f) != 1) { perror(argv[1]); - return 1; + goto bail; } ilen = ftell(f); olen = get_unaligned_le32(&olen); - fclose(f); /* * Now we have the input (compressed) and output (uncompressed) @@ -91,5 +91,9 @@ int main(int argc, char *argv[]) printf(".incbin \"%s\"\n", argv[1]); printf("input_data_end:\n"); - return 0; + retval = 0; +bail: + if (f) + fclose(f); + return retval; } -- cgit v0.10.2 From 122498738417c73943b71294c60ec34fc110f5d6 Mon Sep 17 00:00:00 2001 From: Andre Richter Date: Tue, 8 Oct 2013 11:22:22 +0200 Subject: x86, msr: Use file_inode(), not f_mapping->host As discussed in [1], exchange f_mapping->host with file_inode(). This is a bug, but happens to be non-manifest in this case. [1] http://lkml.kernel.org/r/20131007190357.GA13318@ZenIV.linux.org.uk Signed-off-by: Andre Richter Link: http://lkml.kernel.org/r/1381224142-3267-1-git-send-email-andre.o.richter@gmail.com Signed-off-by: H. Peter Anvin diff --git a/arch/x86/kernel/msr.c b/arch/x86/kernel/msr.c index 88458fa..05266b5 100644 --- a/arch/x86/kernel/msr.c +++ b/arch/x86/kernel/msr.c @@ -46,7 +46,7 @@ static struct class *msr_class; static loff_t msr_seek(struct file *file, loff_t offset, int orig) { loff_t ret; - struct inode *inode = file->f_mapping->host; + struct inode *inode = file_inode(file); mutex_lock(&inode->i_mutex); switch (orig) { -- cgit v0.10.2 From 89fe808ae777728da6e1d78b7d13562792310d17 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 30 Sep 2013 12:07:11 +0200 Subject: tools/perf: Standardize feature support define names to: HAVE_{FEATURE}_SUPPORT Standardize all the feature flags based on the HAVE_{FEATURE}_SUPPORT naming convention: HAVE_ARCH_X86_64_SUPPORT HAVE_BACKTRACE_SUPPORT HAVE_CPLUS_DEMANGLE_SUPPORT HAVE_DWARF_SUPPORT HAVE_ELF_GETPHDRNUM_SUPPORT HAVE_GTK2_SUPPORT HAVE_GTK_INFO_BAR_SUPPORT HAVE_LIBAUDIT_SUPPORT HAVE_LIBELF_MMAP_SUPPORT HAVE_LIBELF_SUPPORT HAVE_LIBNUMA_SUPPORT HAVE_LIBUNWIND_SUPPORT HAVE_ON_EXIT_SUPPORT HAVE_PERF_REGS_SUPPORT HAVE_SLANG_SUPPORT HAVE_STRLCPY_SUPPORT Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-u3zvqejddfZhtrbYbfhi3spa@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/arch/x86/include/perf_regs.h b/tools/perf/arch/x86/include/perf_regs.h index 7fcdcdb..e84ca76 100644 --- a/tools/perf/arch/x86/include/perf_regs.h +++ b/tools/perf/arch/x86/include/perf_regs.h @@ -5,7 +5,7 @@ #include "../../util/types.h" #include -#ifndef ARCH_X86_64 +#ifndef HAVE_ARCH_X86_64_SUPPORT #define PERF_REGS_MASK ((1ULL << PERF_REG_X86_32_MAX) - 1) #else #define REG_NOSUPPORT ((1ULL << PERF_REG_X86_DS) | \ @@ -52,7 +52,7 @@ static inline const char *perf_reg_name(int id) return "FS"; case PERF_REG_X86_GS: return "GS"; -#ifdef ARCH_X86_64 +#ifdef HAVE_ARCH_X86_64_SUPPORT case PERF_REG_X86_R8: return "R8"; case PERF_REG_X86_R9: @@ -69,7 +69,7 @@ static inline const char *perf_reg_name(int id) return "R14"; case PERF_REG_X86_R15: return "R15"; -#endif /* ARCH_X86_64 */ +#endif /* HAVE_ARCH_X86_64_SUPPORT */ default: return NULL; } diff --git a/tools/perf/arch/x86/util/unwind.c b/tools/perf/arch/x86/util/unwind.c index 78d956e..456a88c 100644 --- a/tools/perf/arch/x86/util/unwind.c +++ b/tools/perf/arch/x86/util/unwind.c @@ -4,7 +4,7 @@ #include "perf_regs.h" #include "../../util/unwind.h" -#ifdef ARCH_X86_64 +#ifdef HAVE_ARCH_X86_64_SUPPORT int unwind__arch_reg_id(int regnum) { int id; @@ -108,4 +108,4 @@ int unwind__arch_reg_id(int regnum) return id; } -#endif /* ARCH_X86_64 */ +#endif /* HAVE_ARCH_X86_64_SUPPORT */ diff --git a/tools/perf/bench/mem-memcpy-arch.h b/tools/perf/bench/mem-memcpy-arch.h index a72e36c..57b4ed8 100644 --- a/tools/perf/bench/mem-memcpy-arch.h +++ b/tools/perf/bench/mem-memcpy-arch.h @@ -1,5 +1,5 @@ -#ifdef ARCH_X86_64 +#ifdef HAVE_ARCH_X86_64_SUPPORT #define MEMCPY_FN(fn, name, desc) \ extern void *fn(void *, const void *, size_t); diff --git a/tools/perf/bench/mem-memcpy.c b/tools/perf/bench/mem-memcpy.c index 8cdca43..5ce71d3 100644 --- a/tools/perf/bench/mem-memcpy.c +++ b/tools/perf/bench/mem-memcpy.c @@ -58,7 +58,7 @@ struct routine routines[] = { { "default", "Default memcpy() provided by glibc", memcpy }, -#ifdef ARCH_X86_64 +#ifdef HAVE_ARCH_X86_64_SUPPORT #define MEMCPY_FN(fn, name, desc) { name, desc, fn }, #include "mem-memcpy-x86-64-asm-def.h" diff --git a/tools/perf/bench/mem-memset-arch.h b/tools/perf/bench/mem-memset-arch.h index a040fa7..633800c 100644 --- a/tools/perf/bench/mem-memset-arch.h +++ b/tools/perf/bench/mem-memset-arch.h @@ -1,5 +1,5 @@ -#ifdef ARCH_X86_64 +#ifdef HAVE_ARCH_X86_64_SUPPORT #define MEMSET_FN(fn, name, desc) \ extern void *fn(void *, int, size_t); diff --git a/tools/perf/bench/mem-memset.c b/tools/perf/bench/mem-memset.c index 4a2f120..9af79d2 100644 --- a/tools/perf/bench/mem-memset.c +++ b/tools/perf/bench/mem-memset.c @@ -58,7 +58,7 @@ static const struct routine routines[] = { { "default", "Default memset() provided by glibc", memset }, -#ifdef ARCH_X86_64 +#ifdef HAVE_ARCH_X86_64_SUPPORT #define MEMSET_FN(fn, name, desc) { name, desc, fn }, #include "mem-memset-x86-64-asm-def.h" diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c index 77298bf..33af80f 100644 --- a/tools/perf/builtin-bench.c +++ b/tools/perf/builtin-bench.c @@ -35,7 +35,7 @@ struct bench_suite { /* sentinel: easy for help */ #define suite_all { "all", "Test all benchmark suites", NULL } -#ifdef LIBNUMA_SUPPORT +#ifdef HAVE_LIBNUMA_SUPPORT static struct bench_suite numa_suites[] = { { "mem", "Benchmark for NUMA workloads", @@ -80,7 +80,7 @@ struct bench_subsys { }; static struct bench_subsys subsystems[] = { -#ifdef LIBNUMA_SUPPORT +#ifdef HAVE_LIBNUMA_SUPPORT { "numa", "NUMA scheduling and MM behavior", numa_suites }, diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index afe377b..f51a963 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -231,7 +231,7 @@ static int perf_event__inject_buildid(struct perf_tool *tool, * account this as unresolved. */ } else { -#ifdef LIBELF_SUPPORT +#ifdef HAVE_LIBELF_SUPPORT pr_warning("no symbols found in %s, maybe " "install a debug package?\n", al.map->dso->long_name); diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index e8a66f9..89acc17 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -173,7 +173,7 @@ static int opt_set_target(const struct option *opt, const char *str, if (str && !params.target) { if (!strcmp(opt->long_name, "exec")) params.uprobes = true; -#ifdef DWARF_SUPPORT +#ifdef HAVE_DWARF_SUPPORT else if (!strcmp(opt->long_name, "module")) params.uprobes = false; #endif @@ -187,7 +187,7 @@ static int opt_set_target(const struct option *opt, const char *str, return ret; } -#ifdef DWARF_SUPPORT +#ifdef HAVE_DWARF_SUPPORT static int opt_show_lines(const struct option *opt __maybe_unused, const char *str, int unset __maybe_unused) { @@ -257,7 +257,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) "perf probe [] --add 'PROBEDEF' [--add 'PROBEDEF' ...]", "perf probe [] --del '[GROUP:]EVENT' ...", "perf probe --list", -#ifdef DWARF_SUPPORT +#ifdef HAVE_DWARF_SUPPORT "perf probe [] --line 'LINEDESC'", "perf probe [] --vars 'PROBEPOINT'", #endif @@ -271,7 +271,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.", opt_del_probe_event), OPT_CALLBACK('a', "add", NULL, -#ifdef DWARF_SUPPORT +#ifdef HAVE_DWARF_SUPPORT "[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT" " [[NAME=]ARG ...]", #else @@ -283,7 +283,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) "\t\tFUNC:\tFunction name\n" "\t\tOFF:\tOffset from function entry (in byte)\n" "\t\t%return:\tPut the probe at function return\n" -#ifdef DWARF_SUPPORT +#ifdef HAVE_DWARF_SUPPORT "\t\tSRC:\tSource code path\n" "\t\tRL:\tRelative line number from function entry.\n" "\t\tAL:\tAbsolute line number in file.\n" @@ -296,7 +296,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) opt_add_probe_event), OPT_BOOLEAN('f', "force", ¶ms.force_add, "forcibly add events" " with existing name"), -#ifdef DWARF_SUPPORT +#ifdef HAVE_DWARF_SUPPORT OPT_CALLBACK('L', "line", NULL, "FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]", "Show source code lines.", opt_show_lines), @@ -408,7 +408,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) return ret; } -#ifdef DWARF_SUPPORT +#ifdef HAVE_DWARF_SUPPORT if (params.show_lines && !params.uprobes) { if (params.mod_events) { pr_err(" Error: Don't use --line with" diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index a78db3f..cf36ba2 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -29,7 +29,7 @@ #include #include -#ifndef HAVE_ON_EXIT +#ifndef HAVE_ON_EXIT_SUPPORT #ifndef ATEXIT_MAX #define ATEXIT_MAX 32 #endif @@ -687,7 +687,7 @@ error: return ret; } -#ifdef LIBUNWIND_SUPPORT +#ifdef HAVE_LIBUNWIND_SUPPORT static int get_stack_size(char *str, unsigned long *_size) { char *endptr; @@ -713,7 +713,7 @@ static int get_stack_size(char *str, unsigned long *_size) max_size, str); return -1; } -#endif /* LIBUNWIND_SUPPORT */ +#endif /* HAVE_LIBUNWIND_SUPPORT */ int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset) @@ -751,7 +751,7 @@ int record_parse_callchain_opt(const struct option *opt, "needed for -g fp\n"); break; -#ifdef LIBUNWIND_SUPPORT +#ifdef HAVE_LIBUNWIND_SUPPORT /* Dwarf style */ } else if (!strncmp(name, "dwarf", sizeof("dwarf"))) { const unsigned long default_stack_dump_size = 8192; @@ -771,7 +771,7 @@ int record_parse_callchain_opt(const struct option *opt, if (!ret) pr_debug("callchain: stack dump size %d\n", opts->stack_dump_size); -#endif /* LIBUNWIND_SUPPORT */ +#endif /* HAVE_LIBUNWIND_SUPPORT */ } else { pr_err("callchain: Unknown -g option " "value: %s\n", arg); @@ -818,7 +818,7 @@ static struct perf_record record = { #define CALLCHAIN_HELP "do call-graph (stack chain/backtrace) recording: " -#ifdef LIBUNWIND_SUPPORT +#ifdef HAVE_LIBUNWIND_SUPPORT const char record_callchain_help[] = CALLCHAIN_HELP "[fp] dwarf"; #else const char record_callchain_help[] = CALLCHAIN_HELP "[fp]"; diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 5f6f9b3..34be743 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -23,7 +23,7 @@ ifeq ($(ARCH),x86_64) endif ifeq (${IS_X86_64}, 1) RAW_ARCH := x86_64 - CFLAGS += -DARCH_X86_64 + CFLAGS += -DHAVE_ARCH_X86_64_SUPPORT ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S ../../arch/x86/lib/memset_64.S endif NO_PERF_REGS := 0 @@ -31,7 +31,7 @@ ifeq ($(ARCH),x86_64) endif ifeq ($(NO_PERF_REGS),0) - CFLAGS += -DHAVE_PERF_REGS + CFLAGS += -DHAVE_PERF_REGS_SUPPORT endif ifeq ($(src-perf),) @@ -175,13 +175,13 @@ endif # SOURCE_LIBELF endif # NO_LIBELF ifndef NO_LIBELF -CFLAGS += -DLIBELF_SUPPORT +CFLAGS += -DHAVE_LIBELF_SUPPORT FLAGS_LIBELF=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) -ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_LIBELF),-DLIBELF_MMAP),y) - CFLAGS += -DLIBELF_MMAP +ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_LIBELF),-DHAVE_LIBELF_MMAP_SUPPORT),y) + CFLAGS += -DHAVE_LIBELF_MMAP_SUPPORT endif -ifeq ($(call try-cc,$(SOURCE_ELF_GETPHDRNUM),$(FLAGS_LIBELF),-DHAVE_ELF_GETPHDRNUM),y) - CFLAGS += -DHAVE_ELF_GETPHDRNUM +ifeq ($(call try-cc,$(SOURCE_ELF_GETPHDRNUM),$(FLAGS_LIBELF),-DHAVE_ELF_GETPHDRNUM_SUPPORT),y) + CFLAGS += -DHAVE_ELF_GETPHDRNUM_SUPPORT endif # include ARCH specific config @@ -192,7 +192,7 @@ ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined) msg := $(warning DWARF register mappings have not been defined for architecture $(ARCH), DWARF support disabled); NO_DWARF := 1 else - CFLAGS += -DDWARF_SUPPORT $(LIBDW_CFLAGS) + CFLAGS += -DHAVE_DWARF_SUPPORT $(LIBDW_CFLAGS) LDFLAGS += $(LIBDW_LDFLAGS) EXTLIBS += -lelf -ldw endif # PERF_HAVE_DWARF_REGS @@ -201,10 +201,10 @@ endif # NO_DWARF endif # NO_LIBELF ifndef NO_LIBELF -CFLAGS += -DLIBELF_SUPPORT +CFLAGS += -DHAVE_LIBELF_SUPPORT FLAGS_LIBELF=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) -ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_LIBELF),-DLIBELF_MMAP),y) - CFLAGS += -DLIBELF_MMAP +ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_LIBELF),-DHAVE_LIBELF_MMAP_SUPPORT),y) + CFLAGS += -DHAVE_LIBELF_MMAP_SUPPORT endif # try-cc endif # NO_LIBELF @@ -229,7 +229,7 @@ endif # Libunwind support endif # NO_LIBUNWIND ifndef NO_LIBUNWIND - CFLAGS += -DLIBUNWIND_SUPPORT + CFLAGS += -DHAVE_LIBUNWIND_SUPPORT EXTLIBS += $(LIBUNWIND_LIBS) CFLAGS += $(LIBUNWIND_CFLAGS) LDFLAGS += $(LIBUNWIND_LDFLAGS) @@ -241,7 +241,7 @@ ifndef NO_LIBAUDIT msg := $(warning No libaudit.h found, disables 'trace' tool, please install audit-libs-devel or libaudit-dev); NO_LIBAUDIT := 1 else - CFLAGS += -DLIBAUDIT_SUPPORT + CFLAGS += -DHAVE_LIBAUDIT_SUPPORT EXTLIBS += -laudit endif endif @@ -258,7 +258,7 @@ ifndef NO_SLANG else # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h CFLAGS += -I/usr/include/slang - CFLAGS += -DSLANG_SUPPORT + CFLAGS += -DHAVE_SLANG_SUPPORT EXTLIBS += -lslang endif endif @@ -269,10 +269,10 @@ ifndef NO_GTK2 msg := $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev); NO_GTK2 := 1 else - ifeq ($(call try-cc,$(SOURCE_GTK2_INFOBAR),$(FLAGS_GTK2),-DHAVE_GTK_INFO_BAR),y) - CFLAGS += -DHAVE_GTK_INFO_BAR + ifeq ($(call try-cc,$(SOURCE_GTK2_INFOBAR),$(FLAGS_GTK2),-DHAVE_GTK_INFO_BAR_SUPPORT),y) + CFLAGS += -DHAVE_GTK_INFO_BAR_SUPPORT endif - CFLAGS += -DGTK2_SUPPORT + CFLAGS += -DHAVE_GTK2_SUPPORT CFLAGS += $(shell pkg-config --cflags gtk+-2.0 2>/dev/null) EXTLIBS += $(shell pkg-config --libs gtk+-2.0 2>/dev/null) endif @@ -365,9 +365,9 @@ endif ifdef NO_DEMANGLE CFLAGS += -DNO_DEMANGLE else - ifdef HAVE_CPLUS_DEMANGLE + ifdef HAVE_CPLUS_DEMANGLE_SUPPORT EXTLIBS += -liberty - CFLAGS += -DHAVE_CPLUS_DEMANGLE + CFLAGS += -DHAVE_CPLUS_DEMANGLE_SUPPORT else FLAGS_BFD=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) -DPACKAGE='perf' -lbfd has_bfd := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD),libbfd) @@ -388,7 +388,7 @@ else has_cplus_demangle := $(call try-cc,$(SOURCE_CPLUS_DEMANGLE),$(FLAGS_CPLUS_DEMANGLE),demangle) ifeq ($(has_cplus_demangle),y) EXTLIBS += -liberty - CFLAGS += -DHAVE_CPLUS_DEMANGLE + CFLAGS += -DHAVE_CPLUS_DEMANGLE_SUPPORT else msg := $(warning No bfd.h/libbfd found, install binutils-dev[el]/zlib-static to gain symbol demangling) CFLAGS += -DNO_DEMANGLE @@ -400,20 +400,20 @@ else endif ifndef NO_STRLCPY - ifeq ($(call try-cc,$(SOURCE_STRLCPY),,-DHAVE_STRLCPY),y) - CFLAGS += -DHAVE_STRLCPY + ifeq ($(call try-cc,$(SOURCE_STRLCPY),,-DHAVE_STRLCPY_SUPPORT),y) + CFLAGS += -DHAVE_STRLCPY_SUPPORT endif endif ifndef NO_ON_EXIT - ifeq ($(call try-cc,$(SOURCE_ON_EXIT),,-DHAVE_ON_EXIT),y) - CFLAGS += -DHAVE_ON_EXIT + ifeq ($(call try-cc,$(SOURCE_ON_EXIT),,-DHAVE_ON_EXIT_SUPPORT),y) + CFLAGS += -DHAVE_ON_EXIT_SUPPORT endif endif ifndef NO_BACKTRACE - ifeq ($(call try-cc,$(SOURCE_BACKTRACE),,-DBACKTRACE_SUPPORT),y) - CFLAGS += -DBACKTRACE_SUPPORT + ifeq ($(call try-cc,$(SOURCE_BACKTRACE),,-DHAVE_BACKTRACE_SUPPORT),y) + CFLAGS += -DHAVE_BACKTRACE_SUPPORT endif endif @@ -423,7 +423,7 @@ ifndef NO_LIBNUMA msg := $(warning No numa.h found, disables 'perf bench numa mem' benchmark, please install numa-libs-devel or libnuma-dev); NO_LIBNUMA := 1 else - CFLAGS += -DLIBNUMA_SUPPORT + CFLAGS += -DHAVE_LIBNUMA_SUPPORT EXTLIBS += -lnuma endif endif diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 85e1aed..245020c 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -49,14 +49,14 @@ static struct cmd_struct commands[] = { { "version", cmd_version, 0 }, { "script", cmd_script, 0 }, { "sched", cmd_sched, 0 }, -#ifdef LIBELF_SUPPORT +#ifdef HAVE_LIBELF_SUPPORT { "probe", cmd_probe, 0 }, #endif { "kmem", cmd_kmem, 0 }, { "lock", cmd_lock, 0 }, { "kvm", cmd_kvm, 0 }, { "test", cmd_test, 0 }, -#ifdef LIBAUDIT_SUPPORT +#ifdef HAVE_LIBAUDIT_SUPPORT { "trace", cmd_trace, 0 }, #endif { "inject", cmd_inject, 0 }, diff --git a/tools/perf/ui/gtk/browser.c b/tools/perf/ui/gtk/browser.c index c95012c..c24d912 100644 --- a/tools/perf/ui/gtk/browser.c +++ b/tools/perf/ui/gtk/browser.c @@ -43,7 +43,7 @@ const char *perf_gtk__get_percent_color(double percent) return NULL; } -#ifdef HAVE_GTK_INFO_BAR +#ifdef HAVE_GTK_INFO_BAR_SUPPORT GtkWidget *perf_gtk__setup_info_bar(void) { GtkWidget *info_bar; diff --git a/tools/perf/ui/gtk/gtk.h b/tools/perf/ui/gtk/gtk.h index 3d96785..a72acbc 100644 --- a/tools/perf/ui/gtk/gtk.h +++ b/tools/perf/ui/gtk/gtk.h @@ -12,7 +12,7 @@ struct perf_gtk_context { GtkWidget *main_window; GtkWidget *notebook; -#ifdef HAVE_GTK_INFO_BAR +#ifdef HAVE_GTK_INFO_BAR_SUPPORT GtkWidget *info_bar; GtkWidget *message_label; #endif @@ -39,7 +39,7 @@ void perf_gtk__resize_window(GtkWidget *window); const char *perf_gtk__get_percent_color(double percent); GtkWidget *perf_gtk__setup_statusbar(void); -#ifdef HAVE_GTK_INFO_BAR +#ifdef HAVE_GTK_INFO_BAR_SUPPORT GtkWidget *perf_gtk__setup_info_bar(void); #else static inline GtkWidget *perf_gtk__setup_info_bar(void) diff --git a/tools/perf/ui/gtk/util.c b/tools/perf/ui/gtk/util.c index c06942a..696c1fb 100644 --- a/tools/perf/ui/gtk/util.c +++ b/tools/perf/ui/gtk/util.c @@ -53,7 +53,7 @@ static int perf_gtk__error(const char *format, va_list args) return 0; } -#ifdef HAVE_GTK_INFO_BAR +#ifdef HAVE_GTK_INFO_BAR_SUPPORT static int perf_gtk__warning_info_bar(const char *format, va_list args) { char *msg; @@ -105,7 +105,7 @@ static int perf_gtk__warning_statusbar(const char *format, va_list args) struct perf_error_ops perf_gtk_eops = { .error = perf_gtk__error, -#ifdef HAVE_GTK_INFO_BAR +#ifdef HAVE_GTK_INFO_BAR_SUPPORT .warning = perf_gtk__warning_info_bar, #else .warning = perf_gtk__warning_statusbar, diff --git a/tools/perf/ui/ui.h b/tools/perf/ui/ui.h index 70cb0d4..1349d14 100644 --- a/tools/perf/ui/ui.h +++ b/tools/perf/ui/ui.h @@ -12,7 +12,7 @@ extern int use_browser; void setup_browser(bool fallback_to_pager); void exit_browser(bool wait_for_ok); -#ifdef SLANG_SUPPORT +#ifdef HAVE_SLANG_SUPPORT int ui__init(void); void ui__exit(bool wait_for_ok); #else @@ -23,7 +23,7 @@ static inline int ui__init(void) static inline void ui__exit(bool wait_for_ok __maybe_unused) {} #endif -#ifdef GTK2_SUPPORT +#ifdef HAVE_GTK2_SUPPORT int perf_gtk__init(void); void perf_gtk__exit(bool wait_for_ok); #else diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index af75515..f0699e9 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -150,7 +150,7 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, struct perf_evsel *evsel, bool print_lines, bool full_paths, int min_pcnt, int max_lines); -#ifdef SLANG_SUPPORT +#ifdef HAVE_SLANG_SUPPORT int symbol__tui_annotate(struct symbol *sym, struct map *map, struct perf_evsel *evsel, struct hist_browser_timer *hbt); @@ -165,7 +165,7 @@ static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused, } #endif -#ifdef GTK2_SUPPORT +#ifdef HAVE_GTK2_SUPPORT int symbol__gtk_annotate(struct symbol *sym, struct map *map, struct perf_evsel *evsel, struct hist_browser_timer *hbt); diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 26e3672..442953c 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h @@ -70,7 +70,7 @@ extern char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2 extern char *perf_pathdup(const char *fmt, ...) __attribute__((format (printf, 1, 2))); -#ifndef HAVE_STRLCPY +#ifndef HAVE_STRLCPY_SUPPORT extern size_t strlcpy(char *dest, const char *src, size_t size); #endif diff --git a/tools/perf/util/generate-cmdlist.sh b/tools/perf/util/generate-cmdlist.sh index 3ac3803..36a885d 100755 --- a/tools/perf/util/generate-cmdlist.sh +++ b/tools/perf/util/generate-cmdlist.sh @@ -22,7 +22,7 @@ do }' "Documentation/perf-$cmd.txt" done -echo "#ifdef LIBELF_SUPPORT" +echo "#ifdef HAVE_LIBELF_SUPPORT" sed -n -e 's/^perf-\([^ ]*\)[ ].* full.*/\1/p' command-list.txt | sort | while read cmd @@ -35,5 +35,5 @@ do p }' "Documentation/perf-$cmd.txt" done -echo "#endif /* LIBELF_SUPPORT */" +echo "#endif /* HAVE_LIBELF_SUPPORT */" echo "};" diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 6a048c0..ed4f90e 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -187,7 +187,7 @@ struct hist_browser_timer { int refresh; }; -#ifdef SLANG_SUPPORT +#ifdef HAVE_SLANG_SUPPORT #include "../ui/keysyms.h" int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel, struct hist_browser_timer *hbt); @@ -228,7 +228,7 @@ static inline int script_browse(const char *script_opt __maybe_unused) #define K_SWITCH_INPUT_DATA -3000 #endif -#ifdef GTK2_SUPPORT +#ifdef HAVE_GTK2_SUPPORT int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help, struct hist_browser_timer *hbt __maybe_unused, float min_pcnt); diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h index cf6727e..8f14965 100644 --- a/tools/perf/util/include/dwarf-regs.h +++ b/tools/perf/util/include/dwarf-regs.h @@ -1,7 +1,7 @@ #ifndef _PERF_DWARF_REGS_H_ #define _PERF_DWARF_REGS_H_ -#ifdef DWARF_SUPPORT +#ifdef HAVE_DWARF_SUPPORT const char *get_arch_regstr(unsigned int n); #endif diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 4f6680d..17ee458 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -172,7 +172,7 @@ int map__load(struct map *map, symbol_filter_t filter) pr_warning(", continuing without symbols\n"); return -1; } else if (nr == 0) { -#ifdef LIBELF_SUPPORT +#ifdef HAVE_LIBELF_SUPPORT const size_t len = strlen(name); const size_t real_len = len - sizeof(DSO__DELETED); diff --git a/tools/perf/util/path.c b/tools/perf/util/path.c index a8c4954..f395874 100644 --- a/tools/perf/util/path.c +++ b/tools/perf/util/path.c @@ -22,7 +22,7 @@ static const char *get_perf_dir(void) return "."; } -#ifndef HAVE_STRLCPY +#ifndef HAVE_STRLCPY_SUPPORT size_t strlcpy(char *dest, const char *src, size_t size) { size_t ret = strlen(src); diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h index 5a4f2b6f..a3d42cd 100644 --- a/tools/perf/util/perf_regs.h +++ b/tools/perf/util/perf_regs.h @@ -1,7 +1,7 @@ #ifndef __PERF_REGS_H #define __PERF_REGS_H -#ifdef HAVE_PERF_REGS +#ifdef HAVE_PERF_REGS_SUPPORT #include #else #define PERF_REGS_MASK 0 @@ -10,5 +10,5 @@ static inline const char *perf_reg_name(int id __maybe_unused) { return NULL; } -#endif /* HAVE_PERF_REGS */ +#endif /* HAVE_PERF_REGS_SUPPORT */ #endif /* __PERF_REGS_H */ diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index aa04bf9..779b2da 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -201,7 +201,7 @@ static int convert_to_perf_probe_point(struct probe_trace_point *tp, return 0; } -#ifdef DWARF_SUPPORT +#ifdef HAVE_DWARF_SUPPORT /* Open new debuginfo of given module */ static struct debuginfo *open_debuginfo(const char *module) { @@ -630,7 +630,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs, return ret; } -#else /* !DWARF_SUPPORT */ +#else /* !HAVE_DWARF_SUPPORT */ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, struct perf_probe_point *pp) diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 3b7d630..3f0c29d 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -14,7 +14,7 @@ static inline int is_c_varname(const char *name) return isalpha(name[0]) || name[0] == '_'; } -#ifdef DWARF_SUPPORT +#ifdef HAVE_DWARF_SUPPORT #include "dwarf-aux.h" @@ -105,6 +105,6 @@ struct line_finder { int found; }; -#endif /* DWARF_SUPPORT */ +#endif /* HAVE_DWARF_SUPPORT */ #endif /*_PROBE_FINDER_H */ diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index a9c829b..c376930 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -8,7 +8,7 @@ #include "symbol.h" #include "debug.h" -#ifndef HAVE_ELF_GETPHDRNUM +#ifndef HAVE_ELF_GETPHDRNUM_SUPPORT static int elf_getphdrnum(Elf *elf, size_t *dst) { GElf_Ehdr gehdr; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index fd5b70e..2a97bb1 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -13,7 +13,7 @@ #include #include "build-id.h" -#ifdef LIBELF_SUPPORT +#ifdef HAVE_LIBELF_SUPPORT #include #include #endif @@ -21,7 +21,7 @@ #include "dso.h" -#ifdef HAVE_CPLUS_DEMANGLE +#ifdef HAVE_CPLUS_DEMANGLE_SUPPORT extern char *cplus_demangle(const char *, int); static inline char *bfd_demangle(void __maybe_unused *v, const char *c, int i) @@ -46,7 +46,7 @@ static inline char *bfd_demangle(void __maybe_unused *v, * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP; * for newer versions we can use mmap to reduce memory usage: */ -#ifdef LIBELF_MMAP +#ifdef HAVE_LIBELF_MMAP_SUPPORT # define PERF_ELF_C_READ_MMAP ELF_C_READ_MMAP #else # define PERF_ELF_C_READ_MMAP ELF_C_READ @@ -178,7 +178,7 @@ struct symsrc { int fd; enum dso_binary_type type; -#ifdef LIBELF_SUPPORT +#ifdef HAVE_LIBELF_SUPPORT Elf *elf; GElf_Ehdr ehdr; diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h index cb6bc50..ec0c71a 100644 --- a/tools/perf/util/unwind.h +++ b/tools/perf/util/unwind.h @@ -13,7 +13,7 @@ struct unwind_entry { typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg); -#ifdef LIBUNWIND_SUPPORT +#ifdef HAVE_LIBUNWIND_SUPPORT int unwind__get_entries(unwind_entry_cb_t cb, void *arg, struct machine *machine, struct thread *thread, @@ -31,5 +31,5 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, { return 0; } -#endif /* LIBUNWIND_SUPPORT */ +#endif /* HAVE_LIBUNWIND_SUPPORT */ #endif /* __UNWIND_H */ diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 6d17b18..ccfdeb6 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -1,7 +1,7 @@ #include "../perf.h" #include "util.h" #include -#ifdef BACKTRACE_SUPPORT +#ifdef HAVE_BACKTRACE_SUPPORT #include #endif #include @@ -204,7 +204,7 @@ int hex2u64(const char *ptr, u64 *long_val) } /* Obtain a backtrace and print it to stdout. */ -#ifdef BACKTRACE_SUPPORT +#ifdef HAVE_BACKTRACE_SUPPORT void dump_stack(void) { void *array[16]; -- cgit v0.10.2 From b6aa9979416e2e98a800925d60ad00e83bc7cb7a Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 30 Sep 2013 10:08:24 +0200 Subject: tools/perf/build: Add feature check core code Start the split-out of the feature check code by adding a list of features to be tested, and rules to process that list by building its matching feature-check file in config/feature-checks/test-.c. Add 'hello' as the initial feature. This structure will allow us to build split-out feature checks in parallel and thus speed up feature detection dramatically. No change in functionality: no feature check is used by the build rules yet. Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-pixkihgscFaohfFigq5yt9gs@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 34be743..daefe2d 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -89,6 +89,22 @@ CFLAGS += -std=gnu99 EXTLIBS = -lelf -lpthread -lrt -lm -ldl +feature_check = $(eval $(feature_check_code)); $(info CHK: config/feature-checks/test-$(1)) +define feature_check_code + feature-$(2) := $(shell make -C config/feature-checks test-$1 >/dev/null 2>/dev/null && echo 1 || echo 0) +endef + +# +# Build the feature check binaries in parallel, ignore errors, ignore return value and suppress output: +# +$(info Testing features:) +$(shell make -i -j -C config/feature-checks >/dev/null 2>&1) +$(info done) + +FEATURE_TESTS = hello + +$(foreach test,$(FEATURE_TESTS),$(call feature_check,$(test),$(test))) + ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -fstack-protector-all,-fstack-protector-all),y) CFLAGS += -fstack-protector-all endif diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile new file mode 100644 index 0000000..b3f6372 --- /dev/null +++ b/tools/perf/config/feature-checks/Makefile @@ -0,0 +1,16 @@ + +FILES=test-hello + +all: $(FILES) + +BUILD = $(CC) -o $(OUTPUT)$@ $@.c + +############################### + +test-hello: test-hello.c + $(BUILD) + +############################### + +clean: + rm -f $(FILES) diff --git a/tools/perf/config/feature-checks/test-hello.c b/tools/perf/config/feature-checks/test-hello.c new file mode 100644 index 0000000..c9f398d --- /dev/null +++ b/tools/perf/config/feature-checks/test-hello.c @@ -0,0 +1,6 @@ +#include + +int main(void) +{ + return puts("hi"); +} -- cgit v0.10.2 From 8b6eb56a9570001634df1d2c7f38e7179a357362 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 30 Sep 2013 13:51:28 +0200 Subject: tools/perf/build: Add 'autodep' functionality, generate feature test dependencies automatically Use GCC's -MD feature to generate a dependency file for each feature test .c file, and include that .d file in the config/feature-checks/Makefile. This allows us to do two things: - speed up feature tests - detect removal or changes in build dependencies - including system libraries/headers Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-Jfma8pmPnnqzpxjbs3hpgmsj@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index b3f6372..4708cca 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -1,16 +1,20 @@ FILES=test-hello +CC := $(CC) -MD + all: $(FILES) BUILD = $(CC) -o $(OUTPUT)$@ $@.c ############################### -test-hello: test-hello.c +test-hello: $(BUILD) +-include *.d */*.d + ############################### clean: - rm -f $(FILES) + rm -f $(FILES) *.d -- cgit v0.10.2 From 3ae069cfda88349fa61de9631f878d39ab60764f Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 30 Sep 2013 13:37:10 +0200 Subject: tools/perf/build: Split out feature check: 'libnuma' Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-vixsrpggxFjhz7kppqgrGr6s@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index daefe2d..f39fc22 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -101,7 +101,9 @@ $(info Testing features:) $(shell make -i -j -C config/feature-checks >/dev/null 2>&1) $(info done) -FEATURE_TESTS = hello +FEATURE_TESTS = \ + hello \ + libnuma $(foreach test,$(FEATURE_TESTS),$(call feature_check,$(test),$(test))) @@ -434,8 +436,7 @@ ifndef NO_BACKTRACE endif ifndef NO_LIBNUMA - FLAGS_LIBNUMA = $(CFLAGS) $(LDFLAGS) -lnuma - ifneq ($(call try-cc,$(SOURCE_LIBNUMA),$(FLAGS_LIBNUMA),libnuma),y) + ifeq ($(feature-libnuma), 0) msg := $(warning No numa.h found, disables 'perf bench numa mem' benchmark, please install numa-libs-devel or libnuma-dev); NO_LIBNUMA := 1 else diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 4708cca..6a42ad2 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -1,5 +1,7 @@ -FILES=test-hello +FILES= \ + test-hello \ + test-libnuma CC := $(CC) -MD @@ -12,6 +14,9 @@ BUILD = $(CC) -o $(OUTPUT)$@ $@.c test-hello: $(BUILD) +test-libnuma: + $(BUILD) -lnuma + -include *.d */*.d ############################### diff --git a/tools/perf/config/feature-checks/test-libnuma.c b/tools/perf/config/feature-checks/test-libnuma.c new file mode 100644 index 0000000..70510a9 --- /dev/null +++ b/tools/perf/config/feature-checks/test-libnuma.c @@ -0,0 +1,8 @@ +#include +#include + +int main(void) +{ + numa_available(); + return 0; +} -- cgit v0.10.2 From 90ac5422b617fcab0c6f02249502ffc17dfbf6e3 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 30 Sep 2013 13:48:44 +0200 Subject: tools/perf/build: Split out feature check: 'stackprotector-all' Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-gupddm9clctVYws3lyexfdhg@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index f39fc22..1a67371 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -103,11 +103,12 @@ $(info done) FEATURE_TESTS = \ hello \ + stackprotector-all \ libnuma $(foreach test,$(FEATURE_TESTS),$(call feature_check,$(test),$(test))) -ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -fstack-protector-all,-fstack-protector-all),y) +ifeq ($(feature-stackprotector-all), 1) CFLAGS += -fstack-protector-all endif diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 6a42ad2..7538c14 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -1,6 +1,7 @@ FILES= \ test-hello \ + test-stackprotector-all \ test-libnuma CC := $(CC) -MD @@ -14,6 +15,9 @@ BUILD = $(CC) -o $(OUTPUT)$@ $@.c test-hello: $(BUILD) +test-stackprotector-all: + $(BUILD) -Werror -fstack-protector-all + test-libnuma: $(BUILD) -lnuma diff --git a/tools/perf/config/feature-checks/test-stackprotector-all.c b/tools/perf/config/feature-checks/test-stackprotector-all.c new file mode 100644 index 0000000..c9f398d --- /dev/null +++ b/tools/perf/config/feature-checks/test-stackprotector-all.c @@ -0,0 +1,6 @@ +#include + +int main(void) +{ + return puts("hi"); +} -- cgit v0.10.2 From 430be5ab0a50a9116c689b2b2ea7acd7a635aabe Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 7 Oct 2013 09:47:00 +0200 Subject: tools/perf/build: Split out feature check: 'stackprotector' Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-eyLYjhskzn6qxkoyjtjic4ap@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 1a67371..9f94912 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -104,6 +104,7 @@ $(info done) FEATURE_TESTS = \ hello \ stackprotector-all \ + stackprotector \ libnuma $(foreach test,$(FEATURE_TESTS),$(call feature_check,$(test),$(test))) @@ -112,7 +113,7 @@ ifeq ($(feature-stackprotector-all), 1) CFLAGS += -fstack-protector-all endif -ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -Wstack-protector,-Wstack-protector),y) +ifeq ($(feature-stackprotector), 1) CFLAGS += -Wstack-protector endif diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 7538c14..46b7650 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -2,6 +2,7 @@ FILES= \ test-hello \ test-stackprotector-all \ + test-stackprotector \ test-libnuma CC := $(CC) -MD @@ -18,6 +19,9 @@ test-hello: test-stackprotector-all: $(BUILD) -Werror -fstack-protector-all +test-stackprotector: + $(BUILD) -Werror -fstack-protector + test-libnuma: $(BUILD) -lnuma diff --git a/tools/perf/config/feature-checks/test-stackprotector.c b/tools/perf/config/feature-checks/test-stackprotector.c new file mode 100644 index 0000000..c9f398d --- /dev/null +++ b/tools/perf/config/feature-checks/test-stackprotector.c @@ -0,0 +1,6 @@ +#include + +int main(void) +{ + return puts("hi"); +} -- cgit v0.10.2 From c25104452de94102810e86323eabb7d9a4cad083 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 30 Sep 2013 13:58:12 +0200 Subject: tools/perf/build: Split out feature check: 'volatile-register-var' Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-5dOevlybbwvbk3zTbcxrrqet@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 9f94912..a3de2f8 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -105,6 +105,7 @@ FEATURE_TESTS = \ hello \ stackprotector-all \ stackprotector \ + volatile-register-var \ libnuma $(foreach test,$(FEATURE_TESTS),$(call feature_check,$(test),$(test))) @@ -117,7 +118,7 @@ ifeq ($(feature-stackprotector), 1) CFLAGS += -Wstack-protector endif -ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -Wvolatile-register-var,-Wvolatile-register-var),y) +ifeq ($(feature-volatile-register-var), 1) CFLAGS += -Wvolatile-register-var endif diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 46b7650..5693299 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -3,6 +3,7 @@ FILES= \ test-hello \ test-stackprotector-all \ test-stackprotector \ + test-volatile-register-var \ test-libnuma CC := $(CC) -MD @@ -22,6 +23,9 @@ test-stackprotector-all: test-stackprotector: $(BUILD) -Werror -fstack-protector +test-volatile-register-var: + $(BUILD) -Werror -Wvolatile-register-var + test-libnuma: $(BUILD) -lnuma diff --git a/tools/perf/config/feature-checks/test-volatile-register-var.c b/tools/perf/config/feature-checks/test-volatile-register-var.c new file mode 100644 index 0000000..c9f398d --- /dev/null +++ b/tools/perf/config/feature-checks/test-volatile-register-var.c @@ -0,0 +1,6 @@ +#include + +int main(void) +{ + return puts("hi"); +} -- cgit v0.10.2 From 1ea6f99efd8ae61fce68c97a9cf9f722cfbca3ad Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 7 Oct 2013 09:38:28 +0200 Subject: tools/perf/build: Split out feature check: 'fortify-source' Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-wicrcLCy2wkalka7iwsuzgpb@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index a3de2f8..7a614f9 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -106,6 +106,7 @@ FEATURE_TESTS = \ stackprotector-all \ stackprotector \ volatile-register-var \ + fortify-source \ libnuma $(foreach test,$(FEATURE_TESTS),$(call feature_check,$(test),$(test))) @@ -123,7 +124,7 @@ ifeq ($(feature-volatile-register-var), 1) endif ifndef PERF_DEBUG - ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -D_FORTIFY_SOURCE=2,-D_FORTIFY_SOURCE=2),y) + ifeq ($(feature-fortify-source), 1) CFLAGS += -D_FORTIFY_SOURCE=2 endif endif diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 5693299..529317e 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -4,6 +4,7 @@ FILES= \ test-stackprotector-all \ test-stackprotector \ test-volatile-register-var \ + test-fortify-source \ test-libnuma CC := $(CC) -MD @@ -26,6 +27,9 @@ test-stackprotector: test-volatile-register-var: $(BUILD) -Werror -Wvolatile-register-var +test-fortify-source: + $(BUILD) -O2 -Werror -D_FORTIFY_SOURCE=2 + test-libnuma: $(BUILD) -lnuma diff --git a/tools/perf/config/feature-checks/test-fortify-source.c b/tools/perf/config/feature-checks/test-fortify-source.c new file mode 100644 index 0000000..c9f398d --- /dev/null +++ b/tools/perf/config/feature-checks/test-fortify-source.c @@ -0,0 +1,6 @@ +#include + +int main(void) +{ + return puts("hi"); +} -- cgit v0.10.2 From 78e9d6550807aedfc1f81c199bd4681c09d80ac5 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 30 Sep 2013 14:11:46 +0200 Subject: tools/perf/build: Split out feature check: 'bionic' Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-xjfVewprrfhlo2wuzbnpVb1k@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 7a614f9..09e2ecc 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -107,6 +107,7 @@ FEATURE_TESTS = \ stackprotector \ volatile-register-var \ fortify-source \ + bionic \ libnuma $(foreach test,$(FEATURE_TESTS),$(call feature_check,$(test),$(test))) @@ -150,12 +151,13 @@ CFLAGS += -I$(LIB_INCLUDE) CFLAGS += -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE ifndef NO_BIONIC -ifeq ($(call try-cc,$(SOURCE_BIONIC),$(CFLAGS),bionic),y) - BIONIC := 1 - EXTLIBS := $(filter-out -lrt,$(EXTLIBS)) - EXTLIBS := $(filter-out -lpthread,$(EXTLIBS)) + $(feature_check,bionic) + ifeq ($(feature-bionic), 1) + BIONIC := 1 + EXTLIBS := $(filter-out -lrt,$(EXTLIBS)) + EXTLIBS := $(filter-out -lpthread,$(EXTLIBS)) + endif endif -endif # NO_BIONIC ifdef NO_LIBELF NO_DWARF := 1 diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 529317e..191df97 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -5,6 +5,7 @@ FILES= \ test-stackprotector \ test-volatile-register-var \ test-fortify-source \ + test-bionic \ test-libnuma CC := $(CC) -MD @@ -30,6 +31,9 @@ test-volatile-register-var: test-fortify-source: $(BUILD) -O2 -Werror -D_FORTIFY_SOURCE=2 +test-bionic: + $(BUILD) + test-libnuma: $(BUILD) -lnuma diff --git a/tools/perf/config/feature-checks/test-bionic.c b/tools/perf/config/feature-checks/test-bionic.c new file mode 100644 index 0000000..eac24e9 --- /dev/null +++ b/tools/perf/config/feature-checks/test-bionic.c @@ -0,0 +1,6 @@ +#include + +int main(void) +{ + return __ANDROID_API__; +} -- cgit v0.10.2 From 50eed7a71ebaf6676be8c497192b978f07581326 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 30 Sep 2013 14:11:16 +0200 Subject: tools/perf/build: Clean up the libelf logic in config/Makefile Nest the rules properly. No change in functionality. Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-dDgivr9xtjrof2vmoyOfwxkj@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 09e2ecc..a2e0e1b 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -164,38 +164,38 @@ ifdef NO_LIBELF NO_DEMANGLE := 1 NO_LIBUNWIND := 1 else -FLAGS_LIBELF=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) -ifneq ($(call try-cc,$(SOURCE_LIBELF),$(FLAGS_LIBELF),libelf),y) - FLAGS_GLIBC=$(CFLAGS) $(LDFLAGS) - ifeq ($(call try-cc,$(SOURCE_GLIBC),$(FLAGS_GLIBC),glibc),y) - LIBC_SUPPORT := 1 - endif - ifeq ($(BIONIC),1) - LIBC_SUPPORT := 1 - endif - ifeq ($(LIBC_SUPPORT),1) - msg := $(warning No libelf found, disables 'probe' tool, please install elfutils-libelf-devel/libelf-dev); + FLAGS_LIBELF=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) + ifneq ($(call try-cc,$(SOURCE_LIBELF),$(FLAGS_LIBELF),libelf),y) + FLAGS_GLIBC=$(CFLAGS) $(LDFLAGS) + ifeq ($(call try-cc,$(SOURCE_GLIBC),$(FLAGS_GLIBC),glibc),y) + LIBC_SUPPORT := 1 + endif + ifeq ($(BIONIC),1) + LIBC_SUPPORT := 1 + endif + ifeq ($(LIBC_SUPPORT),1) + msg := $(warning No libelf found, disables 'probe' tool, please install elfutils-libelf-devel/libelf-dev); - NO_LIBELF := 1 - NO_DWARF := 1 - NO_DEMANGLE := 1 + NO_LIBELF := 1 + NO_DWARF := 1 + NO_DEMANGLE := 1 + else + msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static); + endif else - msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static); - endif -else - # for linking with debug library, run like: - # make DEBUG=1 LIBDW_DIR=/opt/libdw/ - ifdef LIBDW_DIR - LIBDW_CFLAGS := -I$(LIBDW_DIR)/include - LIBDW_LDFLAGS := -L$(LIBDW_DIR)/lib - endif + # for linking with debug library, run like: + # make DEBUG=1 LIBDW_DIR=/opt/libdw/ + ifdef LIBDW_DIR + LIBDW_CFLAGS := -I$(LIBDW_DIR)/include + LIBDW_LDFLAGS := -L$(LIBDW_DIR)/lib + endif - FLAGS_DWARF=$(CFLAGS) $(LIBDW_CFLAGS) -ldw -lz -lelf $(LIBDW_LDFLAGS) $(LDFLAGS) $(EXTLIBS) - ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF),libdw),y) - msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev); - NO_DWARF := 1 - endif # Dwarf support -endif # SOURCE_LIBELF + FLAGS_DWARF=$(CFLAGS) $(LIBDW_CFLAGS) -ldw -lz -lelf $(LIBDW_LDFLAGS) $(LDFLAGS) $(EXTLIBS) + ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF),libdw),y) + msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev); + NO_DWARF := 1 + endif # Dwarf support + endif # SOURCE_LIBELF endif # NO_LIBELF ifndef NO_LIBELF -- cgit v0.10.2 From 8f7f8005f526c23d3e5537f5ab68c1c3fb422453 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 30 Sep 2013 14:20:25 +0200 Subject: tools/perf/build: Split out feature check: 'libelf' Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-qznhihaasbysfqO7ffvRsf9q@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index a2e0e1b..6865428 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -108,6 +108,7 @@ FEATURE_TESTS = \ volatile-register-var \ fortify-source \ bionic \ + libelf \ libnuma $(foreach test,$(FEATURE_TESTS),$(call feature_check,$(test),$(test))) @@ -164,8 +165,7 @@ ifdef NO_LIBELF NO_DEMANGLE := 1 NO_LIBUNWIND := 1 else - FLAGS_LIBELF=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) - ifneq ($(call try-cc,$(SOURCE_LIBELF),$(FLAGS_LIBELF),libelf),y) + ifeq ($(feature-libelf), 0) FLAGS_GLIBC=$(CFLAGS) $(LDFLAGS) ifeq ($(call try-cc,$(SOURCE_GLIBC),$(FLAGS_GLIBC),glibc),y) LIBC_SUPPORT := 1 diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 191df97..789a38d 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -6,6 +6,7 @@ FILES= \ test-volatile-register-var \ test-fortify-source \ test-bionic \ + test-libelf \ test-libnuma CC := $(CC) -MD @@ -34,6 +35,9 @@ test-fortify-source: test-bionic: $(BUILD) +test-libelf: + $(BUILD) -lelf + test-libnuma: $(BUILD) -lnuma diff --git a/tools/perf/config/feature-checks/test-libelf.c b/tools/perf/config/feature-checks/test-libelf.c new file mode 100644 index 0000000..1a08f97 --- /dev/null +++ b/tools/perf/config/feature-checks/test-libelf.c @@ -0,0 +1,7 @@ +#include + +int main(void) +{ + Elf *elf = elf_begin(0, ELF_C_READ, 0); + return (long)elf; +} -- cgit v0.10.2 From e12762cfd9b4ec8f9bb5863eea403253175df9f1 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 7 Oct 2013 10:34:20 +0200 Subject: tools/perf/build: Split out feature check: 'glibc' Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-nqnnsptw7ivOzhzbNjiun7ds@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 6865428..8cd0fd8 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -109,6 +109,7 @@ FEATURE_TESTS = \ fortify-source \ bionic \ libelf \ + glibc \ libnuma $(foreach test,$(FEATURE_TESTS),$(call feature_check,$(test),$(test))) @@ -166,8 +167,7 @@ ifdef NO_LIBELF NO_LIBUNWIND := 1 else ifeq ($(feature-libelf), 0) - FLAGS_GLIBC=$(CFLAGS) $(LDFLAGS) - ifeq ($(call try-cc,$(SOURCE_GLIBC),$(FLAGS_GLIBC),glibc),y) + ifeq ($(feature-glibc), 1) LIBC_SUPPORT := 1 endif ifeq ($(BIONIC),1) diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 789a38d..c0569c7 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -7,6 +7,7 @@ FILES= \ test-fortify-source \ test-bionic \ test-libelf \ + test-glibc \ test-libnuma CC := $(CC) -MD @@ -38,6 +39,9 @@ test-bionic: test-libelf: $(BUILD) -lelf +test-glibc: + $(BUILD) + test-libnuma: $(BUILD) -lnuma diff --git a/tools/perf/config/feature-checks/test-glibc.c b/tools/perf/config/feature-checks/test-glibc.c new file mode 100644 index 0000000..13c66a5 --- /dev/null +++ b/tools/perf/config/feature-checks/test-glibc.c @@ -0,0 +1,8 @@ +#include + +int main(void) +{ + const char *version = gnu_get_libc_version(); + return (long)version; +} + -- cgit v0.10.2 From 8295d4e27262dc49177e70dcc8ba34fa26343cf2 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 7 Oct 2013 10:35:39 +0200 Subject: tools/perf/build: Split out feature check: 'dwarf' Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-dsx0fn9mjwfprizboANvtuup@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 8cd0fd8..89630b8 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -110,6 +110,7 @@ FEATURE_TESTS = \ bionic \ libelf \ glibc \ + dwarf \ libnuma $(foreach test,$(FEATURE_TESTS),$(call feature_check,$(test),$(test))) @@ -190,8 +191,7 @@ else LIBDW_LDFLAGS := -L$(LIBDW_DIR)/lib endif - FLAGS_DWARF=$(CFLAGS) $(LIBDW_CFLAGS) -ldw -lz -lelf $(LIBDW_LDFLAGS) $(LDFLAGS) $(EXTLIBS) - ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF),libdw),y) + ifneq ($(feature-dwarf), 1) msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev); NO_DWARF := 1 endif # Dwarf support diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index c0569c7..566a71d 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -8,6 +8,7 @@ FILES= \ test-bionic \ test-libelf \ test-glibc \ + test-dwarf \ test-libnuma CC := $(CC) -MD @@ -42,6 +43,9 @@ test-libelf: test-glibc: $(BUILD) +test-dwarf: + $(BUILD) -ldw + test-libnuma: $(BUILD) -lnuma diff --git a/tools/perf/config/feature-checks/test-dwarf.c b/tools/perf/config/feature-checks/test-dwarf.c new file mode 100644 index 0000000..783dfcd --- /dev/null +++ b/tools/perf/config/feature-checks/test-dwarf.c @@ -0,0 +1,9 @@ +#include +#include +#include + +int main(void) +{ + Dwarf *dbg = dwarf_begin(0, DWARF_C_READ); + return (long)dbg; +} -- cgit v0.10.2 From fb3d333b3faa6bf975f00973c6a6271b2ab93c0c Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 7 Oct 2013 10:05:51 +0200 Subject: tools/perf/build: Clean up the mmap logic in config/Makefile Nest the rules properly. No change in functionality. Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-wwktuHl4Ra5lyrrretkxmxqf@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 89630b8..8a27de2 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -199,37 +199,38 @@ else endif # NO_LIBELF ifndef NO_LIBELF -CFLAGS += -DHAVE_LIBELF_SUPPORT -FLAGS_LIBELF=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) -ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_LIBELF),-DHAVE_LIBELF_MMAP_SUPPORT),y) - CFLAGS += -DHAVE_LIBELF_MMAP_SUPPORT -endif -ifeq ($(call try-cc,$(SOURCE_ELF_GETPHDRNUM),$(FLAGS_LIBELF),-DHAVE_ELF_GETPHDRNUM_SUPPORT),y) - CFLAGS += -DHAVE_ELF_GETPHDRNUM_SUPPORT -endif + CFLAGS += -DHAVE_LIBELF_SUPPORT + FLAGS_LIBELF=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) -# include ARCH specific config --include $(src-perf)/arch/$(ARCH)/Makefile + ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_LIBELF),-DHAVE_LIBELF_MMAP_SUPPORT),y) + CFLAGS += -DHAVE_LIBELF_MMAP_SUPPORT + endif -ifndef NO_DWARF -ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined) - msg := $(warning DWARF register mappings have not been defined for architecture $(ARCH), DWARF support disabled); - NO_DWARF := 1 -else - CFLAGS += -DHAVE_DWARF_SUPPORT $(LIBDW_CFLAGS) - LDFLAGS += $(LIBDW_LDFLAGS) - EXTLIBS += -lelf -ldw -endif # PERF_HAVE_DWARF_REGS -endif # NO_DWARF + ifeq ($(call try-cc,$(SOURCE_ELF_GETPHDRNUM),$(FLAGS_LIBELF),-DHAVE_ELF_GETPHDRNUM_SUPPORT),y) + CFLAGS += -DHAVE_ELF_GETPHDRNUM_SUPPORT + endif + + # include ARCH specific config + -include $(src-perf)/arch/$(ARCH)/Makefile + ifndef NO_DWARF + ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined) + msg := $(warning DWARF register mappings have not been defined for architecture $(ARCH), DWARF support disabled); + NO_DWARF := 1 + else + CFLAGS += -DHAVE_DWARF_SUPPORT $(LIBDW_CFLAGS) + LDFLAGS += $(LIBDW_LDFLAGS) + EXTLIBS += -lelf -ldw + endif # PERF_HAVE_DWARF_REGS + endif # NO_DWARF endif # NO_LIBELF ifndef NO_LIBELF -CFLAGS += -DHAVE_LIBELF_SUPPORT -FLAGS_LIBELF=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) -ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_LIBELF),-DHAVE_LIBELF_MMAP_SUPPORT),y) - CFLAGS += -DHAVE_LIBELF_MMAP_SUPPORT -endif # try-cc + CFLAGS += -DHAVE_LIBELF_SUPPORT + FLAGS_LIBELF=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) + ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_LIBELF),-DHAVE_LIBELF_MMAP_SUPPORT),y) + CFLAGS += -DHAVE_LIBELF_MMAP_SUPPORT + endif # try-cc endif # NO_LIBELF # There's only x86 (both 32 and 64) support for CFI unwind so far -- cgit v0.10.2 From 8869b17ee0bdd0da3d1f7d7ced284ab444c2c6d8 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 30 Sep 2013 15:02:28 +0200 Subject: tools/perf/build: Split out feature check: 'libelf-mmap' Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-9fxnxjcmrgbSvipxlwsdQ8fg@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 8a27de2..bf1f021 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -111,6 +111,7 @@ FEATURE_TESTS = \ libelf \ glibc \ dwarf \ + libelf-mmap \ libnuma $(foreach test,$(FEATURE_TESTS),$(call feature_check,$(test),$(test))) @@ -202,7 +203,7 @@ ifndef NO_LIBELF CFLAGS += -DHAVE_LIBELF_SUPPORT FLAGS_LIBELF=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) - ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_LIBELF),-DHAVE_LIBELF_MMAP_SUPPORT),y) + ifeq ($(feature-libelf-mmap), 1) CFLAGS += -DHAVE_LIBELF_MMAP_SUPPORT endif @@ -227,8 +228,7 @@ endif # NO_LIBELF ifndef NO_LIBELF CFLAGS += -DHAVE_LIBELF_SUPPORT - FLAGS_LIBELF=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) - ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_LIBELF),-DHAVE_LIBELF_MMAP_SUPPORT),y) + ifeq ($(feature-libelf-mmap), 1) CFLAGS += -DHAVE_LIBELF_MMAP_SUPPORT endif # try-cc endif # NO_LIBELF diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 566a71d..bf96e34 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -9,6 +9,7 @@ FILES= \ test-libelf \ test-glibc \ test-dwarf \ + test-libelf-mmap \ test-libnuma CC := $(CC) -MD @@ -46,6 +47,9 @@ test-glibc: test-dwarf: $(BUILD) -ldw +test-libelf-mmap: + $(BUILD) -lelf + test-libnuma: $(BUILD) -lnuma diff --git a/tools/perf/config/feature-checks/test-libelf-mmap.c b/tools/perf/config/feature-checks/test-libelf-mmap.c new file mode 100644 index 0000000..1c64815 --- /dev/null +++ b/tools/perf/config/feature-checks/test-libelf-mmap.c @@ -0,0 +1,7 @@ +#include +# +int main(void) +{ + Elf *elf = elf_begin(0, ELF_C_READ_MMAP, 0); + return (long)elf; +} -- cgit v0.10.2 From b7bcef6f8e61580e883672b6c97148d4f89e9874 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 30 Sep 2013 14:35:27 +0200 Subject: tools/perf/build: Split out feature check: 'libelf-getphdrnum' Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-wa9qstb8erbjreLxiHepzjfw@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index bf1f021..718b476 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -112,6 +112,7 @@ FEATURE_TESTS = \ glibc \ dwarf \ libelf-mmap \ + libelf-getphdrnum \ libnuma $(foreach test,$(FEATURE_TESTS),$(call feature_check,$(test),$(test))) @@ -207,7 +208,7 @@ ifndef NO_LIBELF CFLAGS += -DHAVE_LIBELF_MMAP_SUPPORT endif - ifeq ($(call try-cc,$(SOURCE_ELF_GETPHDRNUM),$(FLAGS_LIBELF),-DHAVE_ELF_GETPHDRNUM_SUPPORT),y) + ifeq ($(feature-libelf-getphdrnum), 1) CFLAGS += -DHAVE_ELF_GETPHDRNUM_SUPPORT endif @@ -230,7 +231,7 @@ ifndef NO_LIBELF CFLAGS += -DHAVE_LIBELF_SUPPORT ifeq ($(feature-libelf-mmap), 1) CFLAGS += -DHAVE_LIBELF_MMAP_SUPPORT - endif # try-cc + endif endif # NO_LIBELF # There's only x86 (both 32 and 64) support for CFI unwind so far diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index bf96e34..83b3a02 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -10,6 +10,7 @@ FILES= \ test-glibc \ test-dwarf \ test-libelf-mmap \ + test-libelf-getphdrnum \ test-libnuma CC := $(CC) -MD @@ -50,6 +51,9 @@ test-dwarf: test-libelf-mmap: $(BUILD) -lelf +test-libelf-getphdrnum: + $(BUILD) -lelf + test-libnuma: $(BUILD) -lnuma diff --git a/tools/perf/config/feature-checks/test-libelf-getphdrnum.c b/tools/perf/config/feature-checks/test-libelf-getphdrnum.c new file mode 100644 index 0000000..58eca53 --- /dev/null +++ b/tools/perf/config/feature-checks/test-libelf-getphdrnum.c @@ -0,0 +1,7 @@ +#include +# +int main(void) +{ + size_t dst; + return elf_getphdrnum(0, &dst); +} -- cgit v0.10.2 From 308e1e700a1337b89c7530a4f4cdde5ccb52fb4e Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 7 Oct 2013 10:30:47 +0200 Subject: tools/perf/build: Clean up the libunwind logic in config/Makefile Nest the rules properly. No change in functionality. Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-jjlmizjmhockUs04wqnScnkl@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 718b476..0d75587 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -240,19 +240,19 @@ ifneq ($(ARCH),x86) endif ifndef NO_LIBUNWIND -# for linking with debug library, run like: -# make DEBUG=1 LIBUNWIND_DIR=/opt/libunwind/ -ifdef LIBUNWIND_DIR - LIBUNWIND_CFLAGS := -I$(LIBUNWIND_DIR)/include - LIBUNWIND_LDFLAGS := -L$(LIBUNWIND_DIR)/lib -endif + # for linking with debug library, run like: + # make DEBUG=1 LIBUNWIND_DIR=/opt/libunwind/ + ifdef LIBUNWIND_DIR + LIBUNWIND_CFLAGS := -I$(LIBUNWIND_DIR)/include + LIBUNWIND_LDFLAGS := -L$(LIBUNWIND_DIR)/lib + endif -FLAGS_UNWIND=$(LIBUNWIND_CFLAGS) $(CFLAGS) $(LIBUNWIND_LDFLAGS) $(LDFLAGS) $(EXTLIBS) $(LIBUNWIND_LIBS) -ifneq ($(call try-cc,$(SOURCE_LIBUNWIND),$(FLAGS_UNWIND),libunwind),y) - msg := $(warning No libunwind found, disabling post unwind support. Please install libunwind-dev[el] >= 0.99); - NO_LIBUNWIND := 1 -endif # Libunwind support -endif # NO_LIBUNWIND + FLAGS_UNWIND=$(LIBUNWIND_CFLAGS) $(CFLAGS) $(LIBUNWIND_LDFLAGS) $(LDFLAGS) $(EXTLIBS) $(LIBUNWIND_LIBS) + ifneq ($(call try-cc,$(SOURCE_LIBUNWIND),$(FLAGS_UNWIND),libunwind),y) + msg := $(warning No libunwind found, disabling post unwind support. Please install libunwind-dev[el] >= 0.99); + NO_LIBUNWIND := 1 + endif +endif ifndef NO_LIBUNWIND CFLAGS += -DHAVE_LIBUNWIND_SUPPORT -- cgit v0.10.2 From 058f952de9b3075cd888dc3cea60691db0ec4d3f Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 30 Sep 2013 14:45:44 +0200 Subject: tools/perf/build: Split out feature check: 'libunwind' Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-vTiatsVyva3tfgh3vhxaidxl@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 0d75587..d684a29 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -113,6 +113,7 @@ FEATURE_TESTS = \ dwarf \ libelf-mmap \ libelf-getphdrnum \ + libunwind \ libnuma $(foreach test,$(FEATURE_TESTS),$(call feature_check,$(test),$(test))) @@ -240,15 +241,17 @@ ifneq ($(ARCH),x86) endif ifndef NO_LIBUNWIND - # for linking with debug library, run like: - # make DEBUG=1 LIBUNWIND_DIR=/opt/libunwind/ + # + # For linking with debug library, run like: + # + # make DEBUG=1 LIBUNWIND_DIR=/opt/libunwind/ + # ifdef LIBUNWIND_DIR LIBUNWIND_CFLAGS := -I$(LIBUNWIND_DIR)/include LIBUNWIND_LDFLAGS := -L$(LIBUNWIND_DIR)/lib endif - FLAGS_UNWIND=$(LIBUNWIND_CFLAGS) $(CFLAGS) $(LIBUNWIND_LDFLAGS) $(LDFLAGS) $(EXTLIBS) $(LIBUNWIND_LIBS) - ifneq ($(call try-cc,$(SOURCE_LIBUNWIND),$(FLAGS_UNWIND),libunwind),y) + ifneq ($(feature-libunwind), 1) msg := $(warning No libunwind found, disabling post unwind support. Please install libunwind-dev[el] >= 0.99); NO_LIBUNWIND := 1 endif @@ -259,7 +262,7 @@ ifndef NO_LIBUNWIND EXTLIBS += $(LIBUNWIND_LIBS) CFLAGS += $(LIBUNWIND_CFLAGS) LDFLAGS += $(LIBUNWIND_LDFLAGS) -endif # NO_LIBUNWIND +endif ifndef NO_LIBAUDIT FLAGS_LIBAUDIT = $(CFLAGS) $(LDFLAGS) -laudit diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 83b3a02..d6d9570 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -11,6 +11,7 @@ FILES= \ test-dwarf \ test-libelf-mmap \ test-libelf-getphdrnum \ + test-libunwind \ test-libnuma CC := $(CC) -MD @@ -57,6 +58,9 @@ test-libelf-getphdrnum: test-libnuma: $(BUILD) -lnuma +test-libunwind: + $(BUILD) -lunwind -lunwind-x86_64 -lelf + -include *.d */*.d ############################### diff --git a/tools/perf/config/feature-checks/test-libunwind.c b/tools/perf/config/feature-checks/test-libunwind.c new file mode 100644 index 0000000..5622746 --- /dev/null +++ b/tools/perf/config/feature-checks/test-libunwind.c @@ -0,0 +1,20 @@ +#include +#include + +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); + + +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) + +int main(void) +{ + unw_addr_space_t addr_space; + addr_space = unw_create_addr_space(NULL, 0); + unw_init_remote(NULL, addr_space, NULL); + dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL); + return 0; +} -- cgit v0.10.2 From d795a658eb0ab5ab0b86cda5abe28954b0a08471 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 30 Sep 2013 14:55:31 +0200 Subject: tools/perf/build: Split out feature check: 'libaudit' Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-orhejqtjao3vf4wxwBUdzhaz@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index d684a29..d4f18f4 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -114,6 +114,7 @@ FEATURE_TESTS = \ libelf-mmap \ libelf-getphdrnum \ libunwind \ + libaudit \ libnuma $(foreach test,$(FEATURE_TESTS),$(call feature_check,$(test),$(test))) @@ -265,8 +266,7 @@ ifndef NO_LIBUNWIND endif ifndef NO_LIBAUDIT - FLAGS_LIBAUDIT = $(CFLAGS) $(LDFLAGS) -laudit - ifneq ($(call try-cc,$(SOURCE_LIBAUDIT),$(FLAGS_LIBAUDIT),libaudit),y) + ifneq ($(feature-libaudit), 1) msg := $(warning No libaudit.h found, disables 'trace' tool, please install audit-libs-devel or libaudit-dev); NO_LIBAUDIT := 1 else diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index d6d9570..8e49fa0 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -12,6 +12,7 @@ FILES= \ test-libelf-mmap \ test-libelf-getphdrnum \ test-libunwind \ + test-libaudit \ test-libnuma CC := $(CC) -MD @@ -61,6 +62,9 @@ test-libnuma: test-libunwind: $(BUILD) -lunwind -lunwind-x86_64 -lelf +test-libaudit: + $(BUILD) -laudit + -include *.d */*.d ############################### diff --git a/tools/perf/config/feature-checks/test-libaudit.c b/tools/perf/config/feature-checks/test-libaudit.c new file mode 100644 index 0000000..854a65d --- /dev/null +++ b/tools/perf/config/feature-checks/test-libaudit.c @@ -0,0 +1,7 @@ +#include + +int main(void) +{ + printf("error message: %s\n", audit_errno_to_name(0)); + return audit_open(); +} -- cgit v0.10.2 From b9498b508a0d601029f1040a51e9a5a4aecbb926 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 30 Sep 2013 14:57:54 +0200 Subject: tools/perf/build: Split out feature check: 'libslang' Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-FGmpkydfwqlkaw7yy8ewjpza@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index d4f18f4..43713c6 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -115,6 +115,7 @@ FEATURE_TESTS = \ libelf-getphdrnum \ libunwind \ libaudit \ + libslang \ libnuma $(foreach test,$(FEATURE_TESTS),$(call feature_check,$(test),$(test))) @@ -280,8 +281,7 @@ ifdef NO_NEWT endif ifndef NO_SLANG - FLAGS_SLANG=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) -I/usr/include/slang -lslang - ifneq ($(call try-cc,$(SOURCE_SLANG),$(FLAGS_SLANG),libslang),y) + ifneq ($(feature-libslang), 1) msg := $(warning slang not found, disables TUI support. Please install slang-devel or libslang-dev); NO_SLANG := 1 else diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 8e49fa0..c9b15b0 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -13,6 +13,7 @@ FILES= \ test-libelf-getphdrnum \ test-libunwind \ test-libaudit \ + test-libslang \ test-libnuma CC := $(CC) -MD @@ -65,6 +66,9 @@ test-libunwind: test-libaudit: $(BUILD) -laudit +test-libslang: + $(BUILD) -I/usr/include/slang -lslang + -include *.d */*.d ############################### diff --git a/tools/perf/config/feature-checks/test-libslang.c b/tools/perf/config/feature-checks/test-libslang.c new file mode 100644 index 0000000..22ff22e --- /dev/null +++ b/tools/perf/config/feature-checks/test-libslang.c @@ -0,0 +1,6 @@ +#include + +int main(void) +{ + return SLsmg_init_smg(); +} -- cgit v0.10.2 From 7ef9e055ce1d8ad93f636bde1bf050eef26c798b Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 30 Sep 2013 15:01:56 +0200 Subject: tools/perf/build: Split out feature check: 'gtk2' Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-gfwzurn7wywiviLp7Swyyqsy@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 43713c6..b3bf931 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -116,6 +116,7 @@ FEATURE_TESTS = \ libunwind \ libaudit \ libslang \ + gtk2 \ libnuma $(foreach test,$(FEATURE_TESTS),$(call feature_check,$(test),$(test))) @@ -294,7 +295,7 @@ endif ifndef NO_GTK2 FLAGS_GTK2=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null) - ifneq ($(call try-cc,$(SOURCE_GTK2),$(FLAGS_GTK2),gtk2),y) + ifneq ($(feature-gtk2), 1) msg := $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev); NO_GTK2 := 1 else diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index c9b15b0..920958c 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -14,6 +14,7 @@ FILES= \ test-libunwind \ test-libaudit \ test-libslang \ + test-gtk2 \ test-libnuma CC := $(CC) -MD @@ -69,6 +70,9 @@ test-libaudit: test-libslang: $(BUILD) -I/usr/include/slang -lslang +test-gtk2: + $(BUILD) $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null) + -include *.d */*.d ############################### diff --git a/tools/perf/config/feature-checks/test-gtk2.c b/tools/perf/config/feature-checks/test-gtk2.c new file mode 100644 index 0000000..1ac6d8a --- /dev/null +++ b/tools/perf/config/feature-checks/test-gtk2.c @@ -0,0 +1,10 @@ +#pragma GCC diagnostic ignored "-Wstrict-prototypes" +#include +#pragma GCC diagnostic error "-Wstrict-prototypes" + +int main(int argc, char *argv[]) +{ + gtk_init(&argc, &argv); + + return 0; +} -- cgit v0.10.2 From c7a79e96dc166a1bdadca1367a39c84887e73de3 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 30 Sep 2013 15:08:30 +0200 Subject: tools/perf/build: Split out feature check: 'gtk2-infobar' Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-oumjyVjonjvgH8ts4mftagel@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index b3bf931..3d3d435 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -117,6 +117,7 @@ FEATURE_TESTS = \ libaudit \ libslang \ gtk2 \ + gtk2-infobar \ libnuma $(foreach test,$(FEATURE_TESTS),$(call feature_check,$(test),$(test))) @@ -299,7 +300,7 @@ ifndef NO_GTK2 msg := $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev); NO_GTK2 := 1 else - ifeq ($(call try-cc,$(SOURCE_GTK2_INFOBAR),$(FLAGS_GTK2),-DHAVE_GTK_INFO_BAR_SUPPORT),y) + ifeq ($(feature-gtk2-infobar), 1) CFLAGS += -DHAVE_GTK_INFO_BAR_SUPPORT endif CFLAGS += -DHAVE_GTK2_SUPPORT diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 920958c..017918f 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -15,6 +15,7 @@ FILES= \ test-libaudit \ test-libslang \ test-gtk2 \ + test-gtk2-infobar \ test-libnuma CC := $(CC) -MD @@ -73,6 +74,9 @@ test-libslang: test-gtk2: $(BUILD) $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null) +test-gtk2-infobar: + $(BUILD) $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null) + -include *.d */*.d ############################### diff --git a/tools/perf/config/feature-checks/test-gtk2-infobar.c b/tools/perf/config/feature-checks/test-gtk2-infobar.c new file mode 100644 index 0000000..eebcfbc --- /dev/null +++ b/tools/perf/config/feature-checks/test-gtk2-infobar.c @@ -0,0 +1,10 @@ +#pragma GCC diagnostic ignored "-Wstrict-prototypes" +#include +#pragma GCC diagnostic error "-Wstrict-prototypes" + +int main(void) +{ + gtk_info_bar_new(); + + return 0; +} -- cgit v0.10.2 From 7181a6714efc5039ffb50db7462d5cefe15b5630 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 30 Sep 2013 15:15:36 +0200 Subject: tools/perf/build: Split out feature check: 'libperl' Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-ggucqbwFwpxyuxde6dm7itHq@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 3d3d435..8124dd5 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -118,6 +118,7 @@ FEATURE_TESTS = \ libslang \ gtk2 \ gtk2-infobar \ + libperl \ libnuma $(foreach test,$(FEATURE_TESTS),$(call feature_check,$(test),$(test))) @@ -321,7 +322,7 @@ else PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null` FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS) - ifneq ($(call try-cc,$(SOURCE_PERL_EMBED),$(FLAGS_PERL_EMBED),perl),y) + ifneq ($(feature-libperl), 1) CFLAGS += -DNO_LIBPERL NO_LIBPERL := 1 else diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 017918f..3033c25 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -16,6 +16,7 @@ FILES= \ test-libslang \ test-gtk2 \ test-gtk2-infobar \ + test-libperl \ test-libnuma CC := $(CC) -MD @@ -77,6 +78,18 @@ test-gtk2: test-gtk2-infobar: $(BUILD) $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null) +grep-libs = $(filter -l%,$(1)) +strip-libs = $(filter-out -l%,$(1)) + +PERL_EMBED_LDOPTS = $(shell perl -MExtUtils::Embed -e ldopts 2>/dev/null) +PERL_EMBED_LDFLAGS = $(call strip-libs,$(PERL_EMBED_LDOPTS)) +PERL_EMBED_LIBADD = $(call grep-libs,$(PERL_EMBED_LDOPTS)) +PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null` +FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS) + +test-libperl: + $(BUILD) $(FLAGS_PERL_EMBED) + -include *.d */*.d ############################### diff --git a/tools/perf/config/feature-checks/test-libperl.c b/tools/perf/config/feature-checks/test-libperl.c new file mode 100644 index 0000000..8871f6a --- /dev/null +++ b/tools/perf/config/feature-checks/test-libperl.c @@ -0,0 +1,9 @@ +#include +#include + +int main(void) +{ + perl_alloc(); + + return 0; +} -- cgit v0.10.2 From 9734163b6ee1425c6fa4b65d7e6ce34c9079420d Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 30 Sep 2013 15:18:37 +0200 Subject: tools/perf/build: Split out feature check: 'libpython' Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-9wfutfb8ufFHrddrwlejqrai@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 8124dd5..595c0e0 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -119,6 +119,7 @@ FEATURE_TESTS = \ gtk2 \ gtk2-infobar \ libperl \ + libpython \ libnuma $(foreach test,$(FEATURE_TESTS),$(call feature_check,$(test),$(test))) @@ -367,7 +368,7 @@ else PYTHON_EMBED_CCOPTS := $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null) FLAGS_PYTHON_EMBED := $(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS) - ifneq ($(call try-cc,$(SOURCE_PYTHON_EMBED),$(FLAGS_PYTHON_EMBED),python),y) + ifneq ($(feature-libpython), 1) $(call disable-python,Python.h (for Python 2.x)) else diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 3033c25..e7ed05a 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -17,6 +17,7 @@ FILES= \ test-gtk2 \ test-gtk2-infobar \ test-libperl \ + test-libpython \ test-libnuma CC := $(CC) -MD @@ -90,6 +91,23 @@ FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS) test-libperl: $(BUILD) $(FLAGS_PERL_EMBED) +override PYTHON := python +override PYTHON_CONFIG := python-config + +escape-for-shell-sq = $(subst ','\'',$(1)) +shell-sq = '$(escape-for-shell-sq)' + +PYTHON_CONFIG_SQ = $(call shell-sq,$(PYTHON_CONFIG)) + +PYTHON_EMBED_LDOPTS = $(shell $(PYTHON_CONFIG_SQ) --ldflags 2>/dev/null) +PYTHON_EMBED_LDFLAGS = $(call strip-libs,$(PYTHON_EMBED_LDOPTS)) +PYTHON_EMBED_LIBADD = $(call grep-libs,$(PYTHON_EMBED_LDOPTS)) +PYTHON_EMBED_CCOPTS = $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null) +FLAGS_PYTHON_EMBED = $(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS) + +test-libpython: + $(BUILD) $(FLAGS_PYTHON_EMBED) + -include *.d */*.d ############################### diff --git a/tools/perf/config/feature-checks/test-libpython.c b/tools/perf/config/feature-checks/test-libpython.c new file mode 100644 index 0000000..7226797 --- /dev/null +++ b/tools/perf/config/feature-checks/test-libpython.c @@ -0,0 +1,7 @@ +#include +# +int main(void) +{ + Py_Initialize(); + return 0; +} -- cgit v0.10.2 From 95d061c8a9f36e8c2cc458f97bc67716571b3fee Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 30 Sep 2013 15:40:04 +0200 Subject: tools/perf/build: Split out feature check: 'libpython-version' Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-raHmlqlnv0zexsrPau8hhane@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 595c0e0..63ba069 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -120,6 +120,7 @@ FEATURE_TESTS = \ gtk2-infobar \ libperl \ libpython \ + libpython-version \ libnuma $(foreach test,$(FEATURE_TESTS),$(call feature_check,$(test),$(test))) @@ -372,7 +373,7 @@ else $(call disable-python,Python.h (for Python 2.x)) else - ifneq ($(call try-cc,$(SOURCE_PYTHON_VERSION),$(FLAGS_PYTHON_EMBED),python version),y) + ifneq ($(feature-libpython-version), 1) $(warning Python 3 is not yet supported; please set) $(warning PYTHON and/or PYTHON_CONFIG appropriately.) $(warning If you also have Python 2 installed, then) diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index e7ed05a..d15074d 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -18,6 +18,7 @@ FILES= \ test-gtk2-infobar \ test-libperl \ test-libpython \ + test-libpython-version \ test-libnuma CC := $(CC) -MD @@ -108,6 +109,9 @@ FLAGS_PYTHON_EMBED = $(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS) test-libpython: $(BUILD) $(FLAGS_PYTHON_EMBED) +test-libpython-version: + $(BUILD) $(FLAGS_PYTHON_EMBED) + -include *.d */*.d ############################### diff --git a/tools/perf/config/feature-checks/test-libpython-version.c b/tools/perf/config/feature-checks/test-libpython-version.c new file mode 100644 index 0000000..facea12 --- /dev/null +++ b/tools/perf/config/feature-checks/test-libpython-version.c @@ -0,0 +1,10 @@ +#include + +#if PY_VERSION_HEX >= 0x03000000 + #error +#endif + +int main(void) +{ + return 0; +} -- cgit v0.10.2 From 3b7646e45d110f53d4c0fa0a63158c3d2a763e60 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 30 Sep 2013 15:53:31 +0200 Subject: tools/perf/build: Split out feature check: 'libbfd' Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-cdxdfv7Corpfvjg9Skezhvjn@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 63ba069..7e13969 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -121,6 +121,7 @@ FEATURE_TESTS = \ libperl \ libpython \ libpython-version \ + libbfd \ libnuma $(foreach test,$(FEATURE_TESTS),$(call feature_check,$(test),$(test))) @@ -404,8 +405,7 @@ else CFLAGS += -DHAVE_CPLUS_DEMANGLE_SUPPORT else FLAGS_BFD=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) -DPACKAGE='perf' -lbfd - has_bfd := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD),libbfd) - ifeq ($(has_bfd),y) + ifeq ($(feature-libbfd), 1) EXTLIBS += -lbfd else FLAGS_BFD_IBERTY=$(FLAGS_BFD) -liberty diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index d15074d..af65aaa 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -19,6 +19,7 @@ FILES= \ test-libperl \ test-libpython \ test-libpython-version \ + test-libbfd \ test-libnuma CC := $(CC) -MD @@ -112,6 +113,9 @@ test-libpython: test-libpython-version: $(BUILD) $(FLAGS_PYTHON_EMBED) +test-libbfd: + $(BUILD) -DPACKAGE='perf' -DPACKAGE=perf -lbfd -ldl + -include *.d */*.d ############################### diff --git a/tools/perf/config/feature-checks/test-libbfd.c b/tools/perf/config/feature-checks/test-libbfd.c new file mode 100644 index 0000000..d03339c --- /dev/null +++ b/tools/perf/config/feature-checks/test-libbfd.c @@ -0,0 +1,7 @@ +#include + +int main(void) +{ + bfd_demangle(0, 0, 0); + return 0; +} -- cgit v0.10.2 From d0707c9172e5d286fc119dcef5ea1621ef3bfade Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Tue, 8 Oct 2013 10:10:45 +0200 Subject: tools/perf/build: Split out feature check: 'strlcpy' Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-ektO8cgvupthhyqqczSok2sr@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 7e13969..c8ac4df 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -121,6 +121,7 @@ FEATURE_TESTS = \ libperl \ libpython \ libpython-version \ + strlcpy \ libbfd \ libnuma @@ -434,7 +435,7 @@ else endif ifndef NO_STRLCPY - ifeq ($(call try-cc,$(SOURCE_STRLCPY),,-DHAVE_STRLCPY_SUPPORT),y) + ifeq ($(feature-strlcpy), 1) CFLAGS += -DHAVE_STRLCPY_SUPPORT endif endif diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index af65aaa..d348aa0 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -19,6 +19,7 @@ FILES= \ test-libperl \ test-libpython \ test-libpython-version \ + test-strlcpy \ test-libbfd \ test-libnuma @@ -113,6 +114,9 @@ test-libpython: test-libpython-version: $(BUILD) $(FLAGS_PYTHON_EMBED) +test-strlcpy: + $(BUILD) + test-libbfd: $(BUILD) -DPACKAGE='perf' -DPACKAGE=perf -lbfd -ldl diff --git a/tools/perf/config/feature-checks/test-strlcpy.c b/tools/perf/config/feature-checks/test-strlcpy.c new file mode 100644 index 0000000..4a6b6ff --- /dev/null +++ b/tools/perf/config/feature-checks/test-strlcpy.c @@ -0,0 +1,8 @@ +#include +extern size_t strlcpy(char *dest, const char *src, size_t size); + +int main(void) +{ + strlcpy(NULL, NULL, 0); + return 0; +} -- cgit v0.10.2 From 34ef21622f9028efafe7950504a9ce90d9cc15a0 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 30 Sep 2013 16:46:49 +0200 Subject: tools/perf/build: Split out feature check: 'on-exit' Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-gmywXandzfxnlcbzlX6bkpw1@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index c8ac4df..44affb5 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -123,6 +123,7 @@ FEATURE_TESTS = \ libpython-version \ strlcpy \ libbfd \ + on-exit \ libnuma $(foreach test,$(FEATURE_TESTS),$(call feature_check,$(test),$(test))) @@ -441,7 +442,7 @@ ifndef NO_STRLCPY endif ifndef NO_ON_EXIT - ifeq ($(call try-cc,$(SOURCE_ON_EXIT),,-DHAVE_ON_EXIT_SUPPORT),y) + ifeq ($(feature-on-exit), 1) CFLAGS += -DHAVE_ON_EXIT_SUPPORT endif endif diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index d348aa0..20f1b8c 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -21,6 +21,7 @@ FILES= \ test-libpython-version \ test-strlcpy \ test-libbfd \ + test-on-exit \ test-libnuma CC := $(CC) -MD @@ -120,6 +121,9 @@ test-strlcpy: test-libbfd: $(BUILD) -DPACKAGE='perf' -DPACKAGE=perf -lbfd -ldl +test-on-exit: + $(BUILD) + -include *.d */*.d ############################### diff --git a/tools/perf/config/feature-checks/test-on-exit.c b/tools/perf/config/feature-checks/test-on-exit.c new file mode 100644 index 0000000..473f1de --- /dev/null +++ b/tools/perf/config/feature-checks/test-on-exit.c @@ -0,0 +1,6 @@ +#include + +int main(void) +{ + return on_exit(NULL, NULL); +} -- cgit v0.10.2 From 4cc9117a35b2810fb84454514a9136e5f2945751 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 30 Sep 2013 16:49:38 +0200 Subject: tools/perf/build: Split out feature check: 'backtrace' Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-ihnwe6cvglVkudyvcavP1wql@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 44affb5..c0c8344 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -124,6 +124,7 @@ FEATURE_TESTS = \ strlcpy \ libbfd \ on-exit \ + backtrace \ libnuma $(foreach test,$(FEATURE_TESTS),$(call feature_check,$(test),$(test))) @@ -448,7 +449,7 @@ ifndef NO_ON_EXIT endif ifndef NO_BACKTRACE - ifeq ($(call try-cc,$(SOURCE_BACKTRACE),,-DHAVE_BACKTRACE_SUPPORT),y) + ifeq ($(feature-backtrace), 1) CFLAGS += -DHAVE_BACKTRACE_SUPPORT endif endif diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 20f1b8c..0e4dbc2 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -22,6 +22,7 @@ FILES= \ test-strlcpy \ test-libbfd \ test-on-exit \ + test-backtrace \ test-libnuma CC := $(CC) -MD @@ -124,6 +125,9 @@ test-libbfd: test-on-exit: $(BUILD) +test-backtrace: + $(BUILD) + -include *.d */*.d ############################### diff --git a/tools/perf/config/feature-checks/test-backtrace.c b/tools/perf/config/feature-checks/test-backtrace.c new file mode 100644 index 0000000..5b79468 --- /dev/null +++ b/tools/perf/config/feature-checks/test-backtrace.c @@ -0,0 +1,10 @@ +#include +#include + +int main(void) +{ + backtrace(NULL, 0); + backtrace_symbols(NULL, 0); + + return 0; +} -- cgit v0.10.2 From 7a10822a30060eceddda16c051086c00acb449c9 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 7 Oct 2013 11:26:18 +0200 Subject: tools/perf: Clean up util/include/linux/compiler.h Use the standard CPP style we use in the kernel: #ifndef foo # define foo bar #endif Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-iqyVrrHqpn0eiwenvgwrh8lf@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/include/linux/compiler.h b/tools/perf/util/include/linux/compiler.h index 96b919d..bef4d3d 100644 --- a/tools/perf/util/include/linux/compiler.h +++ b/tools/perf/util/include/linux/compiler.h @@ -2,20 +2,25 @@ #define _PERF_LINUX_COMPILER_H_ #ifndef __always_inline -#define __always_inline inline +# define __always_inline inline __attribute__((always_inline)) #endif + #define __user + #ifndef __attribute_const__ -#define __attribute_const__ +# define __attribute_const__ #endif #ifndef __maybe_unused -#define __maybe_unused __attribute__((unused)) +# define __maybe_unused __attribute__((unused)) +#endif + +#ifndef __packed +# define __packed __attribute__((__packed__)) #endif -#define __packed __attribute__((__packed__)) #ifndef __force -#define __force +# define __force #endif #endif -- cgit v0.10.2 From fb1c9185e36cf9c616ac15f54e54a01f052672bd Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Tue, 1 Oct 2013 13:26:13 +0200 Subject: tools/perf: Turn strlcpy() into a __weak function The strlcpy() feature check slows every build unnecessarily - so make it a __weak function so it does not have to be auto-detected. If the libc (or any other library) has an strlcpy() implementation it will be used - otherwise our fallback is active. Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-zjbrcupapu08ePsyYhhhxiwk@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index c0c8344..3207c25 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -121,7 +121,6 @@ FEATURE_TESTS = \ libperl \ libpython \ libpython-version \ - strlcpy \ libbfd \ on-exit \ backtrace \ @@ -436,12 +435,6 @@ else endif endif -ifndef NO_STRLCPY - ifeq ($(feature-strlcpy), 1) - CFLAGS += -DHAVE_STRLCPY_SUPPORT - endif -endif - ifndef NO_ON_EXIT ifeq ($(feature-on-exit), 1) CFLAGS += -DHAVE_ON_EXIT_SUPPORT diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 0e4dbc2..c65bdac 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -19,7 +19,6 @@ FILES= \ test-libperl \ test-libpython \ test-libpython-version \ - test-strlcpy \ test-libbfd \ test-on-exit \ test-backtrace \ @@ -116,9 +115,6 @@ test-libpython: test-libpython-version: $(BUILD) $(FLAGS_PYTHON_EMBED) -test-strlcpy: - $(BUILD) - test-libbfd: $(BUILD) -DPACKAGE='perf' -DPACKAGE=perf -lbfd -ldl diff --git a/tools/perf/config/feature-checks/test-strlcpy.c b/tools/perf/config/feature-checks/test-strlcpy.c deleted file mode 100644 index 4a6b6ff..0000000 --- a/tools/perf/config/feature-checks/test-strlcpy.c +++ /dev/null @@ -1,8 +0,0 @@ -#include -extern size_t strlcpy(char *dest, const char *src, size_t size); - -int main(void) -{ - strlcpy(NULL, NULL, 0); - return 0; -} diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 442953c..7b176dd 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h @@ -70,8 +70,7 @@ extern char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2 extern char *perf_pathdup(const char *fmt, ...) __attribute__((format (printf, 1, 2))); -#ifndef HAVE_STRLCPY_SUPPORT +/* Matches the libc/libbsd function attribute so we declare this unconditionally: */ extern size_t strlcpy(char *dest, const char *src, size_t size); -#endif #endif /* __PERF_CACHE_H */ diff --git a/tools/perf/util/include/linux/compiler.h b/tools/perf/util/include/linux/compiler.h index bef4d3d..b003ad7 100644 --- a/tools/perf/util/include/linux/compiler.h +++ b/tools/perf/util/include/linux/compiler.h @@ -23,4 +23,8 @@ # define __force #endif +#ifndef __weak +# define __weak __attribute__((weak)) +#endif + #endif diff --git a/tools/perf/util/path.c b/tools/perf/util/path.c index f395874..5d13cb4 100644 --- a/tools/perf/util/path.c +++ b/tools/perf/util/path.c @@ -22,19 +22,23 @@ static const char *get_perf_dir(void) return "."; } -#ifndef HAVE_STRLCPY_SUPPORT -size_t strlcpy(char *dest, const char *src, size_t size) +/* + * If libc has strlcpy() then that version will override this + * implementation: + */ +size_t __weak strlcpy(char *dest, const char *src, size_t size) { size_t ret = strlen(src); if (size) { size_t len = (ret >= size) ? size - 1 : ret; + memcpy(dest, src, len); dest[len] = '\0'; } + return ret; } -#endif static char *get_pathname(void) { -- cgit v0.10.2 From baa9c30e1e250abf3e53b98e5bcf415dccdc7ba2 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Tue, 1 Oct 2013 14:14:31 +0200 Subject: tools/perf/build: Speed up auto-detection of features by adding a 'test-all' target Concatenate all feature checks into test-all.c. This can be built and checked faster than all the individual tests. If test-all fails then we still check all the individual features, so this is a pure speedup, it should have no effects on functionality. Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-5hlcb2qorzwfwrWTjiygjjih@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 3207c25..cbd7cdc 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -89,17 +89,21 @@ CFLAGS += -std=gnu99 EXTLIBS = -lelf -lpthread -lrt -lm -ldl -feature_check = $(eval $(feature_check_code)); $(info CHK: config/feature-checks/test-$(1)) +feature_check = $(eval $(feature_check_code)) define feature_check_code - feature-$(2) := $(shell make -C config/feature-checks test-$1 >/dev/null 2>/dev/null && echo 1 || echo 0) + feature-$(1) := $(shell $(MAKE) -C config/feature-checks test-$1 >/dev/null 2>/dev/null && echo 1 || echo 0) +endef + +feature_set = $(eval $(feature_set_code)) +define feature_set_code + feature-$(1) := 1 endef # # Build the feature check binaries in parallel, ignore errors, ignore return value and suppress output: # -$(info Testing features:) -$(shell make -i -j -C config/feature-checks >/dev/null 2>&1) -$(info done) +$(info ) +$(info Auto-detecting system features:) FEATURE_TESTS = \ hello \ @@ -126,7 +130,36 @@ FEATURE_TESTS = \ backtrace \ libnuma -$(foreach test,$(FEATURE_TESTS),$(call feature_check,$(test),$(test))) +# +# Special fast-path for the 'all features are available' case: +# +$(call feature_check,all) + +ifeq ($(feature-all), 1) + $(foreach feat,$(FEATURE_TESTS),$(call feature_set,$(feat))) +else + $(shell $(MAKE) -i -j -C config/feature-checks >/dev/null 2>&1) + $(foreach feat,$(FEATURE_TESTS),$(call feature_check,$(feat))) +endif + +feature_print = $(eval $(feature_print_code)) + +# +# Print the result of the feature test: +# +define feature_print_code + ifeq ($(feature-$(1)), 1) + MSG := $(shell printf '...%30s: [ \033[32mon\033[m ]' $(1)) + else + MSG := $(shell printf '...%30s: [ \033[31mOFF\033[m ]' $(1)) + endif + $(info $(MSG)) +endef + +$(foreach feat,$(FEATURE_TESTS) DUMMY,$(call feature_print,$(feat))) + +# newline at the end of the feature printouts: +$(info ) ifeq ($(feature-stackprotector-all), 1) CFLAGS += -fstack-protector-all diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index c65bdac..4b855e0 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -32,6 +32,9 @@ BUILD = $(CC) -o $(OUTPUT)$@ $@.c ############################### +test-all: + $(BUILD) -Werror -fstack-protector -fstack-protector-all -Wvolatile-register-var -O2 -Werror -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lunwind -lunwind-x86_64 -lelf -laudit -I/usr/include/slang -lslang $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='perf' -DPACKAGE=perf -lbfd -ldl + test-hello: $(BUILD) diff --git a/tools/perf/config/feature-checks/test-all.c b/tools/perf/config/feature-checks/test-all.c new file mode 100644 index 0000000..9f7c4b1 --- /dev/null +++ b/tools/perf/config/feature-checks/test-all.c @@ -0,0 +1,196 @@ + +#pragma GCC diagnostic ignored "-Wstrict-prototypes" + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma GCC diagnostic error "-Wstrict-prototypes" + +int main1(void) +{ + return puts("hi"); +} + +int main2(void) +{ + return puts("hi"); +} + +int main3(void) +{ + return puts("hi"); +} + +int main4(void) +{ + Elf *elf = elf_begin(0, ELF_C_READ, 0); + return (long)elf; +} +# +int main5(void) +{ + Elf *elf = elf_begin(0, ELF_C_READ_MMAP, 0); + return (long)elf; +} + +int main6(void) +{ + const char *version = gnu_get_libc_version(); + return (long)version; +} + +int main7(void) +{ + Dwarf *dbg = dwarf_begin(0, DWARF_C_READ); + return (long)dbg; +} + +int main8(void) +{ + size_t dst; + return elf_getphdrnum(0, &dst); +} + +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); + + +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) + +int main9(void) +{ + unw_addr_space_t addr_space; + addr_space = unw_create_addr_space(NULL, 0); + unw_init_remote(NULL, addr_space, NULL); + dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL); + return 0; +} + +int main10(void) +{ + printf("error message: %s\n", audit_errno_to_name(0)); + return audit_open(); +} + +int main11(void) +{ + return SLsmg_init_smg(); +} + +int main12(int argc, char *argv[]) +{ + gtk_init(&argc, &argv); + + return 0; +} + +int main13(void) +{ + gtk_info_bar_new(); + + return 0; +} + +int main14(void) +{ + perl_alloc(); + + return 0; +} + +int main15(void) +{ + Py_Initialize(); + return 0; +} + +#if PY_VERSION_HEX >= 0x03000000 + #error +#endif + +int main16(void) +{ + return 0; +} + +int main17(void) +{ + bfd_demangle(0, 0, 0); + return 0; +} + +void exit_function(int x, void *y) +{ +} + +int main18(void) +{ + return on_exit(exit_function, NULL); +} + +int main19(void) +{ + void *backtrace_fns[1]; + size_t entries; + + entries = backtrace(backtrace_fns, 1); + backtrace_symbols(backtrace_fns, entries); + + return 0; +} + +int main20(void) +{ + numa_available(); + return 0; +} + +int main(int argc, char *argv[]) +{ + main1(); + main2(); + main3(); + main4(); + main5(); + main6(); + main7(); + main8(); + main9(); + main10(); + main11(); + main12(argc, argv); + main13(); + main14(); + main15(); + main16(); + main17(); + main18(); + main19(); + main20(); + + return 0; +} -- cgit v0.10.2 From c72e3f04b45fb2e50cdd81a50c3778c6a57251d8 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Tue, 1 Oct 2013 16:28:09 +0200 Subject: tools/perf/build: Speed up git-version test on re-make util/PERF-VERSION-GEN is currently executed on every build attempt, and this script can take a lot of time on trees that are at a significant git-distance from Linus's tree: $ time util/PERF-VERSION-GEN real 0m4.343s user 0m4.176s sys 0m0.140s It also takes a lot of time if the Git repository is network attached, etc., because the commands it uses: TAG=$(git describe --abbrev=0 --match "v[0-9].[0-9]*" 2>/dev/null ) has to count commits from the nearest tag and thus has to access (and decompress) every git commit blob on the relevant version path. Even on Linus's tree it takes 0.28 seconds on a fast box to count all the commits and get the git version string: $ time util/PERF-VERSION-GEN real 0m0.279s user 0m0.247s sys 0m0.025s But the version string only has to be regenerated if the git repository's head commit changes. So add a dependency of ../../.git/HEAD and touch the file every time it's regenerated, so that Make's build rules can pick it up and cache the result: make: `PERF-VERSION-FILE' is up to date. real 0m0.184s user 0m0.117s sys 0m0.026s Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-wvmlrurufuk6mo1ovtNigguT@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 64c043b..6b5452a 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -70,8 +70,9 @@ ifneq ($(OUTPUT),) #$(info Determined 'OUTPUT' to be $(OUTPUT)) endif -$(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE +$(OUTPUT)PERF-VERSION-FILE: ../../.git/HEAD @$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT) + @touch $(OUTPUT)PERF-VERSION-FILE CC = $(CROSS_COMPILE)gcc AR = $(CROSS_COMPILE)ar @@ -814,6 +815,16 @@ clean: $(LIBTRACEEVENT)-clean $(LIBLK)-clean $(RM) $(OUTPUT)util/*-flex* $(python-clean) +# +# Trick: if ../../.git does not exist - we are building out of tree for example, +# then force version regeneration: +# +ifeq ($(wildcard ../../.git/HEAD),) + GIT-HEAD-PHONY = ../../.git/HEAD +else + GIT-HEAD-PHONY = +endif + .PHONY: all install clean strip $(LIBTRACEEVENT) $(LIBLK) .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell -.PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope .FORCE-PERF-CFLAGS +.PHONY: $(GIT-HEAD-PHONY) TAGS tags cscope .FORCE-PERF-CFLAGS -- cgit v0.10.2 From c9404c6650d8f58ce8cf98d4c5581fc5c9e37faf Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Tue, 1 Oct 2013 17:17:22 +0200 Subject: tools/perf/build: Speed up the final link libtraceevent.a and liblk.a rules have always-missed dependencies, which causes python.so to be relinked at every build attempt - even if none of the affected code changes. This slows down re-builds unnecessarily, by adding more than a second to the build time: comet:~/tip/tools/perf> time make ... SUBDIR /fast/mingo/tip/tools/lib/lk/ make[1]: `liblk.a' is up to date. SUBDIR /fast/mingo/tip/tools/lib/traceevent/ LINK perf GEN python/perf.so real 0m1.701s user 0m1.338s sys 0m0.301s Add the (trivial) dependencies to not force a re-link. This speeds up an empty re-build enormously: comet:~/tip/tools/perf> time make ... real 0m0.207s user 0m0.134s sys 0m0.028s [ This adds some coupling between the build dependencies of libtraceevent and liblk - but until those stay relatively simple this should not be an issue. ] Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-wvmlrurufuk6mo1ovtNigguT@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 6b5452a..df76198 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -669,15 +669,19 @@ $(LIB_FILE): $(LIB_OBJS) $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS) # libtraceevent.a -$(LIBTRACEEVENT): +TE_SOURCES = $(wildcard $(TRACE_EVENT_DIR)*.[ch]) + +$(LIBTRACEEVENT): $(TE_SOURCES) $(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) libtraceevent.a $(LIBTRACEEVENT)-clean: $(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) clean +LIBLK_SOURCES = $(wildcard $(LK_PATH)*.[ch]) + # if subdir is set, we've been called from above so target has been built # already -$(LIBLK): +$(LIBLK): $(LIBLK_SOURCES) ifeq ($(subdir),) $(QUIET_SUBDIR0)$(LK_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) liblk.a endif @@ -825,6 +829,6 @@ else GIT-HEAD-PHONY = endif -.PHONY: all install clean strip $(LIBTRACEEVENT) $(LIBLK) +.PHONY: all install clean strip .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell .PHONY: $(GIT-HEAD-PHONY) TAGS tags cscope .FORCE-PERF-CFLAGS -- cgit v0.10.2 From 31f6be65e08d2cbb876b3b5fc9ae9281b7417c85 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 2 Oct 2013 06:51:27 +0200 Subject: tools/perf: Fix double/triple-build of the feature detection logic during 'make install' et al Linus reported the following perf build system bug: 'Another annoyance during that make was that "make install" seems to want to re-make the thing I just built. That's absolutely horrible, [...]' The thing that got re-built were 'only' the (numerous) feature checks, not the whole project - but still it was mighty annoying as the feature checks took 9+ seconds even on reasonably fast boxes. Even with the autodep patches where feature detection is much faster it wastes resources, wastes screen real estate and confuses users if we execute feature detection twice. There were two sources for these unnecessary re-builds of the feature checks: - Unnecessary nested invocations of $(MAKE), apparently to be able to do conditional compilation dependent on documentation tools presence. Use straight dependencies instead, with no nesting. - A direct invocation of $(MAKE) to rebuild the PERF-VERSION-FILE. This is apparently done to be able to include it into the Makefile: -include $(OUTPUT)PERF-VERSION-FILE but that's entirely pointless for two reasons: 1) the version file gets regenerated by the initial build pass anyway, 2) including it is futile, given its contents: #define PERF_VERSION "3.12.rc3.g8510c7" 'make' will interpret that as a comment line... So just remove this part of the doc-generation logic. With these things fixed a 'make install' now rebuilds only what is needed. A repeated 'make install' on an already built tree is super fast now, it finishes in under 0.3 seconds: # # After the patch: # $ time make install ... real 0m0.280s user 0m0.162s sys 0m0.054s Prior all the autodep changes and prior this fix, a repeat 'make install' took 24.1 seconds (!) on the same system: # # Before the patches: # $ time make install ... real 0m24.109s user 0m21.171s sys 0m2.449s Which almost entirely was caused by fixable build system fat. We are now literally ~86 times faster. A fresh rebuild and install now takes just 11.4 seconds: # # After the patch: # $ make clean $ time make -j16 install ... real 0m11.457s user 1m43.411s sys 0m7.610s Without the patches it took 27.8 seconds: # # Before the patches: # $ make clean $ time make -j16 install ... real 0m27.801s user 1m59.242s sys 0m9.749s So even in the complete rebuild case we are now ~2.5 times faster. Reported-by: Linus Torvalds Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-x4qjnxjGrgxpribq8sdakfTp@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/Documentation/Makefile b/tools/perf/Documentation/Makefile index 5a37a7c..eaa477f 100644 --- a/tools/perf/Documentation/Makefile +++ b/tools/perf/Documentation/Makefile @@ -192,13 +192,14 @@ do-install-man: man install-man: check-man-tools man -try-install-man: ifdef missing_tools - $(warning Please install $(missing_tools) to have the man pages installed) + DO_INSTALL_MAN = $(warning Please install $(missing_tools) to have the man pages installed) else - $(MAKE) do-install-man + DO_INSTALL_MAN = do-install-man endif +try-install-man: $(DO_INSTALL_MAN) + install-info: info $(INSTALL) -d -m 755 $(DESTDIR)$(infodir) $(INSTALL) -m 644 $(OUTPUT)perf.info $(OUTPUT)perfman.info $(DESTDIR)$(infodir) @@ -216,14 +217,6 @@ install-pdf: pdf #install-html: html # '$(SHELL_PATH_SQ)' ./install-webdoc.sh $(DESTDIR)$(htmldir) -ifneq ($(MAKECMDGOALS),clean) -ifneq ($(MAKECMDGOALS),tags) -$(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE - $(QUIET_SUBDIR0)../ $(QUIET_SUBDIR1) $(OUTPUT)PERF-VERSION-FILE - --include $(OUTPUT)PERF-VERSION-FILE -endif -endif # # Determine "include::" file references in asciidoc files. @@ -342,5 +335,3 @@ $(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt #quick-install-html: # '$(SHELL_PATH_SQ)' ./install-doc-quick.sh $(HTML_REF) $(DESTDIR)$(htmldir) - -.PHONY: .FORCE-PERF-VERSION-FILE -- cgit v0.10.2 From de0f03fb8dc268de63d366e735cc576a22df201b Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 2 Oct 2013 09:43:23 +0200 Subject: tools/perf/build: Invoke feature-checks 'clean' target from the main Makefile config/Makefile is not included for the 'clean' target, so invoke the config/feature-checks/Makefile 'clean' target from Makefile.perf. Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-sh2cGvmsjbrazarlqre7pVwt@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/Makefile b/tools/perf/Makefile index df76198..6b7779b 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -809,7 +809,14 @@ $(INSTALL_DOC_TARGETS): ### Cleaning rules -clean: $(LIBTRACEEVENT)-clean $(LIBLK)-clean +# +# This is here, not in config/Makefile, because config/Makefile does +# not get included for the clean target: +# +config-clean: + @$(MAKE) -C config/feature-checks clean + +clean: $(LIBTRACEEVENT)-clean $(LIBLK)-clean config-clean $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS) $(RM) $(ALL_PROGRAMS) perf $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* @@ -829,6 +836,6 @@ else GIT-HEAD-PHONY = endif -.PHONY: all install clean strip +.PHONY: all install clean config-clean strip .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell .PHONY: $(GIT-HEAD-PHONY) TAGS tags cscope .FORCE-PERF-CFLAGS -- cgit v0.10.2 From f1138ec66e839ce90c58af0c264db33271d73466 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 2 Oct 2013 09:54:43 +0200 Subject: tools/perf/build: Speed up auto-detection The detection of certain rarely detected features can be delayed to when they are actually needed. So speed up the common case of auto-detection by pre-building only a core set of features and populating only their feature-flags. [ Features not listed in CORE_FEATURES need to built explicitly via the feature_check() function. ] (Also order the feature names alphabetically, while at it.) Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-xQkuveknd0gqla1dfxrqKpkl@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index cbd7cdc..581a942 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -105,30 +105,36 @@ endef $(info ) $(info Auto-detecting system features:) -FEATURE_TESTS = \ - hello \ - stackprotector-all \ - stackprotector \ - volatile-register-var \ +# +# Note that this is not a complete list of all feature tests, just +# those that are typically built on a fully configured system. +# +# [ Feature tests not mentioned here have to be built explicitly in +# the rule that uses them - an example for that is the 'bionic' +# feature check. ] +# +CORE_FEATURE_TESTS = \ + backtrace \ + dwarf \ fortify-source \ - bionic \ - libelf \ glibc \ - dwarf \ - libelf-mmap \ - libelf-getphdrnum \ - libunwind \ - libaudit \ - libslang \ gtk2 \ gtk2-infobar \ + libaudit \ + libbfd \ + libelf \ + libelf-getphdrnum \ + libelf-mmap \ + libnuma \ libperl \ libpython \ libpython-version \ - libbfd \ + libslang \ + libunwind \ on-exit \ - backtrace \ - libnuma + stackprotector \ + stackprotector-all \ + volatile-register-var # # Special fast-path for the 'all features are available' case: @@ -136,10 +142,13 @@ FEATURE_TESTS = \ $(call feature_check,all) ifeq ($(feature-all), 1) - $(foreach feat,$(FEATURE_TESTS),$(call feature_set,$(feat))) + # + # test-all.c passed - just set all the core feature flags to 1: + # + $(foreach feat,$(CORE_FEATURE_TESTS),$(call feature_set,$(feat))) else - $(shell $(MAKE) -i -j -C config/feature-checks >/dev/null 2>&1) - $(foreach feat,$(FEATURE_TESTS),$(call feature_check,$(feat))) + $(shell $(MAKE) -i -j -C config/feature-checks $(CORE_FEATURE_TESTS) >/dev/null 2>&1) + $(foreach feat,$(CORE_FEATURE_TESTS),$(call feature_check,$(feat))) endif feature_print = $(eval $(feature_print_code)) @@ -156,7 +165,7 @@ define feature_print_code $(info $(MSG)) endef -$(foreach feat,$(FEATURE_TESTS) DUMMY,$(call feature_print,$(feat))) +$(foreach feat,$(CORE_FEATURE_TESTS) DUMMY,$(call feature_print,$(feat))) # newline at the end of the feature printouts: $(info ) diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 4b855e0..d4c55ac 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -1,28 +1,29 @@ FILES= \ - test-hello \ - test-stackprotector-all \ - test-stackprotector \ - test-volatile-register-var \ - test-fortify-source \ + test-all \ + test-backtrace \ test-bionic \ - test-libelf \ - test-glibc \ test-dwarf \ - test-libelf-mmap \ - test-libelf-getphdrnum \ - test-libunwind \ - test-libaudit \ - test-libslang \ + test-fortify-source \ + test-glibc \ test-gtk2 \ test-gtk2-infobar \ + test-hello \ + test-libaudit \ + test-libbfd \ + test-libelf \ + test-libelf-getphdrnum \ + test-libelf-mmap \ + test-libnuma \ test-libperl \ test-libpython \ test-libpython-version \ - test-libbfd \ + test-libslang \ + test-libunwind \ test-on-exit \ - test-backtrace \ - test-libnuma + test-stackprotector-all \ + test-stackprotector \ + test-volatile-register-var CC := $(CC) -MD -- cgit v0.10.2 From b3b64a12239e758573316df19cbbaf0126887440 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 2 Oct 2013 10:01:42 +0200 Subject: tools/perf/build: Improve printout-of auto-detected features Change the print-out of auto-detected features by making sure that repeat invocations of 'make' when all features are successfully detected do not produce the (rather lengthy) autodetection printout. ( When one or more features are missing then we still print out the feature detection table, to make sure people are aware of the resulting limitations. ) Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-qd8sMsshcjomxqx9bQcufmaa@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 581a942..fb6ec06 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -102,8 +102,6 @@ endef # # Build the feature check binaries in parallel, ignore errors, ignore return value and suppress output: # -$(info ) -$(info Auto-detecting system features:) # # Note that this is not a complete list of all feature tests, just @@ -137,9 +135,33 @@ CORE_FEATURE_TESTS = \ volatile-register-var # +# So here we detect whether test-all was rebuilt, to be able +# to skip the print-out of the long features list if the file +# existed before and after it was built: +# +ifeq ($(wildcard config/feature-checks/test-all),) + test-all-failed := 1 +else + test-all-failed := 0 +endif + +# # Special fast-path for the 'all features are available' case: # -$(call feature_check,all) +$(call feature_check,all,$(MSG)) + +# +# Just in case the build freshly failed, make sure we print the +# feature matrix: +# +ifeq ($(feature-all), 0) + test-all-failed := 1 +endif + +ifeq ($(test-all-failed),1) + $(info ) + $(info Auto-detecting system features:) +endif ifeq ($(feature-all), 1) # @@ -151,11 +173,10 @@ else $(foreach feat,$(CORE_FEATURE_TESTS),$(call feature_check,$(feat))) endif -feature_print = $(eval $(feature_print_code)) - # # Print the result of the feature test: # +feature_print = $(eval $(feature_print_code)) define feature_print_code ifeq ($(feature-$(1)), 1) MSG := $(shell printf '...%30s: [ \033[32mon\033[m ]' $(1)) @@ -165,10 +186,13 @@ define feature_print_code $(info $(MSG)) endef -$(foreach feat,$(CORE_FEATURE_TESTS) DUMMY,$(call feature_print,$(feat))) - -# newline at the end of the feature printouts: -$(info ) +# +# Only print out our features if we rebuilt the testcases or if a test failed: +# +ifeq ($(test-all-failed), 1) + $(foreach feat,$(CORE_FEATURE_TESTS) DUMMY,$(call feature_print,$(feat))) + $(info ) +endif ifeq ($(feature-stackprotector-all), 1) CFLAGS += -fstack-protector-all -- cgit v0.10.2 From 2cfbe880f0a8eed7beabade3e06fd53f999b3e32 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 2 Oct 2013 11:18:28 +0200 Subject: tools/perf/build: Automatically build in parallel, based on number of CPUs in the system Implement automatic parallel builds when building in tools/perf: $ time make # [ perf build: Doing 'make -j12' parallel build. ] Auto-detecting system features: ... real 0m9.265s user 0m59.888s sys 0m6.082s On GNU make achieving this is not particularly easy, it requires a separate makefile, which then invokes the main Makefile. ( Note: this patch adds Makefile.parallel to show the concept - the two makefiles will be flipped in the next patch to avoid having to specify -f to get parallelism in the default build. ) Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-dvBjwqiTyzrufzkz8oanhpf9@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/Makefile.parallel b/tools/perf/Makefile.parallel new file mode 100644 index 0000000..ec5e08b --- /dev/null +++ b/tools/perf/Makefile.parallel @@ -0,0 +1,26 @@ +# +# Do a parallel build with multiple jobs, based on the number of CPUs online +# in this system: 'make -j8' on a 8-CPU system, etc. +# +# (To override it, run 'make JOBS=1' and similar.) +# +ifeq ($(JOBS),) + JOBS := $(shell grep -c ^processor /proc/cpuinfo 2>/dev/null) + ifeq ($(JOBS),) + JOBS := 1 + endif +endif + +export JOBS + +$(info $(shell printf '# [ perf build: Doing '\''make \033[33m-j'$(JOBS)'\033[m'\'' parallel build. ]\n')) + +# +# Needed if no target specified: +# +all: + @$(MAKE) --no-print-directory -j$(JOBS) $@ + +%: + @$(MAKE) --no-print-directory -j$(JOBS) $@ + -- cgit v0.10.2 From bd69cc286d817a4ce55b8969b0975e42168fe7c0 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 2 Oct 2013 11:49:08 +0200 Subject: tools/perf/build: Flip Makefile.parallel and Makefile.perf To make it more apparent that there is not change in functionality we introduced Makefile.parallel separately and now flip it with the main Makefile, which moves into Makefile.perf. The renames are: Makefile.parallel => Makefile Makefile => Makefile.perf Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-igRfuw9ugbnnpixLd6wpptzl@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 6b7779b..ce7874b 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -1,841 +1,25 @@ -include ../scripts/Makefile.include - -# The default target of this Makefile is... -all: - -include config/utilities.mak - -# Define V to have a more verbose compile. -# -# Define O to save output files in a separate directory. -# -# Define ARCH as name of target architecture if you want cross-builds. -# -# Define CROSS_COMPILE as prefix name of compiler if you want cross-builds. -# -# Define NO_LIBPERL to disable perl script extension. -# -# Define NO_LIBPYTHON to disable python script extension. -# -# Define PYTHON to point to the python binary if the default -# `python' is not correct; for example: PYTHON=python2 -# -# Define PYTHON_CONFIG to point to the python-config binary if -# the default `$(PYTHON)-config' is not correct. -# -# Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8 -# -# Define DOCBOOK_XSL_172 if you want to format man pages with DocBook XSL v1.72. -# -# Define LDFLAGS=-static to build a static binary. -# -# Define EXTRA_CFLAGS=-m64 or EXTRA_CFLAGS=-m32 as appropriate for cross-builds. -# -# Define NO_DWARF if you do not want debug-info analysis feature at all. -# -# Define WERROR=0 to disable treating any warnings as errors. -# -# Define NO_NEWT if you do not want TUI support. (deprecated) -# -# Define NO_SLANG if you do not want TUI support. -# -# Define NO_GTK2 if you do not want GTK+ GUI support. -# -# Define NO_DEMANGLE if you do not want C++ symbol demangling. -# -# Define NO_LIBELF if you do not want libelf dependency (e.g. cross-builds) -# -# Define NO_LIBUNWIND if you do not want libunwind dependency for dwarf -# backtrace post unwind. # -# Define NO_BACKTRACE if you do not want stack backtrace debug feature +# Do a parallel build with multiple jobs, based on the number of CPUs online +# in this system: 'make -j8' on a 8-CPU system, etc. # -# Define NO_LIBNUMA if you do not want numa perf benchmark +# (To override it, run 'make JOBS=1' and similar.) # -# Define NO_LIBAUDIT if you do not want libaudit support -# -# Define NO_LIBBIONIC if you do not want bionic support - -ifeq ($(srctree),) -srctree := $(patsubst %/,%,$(dir $(shell pwd))) -srctree := $(patsubst %/,%,$(dir $(srctree))) -#$(info Determined 'srctree' to be $(srctree)) -endif - -ifneq ($(objtree),) -#$(info Determined 'objtree' to be $(objtree)) -endif - -ifneq ($(OUTPUT),) -#$(info Determined 'OUTPUT' to be $(OUTPUT)) -endif - -$(OUTPUT)PERF-VERSION-FILE: ../../.git/HEAD - @$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT) - @touch $(OUTPUT)PERF-VERSION-FILE - -CC = $(CROSS_COMPILE)gcc -AR = $(CROSS_COMPILE)ar - -RM = rm -f -MKDIR = mkdir -FIND = find -INSTALL = install -FLEX = flex -BISON = bison -STRIP = strip - -LK_DIR = $(srctree)/tools/lib/lk/ -TRACE_EVENT_DIR = $(srctree)/tools/lib/traceevent/ - -# include config/Makefile by default and rule out -# non-config cases -config := 1 - -NON_CONFIG_TARGETS := clean TAGS tags cscope help - -ifdef MAKECMDGOALS -ifeq ($(filter-out $(NON_CONFIG_TARGETS),$(MAKECMDGOALS)),) - config := 0 -endif -endif - -ifeq ($(config),1) -include config/Makefile -endif - -export prefix bindir sharedir sysconfdir - -# sparse is architecture-neutral, which means that we need to tell it -# explicitly what architecture to check for. Fix this up for yours.. -SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__ - -# Guard against environment variables -BUILTIN_OBJS = -LIB_H = -LIB_OBJS = -PYRF_OBJS = -SCRIPT_SH = - -SCRIPT_SH += perf-archive.sh - -grep-libs = $(filter -l%,$(1)) -strip-libs = $(filter-out -l%,$(1)) - -ifneq ($(OUTPUT),) - TE_PATH=$(OUTPUT) -ifneq ($(subdir),) - LK_PATH=$(OUTPUT)/../lib/lk/ -else - LK_PATH=$(OUTPUT) -endif -else - TE_PATH=$(TRACE_EVENT_DIR) - LK_PATH=$(LK_DIR) -endif - -LIBTRACEEVENT = $(TE_PATH)libtraceevent.a -export LIBTRACEEVENT - -LIBLK = $(LK_PATH)liblk.a -export LIBLK - -# python extension build directories -PYTHON_EXTBUILD := $(OUTPUT)python_ext_build/ -PYTHON_EXTBUILD_LIB := $(PYTHON_EXTBUILD)lib/ -PYTHON_EXTBUILD_TMP := $(PYTHON_EXTBUILD)tmp/ -export PYTHON_EXTBUILD_LIB PYTHON_EXTBUILD_TMP - -python-clean := rm -rf $(PYTHON_EXTBUILD) $(OUTPUT)python/perf.so - -PYTHON_EXT_SRCS := $(shell grep -v ^\# util/python-ext-sources) -PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py $(LIBTRACEEVENT) $(LIBLK) - -$(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS) - $(QUIET_GEN)CFLAGS='$(CFLAGS)' $(PYTHON_WORD) util/setup.py \ - --quiet build_ext; \ - mkdir -p $(OUTPUT)python && \ - cp $(PYTHON_EXTBUILD_LIB)perf.so $(OUTPUT)python/ -# -# No Perl scripts right now: -# - -SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) - -# -# Single 'perf' binary right now: -# -PROGRAMS += $(OUTPUT)perf - -# what 'all' will build and 'install' will install, in perfexecdir -ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) - -# what 'all' will build but not install in perfexecdir -OTHER_PROGRAMS = $(OUTPUT)perf - -# Set paths to tools early so that they can be used for version tests. -ifndef SHELL_PATH - SHELL_PATH = /bin/sh -endif -ifndef PERL_PATH - PERL_PATH = /usr/bin/perl -endif - -export PERL_PATH - -$(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(OUTPUT)util/parse-events-bison.c - $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/parse-events-flex.h $(PARSER_DEBUG_FLEX) -t util/parse-events.l > $(OUTPUT)util/parse-events-flex.c - -$(OUTPUT)util/parse-events-bison.c: util/parse-events.y - $(QUIET_BISON)$(BISON) -v util/parse-events.y -d $(PARSER_DEBUG_BISON) -o $(OUTPUT)util/parse-events-bison.c -p parse_events_ - -$(OUTPUT)util/pmu-flex.c: util/pmu.l $(OUTPUT)util/pmu-bison.c - $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/pmu-flex.h -t util/pmu.l > $(OUTPUT)util/pmu-flex.c - -$(OUTPUT)util/pmu-bison.c: util/pmu.y - $(QUIET_BISON)$(BISON) -v util/pmu.y -d -o $(OUTPUT)util/pmu-bison.c -p perf_pmu_ - -$(OUTPUT)util/parse-events.o: $(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-bison.c -$(OUTPUT)util/pmu.o: $(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-bison.c - -LIB_FILE=$(OUTPUT)libperf.a - -LIB_H += ../../include/uapi/linux/perf_event.h -LIB_H += ../../include/linux/rbtree.h -LIB_H += ../../include/linux/list.h -LIB_H += ../../include/uapi/linux/const.h -LIB_H += ../../include/linux/hash.h -LIB_H += ../../include/linux/stringify.h -LIB_H += util/include/linux/bitmap.h -LIB_H += util/include/linux/bitops.h -LIB_H += util/include/linux/compiler.h -LIB_H += util/include/linux/const.h -LIB_H += util/include/linux/ctype.h -LIB_H += util/include/linux/kernel.h -LIB_H += util/include/linux/list.h -LIB_H += util/include/linux/export.h -LIB_H += util/include/linux/magic.h -LIB_H += util/include/linux/poison.h -LIB_H += util/include/linux/prefetch.h -LIB_H += util/include/linux/rbtree.h -LIB_H += util/include/linux/rbtree_augmented.h -LIB_H += util/include/linux/string.h -LIB_H += util/include/linux/types.h -LIB_H += util/include/linux/linkage.h -LIB_H += util/include/asm/asm-offsets.h -LIB_H += util/include/asm/bug.h -LIB_H += util/include/asm/byteorder.h -LIB_H += util/include/asm/hweight.h -LIB_H += util/include/asm/swab.h -LIB_H += util/include/asm/system.h -LIB_H += util/include/asm/uaccess.h -LIB_H += util/include/dwarf-regs.h -LIB_H += util/include/asm/dwarf2.h -LIB_H += util/include/asm/cpufeature.h -LIB_H += util/include/asm/unistd_32.h -LIB_H += util/include/asm/unistd_64.h -LIB_H += perf.h -LIB_H += util/annotate.h -LIB_H += util/cache.h -LIB_H += util/callchain.h -LIB_H += util/build-id.h -LIB_H += util/debug.h -LIB_H += util/sysfs.h -LIB_H += util/pmu.h -LIB_H += util/event.h -LIB_H += util/evsel.h -LIB_H += util/evlist.h -LIB_H += util/exec_cmd.h -LIB_H += util/types.h -LIB_H += util/levenshtein.h -LIB_H += util/machine.h -LIB_H += util/map.h -LIB_H += util/parse-options.h -LIB_H += util/parse-events.h -LIB_H += util/quote.h -LIB_H += util/util.h -LIB_H += util/xyarray.h -LIB_H += util/header.h -LIB_H += util/help.h -LIB_H += util/session.h -LIB_H += util/strbuf.h -LIB_H += util/strlist.h -LIB_H += util/strfilter.h -LIB_H += util/svghelper.h -LIB_H += util/tool.h -LIB_H += util/run-command.h -LIB_H += util/sigchain.h -LIB_H += util/dso.h -LIB_H += util/symbol.h -LIB_H += util/color.h -LIB_H += util/values.h -LIB_H += util/sort.h -LIB_H += util/hist.h -LIB_H += util/thread.h -LIB_H += util/thread_map.h -LIB_H += util/trace-event.h -LIB_H += util/probe-finder.h -LIB_H += util/dwarf-aux.h -LIB_H += util/probe-event.h -LIB_H += util/pstack.h -LIB_H += util/cpumap.h -LIB_H += util/top.h -LIB_H += $(ARCH_INCLUDE) -LIB_H += util/cgroup.h -LIB_H += $(LIB_INCLUDE)traceevent/event-parse.h -LIB_H += util/target.h -LIB_H += util/rblist.h -LIB_H += util/intlist.h -LIB_H += util/perf_regs.h -LIB_H += util/unwind.h -LIB_H += util/vdso.h -LIB_H += ui/helpline.h -LIB_H += ui/progress.h -LIB_H += ui/util.h -LIB_H += ui/ui.h - -LIB_OBJS += $(OUTPUT)util/abspath.o -LIB_OBJS += $(OUTPUT)util/alias.o -LIB_OBJS += $(OUTPUT)util/annotate.o -LIB_OBJS += $(OUTPUT)util/build-id.o -LIB_OBJS += $(OUTPUT)util/config.o -LIB_OBJS += $(OUTPUT)util/ctype.o -LIB_OBJS += $(OUTPUT)util/sysfs.o -LIB_OBJS += $(OUTPUT)util/pmu.o -LIB_OBJS += $(OUTPUT)util/environment.o -LIB_OBJS += $(OUTPUT)util/event.o -LIB_OBJS += $(OUTPUT)util/evlist.o -LIB_OBJS += $(OUTPUT)util/evsel.o -LIB_OBJS += $(OUTPUT)util/exec_cmd.o -LIB_OBJS += $(OUTPUT)util/help.o -LIB_OBJS += $(OUTPUT)util/levenshtein.o -LIB_OBJS += $(OUTPUT)util/parse-options.o -LIB_OBJS += $(OUTPUT)util/parse-events.o -LIB_OBJS += $(OUTPUT)util/path.o -LIB_OBJS += $(OUTPUT)util/rbtree.o -LIB_OBJS += $(OUTPUT)util/bitmap.o -LIB_OBJS += $(OUTPUT)util/hweight.o -LIB_OBJS += $(OUTPUT)util/run-command.o -LIB_OBJS += $(OUTPUT)util/quote.o -LIB_OBJS += $(OUTPUT)util/strbuf.o -LIB_OBJS += $(OUTPUT)util/string.o -LIB_OBJS += $(OUTPUT)util/strlist.o -LIB_OBJS += $(OUTPUT)util/strfilter.o -LIB_OBJS += $(OUTPUT)util/top.o -LIB_OBJS += $(OUTPUT)util/usage.o -LIB_OBJS += $(OUTPUT)util/wrapper.o -LIB_OBJS += $(OUTPUT)util/sigchain.o -LIB_OBJS += $(OUTPUT)util/dso.o -LIB_OBJS += $(OUTPUT)util/symbol.o -LIB_OBJS += $(OUTPUT)util/symbol-elf.o -LIB_OBJS += $(OUTPUT)util/color.o -LIB_OBJS += $(OUTPUT)util/pager.o -LIB_OBJS += $(OUTPUT)util/header.o -LIB_OBJS += $(OUTPUT)util/callchain.o -LIB_OBJS += $(OUTPUT)util/values.o -LIB_OBJS += $(OUTPUT)util/debug.o -LIB_OBJS += $(OUTPUT)util/machine.o -LIB_OBJS += $(OUTPUT)util/map.o -LIB_OBJS += $(OUTPUT)util/pstack.o -LIB_OBJS += $(OUTPUT)util/session.o -LIB_OBJS += $(OUTPUT)util/thread.o -LIB_OBJS += $(OUTPUT)util/thread_map.o -LIB_OBJS += $(OUTPUT)util/trace-event-parse.o -LIB_OBJS += $(OUTPUT)util/parse-events-flex.o -LIB_OBJS += $(OUTPUT)util/parse-events-bison.o -LIB_OBJS += $(OUTPUT)util/pmu-flex.o -LIB_OBJS += $(OUTPUT)util/pmu-bison.o -LIB_OBJS += $(OUTPUT)util/trace-event-read.o -LIB_OBJS += $(OUTPUT)util/trace-event-info.o -LIB_OBJS += $(OUTPUT)util/trace-event-scripting.o -LIB_OBJS += $(OUTPUT)util/svghelper.o -LIB_OBJS += $(OUTPUT)util/sort.o -LIB_OBJS += $(OUTPUT)util/hist.o -LIB_OBJS += $(OUTPUT)util/probe-event.o -LIB_OBJS += $(OUTPUT)util/util.o -LIB_OBJS += $(OUTPUT)util/xyarray.o -LIB_OBJS += $(OUTPUT)util/cpumap.o -LIB_OBJS += $(OUTPUT)util/cgroup.o -LIB_OBJS += $(OUTPUT)util/target.o -LIB_OBJS += $(OUTPUT)util/rblist.o -LIB_OBJS += $(OUTPUT)util/intlist.o -LIB_OBJS += $(OUTPUT)util/vdso.o -LIB_OBJS += $(OUTPUT)util/stat.o -LIB_OBJS += $(OUTPUT)util/record.o - -LIB_OBJS += $(OUTPUT)ui/setup.o -LIB_OBJS += $(OUTPUT)ui/helpline.o -LIB_OBJS += $(OUTPUT)ui/progress.o -LIB_OBJS += $(OUTPUT)ui/util.o -LIB_OBJS += $(OUTPUT)ui/hist.o -LIB_OBJS += $(OUTPUT)ui/stdio/hist.o - -LIB_OBJS += $(OUTPUT)arch/common.o - -LIB_OBJS += $(OUTPUT)tests/parse-events.o -LIB_OBJS += $(OUTPUT)tests/dso-data.o -LIB_OBJS += $(OUTPUT)tests/attr.o -LIB_OBJS += $(OUTPUT)tests/vmlinux-kallsyms.o -LIB_OBJS += $(OUTPUT)tests/open-syscall.o -LIB_OBJS += $(OUTPUT)tests/open-syscall-all-cpus.o -LIB_OBJS += $(OUTPUT)tests/open-syscall-tp-fields.o -LIB_OBJS += $(OUTPUT)tests/mmap-basic.o -LIB_OBJS += $(OUTPUT)tests/perf-record.o -LIB_OBJS += $(OUTPUT)tests/rdpmc.o -LIB_OBJS += $(OUTPUT)tests/evsel-roundtrip-name.o -LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o -LIB_OBJS += $(OUTPUT)tests/pmu.o -LIB_OBJS += $(OUTPUT)tests/hists_link.o -LIB_OBJS += $(OUTPUT)tests/python-use.o -LIB_OBJS += $(OUTPUT)tests/bp_signal.o -LIB_OBJS += $(OUTPUT)tests/bp_signal_overflow.o -LIB_OBJS += $(OUTPUT)tests/task-exit.o -LIB_OBJS += $(OUTPUT)tests/sw-clock.o -ifeq ($(ARCH),x86) -LIB_OBJS += $(OUTPUT)tests/perf-time-to-tsc.o -endif -LIB_OBJS += $(OUTPUT)tests/code-reading.o -LIB_OBJS += $(OUTPUT)tests/sample-parsing.o -LIB_OBJS += $(OUTPUT)tests/parse-no-sample-id-all.o - -BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o -BUILTIN_OBJS += $(OUTPUT)builtin-bench.o -# Benchmark modules -BUILTIN_OBJS += $(OUTPUT)bench/sched-messaging.o -BUILTIN_OBJS += $(OUTPUT)bench/sched-pipe.o -ifeq ($(RAW_ARCH),x86_64) -BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy-x86-64-asm.o -BUILTIN_OBJS += $(OUTPUT)bench/mem-memset-x86-64-asm.o -endif -BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy.o -BUILTIN_OBJS += $(OUTPUT)bench/mem-memset.o - -BUILTIN_OBJS += $(OUTPUT)builtin-diff.o -BUILTIN_OBJS += $(OUTPUT)builtin-evlist.o -BUILTIN_OBJS += $(OUTPUT)builtin-help.o -BUILTIN_OBJS += $(OUTPUT)builtin-sched.o -BUILTIN_OBJS += $(OUTPUT)builtin-buildid-list.o -BUILTIN_OBJS += $(OUTPUT)builtin-buildid-cache.o -BUILTIN_OBJS += $(OUTPUT)builtin-list.o -BUILTIN_OBJS += $(OUTPUT)builtin-record.o -BUILTIN_OBJS += $(OUTPUT)builtin-report.o -BUILTIN_OBJS += $(OUTPUT)builtin-stat.o -BUILTIN_OBJS += $(OUTPUT)builtin-timechart.o -BUILTIN_OBJS += $(OUTPUT)builtin-top.o -BUILTIN_OBJS += $(OUTPUT)builtin-script.o -BUILTIN_OBJS += $(OUTPUT)builtin-probe.o -BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o -BUILTIN_OBJS += $(OUTPUT)builtin-lock.o -BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o -BUILTIN_OBJS += $(OUTPUT)builtin-inject.o -BUILTIN_OBJS += $(OUTPUT)tests/builtin-test.o -BUILTIN_OBJS += $(OUTPUT)builtin-mem.o - -PERFLIBS = $(LIB_FILE) $(LIBLK) $(LIBTRACEEVENT) - -# We choose to avoid "if .. else if .. else .. endif endif" -# because maintaining the nesting to match is a pain. If -# we had "elif" things would have been much nicer... - --include arch/$(ARCH)/Makefile - -ifneq ($(OUTPUT),) - CFLAGS += -I$(OUTPUT) -endif - -ifdef NO_LIBELF -EXTLIBS := $(filter-out -lelf,$(EXTLIBS)) - -# Remove ELF/DWARF dependent codes -LIB_OBJS := $(filter-out $(OUTPUT)util/symbol-elf.o,$(LIB_OBJS)) -LIB_OBJS := $(filter-out $(OUTPUT)util/dwarf-aux.o,$(LIB_OBJS)) -LIB_OBJS := $(filter-out $(OUTPUT)util/probe-event.o,$(LIB_OBJS)) -LIB_OBJS := $(filter-out $(OUTPUT)util/probe-finder.o,$(LIB_OBJS)) - -BUILTIN_OBJS := $(filter-out $(OUTPUT)builtin-probe.o,$(BUILTIN_OBJS)) - -# Use minimal symbol handling -LIB_OBJS += $(OUTPUT)util/symbol-minimal.o - -else # NO_LIBELF -ifndef NO_DWARF - LIB_OBJS += $(OUTPUT)util/probe-finder.o - LIB_OBJS += $(OUTPUT)util/dwarf-aux.o -endif # NO_DWARF -endif # NO_LIBELF - -ifndef NO_LIBUNWIND - LIB_OBJS += $(OUTPUT)util/unwind.o -endif -LIB_OBJS += $(OUTPUT)tests/keep-tracking.o - -ifndef NO_LIBAUDIT - BUILTIN_OBJS += $(OUTPUT)builtin-trace.o -endif - -ifndef NO_SLANG - LIB_OBJS += $(OUTPUT)ui/browser.o - LIB_OBJS += $(OUTPUT)ui/browsers/annotate.o - LIB_OBJS += $(OUTPUT)ui/browsers/hists.o - LIB_OBJS += $(OUTPUT)ui/browsers/map.o - LIB_OBJS += $(OUTPUT)ui/browsers/scripts.o - LIB_OBJS += $(OUTPUT)ui/tui/setup.o - LIB_OBJS += $(OUTPUT)ui/tui/util.o - LIB_OBJS += $(OUTPUT)ui/tui/helpline.o - LIB_OBJS += $(OUTPUT)ui/tui/progress.o - LIB_H += ui/browser.h - LIB_H += ui/browsers/map.h - LIB_H += ui/keysyms.h - LIB_H += ui/libslang.h -endif - -ifndef NO_GTK2 - LIB_OBJS += $(OUTPUT)ui/gtk/browser.o - LIB_OBJS += $(OUTPUT)ui/gtk/hists.o - LIB_OBJS += $(OUTPUT)ui/gtk/setup.o - LIB_OBJS += $(OUTPUT)ui/gtk/util.o - LIB_OBJS += $(OUTPUT)ui/gtk/helpline.o - LIB_OBJS += $(OUTPUT)ui/gtk/progress.o - LIB_OBJS += $(OUTPUT)ui/gtk/annotate.o -endif - -ifndef NO_LIBPERL - LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-perl.o - LIB_OBJS += $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o -endif - -ifndef NO_LIBPYTHON - LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-python.o - LIB_OBJS += $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o -endif - -ifeq ($(NO_PERF_REGS),0) - ifeq ($(ARCH),x86) - LIB_H += arch/x86/include/perf_regs.h +ifeq ($(JOBS),) + JOBS := $(shell grep -c ^processor /proc/cpuinfo 2>/dev/null) + ifeq ($(JOBS),) + JOBS := 1 endif endif -ifndef NO_LIBNUMA - BUILTIN_OBJS += $(OUTPUT)bench/numa.o -endif - -ifdef ASCIIDOC8 - export ASCIIDOC8 -endif - -LIBS = -Wl,--whole-archive $(PERFLIBS) -Wl,--no-whole-archive -Wl,--start-group $(EXTLIBS) -Wl,--end-group - -export INSTALL SHELL_PATH - -### Build rules - -SHELL = $(SHELL_PATH) - -all: shell_compatibility_test $(ALL_PROGRAMS) $(LANG_BINDINGS) $(OTHER_PROGRAMS) - -please_set_SHELL_PATH_to_a_more_modern_shell: - @$$(:) - -shell_compatibility_test: please_set_SHELL_PATH_to_a_more_modern_shell - -strip: $(PROGRAMS) $(OUTPUT)perf - $(STRIP) $(STRIP_OPTS) $(PROGRAMS) $(OUTPUT)perf - -$(OUTPUT)perf.o: perf.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -include $(OUTPUT)PERF-VERSION-FILE \ - '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ - $(CFLAGS) -c $(filter %.c,$^) -o $@ - -$(OUTPUT)perf: $(OUTPUT)perf.o $(BUILTIN_OBJS) $(PERFLIBS) - $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $(OUTPUT)perf.o \ - $(BUILTIN_OBJS) $(LIBS) -o $@ - -$(OUTPUT)builtin-help.o: builtin-help.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) \ - '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ - '-DPERF_MAN_PATH="$(mandir_SQ)"' \ - '-DPERF_INFO_PATH="$(infodir_SQ)"' $< - -$(OUTPUT)builtin-timechart.o: builtin-timechart.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) \ - '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ - '-DPERF_MAN_PATH="$(mandir_SQ)"' \ - '-DPERF_INFO_PATH="$(infodir_SQ)"' $< - -$(OUTPUT)common-cmds.h: util/generate-cmdlist.sh command-list.txt - -$(OUTPUT)common-cmds.h: $(wildcard Documentation/perf-*.txt) - $(QUIET_GEN). util/generate-cmdlist.sh > $@+ && mv $@+ $@ - -$(SCRIPTS) : % : %.sh - $(QUIET_GEN)$(INSTALL) '$@.sh' '$(OUTPUT)$@' - -# These can record PERF_VERSION -$(OUTPUT)perf.o perf.spec \ - $(SCRIPTS) \ - : $(OUTPUT)PERF-VERSION-FILE - -.SUFFIXES: -.SUFFIXES: .o .c .S .s - -# These two need to be here so that when O= is not used they take precedence -# over the general rule for .o - -$(OUTPUT)util/%-flex.o: $(OUTPUT)util/%-flex.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c -Iutil/ $(CFLAGS) -w $< - -$(OUTPUT)util/%-bison.o: $(OUTPUT)util/%-bison.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c -Iutil/ $(CFLAGS) -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -w $< - -$(OUTPUT)%.o: %.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $< -$(OUTPUT)%.i: %.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -E $(CFLAGS) $< -$(OUTPUT)%.s: %.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -S $(CFLAGS) $< -$(OUTPUT)%.o: %.S - $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $< -$(OUTPUT)%.s: %.S - $(QUIET_CC)$(CC) -o $@ -E $(CFLAGS) $< - -$(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) \ - '-DPERF_EXEC_PATH="$(perfexecdir_SQ)"' \ - '-DPREFIX="$(prefix_SQ)"' \ - $< - -$(OUTPUT)tests/attr.o: tests/attr.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) \ - '-DBINDIR="$(bindir_SQ)"' -DPYTHON='"$(PYTHON_WORD)"' \ - $< - -$(OUTPUT)tests/python-use.o: tests/python-use.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) \ - -DPYTHONPATH='"$(OUTPUT)python"' \ - -DPYTHON='"$(PYTHON_WORD)"' \ - $< - -$(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< - -$(OUTPUT)ui/browser.o: ui/browser.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DENABLE_SLFUTURE_CONST $< - -$(OUTPUT)ui/browsers/annotate.o: ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DENABLE_SLFUTURE_CONST $< - -$(OUTPUT)ui/browsers/hists.o: ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DENABLE_SLFUTURE_CONST $< - -$(OUTPUT)ui/browsers/map.o: ui/browsers/map.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DENABLE_SLFUTURE_CONST $< +export JOBS -$(OUTPUT)ui/browsers/scripts.o: ui/browsers/scripts.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DENABLE_SLFUTURE_CONST $< - -$(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -Wno-unused-parameter -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< - -$(OUTPUT)util/parse-events.o: util/parse-events.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -Wno-redundant-decls $< - -$(OUTPUT)util/scripting-engines/trace-event-perl.o: util/scripting-engines/trace-event-perl.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow -Wno-undef -Wno-switch-default $< - -$(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o: scripts/perl/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs -Wno-undef -Wno-switch-default $< - -$(OUTPUT)util/scripting-engines/trace-event-python.o: util/scripting-engines/trace-event-python.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $< - -$(OUTPUT)scripts/python/Perf-Trace-Util/Context.o: scripts/python/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $< - -$(OUTPUT)perf-%: %.o $(PERFLIBS) - $(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $(LDFLAGS) $(filter %.o,$^) $(LIBS) - -$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) -$(patsubst perf-%,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) - -# we compile into subdirectories. if the target directory is not the source directory, they might not exists. So -# we depend the various files onto their directories. -DIRECTORY_DEPS = $(LIB_OBJS) $(BUILTIN_OBJS) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h -$(DIRECTORY_DEPS): | $(sort $(dir $(DIRECTORY_DEPS))) -# In the second step, we make a rule to actually create these directories -$(sort $(dir $(DIRECTORY_DEPS))): - $(QUIET_MKDIR)$(MKDIR) -p $@ 2>/dev/null - -$(LIB_FILE): $(LIB_OBJS) - $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS) - -# libtraceevent.a -TE_SOURCES = $(wildcard $(TRACE_EVENT_DIR)*.[ch]) - -$(LIBTRACEEVENT): $(TE_SOURCES) - $(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) libtraceevent.a - -$(LIBTRACEEVENT)-clean: - $(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) clean - -LIBLK_SOURCES = $(wildcard $(LK_PATH)*.[ch]) - -# if subdir is set, we've been called from above so target has been built -# already -$(LIBLK): $(LIBLK_SOURCES) -ifeq ($(subdir),) - $(QUIET_SUBDIR0)$(LK_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) liblk.a -endif - -$(LIBLK)-clean: -ifeq ($(subdir),) - $(QUIET_SUBDIR0)$(LK_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) clean -endif - -help: - @echo 'Perf make targets:' - @echo ' doc - make *all* documentation (see below)' - @echo ' man - make manpage documentation (access with man )' - @echo ' html - make html documentation' - @echo ' info - make GNU info documentation (access with info )' - @echo ' pdf - make pdf documentation' - @echo ' TAGS - use etags to make tag information for source browsing' - @echo ' tags - use ctags to make tag information for source browsing' - @echo ' cscope - use cscope to make interactive browsing database' - @echo '' - @echo 'Perf install targets:' - @echo ' NOTE: documentation build requires asciidoc, xmlto packages to be installed' - @echo ' HINT: use "make prefix= " to install to a particular' - @echo ' path like make prefix=/usr/local install install-doc' - @echo ' install - install compiled binaries' - @echo ' install-doc - install *all* documentation' - @echo ' install-man - install manpage documentation' - @echo ' install-html - install html documentation' - @echo ' install-info - install GNU info documentation' - @echo ' install-pdf - install pdf documentation' - @echo '' - @echo ' quick-install-doc - alias for quick-install-man' - @echo ' quick-install-man - install the documentation quickly' - @echo ' quick-install-html - install the html documentation quickly' - @echo '' - @echo 'Perf maintainer targets:' - @echo ' clean - clean all binary objects and build output' - - -DOC_TARGETS := doc man html info pdf - -INSTALL_DOC_TARGETS := $(patsubst %,install-%,$(DOC_TARGETS)) try-install-man -INSTALL_DOC_TARGETS += quick-install-doc quick-install-man quick-install-html - -# 'make doc' should call 'make -C Documentation all' -$(DOC_TARGETS): - $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) $(@:doc=all) - -TAGS: - $(RM) TAGS - $(FIND) . -name '*.[hcS]' -print | xargs etags -a - -tags: - $(RM) tags - $(FIND) . -name '*.[hcS]' -print | xargs ctags -a - -cscope: - $(RM) cscope* - $(FIND) . -name '*.[hcS]' -print | xargs cscope -b - -### Detect prefix changes -TRACK_CFLAGS = $(subst ','\'',$(CFLAGS)):\ - $(bindir_SQ):$(perfexecdir_SQ):$(template_dir_SQ):$(prefix_SQ) - -$(OUTPUT)PERF-CFLAGS: .FORCE-PERF-CFLAGS - @FLAGS='$(TRACK_CFLAGS)'; \ - if test x"$$FLAGS" != x"`cat $(OUTPUT)PERF-CFLAGS 2>/dev/null`" ; then \ - echo 1>&2 " * new build flags or prefix"; \ - echo "$$FLAGS" >$(OUTPUT)PERF-CFLAGS; \ - fi - -### Testing rules - -# GNU make supports exporting all variables by "export" without parameters. -# However, the environment gets quite big, and some programs have problems -# with that. - -check: $(OUTPUT)common-cmds.h - if sparse; \ - then \ - for i in *.c */*.c; \ - do \ - sparse $(CFLAGS) $(SPARSE_FLAGS) $$i || exit; \ - done; \ - else \ - exit 1; \ - fi - -### Installation rules - -install-bin: all - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' - $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)' - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' - $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' -ifndef NO_LIBPERL - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' - $(INSTALL) scripts/perl/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' - $(INSTALL) scripts/perl/*.pl -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl' - $(INSTALL) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' -endif -ifndef NO_LIBPYTHON - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace' - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' - $(INSTALL) scripts/python/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace' - $(INSTALL) scripts/python/*.py -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python' - $(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' -endif - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d' - $(INSTALL) bash_completion '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf' - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests' - $(INSTALL) tests/attr.py '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests' - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr' - $(INSTALL) tests/attr/* '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr' - -install: install-bin try-install-man - -install-python_ext: - $(PYTHON_WORD) util/setup.py --quiet install --root='/$(DESTDIR_SQ)' - -# 'make install-doc' should call 'make -C Documentation install' -$(INSTALL_DOC_TARGETS): - $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) $(@:-doc=) - -### Cleaning rules +$(info $(shell printf '# [ perf build: Doing '\''make \033[33m-j'$(JOBS)'\033[m'\'' parallel build. ]\n')) # -# This is here, not in config/Makefile, because config/Makefile does -# not get included for the clean target: +# Needed if no target specified: # -config-clean: - @$(MAKE) -C config/feature-checks clean - -clean: $(LIBTRACEEVENT)-clean $(LIBLK)-clean config-clean - $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS) - $(RM) $(ALL_PROGRAMS) perf - $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* - $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean - $(RM) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS - $(RM) $(OUTPUT)util/*-bison* - $(RM) $(OUTPUT)util/*-flex* - $(python-clean) - -# -# Trick: if ../../.git does not exist - we are building out of tree for example, -# then force version regeneration: -# -ifeq ($(wildcard ../../.git/HEAD),) - GIT-HEAD-PHONY = ../../.git/HEAD -else - GIT-HEAD-PHONY = -endif +all: + @$(MAKE) -f Makefile.perf --no-print-directory -j$(JOBS) $@ -.PHONY: all install clean config-clean strip -.PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell -.PHONY: $(GIT-HEAD-PHONY) TAGS tags cscope .FORCE-PERF-CFLAGS +%: + @$(MAKE) -f Makefile.perf --no-print-directory -j$(JOBS) $@ diff --git a/tools/perf/Makefile.parallel b/tools/perf/Makefile.parallel deleted file mode 100644 index ec5e08b..0000000 --- a/tools/perf/Makefile.parallel +++ /dev/null @@ -1,26 +0,0 @@ -# -# Do a parallel build with multiple jobs, based on the number of CPUs online -# in this system: 'make -j8' on a 8-CPU system, etc. -# -# (To override it, run 'make JOBS=1' and similar.) -# -ifeq ($(JOBS),) - JOBS := $(shell grep -c ^processor /proc/cpuinfo 2>/dev/null) - ifeq ($(JOBS),) - JOBS := 1 - endif -endif - -export JOBS - -$(info $(shell printf '# [ perf build: Doing '\''make \033[33m-j'$(JOBS)'\033[m'\'' parallel build. ]\n')) - -# -# Needed if no target specified: -# -all: - @$(MAKE) --no-print-directory -j$(JOBS) $@ - -%: - @$(MAKE) --no-print-directory -j$(JOBS) $@ - diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf new file mode 100644 index 0000000..178a1c8 --- /dev/null +++ b/tools/perf/Makefile.perf @@ -0,0 +1,842 @@ +include ../scripts/Makefile.include + +# The default target of this Makefile is... +all: + +include config/utilities.mak + +# Define V to have a more verbose compile. +# +# Define O to save output files in a separate directory. +# +# Define ARCH as name of target architecture if you want cross-builds. +# +# Define CROSS_COMPILE as prefix name of compiler if you want cross-builds. +# +# Define NO_LIBPERL to disable perl script extension. +# +# Define NO_LIBPYTHON to disable python script extension. +# +# Define PYTHON to point to the python binary if the default +# `python' is not correct; for example: PYTHON=python2 +# +# Define PYTHON_CONFIG to point to the python-config binary if +# the default `$(PYTHON)-config' is not correct. +# +# Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8 +# +# Define DOCBOOK_XSL_172 if you want to format man pages with DocBook XSL v1.72. +# +# Define LDFLAGS=-static to build a static binary. +# +# Define EXTRA_CFLAGS=-m64 or EXTRA_CFLAGS=-m32 as appropriate for cross-builds. +# +# Define NO_DWARF if you do not want debug-info analysis feature at all. +# +# Define WERROR=0 to disable treating any warnings as errors. +# +# Define NO_NEWT if you do not want TUI support. (deprecated) +# +# Define NO_SLANG if you do not want TUI support. +# +# Define NO_GTK2 if you do not want GTK+ GUI support. +# +# Define NO_DEMANGLE if you do not want C++ symbol demangling. +# +# Define NO_LIBELF if you do not want libelf dependency (e.g. cross-builds) +# +# Define NO_LIBUNWIND if you do not want libunwind dependency for dwarf +# backtrace post unwind. +# +# Define NO_BACKTRACE if you do not want stack backtrace debug feature +# +# Define NO_LIBNUMA if you do not want numa perf benchmark +# +# Define NO_LIBAUDIT if you do not want libaudit support +# +# Define NO_LIBBIONIC if you do not want bionic support + +ifeq ($(srctree),) +srctree := $(patsubst %/,%,$(dir $(shell pwd))) +srctree := $(patsubst %/,%,$(dir $(srctree))) +#$(info Determined 'srctree' to be $(srctree)) +endif + +ifneq ($(objtree),) +#$(info Determined 'objtree' to be $(objtree)) +endif + +ifneq ($(OUTPUT),) +#$(info Determined 'OUTPUT' to be $(OUTPUT)) +endif + +$(OUTPUT)PERF-VERSION-FILE: ../../.git/HEAD + @$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT) + @touch $(OUTPUT)PERF-VERSION-FILE + +CC = $(CROSS_COMPILE)gcc +AR = $(CROSS_COMPILE)ar + +RM = rm -f +MKDIR = mkdir +FIND = find +INSTALL = install +FLEX = flex +BISON = bison +STRIP = strip + +LK_DIR = $(srctree)/tools/lib/lk/ +TRACE_EVENT_DIR = $(srctree)/tools/lib/traceevent/ + +# include config/Makefile by default and rule out +# non-config cases +config := 1 + +NON_CONFIG_TARGETS := clean TAGS tags cscope help + +ifdef MAKECMDGOALS +ifeq ($(filter-out $(NON_CONFIG_TARGETS),$(MAKECMDGOALS)),) + config := 0 +endif +endif + +ifeq ($(config),1) +include config/Makefile +endif + +export prefix bindir sharedir sysconfdir + +# sparse is architecture-neutral, which means that we need to tell it +# explicitly what architecture to check for. Fix this up for yours.. +SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__ + +# Guard against environment variables +BUILTIN_OBJS = +LIB_H = +LIB_OBJS = +PYRF_OBJS = +SCRIPT_SH = + +SCRIPT_SH += perf-archive.sh + +grep-libs = $(filter -l%,$(1)) +strip-libs = $(filter-out -l%,$(1)) + +ifneq ($(OUTPUT),) + TE_PATH=$(OUTPUT) +ifneq ($(subdir),) + LK_PATH=$(OUTPUT)/../lib/lk/ +else + LK_PATH=$(OUTPUT) +endif +else + TE_PATH=$(TRACE_EVENT_DIR) + LK_PATH=$(LK_DIR) +endif + +LIBTRACEEVENT = $(TE_PATH)libtraceevent.a +export LIBTRACEEVENT + +LIBLK = $(LK_PATH)liblk.a +export LIBLK + +# python extension build directories +PYTHON_EXTBUILD := $(OUTPUT)python_ext_build/ +PYTHON_EXTBUILD_LIB := $(PYTHON_EXTBUILD)lib/ +PYTHON_EXTBUILD_TMP := $(PYTHON_EXTBUILD)tmp/ +export PYTHON_EXTBUILD_LIB PYTHON_EXTBUILD_TMP + +python-clean := rm -rf $(PYTHON_EXTBUILD) $(OUTPUT)python/perf.so + +PYTHON_EXT_SRCS := $(shell grep -v ^\# util/python-ext-sources) +PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py $(LIBTRACEEVENT) $(LIBLK) + +$(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS) + $(QUIET_GEN)CFLAGS='$(CFLAGS)' $(PYTHON_WORD) util/setup.py \ + --quiet build_ext; \ + mkdir -p $(OUTPUT)python && \ + cp $(PYTHON_EXTBUILD_LIB)perf.so $(OUTPUT)python/ +# +# No Perl scripts right now: +# + +SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) + +# +# Single 'perf' binary right now: +# +PROGRAMS += $(OUTPUT)perf + +# what 'all' will build and 'install' will install, in perfexecdir +ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) + +# what 'all' will build but not install in perfexecdir +OTHER_PROGRAMS = $(OUTPUT)perf + +# Set paths to tools early so that they can be used for version tests. +ifndef SHELL_PATH + SHELL_PATH = /bin/sh +endif +ifndef PERL_PATH + PERL_PATH = /usr/bin/perl +endif + +export PERL_PATH + +$(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(OUTPUT)util/parse-events-bison.c + $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/parse-events-flex.h $(PARSER_DEBUG_FLEX) -t util/parse-events.l > $(OUTPUT)util/parse-events-flex.c + +$(OUTPUT)util/parse-events-bison.c: util/parse-events.y + $(QUIET_BISON)$(BISON) -v util/parse-events.y -d $(PARSER_DEBUG_BISON) -o $(OUTPUT)util/parse-events-bison.c -p parse_events_ + +$(OUTPUT)util/pmu-flex.c: util/pmu.l $(OUTPUT)util/pmu-bison.c + $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/pmu-flex.h -t util/pmu.l > $(OUTPUT)util/pmu-flex.c + +$(OUTPUT)util/pmu-bison.c: util/pmu.y + $(QUIET_BISON)$(BISON) -v util/pmu.y -d -o $(OUTPUT)util/pmu-bison.c -p perf_pmu_ + +$(OUTPUT)util/parse-events.o: $(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-bison.c +$(OUTPUT)util/pmu.o: $(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-bison.c + +LIB_FILE=$(OUTPUT)libperf.a + +LIB_H += ../../include/uapi/linux/perf_event.h +LIB_H += ../../include/linux/rbtree.h +LIB_H += ../../include/linux/list.h +LIB_H += ../../include/uapi/linux/const.h +LIB_H += ../../include/linux/hash.h +LIB_H += ../../include/linux/stringify.h +LIB_H += util/include/linux/bitmap.h +LIB_H += util/include/linux/bitops.h +LIB_H += util/include/linux/compiler.h +LIB_H += util/include/linux/const.h +LIB_H += util/include/linux/ctype.h +LIB_H += util/include/linux/kernel.h +LIB_H += util/include/linux/list.h +LIB_H += util/include/linux/export.h +LIB_H += util/include/linux/magic.h +LIB_H += util/include/linux/poison.h +LIB_H += util/include/linux/prefetch.h +LIB_H += util/include/linux/rbtree.h +LIB_H += util/include/linux/rbtree_augmented.h +LIB_H += util/include/linux/string.h +LIB_H += util/include/linux/types.h +LIB_H += util/include/linux/linkage.h +LIB_H += util/include/asm/asm-offsets.h +LIB_H += util/include/asm/bug.h +LIB_H += util/include/asm/byteorder.h +LIB_H += util/include/asm/hweight.h +LIB_H += util/include/asm/swab.h +LIB_H += util/include/asm/system.h +LIB_H += util/include/asm/uaccess.h +LIB_H += util/include/dwarf-regs.h +LIB_H += util/include/asm/dwarf2.h +LIB_H += util/include/asm/cpufeature.h +LIB_H += util/include/asm/unistd_32.h +LIB_H += util/include/asm/unistd_64.h +LIB_H += perf.h +LIB_H += util/annotate.h +LIB_H += util/cache.h +LIB_H += util/callchain.h +LIB_H += util/build-id.h +LIB_H += util/debug.h +LIB_H += util/sysfs.h +LIB_H += util/pmu.h +LIB_H += util/event.h +LIB_H += util/evsel.h +LIB_H += util/evlist.h +LIB_H += util/exec_cmd.h +LIB_H += util/types.h +LIB_H += util/levenshtein.h +LIB_H += util/machine.h +LIB_H += util/map.h +LIB_H += util/parse-options.h +LIB_H += util/parse-events.h +LIB_H += util/quote.h +LIB_H += util/util.h +LIB_H += util/xyarray.h +LIB_H += util/header.h +LIB_H += util/help.h +LIB_H += util/session.h +LIB_H += util/strbuf.h +LIB_H += util/strlist.h +LIB_H += util/strfilter.h +LIB_H += util/svghelper.h +LIB_H += util/tool.h +LIB_H += util/run-command.h +LIB_H += util/sigchain.h +LIB_H += util/dso.h +LIB_H += util/symbol.h +LIB_H += util/color.h +LIB_H += util/values.h +LIB_H += util/sort.h +LIB_H += util/hist.h +LIB_H += util/thread.h +LIB_H += util/thread_map.h +LIB_H += util/trace-event.h +LIB_H += util/probe-finder.h +LIB_H += util/dwarf-aux.h +LIB_H += util/probe-event.h +LIB_H += util/pstack.h +LIB_H += util/cpumap.h +LIB_H += util/top.h +LIB_H += $(ARCH_INCLUDE) +LIB_H += util/cgroup.h +LIB_H += $(LIB_INCLUDE)traceevent/event-parse.h +LIB_H += util/target.h +LIB_H += util/rblist.h +LIB_H += util/intlist.h +LIB_H += util/perf_regs.h +LIB_H += util/unwind.h +LIB_H += util/vdso.h +LIB_H += ui/helpline.h +LIB_H += ui/progress.h +LIB_H += ui/util.h +LIB_H += ui/ui.h + +LIB_OBJS += $(OUTPUT)util/abspath.o +LIB_OBJS += $(OUTPUT)util/alias.o +LIB_OBJS += $(OUTPUT)util/annotate.o +LIB_OBJS += $(OUTPUT)util/build-id.o +LIB_OBJS += $(OUTPUT)util/config.o +LIB_OBJS += $(OUTPUT)util/ctype.o +LIB_OBJS += $(OUTPUT)util/sysfs.o +LIB_OBJS += $(OUTPUT)util/pmu.o +LIB_OBJS += $(OUTPUT)util/environment.o +LIB_OBJS += $(OUTPUT)util/event.o +LIB_OBJS += $(OUTPUT)util/evlist.o +LIB_OBJS += $(OUTPUT)util/evsel.o +LIB_OBJS += $(OUTPUT)util/exec_cmd.o +LIB_OBJS += $(OUTPUT)util/help.o +LIB_OBJS += $(OUTPUT)util/levenshtein.o +LIB_OBJS += $(OUTPUT)util/parse-options.o +LIB_OBJS += $(OUTPUT)util/parse-events.o +LIB_OBJS += $(OUTPUT)util/path.o +LIB_OBJS += $(OUTPUT)util/rbtree.o +LIB_OBJS += $(OUTPUT)util/bitmap.o +LIB_OBJS += $(OUTPUT)util/hweight.o +LIB_OBJS += $(OUTPUT)util/run-command.o +LIB_OBJS += $(OUTPUT)util/quote.o +LIB_OBJS += $(OUTPUT)util/strbuf.o +LIB_OBJS += $(OUTPUT)util/string.o +LIB_OBJS += $(OUTPUT)util/strlist.o +LIB_OBJS += $(OUTPUT)util/strfilter.o +LIB_OBJS += $(OUTPUT)util/top.o +LIB_OBJS += $(OUTPUT)util/usage.o +LIB_OBJS += $(OUTPUT)util/wrapper.o +LIB_OBJS += $(OUTPUT)util/sigchain.o +LIB_OBJS += $(OUTPUT)util/dso.o +LIB_OBJS += $(OUTPUT)util/symbol.o +LIB_OBJS += $(OUTPUT)util/symbol-elf.o +LIB_OBJS += $(OUTPUT)util/color.o +LIB_OBJS += $(OUTPUT)util/pager.o +LIB_OBJS += $(OUTPUT)util/header.o +LIB_OBJS += $(OUTPUT)util/callchain.o +LIB_OBJS += $(OUTPUT)util/values.o +LIB_OBJS += $(OUTPUT)util/debug.o +LIB_OBJS += $(OUTPUT)util/machine.o +LIB_OBJS += $(OUTPUT)util/map.o +LIB_OBJS += $(OUTPUT)util/pstack.o +LIB_OBJS += $(OUTPUT)util/session.o +LIB_OBJS += $(OUTPUT)util/thread.o +LIB_OBJS += $(OUTPUT)util/thread_map.o +LIB_OBJS += $(OUTPUT)util/trace-event-parse.o +LIB_OBJS += $(OUTPUT)util/parse-events-flex.o +LIB_OBJS += $(OUTPUT)util/parse-events-bison.o +LIB_OBJS += $(OUTPUT)util/pmu-flex.o +LIB_OBJS += $(OUTPUT)util/pmu-bison.o +LIB_OBJS += $(OUTPUT)util/trace-event-read.o +LIB_OBJS += $(OUTPUT)util/trace-event-info.o +LIB_OBJS += $(OUTPUT)util/trace-event-scripting.o +LIB_OBJS += $(OUTPUT)util/svghelper.o +LIB_OBJS += $(OUTPUT)util/sort.o +LIB_OBJS += $(OUTPUT)util/hist.o +LIB_OBJS += $(OUTPUT)util/probe-event.o +LIB_OBJS += $(OUTPUT)util/util.o +LIB_OBJS += $(OUTPUT)util/xyarray.o +LIB_OBJS += $(OUTPUT)util/cpumap.o +LIB_OBJS += $(OUTPUT)util/cgroup.o +LIB_OBJS += $(OUTPUT)util/target.o +LIB_OBJS += $(OUTPUT)util/rblist.o +LIB_OBJS += $(OUTPUT)util/intlist.o +LIB_OBJS += $(OUTPUT)util/vdso.o +LIB_OBJS += $(OUTPUT)util/stat.o +LIB_OBJS += $(OUTPUT)util/record.o + +LIB_OBJS += $(OUTPUT)ui/setup.o +LIB_OBJS += $(OUTPUT)ui/helpline.o +LIB_OBJS += $(OUTPUT)ui/progress.o +LIB_OBJS += $(OUTPUT)ui/util.o +LIB_OBJS += $(OUTPUT)ui/hist.o +LIB_OBJS += $(OUTPUT)ui/stdio/hist.o + +LIB_OBJS += $(OUTPUT)arch/common.o + +LIB_OBJS += $(OUTPUT)tests/parse-events.o +LIB_OBJS += $(OUTPUT)tests/dso-data.o +LIB_OBJS += $(OUTPUT)tests/attr.o +LIB_OBJS += $(OUTPUT)tests/vmlinux-kallsyms.o +LIB_OBJS += $(OUTPUT)tests/open-syscall.o +LIB_OBJS += $(OUTPUT)tests/open-syscall-all-cpus.o +LIB_OBJS += $(OUTPUT)tests/open-syscall-tp-fields.o +LIB_OBJS += $(OUTPUT)tests/mmap-basic.o +LIB_OBJS += $(OUTPUT)tests/perf-record.o +LIB_OBJS += $(OUTPUT)tests/rdpmc.o +LIB_OBJS += $(OUTPUT)tests/evsel-roundtrip-name.o +LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o +LIB_OBJS += $(OUTPUT)tests/pmu.o +LIB_OBJS += $(OUTPUT)tests/hists_link.o +LIB_OBJS += $(OUTPUT)tests/python-use.o +LIB_OBJS += $(OUTPUT)tests/bp_signal.o +LIB_OBJS += $(OUTPUT)tests/bp_signal_overflow.o +LIB_OBJS += $(OUTPUT)tests/task-exit.o +LIB_OBJS += $(OUTPUT)tests/sw-clock.o +ifeq ($(ARCH),x86) +LIB_OBJS += $(OUTPUT)tests/perf-time-to-tsc.o +endif +LIB_OBJS += $(OUTPUT)tests/code-reading.o +LIB_OBJS += $(OUTPUT)tests/sample-parsing.o +LIB_OBJS += $(OUTPUT)tests/parse-no-sample-id-all.o + +BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o +BUILTIN_OBJS += $(OUTPUT)builtin-bench.o +# Benchmark modules +BUILTIN_OBJS += $(OUTPUT)bench/sched-messaging.o +BUILTIN_OBJS += $(OUTPUT)bench/sched-pipe.o +ifeq ($(RAW_ARCH),x86_64) +BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy-x86-64-asm.o +BUILTIN_OBJS += $(OUTPUT)bench/mem-memset-x86-64-asm.o +endif +BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy.o +BUILTIN_OBJS += $(OUTPUT)bench/mem-memset.o + +BUILTIN_OBJS += $(OUTPUT)builtin-diff.o +BUILTIN_OBJS += $(OUTPUT)builtin-evlist.o +BUILTIN_OBJS += $(OUTPUT)builtin-help.o +BUILTIN_OBJS += $(OUTPUT)builtin-sched.o +BUILTIN_OBJS += $(OUTPUT)builtin-buildid-list.o +BUILTIN_OBJS += $(OUTPUT)builtin-buildid-cache.o +BUILTIN_OBJS += $(OUTPUT)builtin-list.o +BUILTIN_OBJS += $(OUTPUT)builtin-record.o +BUILTIN_OBJS += $(OUTPUT)builtin-report.o +BUILTIN_OBJS += $(OUTPUT)builtin-stat.o +BUILTIN_OBJS += $(OUTPUT)builtin-timechart.o +BUILTIN_OBJS += $(OUTPUT)builtin-top.o +BUILTIN_OBJS += $(OUTPUT)builtin-script.o +BUILTIN_OBJS += $(OUTPUT)builtin-probe.o +BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o +BUILTIN_OBJS += $(OUTPUT)builtin-lock.o +BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o +BUILTIN_OBJS += $(OUTPUT)builtin-inject.o +BUILTIN_OBJS += $(OUTPUT)tests/builtin-test.o +BUILTIN_OBJS += $(OUTPUT)builtin-mem.o + +PERFLIBS = $(LIB_FILE) $(LIBLK) $(LIBTRACEEVENT) + +# We choose to avoid "if .. else if .. else .. endif endif" +# because maintaining the nesting to match is a pain. If +# we had "elif" things would have been much nicer... + +-include arch/$(ARCH)/Makefile + +ifneq ($(OUTPUT),) + CFLAGS += -I$(OUTPUT) +endif + +ifdef NO_LIBELF +EXTLIBS := $(filter-out -lelf,$(EXTLIBS)) + +# Remove ELF/DWARF dependent codes +LIB_OBJS := $(filter-out $(OUTPUT)util/symbol-elf.o,$(LIB_OBJS)) +LIB_OBJS := $(filter-out $(OUTPUT)util/dwarf-aux.o,$(LIB_OBJS)) +LIB_OBJS := $(filter-out $(OUTPUT)util/probe-event.o,$(LIB_OBJS)) +LIB_OBJS := $(filter-out $(OUTPUT)util/probe-finder.o,$(LIB_OBJS)) + +BUILTIN_OBJS := $(filter-out $(OUTPUT)builtin-probe.o,$(BUILTIN_OBJS)) + +# Use minimal symbol handling +LIB_OBJS += $(OUTPUT)util/symbol-minimal.o + +else # NO_LIBELF +ifndef NO_DWARF + LIB_OBJS += $(OUTPUT)util/probe-finder.o + LIB_OBJS += $(OUTPUT)util/dwarf-aux.o +endif # NO_DWARF +endif # NO_LIBELF + +ifndef NO_LIBUNWIND + LIB_OBJS += $(OUTPUT)util/unwind.o +endif +LIB_OBJS += $(OUTPUT)tests/keep-tracking.o + +ifndef NO_LIBAUDIT + BUILTIN_OBJS += $(OUTPUT)builtin-trace.o +endif + +ifndef NO_SLANG + LIB_OBJS += $(OUTPUT)ui/browser.o + LIB_OBJS += $(OUTPUT)ui/browsers/annotate.o + LIB_OBJS += $(OUTPUT)ui/browsers/hists.o + LIB_OBJS += $(OUTPUT)ui/browsers/map.o + LIB_OBJS += $(OUTPUT)ui/browsers/scripts.o + LIB_OBJS += $(OUTPUT)ui/tui/setup.o + LIB_OBJS += $(OUTPUT)ui/tui/util.o + LIB_OBJS += $(OUTPUT)ui/tui/helpline.o + LIB_OBJS += $(OUTPUT)ui/tui/progress.o + LIB_H += ui/browser.h + LIB_H += ui/browsers/map.h + LIB_H += ui/keysyms.h + LIB_H += ui/libslang.h +endif + +ifndef NO_GTK2 + LIB_OBJS += $(OUTPUT)ui/gtk/browser.o + LIB_OBJS += $(OUTPUT)ui/gtk/hists.o + LIB_OBJS += $(OUTPUT)ui/gtk/setup.o + LIB_OBJS += $(OUTPUT)ui/gtk/util.o + LIB_OBJS += $(OUTPUT)ui/gtk/helpline.o + LIB_OBJS += $(OUTPUT)ui/gtk/progress.o + LIB_OBJS += $(OUTPUT)ui/gtk/annotate.o +endif + +ifndef NO_LIBPERL + LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-perl.o + LIB_OBJS += $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o +endif + +ifndef NO_LIBPYTHON + LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-python.o + LIB_OBJS += $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o +endif + +ifeq ($(NO_PERF_REGS),0) + ifeq ($(ARCH),x86) + LIB_H += arch/x86/include/perf_regs.h + endif +endif + +ifndef NO_LIBNUMA + BUILTIN_OBJS += $(OUTPUT)bench/numa.o +endif + +ifdef ASCIIDOC8 + export ASCIIDOC8 +endif + +LIBS = -Wl,--whole-archive $(PERFLIBS) -Wl,--no-whole-archive -Wl,--start-group $(EXTLIBS) -Wl,--end-group + +export INSTALL SHELL_PATH + +### Build rules + +SHELL = $(SHELL_PATH) + +all: shell_compatibility_test $(ALL_PROGRAMS) $(LANG_BINDINGS) $(OTHER_PROGRAMS) + +please_set_SHELL_PATH_to_a_more_modern_shell: + @$$(:) + +shell_compatibility_test: please_set_SHELL_PATH_to_a_more_modern_shell + +strip: $(PROGRAMS) $(OUTPUT)perf + $(STRIP) $(STRIP_OPTS) $(PROGRAMS) $(OUTPUT)perf + +$(OUTPUT)perf.o: perf.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -include $(OUTPUT)PERF-VERSION-FILE \ + '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ + $(CFLAGS) -c $(filter %.c,$^) -o $@ + +$(OUTPUT)perf: $(OUTPUT)perf.o $(BUILTIN_OBJS) $(PERFLIBS) + $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $(OUTPUT)perf.o \ + $(BUILTIN_OBJS) $(LIBS) -o $@ + +$(OUTPUT)builtin-help.o: builtin-help.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) \ + '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ + '-DPERF_MAN_PATH="$(mandir_SQ)"' \ + '-DPERF_INFO_PATH="$(infodir_SQ)"' $< + +$(OUTPUT)builtin-timechart.o: builtin-timechart.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) \ + '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ + '-DPERF_MAN_PATH="$(mandir_SQ)"' \ + '-DPERF_INFO_PATH="$(infodir_SQ)"' $< + +$(OUTPUT)common-cmds.h: util/generate-cmdlist.sh command-list.txt + +$(OUTPUT)common-cmds.h: $(wildcard Documentation/perf-*.txt) + $(QUIET_GEN). util/generate-cmdlist.sh > $@+ && mv $@+ $@ + +$(SCRIPTS) : % : %.sh + $(QUIET_GEN)$(INSTALL) '$@.sh' '$(OUTPUT)$@' + +# These can record PERF_VERSION +$(OUTPUT)perf.o perf.spec \ + $(SCRIPTS) \ + : $(OUTPUT)PERF-VERSION-FILE + +.SUFFIXES: +.SUFFIXES: .o .c .S .s + +# These two need to be here so that when O= is not used they take precedence +# over the general rule for .o + +$(OUTPUT)util/%-flex.o: $(OUTPUT)util/%-flex.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c -Iutil/ $(CFLAGS) -w $< + +$(OUTPUT)util/%-bison.o: $(OUTPUT)util/%-bison.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c -Iutil/ $(CFLAGS) -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -w $< + +$(OUTPUT)%.o: %.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $< +$(OUTPUT)%.i: %.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -E $(CFLAGS) $< +$(OUTPUT)%.s: %.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -S $(CFLAGS) $< +$(OUTPUT)%.o: %.S + $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $< +$(OUTPUT)%.s: %.S + $(QUIET_CC)$(CC) -o $@ -E $(CFLAGS) $< + +$(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) \ + '-DPERF_EXEC_PATH="$(perfexecdir_SQ)"' \ + '-DPREFIX="$(prefix_SQ)"' \ + $< + +$(OUTPUT)tests/attr.o: tests/attr.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) \ + '-DBINDIR="$(bindir_SQ)"' -DPYTHON='"$(PYTHON_WORD)"' \ + $< + +$(OUTPUT)tests/python-use.o: tests/python-use.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) \ + -DPYTHONPATH='"$(OUTPUT)python"' \ + -DPYTHON='"$(PYTHON_WORD)"' \ + $< + +$(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< + +$(OUTPUT)ui/browser.o: ui/browser.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DENABLE_SLFUTURE_CONST $< + +$(OUTPUT)ui/browsers/annotate.o: ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DENABLE_SLFUTURE_CONST $< + +$(OUTPUT)ui/browsers/hists.o: ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DENABLE_SLFUTURE_CONST $< + +$(OUTPUT)ui/browsers/map.o: ui/browsers/map.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DENABLE_SLFUTURE_CONST $< + +$(OUTPUT)ui/browsers/scripts.o: ui/browsers/scripts.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DENABLE_SLFUTURE_CONST $< + +$(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -Wno-unused-parameter -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< + +$(OUTPUT)util/parse-events.o: util/parse-events.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -Wno-redundant-decls $< + +$(OUTPUT)util/scripting-engines/trace-event-perl.o: util/scripting-engines/trace-event-perl.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow -Wno-undef -Wno-switch-default $< + +$(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o: scripts/perl/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs -Wno-undef -Wno-switch-default $< + +$(OUTPUT)util/scripting-engines/trace-event-python.o: util/scripting-engines/trace-event-python.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $< + +$(OUTPUT)scripts/python/Perf-Trace-Util/Context.o: scripts/python/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $< + +$(OUTPUT)perf-%: %.o $(PERFLIBS) + $(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $(LDFLAGS) $(filter %.o,$^) $(LIBS) + +$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) +$(patsubst perf-%,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) + +# we compile into subdirectories. if the target directory is not the source directory, they might not exists. So +# we depend the various files onto their directories. +DIRECTORY_DEPS = $(LIB_OBJS) $(BUILTIN_OBJS) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h +$(DIRECTORY_DEPS): | $(sort $(dir $(DIRECTORY_DEPS))) +# In the second step, we make a rule to actually create these directories +$(sort $(dir $(DIRECTORY_DEPS))): + $(QUIET_MKDIR)$(MKDIR) -p $@ 2>/dev/null + +$(LIB_FILE): $(LIB_OBJS) + $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS) + +# libtraceevent.a +TE_SOURCES = $(wildcard $(TRACE_EVENT_DIR)*.[ch]) + +$(LIBTRACEEVENT): $(TE_SOURCES) + $(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) libtraceevent.a + +$(LIBTRACEEVENT)-clean: + $(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) clean + +LIBLK_SOURCES = $(wildcard $(LK_PATH)*.[ch]) + +# if subdir is set, we've been called from above so target has been built +# already +$(LIBLK): $(LIBLK_SOURCES) +ifeq ($(subdir),) + $(QUIET_SUBDIR0)$(LK_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) liblk.a +endif + +$(LIBLK)-clean: +ifeq ($(subdir),) + $(QUIET_SUBDIR0)$(LK_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) clean +endif + +help: + @echo 'Perf make targets:' + @echo ' doc - make *all* documentation (see below)' + @echo ' man - make manpage documentation (access with man )' + @echo ' html - make html documentation' + @echo ' info - make GNU info documentation (access with info )' + @echo ' pdf - make pdf documentation' + @echo ' TAGS - use etags to make tag information for source browsing' + @echo ' tags - use ctags to make tag information for source browsing' + @echo ' cscope - use cscope to make interactive browsing database' + @echo '' + @echo 'Perf install targets:' + @echo ' NOTE: documentation build requires asciidoc, xmlto packages to be installed' + @echo ' HINT: use "make prefix= " to install to a particular' + @echo ' path like make prefix=/usr/local install install-doc' + @echo ' install - install compiled binaries' + @echo ' install-doc - install *all* documentation' + @echo ' install-man - install manpage documentation' + @echo ' install-html - install html documentation' + @echo ' install-info - install GNU info documentation' + @echo ' install-pdf - install pdf documentation' + @echo '' + @echo ' quick-install-doc - alias for quick-install-man' + @echo ' quick-install-man - install the documentation quickly' + @echo ' quick-install-html - install the html documentation quickly' + @echo '' + @echo 'Perf maintainer targets:' + @echo ' clean - clean all binary objects and build output' + + +DOC_TARGETS := doc man html info pdf + +INSTALL_DOC_TARGETS := $(patsubst %,install-%,$(DOC_TARGETS)) try-install-man +INSTALL_DOC_TARGETS += quick-install-doc quick-install-man quick-install-html + +# 'make doc' should call 'make -C Documentation all' +$(DOC_TARGETS): + $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) $(@:doc=all) + +TAGS: + $(RM) TAGS + $(FIND) . -name '*.[hcS]' -print | xargs etags -a + +tags: + $(RM) tags + $(FIND) . -name '*.[hcS]' -print | xargs ctags -a + +cscope: + $(RM) cscope* + $(FIND) . -name '*.[hcS]' -print | xargs cscope -b + +### Detect prefix changes +TRACK_CFLAGS = $(subst ','\'',$(CFLAGS)):\ + $(bindir_SQ):$(perfexecdir_SQ):$(template_dir_SQ):$(prefix_SQ) + +$(OUTPUT)PERF-CFLAGS: .FORCE-PERF-CFLAGS + @FLAGS='$(TRACK_CFLAGS)'; \ + if test x"$$FLAGS" != x"`cat $(OUTPUT)PERF-CFLAGS 2>/dev/null`" ; then \ + echo 1>&2 " * new build flags or prefix"; \ + echo "$$FLAGS" >$(OUTPUT)PERF-CFLAGS; \ + fi + +### Testing rules + +# GNU make supports exporting all variables by "export" without parameters. +# However, the environment gets quite big, and some programs have problems +# with that. + +check: $(OUTPUT)common-cmds.h + if sparse; \ + then \ + for i in *.c */*.c; \ + do \ + sparse $(CFLAGS) $(SPARSE_FLAGS) $$i || exit; \ + done; \ + else \ + exit 1; \ + fi + +### Installation rules + +install-bin: all + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' + $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)' + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' + $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' +ifndef NO_LIBPERL + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' + $(INSTALL) scripts/perl/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' + $(INSTALL) scripts/perl/*.pl -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl' + $(INSTALL) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' +endif +ifndef NO_LIBPYTHON + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace' + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' + $(INSTALL) scripts/python/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace' + $(INSTALL) scripts/python/*.py -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python' + $(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' +endif + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d' + $(INSTALL) bash_completion '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf' + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests' + $(INSTALL) tests/attr.py '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests' + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr' + $(INSTALL) tests/attr/* '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr' + +install: install-bin try-install-man + +install-python_ext: + $(PYTHON_WORD) util/setup.py --quiet install --root='/$(DESTDIR_SQ)' + +# 'make install-doc' should call 'make -C Documentation install' +$(INSTALL_DOC_TARGETS): + $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) $(@:-doc=) + +### Cleaning rules + +# +# This is here, not in config/Makefile, because config/Makefile does +# not get included for the clean target: +# +config-clean: + @$(MAKE) -C config/feature-checks clean + +clean: $(LIBTRACEEVENT)-clean $(LIBLK)-clean config-clean + $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS) + $(RM) $(ALL_PROGRAMS) perf + $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* + $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean + $(RM) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS + $(RM) $(OUTPUT)util/*-bison* + $(RM) $(OUTPUT)util/*-flex* + $(python-clean) + +# +# Trick: if ../../.git does not exist - we are building out of tree for example, +# then force version regeneration: +# +ifeq ($(wildcard ../../.git/HEAD),) + GIT-HEAD-PHONY = ../../.git/HEAD +else + GIT-HEAD-PHONY = +endif + +.PHONY: all install clean config-clean strip +.PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell +.PHONY: $(GIT-HEAD-PHONY) TAGS tags cscope .FORCE-PERF-CFLAGS + -- cgit v0.10.2 From 73a725f0008702600f7d987e262f963c0fa64bc6 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 2 Oct 2013 11:58:30 +0200 Subject: tools/perf/build: Standardize the various messages output by parallel make Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-mky0rtpwxi3ivxsvdjoOEmhr@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/Makefile b/tools/perf/Makefile index ce7874b..3b925ad 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -13,13 +13,30 @@ endif export JOBS -$(info $(shell printf '# [ perf build: Doing '\''make \033[33m-j'$(JOBS)'\033[m'\'' parallel build. ]\n')) +define print_msg + @printf ' BUILD: Doing '\''make \033[33m-j'$(JOBS)'\033[m'\'' parallel build\n' +endef + +define make + @$(MAKE) -f Makefile.perf --no-print-directory -j$(JOBS) $@ +endef # # Needed if no target specified: # all: - @$(MAKE) -f Makefile.perf --no-print-directory -j$(JOBS) $@ + $(print_msg) + $(make) + +# +# The clean target is not really parallel, don't print the jobs info: +# +clean: + $(make) +# +# All other targets get passed through: +# %: - @$(MAKE) -f Makefile.perf --no-print-directory -j$(JOBS) $@ + $(print_msg) + $(make) diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index fb6ec06..62d02cd 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -179,9 +179,9 @@ endif feature_print = $(eval $(feature_print_code)) define feature_print_code ifeq ($(feature-$(1)), 1) - MSG := $(shell printf '...%30s: [ \033[32mon\033[m ]' $(1)) + MSG = $(shell printf '...%30s: [ \033[32mon\033[m ]' $(1)) else - MSG := $(shell printf '...%30s: [ \033[31mOFF\033[m ]' $(1)) + MSG = $(shell printf '...%30s: [ \033[31mOFF\033[m ]' $(1)) endif $(info $(MSG)) endef -- cgit v0.10.2 From 1c47661a93fe6f729c80ed18a1f9ab1c7fb0cac2 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 2 Oct 2013 15:15:09 +0200 Subject: tools/perf/build: Split out feature checks: 'liberty', 'liberty-z', 'cplus-demangle' Note that these are rarely executed tests, so we call feature_check() explicitly and don't have them in CORE_FEATURE_CHECKS. Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-pvumlx6mbtfxffgrlwO2mRcx@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 62d02cd..89b2d47 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -472,23 +472,19 @@ else EXTLIBS += -liberty CFLAGS += -DHAVE_CPLUS_DEMANGLE_SUPPORT else - FLAGS_BFD=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) -DPACKAGE='perf' -lbfd ifeq ($(feature-libbfd), 1) EXTLIBS += -lbfd else - FLAGS_BFD_IBERTY=$(FLAGS_BFD) -liberty - has_bfd_iberty := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY),liberty) - ifeq ($(has_bfd_iberty),y) + $(feature_check,liberty) + ifeq ($(feature-liberty), 1) EXTLIBS += -lbfd -liberty else - FLAGS_BFD_IBERTY_Z=$(FLAGS_BFD_IBERTY) -lz - has_bfd_iberty_z := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY_Z),libz) - ifeq ($(has_bfd_iberty_z),y) + $(feature_check,liberty-z) + ifeq ($(feature-liberty-z), 1) EXTLIBS += -lbfd -liberty -lz else - FLAGS_CPLUS_DEMANGLE=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) -liberty - has_cplus_demangle := $(call try-cc,$(SOURCE_CPLUS_DEMANGLE),$(FLAGS_CPLUS_DEMANGLE),demangle) - ifeq ($(has_cplus_demangle),y) + $(feature_check,cplus-demangle) + ifeq ($(feature-cplus-demangle), 1) EXTLIBS += -liberty CFLAGS += -DHAVE_CPLUS_DEMANGLE_SUPPORT else diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index d4c55ac..e21bceb 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -11,6 +11,9 @@ FILES= \ test-hello \ test-libaudit \ test-libbfd \ + test-liberty \ + test-liberty-z \ + test-cplus-demangle \ test-libelf \ test-libelf-getphdrnum \ test-libelf-mmap \ @@ -122,6 +125,15 @@ test-libpython-version: test-libbfd: $(BUILD) -DPACKAGE='perf' -DPACKAGE=perf -lbfd -ldl +test-liberty: + $(CC) -o $(OUTPUT)$@ test-libbfd.c -DPACKAGE='perf' -DPACKAGE=perf -lbfd -ldl -liberty + +test-liberty-z: + $(CC) -o $(OUTPUT)$@ test-libbfd.c -DPACKAGE='perf' -DPACKAGE=perf -lbfd -ldl -liberty -lz + +test-cplus-demangle: + $(BUILD) -liberty + test-on-exit: $(BUILD) diff --git a/tools/perf/config/feature-checks/test-cplus-demangle.c b/tools/perf/config/feature-checks/test-cplus-demangle.c new file mode 100644 index 0000000..5202f50 --- /dev/null +++ b/tools/perf/config/feature-checks/test-cplus-demangle.c @@ -0,0 +1,10 @@ + +extern char *cplus_demangle(const char *, int); + +int main(void) +{ + cplus_demangle(0, 0); + + return 0; +} + -- cgit v0.10.2 From 0648f839ff754e2825fee2cb7080df5874316b3f Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 2 Oct 2013 15:30:35 +0200 Subject: tools/perf/build: Remove unused config/feature-tests.mak Also remove try-cc et al. These got obsoleted by the split-out feature checks in config/feature-checks/. Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-Y6ailbiranadqlrl8Dfivjbi@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 89b2d47..e80ffe8 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -51,7 +51,6 @@ LIB_INCLUDE := $(srctree)/tools/lib/ # include ARCH specific config -include $(src-perf)/arch/$(ARCH)/Makefile -include $(src-perf)/config/feature-tests.mak include $(src-perf)/config/utilities.mak ifeq ($(call get-executable,$(FLEX)),) @@ -274,7 +273,7 @@ else msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev); NO_DWARF := 1 endif # Dwarf support - endif # SOURCE_LIBELF + endif # libelf support endif # NO_LIBELF ifndef NO_LIBELF diff --git a/tools/perf/config/feature-tests.mak b/tools/perf/config/feature-tests.mak index f793057..139597f 100644 --- a/tools/perf/config/feature-tests.mak +++ b/tools/perf/config/feature-tests.mak @@ -1,246 +1,2 @@ -define SOURCE_HELLO -#include -int main(void) -{ - return puts(\"hi\"); -} -endef -ifndef NO_DWARF -define SOURCE_DWARF -#include -#include -#include -#ifndef _ELFUTILS_PREREQ -#error -#endif -int main(void) -{ - Dwarf *dbg = dwarf_begin(0, DWARF_C_READ); - return (long)dbg; -} -endef -endif - -define SOURCE_LIBELF -#include - -int main(void) -{ - Elf *elf = elf_begin(0, ELF_C_READ, 0); - return (long)elf; -} -endef - -define SOURCE_GLIBC -#include - -int main(void) -{ - const char *version = gnu_get_libc_version(); - return (long)version; -} -endef - -define SOURCE_BIONIC -#include - -int main(void) -{ - return __ANDROID_API__; -} -endef - -define SOURCE_ELF_MMAP -#include -int main(void) -{ - Elf *elf = elf_begin(0, ELF_C_READ_MMAP, 0); - return (long)elf; -} -endef - -define SOURCE_ELF_GETPHDRNUM -#include -int main(void) -{ - size_t dst; - return elf_getphdrnum(0, &dst); -} -endef - -ifndef NO_SLANG -define SOURCE_SLANG -#include - -int main(void) -{ - return SLsmg_init_smg(); -} -endef -endif - -ifndef NO_GTK2 -define SOURCE_GTK2 -#pragma GCC diagnostic ignored \"-Wstrict-prototypes\" -#include -#pragma GCC diagnostic error \"-Wstrict-prototypes\" - -int main(int argc, char *argv[]) -{ - gtk_init(&argc, &argv); - - return 0; -} -endef - -define SOURCE_GTK2_INFOBAR -#pragma GCC diagnostic ignored \"-Wstrict-prototypes\" -#include -#pragma GCC diagnostic error \"-Wstrict-prototypes\" - -int main(void) -{ - gtk_info_bar_new(); - - return 0; -} -endef -endif - -ifndef NO_LIBPERL -define SOURCE_PERL_EMBED -#include -#include - -int main(void) -{ -perl_alloc(); -return 0; -} -endef -endif - -ifndef NO_LIBPYTHON -define SOURCE_PYTHON_VERSION -#include -#if PY_VERSION_HEX >= 0x03000000 - #error -#endif -int main(void) -{ - return 0; -} -endef -define SOURCE_PYTHON_EMBED -#include -int main(void) -{ - Py_Initialize(); - return 0; -} -endef -endif - -define SOURCE_BFD -#include - -int main(void) -{ - bfd_demangle(0, 0, 0); - return 0; -} -endef - -define SOURCE_CPLUS_DEMANGLE -extern char *cplus_demangle(const char *, int); - -int main(void) -{ - cplus_demangle(0, 0); - return 0; -} -endef - -define SOURCE_STRLCPY -#include -extern size_t strlcpy(char *dest, const char *src, size_t size); - -int main(void) -{ - strlcpy(NULL, NULL, 0); - return 0; -} -endef - -ifndef NO_LIBUNWIND -define SOURCE_LIBUNWIND -#include -#include - -extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, - unw_word_t ip, - unw_dyn_info_t *di, - unw_proc_info_t *pi, - int need_unwind_info, void *arg); - - -#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) - -int main(void) -{ - unw_addr_space_t addr_space; - addr_space = unw_create_addr_space(NULL, 0); - unw_init_remote(NULL, addr_space, NULL); - dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL); - return 0; -} -endef -endif - -ifndef NO_BACKTRACE -define SOURCE_BACKTRACE -#include -#include - -int main(void) -{ - backtrace(NULL, 0); - backtrace_symbols(NULL, 0); - return 0; -} -endef -endif - -ifndef NO_LIBAUDIT -define SOURCE_LIBAUDIT -#include - -int main(void) -{ - printf(\"error message: %s\", audit_errno_to_name(0)); - return audit_open(); -} -endef -endif - -define SOURCE_ON_EXIT -#include - -int main(void) -{ - return on_exit(NULL, NULL); -} -endef - -define SOURCE_LIBNUMA -#include -#include - -int main(void) -{ - numa_available(); - return 0; -} -endef diff --git a/tools/perf/config/utilities.mak b/tools/perf/config/utilities.mak index 94d2d4f..4d985e0 100644 --- a/tools/perf/config/utilities.mak +++ b/tools/perf/config/utilities.mak @@ -178,17 +178,3 @@ endef _ge_attempt = $(if $(get-executable),$(get-executable),$(_gea_warn)$(call _gea_err,$(2))) _gea_warn = $(warning The path '$(1)' is not executable.) _gea_err = $(if $(1),$(error Please set '$(1)' appropriately)) - -# try-cc -# Usage: option = $(call try-cc, source-to-build, cc-options, msg) -ifneq ($(V),1) -TRY_CC_OUTPUT= > /dev/null 2>&1 -endif -TRY_CC_MSG=echo " CHK $(3)" 1>&2; - -try-cc = $(shell sh -c \ - 'TMP="$(OUTPUT)$(TMPOUT).$$$$"; \ - $(TRY_CC_MSG) \ - echo "$(1)" | \ - $(CC) -x c - $(2) -o "$$TMP" $(TRY_CC_OUTPUT) && echo y; \ - rm -f "$$TMP"') -- cgit v0.10.2 From 1e3f30fae797660a014ac159d93fff9952ec1bf0 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 2 Oct 2013 15:48:49 +0200 Subject: tools/perf/build: Clean up various testcases Prepare to include them into test-all.c directly, by making sure that they build cleanly and without warnings. Also make sure they make a certain amount of sense and don't crash when executed. Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-Mn9gsdutzopoowk3xurqpsxE@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/feature-checks/test-backtrace.c b/tools/perf/config/feature-checks/test-backtrace.c index 5b79468..5b33bcf 100644 --- a/tools/perf/config/feature-checks/test-backtrace.c +++ b/tools/perf/config/feature-checks/test-backtrace.c @@ -3,8 +3,12 @@ int main(void) { - backtrace(NULL, 0); - backtrace_symbols(NULL, 0); + void *backtrace_fns[10]; + size_t entries; + + entries = backtrace(backtrace_fns, 10); + backtrace_symbols_fd(backtrace_fns, entries, 1); return 0; } + diff --git a/tools/perf/config/feature-checks/test-cplus-demangle.c b/tools/perf/config/feature-checks/test-cplus-demangle.c index 5202f50..ab29f80 100644 --- a/tools/perf/config/feature-checks/test-cplus-demangle.c +++ b/tools/perf/config/feature-checks/test-cplus-demangle.c @@ -1,9 +1,14 @@ - +extern int printf(const char *format, ...); extern char *cplus_demangle(const char *, int); int main(void) { - cplus_demangle(0, 0); + char symbol[4096] = "FieldName__9ClassNameFd"; + char *tmp; + + tmp = cplus_demangle(symbol, 0); + + printf("demangled symbol: {%s}\n", tmp); return 0; } diff --git a/tools/perf/config/feature-checks/test-gtk2-infobar.c b/tools/perf/config/feature-checks/test-gtk2-infobar.c index eebcfbc..397b464 100644 --- a/tools/perf/config/feature-checks/test-gtk2-infobar.c +++ b/tools/perf/config/feature-checks/test-gtk2-infobar.c @@ -2,8 +2,9 @@ #include #pragma GCC diagnostic error "-Wstrict-prototypes" -int main(void) +int main(int argc, char *argv[]) { + gtk_init(&argc, &argv); gtk_info_bar_new(); return 0; diff --git a/tools/perf/config/feature-checks/test-gtk2.c b/tools/perf/config/feature-checks/test-gtk2.c index 1ac6d8a..6bd80e5 100644 --- a/tools/perf/config/feature-checks/test-gtk2.c +++ b/tools/perf/config/feature-checks/test-gtk2.c @@ -4,7 +4,7 @@ int main(int argc, char *argv[]) { - gtk_init(&argc, &argv); + gtk_init(&argc, &argv); return 0; } diff --git a/tools/perf/config/feature-checks/test-libaudit.c b/tools/perf/config/feature-checks/test-libaudit.c index 854a65d..f7e791e 100644 --- a/tools/perf/config/feature-checks/test-libaudit.c +++ b/tools/perf/config/feature-checks/test-libaudit.c @@ -1,5 +1,7 @@ #include +extern int printf(const char *format, ...); + int main(void) { printf("error message: %s\n", audit_errno_to_name(0)); diff --git a/tools/perf/config/feature-checks/test-libbfd.c b/tools/perf/config/feature-checks/test-libbfd.c index d03339c..1886c78 100644 --- a/tools/perf/config/feature-checks/test-libbfd.c +++ b/tools/perf/config/feature-checks/test-libbfd.c @@ -1,7 +1,16 @@ #include +extern int printf(const char *format, ...); + int main(void) { - bfd_demangle(0, 0, 0); + char symbol[4096] = "FieldName__9ClassNameFd"; + char *tmp; + + tmp = bfd_demangle(0, symbol, 0); + + printf("demangled symbol: {%s}\n", tmp); + return 0; } + diff --git a/tools/perf/config/feature-checks/test-libunwind.c b/tools/perf/config/feature-checks/test-libunwind.c index 5622746..43b9369 100644 --- a/tools/perf/config/feature-checks/test-libunwind.c +++ b/tools/perf/config/feature-checks/test-libunwind.c @@ -10,11 +10,18 @@ extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, #define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +static unw_accessors_t accessors; + int main(void) { unw_addr_space_t addr_space; - addr_space = unw_create_addr_space(NULL, 0); + + addr_space = unw_create_addr_space(&accessors, 0); + if (addr_space) + return 0; + unw_init_remote(NULL, addr_space, NULL); dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL); + return 0; } diff --git a/tools/perf/config/feature-checks/test-on-exit.c b/tools/perf/config/feature-checks/test-on-exit.c index 473f1de..8f64ed3 100644 --- a/tools/perf/config/feature-checks/test-on-exit.c +++ b/tools/perf/config/feature-checks/test-on-exit.c @@ -1,6 +1,15 @@ #include +static void exit_fn(int status, void *__data) +{ + printf("exit status: %d, data: %d\n", status, *(int *)__data); +} + +static int data = 123; + int main(void) { - return on_exit(NULL, NULL); + on_exit(exit_fn, &data); + + return 321; } -- cgit v0.10.2 From 3ca576a481f8c12fe606fb75232d52637c666ed0 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 2 Oct 2013 16:21:37 +0200 Subject: tools/perf/build: Collapse the test-all.c testcase Simplify test-all.c by including it all the testcases via #include. Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-pcZlwqq5ou7Ebvkekvhtzfbm@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/feature-checks/test-all.c b/tools/perf/config/feature-checks/test-all.c index 9f7c4b1..50d4318 100644 --- a/tools/perf/config/feature-checks/test-all.c +++ b/tools/perf/config/feature-checks/test-all.c @@ -1,196 +1,106 @@ - -#pragma GCC diagnostic ignored "-Wstrict-prototypes" - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#pragma GCC diagnostic error "-Wstrict-prototypes" - -int main1(void) -{ - return puts("hi"); -} - -int main2(void) -{ - return puts("hi"); -} - -int main3(void) -{ - return puts("hi"); -} - -int main4(void) -{ - Elf *elf = elf_begin(0, ELF_C_READ, 0); - return (long)elf; -} -# -int main5(void) -{ - Elf *elf = elf_begin(0, ELF_C_READ_MMAP, 0); - return (long)elf; -} - -int main6(void) -{ - const char *version = gnu_get_libc_version(); - return (long)version; -} - -int main7(void) -{ - Dwarf *dbg = dwarf_begin(0, DWARF_C_READ); - return (long)dbg; -} - -int main8(void) -{ - size_t dst; - return elf_getphdrnum(0, &dst); -} - -extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, - unw_word_t ip, - unw_dyn_info_t *di, - unw_proc_info_t *pi, - int need_unwind_info, void *arg); - - -#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) - -int main9(void) -{ - unw_addr_space_t addr_space; - addr_space = unw_create_addr_space(NULL, 0); - unw_init_remote(NULL, addr_space, NULL); - dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL); - return 0; -} - -int main10(void) -{ - printf("error message: %s\n", audit_errno_to_name(0)); - return audit_open(); -} - -int main11(void) -{ - return SLsmg_init_smg(); -} - -int main12(int argc, char *argv[]) -{ - gtk_init(&argc, &argv); - - return 0; -} - -int main13(void) -{ - gtk_info_bar_new(); - - return 0; -} - -int main14(void) -{ - perl_alloc(); - - return 0; -} - -int main15(void) -{ - Py_Initialize(); - return 0; -} - -#if PY_VERSION_HEX >= 0x03000000 - #error -#endif - -int main16(void) -{ - return 0; -} - -int main17(void) -{ - bfd_demangle(0, 0, 0); - return 0; -} - -void exit_function(int x, void *y) -{ -} - -int main18(void) -{ - return on_exit(exit_function, NULL); -} - -int main19(void) -{ - void *backtrace_fns[1]; - size_t entries; - - entries = backtrace(backtrace_fns, 1); - backtrace_symbols(backtrace_fns, entries); - - return 0; -} - -int main20(void) -{ - numa_available(); - return 0; -} +/* + * test-all.c: Try to build all the main testcases at once. + * + * A well-configured system will have all the prereqs installed, so we can speed + * up auto-detection on such systems. + */ + +/* + * Quirk: Python and Perl headers cannot be in arbitrary places, so keep + * these 3 testcases at the top: + */ +#define main main_test_libpython +# include "test-libpython.c" +#undef main + +#define main main_test_libpython_version +# include "test-libpython-version.c" +#undef main + +#define main main_test_libperl +# include "test-libperl.c" +#undef main + +#define main main_test_hello +# include "test-hello.c" +#undef main + +#define main main_test_libelf +# include "test-libelf.c" +#undef main + +#define main main_test_libelf_mmap +# include "test-libelf-mmap.c" +#undef main + +#define main main_test_glibc +# include "test-glibc.c" +#undef main + +#define main main_test_dwarf +# include "test-dwarf.c" +#undef main + +#define main main_test_libelf_getphdrnum +# include "test-libelf-getphdrnum.c" +#undef main + +#define main main_test_libunwind +# include "test-libunwind.c" +#undef main + +#define main main_test_libaudit +# include "test-libaudit.c" +#undef main + +#define main main_test_libslang +# include "test-libslang.c" +#undef main + +#define main main_test_gtk2 +# include "test-gtk2.c" +#undef main + +#define main main_test_gtk2_infobar +# include "test-gtk2-infobar.c" +#undef main + +#define main main_test_libbfd +# include "test-libbfd.c" +#undef main + +#define main main_test_on_exit +# include "test-on-exit.c" +#undef main + +#define main main_test_backtrace +# include "test-backtrace.c" +#undef main + +#define main main_test_libnuma +# include "test-libnuma.c" +#undef main int main(int argc, char *argv[]) { - main1(); - main2(); - main3(); - main4(); - main5(); - main6(); - main7(); - main8(); - main9(); - main10(); - main11(); - main12(argc, argv); - main13(); - main14(); - main15(); - main16(); - main17(); - main18(); - main19(); - main20(); + main_test_libpython(); + main_test_libpython_version(); + main_test_libperl(); + main_test_hello(); + main_test_libelf(); + main_test_libelf_mmap(); + main_test_glibc(); + main_test_dwarf(); + main_test_libelf_getphdrnum(); + main_test_libunwind(); + main_test_libaudit(); + main_test_libslang(); + main_test_gtk2(argc, argv); + main_test_gtk2_infobar(argc, argv); + main_test_libbfd(); + main_test_on_exit(); + main_test_backtrace(); + main_test_libnuma(); return 0; } -- cgit v0.10.2 From b016a0dd08999218a05a4b176bc08a9ff68ccc5e Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Thu, 3 Oct 2013 14:32:10 +0200 Subject: tools/perf/build: Pass through all targets to Makefile.perf Jiri reported that 'make .o' stopped working: > [jolsa@krava perf]$ make -f Makefile perf.o > cc -c -o perf.o perf.c > In file included from builtin.h:4:0, > from perf.c:9: > util/util.h:74:24: fatal error: lk/debugfs.h: No such file or directory > compilation terminated. > make: *** [perf.o] Error 1 This is due to GNU make having built-in rules for popular targets such as *.o. Clear them out so that all targets as passed through to Makefile.perf. Reported-by: Jiri Olsa Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Link: http://lkml.kernel.org/n/tip-5wkuvmlaaxtfgepKcvRij8sh@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 3b925ad..6f6f13a 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -1,3 +1,10 @@ + +# +# Clear out the built-in rules GNU make defines by default (such as .o targets), +# so that we pass through all targets to Makefile.perf: +# +.SUFFIXES: + # # Do a parallel build with multiple jobs, based on the number of CPUs online # in this system: 'make -j8' on a 8-CPU system, etc. -- cgit v0.10.2 From 1c2d1d8cf4dbef33b8c6af6888bed2f4e659220e Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Thu, 3 Oct 2013 15:05:56 +0200 Subject: tools/perf/build: Make sure autodep feature binaries honor the O= setting Arnaldo noticed that the feature-check binaries are generated in the config/check-features/ directory even if O= is specified. Implement $(OUTPUT) logic for config/check-features/Makefile. Reported-by: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-NLwlnv5prsubuey0vfocebym@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index e80ffe8..3d656e3 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -88,9 +88,14 @@ CFLAGS += -std=gnu99 EXTLIBS = -lelf -lpthread -lrt -lm -ldl +ifneq ($(OUTPUT),) + OUTPUT_FEATURES = $(OUTPUT)config/feature-checks/ + $(shell mkdir -p $(OUTPUT_FEATURES)) +endif + feature_check = $(eval $(feature_check_code)) define feature_check_code - feature-$(1) := $(shell $(MAKE) -C config/feature-checks test-$1 >/dev/null 2>/dev/null && echo 1 || echo 0) + feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) -C config/feature-checks test-$1 >/dev/null 2>/dev/null && echo 1 || echo 0) endef feature_set = $(eval $(feature_set_code)) @@ -138,7 +143,7 @@ CORE_FEATURE_TESTS = \ # to skip the print-out of the long features list if the file # existed before and after it was built: # -ifeq ($(wildcard config/feature-checks/test-all),) +ifeq ($(wildcard $(OUTPUT)config/feature-checks/test-all),) test-all-failed := 1 else test-all-failed := 0 @@ -168,7 +173,7 @@ ifeq ($(feature-all), 1) # $(foreach feat,$(CORE_FEATURE_TESTS),$(call feature_set,$(feat))) else - $(shell $(MAKE) -i -j -C config/feature-checks $(CORE_FEATURE_TESTS) >/dev/null 2>&1) + $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) -i -j -C config/feature-checks $(CORE_FEATURE_TESTS) >/dev/null 2>&1) $(foreach feat,$(CORE_FEATURE_TESTS),$(call feature_check,$(feat))) endif -- cgit v0.10.2 From 684f434cc05a122938b75e055d7d799f1dd58d55 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Fri, 4 Oct 2013 11:11:32 +0200 Subject: tools/perf/build: Exclude MAKEFLAGS from nested invocation In case the user specifies MAKEFLAGS as an environment variable, or uses 'make -jN' explicitly, the options can conflict and result in: BUILD: Doing 'make -j8' parallel build make[1]: warning: -jN forced in submake: disabling jobserver mode. GEN common-cmds.h make[1]: *** write jobserver: Bad file descriptor. Stop. Make sure we invoke the main makefile in a pristine state. Users who want to do something non-standard can use the: make -f Makefile.perf method to invoke the makefile. Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-uen6hzTvkqqngqwjma9yoEgw@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 6f6f13a..74f52d8 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -1,3 +1,10 @@ +# +# This is a simple wrapper Makefile that calls the main Makefile.perf +# with a -j option to do parallel builds +# +# If you want to invoke the perf build in some non-standard way then +# you can use the 'make -f Makefile.perf' method to invoke it. +# # # Clear out the built-in rules GNU make defines by default (such as .o targets), @@ -6,6 +13,11 @@ .SUFFIXES: # +# We don't want to pass along options like -j: +# +unexport MAKEFLAGS + +# # Do a parallel build with multiple jobs, based on the number of CPUs online # in this system: 'make -j8' on a 8-CPU system, etc. # @@ -18,14 +30,12 @@ ifeq ($(JOBS),) endif endif -export JOBS - define print_msg @printf ' BUILD: Doing '\''make \033[33m-j'$(JOBS)'\033[m'\'' parallel build\n' endef define make - @$(MAKE) -f Makefile.perf --no-print-directory -j$(JOBS) $@ + @$(MAKE) -f Makefile.perf --no-print-directory -j$(JOBS) O=$(O) $@ endef # -- cgit v0.10.2 From b102420b500da97e0fc18d94f0600bddeced1b99 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Fri, 4 Oct 2013 12:08:05 +0200 Subject: tools/perf/build: Fix non-canonical directory names in O= This was a long-standing bug, relative pathnames like O=dir did not fully work in the build system: $ make O=localdir clean SUBDIR Documentation ../../scripts/Makefile.include:3: *** O=localdir does not exist. Stop. make[1]: *** [clean] Error 2 make: *** [clean] Error 2 Fix this by canonizing the directory before passing it to Makefile.perf. Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-hchMp1hozn9tqgswWcooxcru@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 74f52d8..9580ebe 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -30,12 +30,19 @@ ifeq ($(JOBS),) endif endif +# +# Only pass canonical directory names as the output directory: +# +ifneq ($(O),) + FULL_O := $(shell readlink -f $(O)) +endif + define print_msg @printf ' BUILD: Doing '\''make \033[33m-j'$(JOBS)'\033[m'\'' parallel build\n' endef define make - @$(MAKE) -f Makefile.perf --no-print-directory -j$(JOBS) O=$(O) $@ + @$(MAKE) -f Makefile.perf --no-print-directory -j$(JOBS) O=$(FULL_O) $@ endef # -- cgit v0.10.2 From 1f7c645ab4b8326fef5afcd842795e071ecce9df Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Fri, 4 Oct 2013 12:14:59 +0200 Subject: tools/perf/build: Fix O=/some/dir perf.o type of targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If someone specifies a single target, mixed with O=, the following way: hubble:~/tip/tools/perf> make O=/tmp/perf util/stat.o BUILD: Doing 'make -j8' parallel build gcc -Wbad-function-cast -Wdeclaration-after-statement -Wformat-security -Wformat-y2k [...] The build might even fail, if a target depends on other targets: hubble:~/tip/tools/perf> make O=/tmp/perf perf.o ... perf.c: In function ‘handle_options’: perf.c:155:21: error: ‘PERF_HTML_PATH’ undeclared (first use in this function) The correct way to invoke such targets is: hubble:~/tip/tools/perf> make O=/tmp/perf /tmp/perf/perf.o BUILD: Doing 'make -j8' parallel build GEN /tmp/perf/common-cmds.h CC /tmp/perf/perf.o But that's unnecessary typing and it's also easy to mistakenly build into the source directory. To fix this remove the generic suffix rules and add redirection to $(OUTPUT) for the most popular .o targets. Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-mk0oiukmhgSbrll6chrPkkqr@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 178a1c8..a24f6c2 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -576,7 +576,21 @@ $(OUTPUT)perf.o perf.spec \ : $(OUTPUT)PERF-VERSION-FILE .SUFFIXES: -.SUFFIXES: .o .c .S .s + +# +# If a target does not match any of the later rules then prefix it by $(OUTPUT) +# This makes targets like 'make O=/tmp/perf perf.o' work in a natural way. +# +ifneq ($(OUTPUT),) +%.o: $(OUTPUT)%.o + @echo " # Redirected target $@ => $(OUTPUT)$@" +util/%.o: $(OUTPUT)util/%.o + @echo " # Redirected target $@ => $(OUTPUT)util/$@" +bench/%.o: $(OUTPUT)bench/%.o + @echo " # Redirected target $@ => $(OUTPUT)bench/$@" +tests/%.o: $(OUTPUT)tests/%.o + @echo " # Redirected target $@ => $(OUTPUT)tests/$@" +endif # These two need to be here so that when O= is not used they take precedence # over the general rule for .o -- cgit v0.10.2 From 20c99e82173bed9e4e0013ec45c0b2b3b80b65d5 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 7 Oct 2013 13:27:23 +0200 Subject: tools/perf/build: Harmonize the style of the feature testcases The various testcases used different styles, which was not really visible as long as they hid in feature-tests.mak. Now that they are out in the open make them prettier. ( Also delete the leftover, empty feature-tests.mak file. ) Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-drDWk8xltndjdsespzjbhu6w@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/feature-checks/test-backtrace.c b/tools/perf/config/feature-checks/test-backtrace.c index 5b33bcf..7124aa1 100644 --- a/tools/perf/config/feature-checks/test-backtrace.c +++ b/tools/perf/config/feature-checks/test-backtrace.c @@ -11,4 +11,3 @@ int main(void) return 0; } - diff --git a/tools/perf/config/feature-checks/test-cplus-demangle.c b/tools/perf/config/feature-checks/test-cplus-demangle.c index ab29f80..610c686 100644 --- a/tools/perf/config/feature-checks/test-cplus-demangle.c +++ b/tools/perf/config/feature-checks/test-cplus-demangle.c @@ -12,4 +12,3 @@ int main(void) return 0; } - diff --git a/tools/perf/config/feature-checks/test-dwarf.c b/tools/perf/config/feature-checks/test-dwarf.c index 783dfcd..3fc1801 100644 --- a/tools/perf/config/feature-checks/test-dwarf.c +++ b/tools/perf/config/feature-checks/test-dwarf.c @@ -5,5 +5,6 @@ int main(void) { Dwarf *dbg = dwarf_begin(0, DWARF_C_READ); + return (long)dbg; } diff --git a/tools/perf/config/feature-checks/test-glibc.c b/tools/perf/config/feature-checks/test-glibc.c index 13c66a5..b082034 100644 --- a/tools/perf/config/feature-checks/test-glibc.c +++ b/tools/perf/config/feature-checks/test-glibc.c @@ -3,6 +3,6 @@ int main(void) { const char *version = gnu_get_libc_version(); + return (long)version; } - diff --git a/tools/perf/config/feature-checks/test-libaudit.c b/tools/perf/config/feature-checks/test-libaudit.c index f7e791e..afc019f 100644 --- a/tools/perf/config/feature-checks/test-libaudit.c +++ b/tools/perf/config/feature-checks/test-libaudit.c @@ -5,5 +5,6 @@ extern int printf(const char *format, ...); int main(void) { printf("error message: %s\n", audit_errno_to_name(0)); + return audit_open(); } diff --git a/tools/perf/config/feature-checks/test-libbfd.c b/tools/perf/config/feature-checks/test-libbfd.c index 1886c78..2405990 100644 --- a/tools/perf/config/feature-checks/test-libbfd.c +++ b/tools/perf/config/feature-checks/test-libbfd.c @@ -13,4 +13,3 @@ int main(void) return 0; } - diff --git a/tools/perf/config/feature-checks/test-libelf-getphdrnum.c b/tools/perf/config/feature-checks/test-libelf-getphdrnum.c index 58eca53..d710459 100644 --- a/tools/perf/config/feature-checks/test-libelf-getphdrnum.c +++ b/tools/perf/config/feature-checks/test-libelf-getphdrnum.c @@ -1,7 +1,8 @@ #include -# + int main(void) { size_t dst; + return elf_getphdrnum(0, &dst); } diff --git a/tools/perf/config/feature-checks/test-libelf-mmap.c b/tools/perf/config/feature-checks/test-libelf-mmap.c index 1c64815..564427d 100644 --- a/tools/perf/config/feature-checks/test-libelf-mmap.c +++ b/tools/perf/config/feature-checks/test-libelf-mmap.c @@ -1,7 +1,8 @@ #include -# + int main(void) { Elf *elf = elf_begin(0, ELF_C_READ_MMAP, 0); + return (long)elf; } diff --git a/tools/perf/config/feature-checks/test-libelf.c b/tools/perf/config/feature-checks/test-libelf.c index 1a08f97..08db322 100644 --- a/tools/perf/config/feature-checks/test-libelf.c +++ b/tools/perf/config/feature-checks/test-libelf.c @@ -3,5 +3,6 @@ int main(void) { Elf *elf = elf_begin(0, ELF_C_READ, 0); + return (long)elf; } diff --git a/tools/perf/config/feature-checks/test-libnuma.c b/tools/perf/config/feature-checks/test-libnuma.c index 70510a9..4763d9c 100644 --- a/tools/perf/config/feature-checks/test-libnuma.c +++ b/tools/perf/config/feature-checks/test-libnuma.c @@ -4,5 +4,6 @@ int main(void) { numa_available(); + return 0; } diff --git a/tools/perf/config/feature-checks/test-libpython.c b/tools/perf/config/feature-checks/test-libpython.c index 7226797..b24b28a 100644 --- a/tools/perf/config/feature-checks/test-libpython.c +++ b/tools/perf/config/feature-checks/test-libpython.c @@ -1,7 +1,8 @@ #include -# + int main(void) { Py_Initialize(); + return 0; } diff --git a/tools/perf/config/feature-tests.mak b/tools/perf/config/feature-tests.mak deleted file mode 100644 index 139597f..0000000 --- a/tools/perf/config/feature-tests.mak +++ /dev/null @@ -1,2 +0,0 @@ - - -- cgit v0.10.2 From aa4acf6cf102f639a2023b389ca8be8b8d9cad52 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 7 Oct 2013 17:51:29 +0200 Subject: tools/perf/build: Pass through LDFLAGS to feature tests David Ahern reported that when passing in LDFLAGS=-static then the feature checks still succeed - causing build failures down the line because the static libraries are missing. Solve this by passing through LDFLAGS to the feature-check Makefile. Reported-by: David Ahern Tested-by: David Ahern Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: Jiri Olsa Link: http://lkml.kernel.org/r/20131007155129.GA1066@gmail.com Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 3d656e3..78f3b3e 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -95,7 +95,7 @@ endif feature_check = $(eval $(feature_check_code)) define feature_check_code - feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) -C config/feature-checks test-$1 >/dev/null 2>/dev/null && echo 1 || echo 0) + feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) LDFLAGS=$(LDFLAGS) -C config/feature-checks test-$1 >/dev/null 2>/dev/null && echo 1 || echo 0) endef feature_set = $(eval $(feature_set_code)) @@ -173,7 +173,7 @@ ifeq ($(feature-all), 1) # $(foreach feat,$(CORE_FEATURE_TESTS),$(call feature_set,$(feat))) else - $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) -i -j -C config/feature-checks $(CORE_FEATURE_TESTS) >/dev/null 2>&1) + $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) LDFLAGS=$(LDFLAGS) -i -j -C config/feature-checks $(CORE_FEATURE_TESTS) >/dev/null 2>&1) $(foreach feat,$(CORE_FEATURE_TESTS),$(call feature_check,$(feat))) endif diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index e21bceb..8ecac19 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -32,7 +32,7 @@ CC := $(CC) -MD all: $(FILES) -BUILD = $(CC) -o $(OUTPUT)$@ $@.c +BUILD = $(CC) $(LDFLAGS) -o $(OUTPUT)$@ $@.c ############################### -- cgit v0.10.2 From 165108a92fc554d51e73b143b69b77e7c278da78 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 8 Oct 2013 17:51:10 +0200 Subject: tools/perf/build: Clean up feature_print_code() Remove DUMMY by making sure 'feature_print' is evaluated and thus all messages are printed. Signed-off-by: Jiri Olsa Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Namhyung Kim Cc: David Ahern Link: http://lkml.kernel.org/r/20131008155110.GA15558@krava.redhat.com Signed-off-by: Ingo Molnar diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 78f3b3e..f5d661f 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -180,21 +180,21 @@ endif # # Print the result of the feature test: # -feature_print = $(eval $(feature_print_code)) +feature_print = $(eval $(feature_print_code)) $(info $(MSG)) + define feature_print_code ifeq ($(feature-$(1)), 1) MSG = $(shell printf '...%30s: [ \033[32mon\033[m ]' $(1)) else MSG = $(shell printf '...%30s: [ \033[31mOFF\033[m ]' $(1)) endif - $(info $(MSG)) endef # # Only print out our features if we rebuilt the testcases or if a test failed: # ifeq ($(test-all-failed), 1) - $(foreach feat,$(CORE_FEATURE_TESTS) DUMMY,$(call feature_print,$(feat))) + $(foreach feat,$(CORE_FEATURE_TESTS),$(call feature_print,$(feat))) $(info ) endif -- cgit v0.10.2 From 10fc05d0e551146ad6feb0ab8902d28a2d3c5624 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:28:40 +0100 Subject: mm: numa: Document automatic NUMA balancing sysctls Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-3-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index 9d4c1d1..1428c66 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -355,6 +355,72 @@ utilize. ============================================================== +numa_balancing + +Enables/disables automatic page fault based NUMA memory +balancing. Memory is moved automatically to nodes +that access it often. + +Enables/disables automatic NUMA memory balancing. On NUMA machines, there +is a performance penalty if remote memory is accessed by a CPU. When this +feature is enabled the kernel samples what task thread is accessing memory +by periodically unmapping pages and later trapping a page fault. At the +time of the page fault, it is determined if the data being accessed should +be migrated to a local memory node. + +The unmapping of pages and trapping faults incur additional overhead that +ideally is offset by improved memory locality but there is no universal +guarantee. If the target workload is already bound to NUMA nodes then this +feature should be disabled. Otherwise, if the system overhead from the +feature is too high then the rate the kernel samples for NUMA hinting +faults may be controlled by the numa_balancing_scan_period_min_ms, +numa_balancing_scan_delay_ms, numa_balancing_scan_period_reset, +numa_balancing_scan_period_max_ms and numa_balancing_scan_size_mb sysctls. + +============================================================== + +numa_balancing_scan_period_min_ms, numa_balancing_scan_delay_ms, +numa_balancing_scan_period_max_ms, numa_balancing_scan_period_reset, +numa_balancing_scan_size_mb + +Automatic NUMA balancing scans tasks address space and unmaps pages to +detect if pages are properly placed or if the data should be migrated to a +memory node local to where the task is running. Every "scan delay" the task +scans the next "scan size" number of pages in its address space. When the +end of the address space is reached the scanner restarts from the beginning. + +In combination, the "scan delay" and "scan size" determine the scan rate. +When "scan delay" decreases, the scan rate increases. The scan delay and +hence the scan rate of every task is adaptive and depends on historical +behaviour. If pages are properly placed then the scan delay increases, +otherwise the scan delay decreases. The "scan size" is not adaptive but +the higher the "scan size", the higher the scan rate. + +Higher scan rates incur higher system overhead as page faults must be +trapped and potentially data must be migrated. However, the higher the scan +rate, the more quickly a tasks memory is migrated to a local node if the +workload pattern changes and minimises performance impact due to remote +memory accesses. These sysctls control the thresholds for scan delays and +the number of pages scanned. + +numa_balancing_scan_period_min_ms is the minimum delay in milliseconds +between scans. It effectively controls the maximum scanning rate for +each task. + +numa_balancing_scan_delay_ms is the starting "scan delay" used for a task +when it initially forks. + +numa_balancing_scan_period_max_ms is the maximum delay between scans. It +effectively controls the minimum scanning rate for each task. + +numa_balancing_scan_size_mb is how many megabytes worth of pages are +scanned for a given scan. + +numa_balancing_scan_period_reset is a blunt instrument that controls how +often a tasks scan delay is reset to detect sudden changes in task behaviour. + +============================================================== + osrelease, ostype & version: # cat osrelease -- cgit v0.10.2 From c69307d533d7aa7cc8894dbbb8a274599f8630d7 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 7 Oct 2013 11:28:41 +0100 Subject: sched/numa: Fix comments Fix a 80 column violation and a PTE vs PMD reference. Signed-off-by: Peter Zijlstra Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Link: http://lkml.kernel.org/r/1381141781-10992-4-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 2b89cd2..817cd7b 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -988,10 +988,10 @@ void task_numa_work(struct callback_head *work) out: /* - * It is possible to reach the end of the VMA list but the last few VMAs are - * not guaranteed to the vma_migratable. If they are not, we would find the - * !migratable VMA on the next scan but not reset the scanner to the start - * so check it now. + * It is possible to reach the end of the VMA list but the last few + * VMAs are not guaranteed to the vma_migratable. If they are not, we + * would find the !migratable VMA on the next scan but not reset the + * scanner to the start so check it now. */ if (vma) mm->numa_scan_offset = start; diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 7489884..19dbb08 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -1305,7 +1305,7 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, spin_unlock(&mm->page_table_lock); lock_page(page); - /* Confirm the PTE did not while locked */ + /* Confirm the PMD did not change while page_table_lock was released */ spin_lock(&mm->page_table_lock); if (unlikely(!pmd_same(pmd, *pmdp))) { unlock_page(page); -- cgit v0.10.2 From 0c3a775e1e0b069bf765f8355b723ce0d18dcc6c Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:28:42 +0100 Subject: mm: numa: Do not account for a hinting fault if we raced If another task handled a hinting fault in parallel then do not double account for it. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-5-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 19dbb08..dab2bab 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -1325,8 +1325,11 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, check_same: spin_lock(&mm->page_table_lock); - if (unlikely(!pmd_same(pmd, *pmdp))) + if (unlikely(!pmd_same(pmd, *pmdp))) { + /* Someone else took our fault */ + current_nid = -1; goto out_unlock; + } clear_pmdnuma: pmd = pmd_mknonnuma(pmd); set_pmd_at(mm, haddr, pmdp, pmd); -- cgit v0.10.2 From ff9042b11a71c81238c70af168cd36b98a6d5a3c Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:28:43 +0100 Subject: mm: Wait for THP migrations to complete during NUMA hinting faults The locking for migrating THP is unusual. While normal page migration prevents parallel accesses using a migration PTE, THP migration relies on a combination of the page_table_lock, the page lock and the existance of the NUMA hinting PTE to guarantee safety but there is a bug in the scheme. If a THP page is currently being migrated and another thread traps a fault on the same page it checks if the page is misplaced. If it is not, then pmd_numa is cleared. The problem is that it checks if the page is misplaced without holding the page lock meaning that the racing thread can be migrating the THP when the second thread clears the NUMA bit and faults a stale page. This patch checks if the page is potentially being migrated and stalls using the lock_page if it is potentially being migrated before checking if the page is misplaced or not. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-6-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/mm/huge_memory.c b/mm/huge_memory.c index dab2bab..f362363 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -1295,13 +1295,14 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, if (current_nid == numa_node_id()) count_vm_numa_event(NUMA_HINT_FAULTS_LOCAL); - target_nid = mpol_misplaced(page, vma, haddr); - if (target_nid == -1) { - put_page(page); - goto clear_pmdnuma; - } + /* + * Acquire the page lock to serialise THP migrations but avoid dropping + * page_table_lock if at all possible + */ + if (trylock_page(page)) + goto got_lock; - /* Acquire the page lock to serialise THP migrations */ + /* Serialise against migrationa and check placement check placement */ spin_unlock(&mm->page_table_lock); lock_page(page); @@ -1312,9 +1313,17 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, put_page(page); goto out_unlock; } - spin_unlock(&mm->page_table_lock); + +got_lock: + target_nid = mpol_misplaced(page, vma, haddr); + if (target_nid == -1) { + unlock_page(page); + put_page(page); + goto clear_pmdnuma; + } /* Migrate the THP to the requested node */ + spin_unlock(&mm->page_table_lock); migrated = migrate_misplaced_transhuge_page(mm, vma, pmdp, pmd, addr, page, target_nid); if (!migrated) -- cgit v0.10.2 From b8916634b77bffb233d8f2f45703c80343457cc1 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:28:44 +0100 Subject: mm: Prevent parallel splits during THP migration THP migrations are serialised by the page lock but on its own that does not prevent THP splits. If the page is split during THP migration then the pmd_same checks will prevent page table corruption but the unlock page and other fix-ups potentially will cause corruption. This patch takes the anon_vma lock to prevent parallel splits during migration. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-7-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/mm/huge_memory.c b/mm/huge_memory.c index f362363..1d6334f 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -1278,18 +1278,18 @@ out: int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long addr, pmd_t pmd, pmd_t *pmdp) { + struct anon_vma *anon_vma = NULL; struct page *page; unsigned long haddr = addr & HPAGE_PMD_MASK; int target_nid; int current_nid = -1; - bool migrated; + bool migrated, page_locked; spin_lock(&mm->page_table_lock); if (unlikely(!pmd_same(pmd, *pmdp))) goto out_unlock; page = pmd_page(pmd); - get_page(page); current_nid = page_to_nid(page); count_vm_numa_event(NUMA_HINT_FAULTS); if (current_nid == numa_node_id()) @@ -1299,12 +1299,29 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, * Acquire the page lock to serialise THP migrations but avoid dropping * page_table_lock if at all possible */ - if (trylock_page(page)) - goto got_lock; + page_locked = trylock_page(page); + target_nid = mpol_misplaced(page, vma, haddr); + if (target_nid == -1) { + /* If the page was locked, there are no parallel migrations */ + if (page_locked) { + unlock_page(page); + goto clear_pmdnuma; + } - /* Serialise against migrationa and check placement check placement */ + /* Otherwise wait for potential migrations and retry fault */ + spin_unlock(&mm->page_table_lock); + wait_on_page_locked(page); + goto out; + } + + /* Page is misplaced, serialise migrations and parallel THP splits */ + get_page(page); spin_unlock(&mm->page_table_lock); - lock_page(page); + if (!page_locked) { + lock_page(page); + page_locked = true; + } + anon_vma = page_lock_anon_vma_read(page); /* Confirm the PMD did not change while page_table_lock was released */ spin_lock(&mm->page_table_lock); @@ -1314,14 +1331,6 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, goto out_unlock; } -got_lock: - target_nid = mpol_misplaced(page, vma, haddr); - if (target_nid == -1) { - unlock_page(page); - put_page(page); - goto clear_pmdnuma; - } - /* Migrate the THP to the requested node */ spin_unlock(&mm->page_table_lock); migrated = migrate_misplaced_transhuge_page(mm, vma, @@ -1330,6 +1339,8 @@ got_lock: goto check_same; task_numa_fault(target_nid, HPAGE_PMD_NR, true); + if (anon_vma) + page_unlock_anon_vma_read(anon_vma); return 0; check_same: @@ -1346,6 +1357,11 @@ clear_pmdnuma: update_mmu_cache_pmd(vma, addr, pmdp); out_unlock: spin_unlock(&mm->page_table_lock); + +out: + if (anon_vma) + page_unlock_anon_vma_read(anon_vma); + if (current_nid != -1) task_numa_fault(current_nid, HPAGE_PMD_NR, false); return 0; -- cgit v0.10.2 From 8191acbd30c73e45c24ad16c372e0b42cc7ac8f8 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:28:45 +0100 Subject: mm: numa: Sanitize task_numa_fault() callsites There are three callers of task_numa_fault(): - do_huge_pmd_numa_page(): Accounts against the current node, not the node where the page resides, unless we migrated, in which case it accounts against the node we migrated to. - do_numa_page(): Accounts against the current node, not the node where the page resides, unless we migrated, in which case it accounts against the node we migrated to. - do_pmd_numa_page(): Accounts not at all when the page isn't migrated, otherwise accounts against the node we migrated towards. This seems wrong to me; all three sites should have the same sementaics, furthermore we should accounts against where the page really is, we already know where the task is. So modify all three sites to always account; we did after all receive the fault; and always account to where the page is after migration, regardless of success. They all still differ on when they clear the PTE/PMD; ideally that would get sorted too. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-8-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 1d6334f..c3bb65f 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -1281,18 +1281,19 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, struct anon_vma *anon_vma = NULL; struct page *page; unsigned long haddr = addr & HPAGE_PMD_MASK; + int page_nid = -1, this_nid = numa_node_id(); int target_nid; - int current_nid = -1; - bool migrated, page_locked; + bool page_locked; + bool migrated = false; spin_lock(&mm->page_table_lock); if (unlikely(!pmd_same(pmd, *pmdp))) goto out_unlock; page = pmd_page(pmd); - current_nid = page_to_nid(page); + page_nid = page_to_nid(page); count_vm_numa_event(NUMA_HINT_FAULTS); - if (current_nid == numa_node_id()) + if (page_nid == this_nid) count_vm_numa_event(NUMA_HINT_FAULTS_LOCAL); /* @@ -1335,19 +1336,18 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, spin_unlock(&mm->page_table_lock); migrated = migrate_misplaced_transhuge_page(mm, vma, pmdp, pmd, addr, page, target_nid); - if (!migrated) + if (migrated) + page_nid = target_nid; + else goto check_same; - task_numa_fault(target_nid, HPAGE_PMD_NR, true); - if (anon_vma) - page_unlock_anon_vma_read(anon_vma); - return 0; + goto out; check_same: spin_lock(&mm->page_table_lock); if (unlikely(!pmd_same(pmd, *pmdp))) { /* Someone else took our fault */ - current_nid = -1; + page_nid = -1; goto out_unlock; } clear_pmdnuma: @@ -1362,8 +1362,9 @@ out: if (anon_vma) page_unlock_anon_vma_read(anon_vma); - if (current_nid != -1) - task_numa_fault(current_nid, HPAGE_PMD_NR, false); + if (page_nid != -1) + task_numa_fault(page_nid, HPAGE_PMD_NR, migrated); + return 0; } diff --git a/mm/memory.c b/mm/memory.c index ca00039..42ae82e 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -3519,12 +3519,12 @@ static int do_nonlinear_fault(struct mm_struct *mm, struct vm_area_struct *vma, } int numa_migrate_prep(struct page *page, struct vm_area_struct *vma, - unsigned long addr, int current_nid) + unsigned long addr, int page_nid) { get_page(page); count_vm_numa_event(NUMA_HINT_FAULTS); - if (current_nid == numa_node_id()) + if (page_nid == numa_node_id()) count_vm_numa_event(NUMA_HINT_FAULTS_LOCAL); return mpol_misplaced(page, vma, addr); @@ -3535,7 +3535,7 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, { struct page *page = NULL; spinlock_t *ptl; - int current_nid = -1; + int page_nid = -1; int target_nid; bool migrated = false; @@ -3565,15 +3565,10 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, return 0; } - current_nid = page_to_nid(page); - target_nid = numa_migrate_prep(page, vma, addr, current_nid); + page_nid = page_to_nid(page); + target_nid = numa_migrate_prep(page, vma, addr, page_nid); pte_unmap_unlock(ptep, ptl); if (target_nid == -1) { - /* - * Account for the fault against the current node if it not - * being replaced regardless of where the page is located. - */ - current_nid = numa_node_id(); put_page(page); goto out; } @@ -3581,11 +3576,11 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, /* Migrate to the requested node */ migrated = migrate_misplaced_page(page, target_nid); if (migrated) - current_nid = target_nid; + page_nid = target_nid; out: - if (current_nid != -1) - task_numa_fault(current_nid, 1, migrated); + if (page_nid != -1) + task_numa_fault(page_nid, 1, migrated); return 0; } @@ -3600,7 +3595,6 @@ static int do_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long offset; spinlock_t *ptl; bool numa = false; - int local_nid = numa_node_id(); spin_lock(&mm->page_table_lock); pmd = *pmdp; @@ -3623,9 +3617,10 @@ static int do_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, for (addr = _addr + offset; addr < _addr + PMD_SIZE; pte++, addr += PAGE_SIZE) { pte_t pteval = *pte; struct page *page; - int curr_nid = local_nid; + int page_nid = -1; int target_nid; - bool migrated; + bool migrated = false; + if (!pte_present(pteval)) continue; if (!pte_numa(pteval)) @@ -3647,25 +3642,19 @@ static int do_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, if (unlikely(page_mapcount(page) != 1)) continue; - /* - * Note that the NUMA fault is later accounted to either - * the node that is currently running or where the page is - * migrated to. - */ - curr_nid = local_nid; - target_nid = numa_migrate_prep(page, vma, addr, - page_to_nid(page)); - if (target_nid == -1) { + page_nid = page_to_nid(page); + target_nid = numa_migrate_prep(page, vma, addr, page_nid); + pte_unmap_unlock(pte, ptl); + if (target_nid != -1) { + migrated = migrate_misplaced_page(page, target_nid); + if (migrated) + page_nid = target_nid; + } else { put_page(page); - continue; } - /* Migrate to the requested node */ - pte_unmap_unlock(pte, ptl); - migrated = migrate_misplaced_page(page, target_nid); - if (migrated) - curr_nid = target_nid; - task_numa_fault(curr_nid, 1, migrated); + if (page_nid != -1) + task_numa_fault(page_nid, 1, migrated); pte = pte_offset_map_lock(mm, pmdp, addr, &ptl); } -- cgit v0.10.2 From a54a407fbf7735fd8f7841375574f5d9b0375f93 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:28:46 +0100 Subject: mm: Close races between THP migration and PMD numa clearing THP migration uses the page lock to guard against parallel allocations but there are cases like this still open Task A Task B --------------------- --------------------- do_huge_pmd_numa_page do_huge_pmd_numa_page lock_page mpol_misplaced == -1 unlock_page goto clear_pmdnuma lock_page mpol_misplaced == 2 migrate_misplaced_transhuge pmd = pmd_mknonnuma set_pmd_at During hours of testing, one crashed with weird errors and while I have no direct evidence, I suspect something like the race above happened. This patch extends the page lock to being held until the pmd_numa is cleared to prevent migration starting in parallel while the pmd_numa is being cleared. It also flushes the old pmd entry and orders pagetable insertion before rmap insertion. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-9-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/mm/huge_memory.c b/mm/huge_memory.c index c3bb65f..d4928769 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -1304,24 +1304,25 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, target_nid = mpol_misplaced(page, vma, haddr); if (target_nid == -1) { /* If the page was locked, there are no parallel migrations */ - if (page_locked) { - unlock_page(page); + if (page_locked) goto clear_pmdnuma; - } - /* Otherwise wait for potential migrations and retry fault */ + /* + * Otherwise wait for potential migrations and retry. We do + * relock and check_same as the page may no longer be mapped. + * As the fault is being retried, do not account for it. + */ spin_unlock(&mm->page_table_lock); wait_on_page_locked(page); + page_nid = -1; goto out; } /* Page is misplaced, serialise migrations and parallel THP splits */ get_page(page); spin_unlock(&mm->page_table_lock); - if (!page_locked) { + if (!page_locked) lock_page(page); - page_locked = true; - } anon_vma = page_lock_anon_vma_read(page); /* Confirm the PMD did not change while page_table_lock was released */ @@ -1329,32 +1330,28 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, if (unlikely(!pmd_same(pmd, *pmdp))) { unlock_page(page); put_page(page); + page_nid = -1; goto out_unlock; } - /* Migrate the THP to the requested node */ + /* + * Migrate the THP to the requested node, returns with page unlocked + * and pmd_numa cleared. + */ spin_unlock(&mm->page_table_lock); migrated = migrate_misplaced_transhuge_page(mm, vma, pmdp, pmd, addr, page, target_nid); if (migrated) page_nid = target_nid; - else - goto check_same; goto out; - -check_same: - spin_lock(&mm->page_table_lock); - if (unlikely(!pmd_same(pmd, *pmdp))) { - /* Someone else took our fault */ - page_nid = -1; - goto out_unlock; - } clear_pmdnuma: + BUG_ON(!PageLocked(page)); pmd = pmd_mknonnuma(pmd); set_pmd_at(mm, haddr, pmdp, pmd); VM_BUG_ON(pmd_numa(*pmdp)); update_mmu_cache_pmd(vma, addr, pmdp); + unlock_page(page); out_unlock: spin_unlock(&mm->page_table_lock); diff --git a/mm/migrate.c b/mm/migrate.c index a26bccd..7bd90d3 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -1713,12 +1713,12 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm, unlock_page(new_page); put_page(new_page); /* Free it */ - unlock_page(page); + /* Retake the callers reference and putback on LRU */ + get_page(page); putback_lru_page(page); - - count_vm_events(PGMIGRATE_FAIL, HPAGE_PMD_NR); - isolated = 0; - goto out; + mod_zone_page_state(page_zone(page), + NR_ISOLATED_ANON + page_lru, -HPAGE_PMD_NR); + goto out_fail; } /* @@ -1735,9 +1735,9 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm, entry = maybe_pmd_mkwrite(pmd_mkdirty(entry), vma); entry = pmd_mkhuge(entry); - page_add_new_anon_rmap(new_page, vma, haddr); - + pmdp_clear_flush(vma, haddr, pmd); set_pmd_at(mm, haddr, pmd, entry); + page_add_new_anon_rmap(new_page, vma, haddr); update_mmu_cache_pmd(vma, address, &entry); page_remove_rmap(page); /* @@ -1756,7 +1756,6 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm, count_vm_events(PGMIGRATE_SUCCESS, HPAGE_PMD_NR); count_vm_numa_events(NUMA_PAGE_MIGRATE, HPAGE_PMD_NR); -out: mod_zone_page_state(page_zone(page), NR_ISOLATED_ANON + page_lru, -HPAGE_PMD_NR); @@ -1765,6 +1764,10 @@ out: out_fail: count_vm_events(PGMIGRATE_FAIL, HPAGE_PMD_NR); out_dropref: + entry = pmd_mknonnuma(entry); + set_pmd_at(mm, haddr, pmd, entry); + update_mmu_cache_pmd(vma, address, &entry); + unlock_page(page); put_page(page); return 0; -- cgit v0.10.2 From afcae2655b0ab67e65f161b1bb214efcfa1db415 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:28:47 +0100 Subject: mm: Account for a THP NUMA hinting update as one PTE update A THP PMD update is accounted for as 512 pages updated in vmstat. This is large difference when estimating the cost of automatic NUMA balancing and can be misleading when comparing results that had collapsed versus split THP. This patch addresses the accounting issue. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-10-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/mm/mprotect.c b/mm/mprotect.c index 94722a4..2bbb648 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -145,7 +145,7 @@ static inline unsigned long change_pmd_range(struct vm_area_struct *vma, split_huge_page_pmd(vma, addr, pmd); else if (change_huge_pmd(vma, pmd, addr, newprot, prot_numa)) { - pages += HPAGE_PMD_NR; + pages++; continue; } /* fall through */ -- cgit v0.10.2 From e920e14ca29b0b2a981cfc90e4e20edd6f078d19 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:28:48 +0100 Subject: mm: Do not flush TLB during protection change if !pte_present && !migration_entry NUMA PTE scanning is expensive both in terms of the scanning itself and the TLB flush if there are any updates. Currently non-present PTEs are accounted for as an update and incurring a TLB flush where it is only necessary for anonymous migration entries. This patch addresses the problem and should reduce TLB flushes. Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Reviewed-by: Rik van Riel Signed-off-by: Mel Gorman Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-11-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/mm/mprotect.c b/mm/mprotect.c index 2bbb648..7bdbd4b 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -101,8 +101,9 @@ static unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd, make_migration_entry_read(&entry); set_pte_at(mm, addr, pte, swp_entry_to_pte(entry)); + + pages++; } - pages++; } } while (pte++, addr += PAGE_SIZE, addr != end); arch_leave_lazy_mmu_mode(); -- cgit v0.10.2 From f123d74abf91574837d14e5ea58f6a779a387bf5 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:28:49 +0100 Subject: mm: Only flush TLBs if a transhuge PMD is modified for NUMA pte scanning NUMA PTE scanning is expensive both in terms of the scanning itself and the TLB flush if there are any updates. The TLB flush is avoided if no PTEs are updated but there is a bug where transhuge PMDs are considered to be updated even if they were already pmd_numa. This patch addresses the problem and TLB flushes should be reduced. Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Reviewed-by: Rik van Riel Signed-off-by: Mel Gorman Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-12-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/mm/huge_memory.c b/mm/huge_memory.c index d4928769..de8d5cf 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -1458,6 +1458,12 @@ out: return ret; } +/* + * Returns + * - 0 if PMD could not be locked + * - 1 if PMD was locked but protections unchange and TLB flush unnecessary + * - HPAGE_PMD_NR is protections changed and TLB flush necessary + */ int change_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd, unsigned long addr, pgprot_t newprot, int prot_numa) { @@ -1466,9 +1472,11 @@ int change_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd, if (__pmd_trans_huge_lock(pmd, vma) == 1) { pmd_t entry; - entry = pmdp_get_and_clear(mm, addr, pmd); + ret = 1; if (!prot_numa) { + entry = pmdp_get_and_clear(mm, addr, pmd); entry = pmd_modify(entry, newprot); + ret = HPAGE_PMD_NR; BUG_ON(pmd_write(entry)); } else { struct page *page = pmd_page(*pmd); @@ -1476,12 +1484,17 @@ int change_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd, /* only check non-shared pages */ if (page_mapcount(page) == 1 && !pmd_numa(*pmd)) { + entry = pmdp_get_and_clear(mm, addr, pmd); entry = pmd_mknuma(entry); + ret = HPAGE_PMD_NR; } } - set_pmd_at(mm, addr, pmd, entry); + + /* Set PMD if cleared earlier */ + if (ret == HPAGE_PMD_NR) + set_pmd_at(mm, addr, pmd, entry); + spin_unlock(&vma->vm_mm->page_table_lock); - ret = 1; } return ret; diff --git a/mm/mprotect.c b/mm/mprotect.c index 7bdbd4b..2da33dc 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -144,10 +144,16 @@ static inline unsigned long change_pmd_range(struct vm_area_struct *vma, if (pmd_trans_huge(*pmd)) { if (next - addr != HPAGE_PMD_SIZE) split_huge_page_pmd(vma, addr, pmd); - else if (change_huge_pmd(vma, pmd, addr, newprot, - prot_numa)) { - pages++; - continue; + else { + int nr_ptes = change_huge_pmd(vma, pmd, addr, + newprot, prot_numa); + + if (nr_ptes) { + if (nr_ptes == HPAGE_PMD_NR) + pages++; + + continue; + } } /* fall through */ } -- cgit v0.10.2 From a1a46184e34cfd0764f06a54870defa052b0a094 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:28:50 +0100 Subject: mm: numa: Do not migrate or account for hinting faults on the zero page The zero page is not replicated between nodes and is often shared between processes. The data is read-only and likely to be cached in local CPUs if heavily accessed meaning that the remote memory access cost is less of a concern. This patch prevents trapping faults on the zero pages. For tasks using the zero page this will reduce the number of PTE updates, TLB flushes and hinting faults. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju [ Correct use of is_huge_zero_page] Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-13-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/mm/huge_memory.c b/mm/huge_memory.c index de8d5cf..8677dbf 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -1291,6 +1291,7 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, goto out_unlock; page = pmd_page(pmd); + BUG_ON(is_huge_zero_page(page)); page_nid = page_to_nid(page); count_vm_numa_event(NUMA_HINT_FAULTS); if (page_nid == this_nid) @@ -1481,8 +1482,15 @@ int change_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd, } else { struct page *page = pmd_page(*pmd); - /* only check non-shared pages */ + /* + * Only check non-shared pages. Do not trap faults + * against the zero page. The read-only data is likely + * to be read-cached on the local CPU cache and it is + * less useful to know about local vs remote hits on + * the zero page. + */ if (page_mapcount(page) == 1 && + !is_huge_zero_page(page) && !pmd_numa(*pmd)) { entry = pmdp_get_and_clear(mm, addr, pmd); entry = pmd_mknuma(entry); diff --git a/mm/memory.c b/mm/memory.c index 42ae82e..ed51f15 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -3564,6 +3564,7 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, pte_unmap_unlock(ptep, ptl); return 0; } + BUG_ON(is_zero_pfn(page_to_pfn(page))); page_nid = page_to_nid(page); target_nid = numa_migrate_prep(page, vma, addr, page_nid); -- cgit v0.10.2 From 19a78d110d7a8045aeb90d38ee8fe9743ce88c2d Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 7 Oct 2013 11:28:51 +0100 Subject: sched/numa: Mitigate chance that same task always updates PTEs With a trace_printk("working\n"); right after the cmpxchg in task_numa_work() we can see that of a 4 thread process, its always the same task winning the race and doing the protection change. This is a problem since the task doing the protection change has a penalty for taking faults -- it is busy when marking the PTEs. If its always the same task the ->numa_faults[] get severely skewed. Avoid this by delaying the task doing the protection change such that it is unlikely to win the privilege again. Before: root@interlagos:~# grep "thread 0/.*working" /debug/tracing/trace | tail -15 thread 0/0-3232 [022] .... 212.787402: task_numa_work: working thread 0/0-3232 [022] .... 212.888473: task_numa_work: working thread 0/0-3232 [022] .... 212.989538: task_numa_work: working thread 0/0-3232 [022] .... 213.090602: task_numa_work: working thread 0/0-3232 [022] .... 213.191667: task_numa_work: working thread 0/0-3232 [022] .... 213.292734: task_numa_work: working thread 0/0-3232 [022] .... 213.393804: task_numa_work: working thread 0/0-3232 [022] .... 213.494869: task_numa_work: working thread 0/0-3232 [022] .... 213.596937: task_numa_work: working thread 0/0-3232 [022] .... 213.699000: task_numa_work: working thread 0/0-3232 [022] .... 213.801067: task_numa_work: working thread 0/0-3232 [022] .... 213.903155: task_numa_work: working thread 0/0-3232 [022] .... 214.005201: task_numa_work: working thread 0/0-3232 [022] .... 214.107266: task_numa_work: working thread 0/0-3232 [022] .... 214.209342: task_numa_work: working After: root@interlagos:~# grep "thread 0/.*working" /debug/tracing/trace | tail -15 thread 0/0-3253 [005] .... 136.865051: task_numa_work: working thread 0/2-3255 [026] .... 136.965134: task_numa_work: working thread 0/3-3256 [024] .... 137.065217: task_numa_work: working thread 0/3-3256 [024] .... 137.165302: task_numa_work: working thread 0/3-3256 [024] .... 137.265382: task_numa_work: working thread 0/0-3253 [004] .... 137.366465: task_numa_work: working thread 0/2-3255 [026] .... 137.466549: task_numa_work: working thread 0/0-3253 [004] .... 137.566629: task_numa_work: working thread 0/0-3253 [004] .... 137.666711: task_numa_work: working thread 0/1-3254 [028] .... 137.766799: task_numa_work: working thread 0/0-3253 [004] .... 137.866876: task_numa_work: working thread 0/2-3255 [026] .... 137.966960: task_numa_work: working thread 0/1-3254 [028] .... 138.067041: task_numa_work: working thread 0/2-3255 [026] .... 138.167123: task_numa_work: working thread 0/3-3256 [024] .... 138.267207: task_numa_work: working Signed-off-by: Peter Zijlstra Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Link: http://lkml.kernel.org/r/1381141781-10992-14-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 817cd7b..573d815e 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -946,6 +946,12 @@ void task_numa_work(struct callback_head *work) return; /* + * Delay this task enough that another task of this mm will likely win + * the next time around. + */ + p->node_stamp += 2 * TICK_NSEC; + + /* * Do not set pte_numa if the current running node is rate-limited. * This loses statistics on the fault but if we are unwilling to * migrate to this node, it is less likely we can do useful work @@ -1026,7 +1032,7 @@ void task_tick_numa(struct rq *rq, struct task_struct *curr) if (now - curr->node_stamp > period) { if (!curr->node_stamp) curr->numa_scan_period = sysctl_numa_balancing_scan_period_min; - curr->node_stamp = now; + curr->node_stamp += period; if (!time_before(jiffies, curr->mm->numa_next_scan)) { init_task_work(work, task_numa_work); /* TODO: move this into sched_fork() */ -- cgit v0.10.2 From 9e645ab6d089f5822479a833c6977c785bcfffe3 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 7 Oct 2013 11:28:52 +0100 Subject: sched/numa: Continue PTE scanning even if migrate rate limited Avoiding marking PTEs pte_numa because a particular NUMA node is migrate rate limited sees like a bad idea. Even if this node can't migrate anymore other nodes might and we want up-to-date information to do balance decisions. We already rate limit the actual migrations, this should leave enough bandwidth to allow the non-migrating scanning. I think its important we keep up-to-date information if we're going to do placement based on it. Signed-off-by: Peter Zijlstra Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Link: http://lkml.kernel.org/r/1381141781-10992-15-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 573d815e..464207f 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -951,14 +951,6 @@ void task_numa_work(struct callback_head *work) */ p->node_stamp += 2 * TICK_NSEC; - /* - * Do not set pte_numa if the current running node is rate-limited. - * This loses statistics on the fault but if we are unwilling to - * migrate to this node, it is less likely we can do useful work - */ - if (migrate_ratelimited(numa_node_id())) - return; - start = mm->numa_scan_offset; pages = sysctl_numa_balancing_scan_size; pages <<= 20 - PAGE_SHIFT; /* MB in pages */ -- cgit v0.10.2 From b726b7dfb400c937546fa91cf8523dcb1aa2fc6e Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:28:53 +0100 Subject: Revert "mm: sched: numa: Delay PTE scanning until a task is scheduled on a new node" PTE scanning and NUMA hinting fault handling is expensive so commit 5bca2303 ("mm: sched: numa: Delay PTE scanning until a task is scheduled on a new node") deferred the PTE scan until a task had been scheduled on another node. The problem is that in the purely shared memory case that this may never happen and no NUMA hinting fault information will be captured. We are not ruling out the possibility that something better can be done here but for now, this patch needs to be reverted and depend entirely on the scan_delay to avoid punishing short-lived processes. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-16-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index d9851ee..b7adf1d 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -428,20 +428,10 @@ struct mm_struct { /* numa_scan_seq prevents two threads setting pte_numa */ int numa_scan_seq; - - /* - * The first node a task was scheduled on. If a task runs on - * a different node than Make PTE Scan Go Now. - */ - int first_nid; #endif struct uprobes_state uprobes_state; }; -/* first nid will either be a valid NID or one of these values */ -#define NUMA_PTE_SCAN_INIT -1 -#define NUMA_PTE_SCAN_ACTIVE -2 - static inline void mm_init_cpumask(struct mm_struct *mm) { #ifdef CONFIG_CPUMASK_OFFSTACK diff --git a/kernel/fork.c b/kernel/fork.c index 086fe73..7192d91 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -817,9 +817,6 @@ struct mm_struct *dup_mm(struct task_struct *tsk) #ifdef CONFIG_TRANSPARENT_HUGEPAGE mm->pmd_huge_pte = NULL; #endif -#ifdef CONFIG_NUMA_BALANCING - mm->first_nid = NUMA_PTE_SCAN_INIT; -#endif if (!mm_init(mm, tsk)) goto fail_nomem; diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 464207f..49b11fa 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -901,24 +901,6 @@ void task_numa_work(struct callback_head *work) return; /* - * We do not care about task placement until a task runs on a node - * other than the first one used by the address space. This is - * largely because migrations are driven by what CPU the task - * is running on. If it's never scheduled on another node, it'll - * not migrate so why bother trapping the fault. - */ - if (mm->first_nid == NUMA_PTE_SCAN_INIT) - mm->first_nid = numa_node_id(); - if (mm->first_nid != NUMA_PTE_SCAN_ACTIVE) { - /* Are we running on a new node yet? */ - if (numa_node_id() == mm->first_nid && - !sched_feat_numa(NUMA_FORCE)) - return; - - mm->first_nid = NUMA_PTE_SCAN_ACTIVE; - } - - /* * Reset the scan period if enough time has gone by. Objective is that * scanning will be reduced if pages are properly placed. As tasks * can enter different phases this needs to be re-examined. Lacking diff --git a/kernel/sched/features.h b/kernel/sched/features.h index 99399f8..cba5c61 100644 --- a/kernel/sched/features.h +++ b/kernel/sched/features.h @@ -63,10 +63,8 @@ SCHED_FEAT(LB_MIN, false) /* * Apply the automatic NUMA scheduling policy. Enabled automatically * at runtime if running on a NUMA machine. Can be controlled via - * numa_balancing=. Allow PTE scanning to be forced on UMA machines - * for debugging the core machinery. + * numa_balancing= */ #ifdef CONFIG_NUMA_BALANCING SCHED_FEAT(NUMA, false) -SCHED_FEAT(NUMA_FORCE, false) #endif -- cgit v0.10.2 From 7e8d16b6cbccb2f5da579f5085479fb82ba851b8 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:28:54 +0100 Subject: sched/numa: Initialise numa_next_scan properly Scan delay logic and resets are currently initialised to start scanning immediately instead of delaying properly. Initialise them properly at fork time and catch when a new mm has been allocated. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-17-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/kernel/sched/core.c b/kernel/sched/core.c index f575d5b..aee7e4d 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -1624,8 +1624,8 @@ static void __sched_fork(struct task_struct *p) #ifdef CONFIG_NUMA_BALANCING if (p->mm && atomic_read(&p->mm->mm_users) == 1) { - p->mm->numa_next_scan = jiffies; - p->mm->numa_next_reset = jiffies; + p->mm->numa_next_scan = jiffies + msecs_to_jiffies(sysctl_numa_balancing_scan_delay); + p->mm->numa_next_reset = jiffies + msecs_to_jiffies(sysctl_numa_balancing_scan_period_reset); p->mm->numa_scan_seq = 0; } diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 49b11fa..0966f0c 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -900,6 +900,13 @@ void task_numa_work(struct callback_head *work) if (p->flags & PF_EXITING) return; + if (!mm->numa_next_reset || !mm->numa_next_scan) { + mm->numa_next_scan = now + + msecs_to_jiffies(sysctl_numa_balancing_scan_delay); + mm->numa_next_reset = now + + msecs_to_jiffies(sysctl_numa_balancing_scan_period_reset); + } + /* * Reset the scan period if enough time has gone by. Objective is that * scanning will be reduced if pages are properly placed. As tasks -- cgit v0.10.2 From 598f0ec0bc996e90a806ee9564af919ea5aad401 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:28:55 +0100 Subject: sched/numa: Set the scan rate proportional to the memory usage of the task being scanned The NUMA PTE scan rate is controlled with a combination of the numa_balancing_scan_period_min, numa_balancing_scan_period_max and numa_balancing_scan_size. This scan rate is independent of the size of the task and as an aside it is further complicated by the fact that numa_balancing_scan_size controls how many pages are marked pte_numa and not how much virtual memory is scanned. In combination, it is almost impossible to meaningfully tune the min and max scan periods and reasoning about performance is complex when the time to complete a full scan is is partially a function of the tasks memory size. This patch alters the semantic of the min and max tunables to be about tuning the length time it takes to complete a scan of a tasks occupied virtual address space. Conceptually this is a lot easier to understand. There is a "sanity" check to ensure the scan rate is never extremely fast based on the amount of virtual memory that should be scanned in a second. The default of 2.5G seems arbitrary but it is to have the maximum scan rate after the patch roughly match the maximum scan rate before the patch was applied. On a similar note, numa_scan_period is in milliseconds and not jiffies. Properly placed pages slow the scanning rate but adding 10 jiffies to numa_scan_period means that the rate scanning slows depends on HZ which is confusing. Get rid of the jiffies_to_msec conversion and treat it as ms. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-18-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index 1428c66..8cd7e5f 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -403,15 +403,16 @@ workload pattern changes and minimises performance impact due to remote memory accesses. These sysctls control the thresholds for scan delays and the number of pages scanned. -numa_balancing_scan_period_min_ms is the minimum delay in milliseconds -between scans. It effectively controls the maximum scanning rate for -each task. +numa_balancing_scan_period_min_ms is the minimum time in milliseconds to +scan a tasks virtual memory. It effectively controls the maximum scanning +rate for each task. numa_balancing_scan_delay_ms is the starting "scan delay" used for a task when it initially forks. -numa_balancing_scan_period_max_ms is the maximum delay between scans. It -effectively controls the minimum scanning rate for each task. +numa_balancing_scan_period_max_ms is the maximum time in milliseconds to +scan a tasks virtual memory. It effectively controls the minimum scanning +rate for each task. numa_balancing_scan_size_mb is how many megabytes worth of pages are scanned for a given scan. diff --git a/include/linux/sched.h b/include/linux/sched.h index 2ac5285..fdcb4c8 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1339,6 +1339,7 @@ struct task_struct { int numa_scan_seq; int numa_migrate_seq; unsigned int numa_scan_period; + unsigned int numa_scan_period_max; u64 node_stamp; /* migration stamp */ struct callback_head numa_work; #endif /* CONFIG_NUMA_BALANCING */ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 0966f0c..e08d757 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -818,11 +818,13 @@ update_stats_curr_start(struct cfs_rq *cfs_rq, struct sched_entity *se) #ifdef CONFIG_NUMA_BALANCING /* - * numa task sample period in ms + * Approximate time to scan a full NUMA task in ms. The task scan period is + * calculated based on the tasks virtual memory size and + * numa_balancing_scan_size. */ -unsigned int sysctl_numa_balancing_scan_period_min = 100; -unsigned int sysctl_numa_balancing_scan_period_max = 100*50; -unsigned int sysctl_numa_balancing_scan_period_reset = 100*600; +unsigned int sysctl_numa_balancing_scan_period_min = 1000; +unsigned int sysctl_numa_balancing_scan_period_max = 60000; +unsigned int sysctl_numa_balancing_scan_period_reset = 60000; /* Portion of address space to scan in MB */ unsigned int sysctl_numa_balancing_scan_size = 256; @@ -830,6 +832,51 @@ unsigned int sysctl_numa_balancing_scan_size = 256; /* Scan @scan_size MB every @scan_period after an initial @scan_delay in ms */ unsigned int sysctl_numa_balancing_scan_delay = 1000; +static unsigned int task_nr_scan_windows(struct task_struct *p) +{ + unsigned long rss = 0; + unsigned long nr_scan_pages; + + /* + * Calculations based on RSS as non-present and empty pages are skipped + * by the PTE scanner and NUMA hinting faults should be trapped based + * on resident pages + */ + nr_scan_pages = sysctl_numa_balancing_scan_size << (20 - PAGE_SHIFT); + rss = get_mm_rss(p->mm); + if (!rss) + rss = nr_scan_pages; + + rss = round_up(rss, nr_scan_pages); + return rss / nr_scan_pages; +} + +/* For sanitys sake, never scan more PTEs than MAX_SCAN_WINDOW MB/sec. */ +#define MAX_SCAN_WINDOW 2560 + +static unsigned int task_scan_min(struct task_struct *p) +{ + unsigned int scan, floor; + unsigned int windows = 1; + + if (sysctl_numa_balancing_scan_size < MAX_SCAN_WINDOW) + windows = MAX_SCAN_WINDOW / sysctl_numa_balancing_scan_size; + floor = 1000 / windows; + + scan = sysctl_numa_balancing_scan_period_min / task_nr_scan_windows(p); + return max_t(unsigned int, floor, scan); +} + +static unsigned int task_scan_max(struct task_struct *p) +{ + unsigned int smin = task_scan_min(p); + unsigned int smax; + + /* Watch for min being lower than max due to floor calculations */ + smax = sysctl_numa_balancing_scan_period_max / task_nr_scan_windows(p); + return max(smin, smax); +} + static void task_numa_placement(struct task_struct *p) { int seq; @@ -840,6 +887,7 @@ static void task_numa_placement(struct task_struct *p) if (p->numa_scan_seq == seq) return; p->numa_scan_seq = seq; + p->numa_scan_period_max = task_scan_max(p); /* FIXME: Scheduling placement policy hints go here */ } @@ -860,9 +908,14 @@ void task_numa_fault(int node, int pages, bool migrated) * If pages are properly placed (did not migrate) then scan slower. * This is reset periodically in case of phase changes */ - if (!migrated) - p->numa_scan_period = min(sysctl_numa_balancing_scan_period_max, - p->numa_scan_period + jiffies_to_msecs(10)); + if (!migrated) { + /* Initialise if necessary */ + if (!p->numa_scan_period_max) + p->numa_scan_period_max = task_scan_max(p); + + p->numa_scan_period = min(p->numa_scan_period_max, + p->numa_scan_period + 10); + } task_numa_placement(p); } @@ -884,6 +937,7 @@ void task_numa_work(struct callback_head *work) struct mm_struct *mm = p->mm; struct vm_area_struct *vma; unsigned long start, end; + unsigned long nr_pte_updates = 0; long pages; WARN_ON_ONCE(p != container_of(work, struct task_struct, numa_work)); @@ -915,7 +969,7 @@ void task_numa_work(struct callback_head *work) */ migrate = mm->numa_next_reset; if (time_after(now, migrate)) { - p->numa_scan_period = sysctl_numa_balancing_scan_period_min; + p->numa_scan_period = task_scan_min(p); next_scan = now + msecs_to_jiffies(sysctl_numa_balancing_scan_period_reset); xchg(&mm->numa_next_reset, next_scan); } @@ -927,8 +981,10 @@ void task_numa_work(struct callback_head *work) if (time_before(now, migrate)) return; - if (p->numa_scan_period == 0) - p->numa_scan_period = sysctl_numa_balancing_scan_period_min; + if (p->numa_scan_period == 0) { + p->numa_scan_period_max = task_scan_max(p); + p->numa_scan_period = task_scan_min(p); + } next_scan = now + msecs_to_jiffies(p->numa_scan_period); if (cmpxchg(&mm->numa_next_scan, migrate, next_scan) != migrate) @@ -965,7 +1021,15 @@ void task_numa_work(struct callback_head *work) start = max(start, vma->vm_start); end = ALIGN(start + (pages << PAGE_SHIFT), HPAGE_SIZE); end = min(end, vma->vm_end); - pages -= change_prot_numa(vma, start, end); + nr_pte_updates += change_prot_numa(vma, start, end); + + /* + * Scan sysctl_numa_balancing_scan_size but ensure that + * at least one PTE is updated so that unused virtual + * address space is quickly skipped. + */ + if (nr_pte_updates) + pages -= (end - start) >> PAGE_SHIFT; start = end; if (pages <= 0) @@ -1012,7 +1076,7 @@ void task_tick_numa(struct rq *rq, struct task_struct *curr) if (now - curr->node_stamp > period) { if (!curr->node_stamp) - curr->numa_scan_period = sysctl_numa_balancing_scan_period_min; + curr->numa_scan_period = task_scan_min(curr); curr->node_stamp += period; if (!time_before(jiffies, curr->mm->numa_next_scan)) { -- cgit v0.10.2 From f307cd1a32fab53012b01749a1f5ba10b0a7243f Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:28:56 +0100 Subject: sched/numa: Slow scan rate if no NUMA hinting faults are being recorded NUMA PTE scanning slows if a NUMA hinting fault was trapped and no page was migrated. For long-lived but idle processes there may be no faults but the scan rate will be high and just waste CPU. This patch will slow the scan rate for processes that are not trapping faults. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-19-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index e08d757..c6c3302 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1039,6 +1039,18 @@ void task_numa_work(struct callback_head *work) out: /* + * If the whole process was scanned without updates then no NUMA + * hinting faults are being recorded and scan rate should be lower. + */ + if (mm->numa_scan_offset == 0 && !nr_pte_updates) { + p->numa_scan_period = min(p->numa_scan_period_max, + p->numa_scan_period << 1); + + next_scan = now + msecs_to_jiffies(p->numa_scan_period); + mm->numa_next_scan = next_scan; + } + + /* * It is possible to reach the end of the VMA list but the last few * VMAs are not guaranteed to the vma_migratable. If they are not, we * would find the !migratable VMA on the next scan but not reset the -- cgit v0.10.2 From f809ca9a554dda49fb264c79e31c722e0b063ff8 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:28:57 +0100 Subject: sched/numa: Track NUMA hinting faults on per-node basis This patch tracks what nodes numa hinting faults were incurred on. This information is later used to schedule a task on the node storing the pages most frequently faulted by the task. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-20-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/include/linux/sched.h b/include/linux/sched.h index fdcb4c8..a810e95 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1342,6 +1342,8 @@ struct task_struct { unsigned int numa_scan_period_max; u64 node_stamp; /* migration stamp */ struct callback_head numa_work; + + unsigned long *numa_faults; #endif /* CONFIG_NUMA_BALANCING */ struct rcu_head rcu; diff --git a/kernel/sched/core.c b/kernel/sched/core.c index aee7e4d..6808d35 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -1634,6 +1634,7 @@ static void __sched_fork(struct task_struct *p) p->numa_migrate_seq = p->mm ? p->mm->numa_scan_seq - 1 : 0; p->numa_scan_period = sysctl_numa_balancing_scan_delay; p->numa_work.next = &p->numa_work; + p->numa_faults = NULL; #endif /* CONFIG_NUMA_BALANCING */ } @@ -1892,6 +1893,8 @@ static void finish_task_switch(struct rq *rq, struct task_struct *prev) if (mm) mmdrop(mm); if (unlikely(prev_state == TASK_DEAD)) { + task_numa_free(prev); + /* * Remove function-return probe instances associated with this * task and put them back on the free list. diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index c6c3302..0bb3e0a 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -902,7 +902,14 @@ void task_numa_fault(int node, int pages, bool migrated) if (!numabalancing_enabled) return; - /* FIXME: Allocate task-specific structure for placement policy here */ + /* Allocate buffer to track faults on a per-node basis */ + if (unlikely(!p->numa_faults)) { + int size = sizeof(*p->numa_faults) * nr_node_ids; + + p->numa_faults = kzalloc(size, GFP_KERNEL|__GFP_NOWARN); + if (!p->numa_faults) + return; + } /* * If pages are properly placed (did not migrate) then scan slower. @@ -918,6 +925,8 @@ void task_numa_fault(int node, int pages, bool migrated) } task_numa_placement(p); + + p->numa_faults[node] += pages; } static void reset_ptenuma_scan(struct task_struct *p) diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index e82484d..199099c 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -6,6 +6,7 @@ #include #include #include +#include #include "cpupri.h" #include "cpuacct.h" @@ -555,6 +556,17 @@ static inline u64 rq_clock_task(struct rq *rq) return rq->clock_task; } +#ifdef CONFIG_NUMA_BALANCING +static inline void task_numa_free(struct task_struct *p) +{ + kfree(p->numa_faults); +} +#else /* CONFIG_NUMA_BALANCING */ +static inline void task_numa_free(struct task_struct *p) +{ +} +#endif /* CONFIG_NUMA_BALANCING */ + #ifdef CONFIG_SMP #define rcu_dereference_check_sched_domain(p) \ -- cgit v0.10.2 From 688b7585d16ab57a17aa4422a3b290b3a55fa679 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:28:58 +0100 Subject: sched/numa: Select a preferred node with the most numa hinting faults This patch selects a preferred node for a task to run on based on the NUMA hinting faults. This information is later used to migrate tasks towards the node during balancing. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-21-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/include/linux/sched.h b/include/linux/sched.h index a810e95..b1fc75e 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1344,6 +1344,7 @@ struct task_struct { struct callback_head numa_work; unsigned long *numa_faults; + int numa_preferred_nid; #endif /* CONFIG_NUMA_BALANCING */ struct rcu_head rcu; diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 6808d35..d15cd70 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -1633,6 +1633,7 @@ static void __sched_fork(struct task_struct *p) p->numa_scan_seq = p->mm ? p->mm->numa_scan_seq : 0; p->numa_migrate_seq = p->mm ? p->mm->numa_scan_seq - 1 : 0; p->numa_scan_period = sysctl_numa_balancing_scan_delay; + p->numa_preferred_nid = -1; p->numa_work.next = &p->numa_work; p->numa_faults = NULL; #endif /* CONFIG_NUMA_BALANCING */ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 0bb3e0a..9efd34f 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -879,7 +879,8 @@ static unsigned int task_scan_max(struct task_struct *p) static void task_numa_placement(struct task_struct *p) { - int seq; + int seq, nid, max_nid = -1; + unsigned long max_faults = 0; if (!p->mm) /* for example, ksmd faulting in a user's mm */ return; @@ -889,7 +890,19 @@ static void task_numa_placement(struct task_struct *p) p->numa_scan_seq = seq; p->numa_scan_period_max = task_scan_max(p); - /* FIXME: Scheduling placement policy hints go here */ + /* Find the node with the highest number of faults */ + for_each_online_node(nid) { + unsigned long faults = p->numa_faults[nid]; + p->numa_faults[nid] >>= 1; + if (faults > max_faults) { + max_faults = faults; + max_nid = nid; + } + } + + /* Update the tasks preferred node if necessary */ + if (max_faults && max_nid != p->numa_preferred_nid) + p->numa_preferred_nid = max_nid; } /* -- cgit v0.10.2 From 745d61476ddb737aad3495fa6d9a8f8c2ee59f86 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:28:59 +0100 Subject: sched/numa: Update NUMA hinting faults once per scan NUMA hinting fault counts and placement decisions are both recorded in the same array which distorts the samples in an unpredictable fashion. The values linearly accumulate during the scan and then decay creating a sawtooth-like pattern in the per-node counts. It also means that placement decisions are time sensitive. At best it means that it is very difficult to state that the buffer holds a decaying average of past faulting behaviour. At worst, it can confuse the load balancer if it sees one node with an artifically high count due to very recent faulting activity and may create a bouncing effect. This patch adds a second array. numa_faults stores the historical data which is used for placement decisions. numa_faults_buffer holds the fault activity during the current scan window. When the scan completes, numa_faults decays and the values from numa_faults_buffer are copied across. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-22-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/include/linux/sched.h b/include/linux/sched.h index b1fc75e..a463bc3 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1343,7 +1343,20 @@ struct task_struct { u64 node_stamp; /* migration stamp */ struct callback_head numa_work; + /* + * Exponential decaying average of faults on a per-node basis. + * Scheduling placement decisions are made based on the these counts. + * The values remain static for the duration of a PTE scan + */ unsigned long *numa_faults; + + /* + * numa_faults_buffer records faults per node during the current + * scan window. When the scan completes, the counts in numa_faults + * decay and these values are copied. + */ + unsigned long *numa_faults_buffer; + int numa_preferred_nid; #endif /* CONFIG_NUMA_BALANCING */ diff --git a/kernel/sched/core.c b/kernel/sched/core.c index d15cd70..064a0af 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -1636,6 +1636,7 @@ static void __sched_fork(struct task_struct *p) p->numa_preferred_nid = -1; p->numa_work.next = &p->numa_work; p->numa_faults = NULL; + p->numa_faults_buffer = NULL; #endif /* CONFIG_NUMA_BALANCING */ } diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 9efd34f..3abc651 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -892,8 +892,14 @@ static void task_numa_placement(struct task_struct *p) /* Find the node with the highest number of faults */ for_each_online_node(nid) { - unsigned long faults = p->numa_faults[nid]; + unsigned long faults; + + /* Decay existing window and copy faults since last scan */ p->numa_faults[nid] >>= 1; + p->numa_faults[nid] += p->numa_faults_buffer[nid]; + p->numa_faults_buffer[nid] = 0; + + faults = p->numa_faults[nid]; if (faults > max_faults) { max_faults = faults; max_nid = nid; @@ -919,9 +925,13 @@ void task_numa_fault(int node, int pages, bool migrated) if (unlikely(!p->numa_faults)) { int size = sizeof(*p->numa_faults) * nr_node_ids; - p->numa_faults = kzalloc(size, GFP_KERNEL|__GFP_NOWARN); + /* numa_faults and numa_faults_buffer share the allocation */ + p->numa_faults = kzalloc(size * 2, GFP_KERNEL|__GFP_NOWARN); if (!p->numa_faults) return; + + BUG_ON(p->numa_faults_buffer); + p->numa_faults_buffer = p->numa_faults + nr_node_ids; } /* @@ -939,7 +949,7 @@ void task_numa_fault(int node, int pages, bool migrated) task_numa_placement(p); - p->numa_faults[node] += pages; + p->numa_faults_buffer[node] += pages; } static void reset_ptenuma_scan(struct task_struct *p) -- cgit v0.10.2 From 3a7053b3224f4a8b0e8184166190076593621617 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:29:00 +0100 Subject: sched/numa: Favour moving tasks towards the preferred node This patch favours moving tasks towards NUMA node that recorded a higher number of NUMA faults during active load balancing. Ideally this is self-reinforcing as the longer the task runs on that node, the more faults it should incur causing task_numa_placement to keep the task running on that node. In reality a big weakness is that the nodes CPUs can be overloaded and it would be more efficient to queue tasks on an idle node and migrate to the new node. This would require additional smarts in the balancer so for now the balancer will simply prefer to place the task on the preferred node for a PTE scans which is controlled by the numa_balancing_settle_count sysctl. Once the settle_count number of scans has complete the schedule is free to place the task on an alternative node if the load is imbalanced. [srikar@linux.vnet.ibm.com: Fixed statistics] Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju [ Tunable and use higher faults instead of preferred. ] Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-23-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index 8cd7e5f..d48bca4 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -375,7 +375,8 @@ feature should be disabled. Otherwise, if the system overhead from the feature is too high then the rate the kernel samples for NUMA hinting faults may be controlled by the numa_balancing_scan_period_min_ms, numa_balancing_scan_delay_ms, numa_balancing_scan_period_reset, -numa_balancing_scan_period_max_ms and numa_balancing_scan_size_mb sysctls. +numa_balancing_scan_period_max_ms, numa_balancing_scan_size_mb and +numa_balancing_settle_count sysctls. ============================================================== @@ -420,6 +421,11 @@ scanned for a given scan. numa_balancing_scan_period_reset is a blunt instrument that controls how often a tasks scan delay is reset to detect sudden changes in task behaviour. +numa_balancing_settle_count is how many scan periods must complete before +the schedule balancer stops pushing the task towards a preferred node. This +gives the scheduler a chance to place the task on an alternative node if the +preferred node is overloaded. + ============================================================== osrelease, ostype & version: diff --git a/include/linux/sched.h b/include/linux/sched.h index a463bc3..aecdc5a 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -777,6 +777,7 @@ enum cpu_idle_type { #define SD_ASYM_PACKING 0x0800 /* Place busy groups earlier in the domain */ #define SD_PREFER_SIBLING 0x1000 /* Prefer to place tasks in a sibling domain */ #define SD_OVERLAP 0x2000 /* sched_domains of this level overlap */ +#define SD_NUMA 0x4000 /* cross-node balancing */ extern int __weak arch_sd_sibiling_asym_packing(void); diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 064a0af..b7e6b6f 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -1631,7 +1631,7 @@ static void __sched_fork(struct task_struct *p) p->node_stamp = 0ULL; p->numa_scan_seq = p->mm ? p->mm->numa_scan_seq : 0; - p->numa_migrate_seq = p->mm ? p->mm->numa_scan_seq - 1 : 0; + p->numa_migrate_seq = 0; p->numa_scan_period = sysctl_numa_balancing_scan_delay; p->numa_preferred_nid = -1; p->numa_work.next = &p->numa_work; @@ -5656,6 +5656,7 @@ sd_numa_init(struct sched_domain_topology_level *tl, int cpu) | 0*SD_SHARE_PKG_RESOURCES | 1*SD_SERIALIZE | 0*SD_PREFER_SIBLING + | 1*SD_NUMA | sd_local_flags(level) , .last_balance = jiffies, diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 3abc651..6ffddca 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -877,6 +877,15 @@ static unsigned int task_scan_max(struct task_struct *p) return max(smin, smax); } +/* + * Once a preferred node is selected the scheduler balancer will prefer moving + * a task to that node for sysctl_numa_balancing_settle_count number of PTE + * scans. This will give the process the chance to accumulate more faults on + * the preferred node but still allow the scheduler to move the task again if + * the nodes CPUs are overloaded. + */ +unsigned int sysctl_numa_balancing_settle_count __read_mostly = 3; + static void task_numa_placement(struct task_struct *p) { int seq, nid, max_nid = -1; @@ -888,6 +897,7 @@ static void task_numa_placement(struct task_struct *p) if (p->numa_scan_seq == seq) return; p->numa_scan_seq = seq; + p->numa_migrate_seq++; p->numa_scan_period_max = task_scan_max(p); /* Find the node with the highest number of faults */ @@ -907,8 +917,10 @@ static void task_numa_placement(struct task_struct *p) } /* Update the tasks preferred node if necessary */ - if (max_faults && max_nid != p->numa_preferred_nid) + if (max_faults && max_nid != p->numa_preferred_nid) { p->numa_preferred_nid = max_nid; + p->numa_migrate_seq = 0; + } } /* @@ -4071,6 +4083,38 @@ task_hot(struct task_struct *p, u64 now, struct sched_domain *sd) return delta < (s64)sysctl_sched_migration_cost; } +#ifdef CONFIG_NUMA_BALANCING +/* Returns true if the destination node has incurred more faults */ +static bool migrate_improves_locality(struct task_struct *p, struct lb_env *env) +{ + int src_nid, dst_nid; + + if (!sched_feat(NUMA_FAVOUR_HIGHER) || !p->numa_faults || + !(env->sd->flags & SD_NUMA)) { + return false; + } + + src_nid = cpu_to_node(env->src_cpu); + dst_nid = cpu_to_node(env->dst_cpu); + + if (src_nid == dst_nid || + p->numa_migrate_seq >= sysctl_numa_balancing_settle_count) + return false; + + if (dst_nid == p->numa_preferred_nid || + p->numa_faults[dst_nid] > p->numa_faults[src_nid]) + return true; + + return false; +} +#else +static inline bool migrate_improves_locality(struct task_struct *p, + struct lb_env *env) +{ + return false; +} +#endif + /* * can_migrate_task - may task p from runqueue rq be migrated to this_cpu? */ @@ -4128,11 +4172,22 @@ int can_migrate_task(struct task_struct *p, struct lb_env *env) /* * Aggressive migration if: - * 1) task is cache cold, or - * 2) too many balance attempts have failed. + * 1) destination numa is preferred + * 2) task is cache cold, or + * 3) too many balance attempts have failed. */ - tsk_cache_hot = task_hot(p, rq_clock_task(env->src_rq), env->sd); + + if (migrate_improves_locality(p, env)) { +#ifdef CONFIG_SCHEDSTATS + if (tsk_cache_hot) { + schedstat_inc(env->sd, lb_hot_gained[env->idle]); + schedstat_inc(p, se.statistics.nr_forced_migrations); + } +#endif + return 1; + } + if (!tsk_cache_hot || env->sd->nr_balance_failed > env->sd->cache_nice_tries) { diff --git a/kernel/sched/features.h b/kernel/sched/features.h index cba5c61..d9278ce 100644 --- a/kernel/sched/features.h +++ b/kernel/sched/features.h @@ -67,4 +67,11 @@ SCHED_FEAT(LB_MIN, false) */ #ifdef CONFIG_NUMA_BALANCING SCHED_FEAT(NUMA, false) + +/* + * NUMA_FAVOUR_HIGHER will favor moving tasks towards nodes where a + * higher number of hinting faults are recorded during active load + * balancing. + */ +SCHED_FEAT(NUMA_FAVOUR_HIGHER, true) #endif diff --git a/kernel/sysctl.c b/kernel/sysctl.c index b2f06f3..42f616a 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -391,6 +391,13 @@ static struct ctl_table kern_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, + { + .procname = "numa_balancing_settle_count", + .data = &sysctl_numa_balancing_settle_count, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, #endif /* CONFIG_NUMA_BALANCING */ #endif /* CONFIG_SCHED_DEBUG */ { -- cgit v0.10.2 From 7a0f308337d11fd5caa9f845c6d08cc5d6067988 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:29:01 +0100 Subject: sched/numa: Resist moving tasks towards nodes with fewer hinting faults Just as "sched: Favour moving tasks towards the preferred node" favours moving tasks towards nodes with a higher number of recorded NUMA hinting faults, this patch resists moving tasks towards nodes with lower faults. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-24-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 6ffddca..8943124 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -4107,12 +4107,43 @@ static bool migrate_improves_locality(struct task_struct *p, struct lb_env *env) return false; } + + +static bool migrate_degrades_locality(struct task_struct *p, struct lb_env *env) +{ + int src_nid, dst_nid; + + if (!sched_feat(NUMA) || !sched_feat(NUMA_RESIST_LOWER)) + return false; + + if (!p->numa_faults || !(env->sd->flags & SD_NUMA)) + return false; + + src_nid = cpu_to_node(env->src_cpu); + dst_nid = cpu_to_node(env->dst_cpu); + + if (src_nid == dst_nid || + p->numa_migrate_seq >= sysctl_numa_balancing_settle_count) + return false; + + if (p->numa_faults[dst_nid] < p->numa_faults[src_nid]) + return true; + + return false; +} + #else static inline bool migrate_improves_locality(struct task_struct *p, struct lb_env *env) { return false; } + +static inline bool migrate_degrades_locality(struct task_struct *p, + struct lb_env *env) +{ + return false; +} #endif /* @@ -4177,6 +4208,8 @@ int can_migrate_task(struct task_struct *p, struct lb_env *env) * 3) too many balance attempts have failed. */ tsk_cache_hot = task_hot(p, rq_clock_task(env->src_rq), env->sd); + if (!tsk_cache_hot) + tsk_cache_hot = migrate_degrades_locality(p, env); if (migrate_improves_locality(p, env)) { #ifdef CONFIG_SCHEDSTATS diff --git a/kernel/sched/features.h b/kernel/sched/features.h index d9278ce..5716929 100644 --- a/kernel/sched/features.h +++ b/kernel/sched/features.h @@ -74,4 +74,12 @@ SCHED_FEAT(NUMA, false) * balancing. */ SCHED_FEAT(NUMA_FAVOUR_HIGHER, true) + +/* + * NUMA_RESIST_LOWER will resist moving tasks towards nodes where a + * lower number of hinting faults have been recorded. As this has + * the potential to prevent a task ever migrating to a new node + * due to CPU overload it is disabled by default. + */ +SCHED_FEAT(NUMA_RESIST_LOWER, false) #endif -- cgit v0.10.2 From e6628d5b0a2979f3e0ee6f7783ede5df50cb9ede Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:29:02 +0100 Subject: sched/numa: Reschedule task on preferred NUMA node once selected A preferred node is selected based on the node the most NUMA hinting faults was incurred on. There is no guarantee that the task is running on that node at the time so this patch rescheules the task to run on the most idle CPU of the selected node when selected. This avoids waiting for the balancer to make a decision. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-25-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/kernel/sched/core.c b/kernel/sched/core.c index b7e6b6f..66b878e 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -4348,6 +4348,25 @@ fail: return ret; } +#ifdef CONFIG_NUMA_BALANCING +/* Migrate current task p to target_cpu */ +int migrate_task_to(struct task_struct *p, int target_cpu) +{ + struct migration_arg arg = { p, target_cpu }; + int curr_cpu = task_cpu(p); + + if (curr_cpu == target_cpu) + return 0; + + if (!cpumask_test_cpu(target_cpu, tsk_cpus_allowed(p))) + return -EINVAL; + + /* TODO: This is not properly updating schedstats */ + + return stop_one_cpu(curr_cpu, migration_cpu_stop, &arg); +} +#endif + /* * migration_cpu_stop - this will be executed by a highprio stopper thread * and performs thread migration by bumping thread off CPU then diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 8943124..8b15e9e 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -886,6 +886,31 @@ static unsigned int task_scan_max(struct task_struct *p) */ unsigned int sysctl_numa_balancing_settle_count __read_mostly = 3; +static unsigned long weighted_cpuload(const int cpu); + + +static int +find_idlest_cpu_node(int this_cpu, int nid) +{ + unsigned long load, min_load = ULONG_MAX; + int i, idlest_cpu = this_cpu; + + BUG_ON(cpu_to_node(this_cpu) == nid); + + rcu_read_lock(); + for_each_cpu(i, cpumask_of_node(nid)) { + load = weighted_cpuload(i); + + if (load < min_load) { + min_load = load; + idlest_cpu = i; + } + } + rcu_read_unlock(); + + return idlest_cpu; +} + static void task_numa_placement(struct task_struct *p) { int seq, nid, max_nid = -1; @@ -916,10 +941,29 @@ static void task_numa_placement(struct task_struct *p) } } - /* Update the tasks preferred node if necessary */ + /* + * Record the preferred node as the node with the most faults, + * requeue the task to be running on the idlest CPU on the + * preferred node and reset the scanning rate to recheck + * the working set placement. + */ if (max_faults && max_nid != p->numa_preferred_nid) { + int preferred_cpu; + + /* + * If the task is not on the preferred node then find the most + * idle CPU to migrate to. + */ + preferred_cpu = task_cpu(p); + if (cpu_to_node(preferred_cpu) != max_nid) { + preferred_cpu = find_idlest_cpu_node(preferred_cpu, + max_nid); + } + + /* Update the preferred nid and migrate task if possible */ p->numa_preferred_nid = max_nid; p->numa_migrate_seq = 0; + migrate_task_to(p, preferred_cpu); } } diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 199099c..66458c9 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -557,6 +557,7 @@ static inline u64 rq_clock_task(struct rq *rq) } #ifdef CONFIG_NUMA_BALANCING +extern int migrate_task_to(struct task_struct *p, int cpu); static inline void task_numa_free(struct task_struct *p) { kfree(p->numa_faults); -- cgit v0.10.2 From ac8e895bd260cb8bb19ade6a3abd44e7abe9a01d Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:29:03 +0100 Subject: sched/numa: Add infrastructure for split shared/private accounting of NUMA hinting faults Ideally it would be possible to distinguish between NUMA hinting faults that are private to a task and those that are shared. This patch prepares infrastructure for separately accounting shared and private faults by allocating the necessary buffers and passing in relevant information. For now, all faults are treated as private and detection will be introduced later. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-26-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/include/linux/sched.h b/include/linux/sched.h index aecdc5a..d946195 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1445,10 +1445,11 @@ struct task_struct { #define tsk_cpus_allowed(tsk) (&(tsk)->cpus_allowed) #ifdef CONFIG_NUMA_BALANCING -extern void task_numa_fault(int node, int pages, bool migrated); +extern void task_numa_fault(int last_node, int node, int pages, bool migrated); extern void set_numabalancing_state(bool enabled); #else -static inline void task_numa_fault(int node, int pages, bool migrated) +static inline void task_numa_fault(int last_node, int node, int pages, + bool migrated) { } static inline void set_numabalancing_state(bool enabled) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 8b15e9e..89eeb89 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -886,6 +886,20 @@ static unsigned int task_scan_max(struct task_struct *p) */ unsigned int sysctl_numa_balancing_settle_count __read_mostly = 3; +static inline int task_faults_idx(int nid, int priv) +{ + return 2 * nid + priv; +} + +static inline unsigned long task_faults(struct task_struct *p, int nid) +{ + if (!p->numa_faults) + return 0; + + return p->numa_faults[task_faults_idx(nid, 0)] + + p->numa_faults[task_faults_idx(nid, 1)]; +} + static unsigned long weighted_cpuload(const int cpu); @@ -928,13 +942,19 @@ static void task_numa_placement(struct task_struct *p) /* Find the node with the highest number of faults */ for_each_online_node(nid) { unsigned long faults; + int priv, i; - /* Decay existing window and copy faults since last scan */ - p->numa_faults[nid] >>= 1; - p->numa_faults[nid] += p->numa_faults_buffer[nid]; - p->numa_faults_buffer[nid] = 0; + for (priv = 0; priv < 2; priv++) { + i = task_faults_idx(nid, priv); - faults = p->numa_faults[nid]; + /* Decay existing window, copy faults since last scan */ + p->numa_faults[i] >>= 1; + p->numa_faults[i] += p->numa_faults_buffer[i]; + p->numa_faults_buffer[i] = 0; + } + + /* Find maximum private faults */ + faults = p->numa_faults[task_faults_idx(nid, 1)]; if (faults > max_faults) { max_faults = faults; max_nid = nid; @@ -970,16 +990,20 @@ static void task_numa_placement(struct task_struct *p) /* * Got a PROT_NONE fault for a page on @node. */ -void task_numa_fault(int node, int pages, bool migrated) +void task_numa_fault(int last_nid, int node, int pages, bool migrated) { struct task_struct *p = current; + int priv; if (!numabalancing_enabled) return; + /* For now, do not attempt to detect private/shared accesses */ + priv = 1; + /* Allocate buffer to track faults on a per-node basis */ if (unlikely(!p->numa_faults)) { - int size = sizeof(*p->numa_faults) * nr_node_ids; + int size = sizeof(*p->numa_faults) * 2 * nr_node_ids; /* numa_faults and numa_faults_buffer share the allocation */ p->numa_faults = kzalloc(size * 2, GFP_KERNEL|__GFP_NOWARN); @@ -987,7 +1011,7 @@ void task_numa_fault(int node, int pages, bool migrated) return; BUG_ON(p->numa_faults_buffer); - p->numa_faults_buffer = p->numa_faults + nr_node_ids; + p->numa_faults_buffer = p->numa_faults + (2 * nr_node_ids); } /* @@ -1005,7 +1029,7 @@ void task_numa_fault(int node, int pages, bool migrated) task_numa_placement(p); - p->numa_faults_buffer[node] += pages; + p->numa_faults_buffer[task_faults_idx(node, priv)] += pages; } static void reset_ptenuma_scan(struct task_struct *p) @@ -4146,7 +4170,7 @@ static bool migrate_improves_locality(struct task_struct *p, struct lb_env *env) return false; if (dst_nid == p->numa_preferred_nid || - p->numa_faults[dst_nid] > p->numa_faults[src_nid]) + task_faults(p, dst_nid) > task_faults(p, src_nid)) return true; return false; @@ -4170,7 +4194,7 @@ static bool migrate_degrades_locality(struct task_struct *p, struct lb_env *env) p->numa_migrate_seq >= sysctl_numa_balancing_settle_count) return false; - if (p->numa_faults[dst_nid] < p->numa_faults[src_nid]) + if (task_faults(p, dst_nid) < task_faults(p, src_nid)) return true; return false; diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 8677dbf..9142167 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -1282,7 +1282,7 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, struct page *page; unsigned long haddr = addr & HPAGE_PMD_MASK; int page_nid = -1, this_nid = numa_node_id(); - int target_nid; + int target_nid, last_nid = -1; bool page_locked; bool migrated = false; @@ -1293,6 +1293,7 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, page = pmd_page(pmd); BUG_ON(is_huge_zero_page(page)); page_nid = page_to_nid(page); + last_nid = page_nid_last(page); count_vm_numa_event(NUMA_HINT_FAULTS); if (page_nid == this_nid) count_vm_numa_event(NUMA_HINT_FAULTS_LOCAL); @@ -1361,7 +1362,7 @@ out: page_unlock_anon_vma_read(anon_vma); if (page_nid != -1) - task_numa_fault(page_nid, HPAGE_PMD_NR, migrated); + task_numa_fault(last_nid, page_nid, HPAGE_PMD_NR, migrated); return 0; } diff --git a/mm/memory.c b/mm/memory.c index ed51f15..24bc9b8 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -3536,6 +3536,7 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, struct page *page = NULL; spinlock_t *ptl; int page_nid = -1; + int last_nid; int target_nid; bool migrated = false; @@ -3566,6 +3567,7 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, } BUG_ON(is_zero_pfn(page_to_pfn(page))); + last_nid = page_nid_last(page); page_nid = page_to_nid(page); target_nid = numa_migrate_prep(page, vma, addr, page_nid); pte_unmap_unlock(ptep, ptl); @@ -3581,7 +3583,7 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, out: if (page_nid != -1) - task_numa_fault(page_nid, 1, migrated); + task_numa_fault(last_nid, page_nid, 1, migrated); return 0; } @@ -3596,6 +3598,7 @@ static int do_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long offset; spinlock_t *ptl; bool numa = false; + int last_nid; spin_lock(&mm->page_table_lock); pmd = *pmdp; @@ -3643,6 +3646,7 @@ static int do_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, if (unlikely(page_mapcount(page) != 1)) continue; + last_nid = page_nid_last(page); page_nid = page_to_nid(page); target_nid = numa_migrate_prep(page, vma, addr, page_nid); pte_unmap_unlock(pte, ptl); @@ -3655,7 +3659,7 @@ static int do_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, } if (page_nid != -1) - task_numa_fault(page_nid, 1, migrated); + task_numa_fault(last_nid, page_nid, 1, migrated); pte = pte_offset_map_lock(mm, pmdp, addr, &ptl); } -- cgit v0.10.2 From 9ff1d9ff3c2c8ab3feaeb2e8056a07ca293f7bde Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:29:04 +0100 Subject: sched/numa: Check current->mm before allocating NUMA faults task_numa_placement checks current->mm but after buffers for faults have already been uselessly allocated. Move the check earlier. [peterz@infradead.org: Identified the problem] Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-27-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 89eeb89..3383079 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -930,8 +930,6 @@ static void task_numa_placement(struct task_struct *p) int seq, nid, max_nid = -1; unsigned long max_faults = 0; - if (!p->mm) /* for example, ksmd faulting in a user's mm */ - return; seq = ACCESS_ONCE(p->mm->numa_scan_seq); if (p->numa_scan_seq == seq) return; @@ -998,6 +996,10 @@ void task_numa_fault(int last_nid, int node, int pages, bool migrated) if (!numabalancing_enabled) return; + /* for example, ksmd faulting in a user's mm */ + if (!p->mm) + return; + /* For now, do not attempt to detect private/shared accesses */ priv = 1; -- cgit v0.10.2 From 1bc115d87dffd1c43bdc3c9c9d1e3a51c195d18e Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:29:05 +0100 Subject: mm: numa: Scan pages with elevated page_mapcount Currently automatic NUMA balancing is unable to distinguish between false shared versus private pages except by ignoring pages with an elevated page_mapcount entirely. This avoids shared pages bouncing between the nodes whose task is using them but that is ignored quite a lot of data. This patch kicks away the training wheels in preparation for adding support for identifying shared/private pages is now in place. The ordering is so that the impact of the shared/private detection can be easily measured. Note that the patch does not migrate shared, file-backed within vmas marked VM_EXEC as these are generally shared library pages. Migrating such pages is not beneficial as there is an expectation they are read-shared between caches and iTLB and iCache pressure is generally low. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-28-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/include/linux/migrate.h b/include/linux/migrate.h index 8d3c57f..f5096b5 100644 --- a/include/linux/migrate.h +++ b/include/linux/migrate.h @@ -90,11 +90,12 @@ static inline int migrate_huge_page_move_mapping(struct address_space *mapping, #endif /* CONFIG_MIGRATION */ #ifdef CONFIG_NUMA_BALANCING -extern int migrate_misplaced_page(struct page *page, int node); -extern int migrate_misplaced_page(struct page *page, int node); +extern int migrate_misplaced_page(struct page *page, + struct vm_area_struct *vma, int node); extern bool migrate_ratelimited(int node); #else -static inline int migrate_misplaced_page(struct page *page, int node) +static inline int migrate_misplaced_page(struct page *page, + struct vm_area_struct *vma, int node) { return -EAGAIN; /* can't migrate now */ } diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 9142167..2a28c2c 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -1484,14 +1484,12 @@ int change_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd, struct page *page = pmd_page(*pmd); /* - * Only check non-shared pages. Do not trap faults - * against the zero page. The read-only data is likely - * to be read-cached on the local CPU cache and it is - * less useful to know about local vs remote hits on - * the zero page. + * Do not trap faults against the zero page. The + * read-only data is likely to be read-cached on the + * local CPU cache and it is less useful to know about + * local vs remote hits on the zero page. */ - if (page_mapcount(page) == 1 && - !is_huge_zero_page(page) && + if (!is_huge_zero_page(page) && !pmd_numa(*pmd)) { entry = pmdp_get_and_clear(mm, addr, pmd); entry = pmd_mknuma(entry); diff --git a/mm/memory.c b/mm/memory.c index 24bc9b8..3e3b4b8 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -3577,7 +3577,7 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, } /* Migrate to the requested node */ - migrated = migrate_misplaced_page(page, target_nid); + migrated = migrate_misplaced_page(page, vma, target_nid); if (migrated) page_nid = target_nid; @@ -3642,16 +3642,13 @@ static int do_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, page = vm_normal_page(vma, addr, pteval); if (unlikely(!page)) continue; - /* only check non-shared pages */ - if (unlikely(page_mapcount(page) != 1)) - continue; last_nid = page_nid_last(page); page_nid = page_to_nid(page); target_nid = numa_migrate_prep(page, vma, addr, page_nid); pte_unmap_unlock(pte, ptl); if (target_nid != -1) { - migrated = migrate_misplaced_page(page, target_nid); + migrated = migrate_misplaced_page(page, vma, target_nid); if (migrated) page_nid = target_nid; } else { diff --git a/mm/migrate.c b/mm/migrate.c index 7bd90d3..fcba2f4 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -1599,7 +1599,8 @@ int numamigrate_isolate_page(pg_data_t *pgdat, struct page *page) * node. Caller is expected to have an elevated reference count on * the page that will be dropped by this function before returning. */ -int migrate_misplaced_page(struct page *page, int node) +int migrate_misplaced_page(struct page *page, struct vm_area_struct *vma, + int node) { pg_data_t *pgdat = NODE_DATA(node); int isolated; @@ -1607,10 +1608,11 @@ int migrate_misplaced_page(struct page *page, int node) LIST_HEAD(migratepages); /* - * Don't migrate pages that are mapped in multiple processes. - * TODO: Handle false sharing detection instead of this hammer + * Don't migrate file pages that are mapped in multiple processes + * with execute permissions as they are probably shared libraries. */ - if (page_mapcount(page) != 1) + if (page_mapcount(page) != 1 && page_is_file_cache(page) && + (vma->vm_flags & VM_EXEC)) goto out; /* @@ -1661,13 +1663,6 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm, int page_lru = page_is_file_cache(page); /* - * Don't migrate pages that are mapped in multiple processes. - * TODO: Handle false sharing detection instead of this hammer - */ - if (page_mapcount(page) != 1) - goto out_dropref; - - /* * Rate-limit the amount of data that is being migrated to a node. * Optimal placement is no good if the memory bus is saturated and * all the time is being spent migrating! diff --git a/mm/mprotect.c b/mm/mprotect.c index 2da33dc..41e0292 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -69,9 +69,7 @@ static unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd, if (last_nid != this_nid) all_same_node = false; - /* only check non-shared pages */ - if (!pte_numa(oldpte) && - page_mapcount(page) == 1) { + if (!pte_numa(oldpte)) { ptent = pte_mknuma(ptent); updated = true; } -- cgit v0.10.2 From 073b5beea735c7e1970686c94ff1f3aaac790a2a Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:29:06 +0100 Subject: sched/numa: Remove check that skips small VMAs task_numa_work skips small VMAs. At the time the logic was to reduce the scanning overhead which was considerable. It is a dubious hack at best. It would make much more sense to cache where faults have been observed and only rescan those regions during subsequent PTE scans. Remove this hack as motivation to do it properly in the future. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-29-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 3383079..862d20d 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1127,10 +1127,6 @@ void task_numa_work(struct callback_head *work) if (!vma_migratable(vma)) continue; - /* Skip small VMAs. They are not likely to be of relevance */ - if (vma->vm_end - vma->vm_start < HPAGE_SIZE) - continue; - do { start = max(start, vma->vm_start); end = ALIGN(start + (pages << PAGE_SHIFT), HPAGE_SIZE); -- cgit v0.10.2 From b795854b1fa70f6aee923ae5df74ff7afeaddcaa Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:29:07 +0100 Subject: sched/numa: Set preferred NUMA node based on number of private faults Ideally it would be possible to distinguish between NUMA hinting faults that are private to a task and those that are shared. If treated identically there is a risk that shared pages bounce between nodes depending on the order they are referenced by tasks. Ultimately what is desirable is that task private pages remain local to the task while shared pages are interleaved between sharing tasks running on different nodes to give good average performance. This is further complicated by THP as even applications that partition their data may not be partitioning on a huge page boundary. To start with, this patch assumes that multi-threaded or multi-process applications partition their data and that in general the private accesses are more important for cpu->memory locality in the general case. Also, no new infrastructure is required to treat private pages properly but interleaving for shared pages requires additional infrastructure. To detect private accesses the pid of the last accessing task is required but the storage requirements are a high. This patch borrows heavily from Ingo Molnar's patch "numa, mm, sched: Implement last-CPU+PID hash tracking" to encode some bits from the last accessing task in the page flags as well as the node information. Collisions will occur but it is better than just depending on the node information. Node information is then used to determine if a page needs to migrate. The PID information is used to detect private/shared accesses. The preferred NUMA node is selected based on where the maximum number of approximately private faults were measured. Shared faults are not taken into consideration for a few reasons. First, if there are many tasks sharing the page then they'll all move towards the same node. The node will be compute overloaded and then scheduled away later only to bounce back again. Alternatively the shared tasks would just bounce around nodes because the fault information is effectively noise. Either way accounting for shared faults the same as private faults can result in lower performance overall. The second reason is based on a hypothetical workload that has a small number of very important, heavily accessed private pages but a large shared array. The shared array would dominate the number of faults and be selected as a preferred node even though it's the wrong decision. The third reason is that multiple threads in a process will race each other to fault the shared page making the fault information unreliable. Signed-off-by: Mel Gorman [ Fix complication error when !NUMA_BALANCING. ] Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-30-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/include/linux/mm.h b/include/linux/mm.h index 8b6e55e..bb412ce 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -581,11 +581,11 @@ static inline pte_t maybe_mkwrite(pte_t pte, struct vm_area_struct *vma) * sets it, so none of the operations on it need to be atomic. */ -/* Page flags: | [SECTION] | [NODE] | ZONE | [LAST_NID] | ... | FLAGS | */ +/* Page flags: | [SECTION] | [NODE] | ZONE | [LAST_NIDPID] | ... | FLAGS | */ #define SECTIONS_PGOFF ((sizeof(unsigned long)*8) - SECTIONS_WIDTH) #define NODES_PGOFF (SECTIONS_PGOFF - NODES_WIDTH) #define ZONES_PGOFF (NODES_PGOFF - ZONES_WIDTH) -#define LAST_NID_PGOFF (ZONES_PGOFF - LAST_NID_WIDTH) +#define LAST_NIDPID_PGOFF (ZONES_PGOFF - LAST_NIDPID_WIDTH) /* * Define the bit shifts to access each section. For non-existent @@ -595,7 +595,7 @@ static inline pte_t maybe_mkwrite(pte_t pte, struct vm_area_struct *vma) #define SECTIONS_PGSHIFT (SECTIONS_PGOFF * (SECTIONS_WIDTH != 0)) #define NODES_PGSHIFT (NODES_PGOFF * (NODES_WIDTH != 0)) #define ZONES_PGSHIFT (ZONES_PGOFF * (ZONES_WIDTH != 0)) -#define LAST_NID_PGSHIFT (LAST_NID_PGOFF * (LAST_NID_WIDTH != 0)) +#define LAST_NIDPID_PGSHIFT (LAST_NIDPID_PGOFF * (LAST_NIDPID_WIDTH != 0)) /* NODE:ZONE or SECTION:ZONE is used to ID a zone for the buddy allocator */ #ifdef NODE_NOT_IN_PAGE_FLAGS @@ -617,7 +617,7 @@ static inline pte_t maybe_mkwrite(pte_t pte, struct vm_area_struct *vma) #define ZONES_MASK ((1UL << ZONES_WIDTH) - 1) #define NODES_MASK ((1UL << NODES_WIDTH) - 1) #define SECTIONS_MASK ((1UL << SECTIONS_WIDTH) - 1) -#define LAST_NID_MASK ((1UL << LAST_NID_WIDTH) - 1) +#define LAST_NIDPID_MASK ((1UL << LAST_NIDPID_WIDTH) - 1) #define ZONEID_MASK ((1UL << ZONEID_SHIFT) - 1) static inline enum zone_type page_zonenum(const struct page *page) @@ -661,48 +661,93 @@ static inline int page_to_nid(const struct page *page) #endif #ifdef CONFIG_NUMA_BALANCING -#ifdef LAST_NID_NOT_IN_PAGE_FLAGS -static inline int page_nid_xchg_last(struct page *page, int nid) +static inline int nid_pid_to_nidpid(int nid, int pid) { - return xchg(&page->_last_nid, nid); + return ((nid & LAST__NID_MASK) << LAST__PID_SHIFT) | (pid & LAST__PID_MASK); } -static inline int page_nid_last(struct page *page) +static inline int nidpid_to_pid(int nidpid) { - return page->_last_nid; + return nidpid & LAST__PID_MASK; } -static inline void page_nid_reset_last(struct page *page) + +static inline int nidpid_to_nid(int nidpid) +{ + return (nidpid >> LAST__PID_SHIFT) & LAST__NID_MASK; +} + +static inline bool nidpid_pid_unset(int nidpid) +{ + return nidpid_to_pid(nidpid) == (-1 & LAST__PID_MASK); +} + +static inline bool nidpid_nid_unset(int nidpid) { - page->_last_nid = -1; + return nidpid_to_nid(nidpid) == (-1 & LAST__NID_MASK); +} + +#ifdef LAST_NIDPID_NOT_IN_PAGE_FLAGS +static inline int page_nidpid_xchg_last(struct page *page, int nid) +{ + return xchg(&page->_last_nidpid, nid); +} + +static inline int page_nidpid_last(struct page *page) +{ + return page->_last_nidpid; +} +static inline void page_nidpid_reset_last(struct page *page) +{ + page->_last_nidpid = -1; } #else -static inline int page_nid_last(struct page *page) +static inline int page_nidpid_last(struct page *page) { - return (page->flags >> LAST_NID_PGSHIFT) & LAST_NID_MASK; + return (page->flags >> LAST_NIDPID_PGSHIFT) & LAST_NIDPID_MASK; } -extern int page_nid_xchg_last(struct page *page, int nid); +extern int page_nidpid_xchg_last(struct page *page, int nidpid); -static inline void page_nid_reset_last(struct page *page) +static inline void page_nidpid_reset_last(struct page *page) { - int nid = (1 << LAST_NID_SHIFT) - 1; + int nidpid = (1 << LAST_NIDPID_SHIFT) - 1; - page->flags &= ~(LAST_NID_MASK << LAST_NID_PGSHIFT); - page->flags |= (nid & LAST_NID_MASK) << LAST_NID_PGSHIFT; + page->flags &= ~(LAST_NIDPID_MASK << LAST_NIDPID_PGSHIFT); + page->flags |= (nidpid & LAST_NIDPID_MASK) << LAST_NIDPID_PGSHIFT; } -#endif /* LAST_NID_NOT_IN_PAGE_FLAGS */ +#endif /* LAST_NIDPID_NOT_IN_PAGE_FLAGS */ #else -static inline int page_nid_xchg_last(struct page *page, int nid) +static inline int page_nidpid_xchg_last(struct page *page, int nidpid) { return page_to_nid(page); } -static inline int page_nid_last(struct page *page) +static inline int page_nidpid_last(struct page *page) { return page_to_nid(page); } -static inline void page_nid_reset_last(struct page *page) +static inline int nidpid_to_nid(int nidpid) +{ + return -1; +} + +static inline int nidpid_to_pid(int nidpid) +{ + return -1; +} + +static inline int nid_pid_to_nidpid(int nid, int pid) +{ + return -1; +} + +static inline bool nidpid_pid_unset(int nidpid) +{ + return 1; +} + +static inline void page_nidpid_reset_last(struct page *page) { } #endif diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index b7adf1d..38a902a 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -174,8 +174,8 @@ struct page { void *shadow; #endif -#ifdef LAST_NID_NOT_IN_PAGE_FLAGS - int _last_nid; +#ifdef LAST_NIDPID_NOT_IN_PAGE_FLAGS + int _last_nidpid; #endif } /* diff --git a/include/linux/page-flags-layout.h b/include/linux/page-flags-layout.h index 93506a1..02bc918 100644 --- a/include/linux/page-flags-layout.h +++ b/include/linux/page-flags-layout.h @@ -38,10 +38,10 @@ * The last is when there is insufficient space in page->flags and a separate * lookup is necessary. * - * No sparsemem or sparsemem vmemmap: | NODE | ZONE | ... | FLAGS | - * " plus space for last_nid: | NODE | ZONE | LAST_NID ... | FLAGS | - * classic sparse with space for node:| SECTION | NODE | ZONE | ... | FLAGS | - * " plus space for last_nid: | SECTION | NODE | ZONE | LAST_NID ... | FLAGS | + * No sparsemem or sparsemem vmemmap: | NODE | ZONE | ... | FLAGS | + * " plus space for last_nidpid: | NODE | ZONE | LAST_NIDPID ... | FLAGS | + * classic sparse with space for node:| SECTION | NODE | ZONE | ... | FLAGS | + * " plus space for last_nidpid: | SECTION | NODE | ZONE | LAST_NIDPID ... | FLAGS | * classic sparse no space for node: | SECTION | ZONE | ... | FLAGS | */ #if defined(CONFIG_SPARSEMEM) && !defined(CONFIG_SPARSEMEM_VMEMMAP) @@ -62,15 +62,21 @@ #endif #ifdef CONFIG_NUMA_BALANCING -#define LAST_NID_SHIFT NODES_SHIFT +#define LAST__PID_SHIFT 8 +#define LAST__PID_MASK ((1 << LAST__PID_SHIFT)-1) + +#define LAST__NID_SHIFT NODES_SHIFT +#define LAST__NID_MASK ((1 << LAST__NID_SHIFT)-1) + +#define LAST_NIDPID_SHIFT (LAST__PID_SHIFT+LAST__NID_SHIFT) #else -#define LAST_NID_SHIFT 0 +#define LAST_NIDPID_SHIFT 0 #endif -#if SECTIONS_WIDTH+ZONES_WIDTH+NODES_SHIFT+LAST_NID_SHIFT <= BITS_PER_LONG - NR_PAGEFLAGS -#define LAST_NID_WIDTH LAST_NID_SHIFT +#if SECTIONS_WIDTH+ZONES_WIDTH+NODES_SHIFT+LAST_NIDPID_SHIFT <= BITS_PER_LONG - NR_PAGEFLAGS +#define LAST_NIDPID_WIDTH LAST_NIDPID_SHIFT #else -#define LAST_NID_WIDTH 0 +#define LAST_NIDPID_WIDTH 0 #endif /* @@ -81,8 +87,8 @@ #define NODE_NOT_IN_PAGE_FLAGS #endif -#if defined(CONFIG_NUMA_BALANCING) && LAST_NID_WIDTH == 0 -#define LAST_NID_NOT_IN_PAGE_FLAGS +#if defined(CONFIG_NUMA_BALANCING) && LAST_NIDPID_WIDTH == 0 +#define LAST_NIDPID_NOT_IN_PAGE_FLAGS #endif #endif /* _LINUX_PAGE_FLAGS_LAYOUT */ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 862d20d..b1de7c5 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -988,7 +988,7 @@ static void task_numa_placement(struct task_struct *p) /* * Got a PROT_NONE fault for a page on @node. */ -void task_numa_fault(int last_nid, int node, int pages, bool migrated) +void task_numa_fault(int last_nidpid, int node, int pages, bool migrated) { struct task_struct *p = current; int priv; @@ -1000,8 +1000,14 @@ void task_numa_fault(int last_nid, int node, int pages, bool migrated) if (!p->mm) return; - /* For now, do not attempt to detect private/shared accesses */ - priv = 1; + /* + * First accesses are treated as private, otherwise consider accesses + * to be private if the accessing pid has not changed + */ + if (!nidpid_pid_unset(last_nidpid)) + priv = ((p->pid & LAST__PID_MASK) == nidpid_to_pid(last_nidpid)); + else + priv = 1; /* Allocate buffer to track faults on a per-node basis */ if (unlikely(!p->numa_faults)) { diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 2a28c2c..0baf0e4 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -1282,7 +1282,7 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, struct page *page; unsigned long haddr = addr & HPAGE_PMD_MASK; int page_nid = -1, this_nid = numa_node_id(); - int target_nid, last_nid = -1; + int target_nid, last_nidpid = -1; bool page_locked; bool migrated = false; @@ -1293,7 +1293,7 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, page = pmd_page(pmd); BUG_ON(is_huge_zero_page(page)); page_nid = page_to_nid(page); - last_nid = page_nid_last(page); + last_nidpid = page_nidpid_last(page); count_vm_numa_event(NUMA_HINT_FAULTS); if (page_nid == this_nid) count_vm_numa_event(NUMA_HINT_FAULTS_LOCAL); @@ -1362,7 +1362,7 @@ out: page_unlock_anon_vma_read(anon_vma); if (page_nid != -1) - task_numa_fault(last_nid, page_nid, HPAGE_PMD_NR, migrated); + task_numa_fault(last_nidpid, page_nid, HPAGE_PMD_NR, migrated); return 0; } @@ -1682,7 +1682,7 @@ static void __split_huge_page_refcount(struct page *page, page_tail->mapping = page->mapping; page_tail->index = page->index + i; - page_nid_xchg_last(page_tail, page_nid_last(page)); + page_nidpid_xchg_last(page_tail, page_nidpid_last(page)); BUG_ON(!PageAnon(page_tail)); BUG_ON(!PageUptodate(page_tail)); diff --git a/mm/memory.c b/mm/memory.c index 3e3b4b8..cc7f206 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -69,8 +69,8 @@ #include "internal.h" -#ifdef LAST_NID_NOT_IN_PAGE_FLAGS -#warning Unfortunate NUMA and NUMA Balancing config, growing page-frame for last_nid. +#ifdef LAST_NIDPID_NOT_IN_PAGE_FLAGS +#warning Unfortunate NUMA and NUMA Balancing config, growing page-frame for last_nidpid. #endif #ifndef CONFIG_NEED_MULTIPLE_NODES @@ -3536,7 +3536,7 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, struct page *page = NULL; spinlock_t *ptl; int page_nid = -1; - int last_nid; + int last_nidpid; int target_nid; bool migrated = false; @@ -3567,7 +3567,7 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, } BUG_ON(is_zero_pfn(page_to_pfn(page))); - last_nid = page_nid_last(page); + last_nidpid = page_nidpid_last(page); page_nid = page_to_nid(page); target_nid = numa_migrate_prep(page, vma, addr, page_nid); pte_unmap_unlock(ptep, ptl); @@ -3583,7 +3583,7 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, out: if (page_nid != -1) - task_numa_fault(last_nid, page_nid, 1, migrated); + task_numa_fault(last_nidpid, page_nid, 1, migrated); return 0; } @@ -3598,7 +3598,7 @@ static int do_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long offset; spinlock_t *ptl; bool numa = false; - int last_nid; + int last_nidpid; spin_lock(&mm->page_table_lock); pmd = *pmdp; @@ -3643,7 +3643,7 @@ static int do_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, if (unlikely(!page)) continue; - last_nid = page_nid_last(page); + last_nidpid = page_nidpid_last(page); page_nid = page_to_nid(page); target_nid = numa_migrate_prep(page, vma, addr, page_nid); pte_unmap_unlock(pte, ptl); @@ -3656,7 +3656,7 @@ static int do_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, } if (page_nid != -1) - task_numa_fault(last_nid, page_nid, 1, migrated); + task_numa_fault(last_nidpid, page_nid, 1, migrated); pte = pte_offset_map_lock(mm, pmdp, addr, &ptl); } diff --git a/mm/mempolicy.c b/mm/mempolicy.c index 0472964..aff1f1e 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -2348,9 +2348,11 @@ int mpol_misplaced(struct page *page, struct vm_area_struct *vma, unsigned long /* Migrate the page towards the node whose CPU is referencing it */ if (pol->flags & MPOL_F_MORON) { - int last_nid; + int last_nidpid; + int this_nidpid; polnid = numa_node_id(); + this_nidpid = nid_pid_to_nidpid(polnid, current->pid); /* * Multi-stage node selection is used in conjunction @@ -2373,8 +2375,8 @@ int mpol_misplaced(struct page *page, struct vm_area_struct *vma, unsigned long * it less likely we act on an unlikely task<->page * relation. */ - last_nid = page_nid_xchg_last(page, polnid); - if (last_nid != polnid) + last_nidpid = page_nidpid_xchg_last(page, this_nidpid); + if (!nidpid_pid_unset(last_nidpid) && nidpid_to_nid(last_nidpid) != polnid) goto out; } diff --git a/mm/migrate.c b/mm/migrate.c index fcba2f4..025d1e3 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -1498,7 +1498,7 @@ static struct page *alloc_misplaced_dst_page(struct page *page, __GFP_NOWARN) & ~GFP_IOFS, 0); if (newpage) - page_nid_xchg_last(newpage, page_nid_last(page)); + page_nidpid_xchg_last(newpage, page_nidpid_last(page)); return newpage; } @@ -1675,7 +1675,7 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm, if (!new_page) goto out_fail; - page_nid_xchg_last(new_page, page_nid_last(page)); + page_nidpid_xchg_last(new_page, page_nidpid_last(page)); isolated = numamigrate_isolate_page(pgdat, page); if (!isolated) { diff --git a/mm/mm_init.c b/mm/mm_init.c index 633c088..467de57 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -71,26 +71,26 @@ void __init mminit_verify_pageflags_layout(void) unsigned long or_mask, add_mask; shift = 8 * sizeof(unsigned long); - width = shift - SECTIONS_WIDTH - NODES_WIDTH - ZONES_WIDTH - LAST_NID_SHIFT; + width = shift - SECTIONS_WIDTH - NODES_WIDTH - ZONES_WIDTH - LAST_NIDPID_SHIFT; mminit_dprintk(MMINIT_TRACE, "pageflags_layout_widths", - "Section %d Node %d Zone %d Lastnid %d Flags %d\n", + "Section %d Node %d Zone %d Lastnidpid %d Flags %d\n", SECTIONS_WIDTH, NODES_WIDTH, ZONES_WIDTH, - LAST_NID_WIDTH, + LAST_NIDPID_WIDTH, NR_PAGEFLAGS); mminit_dprintk(MMINIT_TRACE, "pageflags_layout_shifts", - "Section %d Node %d Zone %d Lastnid %d\n", + "Section %d Node %d Zone %d Lastnidpid %d\n", SECTIONS_SHIFT, NODES_SHIFT, ZONES_SHIFT, - LAST_NID_SHIFT); + LAST_NIDPID_SHIFT); mminit_dprintk(MMINIT_TRACE, "pageflags_layout_pgshifts", - "Section %lu Node %lu Zone %lu Lastnid %lu\n", + "Section %lu Node %lu Zone %lu Lastnidpid %lu\n", (unsigned long)SECTIONS_PGSHIFT, (unsigned long)NODES_PGSHIFT, (unsigned long)ZONES_PGSHIFT, - (unsigned long)LAST_NID_PGSHIFT); + (unsigned long)LAST_NIDPID_PGSHIFT); mminit_dprintk(MMINIT_TRACE, "pageflags_layout_nodezoneid", "Node/Zone ID: %lu -> %lu\n", (unsigned long)(ZONEID_PGOFF + ZONEID_SHIFT), @@ -102,9 +102,9 @@ void __init mminit_verify_pageflags_layout(void) mminit_dprintk(MMINIT_TRACE, "pageflags_layout_nodeflags", "Node not in page flags"); #endif -#ifdef LAST_NID_NOT_IN_PAGE_FLAGS +#ifdef LAST_NIDPID_NOT_IN_PAGE_FLAGS mminit_dprintk(MMINIT_TRACE, "pageflags_layout_nodeflags", - "Last nid not in page flags"); + "Last nidpid not in page flags"); #endif if (SECTIONS_WIDTH) { diff --git a/mm/mmzone.c b/mm/mmzone.c index 2ac0afb..25bb477 100644 --- a/mm/mmzone.c +++ b/mm/mmzone.c @@ -97,20 +97,20 @@ void lruvec_init(struct lruvec *lruvec) INIT_LIST_HEAD(&lruvec->lists[lru]); } -#if defined(CONFIG_NUMA_BALANCING) && !defined(LAST_NID_NOT_IN_PAGE_FLAGS) -int page_nid_xchg_last(struct page *page, int nid) +#if defined(CONFIG_NUMA_BALANCING) && !defined(LAST_NIDPID_NOT_IN_PAGE_FLAGS) +int page_nidpid_xchg_last(struct page *page, int nidpid) { unsigned long old_flags, flags; - int last_nid; + int last_nidpid; do { old_flags = flags = page->flags; - last_nid = page_nid_last(page); + last_nidpid = page_nidpid_last(page); - flags &= ~(LAST_NID_MASK << LAST_NID_PGSHIFT); - flags |= (nid & LAST_NID_MASK) << LAST_NID_PGSHIFT; + flags &= ~(LAST_NIDPID_MASK << LAST_NIDPID_PGSHIFT); + flags |= (nidpid & LAST_NIDPID_MASK) << LAST_NIDPID_PGSHIFT; } while (unlikely(cmpxchg(&page->flags, old_flags, flags) != old_flags)); - return last_nid; + return last_nidpid; } #endif diff --git a/mm/mprotect.c b/mm/mprotect.c index 41e0292..f0b087d 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -37,14 +37,15 @@ static inline pgprot_t pgprot_modify(pgprot_t oldprot, pgprot_t newprot) static unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd, unsigned long addr, unsigned long end, pgprot_t newprot, - int dirty_accountable, int prot_numa, bool *ret_all_same_node) + int dirty_accountable, int prot_numa, bool *ret_all_same_nidpid) { struct mm_struct *mm = vma->vm_mm; pte_t *pte, oldpte; spinlock_t *ptl; unsigned long pages = 0; - bool all_same_node = true; + bool all_same_nidpid = true; int last_nid = -1; + int last_pid = -1; pte = pte_offset_map_lock(mm, pmd, addr, &ptl); arch_enter_lazy_mmu_mode(); @@ -63,11 +64,18 @@ static unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd, page = vm_normal_page(vma, addr, oldpte); if (page) { - int this_nid = page_to_nid(page); + int nidpid = page_nidpid_last(page); + int this_nid = nidpid_to_nid(nidpid); + int this_pid = nidpid_to_pid(nidpid); + if (last_nid == -1) last_nid = this_nid; - if (last_nid != this_nid) - all_same_node = false; + if (last_pid == -1) + last_pid = this_pid; + if (last_nid != this_nid || + last_pid != this_pid) { + all_same_nidpid = false; + } if (!pte_numa(oldpte)) { ptent = pte_mknuma(ptent); @@ -107,7 +115,7 @@ static unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd, arch_leave_lazy_mmu_mode(); pte_unmap_unlock(pte - 1, ptl); - *ret_all_same_node = all_same_node; + *ret_all_same_nidpid = all_same_nidpid; return pages; } @@ -134,7 +142,7 @@ static inline unsigned long change_pmd_range(struct vm_area_struct *vma, pmd_t *pmd; unsigned long next; unsigned long pages = 0; - bool all_same_node; + bool all_same_nidpid; pmd = pmd_offset(pud, addr); do { @@ -158,7 +166,7 @@ static inline unsigned long change_pmd_range(struct vm_area_struct *vma, if (pmd_none_or_clear_bad(pmd)) continue; pages += change_pte_range(vma, pmd, addr, next, newprot, - dirty_accountable, prot_numa, &all_same_node); + dirty_accountable, prot_numa, &all_same_nidpid); /* * If we are changing protections for NUMA hinting faults then @@ -166,7 +174,7 @@ static inline unsigned long change_pmd_range(struct vm_area_struct *vma, * node. This allows a regular PMD to be handled as one fault * and effectively batches the taking of the PTL */ - if (prot_numa && all_same_node) + if (prot_numa && all_same_nidpid) change_pmd_protnuma(vma->vm_mm, addr, pmd); } while (pmd++, addr = next, addr != end); diff --git a/mm/page_alloc.c b/mm/page_alloc.c index dd886fa..89bedd0 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -626,7 +626,7 @@ static inline int free_pages_check(struct page *page) bad_page(page); return 1; } - page_nid_reset_last(page); + page_nidpid_reset_last(page); if (page->flags & PAGE_FLAGS_CHECK_AT_PREP) page->flags &= ~PAGE_FLAGS_CHECK_AT_PREP; return 0; @@ -4015,7 +4015,7 @@ void __meminit memmap_init_zone(unsigned long size, int nid, unsigned long zone, mminit_verify_page_links(page, zone, nid, pfn); init_page_count(page); page_mapcount_reset(page); - page_nid_reset_last(page); + page_nidpid_reset_last(page); SetPageReserved(page); /* * Mark the block movable so that blocks are reserved for -- cgit v0.10.2 From 6fe6b2d6dabf392aceb3ad3a5e859b46a04465c6 Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Mon, 7 Oct 2013 11:29:08 +0100 Subject: sched/numa: Do not migrate memory immediately after switching node The load balancer can move tasks between nodes and does not take NUMA locality into account. With automatic NUMA balancing this may result in the tasks working set being migrated to the new node. However, as the fault buffer will still store faults from the old node the schduler may decide to reset the preferred node and migrate the task back resulting in more migrations. The ideal would be that the scheduler did not migrate tasks with a heavy memory footprint but this may result nodes being overloaded. We could also discard the fault information on task migration but this would still cause all the tasks working set to be migrated. This patch simply avoids migrating the memory for a short time after a task is migrated. Signed-off-by: Rik van Riel Signed-off-by: Mel Gorman Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-31-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 66b878e..9060a7f 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -1631,7 +1631,7 @@ static void __sched_fork(struct task_struct *p) p->node_stamp = 0ULL; p->numa_scan_seq = p->mm ? p->mm->numa_scan_seq : 0; - p->numa_migrate_seq = 0; + p->numa_migrate_seq = 1; p->numa_scan_period = sysctl_numa_balancing_scan_delay; p->numa_preferred_nid = -1; p->numa_work.next = &p->numa_work; diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index b1de7c5..61ec0d4 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -884,7 +884,7 @@ static unsigned int task_scan_max(struct task_struct *p) * the preferred node but still allow the scheduler to move the task again if * the nodes CPUs are overloaded. */ -unsigned int sysctl_numa_balancing_settle_count __read_mostly = 3; +unsigned int sysctl_numa_balancing_settle_count __read_mostly = 4; static inline int task_faults_idx(int nid, int priv) { @@ -980,7 +980,7 @@ static void task_numa_placement(struct task_struct *p) /* Update the preferred nid and migrate task if possible */ p->numa_preferred_nid = max_nid; - p->numa_migrate_seq = 0; + p->numa_migrate_seq = 1; migrate_task_to(p, preferred_cpu); } } @@ -4121,6 +4121,20 @@ static void move_task(struct task_struct *p, struct lb_env *env) set_task_cpu(p, env->dst_cpu); activate_task(env->dst_rq, p, 0); check_preempt_curr(env->dst_rq, p, 0); +#ifdef CONFIG_NUMA_BALANCING + if (p->numa_preferred_nid != -1) { + int src_nid = cpu_to_node(env->src_cpu); + int dst_nid = cpu_to_node(env->dst_cpu); + + /* + * If the load balancer has moved the task then limit + * migrations from taking place in the short term in + * case this is a short-lived migration. + */ + if (src_nid != dst_nid && dst_nid != p->numa_preferred_nid) + p->numa_migrate_seq = 0; + } +#endif } /* diff --git a/mm/mempolicy.c b/mm/mempolicy.c index aff1f1e..196d8da 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -2378,6 +2378,18 @@ int mpol_misplaced(struct page *page, struct vm_area_struct *vma, unsigned long last_nidpid = page_nidpid_xchg_last(page, this_nidpid); if (!nidpid_pid_unset(last_nidpid) && nidpid_to_nid(last_nidpid) != polnid) goto out; + +#ifdef CONFIG_NUMA_BALANCING + /* + * If the scheduler has just moved us away from our + * preferred node, do not bother migrating pages yet. + * This way a short and temporary process migration will + * not cause excessive memory migration. + */ + if (polnid != current->numa_preferred_nid && + !current->numa_migrate_seq) + goto out; +#endif } if (curnid != polnid) -- cgit v0.10.2 From fc3147245d193bd0f57307859c698fa28a20b0fe Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:29:09 +0100 Subject: mm: numa: Limit NUMA scanning to migrate-on-fault VMAs There is a 90% regression observed with a large Oracle performance test on a 4 node system. Profiles indicated that the overhead was due to contention on sp_lock when looking up shared memory policies. These policies do not have the appropriate flags to allow them to be automatically balanced so trapping faults on them is pointless. This patch skips VMAs that do not have MPOL_F_MOF set. [riel@redhat.com: Initial patch] Signed-off-by: Mel Gorman Reported-and-tested-by: Joe Mario Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-32-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/include/linux/mempolicy.h b/include/linux/mempolicy.h index da6716b..ea4d249 100644 --- a/include/linux/mempolicy.h +++ b/include/linux/mempolicy.h @@ -136,6 +136,7 @@ struct mempolicy *mpol_shared_policy_lookup(struct shared_policy *sp, struct mempolicy *get_vma_policy(struct task_struct *tsk, struct vm_area_struct *vma, unsigned long addr); +bool vma_policy_mof(struct task_struct *task, struct vm_area_struct *vma); extern void numa_default_policy(void); extern void numa_policy_init(void); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 61ec0d4..d98175d 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1130,7 +1130,7 @@ void task_numa_work(struct callback_head *work) vma = mm->mmap; } for (; vma; vma = vma->vm_next) { - if (!vma_migratable(vma)) + if (!vma_migratable(vma) || !vma_policy_mof(p, vma)) continue; do { diff --git a/mm/mempolicy.c b/mm/mempolicy.c index 196d8da..0e895a2 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -1679,6 +1679,30 @@ struct mempolicy *get_vma_policy(struct task_struct *task, return pol; } +bool vma_policy_mof(struct task_struct *task, struct vm_area_struct *vma) +{ + struct mempolicy *pol = get_task_policy(task); + if (vma) { + if (vma->vm_ops && vma->vm_ops->get_policy) { + bool ret = false; + + pol = vma->vm_ops->get_policy(vma, vma->vm_start); + if (pol && (pol->flags & MPOL_F_MOF)) + ret = true; + mpol_cond_put(pol); + + return ret; + } else if (vma->vm_policy) { + pol = vma->vm_policy; + } + } + + if (!pol) + return default_policy.flags & MPOL_F_MOF; + + return pol->flags & MPOL_F_MOF; +} + static int apply_policy_zone(struct mempolicy *policy, enum zone_type zone) { enum zone_type dynamic_policy_zone = policy_zone; -- cgit v0.10.2 From 58d081b5082dd85e02ac9a1fb151d97395340a09 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:29:10 +0100 Subject: sched/numa: Avoid overloading CPUs on a preferred NUMA node This patch replaces find_idlest_cpu_node with task_numa_find_cpu. find_idlest_cpu_node has two critical limitations. It does not take the scheduling class into account when calculating the load and it is unsuitable for using when comparing loads between NUMA nodes. task_numa_find_cpu uses similar load calculations to wake_affine() when selecting the least loaded CPU within a scheduling domain common to the source and destimation nodes. It avoids causing CPU load imbalances in the machine by refusing to migrate if the relative load on the target CPU is higher than the source CPU. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-33-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index d98175d..51a7600 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -901,28 +901,114 @@ static inline unsigned long task_faults(struct task_struct *p, int nid) } static unsigned long weighted_cpuload(const int cpu); +static unsigned long source_load(int cpu, int type); +static unsigned long target_load(int cpu, int type); +static unsigned long power_of(int cpu); +static long effective_load(struct task_group *tg, int cpu, long wl, long wg); +struct numa_stats { + unsigned long load; + s64 eff_load; + unsigned long faults; +}; -static int -find_idlest_cpu_node(int this_cpu, int nid) -{ - unsigned long load, min_load = ULONG_MAX; - int i, idlest_cpu = this_cpu; +struct task_numa_env { + struct task_struct *p; - BUG_ON(cpu_to_node(this_cpu) == nid); + int src_cpu, src_nid; + int dst_cpu, dst_nid; - rcu_read_lock(); - for_each_cpu(i, cpumask_of_node(nid)) { - load = weighted_cpuload(i); + struct numa_stats src_stats, dst_stats; - if (load < min_load) { - min_load = load; - idlest_cpu = i; + unsigned long best_load; + int best_cpu; +}; + +static int task_numa_migrate(struct task_struct *p) +{ + int node_cpu = cpumask_first(cpumask_of_node(p->numa_preferred_nid)); + struct task_numa_env env = { + .p = p, + .src_cpu = task_cpu(p), + .src_nid = cpu_to_node(task_cpu(p)), + .dst_cpu = node_cpu, + .dst_nid = p->numa_preferred_nid, + .best_load = ULONG_MAX, + .best_cpu = task_cpu(p), + }; + struct sched_domain *sd; + int cpu; + struct task_group *tg = task_group(p); + unsigned long weight; + bool balanced; + int imbalance_pct, idx = -1; + + /* + * Find the lowest common scheduling domain covering the nodes of both + * the CPU the task is currently running on and the target NUMA node. + */ + rcu_read_lock(); + for_each_domain(env.src_cpu, sd) { + if (cpumask_test_cpu(node_cpu, sched_domain_span(sd))) { + /* + * busy_idx is used for the load decision as it is the + * same index used by the regular load balancer for an + * active cpu. + */ + idx = sd->busy_idx; + imbalance_pct = sd->imbalance_pct; + break; } } rcu_read_unlock(); - return idlest_cpu; + if (WARN_ON_ONCE(idx == -1)) + return 0; + + /* + * XXX the below is mostly nicked from wake_affine(); we should + * see about sharing a bit if at all possible; also it might want + * some per entity weight love. + */ + weight = p->se.load.weight; + env.src_stats.load = source_load(env.src_cpu, idx); + env.src_stats.eff_load = 100 + (imbalance_pct - 100) / 2; + env.src_stats.eff_load *= power_of(env.src_cpu); + env.src_stats.eff_load *= env.src_stats.load + effective_load(tg, env.src_cpu, -weight, -weight); + + for_each_cpu(cpu, cpumask_of_node(env.dst_nid)) { + env.dst_cpu = cpu; + env.dst_stats.load = target_load(cpu, idx); + + /* If the CPU is idle, use it */ + if (!env.dst_stats.load) { + env.best_cpu = cpu; + goto migrate; + } + + /* Otherwise check the target CPU load */ + env.dst_stats.eff_load = 100; + env.dst_stats.eff_load *= power_of(cpu); + env.dst_stats.eff_load *= env.dst_stats.load + effective_load(tg, cpu, weight, weight); + + /* + * Destination is considered balanced if the destination CPU is + * less loaded than the source CPU. Unfortunately there is a + * risk that a task running on a lightly loaded CPU will not + * migrate to its preferred node due to load imbalances. + */ + balanced = (env.dst_stats.eff_load <= env.src_stats.eff_load); + if (!balanced) + continue; + + if (env.dst_stats.eff_load < env.best_load) { + env.best_load = env.dst_stats.eff_load; + env.best_cpu = cpu; + } + } + +migrate: + return migrate_task_to(p, env.best_cpu); } static void task_numa_placement(struct task_struct *p) @@ -966,22 +1052,10 @@ static void task_numa_placement(struct task_struct *p) * the working set placement. */ if (max_faults && max_nid != p->numa_preferred_nid) { - int preferred_cpu; - - /* - * If the task is not on the preferred node then find the most - * idle CPU to migrate to. - */ - preferred_cpu = task_cpu(p); - if (cpu_to_node(preferred_cpu) != max_nid) { - preferred_cpu = find_idlest_cpu_node(preferred_cpu, - max_nid); - } - /* Update the preferred nid and migrate task if possible */ p->numa_preferred_nid = max_nid; p->numa_migrate_seq = 1; - migrate_task_to(p, preferred_cpu); + task_numa_migrate(p); } } @@ -3292,7 +3366,7 @@ static long effective_load(struct task_group *tg, int cpu, long wl, long wg) { struct sched_entity *se = tg->se[cpu]; - if (!tg->parent) /* the trivial, non-cgroup case */ + if (!tg->parent || !wl) /* the trivial, non-cgroup case */ return wl; for_each_sched_entity(se) { @@ -3345,8 +3419,7 @@ static long effective_load(struct task_group *tg, int cpu, long wl, long wg) } #else -static inline unsigned long effective_load(struct task_group *tg, int cpu, - unsigned long wl, unsigned long wg) +static long effective_load(struct task_group *tg, int cpu, long wl, long wg) { return wl; } -- cgit v0.10.2 From 6b9a7460b6baf6c77fc3d23d927ddfc3f3f05bf3 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:29:11 +0100 Subject: sched/numa: Retry migration of tasks to CPU on a preferred node When a preferred node is selected for a tasks there is an attempt to migrate the task to a CPU there. This may fail in which case the task will only migrate if the active load balancer takes action. This may never happen if the conditions are not right. This patch will check at NUMA hinting fault time if another attempt should be made to migrate the task. It will only make an attempt once every five seconds. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-34-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/include/linux/sched.h b/include/linux/sched.h index d946195..14251a8 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1341,6 +1341,7 @@ struct task_struct { int numa_migrate_seq; unsigned int numa_scan_period; unsigned int numa_scan_period_max; + unsigned long numa_migrate_retry; u64 node_stamp; /* migration stamp */ struct callback_head numa_work; diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 51a7600..f84ac3f 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1011,6 +1011,23 @@ migrate: return migrate_task_to(p, env.best_cpu); } +/* Attempt to migrate a task to a CPU on the preferred node. */ +static void numa_migrate_preferred(struct task_struct *p) +{ + /* Success if task is already running on preferred CPU */ + p->numa_migrate_retry = 0; + if (cpu_to_node(task_cpu(p)) == p->numa_preferred_nid) + return; + + /* This task has no NUMA fault statistics yet */ + if (unlikely(p->numa_preferred_nid == -1)) + return; + + /* Otherwise, try migrate to a CPU on the preferred node */ + if (task_numa_migrate(p) != 0) + p->numa_migrate_retry = jiffies + HZ*5; +} + static void task_numa_placement(struct task_struct *p) { int seq, nid, max_nid = -1; @@ -1045,17 +1062,12 @@ static void task_numa_placement(struct task_struct *p) } } - /* - * Record the preferred node as the node with the most faults, - * requeue the task to be running on the idlest CPU on the - * preferred node and reset the scanning rate to recheck - * the working set placement. - */ + /* Preferred node as the node with the most faults */ if (max_faults && max_nid != p->numa_preferred_nid) { /* Update the preferred nid and migrate task if possible */ p->numa_preferred_nid = max_nid; p->numa_migrate_seq = 1; - task_numa_migrate(p); + numa_migrate_preferred(p); } } @@ -1111,6 +1123,10 @@ void task_numa_fault(int last_nidpid, int node, int pages, bool migrated) task_numa_placement(p); + /* Retry task to preferred node migration if it previously failed */ + if (p->numa_migrate_retry && time_after(jiffies, p->numa_migrate_retry)) + numa_migrate_preferred(p); + p->numa_faults_buffer[task_faults_idx(node, priv)] += pages; } -- cgit v0.10.2 From 06ea5e035b4e66cc77790457a89fc7e368060c4b Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Mon, 7 Oct 2013 11:29:12 +0100 Subject: sched/numa: Increment numa_migrate_seq when task runs in correct location When a task is already running on its preferred node, increment numa_migrate_seq to indicate that the task is settled if migration is temporarily disabled, and memory should migrate towards it. Signed-off-by: Rik van Riel [ Only increment migrate_seq if migration temporarily disabled. ] Signed-off-by: Mel Gorman Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-35-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index f84ac3f..de9b4d8 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1016,8 +1016,16 @@ static void numa_migrate_preferred(struct task_struct *p) { /* Success if task is already running on preferred CPU */ p->numa_migrate_retry = 0; - if (cpu_to_node(task_cpu(p)) == p->numa_preferred_nid) + if (cpu_to_node(task_cpu(p)) == p->numa_preferred_nid) { + /* + * If migration is temporarily disabled due to a task migration + * then re-enable it now as the task is running on its + * preferred node and memory should migrate locally + */ + if (!p->numa_migrate_seq) + p->numa_migrate_seq++; return; + } /* This task has no NUMA fault statistics yet */ if (unlikely(p->numa_preferred_nid == -1)) -- cgit v0.10.2 From 4591ce4f2d22dc9de7a6719161ce409b5fd1caac Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:29:13 +0100 Subject: sched/numa: Do not trap hinting faults for shared libraries NUMA hinting faults will not migrate a shared executable page mapped by multiple processes on the grounds that the data is probably in the CPU cache already and the page may just bounce between tasks running on multipl nodes. Even if the migration is avoided, there is still the overhead of trapping the fault, updating the statistics, making scheduler placement decisions based on the information etc. If we are never going to migrate the page, it is overhead for no gain and worse a process may be placed on a sub-optimal node for shared executable pages. This patch avoids trapping faults for shared libraries entirely. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-36-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index de9b4d8..fbc0c84 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1231,6 +1231,16 @@ void task_numa_work(struct callback_head *work) if (!vma_migratable(vma) || !vma_policy_mof(p, vma)) continue; + /* + * Shared library pages mapped by multiple processes are not + * migrated as it is expected they are cache replicated. Avoid + * hinting faults in read-only file-backed mappings or the vdso + * as migrating the pages will be of marginal benefit. + */ + if (!vma->vm_mm || + (vma->vm_file && (vma->vm_flags & (VM_READ|VM_WRITE)) == (VM_READ))) + continue; + do { start = max(start, vma->vm_start); end = ALIGN(start + (pages << PAGE_SHIFT), HPAGE_SIZE); -- cgit v0.10.2 From 25cbbef1924299249756bc4030fcb2436c019813 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:29:14 +0100 Subject: mm: numa: Trap pmd hinting faults only if we would otherwise trap PTE faults Base page PMD faulting is meant to batch handle NUMA hinting faults from PTEs. However, even is no PTE faults would ever be handled within a range the kernel still traps PMD hinting faults. This patch avoids the overhead. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-37-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/mm/mprotect.c b/mm/mprotect.c index f0b087d..5aae390 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -146,6 +146,8 @@ static inline unsigned long change_pmd_range(struct vm_area_struct *vma, pmd = pmd_offset(pud, addr); do { + unsigned long this_pages; + next = pmd_addr_end(addr, end); if (pmd_trans_huge(*pmd)) { if (next - addr != HPAGE_PMD_SIZE) @@ -165,8 +167,9 @@ static inline unsigned long change_pmd_range(struct vm_area_struct *vma, } if (pmd_none_or_clear_bad(pmd)) continue; - pages += change_pte_range(vma, pmd, addr, next, newprot, + this_pages = change_pte_range(vma, pmd, addr, next, newprot, dirty_accountable, prot_numa, &all_same_nidpid); + pages += this_pages; /* * If we are changing protections for NUMA hinting faults then @@ -174,7 +177,7 @@ static inline unsigned long change_pmd_range(struct vm_area_struct *vma, * node. This allows a regular PMD to be handled as one fault * and effectively batches the taking of the PTL */ - if (prot_numa && all_same_nidpid) + if (prot_numa && this_pages && all_same_nidpid) change_pmd_protnuma(vma->vm_mm, addr, pmd); } while (pmd++, addr = next, addr != end); -- cgit v0.10.2 From 1be0bd77c5dd7c903f46abf52f9a3650face3c1d Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 7 Oct 2013 11:29:15 +0100 Subject: stop_machine: Introduce stop_two_cpus() Introduce stop_two_cpus() in order to allow controlled swapping of two tasks. It repurposes the stop_machine() state machine but only stops the two cpus which we can do with on-stack structures and avoid machine wide synchronization issues. The ordering of CPUs is important to avoid deadlocks. If unordered then two cpus calling stop_two_cpus on each other simultaneously would attempt to queue in the opposite order on each CPU causing an AB-BA style deadlock. By always having the lowest number CPU doing the queueing of works, we can guarantee that works are always queued in the same order, and deadlocks are avoided. Signed-off-by: Peter Zijlstra [ Implemented deadlock avoidance. ] Signed-off-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Mel Gorman Link: http://lkml.kernel.org/r/1381141781-10992-38-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/include/linux/stop_machine.h b/include/linux/stop_machine.h index 3b5e910..d2abbdb 100644 --- a/include/linux/stop_machine.h +++ b/include/linux/stop_machine.h @@ -28,6 +28,7 @@ struct cpu_stop_work { }; int stop_one_cpu(unsigned int cpu, cpu_stop_fn_t fn, void *arg); +int stop_two_cpus(unsigned int cpu1, unsigned int cpu2, cpu_stop_fn_t fn, void *arg); void stop_one_cpu_nowait(unsigned int cpu, cpu_stop_fn_t fn, void *arg, struct cpu_stop_work *work_buf); int stop_cpus(const struct cpumask *cpumask, cpu_stop_fn_t fn, void *arg); diff --git a/kernel/stop_machine.c b/kernel/stop_machine.c index c09f295..32a6c44 100644 --- a/kernel/stop_machine.c +++ b/kernel/stop_machine.c @@ -115,6 +115,166 @@ int stop_one_cpu(unsigned int cpu, cpu_stop_fn_t fn, void *arg) return done.executed ? done.ret : -ENOENT; } +/* This controls the threads on each CPU. */ +enum multi_stop_state { + /* Dummy starting state for thread. */ + MULTI_STOP_NONE, + /* Awaiting everyone to be scheduled. */ + MULTI_STOP_PREPARE, + /* Disable interrupts. */ + MULTI_STOP_DISABLE_IRQ, + /* Run the function */ + MULTI_STOP_RUN, + /* Exit */ + MULTI_STOP_EXIT, +}; + +struct multi_stop_data { + int (*fn)(void *); + void *data; + /* Like num_online_cpus(), but hotplug cpu uses us, so we need this. */ + unsigned int num_threads; + const struct cpumask *active_cpus; + + enum multi_stop_state state; + atomic_t thread_ack; +}; + +static void set_state(struct multi_stop_data *msdata, + enum multi_stop_state newstate) +{ + /* Reset ack counter. */ + atomic_set(&msdata->thread_ack, msdata->num_threads); + smp_wmb(); + msdata->state = newstate; +} + +/* Last one to ack a state moves to the next state. */ +static void ack_state(struct multi_stop_data *msdata) +{ + if (atomic_dec_and_test(&msdata->thread_ack)) + set_state(msdata, msdata->state + 1); +} + +/* This is the cpu_stop function which stops the CPU. */ +static int multi_cpu_stop(void *data) +{ + struct multi_stop_data *msdata = data; + enum multi_stop_state curstate = MULTI_STOP_NONE; + int cpu = smp_processor_id(), err = 0; + unsigned long flags; + bool is_active; + + /* + * When called from stop_machine_from_inactive_cpu(), irq might + * already be disabled. Save the state and restore it on exit. + */ + local_save_flags(flags); + + if (!msdata->active_cpus) + is_active = cpu == cpumask_first(cpu_online_mask); + else + is_active = cpumask_test_cpu(cpu, msdata->active_cpus); + + /* Simple state machine */ + do { + /* Chill out and ensure we re-read multi_stop_state. */ + cpu_relax(); + if (msdata->state != curstate) { + curstate = msdata->state; + switch (curstate) { + case MULTI_STOP_DISABLE_IRQ: + local_irq_disable(); + hard_irq_disable(); + break; + case MULTI_STOP_RUN: + if (is_active) + err = msdata->fn(msdata->data); + break; + default: + break; + } + ack_state(msdata); + } + } while (curstate != MULTI_STOP_EXIT); + + local_irq_restore(flags); + return err; +} + +struct irq_cpu_stop_queue_work_info { + int cpu1; + int cpu2; + struct cpu_stop_work *work1; + struct cpu_stop_work *work2; +}; + +/* + * This function is always run with irqs and preemption disabled. + * This guarantees that both work1 and work2 get queued, before + * our local migrate thread gets the chance to preempt us. + */ +static void irq_cpu_stop_queue_work(void *arg) +{ + struct irq_cpu_stop_queue_work_info *info = arg; + cpu_stop_queue_work(info->cpu1, info->work1); + cpu_stop_queue_work(info->cpu2, info->work2); +} + +/** + * stop_two_cpus - stops two cpus + * @cpu1: the cpu to stop + * @cpu2: the other cpu to stop + * @fn: function to execute + * @arg: argument to @fn + * + * Stops both the current and specified CPU and runs @fn on one of them. + * + * returns when both are completed. + */ +int stop_two_cpus(unsigned int cpu1, unsigned int cpu2, cpu_stop_fn_t fn, void *arg) +{ + int call_cpu; + struct cpu_stop_done done; + struct cpu_stop_work work1, work2; + struct irq_cpu_stop_queue_work_info call_args; + struct multi_stop_data msdata = { + .fn = fn, + .data = arg, + .num_threads = 2, + .active_cpus = cpumask_of(cpu1), + }; + + work1 = work2 = (struct cpu_stop_work){ + .fn = multi_cpu_stop, + .arg = &msdata, + .done = &done + }; + + call_args = (struct irq_cpu_stop_queue_work_info){ + .cpu1 = cpu1, + .cpu2 = cpu2, + .work1 = &work1, + .work2 = &work2, + }; + + cpu_stop_init_done(&done, 2); + set_state(&msdata, MULTI_STOP_PREPARE); + + /* + * Queuing needs to be done by the lowest numbered CPU, to ensure + * that works are always queued in the same order on every CPU. + * This prevents deadlocks. + */ + call_cpu = min(cpu1, cpu2); + + smp_call_function_single(call_cpu, &irq_cpu_stop_queue_work, + &call_args, 0); + + wait_for_completion(&done.completion); + return done.executed ? done.ret : -ENOENT; +} + /** * stop_one_cpu_nowait - stop a cpu but don't wait for completion * @cpu: cpu to stop @@ -359,98 +519,14 @@ early_initcall(cpu_stop_init); #ifdef CONFIG_STOP_MACHINE -/* This controls the threads on each CPU. */ -enum stopmachine_state { - /* Dummy starting state for thread. */ - STOPMACHINE_NONE, - /* Awaiting everyone to be scheduled. */ - STOPMACHINE_PREPARE, - /* Disable interrupts. */ - STOPMACHINE_DISABLE_IRQ, - /* Run the function */ - STOPMACHINE_RUN, - /* Exit */ - STOPMACHINE_EXIT, -}; - -struct stop_machine_data { - int (*fn)(void *); - void *data; - /* Like num_online_cpus(), but hotplug cpu uses us, so we need this. */ - unsigned int num_threads; - const struct cpumask *active_cpus; - - enum stopmachine_state state; - atomic_t thread_ack; -}; - -static void set_state(struct stop_machine_data *smdata, - enum stopmachine_state newstate) -{ - /* Reset ack counter. */ - atomic_set(&smdata->thread_ack, smdata->num_threads); - smp_wmb(); - smdata->state = newstate; -} - -/* Last one to ack a state moves to the next state. */ -static void ack_state(struct stop_machine_data *smdata) -{ - if (atomic_dec_and_test(&smdata->thread_ack)) - set_state(smdata, smdata->state + 1); -} - -/* This is the cpu_stop function which stops the CPU. */ -static int stop_machine_cpu_stop(void *data) -{ - struct stop_machine_data *smdata = data; - enum stopmachine_state curstate = STOPMACHINE_NONE; - int cpu = smp_processor_id(), err = 0; - unsigned long flags; - bool is_active; - - /* - * When called from stop_machine_from_inactive_cpu(), irq might - * already be disabled. Save the state and restore it on exit. - */ - local_save_flags(flags); - - if (!smdata->active_cpus) - is_active = cpu == cpumask_first(cpu_online_mask); - else - is_active = cpumask_test_cpu(cpu, smdata->active_cpus); - - /* Simple state machine */ - do { - /* Chill out and ensure we re-read stopmachine_state. */ - cpu_relax(); - if (smdata->state != curstate) { - curstate = smdata->state; - switch (curstate) { - case STOPMACHINE_DISABLE_IRQ: - local_irq_disable(); - hard_irq_disable(); - break; - case STOPMACHINE_RUN: - if (is_active) - err = smdata->fn(smdata->data); - break; - default: - break; - } - ack_state(smdata); - } - } while (curstate != STOPMACHINE_EXIT); - - local_irq_restore(flags); - return err; -} - int __stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus) { - struct stop_machine_data smdata = { .fn = fn, .data = data, - .num_threads = num_online_cpus(), - .active_cpus = cpus }; + struct multi_stop_data msdata = { + .fn = fn, + .data = data, + .num_threads = num_online_cpus(), + .active_cpus = cpus, + }; if (!stop_machine_initialized) { /* @@ -461,7 +537,7 @@ int __stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus) unsigned long flags; int ret; - WARN_ON_ONCE(smdata.num_threads != 1); + WARN_ON_ONCE(msdata.num_threads != 1); local_irq_save(flags); hard_irq_disable(); @@ -472,8 +548,8 @@ int __stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus) } /* Set the initial state and stop all online cpus. */ - set_state(&smdata, STOPMACHINE_PREPARE); - return stop_cpus(cpu_online_mask, stop_machine_cpu_stop, &smdata); + set_state(&msdata, MULTI_STOP_PREPARE); + return stop_cpus(cpu_online_mask, multi_cpu_stop, &msdata); } int stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus) @@ -513,25 +589,25 @@ EXPORT_SYMBOL_GPL(stop_machine); int stop_machine_from_inactive_cpu(int (*fn)(void *), void *data, const struct cpumask *cpus) { - struct stop_machine_data smdata = { .fn = fn, .data = data, + struct multi_stop_data msdata = { .fn = fn, .data = data, .active_cpus = cpus }; struct cpu_stop_done done; int ret; /* Local CPU must be inactive and CPU hotplug in progress. */ BUG_ON(cpu_active(raw_smp_processor_id())); - smdata.num_threads = num_active_cpus() + 1; /* +1 for local */ + msdata.num_threads = num_active_cpus() + 1; /* +1 for local */ /* No proper task established and can't sleep - busy wait for lock. */ while (!mutex_trylock(&stop_cpus_mutex)) cpu_relax(); /* Schedule work on other CPUs and execute directly for local CPU */ - set_state(&smdata, STOPMACHINE_PREPARE); + set_state(&msdata, MULTI_STOP_PREPARE); cpu_stop_init_done(&done, num_active_cpus()); - queue_stop_cpus_work(cpu_active_mask, stop_machine_cpu_stop, &smdata, + queue_stop_cpus_work(cpu_active_mask, multi_cpu_stop, &msdata, &done); - ret = stop_machine_cpu_stop(&smdata); + ret = multi_cpu_stop(&msdata); /* Busy wait for completion. */ while (!completion_done(&done.completion)) -- cgit v0.10.2 From ac66f5477239ebd3c4e2cbf2f591ef387aa09884 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 7 Oct 2013 11:29:16 +0100 Subject: sched/numa: Introduce migrate_swap() Use the new stop_two_cpus() to implement migrate_swap(), a function that flips two tasks between their respective cpus. I'm fairly sure there's a less crude way than employing the stop_two_cpus() method, but everything I tried either got horribly fragile and/or complex. So keep it simple for now. The notable detail is how we 'migrate' tasks that aren't runnable anymore. We'll make it appear like we migrated them before they went to sleep. The sole difference is the previous cpu in the wakeup path, so we override this. Signed-off-by: Peter Zijlstra Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Mel Gorman Link: http://lkml.kernel.org/r/1381141781-10992-39-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/include/linux/sched.h b/include/linux/sched.h index 14251a8..b661979 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1043,6 +1043,8 @@ struct task_struct { struct task_struct *last_wakee; unsigned long wakee_flips; unsigned long wakee_flip_decay_ts; + + int wake_cpu; #endif int on_rq; diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 9060a7f..32a2b29 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -1013,6 +1013,102 @@ void set_task_cpu(struct task_struct *p, unsigned int new_cpu) __set_task_cpu(p, new_cpu); } +static void __migrate_swap_task(struct task_struct *p, int cpu) +{ + if (p->on_rq) { + struct rq *src_rq, *dst_rq; + + src_rq = task_rq(p); + dst_rq = cpu_rq(cpu); + + deactivate_task(src_rq, p, 0); + set_task_cpu(p, cpu); + activate_task(dst_rq, p, 0); + check_preempt_curr(dst_rq, p, 0); + } else { + /* + * Task isn't running anymore; make it appear like we migrated + * it before it went to sleep. This means on wakeup we make the + * previous cpu our targer instead of where it really is. + */ + p->wake_cpu = cpu; + } +} + +struct migration_swap_arg { + struct task_struct *src_task, *dst_task; + int src_cpu, dst_cpu; +}; + +static int migrate_swap_stop(void *data) +{ + struct migration_swap_arg *arg = data; + struct rq *src_rq, *dst_rq; + int ret = -EAGAIN; + + src_rq = cpu_rq(arg->src_cpu); + dst_rq = cpu_rq(arg->dst_cpu); + + double_rq_lock(src_rq, dst_rq); + if (task_cpu(arg->dst_task) != arg->dst_cpu) + goto unlock; + + if (task_cpu(arg->src_task) != arg->src_cpu) + goto unlock; + + if (!cpumask_test_cpu(arg->dst_cpu, tsk_cpus_allowed(arg->src_task))) + goto unlock; + + if (!cpumask_test_cpu(arg->src_cpu, tsk_cpus_allowed(arg->dst_task))) + goto unlock; + + __migrate_swap_task(arg->src_task, arg->dst_cpu); + __migrate_swap_task(arg->dst_task, arg->src_cpu); + + ret = 0; + +unlock: + double_rq_unlock(src_rq, dst_rq); + + return ret; +} + +/* + * Cross migrate two tasks + */ +int migrate_swap(struct task_struct *cur, struct task_struct *p) +{ + struct migration_swap_arg arg; + int ret = -EINVAL; + + get_online_cpus(); + + arg = (struct migration_swap_arg){ + .src_task = cur, + .src_cpu = task_cpu(cur), + .dst_task = p, + .dst_cpu = task_cpu(p), + }; + + if (arg.src_cpu == arg.dst_cpu) + goto out; + + if (!cpu_active(arg.src_cpu) || !cpu_active(arg.dst_cpu)) + goto out; + + if (!cpumask_test_cpu(arg.dst_cpu, tsk_cpus_allowed(arg.src_task))) + goto out; + + if (!cpumask_test_cpu(arg.src_cpu, tsk_cpus_allowed(arg.dst_task))) + goto out; + + ret = stop_two_cpus(arg.dst_cpu, arg.src_cpu, migrate_swap_stop, &arg); + +out: + put_online_cpus(); + return ret; +} + struct migration_arg { struct task_struct *task; int dest_cpu; @@ -1232,9 +1328,9 @@ out: * The caller (fork, wakeup) owns p->pi_lock, ->cpus_allowed is stable. */ static inline -int select_task_rq(struct task_struct *p, int sd_flags, int wake_flags) +int select_task_rq(struct task_struct *p, int cpu, int sd_flags, int wake_flags) { - int cpu = p->sched_class->select_task_rq(p, sd_flags, wake_flags); + cpu = p->sched_class->select_task_rq(p, cpu, sd_flags, wake_flags); /* * In order not to call set_task_cpu() on a blocking task we need @@ -1518,7 +1614,7 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags) if (p->sched_class->task_waking) p->sched_class->task_waking(p); - cpu = select_task_rq(p, SD_BALANCE_WAKE, wake_flags); + cpu = select_task_rq(p, p->wake_cpu, SD_BALANCE_WAKE, wake_flags); if (task_cpu(p) != cpu) { wake_flags |= WF_MIGRATED; set_task_cpu(p, cpu); @@ -1752,7 +1848,7 @@ void wake_up_new_task(struct task_struct *p) * - cpus_allowed can change in the fork path * - any previously selected cpu might disappear through hotplug */ - set_task_cpu(p, select_task_rq(p, SD_BALANCE_FORK, 0)); + set_task_cpu(p, select_task_rq(p, task_cpu(p), SD_BALANCE_FORK, 0)); #endif /* Initialize new task's runnable average */ @@ -2080,7 +2176,7 @@ void sched_exec(void) int dest_cpu; raw_spin_lock_irqsave(&p->pi_lock, flags); - dest_cpu = p->sched_class->select_task_rq(p, SD_BALANCE_EXEC, 0); + dest_cpu = p->sched_class->select_task_rq(p, task_cpu(p), SD_BALANCE_EXEC, 0); if (dest_cpu == smp_processor_id()) goto unlock; diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index fbc0c84..b1e5061 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -3706,11 +3706,10 @@ done: * preempt must be disabled. */ static int -select_task_rq_fair(struct task_struct *p, int sd_flag, int wake_flags) +select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_flags) { struct sched_domain *tmp, *affine_sd = NULL, *sd = NULL; int cpu = smp_processor_id(); - int prev_cpu = task_cpu(p); int new_cpu = cpu; int want_affine = 0; int sync = wake_flags & WF_SYNC; diff --git a/kernel/sched/idle_task.c b/kernel/sched/idle_task.c index d8da010..516c3d9 100644 --- a/kernel/sched/idle_task.c +++ b/kernel/sched/idle_task.c @@ -9,7 +9,7 @@ #ifdef CONFIG_SMP static int -select_task_rq_idle(struct task_struct *p, int sd_flag, int flags) +select_task_rq_idle(struct task_struct *p, int cpu, int sd_flag, int flags) { return task_cpu(p); /* IDLE tasks as never migrated */ } diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index ceebfba..e9304cd 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -1169,13 +1169,10 @@ static void yield_task_rt(struct rq *rq) static int find_lowest_rq(struct task_struct *task); static int -select_task_rq_rt(struct task_struct *p, int sd_flag, int flags) +select_task_rq_rt(struct task_struct *p, int cpu, int sd_flag, int flags) { struct task_struct *curr; struct rq *rq; - int cpu; - - cpu = task_cpu(p); if (p->nr_cpus_allowed == 1) goto out; diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 66458c9..4dc92d0 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -558,6 +558,7 @@ static inline u64 rq_clock_task(struct rq *rq) #ifdef CONFIG_NUMA_BALANCING extern int migrate_task_to(struct task_struct *p, int cpu); +extern int migrate_swap(struct task_struct *, struct task_struct *); static inline void task_numa_free(struct task_struct *p) { kfree(p->numa_faults); @@ -736,6 +737,7 @@ static inline void __set_task_cpu(struct task_struct *p, unsigned int cpu) */ smp_wmb(); task_thread_info(p)->cpu = cpu; + p->wake_cpu = cpu; #endif } @@ -991,7 +993,7 @@ struct sched_class { void (*put_prev_task) (struct rq *rq, struct task_struct *p); #ifdef CONFIG_SMP - int (*select_task_rq)(struct task_struct *p, int sd_flag, int flags); + int (*select_task_rq)(struct task_struct *p, int task_cpu, int sd_flag, int flags); void (*migrate_task_rq)(struct task_struct *p, int next_cpu); void (*pre_schedule) (struct rq *this_rq, struct task_struct *task); diff --git a/kernel/sched/stop_task.c b/kernel/sched/stop_task.c index e08fbee..47197de 100644 --- a/kernel/sched/stop_task.c +++ b/kernel/sched/stop_task.c @@ -11,7 +11,7 @@ #ifdef CONFIG_SMP static int -select_task_rq_stop(struct task_struct *p, int sd_flag, int flags) +select_task_rq_stop(struct task_struct *p, int cpu, int sd_flag, int flags) { return task_cpu(p); /* stop tasks as never migrate */ } -- cgit v0.10.2 From 75dab1bfbbaa54c4e8cdca0f46b4ed65492559c6 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 8 Oct 2013 22:35:37 +0200 Subject: spi: spi-au1550: replace platform_driver_probe to support deferred probing Subsystems like pinctrl and gpio rightfully make use of deferred probing at core level. Now, deferred drivers won't be retried if they don't have a .probe function specified in the driver struct. Fix this driver to have that, so the devices it supports won't get lost in a deferred probe. Signed-off-by: Wolfram Sang Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-au1550.c b/drivers/spi/spi-au1550.c index 1d00d9b3..313dd49 100644 --- a/drivers/spi/spi-au1550.c +++ b/drivers/spi/spi-au1550.c @@ -985,6 +985,7 @@ static int au1550_spi_remove(struct platform_device *pdev) MODULE_ALIAS("platform:au1550-spi"); static struct platform_driver au1550_spi_drv = { + .probe = au1550_spi_probe, .remove = au1550_spi_remove, .driver = { .name = "au1550-spi", @@ -1004,7 +1005,7 @@ static int __init au1550_spi_init(void) printk(KERN_ERR "au1550-spi: cannot add memory" "dbdma device\n"); } - return platform_driver_probe(&au1550_spi_drv, au1550_spi_probe); + return platform_driver_register(&au1550_spi_drv); } module_init(au1550_spi_init); -- cgit v0.10.2 From 6a009e8d886be476d7e6ea978c27c9517c449d3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 8 Oct 2013 20:49:59 +0200 Subject: spi: efm32: drop unused struct and fix size check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The variable efm32_spi_pdata_default origins from an earlier revision of the patch introducing the driver, its use was dropped because of review comments but I forgot to also drop the variable itself. Signed-off-by: Uwe Kleine-König Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-efm32.c b/drivers/spi/spi-efm32.c index 5ce61e5..d4d3cc5 100644 --- a/drivers/spi/spi-efm32.c +++ b/drivers/spi/spi-efm32.c @@ -280,10 +280,6 @@ static irqreturn_t efm32_spi_txirq(int irq, void *data) return IRQ_HANDLED; } -static const struct efm32_spi_pdata efm32_spi_pdata_default = { - .location = 1, -}; - static u32 efm32_spi_get_configured_location(struct efm32_spi_ddata *ddata) { u32 reg = efm32_spi_read32(ddata, REG_ROUTE); @@ -387,7 +383,7 @@ static int efm32_spi_probe(struct platform_device *pdev) goto err; } - if (resource_size(res) < 60) { + if (resource_size(res) < 0x60) { ret = -EINVAL; dev_err(&pdev->dev, "memory resource too small\n"); goto err; -- cgit v0.10.2 From 010c51e189c6a37da2908d27c93e73584f6e8fa8 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Tue, 8 Oct 2013 15:30:21 +0800 Subject: pinctrl: pinctrl-adi2: Remove nested lock+irqsave that resue flags. Also avoid use NULL pointer in error message. v2-changes: - use port pinter only after checking Signed-off-by: Sonic Zhang Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/pinctrl-adi2.c b/drivers/pinctrl/pinctrl-adi2.c index 8089fda..a74e6f4 100644 --- a/drivers/pinctrl/pinctrl-adi2.c +++ b/drivers/pinctrl/pinctrl-adi2.c @@ -247,7 +247,7 @@ static void adi_gpio_ack_irq(struct irq_data *d) unsigned pintbit = hwirq_to_pintbit(port, d->hwirq); spin_lock_irqsave(&port->lock, flags); - spin_lock_irqsave(&port->pint->lock, flags); + spin_lock(&port->pint->lock); if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH) { if (readl(®s->invert_set) & pintbit) @@ -258,7 +258,7 @@ static void adi_gpio_ack_irq(struct irq_data *d) writel(pintbit, ®s->request); - spin_unlock_irqrestore(&port->pint->lock, flags); + spin_unlock(&port->pint->lock); spin_unlock_irqrestore(&port->lock, flags); } @@ -270,7 +270,7 @@ static void adi_gpio_mask_ack_irq(struct irq_data *d) unsigned pintbit = hwirq_to_pintbit(port, d->hwirq); spin_lock_irqsave(&port->lock, flags); - spin_lock_irqsave(&port->pint->lock, flags); + spin_lock(&port->pint->lock); if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH) { if (readl(®s->invert_set) & pintbit) @@ -282,7 +282,7 @@ static void adi_gpio_mask_ack_irq(struct irq_data *d) writel(pintbit, ®s->request); writel(pintbit, ®s->mask_clear); - spin_unlock_irqrestore(&port->pint->lock, flags); + spin_unlock(&port->pint->lock); spin_unlock_irqrestore(&port->lock, flags); } @@ -293,11 +293,11 @@ static void adi_gpio_mask_irq(struct irq_data *d) struct gpio_pint_regs *regs = port->pint->regs; spin_lock_irqsave(&port->lock, flags); - spin_lock_irqsave(&port->pint->lock, flags); + spin_lock(&port->pint->lock); writel(hwirq_to_pintbit(port, d->hwirq), ®s->mask_clear); - spin_unlock_irqrestore(&port->pint->lock, flags); + spin_unlock(&port->pint->lock); spin_unlock_irqrestore(&port->lock, flags); } @@ -308,11 +308,11 @@ static void adi_gpio_unmask_irq(struct irq_data *d) struct gpio_pint_regs *regs = port->pint->regs; spin_lock_irqsave(&port->lock, flags); - spin_lock_irqsave(&port->pint->lock, flags); + spin_lock(&port->pint->lock); writel(hwirq_to_pintbit(port, d->hwirq), ®s->mask_set); - spin_unlock_irqrestore(&port->pint->lock, flags); + spin_unlock(&port->pint->lock); spin_unlock_irqrestore(&port->lock, flags); } @@ -320,15 +320,17 @@ static unsigned int adi_gpio_irq_startup(struct irq_data *d) { unsigned long flags; struct gpio_port *port = irq_data_get_irq_chip_data(d); - struct gpio_pint_regs *regs = port->pint->regs; + struct gpio_pint_regs *regs; if (!port) { - dev_err(port->dev, "GPIO IRQ %d :Not exist\n", d->irq); + pr_err("GPIO IRQ %d :Not exist\n", d->irq); return -ENODEV; } + regs = port->pint->regs; + spin_lock_irqsave(&port->lock, flags); - spin_lock_irqsave(&port->pint->lock, flags); + spin_lock(&port->pint->lock); port_setup(port, d->hwirq, true); writew(BIT(d->hwirq), &port->regs->dir_clear); @@ -336,7 +338,7 @@ static unsigned int adi_gpio_irq_startup(struct irq_data *d) writel(hwirq_to_pintbit(port, d->hwirq), ®s->mask_set); - spin_unlock_irqrestore(&port->pint->lock, flags); + spin_unlock(&port->pint->lock); spin_unlock_irqrestore(&port->lock, flags); return 0; @@ -349,11 +351,11 @@ static void adi_gpio_irq_shutdown(struct irq_data *d) struct gpio_pint_regs *regs = port->pint->regs; spin_lock_irqsave(&port->lock, flags); - spin_lock_irqsave(&port->pint->lock, flags); + spin_lock(&port->pint->lock); writel(hwirq_to_pintbit(port, d->hwirq), ®s->mask_clear); - spin_unlock_irqrestore(&port->pint->lock, flags); + spin_unlock(&port->pint->lock); spin_unlock_irqrestore(&port->lock, flags); } @@ -361,21 +363,23 @@ static int adi_gpio_irq_type(struct irq_data *d, unsigned int type) { unsigned long flags; struct gpio_port *port = irq_data_get_irq_chip_data(d); - struct gpio_pint_regs *pint_regs = port->pint->regs; + struct gpio_pint_regs *pint_regs; unsigned pintmask; unsigned int irq = d->irq; int ret = 0; char buf[16]; if (!port) { - dev_err(port->dev, "GPIO IRQ %d :Not exist\n", irq); + pr_err("GPIO IRQ %d :Not exist\n", d->irq); return -ENODEV; } + pint_regs = port->pint->regs; + pintmask = hwirq_to_pintbit(port, d->hwirq); spin_lock_irqsave(&port->lock, flags); - spin_lock_irqsave(&port->pint->lock, flags); + spin_lock(&port->pint->lock); /* In case of interrupt autodetect, set irq type to edge sensitive. */ if (type == IRQ_TYPE_PROBE) @@ -416,7 +420,7 @@ static int adi_gpio_irq_type(struct irq_data *d, unsigned int type) } out: - spin_unlock_irqrestore(&port->pint->lock, flags); + spin_unlock(&port->pint->lock); spin_unlock_irqrestore(&port->lock, flags); return ret; -- cgit v0.10.2 From db9371b853e7cebce93f0775215ef297b8d0bf93 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 8 Oct 2013 22:35:38 +0200 Subject: spi: spi-bfin5xx: replace platform_driver_probe to support deferred probing Subsystems like pinctrl and gpio rightfully make use of deferred probing at core level. Now, deferred drivers won't be retried if they don't have a .probe function specified in the driver struct. Fix this driver to have that, so the devices it supports won't get lost in a deferred probe. Signed-off-by: Wolfram Sang Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-bfin5xx.c b/drivers/spi/spi-bfin5xx.c index 1e28943..5284f15 100644 --- a/drivers/spi/spi-bfin5xx.c +++ b/drivers/spi/spi-bfin5xx.c @@ -1464,12 +1464,13 @@ static struct platform_driver bfin_spi_driver = { .owner = THIS_MODULE, .pm = BFIN_SPI_PM_OPS, }, + .probe = bfin_spi_probe, .remove = bfin_spi_remove, }; static int __init bfin_spi_init(void) { - return platform_driver_probe(&bfin_spi_driver, bfin_spi_probe); + return platform_driver_register(&bfin_spi_driver); } subsys_initcall(bfin_spi_init); -- cgit v0.10.2 From 93e9c900102aa2e9b10ec4758b441c8b6e53daf3 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 8 Oct 2013 22:35:39 +0200 Subject: spi: spi-omap-uwire: replace platform_driver_probe to support deferred probing Subsystems like pinctrl and gpio rightfully make use of deferred probing at core level. Now, deferred drivers won't be retried if they don't have a .probe function specified in the driver struct. Fix this driver to have that, so the devices it supports won't get lost in a deferred probe. Signed-off-by: Wolfram Sang Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-omap-uwire.c b/drivers/spi/spi-omap-uwire.c index a6a8f09..9313fd3 100644 --- a/drivers/spi/spi-omap-uwire.c +++ b/drivers/spi/spi-omap-uwire.c @@ -557,7 +557,8 @@ static struct platform_driver uwire_driver = { .name = "omap_uwire", .owner = THIS_MODULE, }, - .remove = uwire_remove, + .probe = uwire_probe, + .remove = uwire_remove, // suspend ... unuse ck // resume ... use ck }; @@ -579,7 +580,7 @@ static int __init omap_uwire_init(void) omap_writel(val | 0x00AAA000, OMAP7XX_IO_CONF_9); } - return platform_driver_probe(&uwire_driver, uwire_probe); + return platform_driver_register(&uwire_driver); } static void __exit omap_uwire_exit(void) -- cgit v0.10.2 From d3224ed140dc440c43e1da607b7685635e8064a6 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Tue, 8 Oct 2013 15:31:21 +0800 Subject: pinctrl: pinctrl-adi2: disable IRQ when setting value GPIO output value should be set after the GPIO interrupt is disabled. Use BIT macro as well. Signed-off-by: Sonic Zhang [Edited commit message] Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/pinctrl-adi2.c b/drivers/pinctrl/pinctrl-adi2.c index a74e6f4..7a39562 100644 --- a/drivers/pinctrl/pinctrl-adi2.c +++ b/drivers/pinctrl/pinctrl-adi2.c @@ -766,9 +766,9 @@ static void adi_gpio_set_value(struct gpio_chip *chip, unsigned offset, spin_lock_irqsave(&port->lock, flags); if (value) - writew(1 << offset, ®s->data_set); + writew(BIT(offset), ®s->data_set); else - writew(1 << offset, ®s->data_clear); + writew(BIT(offset), ®s->data_clear); spin_unlock_irqrestore(&port->lock, flags); } @@ -780,12 +780,14 @@ static int adi_gpio_direction_output(struct gpio_chip *chip, unsigned offset, struct gpio_port_t *regs = port->regs; unsigned long flags; - adi_gpio_set_value(chip, offset, value); - spin_lock_irqsave(&port->lock, flags); - writew(readw(®s->inen) & ~(1 << offset), ®s->inen); - writew(1 << offset, ®s->dir_set); + writew(readw(®s->inen) & ~BIT(offset), ®s->inen); + if (value) + writew(BIT(offset), ®s->data_set); + else + writew(BIT(offset), ®s->data_clear); + writew(BIT(offset), ®s->dir_set); spin_unlock_irqrestore(&port->lock, flags); -- cgit v0.10.2 From 1d82d0c2682ec88a84aaae40c830bddf6ab09482 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 8 Oct 2013 22:35:41 +0200 Subject: spi: spi-txx9: replace platform_driver_probe to support deferred probing Subsystems like pinctrl and gpio rightfully make use of deferred probing at core level. Now, deferred drivers won't be retried if they don't have a .probe function specified in the driver struct. Fix this driver to have that, so the devices it supports won't get lost in a deferred probe. Signed-off-by: Wolfram Sang Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-txx9.c b/drivers/spi/spi-txx9.c index 7c6d157..c67a1b8 100644 --- a/drivers/spi/spi-txx9.c +++ b/drivers/spi/spi-txx9.c @@ -440,6 +440,7 @@ static int txx9spi_remove(struct platform_device *dev) MODULE_ALIAS("platform:spi_txx9"); static struct platform_driver txx9spi_driver = { + .probe = txx9spi_probe, .remove = txx9spi_remove, .driver = { .name = "spi_txx9", @@ -449,7 +450,7 @@ static struct platform_driver txx9spi_driver = { static int __init txx9spi_init(void) { - return platform_driver_probe(&txx9spi_driver, txx9spi_probe); + return platform_driver_register(&txx9spi_driver); } subsys_initcall(txx9spi_init); -- cgit v0.10.2 From 6b5a7c66ce396242e504dbf3d9d5c06d8b1aa488 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 9 Oct 2013 17:22:32 +0530 Subject: ALSA: usb-audio: Use module_usb_driver module_usb_driver makes code simpler by removing the boilerplate. Signed-off-by: Sachin Kamat Signed-off-by: Takashi Iwai diff --git a/sound/usb/card.c b/sound/usb/card.c index 9d9de8d..d979050 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -751,15 +751,4 @@ static struct usb_driver usb_audio_driver = { .supports_autosuspend = 1, }; -static int __init snd_usb_audio_init(void) -{ - return usb_register(&usb_audio_driver); -} - -static void __exit snd_usb_audio_cleanup(void) -{ - usb_deregister(&usb_audio_driver); -} - -module_init(snd_usb_audio_init); -module_exit(snd_usb_audio_cleanup); +module_usb_driver(usb_audio_driver); -- cgit v0.10.2 From d1c30115d06e255a7ba69c7b63129a92c2f34de6 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 9 Oct 2013 14:52:11 +0800 Subject: pinctrl: imx: update fsl,pins description in bindings doc While commit e164153 (pinctrl: imx: move hard-coding data into device tree) moves to use DTC macro for imx pinctrl device tree setting, it changes the semantics of fsl,pins without updating the bindings doc properly. Let's update the fsl,pins description to stop confusing people. While at it, the example in the document is updated, and the stale TODO gets removed. Signed-off-by: Shawn Guo Signed-off-by: Linus Walleij diff --git a/Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt index 3a7caf7..9fde25f 100644 --- a/Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt @@ -22,11 +22,12 @@ Required properties for iomux controller: Please refer to each fsl,-pinctrl.txt binding doc for supported SoCs. Required properties for pin configuration node: -- fsl,pins: two integers array, represents a group of pins mux and config - setting. The format is fsl,pins = , PIN_FUNC_ID is a - pin working on a specific function, which consists of a tuple of - . CONFIG is the pad setting - value like pull-up on this pin. +- fsl,pins: each entry consists of 6 integers and represents the mux and config + setting for one pin. The first 5 integers are specified using a PIN_FUNC_ID macro, which can be found in + imx*-pinfunc.h under device tree source folder. The last integer CONFIG is + the pad setting value like pull-up on this pin. And that's why fsl,pins entry + looks like in the example below. Bits used for CONFIG: NO_PAD_CTL(1 << 31): indicate this pin does not need config. @@ -72,17 +73,18 @@ iomuxc@020e0000 { /* shared pinctrl settings */ usdhc4 { pinctrl_usdhc4_1: usdhc4grp-1 { - fsl,pins = <1386 0x17059 /* MX6Q_PAD_SD4_CMD__USDHC4_CMD */ - 1392 0x10059 /* MX6Q_PAD_SD4_CLK__USDHC4_CLK */ - 1462 0x17059 /* MX6Q_PAD_SD4_DAT0__USDHC4_DAT0 */ - 1470 0x17059 /* MX6Q_PAD_SD4_DAT1__USDHC4_DAT1 */ - 1478 0x17059 /* MX6Q_PAD_SD4_DAT2__USDHC4_DAT2 */ - 1486 0x17059 /* MX6Q_PAD_SD4_DAT3__USDHC4_DAT3 */ - 1493 0x17059 /* MX6Q_PAD_SD4_DAT4__USDHC4_DAT4 */ - 1501 0x17059 /* MX6Q_PAD_SD4_DAT5__USDHC4_DAT5 */ - 1509 0x17059 /* MX6Q_PAD_SD4_DAT6__USDHC4_DAT6 */ - 1517 0x17059>; /* MX6Q_PAD_SD4_DAT7__USDHC4_DAT7 */ - }; + fsl,pins = < + MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059 + MX6QDL_PAD_SD4_CLK__SD4_CLK 0x10059 + MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059 + MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059 + MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059 + MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059 + MX6QDL_PAD_SD4_DAT4__SD4_DATA4 0x17059 + MX6QDL_PAD_SD4_DAT5__SD4_DATA5 0x17059 + MX6QDL_PAD_SD4_DAT6__SD4_DATA6 0x17059 + MX6QDL_PAD_SD4_DAT7__SD4_DATA7 0x17059 + >; }; .... }; @@ -90,6 +92,3 @@ Refer to the IOMUXC controller chapter in imx6q datasheet, 0x17059 means enable hysteresis, 47KOhm Pull Up, 50Mhz speed, 80Ohm driver strength and Fast Slew Rate. User should refer to each SoC spec to set the correct value. - -TODO: when dtc macro support is available, we can change above raw data -to dt macro which can get better readability in dts file. -- cgit v0.10.2 From fb13c7ee0ed387bd6bec4b4024a4d49b1bd504f1 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:29:17 +0100 Subject: sched/numa: Use a system-wide search to find swap/migration candidates This patch implements a system-wide search for swap/migration candidates based on total NUMA hinting faults. It has a balance limit, however it doesn't properly consider total node balance. In the old scheme a task selected a preferred node based on the highest number of private faults recorded on the node. In this scheme, the preferred node is based on the total number of faults. If the preferred node for a task changes then task_numa_migrate will search the whole system looking for tasks to swap with that would improve both the overall compute balance and minimise the expected number of remote NUMA hinting faults. Not there is no guarantee that the node the source task is placed on by task_numa_migrate() has any relationship to the newly selected task->numa_preferred_nid due to compute overloading. Signed-off-by: Mel Gorman [ Do not swap with tasks that cannot run on source cpu] Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju [ Fixed compiler warning on UP. ] Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-40-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 32a2b29..1fe59da 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -5236,6 +5236,7 @@ static void destroy_sched_domains(struct sched_domain *sd, int cpu) DEFINE_PER_CPU(struct sched_domain *, sd_llc); DEFINE_PER_CPU(int, sd_llc_size); DEFINE_PER_CPU(int, sd_llc_id); +DEFINE_PER_CPU(struct sched_domain *, sd_numa); static void update_top_cache_domain(int cpu) { @@ -5252,6 +5253,9 @@ static void update_top_cache_domain(int cpu) rcu_assign_pointer(per_cpu(sd_llc, cpu), sd); per_cpu(sd_llc_size, cpu) = size; per_cpu(sd_llc_id, cpu) = id; + + sd = lowest_flag_domain(cpu, SD_NUMA); + rcu_assign_pointer(per_cpu(sd_numa, cpu), sd); } /* diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index b1e5061..1422765 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -681,6 +681,8 @@ static u64 sched_vslice(struct cfs_rq *cfs_rq, struct sched_entity *se) } #ifdef CONFIG_SMP +static unsigned long task_h_load(struct task_struct *p); + static inline void __update_task_entity_contrib(struct sched_entity *se); /* Give new task start runnable values to heavy its load in infant time */ @@ -906,12 +908,40 @@ static unsigned long target_load(int cpu, int type); static unsigned long power_of(int cpu); static long effective_load(struct task_group *tg, int cpu, long wl, long wg); +/* Cached statistics for all CPUs within a node */ struct numa_stats { + unsigned long nr_running; unsigned long load; - s64 eff_load; - unsigned long faults; + + /* Total compute capacity of CPUs on a node */ + unsigned long power; + + /* Approximate capacity in terms of runnable tasks on a node */ + unsigned long capacity; + int has_capacity; }; +/* + * XXX borrowed from update_sg_lb_stats + */ +static void update_numa_stats(struct numa_stats *ns, int nid) +{ + int cpu; + + memset(ns, 0, sizeof(*ns)); + for_each_cpu(cpu, cpumask_of_node(nid)) { + struct rq *rq = cpu_rq(cpu); + + ns->nr_running += rq->nr_running; + ns->load += weighted_cpuload(cpu); + ns->power += power_of(cpu); + } + + ns->load = (ns->load * SCHED_POWER_SCALE) / ns->power; + ns->capacity = DIV_ROUND_CLOSEST(ns->power, SCHED_POWER_SCALE); + ns->has_capacity = (ns->nr_running < ns->capacity); +} + struct task_numa_env { struct task_struct *p; @@ -920,95 +950,178 @@ struct task_numa_env { struct numa_stats src_stats, dst_stats; - unsigned long best_load; + int imbalance_pct, idx; + + struct task_struct *best_task; + long best_imp; int best_cpu; }; +static void task_numa_assign(struct task_numa_env *env, + struct task_struct *p, long imp) +{ + if (env->best_task) + put_task_struct(env->best_task); + if (p) + get_task_struct(p); + + env->best_task = p; + env->best_imp = imp; + env->best_cpu = env->dst_cpu; +} + +/* + * This checks if the overall compute and NUMA accesses of the system would + * be improved if the source tasks was migrated to the target dst_cpu taking + * into account that it might be best if task running on the dst_cpu should + * be exchanged with the source task + */ +static void task_numa_compare(struct task_numa_env *env, long imp) +{ + struct rq *src_rq = cpu_rq(env->src_cpu); + struct rq *dst_rq = cpu_rq(env->dst_cpu); + struct task_struct *cur; + long dst_load, src_load; + long load; + + rcu_read_lock(); + cur = ACCESS_ONCE(dst_rq->curr); + if (cur->pid == 0) /* idle */ + cur = NULL; + + /* + * "imp" is the fault differential for the source task between the + * source and destination node. Calculate the total differential for + * the source task and potential destination task. The more negative + * the value is, the more rmeote accesses that would be expected to + * be incurred if the tasks were swapped. + */ + if (cur) { + /* Skip this swap candidate if cannot move to the source cpu */ + if (!cpumask_test_cpu(env->src_cpu, tsk_cpus_allowed(cur))) + goto unlock; + + imp += task_faults(cur, env->src_nid) - + task_faults(cur, env->dst_nid); + } + + if (imp < env->best_imp) + goto unlock; + + if (!cur) { + /* Is there capacity at our destination? */ + if (env->src_stats.has_capacity && + !env->dst_stats.has_capacity) + goto unlock; + + goto balance; + } + + /* Balance doesn't matter much if we're running a task per cpu */ + if (src_rq->nr_running == 1 && dst_rq->nr_running == 1) + goto assign; + + /* + * In the overloaded case, try and keep the load balanced. + */ +balance: + dst_load = env->dst_stats.load; + src_load = env->src_stats.load; + + /* XXX missing power terms */ + load = task_h_load(env->p); + dst_load += load; + src_load -= load; + + if (cur) { + load = task_h_load(cur); + dst_load -= load; + src_load += load; + } + + /* make src_load the smaller */ + if (dst_load < src_load) + swap(dst_load, src_load); + + if (src_load * env->imbalance_pct < dst_load * 100) + goto unlock; + +assign: + task_numa_assign(env, cur, imp); +unlock: + rcu_read_unlock(); +} + static int task_numa_migrate(struct task_struct *p) { - int node_cpu = cpumask_first(cpumask_of_node(p->numa_preferred_nid)); struct task_numa_env env = { .p = p, + .src_cpu = task_cpu(p), .src_nid = cpu_to_node(task_cpu(p)), - .dst_cpu = node_cpu, - .dst_nid = p->numa_preferred_nid, - .best_load = ULONG_MAX, - .best_cpu = task_cpu(p), + + .imbalance_pct = 112, + + .best_task = NULL, + .best_imp = 0, + .best_cpu = -1 }; struct sched_domain *sd; - int cpu; - struct task_group *tg = task_group(p); - unsigned long weight; - bool balanced; - int imbalance_pct, idx = -1; + unsigned long faults; + int nid, cpu, ret; /* - * Find the lowest common scheduling domain covering the nodes of both - * the CPU the task is currently running on and the target NUMA node. + * Pick the lowest SD_NUMA domain, as that would have the smallest + * imbalance and would be the first to start moving tasks about. + * + * And we want to avoid any moving of tasks about, as that would create + * random movement of tasks -- counter the numa conditions we're trying + * to satisfy here. */ rcu_read_lock(); - for_each_domain(env.src_cpu, sd) { - if (cpumask_test_cpu(node_cpu, sched_domain_span(sd))) { - /* - * busy_idx is used for the load decision as it is the - * same index used by the regular load balancer for an - * active cpu. - */ - idx = sd->busy_idx; - imbalance_pct = sd->imbalance_pct; - break; - } - } + sd = rcu_dereference(per_cpu(sd_numa, env.src_cpu)); + env.imbalance_pct = 100 + (sd->imbalance_pct - 100) / 2; rcu_read_unlock(); - if (WARN_ON_ONCE(idx == -1)) - return 0; + faults = task_faults(p, env.src_nid); + update_numa_stats(&env.src_stats, env.src_nid); - /* - * XXX the below is mostly nicked from wake_affine(); we should - * see about sharing a bit if at all possible; also it might want - * some per entity weight love. - */ - weight = p->se.load.weight; - env.src_stats.load = source_load(env.src_cpu, idx); - env.src_stats.eff_load = 100 + (imbalance_pct - 100) / 2; - env.src_stats.eff_load *= power_of(env.src_cpu); - env.src_stats.eff_load *= env.src_stats.load + effective_load(tg, env.src_cpu, -weight, -weight); - - for_each_cpu(cpu, cpumask_of_node(env.dst_nid)) { - env.dst_cpu = cpu; - env.dst_stats.load = target_load(cpu, idx); - - /* If the CPU is idle, use it */ - if (!env.dst_stats.load) { - env.best_cpu = cpu; - goto migrate; - } + /* Find an alternative node with relatively better statistics */ + for_each_online_node(nid) { + long imp; - /* Otherwise check the target CPU load */ - env.dst_stats.eff_load = 100; - env.dst_stats.eff_load *= power_of(cpu); - env.dst_stats.eff_load *= env.dst_stats.load + effective_load(tg, cpu, weight, weight); + if (nid == env.src_nid) + continue; - /* - * Destination is considered balanced if the destination CPU is - * less loaded than the source CPU. Unfortunately there is a - * risk that a task running on a lightly loaded CPU will not - * migrate to its preferred node due to load imbalances. - */ - balanced = (env.dst_stats.eff_load <= env.src_stats.eff_load); - if (!balanced) + /* Only consider nodes that recorded more faults */ + imp = task_faults(p, nid) - faults; + if (imp < 0) continue; - if (env.dst_stats.eff_load < env.best_load) { - env.best_load = env.dst_stats.eff_load; - env.best_cpu = cpu; + env.dst_nid = nid; + update_numa_stats(&env.dst_stats, env.dst_nid); + for_each_cpu(cpu, cpumask_of_node(nid)) { + /* Skip this CPU if the source task cannot migrate */ + if (!cpumask_test_cpu(cpu, tsk_cpus_allowed(p))) + continue; + + env.dst_cpu = cpu; + task_numa_compare(&env, imp); } } -migrate: - return migrate_task_to(p, env.best_cpu); + /* No better CPU than the current one was found. */ + if (env.best_cpu == -1) + return -EAGAIN; + + if (env.best_task == NULL) { + int ret = migrate_task_to(p, env.best_cpu); + return ret; + } + + ret = migrate_swap(p, env.best_task); + put_task_struct(env.best_task); + return ret; } /* Attempt to migrate a task to a CPU on the preferred node. */ @@ -1050,7 +1163,7 @@ static void task_numa_placement(struct task_struct *p) /* Find the node with the highest number of faults */ for_each_online_node(nid) { - unsigned long faults; + unsigned long faults = 0; int priv, i; for (priv = 0; priv < 2; priv++) { @@ -1060,10 +1173,10 @@ static void task_numa_placement(struct task_struct *p) p->numa_faults[i] >>= 1; p->numa_faults[i] += p->numa_faults_buffer[i]; p->numa_faults_buffer[i] = 0; + + faults += p->numa_faults[i]; } - /* Find maximum private faults */ - faults = p->numa_faults[task_faults_idx(nid, 1)]; if (faults > max_faults) { max_faults = faults; max_nid = nid; @@ -4455,8 +4568,6 @@ static int move_one_task(struct lb_env *env) return 0; } -static unsigned long task_h_load(struct task_struct *p); - static const unsigned int sched_nr_migrate_break = 32; /* diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 4dc92d0..691e969 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -610,9 +610,22 @@ static inline struct sched_domain *highest_flag_domain(int cpu, int flag) return hsd; } +static inline struct sched_domain *lowest_flag_domain(int cpu, int flag) +{ + struct sched_domain *sd; + + for_each_domain(cpu, sd) { + if (sd->flags & flag) + break; + } + + return sd; +} + DECLARE_PER_CPU(struct sched_domain *, sd_llc); DECLARE_PER_CPU(int, sd_llc_size); DECLARE_PER_CPU(int, sd_llc_id); +DECLARE_PER_CPU(struct sched_domain *, sd_numa); struct sched_group_power { atomic_t ref; -- cgit v0.10.2 From 2c8a50aa873a7e1d6cc0913362051ff9912dc6ca Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:29:18 +0100 Subject: sched/numa: Favor placing a task on the preferred node A tasks preferred node is selected based on the number of faults recorded for a node but the actual task_numa_migate() conducts a global search regardless of the preferred nid. This patch checks if the preferred nid has capacity and if so, searches for a CPU within that node. This avoids a global search when the preferred node is not overloaded. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-41-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 1422765..09aac90 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1052,6 +1052,20 @@ unlock: rcu_read_unlock(); } +static void task_numa_find_cpu(struct task_numa_env *env, long imp) +{ + int cpu; + + for_each_cpu(cpu, cpumask_of_node(env->dst_nid)) { + /* Skip this CPU if the source task cannot migrate */ + if (!cpumask_test_cpu(cpu, tsk_cpus_allowed(env->p))) + continue; + + env->dst_cpu = cpu; + task_numa_compare(env, imp); + } +} + static int task_numa_migrate(struct task_struct *p) { struct task_numa_env env = { @@ -1068,7 +1082,8 @@ static int task_numa_migrate(struct task_struct *p) }; struct sched_domain *sd; unsigned long faults; - int nid, cpu, ret; + int nid, ret; + long imp; /* * Pick the lowest SD_NUMA domain, as that would have the smallest @@ -1085,28 +1100,29 @@ static int task_numa_migrate(struct task_struct *p) faults = task_faults(p, env.src_nid); update_numa_stats(&env.src_stats, env.src_nid); + env.dst_nid = p->numa_preferred_nid; + imp = task_faults(env.p, env.dst_nid) - faults; + update_numa_stats(&env.dst_stats, env.dst_nid); - /* Find an alternative node with relatively better statistics */ - for_each_online_node(nid) { - long imp; - - if (nid == env.src_nid) - continue; - - /* Only consider nodes that recorded more faults */ - imp = task_faults(p, nid) - faults; - if (imp < 0) - continue; + /* + * If the preferred nid has capacity then use it. Otherwise find an + * alternative node with relatively better statistics. + */ + if (env.dst_stats.has_capacity) { + task_numa_find_cpu(&env, imp); + } else { + for_each_online_node(nid) { + if (nid == env.src_nid || nid == p->numa_preferred_nid) + continue; - env.dst_nid = nid; - update_numa_stats(&env.dst_stats, env.dst_nid); - for_each_cpu(cpu, cpumask_of_node(nid)) { - /* Skip this CPU if the source task cannot migrate */ - if (!cpumask_test_cpu(cpu, tsk_cpus_allowed(p))) + /* Only consider nodes that recorded more faults */ + imp = task_faults(env.p, nid) - faults; + if (imp < 0) continue; - env.dst_cpu = cpu; - task_numa_compare(&env, imp); + env.dst_nid = nid; + update_numa_stats(&env.dst_stats, env.dst_nid); + task_numa_find_cpu(&env, imp); } } -- cgit v0.10.2 From e1dda8a797b59d7ec4b17e393152ec3273a552d5 Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Mon, 7 Oct 2013 11:29:19 +0100 Subject: sched/numa: Fix placement of workloads spread across multiple nodes The load balancer will spread workloads across multiple NUMA nodes, in order to balance the load on the system. This means that sometimes a task's preferred node has available capacity, but moving the task there will not succeed, because that would create too large an imbalance. In that case, other NUMA nodes need to be considered. Signed-off-by: Rik van Riel Signed-off-by: Mel Gorman Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-42-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 09aac90..aa561c8 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1104,13 +1104,12 @@ static int task_numa_migrate(struct task_struct *p) imp = task_faults(env.p, env.dst_nid) - faults; update_numa_stats(&env.dst_stats, env.dst_nid); - /* - * If the preferred nid has capacity then use it. Otherwise find an - * alternative node with relatively better statistics. - */ - if (env.dst_stats.has_capacity) { + /* If the preferred nid has capacity, try to use it. */ + if (env.dst_stats.has_capacity) task_numa_find_cpu(&env, imp); - } else { + + /* No space available on the preferred nid. Look elsewhere. */ + if (env.best_cpu == -1) { for_each_online_node(nid) { if (nid == env.src_nid || nid == p->numa_preferred_nid) continue; -- cgit v0.10.2 From 90572890d202527c366aa9489b32404e88a7c020 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 7 Oct 2013 11:29:20 +0100 Subject: mm: numa: Change page last {nid,pid} into {cpu,pid} Change the per page last fault tracking to use cpu,pid instead of nid,pid. This will allow us to try and lookup the alternate task more easily. Note that even though it is the cpu that is store in the page flags that the mpol_misplaced decision is still based on the node. Signed-off-by: Peter Zijlstra Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Link: http://lkml.kernel.org/r/1381141781-10992-43-git-send-email-mgorman@suse.de [ Fixed build failure on 32-bit systems. ] Signed-off-by: Ingo Molnar diff --git a/include/linux/mm.h b/include/linux/mm.h index bb412ce..ce464cd 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -581,11 +581,11 @@ static inline pte_t maybe_mkwrite(pte_t pte, struct vm_area_struct *vma) * sets it, so none of the operations on it need to be atomic. */ -/* Page flags: | [SECTION] | [NODE] | ZONE | [LAST_NIDPID] | ... | FLAGS | */ +/* Page flags: | [SECTION] | [NODE] | ZONE | [LAST_CPUPID] | ... | FLAGS | */ #define SECTIONS_PGOFF ((sizeof(unsigned long)*8) - SECTIONS_WIDTH) #define NODES_PGOFF (SECTIONS_PGOFF - NODES_WIDTH) #define ZONES_PGOFF (NODES_PGOFF - ZONES_WIDTH) -#define LAST_NIDPID_PGOFF (ZONES_PGOFF - LAST_NIDPID_WIDTH) +#define LAST_CPUPID_PGOFF (ZONES_PGOFF - LAST_CPUPID_WIDTH) /* * Define the bit shifts to access each section. For non-existent @@ -595,7 +595,7 @@ static inline pte_t maybe_mkwrite(pte_t pte, struct vm_area_struct *vma) #define SECTIONS_PGSHIFT (SECTIONS_PGOFF * (SECTIONS_WIDTH != 0)) #define NODES_PGSHIFT (NODES_PGOFF * (NODES_WIDTH != 0)) #define ZONES_PGSHIFT (ZONES_PGOFF * (ZONES_WIDTH != 0)) -#define LAST_NIDPID_PGSHIFT (LAST_NIDPID_PGOFF * (LAST_NIDPID_WIDTH != 0)) +#define LAST_CPUPID_PGSHIFT (LAST_CPUPID_PGOFF * (LAST_CPUPID_WIDTH != 0)) /* NODE:ZONE or SECTION:ZONE is used to ID a zone for the buddy allocator */ #ifdef NODE_NOT_IN_PAGE_FLAGS @@ -617,7 +617,7 @@ static inline pte_t maybe_mkwrite(pte_t pte, struct vm_area_struct *vma) #define ZONES_MASK ((1UL << ZONES_WIDTH) - 1) #define NODES_MASK ((1UL << NODES_WIDTH) - 1) #define SECTIONS_MASK ((1UL << SECTIONS_WIDTH) - 1) -#define LAST_NIDPID_MASK ((1UL << LAST_NIDPID_WIDTH) - 1) +#define LAST_CPUPID_MASK ((1UL << LAST_CPUPID_WIDTH) - 1) #define ZONEID_MASK ((1UL << ZONEID_SHIFT) - 1) static inline enum zone_type page_zonenum(const struct page *page) @@ -661,96 +661,106 @@ static inline int page_to_nid(const struct page *page) #endif #ifdef CONFIG_NUMA_BALANCING -static inline int nid_pid_to_nidpid(int nid, int pid) +static inline int cpu_pid_to_cpupid(int cpu, int pid) { - return ((nid & LAST__NID_MASK) << LAST__PID_SHIFT) | (pid & LAST__PID_MASK); + return ((cpu & LAST__CPU_MASK) << LAST__PID_SHIFT) | (pid & LAST__PID_MASK); } -static inline int nidpid_to_pid(int nidpid) +static inline int cpupid_to_pid(int cpupid) { - return nidpid & LAST__PID_MASK; + return cpupid & LAST__PID_MASK; } -static inline int nidpid_to_nid(int nidpid) +static inline int cpupid_to_cpu(int cpupid) { - return (nidpid >> LAST__PID_SHIFT) & LAST__NID_MASK; + return (cpupid >> LAST__PID_SHIFT) & LAST__CPU_MASK; } -static inline bool nidpid_pid_unset(int nidpid) +static inline int cpupid_to_nid(int cpupid) { - return nidpid_to_pid(nidpid) == (-1 & LAST__PID_MASK); + return cpu_to_node(cpupid_to_cpu(cpupid)); } -static inline bool nidpid_nid_unset(int nidpid) +static inline bool cpupid_pid_unset(int cpupid) { - return nidpid_to_nid(nidpid) == (-1 & LAST__NID_MASK); + return cpupid_to_pid(cpupid) == (-1 & LAST__PID_MASK); } -#ifdef LAST_NIDPID_NOT_IN_PAGE_FLAGS -static inline int page_nidpid_xchg_last(struct page *page, int nid) +static inline bool cpupid_cpu_unset(int cpupid) { - return xchg(&page->_last_nidpid, nid); + return cpupid_to_cpu(cpupid) == (-1 & LAST__CPU_MASK); } -static inline int page_nidpid_last(struct page *page) +#ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS +static inline int page_cpupid_xchg_last(struct page *page, int cpupid) { - return page->_last_nidpid; + return xchg(&page->_last_cpupid, cpupid); } -static inline void page_nidpid_reset_last(struct page *page) + +static inline int page_cpupid_last(struct page *page) +{ + return page->_last_cpupid; +} +static inline void page_cpupid_reset_last(struct page *page) { - page->_last_nidpid = -1; + page->_last_cpupid = -1; } #else -static inline int page_nidpid_last(struct page *page) +static inline int page_cpupid_last(struct page *page) { - return (page->flags >> LAST_NIDPID_PGSHIFT) & LAST_NIDPID_MASK; + return (page->flags >> LAST_CPUPID_PGSHIFT) & LAST_CPUPID_MASK; } -extern int page_nidpid_xchg_last(struct page *page, int nidpid); +extern int page_cpupid_xchg_last(struct page *page, int cpupid); -static inline void page_nidpid_reset_last(struct page *page) +static inline void page_cpupid_reset_last(struct page *page) { - int nidpid = (1 << LAST_NIDPID_SHIFT) - 1; + int cpupid = (1 << LAST_CPUPID_SHIFT) - 1; - page->flags &= ~(LAST_NIDPID_MASK << LAST_NIDPID_PGSHIFT); - page->flags |= (nidpid & LAST_NIDPID_MASK) << LAST_NIDPID_PGSHIFT; + page->flags &= ~(LAST_CPUPID_MASK << LAST_CPUPID_PGSHIFT); + page->flags |= (cpupid & LAST_CPUPID_MASK) << LAST_CPUPID_PGSHIFT; } -#endif /* LAST_NIDPID_NOT_IN_PAGE_FLAGS */ -#else -static inline int page_nidpid_xchg_last(struct page *page, int nidpid) +#endif /* LAST_CPUPID_NOT_IN_PAGE_FLAGS */ +#else /* !CONFIG_NUMA_BALANCING */ +static inline int page_cpupid_xchg_last(struct page *page, int cpupid) { - return page_to_nid(page); + return page_to_nid(page); /* XXX */ } -static inline int page_nidpid_last(struct page *page) +static inline int page_cpupid_last(struct page *page) { - return page_to_nid(page); + return page_to_nid(page); /* XXX */ } -static inline int nidpid_to_nid(int nidpid) +static inline int cpupid_to_nid(int cpupid) { return -1; } -static inline int nidpid_to_pid(int nidpid) +static inline int cpupid_to_pid(int cpupid) { return -1; } -static inline int nid_pid_to_nidpid(int nid, int pid) +static inline int cpupid_to_cpu(int cpupid) { return -1; } -static inline bool nidpid_pid_unset(int nidpid) +static inline int cpu_pid_to_cpupid(int nid, int pid) +{ + return -1; +} + +static inline bool cpupid_pid_unset(int cpupid) { return 1; } -static inline void page_nidpid_reset_last(struct page *page) +static inline void page_cpupid_reset_last(struct page *page) { } -#endif +#endif /* CONFIG_NUMA_BALANCING */ static inline struct zone *page_zone(const struct page *page) { diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 38a902a..a30f9ca 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -174,8 +174,8 @@ struct page { void *shadow; #endif -#ifdef LAST_NIDPID_NOT_IN_PAGE_FLAGS - int _last_nidpid; +#ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS + int _last_cpupid; #endif } /* diff --git a/include/linux/page-flags-layout.h b/include/linux/page-flags-layout.h index 02bc918..da52366 100644 --- a/include/linux/page-flags-layout.h +++ b/include/linux/page-flags-layout.h @@ -39,9 +39,9 @@ * lookup is necessary. * * No sparsemem or sparsemem vmemmap: | NODE | ZONE | ... | FLAGS | - * " plus space for last_nidpid: | NODE | ZONE | LAST_NIDPID ... | FLAGS | + * " plus space for last_cpupid: | NODE | ZONE | LAST_CPUPID ... | FLAGS | * classic sparse with space for node:| SECTION | NODE | ZONE | ... | FLAGS | - * " plus space for last_nidpid: | SECTION | NODE | ZONE | LAST_NIDPID ... | FLAGS | + * " plus space for last_cpupid: | SECTION | NODE | ZONE | LAST_CPUPID ... | FLAGS | * classic sparse no space for node: | SECTION | ZONE | ... | FLAGS | */ #if defined(CONFIG_SPARSEMEM) && !defined(CONFIG_SPARSEMEM_VMEMMAP) @@ -65,18 +65,18 @@ #define LAST__PID_SHIFT 8 #define LAST__PID_MASK ((1 << LAST__PID_SHIFT)-1) -#define LAST__NID_SHIFT NODES_SHIFT -#define LAST__NID_MASK ((1 << LAST__NID_SHIFT)-1) +#define LAST__CPU_SHIFT NR_CPUS_BITS +#define LAST__CPU_MASK ((1 << LAST__CPU_SHIFT)-1) -#define LAST_NIDPID_SHIFT (LAST__PID_SHIFT+LAST__NID_SHIFT) +#define LAST_CPUPID_SHIFT (LAST__PID_SHIFT+LAST__CPU_SHIFT) #else -#define LAST_NIDPID_SHIFT 0 +#define LAST_CPUPID_SHIFT 0 #endif -#if SECTIONS_WIDTH+ZONES_WIDTH+NODES_SHIFT+LAST_NIDPID_SHIFT <= BITS_PER_LONG - NR_PAGEFLAGS -#define LAST_NIDPID_WIDTH LAST_NIDPID_SHIFT +#if SECTIONS_WIDTH+ZONES_WIDTH+NODES_SHIFT+LAST_CPUPID_SHIFT <= BITS_PER_LONG - NR_PAGEFLAGS +#define LAST_CPUPID_WIDTH LAST_CPUPID_SHIFT #else -#define LAST_NIDPID_WIDTH 0 +#define LAST_CPUPID_WIDTH 0 #endif /* @@ -87,8 +87,8 @@ #define NODE_NOT_IN_PAGE_FLAGS #endif -#if defined(CONFIG_NUMA_BALANCING) && LAST_NIDPID_WIDTH == 0 -#define LAST_NIDPID_NOT_IN_PAGE_FLAGS +#if defined(CONFIG_NUMA_BALANCING) && LAST_CPUPID_WIDTH == 0 +#define LAST_CPUPID_NOT_IN_PAGE_FLAGS #endif #endif /* _LINUX_PAGE_FLAGS_LAYOUT */ diff --git a/kernel/bounds.c b/kernel/bounds.c index 0c9b862..e8ca97b 100644 --- a/kernel/bounds.c +++ b/kernel/bounds.c @@ -10,6 +10,7 @@ #include #include #include +#include void foo(void) { @@ -17,5 +18,8 @@ void foo(void) DEFINE(NR_PAGEFLAGS, __NR_PAGEFLAGS); DEFINE(MAX_NR_ZONES, __MAX_NR_ZONES); DEFINE(NR_PCG_FLAGS, __NR_PCG_FLAGS); +#ifdef CONFIG_SMP + DEFINE(NR_CPUS_BITS, ilog2(CONFIG_NR_CPUS)); +#endif /* End of constants */ } diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index aa561c8..dbe0f62 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1210,7 +1210,7 @@ static void task_numa_placement(struct task_struct *p) /* * Got a PROT_NONE fault for a page on @node. */ -void task_numa_fault(int last_nidpid, int node, int pages, bool migrated) +void task_numa_fault(int last_cpupid, int node, int pages, bool migrated) { struct task_struct *p = current; int priv; @@ -1226,8 +1226,8 @@ void task_numa_fault(int last_nidpid, int node, int pages, bool migrated) * First accesses are treated as private, otherwise consider accesses * to be private if the accessing pid has not changed */ - if (!nidpid_pid_unset(last_nidpid)) - priv = ((p->pid & LAST__PID_MASK) == nidpid_to_pid(last_nidpid)); + if (!cpupid_pid_unset(last_cpupid)) + priv = ((p->pid & LAST__PID_MASK) == cpupid_to_pid(last_cpupid)); else priv = 1; diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 0baf0e4..becf92c 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -1282,7 +1282,7 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, struct page *page; unsigned long haddr = addr & HPAGE_PMD_MASK; int page_nid = -1, this_nid = numa_node_id(); - int target_nid, last_nidpid = -1; + int target_nid, last_cpupid = -1; bool page_locked; bool migrated = false; @@ -1293,7 +1293,7 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, page = pmd_page(pmd); BUG_ON(is_huge_zero_page(page)); page_nid = page_to_nid(page); - last_nidpid = page_nidpid_last(page); + last_cpupid = page_cpupid_last(page); count_vm_numa_event(NUMA_HINT_FAULTS); if (page_nid == this_nid) count_vm_numa_event(NUMA_HINT_FAULTS_LOCAL); @@ -1362,7 +1362,7 @@ out: page_unlock_anon_vma_read(anon_vma); if (page_nid != -1) - task_numa_fault(last_nidpid, page_nid, HPAGE_PMD_NR, migrated); + task_numa_fault(last_cpupid, page_nid, HPAGE_PMD_NR, migrated); return 0; } @@ -1682,7 +1682,7 @@ static void __split_huge_page_refcount(struct page *page, page_tail->mapping = page->mapping; page_tail->index = page->index + i; - page_nidpid_xchg_last(page_tail, page_nidpid_last(page)); + page_cpupid_xchg_last(page_tail, page_cpupid_last(page)); BUG_ON(!PageAnon(page_tail)); BUG_ON(!PageUptodate(page_tail)); diff --git a/mm/memory.c b/mm/memory.c index cc7f206..5162e6d 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -69,8 +69,8 @@ #include "internal.h" -#ifdef LAST_NIDPID_NOT_IN_PAGE_FLAGS -#warning Unfortunate NUMA and NUMA Balancing config, growing page-frame for last_nidpid. +#ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS +#warning Unfortunate NUMA and NUMA Balancing config, growing page-frame for last_cpupid. #endif #ifndef CONFIG_NEED_MULTIPLE_NODES @@ -3536,7 +3536,7 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, struct page *page = NULL; spinlock_t *ptl; int page_nid = -1; - int last_nidpid; + int last_cpupid; int target_nid; bool migrated = false; @@ -3567,7 +3567,7 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, } BUG_ON(is_zero_pfn(page_to_pfn(page))); - last_nidpid = page_nidpid_last(page); + last_cpupid = page_cpupid_last(page); page_nid = page_to_nid(page); target_nid = numa_migrate_prep(page, vma, addr, page_nid); pte_unmap_unlock(ptep, ptl); @@ -3583,7 +3583,7 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, out: if (page_nid != -1) - task_numa_fault(last_nidpid, page_nid, 1, migrated); + task_numa_fault(last_cpupid, page_nid, 1, migrated); return 0; } @@ -3598,7 +3598,7 @@ static int do_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long offset; spinlock_t *ptl; bool numa = false; - int last_nidpid; + int last_cpupid; spin_lock(&mm->page_table_lock); pmd = *pmdp; @@ -3643,7 +3643,7 @@ static int do_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, if (unlikely(!page)) continue; - last_nidpid = page_nidpid_last(page); + last_cpupid = page_cpupid_last(page); page_nid = page_to_nid(page); target_nid = numa_migrate_prep(page, vma, addr, page_nid); pte_unmap_unlock(pte, ptl); @@ -3656,7 +3656,7 @@ static int do_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, } if (page_nid != -1) - task_numa_fault(last_nidpid, page_nid, 1, migrated); + task_numa_fault(last_cpupid, page_nid, 1, migrated); pte = pte_offset_map_lock(mm, pmdp, addr, &ptl); } diff --git a/mm/mempolicy.c b/mm/mempolicy.c index 0e895a2..a5867ef 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -2324,6 +2324,8 @@ int mpol_misplaced(struct page *page, struct vm_area_struct *vma, unsigned long struct zone *zone; int curnid = page_to_nid(page); unsigned long pgoff; + int thiscpu = raw_smp_processor_id(); + int thisnid = cpu_to_node(thiscpu); int polnid = -1; int ret = -1; @@ -2372,11 +2374,11 @@ int mpol_misplaced(struct page *page, struct vm_area_struct *vma, unsigned long /* Migrate the page towards the node whose CPU is referencing it */ if (pol->flags & MPOL_F_MORON) { - int last_nidpid; - int this_nidpid; + int last_cpupid; + int this_cpupid; - polnid = numa_node_id(); - this_nidpid = nid_pid_to_nidpid(polnid, current->pid); + polnid = thisnid; + this_cpupid = cpu_pid_to_cpupid(thiscpu, current->pid); /* * Multi-stage node selection is used in conjunction @@ -2399,8 +2401,8 @@ int mpol_misplaced(struct page *page, struct vm_area_struct *vma, unsigned long * it less likely we act on an unlikely task<->page * relation. */ - last_nidpid = page_nidpid_xchg_last(page, this_nidpid); - if (!nidpid_pid_unset(last_nidpid) && nidpid_to_nid(last_nidpid) != polnid) + last_cpupid = page_cpupid_xchg_last(page, this_cpupid); + if (!cpupid_pid_unset(last_cpupid) && cpupid_to_nid(last_cpupid) != thisnid) goto out; #ifdef CONFIG_NUMA_BALANCING @@ -2410,7 +2412,7 @@ int mpol_misplaced(struct page *page, struct vm_area_struct *vma, unsigned long * This way a short and temporary process migration will * not cause excessive memory migration. */ - if (polnid != current->numa_preferred_nid && + if (thisnid != current->numa_preferred_nid && !current->numa_migrate_seq) goto out; #endif diff --git a/mm/migrate.c b/mm/migrate.c index 025d1e3..ff53774 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -1498,7 +1498,7 @@ static struct page *alloc_misplaced_dst_page(struct page *page, __GFP_NOWARN) & ~GFP_IOFS, 0); if (newpage) - page_nidpid_xchg_last(newpage, page_nidpid_last(page)); + page_cpupid_xchg_last(newpage, page_cpupid_last(page)); return newpage; } @@ -1675,7 +1675,7 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm, if (!new_page) goto out_fail; - page_nidpid_xchg_last(new_page, page_nidpid_last(page)); + page_cpupid_xchg_last(new_page, page_cpupid_last(page)); isolated = numamigrate_isolate_page(pgdat, page); if (!isolated) { diff --git a/mm/mm_init.c b/mm/mm_init.c index 467de57..68562e9 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -71,26 +71,26 @@ void __init mminit_verify_pageflags_layout(void) unsigned long or_mask, add_mask; shift = 8 * sizeof(unsigned long); - width = shift - SECTIONS_WIDTH - NODES_WIDTH - ZONES_WIDTH - LAST_NIDPID_SHIFT; + width = shift - SECTIONS_WIDTH - NODES_WIDTH - ZONES_WIDTH - LAST_CPUPID_SHIFT; mminit_dprintk(MMINIT_TRACE, "pageflags_layout_widths", - "Section %d Node %d Zone %d Lastnidpid %d Flags %d\n", + "Section %d Node %d Zone %d Lastcpupid %d Flags %d\n", SECTIONS_WIDTH, NODES_WIDTH, ZONES_WIDTH, - LAST_NIDPID_WIDTH, + LAST_CPUPID_WIDTH, NR_PAGEFLAGS); mminit_dprintk(MMINIT_TRACE, "pageflags_layout_shifts", - "Section %d Node %d Zone %d Lastnidpid %d\n", + "Section %d Node %d Zone %d Lastcpupid %d\n", SECTIONS_SHIFT, NODES_SHIFT, ZONES_SHIFT, - LAST_NIDPID_SHIFT); + LAST_CPUPID_SHIFT); mminit_dprintk(MMINIT_TRACE, "pageflags_layout_pgshifts", - "Section %lu Node %lu Zone %lu Lastnidpid %lu\n", + "Section %lu Node %lu Zone %lu Lastcpupid %lu\n", (unsigned long)SECTIONS_PGSHIFT, (unsigned long)NODES_PGSHIFT, (unsigned long)ZONES_PGSHIFT, - (unsigned long)LAST_NIDPID_PGSHIFT); + (unsigned long)LAST_CPUPID_PGSHIFT); mminit_dprintk(MMINIT_TRACE, "pageflags_layout_nodezoneid", "Node/Zone ID: %lu -> %lu\n", (unsigned long)(ZONEID_PGOFF + ZONEID_SHIFT), @@ -102,9 +102,9 @@ void __init mminit_verify_pageflags_layout(void) mminit_dprintk(MMINIT_TRACE, "pageflags_layout_nodeflags", "Node not in page flags"); #endif -#ifdef LAST_NIDPID_NOT_IN_PAGE_FLAGS +#ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS mminit_dprintk(MMINIT_TRACE, "pageflags_layout_nodeflags", - "Last nidpid not in page flags"); + "Last cpupid not in page flags"); #endif if (SECTIONS_WIDTH) { diff --git a/mm/mmzone.c b/mm/mmzone.c index 25bb477..bf34fb8 100644 --- a/mm/mmzone.c +++ b/mm/mmzone.c @@ -97,20 +97,20 @@ void lruvec_init(struct lruvec *lruvec) INIT_LIST_HEAD(&lruvec->lists[lru]); } -#if defined(CONFIG_NUMA_BALANCING) && !defined(LAST_NIDPID_NOT_IN_PAGE_FLAGS) -int page_nidpid_xchg_last(struct page *page, int nidpid) +#if defined(CONFIG_NUMA_BALANCING) && !defined(LAST_CPUPID_NOT_IN_PAGE_FLAGS) +int page_cpupid_xchg_last(struct page *page, int cpupid) { unsigned long old_flags, flags; - int last_nidpid; + int last_cpupid; do { old_flags = flags = page->flags; - last_nidpid = page_nidpid_last(page); + last_cpupid = page_cpupid_last(page); - flags &= ~(LAST_NIDPID_MASK << LAST_NIDPID_PGSHIFT); - flags |= (nidpid & LAST_NIDPID_MASK) << LAST_NIDPID_PGSHIFT; + flags &= ~(LAST_CPUPID_MASK << LAST_CPUPID_PGSHIFT); + flags |= (cpupid & LAST_CPUPID_MASK) << LAST_CPUPID_PGSHIFT; } while (unlikely(cmpxchg(&page->flags, old_flags, flags) != old_flags)); - return last_nidpid; + return last_cpupid; } #endif diff --git a/mm/mprotect.c b/mm/mprotect.c index 5aae390..9a74855 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -37,14 +37,14 @@ static inline pgprot_t pgprot_modify(pgprot_t oldprot, pgprot_t newprot) static unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd, unsigned long addr, unsigned long end, pgprot_t newprot, - int dirty_accountable, int prot_numa, bool *ret_all_same_nidpid) + int dirty_accountable, int prot_numa, bool *ret_all_same_cpupid) { struct mm_struct *mm = vma->vm_mm; pte_t *pte, oldpte; spinlock_t *ptl; unsigned long pages = 0; - bool all_same_nidpid = true; - int last_nid = -1; + bool all_same_cpupid = true; + int last_cpu = -1; int last_pid = -1; pte = pte_offset_map_lock(mm, pmd, addr, &ptl); @@ -64,17 +64,17 @@ static unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd, page = vm_normal_page(vma, addr, oldpte); if (page) { - int nidpid = page_nidpid_last(page); - int this_nid = nidpid_to_nid(nidpid); - int this_pid = nidpid_to_pid(nidpid); + int cpupid = page_cpupid_last(page); + int this_cpu = cpupid_to_cpu(cpupid); + int this_pid = cpupid_to_pid(cpupid); - if (last_nid == -1) - last_nid = this_nid; + if (last_cpu == -1) + last_cpu = this_cpu; if (last_pid == -1) last_pid = this_pid; - if (last_nid != this_nid || + if (last_cpu != this_cpu || last_pid != this_pid) { - all_same_nidpid = false; + all_same_cpupid = false; } if (!pte_numa(oldpte)) { @@ -115,7 +115,7 @@ static unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd, arch_leave_lazy_mmu_mode(); pte_unmap_unlock(pte - 1, ptl); - *ret_all_same_nidpid = all_same_nidpid; + *ret_all_same_cpupid = all_same_cpupid; return pages; } @@ -142,7 +142,7 @@ static inline unsigned long change_pmd_range(struct vm_area_struct *vma, pmd_t *pmd; unsigned long next; unsigned long pages = 0; - bool all_same_nidpid; + bool all_same_cpupid; pmd = pmd_offset(pud, addr); do { @@ -168,7 +168,7 @@ static inline unsigned long change_pmd_range(struct vm_area_struct *vma, if (pmd_none_or_clear_bad(pmd)) continue; this_pages = change_pte_range(vma, pmd, addr, next, newprot, - dirty_accountable, prot_numa, &all_same_nidpid); + dirty_accountable, prot_numa, &all_same_cpupid); pages += this_pages; /* @@ -177,7 +177,7 @@ static inline unsigned long change_pmd_range(struct vm_area_struct *vma, * node. This allows a regular PMD to be handled as one fault * and effectively batches the taking of the PTL */ - if (prot_numa && this_pages && all_same_nidpid) + if (prot_numa && this_pages && all_same_cpupid) change_pmd_protnuma(vma->vm_mm, addr, pmd); } while (pmd++, addr = next, addr != end); diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 89bedd0..73d812f 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -626,7 +626,7 @@ static inline int free_pages_check(struct page *page) bad_page(page); return 1; } - page_nidpid_reset_last(page); + page_cpupid_reset_last(page); if (page->flags & PAGE_FLAGS_CHECK_AT_PREP) page->flags &= ~PAGE_FLAGS_CHECK_AT_PREP; return 0; @@ -4015,7 +4015,7 @@ void __meminit memmap_init_zone(unsigned long size, int nid, unsigned long zone, mminit_verify_page_links(page, zone, nid, pfn); init_page_count(page); page_mapcount_reset(page); - page_nidpid_reset_last(page); + page_cpupid_reset_last(page); SetPageReserved(page); /* * Mark the block movable so that blocks are reserved for -- cgit v0.10.2 From 8c8a743c5087bac9caac8155b8f3b367e75cdd0b Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 7 Oct 2013 11:29:21 +0100 Subject: sched/numa: Use {cpu, pid} to create task groups for shared faults While parallel applications tend to align their data on the cache boundary, they tend not to align on the page or THP boundary. Consequently tasks that partition their data can still "false-share" pages presenting a problem for optimal NUMA placement. This patch uses NUMA hinting faults to chain tasks together into numa_groups. As well as storing the NID a task was running on when accessing a page a truncated representation of the faulting PID is stored. If subsequent faults are from different PIDs it is reasonable to assume that those two tasks share a page and are candidates for being grouped together. Note that this patch makes no scheduling decisions based on the grouping information. Signed-off-by: Peter Zijlstra Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Link: http://lkml.kernel.org/r/1381141781-10992-44-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/include/linux/mm.h b/include/linux/mm.h index ce464cd..81443d5 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -691,6 +691,12 @@ static inline bool cpupid_cpu_unset(int cpupid) return cpupid_to_cpu(cpupid) == (-1 & LAST__CPU_MASK); } +static inline bool __cpupid_match_pid(pid_t task_pid, int cpupid) +{ + return (task_pid & LAST__PID_MASK) == cpupid_to_pid(cpupid); +} + +#define cpupid_match_pid(task, cpupid) __cpupid_match_pid(task->pid, cpupid) #ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS static inline int page_cpupid_xchg_last(struct page *page, int cpupid) { @@ -760,6 +766,11 @@ static inline bool cpupid_pid_unset(int cpupid) static inline void page_cpupid_reset_last(struct page *page) { } + +static inline bool cpupid_match_pid(struct task_struct *task, int cpupid) +{ + return false; +} #endif /* CONFIG_NUMA_BALANCING */ static inline struct zone *page_zone(const struct page *page) diff --git a/include/linux/sched.h b/include/linux/sched.h index b661979..f587ded 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1347,6 +1347,9 @@ struct task_struct { u64 node_stamp; /* migration stamp */ struct callback_head numa_work; + struct list_head numa_entry; + struct numa_group *numa_group; + /* * Exponential decaying average of faults on a per-node basis. * Scheduling placement decisions are made based on the these counts. diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 1fe59da..51092d5 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -1733,6 +1733,9 @@ static void __sched_fork(struct task_struct *p) p->numa_work.next = &p->numa_work; p->numa_faults = NULL; p->numa_faults_buffer = NULL; + + INIT_LIST_HEAD(&p->numa_entry); + p->numa_group = NULL; #endif /* CONFIG_NUMA_BALANCING */ } diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index dbe0f62..8556505 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -888,6 +888,17 @@ static unsigned int task_scan_max(struct task_struct *p) */ unsigned int sysctl_numa_balancing_settle_count __read_mostly = 4; +struct numa_group { + atomic_t refcount; + + spinlock_t lock; /* nr_tasks, tasks */ + int nr_tasks; + struct list_head task_list; + + struct rcu_head rcu; + atomic_long_t faults[0]; +}; + static inline int task_faults_idx(int nid, int priv) { return 2 * nid + priv; @@ -1182,7 +1193,10 @@ static void task_numa_placement(struct task_struct *p) int priv, i; for (priv = 0; priv < 2; priv++) { + long diff; + i = task_faults_idx(nid, priv); + diff = -p->numa_faults[i]; /* Decay existing window, copy faults since last scan */ p->numa_faults[i] >>= 1; @@ -1190,6 +1204,11 @@ static void task_numa_placement(struct task_struct *p) p->numa_faults_buffer[i] = 0; faults += p->numa_faults[i]; + diff += p->numa_faults[i]; + if (p->numa_group) { + /* safe because we can only change our own group */ + atomic_long_add(diff, &p->numa_group->faults[i]); + } } if (faults > max_faults) { @@ -1207,6 +1226,131 @@ static void task_numa_placement(struct task_struct *p) } } +static inline int get_numa_group(struct numa_group *grp) +{ + return atomic_inc_not_zero(&grp->refcount); +} + +static inline void put_numa_group(struct numa_group *grp) +{ + if (atomic_dec_and_test(&grp->refcount)) + kfree_rcu(grp, rcu); +} + +static void double_lock(spinlock_t *l1, spinlock_t *l2) +{ + if (l1 > l2) + swap(l1, l2); + + spin_lock(l1); + spin_lock_nested(l2, SINGLE_DEPTH_NESTING); +} + +static void task_numa_group(struct task_struct *p, int cpupid) +{ + struct numa_group *grp, *my_grp; + struct task_struct *tsk; + bool join = false; + int cpu = cpupid_to_cpu(cpupid); + int i; + + if (unlikely(!p->numa_group)) { + unsigned int size = sizeof(struct numa_group) + + 2*nr_node_ids*sizeof(atomic_long_t); + + grp = kzalloc(size, GFP_KERNEL | __GFP_NOWARN); + if (!grp) + return; + + atomic_set(&grp->refcount, 1); + spin_lock_init(&grp->lock); + INIT_LIST_HEAD(&grp->task_list); + + for (i = 0; i < 2*nr_node_ids; i++) + atomic_long_set(&grp->faults[i], p->numa_faults[i]); + + list_add(&p->numa_entry, &grp->task_list); + grp->nr_tasks++; + rcu_assign_pointer(p->numa_group, grp); + } + + rcu_read_lock(); + tsk = ACCESS_ONCE(cpu_rq(cpu)->curr); + + if (!cpupid_match_pid(tsk, cpupid)) + goto unlock; + + grp = rcu_dereference(tsk->numa_group); + if (!grp) + goto unlock; + + my_grp = p->numa_group; + if (grp == my_grp) + goto unlock; + + /* + * Only join the other group if its bigger; if we're the bigger group, + * the other task will join us. + */ + if (my_grp->nr_tasks > grp->nr_tasks) + goto unlock; + + /* + * Tie-break on the grp address. + */ + if (my_grp->nr_tasks == grp->nr_tasks && my_grp > grp) + goto unlock; + + if (!get_numa_group(grp)) + goto unlock; + + join = true; + +unlock: + rcu_read_unlock(); + + if (!join) + return; + + for (i = 0; i < 2*nr_node_ids; i++) { + atomic_long_sub(p->numa_faults[i], &my_grp->faults[i]); + atomic_long_add(p->numa_faults[i], &grp->faults[i]); + } + + double_lock(&my_grp->lock, &grp->lock); + + list_move(&p->numa_entry, &grp->task_list); + my_grp->nr_tasks--; + grp->nr_tasks++; + + spin_unlock(&my_grp->lock); + spin_unlock(&grp->lock); + + rcu_assign_pointer(p->numa_group, grp); + + put_numa_group(my_grp); +} + +void task_numa_free(struct task_struct *p) +{ + struct numa_group *grp = p->numa_group; + int i; + + if (grp) { + for (i = 0; i < 2*nr_node_ids; i++) + atomic_long_sub(p->numa_faults[i], &grp->faults[i]); + + spin_lock(&grp->lock); + list_del(&p->numa_entry); + grp->nr_tasks--; + spin_unlock(&grp->lock); + rcu_assign_pointer(p->numa_group, NULL); + put_numa_group(grp); + } + + kfree(p->numa_faults); +} + /* * Got a PROT_NONE fault for a page on @node. */ @@ -1222,15 +1366,6 @@ void task_numa_fault(int last_cpupid, int node, int pages, bool migrated) if (!p->mm) return; - /* - * First accesses are treated as private, otherwise consider accesses - * to be private if the accessing pid has not changed - */ - if (!cpupid_pid_unset(last_cpupid)) - priv = ((p->pid & LAST__PID_MASK) == cpupid_to_pid(last_cpupid)); - else - priv = 1; - /* Allocate buffer to track faults on a per-node basis */ if (unlikely(!p->numa_faults)) { int size = sizeof(*p->numa_faults) * 2 * nr_node_ids; @@ -1245,6 +1380,18 @@ void task_numa_fault(int last_cpupid, int node, int pages, bool migrated) } /* + * First accesses are treated as private, otherwise consider accesses + * to be private if the accessing pid has not changed + */ + if (unlikely(last_cpupid == (-1 & LAST_CPUPID_MASK))) { + priv = 1; + } else { + priv = cpupid_match_pid(p, last_cpupid); + if (!priv) + task_numa_group(p, last_cpupid); + } + + /* * If pages are properly placed (did not migrate) then scan slower. * This is reset periodically in case of phase changes */ diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 691e969..8037b10 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -559,10 +559,7 @@ static inline u64 rq_clock_task(struct rq *rq) #ifdef CONFIG_NUMA_BALANCING extern int migrate_task_to(struct task_struct *p, int cpu); extern int migrate_swap(struct task_struct *, struct task_struct *); -static inline void task_numa_free(struct task_struct *p) -{ - kfree(p->numa_faults); -} +extern void task_numa_free(struct task_struct *p); #else /* CONFIG_NUMA_BALANCING */ static inline void task_numa_free(struct task_struct *p) { diff --git a/mm/memory.c b/mm/memory.c index 5162e6d..c57efa2 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -2719,6 +2719,14 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma, get_page(dirty_page); reuse: + /* + * Clear the pages cpupid information as the existing + * information potentially belongs to a now completely + * unrelated process. + */ + if (old_page) + page_cpupid_xchg_last(old_page, (1 << LAST_CPUPID_SHIFT) - 1); + flush_cache_page(vma, address, pte_pfn(orig_pte)); entry = pte_mkyoung(orig_pte); entry = maybe_mkwrite(pte_mkdirty(entry), vma); -- cgit v0.10.2 From e29cf08b05dc0b8151d65704d96d525a9e179a6b Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:29:22 +0100 Subject: sched/numa: Report a NUMA task group ID It is desirable to model from userspace how the scheduler groups tasks over time. This patch adds an ID to the numa_group and reports it via /proc/PID/status. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-45-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/fs/proc/array.c b/fs/proc/array.c index cbd0f1b..1bd2077 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -183,6 +183,7 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns, seq_printf(m, "State:\t%s\n" "Tgid:\t%d\n" + "Ngid:\t%d\n" "Pid:\t%d\n" "PPid:\t%d\n" "TracerPid:\t%d\n" @@ -190,6 +191,7 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns, "Gid:\t%d\t%d\t%d\t%d\n", get_task_state(p), task_tgid_nr_ns(p, ns), + task_numa_group_id(p), pid_nr_ns(pid, ns), ppid, tpid, from_kuid_munged(user_ns, cred->uid), diff --git a/include/linux/sched.h b/include/linux/sched.h index f587ded..b0b343b 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1452,12 +1452,17 @@ struct task_struct { #ifdef CONFIG_NUMA_BALANCING extern void task_numa_fault(int last_node, int node, int pages, bool migrated); +extern pid_t task_numa_group_id(struct task_struct *p); extern void set_numabalancing_state(bool enabled); #else static inline void task_numa_fault(int last_node, int node, int pages, bool migrated) { } +static inline pid_t task_numa_group_id(struct task_struct *p) +{ + return 0; +} static inline void set_numabalancing_state(bool enabled) { } diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 8556505..5bd309c 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -893,12 +893,18 @@ struct numa_group { spinlock_t lock; /* nr_tasks, tasks */ int nr_tasks; + pid_t gid; struct list_head task_list; struct rcu_head rcu; atomic_long_t faults[0]; }; +pid_t task_numa_group_id(struct task_struct *p) +{ + return p->numa_group ? p->numa_group->gid : 0; +} + static inline int task_faults_idx(int nid, int priv) { return 2 * nid + priv; @@ -1265,6 +1271,7 @@ static void task_numa_group(struct task_struct *p, int cpupid) atomic_set(&grp->refcount, 1); spin_lock_init(&grp->lock); INIT_LIST_HEAD(&grp->task_list); + grp->gid = p->pid; for (i = 0; i < 2*nr_node_ids; i++) atomic_long_set(&grp->faults[i], p->numa_faults[i]); -- cgit v0.10.2 From 7851a45cd3f6198bf542c30e27b330e8eeb3736c Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Mon, 7 Oct 2013 11:29:23 +0100 Subject: mm: numa: Copy cpupid on page migration After page migration, the new page has the nidpid unset. This makes every fault on a recently migrated page look like a first numa fault, leading to another page migration. Copying over the nidpid at page migration time should prevent erroneous migrations of recently migrated pages. Signed-off-by: Rik van Riel Signed-off-by: Mel Gorman Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-46-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/mm/migrate.c b/mm/migrate.c index ff53774..44c1fa9 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -443,6 +443,8 @@ int migrate_huge_page_move_mapping(struct address_space *mapping, */ void migrate_page_copy(struct page *newpage, struct page *page) { + int cpupid; + if (PageHuge(page) || PageTransHuge(page)) copy_huge_page(newpage, page); else @@ -479,6 +481,13 @@ void migrate_page_copy(struct page *newpage, struct page *page) __set_page_dirty_nobuffers(newpage); } + /* + * Copy NUMA information to the new page, to prevent over-eager + * future migrations of this same page. + */ + cpupid = page_cpupid_xchg_last(page, -1); + page_cpupid_xchg_last(newpage, cpupid); + mlock_migrate_page(newpage, page); ksm_migrate_page(newpage, page); /* -- cgit v0.10.2 From 6688cc05473b36a0a3d3971e1adf1712919b32eb Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 7 Oct 2013 11:29:24 +0100 Subject: mm: numa: Do not group on RO pages And here's a little something to make sure not the whole world ends up in a single group. As while we don't migrate shared executable pages, we do scan/fault on them. And since everybody links to libc, everybody ends up in the same group. Suggested-by: Rik van Riel Signed-off-by: Peter Zijlstra Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Link: http://lkml.kernel.org/r/1381141781-10992-47-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/include/linux/sched.h b/include/linux/sched.h index b0b343b..ff54385 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1450,13 +1450,16 @@ struct task_struct { /* Future-safe accessor for struct task_struct's cpus_allowed. */ #define tsk_cpus_allowed(tsk) (&(tsk)->cpus_allowed) +#define TNF_MIGRATED 0x01 +#define TNF_NO_GROUP 0x02 + #ifdef CONFIG_NUMA_BALANCING -extern void task_numa_fault(int last_node, int node, int pages, bool migrated); +extern void task_numa_fault(int last_node, int node, int pages, int flags); extern pid_t task_numa_group_id(struct task_struct *p); extern void set_numabalancing_state(bool enabled); #else static inline void task_numa_fault(int last_node, int node, int pages, - bool migrated) + int flags) { } static inline pid_t task_numa_group_id(struct task_struct *p) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 5bd309c..35661b8 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1361,9 +1361,10 @@ void task_numa_free(struct task_struct *p) /* * Got a PROT_NONE fault for a page on @node. */ -void task_numa_fault(int last_cpupid, int node, int pages, bool migrated) +void task_numa_fault(int last_cpupid, int node, int pages, int flags) { struct task_struct *p = current; + bool migrated = flags & TNF_MIGRATED; int priv; if (!numabalancing_enabled) @@ -1394,7 +1395,7 @@ void task_numa_fault(int last_cpupid, int node, int pages, bool migrated) priv = 1; } else { priv = cpupid_match_pid(p, last_cpupid); - if (!priv) + if (!priv && !(flags & TNF_NO_GROUP)) task_numa_group(p, last_cpupid); } diff --git a/mm/huge_memory.c b/mm/huge_memory.c index becf92c..7ab4e32 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -1285,6 +1285,7 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, int target_nid, last_cpupid = -1; bool page_locked; bool migrated = false; + int flags = 0; spin_lock(&mm->page_table_lock); if (unlikely(!pmd_same(pmd, *pmdp))) @@ -1299,6 +1300,14 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, count_vm_numa_event(NUMA_HINT_FAULTS_LOCAL); /* + * Avoid grouping on DSO/COW pages in specific and RO pages + * in general, RO pages shouldn't hurt as much anyway since + * they can be in shared cache state. + */ + if (!pmd_write(pmd)) + flags |= TNF_NO_GROUP; + + /* * Acquire the page lock to serialise THP migrations but avoid dropping * page_table_lock if at all possible */ @@ -1343,8 +1352,10 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, spin_unlock(&mm->page_table_lock); migrated = migrate_misplaced_transhuge_page(mm, vma, pmdp, pmd, addr, page, target_nid); - if (migrated) + if (migrated) { + flags |= TNF_MIGRATED; page_nid = target_nid; + } goto out; clear_pmdnuma: @@ -1362,7 +1373,7 @@ out: page_unlock_anon_vma_read(anon_vma); if (page_nid != -1) - task_numa_fault(last_cpupid, page_nid, HPAGE_PMD_NR, migrated); + task_numa_fault(last_cpupid, page_nid, HPAGE_PMD_NR, flags); return 0; } diff --git a/mm/memory.c b/mm/memory.c index c57efa2..eba846b 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -3547,6 +3547,7 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, int last_cpupid; int target_nid; bool migrated = false; + int flags = 0; /* * The "pte" at this point cannot be used safely without @@ -3575,6 +3576,14 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, } BUG_ON(is_zero_pfn(page_to_pfn(page))); + /* + * Avoid grouping on DSO/COW pages in specific and RO pages + * in general, RO pages shouldn't hurt as much anyway since + * they can be in shared cache state. + */ + if (!pte_write(pte)) + flags |= TNF_NO_GROUP; + last_cpupid = page_cpupid_last(page); page_nid = page_to_nid(page); target_nid = numa_migrate_prep(page, vma, addr, page_nid); @@ -3586,12 +3595,14 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, /* Migrate to the requested node */ migrated = migrate_misplaced_page(page, vma, target_nid); - if (migrated) + if (migrated) { page_nid = target_nid; + flags |= TNF_MIGRATED; + } out: if (page_nid != -1) - task_numa_fault(last_cpupid, page_nid, 1, migrated); + task_numa_fault(last_cpupid, page_nid, 1, flags); return 0; } @@ -3632,6 +3643,7 @@ static int do_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, int page_nid = -1; int target_nid; bool migrated = false; + int flags = 0; if (!pte_present(pteval)) continue; @@ -3651,20 +3663,30 @@ static int do_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, if (unlikely(!page)) continue; + /* + * Avoid grouping on DSO/COW pages in specific and RO pages + * in general, RO pages shouldn't hurt as much anyway since + * they can be in shared cache state. + */ + if (!pte_write(pteval)) + flags |= TNF_NO_GROUP; + last_cpupid = page_cpupid_last(page); page_nid = page_to_nid(page); target_nid = numa_migrate_prep(page, vma, addr, page_nid); pte_unmap_unlock(pte, ptl); if (target_nid != -1) { migrated = migrate_misplaced_page(page, vma, target_nid); - if (migrated) + if (migrated) { page_nid = target_nid; + flags |= TNF_MIGRATED; + } } else { put_page(page); } if (page_nid != -1) - task_numa_fault(last_cpupid, page_nid, 1, migrated); + task_numa_fault(last_cpupid, page_nid, 1, flags); pte = pte_offset_map_lock(mm, pmdp, addr, &ptl); } -- cgit v0.10.2 From 0f19c17929c952c6f0966d93ab05558e7bf814cc Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:29:25 +0100 Subject: mm: numa: Do not batch handle PMD pages With the THP migration races closed it is still possible to occasionally see corruption. The problem is related to handling PMD pages in batch. When a page fault is handled it can be assumed that the page being faulted will also be flushed from the TLB. The same flushing does not happen when handling PMD pages in batch. Fixing is straight forward but there are a number of reasons not to 1. Multiple TLB flushes may have to be sent depending on what pages get migrated 2. The handling of PMDs in batch means that faults get accounted to the task that is handling the fault. While care is taken to only mark PMDs where the last CPU and PID match it can still have problems due to PID truncation when matching PIDs. 3. Batching on the PMD level may reduce faults but setting pmd_numa requires taking a heavy lock that can contend with THP migration and handling the fault requires the release/acquisition of the PTL for every page migrated. It's still pretty heavy. PMD batch handling is not something that people ever have been happy with. This patch removes it and later patches will deal with the additional fault overhead using more installigent migrate rate adaption. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-48-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/mm/memory.c b/mm/memory.c index eba846b..9898eeb 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -3606,103 +3606,6 @@ out: return 0; } -/* NUMA hinting page fault entry point for regular pmds */ -#ifdef CONFIG_NUMA_BALANCING -static int do_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, - unsigned long addr, pmd_t *pmdp) -{ - pmd_t pmd; - pte_t *pte, *orig_pte; - unsigned long _addr = addr & PMD_MASK; - unsigned long offset; - spinlock_t *ptl; - bool numa = false; - int last_cpupid; - - spin_lock(&mm->page_table_lock); - pmd = *pmdp; - if (pmd_numa(pmd)) { - set_pmd_at(mm, _addr, pmdp, pmd_mknonnuma(pmd)); - numa = true; - } - spin_unlock(&mm->page_table_lock); - - if (!numa) - return 0; - - /* we're in a page fault so some vma must be in the range */ - BUG_ON(!vma); - BUG_ON(vma->vm_start >= _addr + PMD_SIZE); - offset = max(_addr, vma->vm_start) & ~PMD_MASK; - VM_BUG_ON(offset >= PMD_SIZE); - orig_pte = pte = pte_offset_map_lock(mm, pmdp, _addr, &ptl); - pte += offset >> PAGE_SHIFT; - for (addr = _addr + offset; addr < _addr + PMD_SIZE; pte++, addr += PAGE_SIZE) { - pte_t pteval = *pte; - struct page *page; - int page_nid = -1; - int target_nid; - bool migrated = false; - int flags = 0; - - if (!pte_present(pteval)) - continue; - if (!pte_numa(pteval)) - continue; - if (addr >= vma->vm_end) { - vma = find_vma(mm, addr); - /* there's a pte present so there must be a vma */ - BUG_ON(!vma); - BUG_ON(addr < vma->vm_start); - } - if (pte_numa(pteval)) { - pteval = pte_mknonnuma(pteval); - set_pte_at(mm, addr, pte, pteval); - } - page = vm_normal_page(vma, addr, pteval); - if (unlikely(!page)) - continue; - - /* - * Avoid grouping on DSO/COW pages in specific and RO pages - * in general, RO pages shouldn't hurt as much anyway since - * they can be in shared cache state. - */ - if (!pte_write(pteval)) - flags |= TNF_NO_GROUP; - - last_cpupid = page_cpupid_last(page); - page_nid = page_to_nid(page); - target_nid = numa_migrate_prep(page, vma, addr, page_nid); - pte_unmap_unlock(pte, ptl); - if (target_nid != -1) { - migrated = migrate_misplaced_page(page, vma, target_nid); - if (migrated) { - page_nid = target_nid; - flags |= TNF_MIGRATED; - } - } else { - put_page(page); - } - - if (page_nid != -1) - task_numa_fault(last_cpupid, page_nid, 1, flags); - - pte = pte_offset_map_lock(mm, pmdp, addr, &ptl); - } - pte_unmap_unlock(orig_pte, ptl); - - return 0; -} -#else -static int do_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, - unsigned long addr, pmd_t *pmdp) -{ - BUG(); - return 0; -} -#endif /* CONFIG_NUMA_BALANCING */ - /* * These routines also need to handle stuff like marking pages dirty * and/or accessed for architectures that don't do it in hardware (most @@ -3841,8 +3744,8 @@ retry: } } - if (pmd_numa(*pmd)) - return do_pmd_numa_page(mm, vma, address, pmd); + /* THP should already have been handled */ + BUG_ON(pmd_numa(*pmd)); /* * Use __pte_alloc instead of pte_alloc_map, because we can't diff --git a/mm/mprotect.c b/mm/mprotect.c index 9a74855..a0302ac 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -37,15 +37,12 @@ static inline pgprot_t pgprot_modify(pgprot_t oldprot, pgprot_t newprot) static unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd, unsigned long addr, unsigned long end, pgprot_t newprot, - int dirty_accountable, int prot_numa, bool *ret_all_same_cpupid) + int dirty_accountable, int prot_numa) { struct mm_struct *mm = vma->vm_mm; pte_t *pte, oldpte; spinlock_t *ptl; unsigned long pages = 0; - bool all_same_cpupid = true; - int last_cpu = -1; - int last_pid = -1; pte = pte_offset_map_lock(mm, pmd, addr, &ptl); arch_enter_lazy_mmu_mode(); @@ -64,19 +61,6 @@ static unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd, page = vm_normal_page(vma, addr, oldpte); if (page) { - int cpupid = page_cpupid_last(page); - int this_cpu = cpupid_to_cpu(cpupid); - int this_pid = cpupid_to_pid(cpupid); - - if (last_cpu == -1) - last_cpu = this_cpu; - if (last_pid == -1) - last_pid = this_pid; - if (last_cpu != this_cpu || - last_pid != this_pid) { - all_same_cpupid = false; - } - if (!pte_numa(oldpte)) { ptent = pte_mknuma(ptent); updated = true; @@ -115,26 +99,9 @@ static unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd, arch_leave_lazy_mmu_mode(); pte_unmap_unlock(pte - 1, ptl); - *ret_all_same_cpupid = all_same_cpupid; return pages; } -#ifdef CONFIG_NUMA_BALANCING -static inline void change_pmd_protnuma(struct mm_struct *mm, unsigned long addr, - pmd_t *pmd) -{ - spin_lock(&mm->page_table_lock); - set_pmd_at(mm, addr & PMD_MASK, pmd, pmd_mknuma(*pmd)); - spin_unlock(&mm->page_table_lock); -} -#else -static inline void change_pmd_protnuma(struct mm_struct *mm, unsigned long addr, - pmd_t *pmd) -{ - BUG(); -} -#endif /* CONFIG_NUMA_BALANCING */ - static inline unsigned long change_pmd_range(struct vm_area_struct *vma, pud_t *pud, unsigned long addr, unsigned long end, pgprot_t newprot, int dirty_accountable, int prot_numa) @@ -142,7 +109,6 @@ static inline unsigned long change_pmd_range(struct vm_area_struct *vma, pmd_t *pmd; unsigned long next; unsigned long pages = 0; - bool all_same_cpupid; pmd = pmd_offset(pud, addr); do { @@ -168,17 +134,8 @@ static inline unsigned long change_pmd_range(struct vm_area_struct *vma, if (pmd_none_or_clear_bad(pmd)) continue; this_pages = change_pte_range(vma, pmd, addr, next, newprot, - dirty_accountable, prot_numa, &all_same_cpupid); + dirty_accountable, prot_numa); pages += this_pages; - - /* - * If we are changing protections for NUMA hinting faults then - * set pmd_numa if the examined pages were all on the same - * node. This allows a regular PMD to be handled as one fault - * and effectively batches the taking of the PTL - */ - if (prot_numa && this_pages && all_same_cpupid) - change_pmd_protnuma(vma->vm_mm, addr, pmd); } while (pmd++, addr = next, addr != end); return pages; -- cgit v0.10.2 From 5e1576ed0e54d419286a8096133029062b6ad456 Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Mon, 7 Oct 2013 11:29:26 +0100 Subject: sched/numa: Stay on the same node if CLONE_VM A newly spawned thread inside a process should stay on the same NUMA node as its parent. This prevents processes from being "torn" across multiple NUMA nodes every time they spawn a new thread. Signed-off-by: Rik van Riel Signed-off-by: Mel Gorman Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-49-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/include/linux/sched.h b/include/linux/sched.h index ff54385..8563e3d 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2021,7 +2021,7 @@ extern void wake_up_new_task(struct task_struct *tsk); #else static inline void kick_process(struct task_struct *tsk) { } #endif -extern void sched_fork(struct task_struct *p); +extern void sched_fork(unsigned long clone_flags, struct task_struct *p); extern void sched_dead(struct task_struct *p); extern void proc_caches_init(void); diff --git a/kernel/fork.c b/kernel/fork.c index 7192d91..c93be06 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1310,7 +1310,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, #endif /* Perform scheduler related setup. Assign this task to a CPU. */ - sched_fork(p); + sched_fork(clone_flags, p); retval = perf_event_init_task(p); if (retval) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 51092d5..3e2c893 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -1696,7 +1696,7 @@ int wake_up_state(struct task_struct *p, unsigned int state) * * __sched_fork() is basic setup used by init_idle() too: */ -static void __sched_fork(struct task_struct *p) +static void __sched_fork(unsigned long clone_flags, struct task_struct *p) { p->on_rq = 0; @@ -1725,11 +1725,15 @@ static void __sched_fork(struct task_struct *p) p->mm->numa_scan_seq = 0; } + if (clone_flags & CLONE_VM) + p->numa_preferred_nid = current->numa_preferred_nid; + else + p->numa_preferred_nid = -1; + p->node_stamp = 0ULL; p->numa_scan_seq = p->mm ? p->mm->numa_scan_seq : 0; p->numa_migrate_seq = 1; p->numa_scan_period = sysctl_numa_balancing_scan_delay; - p->numa_preferred_nid = -1; p->numa_work.next = &p->numa_work; p->numa_faults = NULL; p->numa_faults_buffer = NULL; @@ -1761,12 +1765,12 @@ void set_numabalancing_state(bool enabled) /* * fork()/clone()-time setup: */ -void sched_fork(struct task_struct *p) +void sched_fork(unsigned long clone_flags, struct task_struct *p) { unsigned long flags; int cpu = get_cpu(); - __sched_fork(p); + __sched_fork(clone_flags, p); /* * We mark the process as running here. This guarantees that * nobody will actually run it, and a signal or other external @@ -4287,7 +4291,7 @@ void init_idle(struct task_struct *idle, int cpu) raw_spin_lock_irqsave(&rq->lock, flags); - __sched_fork(idle); + __sched_fork(0, idle); idle->state = TASK_RUNNING; idle->se.exec_start = sched_clock(); -- cgit v0.10.2 From 83e1d2cd9eabec5164afea295ff06b941ae8e4a9 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:29:27 +0100 Subject: sched/numa: Use group fault statistics in numa placement This patch uses the fraction of faults on a particular node for both task and group, to figure out the best node to place a task. If the task and group statistics disagree on what the preferred node should be then a full rescan will select the node with the best combined weight. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-50-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/include/linux/sched.h b/include/linux/sched.h index 8563e3d..7244822 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1356,6 +1356,7 @@ struct task_struct { * The values remain static for the duration of a PTE scan */ unsigned long *numa_faults; + unsigned long total_numa_faults; /* * numa_faults_buffer records faults per node during the current diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 35661b8..4c40e13 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -897,6 +897,7 @@ struct numa_group { struct list_head task_list; struct rcu_head rcu; + atomic_long_t total_faults; atomic_long_t faults[0]; }; @@ -919,6 +920,51 @@ static inline unsigned long task_faults(struct task_struct *p, int nid) p->numa_faults[task_faults_idx(nid, 1)]; } +static inline unsigned long group_faults(struct task_struct *p, int nid) +{ + if (!p->numa_group) + return 0; + + return atomic_long_read(&p->numa_group->faults[2*nid]) + + atomic_long_read(&p->numa_group->faults[2*nid+1]); +} + +/* + * These return the fraction of accesses done by a particular task, or + * task group, on a particular numa node. The group weight is given a + * larger multiplier, in order to group tasks together that are almost + * evenly spread out between numa nodes. + */ +static inline unsigned long task_weight(struct task_struct *p, int nid) +{ + unsigned long total_faults; + + if (!p->numa_faults) + return 0; + + total_faults = p->total_numa_faults; + + if (!total_faults) + return 0; + + return 1000 * task_faults(p, nid) / total_faults; +} + +static inline unsigned long group_weight(struct task_struct *p, int nid) +{ + unsigned long total_faults; + + if (!p->numa_group) + return 0; + + total_faults = atomic_long_read(&p->numa_group->total_faults); + + if (!total_faults) + return 0; + + return 1200 * group_faults(p, nid) / total_faults; +} + static unsigned long weighted_cpuload(const int cpu); static unsigned long source_load(int cpu, int type); static unsigned long target_load(int cpu, int type); @@ -1018,8 +1064,10 @@ static void task_numa_compare(struct task_numa_env *env, long imp) if (!cpumask_test_cpu(env->src_cpu, tsk_cpus_allowed(cur))) goto unlock; - imp += task_faults(cur, env->src_nid) - - task_faults(cur, env->dst_nid); + imp += task_weight(cur, env->src_nid) + + group_weight(cur, env->src_nid) - + task_weight(cur, env->dst_nid) - + group_weight(cur, env->dst_nid); } if (imp < env->best_imp) @@ -1098,7 +1146,7 @@ static int task_numa_migrate(struct task_struct *p) .best_cpu = -1 }; struct sched_domain *sd; - unsigned long faults; + unsigned long weight; int nid, ret; long imp; @@ -1115,10 +1163,10 @@ static int task_numa_migrate(struct task_struct *p) env.imbalance_pct = 100 + (sd->imbalance_pct - 100) / 2; rcu_read_unlock(); - faults = task_faults(p, env.src_nid); + weight = task_weight(p, env.src_nid) + group_weight(p, env.src_nid); update_numa_stats(&env.src_stats, env.src_nid); env.dst_nid = p->numa_preferred_nid; - imp = task_faults(env.p, env.dst_nid) - faults; + imp = task_weight(p, env.dst_nid) + group_weight(p, env.dst_nid) - weight; update_numa_stats(&env.dst_stats, env.dst_nid); /* If the preferred nid has capacity, try to use it. */ @@ -1131,8 +1179,8 @@ static int task_numa_migrate(struct task_struct *p) if (nid == env.src_nid || nid == p->numa_preferred_nid) continue; - /* Only consider nodes that recorded more faults */ - imp = task_faults(env.p, nid) - faults; + /* Only consider nodes where both task and groups benefit */ + imp = task_weight(p, nid) + group_weight(p, nid) - weight; if (imp < 0) continue; @@ -1183,8 +1231,8 @@ static void numa_migrate_preferred(struct task_struct *p) static void task_numa_placement(struct task_struct *p) { - int seq, nid, max_nid = -1; - unsigned long max_faults = 0; + int seq, nid, max_nid = -1, max_group_nid = -1; + unsigned long max_faults = 0, max_group_faults = 0; seq = ACCESS_ONCE(p->mm->numa_scan_seq); if (p->numa_scan_seq == seq) @@ -1195,7 +1243,7 @@ static void task_numa_placement(struct task_struct *p) /* Find the node with the highest number of faults */ for_each_online_node(nid) { - unsigned long faults = 0; + unsigned long faults = 0, group_faults = 0; int priv, i; for (priv = 0; priv < 2; priv++) { @@ -1211,9 +1259,12 @@ static void task_numa_placement(struct task_struct *p) faults += p->numa_faults[i]; diff += p->numa_faults[i]; + p->total_numa_faults += diff; if (p->numa_group) { /* safe because we can only change our own group */ atomic_long_add(diff, &p->numa_group->faults[i]); + atomic_long_add(diff, &p->numa_group->total_faults); + group_faults += atomic_long_read(&p->numa_group->faults[i]); } } @@ -1221,6 +1272,27 @@ static void task_numa_placement(struct task_struct *p) max_faults = faults; max_nid = nid; } + + if (group_faults > max_group_faults) { + max_group_faults = group_faults; + max_group_nid = nid; + } + } + + /* + * If the preferred task and group nids are different, + * iterate over the nodes again to find the best place. + */ + if (p->numa_group && max_nid != max_group_nid) { + unsigned long weight, max_weight = 0; + + for_each_online_node(nid) { + weight = task_weight(p, nid) + group_weight(p, nid); + if (weight > max_weight) { + max_weight = weight; + max_nid = nid; + } + } } /* Preferred node as the node with the most faults */ @@ -1276,6 +1348,8 @@ static void task_numa_group(struct task_struct *p, int cpupid) for (i = 0; i < 2*nr_node_ids; i++) atomic_long_set(&grp->faults[i], p->numa_faults[i]); + atomic_long_set(&grp->total_faults, p->total_numa_faults); + list_add(&p->numa_entry, &grp->task_list); grp->nr_tasks++; rcu_assign_pointer(p->numa_group, grp); @@ -1323,6 +1397,8 @@ unlock: atomic_long_sub(p->numa_faults[i], &my_grp->faults[i]); atomic_long_add(p->numa_faults[i], &grp->faults[i]); } + atomic_long_sub(p->total_numa_faults, &my_grp->total_faults); + atomic_long_add(p->total_numa_faults, &grp->total_faults); double_lock(&my_grp->lock, &grp->lock); @@ -1347,6 +1423,8 @@ void task_numa_free(struct task_struct *p) for (i = 0; i < 2*nr_node_ids; i++) atomic_long_sub(p->numa_faults[i], &grp->faults[i]); + atomic_long_sub(p->total_numa_faults, &grp->total_faults); + spin_lock(&grp->lock); list_del(&p->numa_entry); grp->nr_tasks--; @@ -1385,6 +1463,7 @@ void task_numa_fault(int last_cpupid, int node, int pages, int flags) BUG_ON(p->numa_faults_buffer); p->numa_faults_buffer = p->numa_faults + (2 * nr_node_ids); + p->total_numa_faults = 0; } /* @@ -4572,12 +4651,17 @@ static bool migrate_improves_locality(struct task_struct *p, struct lb_env *env) src_nid = cpu_to_node(env->src_cpu); dst_nid = cpu_to_node(env->dst_cpu); - if (src_nid == dst_nid || - p->numa_migrate_seq >= sysctl_numa_balancing_settle_count) + if (src_nid == dst_nid) return false; - if (dst_nid == p->numa_preferred_nid || - task_faults(p, dst_nid) > task_faults(p, src_nid)) + /* Always encourage migration to the preferred node. */ + if (dst_nid == p->numa_preferred_nid) + return true; + + /* After the task has settled, check if the new node is better. */ + if (p->numa_migrate_seq >= sysctl_numa_balancing_settle_count && + task_weight(p, dst_nid) + group_weight(p, dst_nid) > + task_weight(p, src_nid) + group_weight(p, src_nid)) return true; return false; @@ -4597,11 +4681,17 @@ static bool migrate_degrades_locality(struct task_struct *p, struct lb_env *env) src_nid = cpu_to_node(env->src_cpu); dst_nid = cpu_to_node(env->dst_cpu); - if (src_nid == dst_nid || - p->numa_migrate_seq >= sysctl_numa_balancing_settle_count) + if (src_nid == dst_nid) return false; - if (task_faults(p, dst_nid) < task_faults(p, src_nid)) + /* Migrating away from the preferred node is always bad. */ + if (src_nid == p->numa_preferred_nid) + return true; + + /* After the task has settled, check if the new node is worse. */ + if (p->numa_migrate_seq >= sysctl_numa_balancing_settle_count && + task_weight(p, dst_nid) + group_weight(p, dst_nid) < + task_weight(p, src_nid) + group_weight(p, src_nid)) return true; return false; -- cgit v0.10.2 From 82727018b0d33d188e9916bcf76f18387484cb04 Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Mon, 7 Oct 2013 11:29:28 +0100 Subject: sched/numa: Call task_numa_free() from do_execve() It is possible for a task in a numa group to call exec, and have the new (unrelated) executable inherit the numa group association from its former self. This has the potential to break numa grouping, and is trivial to fix. Signed-off-by: Rik van Riel Signed-off-by: Mel Gorman Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-51-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/fs/exec.c b/fs/exec.c index 8875dd1..2ea437e 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1547,6 +1547,7 @@ static int do_execve_common(const char *filename, current->fs->in_exec = 0; current->in_execve = 0; acct_update_integrals(current); + task_numa_free(current); free_bprm(bprm); if (displaced) put_files_struct(displaced); diff --git a/include/linux/sched.h b/include/linux/sched.h index 7244822..f638510 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1458,6 +1458,7 @@ struct task_struct { extern void task_numa_fault(int last_node, int node, int pages, int flags); extern pid_t task_numa_group_id(struct task_struct *p); extern void set_numabalancing_state(bool enabled); +extern void task_numa_free(struct task_struct *p); #else static inline void task_numa_fault(int last_node, int node, int pages, int flags) @@ -1470,6 +1471,9 @@ static inline pid_t task_numa_group_id(struct task_struct *p) static inline void set_numabalancing_state(bool enabled) { } +static inline void task_numa_free(struct task_struct *p) +{ +} #endif static inline struct pid *task_pid(struct task_struct *task) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 4c40e13..c4df2de 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1418,6 +1418,7 @@ void task_numa_free(struct task_struct *p) { struct numa_group *grp = p->numa_group; int i; + void *numa_faults = p->numa_faults; if (grp) { for (i = 0; i < 2*nr_node_ids; i++) @@ -1433,7 +1434,9 @@ void task_numa_free(struct task_struct *p) put_numa_group(grp); } - kfree(p->numa_faults); + p->numa_faults = NULL; + p->numa_faults_buffer = NULL; + kfree(numa_faults); } /* @@ -1452,6 +1455,10 @@ void task_numa_fault(int last_cpupid, int node, int pages, int flags) if (!p->mm) return; + /* Do not worry about placement if exiting */ + if (p->state == TASK_DEAD) + return; + /* Allocate buffer to track faults on a per-node basis */ if (unlikely(!p->numa_faults)) { int size = sizeof(*p->numa_faults) * 2 * nr_node_ids; diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 8037b10..eeb1923 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -559,11 +559,6 @@ static inline u64 rq_clock_task(struct rq *rq) #ifdef CONFIG_NUMA_BALANCING extern int migrate_task_to(struct task_struct *p, int cpu); extern int migrate_swap(struct task_struct *, struct task_struct *); -extern void task_numa_free(struct task_struct *p); -#else /* CONFIG_NUMA_BALANCING */ -static inline void task_numa_free(struct task_struct *p) -{ -} #endif /* CONFIG_NUMA_BALANCING */ #ifdef CONFIG_SMP -- cgit v0.10.2 From 7dbd13ed06513b047216a7ffc718bad9df0660f1 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:29:29 +0100 Subject: sched/numa: Prevent parallel updates to group stats during placement Having multiple tasks in a group go through task_numa_placement simultaneously can lead to a task picking a wrong node to run on, because the group stats may be in the middle of an update. This patch avoids parallel updates by holding the numa_group lock during placement decisions. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-52-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index c4df2de..1473499 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1233,6 +1233,7 @@ static void task_numa_placement(struct task_struct *p) { int seq, nid, max_nid = -1, max_group_nid = -1; unsigned long max_faults = 0, max_group_faults = 0; + spinlock_t *group_lock = NULL; seq = ACCESS_ONCE(p->mm->numa_scan_seq); if (p->numa_scan_seq == seq) @@ -1241,6 +1242,12 @@ static void task_numa_placement(struct task_struct *p) p->numa_migrate_seq++; p->numa_scan_period_max = task_scan_max(p); + /* If the task is part of a group prevent parallel updates to group stats */ + if (p->numa_group) { + group_lock = &p->numa_group->lock; + spin_lock(group_lock); + } + /* Find the node with the highest number of faults */ for_each_online_node(nid) { unsigned long faults = 0, group_faults = 0; @@ -1279,20 +1286,24 @@ static void task_numa_placement(struct task_struct *p) } } - /* - * If the preferred task and group nids are different, - * iterate over the nodes again to find the best place. - */ - if (p->numa_group && max_nid != max_group_nid) { - unsigned long weight, max_weight = 0; - - for_each_online_node(nid) { - weight = task_weight(p, nid) + group_weight(p, nid); - if (weight > max_weight) { - max_weight = weight; - max_nid = nid; + if (p->numa_group) { + /* + * If the preferred task and group nids are different, + * iterate over the nodes again to find the best place. + */ + if (max_nid != max_group_nid) { + unsigned long weight, max_weight = 0; + + for_each_online_node(nid) { + weight = task_weight(p, nid) + group_weight(p, nid); + if (weight > max_weight) { + max_weight = weight; + max_nid = nid; + } } } + + spin_unlock(group_lock); } /* Preferred node as the node with the most faults */ -- cgit v0.10.2 From b32e86b4301e345611f0446265f782a229faadf6 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 7 Oct 2013 11:29:30 +0100 Subject: sched/numa: Add debugging Signed-off-by: Ingo Molnar Reviewed-by: Rik van Riel Cc: Johannes Weiner Cc: Mel Gorman Cc: Srikar Dronamraju Cc: Andrea Arcangeli Signed-off-by: Peter Zijlstra Signed-off-by: Ingo Molnar Link: http://lkml.kernel.org/r/1381141781-10992-53-git-send-email-mgorman@suse.de diff --git a/include/linux/sched.h b/include/linux/sched.h index f638510..1127a46 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1366,6 +1366,7 @@ struct task_struct { unsigned long *numa_faults_buffer; int numa_preferred_nid; + unsigned long numa_pages_migrated; #endif /* CONFIG_NUMA_BALANCING */ struct rcu_head rcu; @@ -2661,6 +2662,11 @@ static inline unsigned int task_cpu(const struct task_struct *p) return task_thread_info(p)->cpu; } +static inline int task_node(const struct task_struct *p) +{ + return cpu_to_node(task_cpu(p)); +} + extern void set_task_cpu(struct task_struct *p, unsigned int cpu); #else diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c index 1965599..e6ba5e3 100644 --- a/kernel/sched/debug.c +++ b/kernel/sched/debug.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "sched.h" @@ -137,6 +138,9 @@ print_task(struct seq_file *m, struct rq *rq, struct task_struct *p) SEQ_printf(m, "%15Ld %15Ld %15Ld.%06ld %15Ld.%06ld %15Ld.%06ld", 0LL, 0LL, 0LL, 0L, 0LL, 0L, 0LL, 0L); #endif +#ifdef CONFIG_NUMA_BALANCING + SEQ_printf(m, " %d", cpu_to_node(task_cpu(p))); +#endif #ifdef CONFIG_CGROUP_SCHED SEQ_printf(m, " %s", task_group_path(task_group(p))); #endif @@ -159,7 +163,7 @@ static void print_rq(struct seq_file *m, struct rq *rq, int rq_cpu) read_lock_irqsave(&tasklist_lock, flags); do_each_thread(g, p) { - if (!p->on_rq || task_cpu(p) != rq_cpu) + if (task_cpu(p) != rq_cpu) continue; print_task(m, rq, p); @@ -345,7 +349,7 @@ static void sched_debug_header(struct seq_file *m) cpu_clk = local_clock(); local_irq_restore(flags); - SEQ_printf(m, "Sched Debug Version: v0.10, %s %.*s\n", + SEQ_printf(m, "Sched Debug Version: v0.11, %s %.*s\n", init_utsname()->release, (int)strcspn(init_utsname()->version, " "), init_utsname()->version); @@ -488,6 +492,56 @@ static int __init init_sched_debug_procfs(void) __initcall(init_sched_debug_procfs); +#define __P(F) \ + SEQ_printf(m, "%-45s:%21Ld\n", #F, (long long)F) +#define P(F) \ + SEQ_printf(m, "%-45s:%21Ld\n", #F, (long long)p->F) +#define __PN(F) \ + SEQ_printf(m, "%-45s:%14Ld.%06ld\n", #F, SPLIT_NS((long long)F)) +#define PN(F) \ + SEQ_printf(m, "%-45s:%14Ld.%06ld\n", #F, SPLIT_NS((long long)p->F)) + + +static void sched_show_numa(struct task_struct *p, struct seq_file *m) +{ +#ifdef CONFIG_NUMA_BALANCING + struct mempolicy *pol; + int node, i; + + if (p->mm) + P(mm->numa_scan_seq); + + task_lock(p); + pol = p->mempolicy; + if (pol && !(pol->flags & MPOL_F_MORON)) + pol = NULL; + mpol_get(pol); + task_unlock(p); + + SEQ_printf(m, "numa_migrations, %ld\n", xchg(&p->numa_pages_migrated, 0)); + + for_each_online_node(node) { + for (i = 0; i < 2; i++) { + unsigned long nr_faults = -1; + int cpu_current, home_node; + + if (p->numa_faults) + nr_faults = p->numa_faults[2*node + i]; + + cpu_current = !i ? (task_node(p) == node) : + (pol && node_isset(node, pol->v.nodes)); + + home_node = (p->numa_preferred_nid == node); + + SEQ_printf(m, "numa_faults, %d, %d, %d, %d, %ld\n", + i, node, cpu_current, home_node, nr_faults); + } + } + + mpol_put(pol); +#endif +} + void proc_sched_show_task(struct task_struct *p, struct seq_file *m) { unsigned long nr_switches; @@ -591,6 +645,8 @@ void proc_sched_show_task(struct task_struct *p, struct seq_file *m) SEQ_printf(m, "%-45s:%21Ld\n", "clock-delta", (long long)(t1-t0)); } + + sched_show_numa(p, m); } void proc_sched_set_task(struct task_struct *p) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 1473499..2876a37 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1137,7 +1137,7 @@ static int task_numa_migrate(struct task_struct *p) .p = p, .src_cpu = task_cpu(p), - .src_nid = cpu_to_node(task_cpu(p)), + .src_nid = task_node(p), .imbalance_pct = 112, @@ -1515,6 +1515,9 @@ void task_numa_fault(int last_cpupid, int node, int pages, int flags) if (p->numa_migrate_retry && time_after(jiffies, p->numa_migrate_retry)) numa_migrate_preferred(p); + if (migrated) + p->numa_pages_migrated += pages; + p->numa_faults_buffer[task_faults_idx(node, priv)] += pages; } -- cgit v0.10.2 From 887c290e82e8950d854730c084904c115fc367ac Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Mon, 7 Oct 2013 11:29:31 +0100 Subject: sched/numa: Decide whether to favour task or group weights based on swap candidate relationships This patch separately considers task and group affinities when searching for swap candidates during task NUMA placement. If tasks are not part of a group or the same group then the task weights are considered. Otherwise the group weights are compared. Signed-off-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Mel Gorman Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-54-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 2876a37..6f45461 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1039,13 +1039,15 @@ static void task_numa_assign(struct task_numa_env *env, * into account that it might be best if task running on the dst_cpu should * be exchanged with the source task */ -static void task_numa_compare(struct task_numa_env *env, long imp) +static void task_numa_compare(struct task_numa_env *env, + long taskimp, long groupimp) { struct rq *src_rq = cpu_rq(env->src_cpu); struct rq *dst_rq = cpu_rq(env->dst_cpu); struct task_struct *cur; long dst_load, src_load; long load; + long imp = (groupimp > 0) ? groupimp : taskimp; rcu_read_lock(); cur = ACCESS_ONCE(dst_rq->curr); @@ -1064,10 +1066,19 @@ static void task_numa_compare(struct task_numa_env *env, long imp) if (!cpumask_test_cpu(env->src_cpu, tsk_cpus_allowed(cur))) goto unlock; - imp += task_weight(cur, env->src_nid) + - group_weight(cur, env->src_nid) - - task_weight(cur, env->dst_nid) - - group_weight(cur, env->dst_nid); + /* + * If dst and source tasks are in the same NUMA group, or not + * in any group then look only at task weights otherwise give + * priority to the group weights. + */ + if (!cur->numa_group || !env->p->numa_group || + cur->numa_group == env->p->numa_group) { + imp = taskimp + task_weight(cur, env->src_nid) - + task_weight(cur, env->dst_nid); + } else { + imp = groupimp + group_weight(cur, env->src_nid) - + group_weight(cur, env->dst_nid); + } } if (imp < env->best_imp) @@ -1117,7 +1128,8 @@ unlock: rcu_read_unlock(); } -static void task_numa_find_cpu(struct task_numa_env *env, long imp) +static void task_numa_find_cpu(struct task_numa_env *env, + long taskimp, long groupimp) { int cpu; @@ -1127,7 +1139,7 @@ static void task_numa_find_cpu(struct task_numa_env *env, long imp) continue; env->dst_cpu = cpu; - task_numa_compare(env, imp); + task_numa_compare(env, taskimp, groupimp); } } @@ -1146,9 +1158,9 @@ static int task_numa_migrate(struct task_struct *p) .best_cpu = -1 }; struct sched_domain *sd; - unsigned long weight; + unsigned long taskweight, groupweight; int nid, ret; - long imp; + long taskimp, groupimp; /* * Pick the lowest SD_NUMA domain, as that would have the smallest @@ -1163,15 +1175,17 @@ static int task_numa_migrate(struct task_struct *p) env.imbalance_pct = 100 + (sd->imbalance_pct - 100) / 2; rcu_read_unlock(); - weight = task_weight(p, env.src_nid) + group_weight(p, env.src_nid); + taskweight = task_weight(p, env.src_nid); + groupweight = group_weight(p, env.src_nid); update_numa_stats(&env.src_stats, env.src_nid); env.dst_nid = p->numa_preferred_nid; - imp = task_weight(p, env.dst_nid) + group_weight(p, env.dst_nid) - weight; + taskimp = task_weight(p, env.dst_nid) - taskweight; + groupimp = group_weight(p, env.dst_nid) - groupweight; update_numa_stats(&env.dst_stats, env.dst_nid); /* If the preferred nid has capacity, try to use it. */ if (env.dst_stats.has_capacity) - task_numa_find_cpu(&env, imp); + task_numa_find_cpu(&env, taskimp, groupimp); /* No space available on the preferred nid. Look elsewhere. */ if (env.best_cpu == -1) { @@ -1180,13 +1194,14 @@ static int task_numa_migrate(struct task_struct *p) continue; /* Only consider nodes where both task and groups benefit */ - imp = task_weight(p, nid) + group_weight(p, nid) - weight; - if (imp < 0) + taskimp = task_weight(p, nid) - taskweight; + groupimp = group_weight(p, nid) - groupweight; + if (taskimp < 0 && groupimp < 0) continue; env.dst_nid = nid; update_numa_stats(&env.dst_stats, env.dst_nid); - task_numa_find_cpu(&env, imp); + task_numa_find_cpu(&env, taskimp, groupimp); } } @@ -4679,10 +4694,9 @@ static bool migrate_improves_locality(struct task_struct *p, struct lb_env *env) if (dst_nid == p->numa_preferred_nid) return true; - /* After the task has settled, check if the new node is better. */ - if (p->numa_migrate_seq >= sysctl_numa_balancing_settle_count && - task_weight(p, dst_nid) + group_weight(p, dst_nid) > - task_weight(p, src_nid) + group_weight(p, src_nid)) + /* If both task and group weight improve, this move is a winner. */ + if (task_weight(p, dst_nid) > task_weight(p, src_nid) && + group_weight(p, dst_nid) > group_weight(p, src_nid)) return true; return false; @@ -4709,10 +4723,9 @@ static bool migrate_degrades_locality(struct task_struct *p, struct lb_env *env) if (src_nid == p->numa_preferred_nid) return true; - /* After the task has settled, check if the new node is worse. */ - if (p->numa_migrate_seq >= sysctl_numa_balancing_settle_count && - task_weight(p, dst_nid) + group_weight(p, dst_nid) < - task_weight(p, src_nid) + group_weight(p, src_nid)) + /* If either task or group weight get worse, don't do it. */ + if (task_weight(p, dst_nid) < task_weight(p, src_nid) || + group_weight(p, dst_nid) < group_weight(p, src_nid)) return true; return false; -- cgit v0.10.2 From ca28aa53dd95868c9e38917b9881c09dacfacf1a Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Mon, 7 Oct 2013 11:29:32 +0100 Subject: sched/numa: Fix task or group comparison This patch separately considers task and group affinities when searching for swap candidates during NUMA placement. If tasks are part of the same group, or no group at all, the task weights are considered. Some hysteresis is added to prevent tasks within one group from getting bounced between NUMA nodes due to tiny differences. If tasks are part of different groups, the code compares group weights, in order to favor grouping task groups together. The patch also changes the group weight multiplier to be the same as the task weight multiplier, since the two are no longer added up like before. Signed-off-by: Rik van Riel Signed-off-by: Mel Gorman Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-55-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 6f45461..423316c 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -962,7 +962,7 @@ static inline unsigned long group_weight(struct task_struct *p, int nid) if (!total_faults) return 0; - return 1200 * group_faults(p, nid) / total_faults; + return 1000 * group_faults(p, nid) / total_faults; } static unsigned long weighted_cpuload(const int cpu); @@ -1068,16 +1068,34 @@ static void task_numa_compare(struct task_numa_env *env, /* * If dst and source tasks are in the same NUMA group, or not - * in any group then look only at task weights otherwise give - * priority to the group weights. + * in any group then look only at task weights. */ - if (!cur->numa_group || !env->p->numa_group || - cur->numa_group == env->p->numa_group) { + if (cur->numa_group == env->p->numa_group) { imp = taskimp + task_weight(cur, env->src_nid) - task_weight(cur, env->dst_nid); + /* + * Add some hysteresis to prevent swapping the + * tasks within a group over tiny differences. + */ + if (cur->numa_group) + imp -= imp/16; } else { - imp = groupimp + group_weight(cur, env->src_nid) - - group_weight(cur, env->dst_nid); + /* + * Compare the group weights. If a task is all by + * itself (not part of a group), use the task weight + * instead. + */ + if (env->p->numa_group) + imp = groupimp; + else + imp = taskimp; + + if (cur->numa_group) + imp += group_weight(cur, env->src_nid) - + group_weight(cur, env->dst_nid); + else + imp += task_weight(cur, env->src_nid) - + task_weight(cur, env->dst_nid); } } -- cgit v0.10.2 From 0ec8aa00f2b4dc457836ef4e2662b02483e94fb7 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 7 Oct 2013 11:29:33 +0100 Subject: sched/numa: Avoid migrating tasks that are placed on their preferred node This patch classifies scheduler domains and runqueues into types depending the number of tasks that are about their NUMA placement and the number that are currently running on their preferred node. The types are regular: There are tasks running that do not care about their NUMA placement. remote: There are tasks running that care about their placement but are currently running on a node remote to their ideal placement all: No distinction To implement this the patch tracks the number of tasks that are optimally NUMA placed (rq->nr_preferred_running) and the number of tasks running that care about their placement (nr_numa_running). The load balancer uses this information to avoid migrating idea placed NUMA tasks as long as better options for load balancing exists. For example, it will not consider balancing between a group whose tasks are all perfectly placed and a group with remote tasks. Signed-off-by: Peter Zijlstra Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Link: http://lkml.kernel.org/r/1381141781-10992-56-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 3e2c893..8cfd51f 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -4468,6 +4468,35 @@ int migrate_task_to(struct task_struct *p, int target_cpu) return stop_one_cpu(curr_cpu, migration_cpu_stop, &arg); } + +/* + * Requeue a task on a given node and accurately track the number of NUMA + * tasks on the runqueues + */ +void sched_setnuma(struct task_struct *p, int nid) +{ + struct rq *rq; + unsigned long flags; + bool on_rq, running; + + rq = task_rq_lock(p, &flags); + on_rq = p->on_rq; + running = task_current(rq, p); + + if (on_rq) + dequeue_task(rq, p, 0); + if (running) + p->sched_class->put_prev_task(rq, p); + + p->numa_preferred_nid = nid; + p->numa_migrate_seq = 1; + + if (running) + p->sched_class->set_curr_task(rq); + if (on_rq) + enqueue_task(rq, p, 0); + task_rq_unlock(rq, p, &flags); +} #endif /* diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 423316c..5166b9b 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -888,6 +888,18 @@ static unsigned int task_scan_max(struct task_struct *p) */ unsigned int sysctl_numa_balancing_settle_count __read_mostly = 4; +static void account_numa_enqueue(struct rq *rq, struct task_struct *p) +{ + rq->nr_numa_running += (p->numa_preferred_nid != -1); + rq->nr_preferred_running += (p->numa_preferred_nid == task_node(p)); +} + +static void account_numa_dequeue(struct rq *rq, struct task_struct *p) +{ + rq->nr_numa_running -= (p->numa_preferred_nid != -1); + rq->nr_preferred_running -= (p->numa_preferred_nid == task_node(p)); +} + struct numa_group { atomic_t refcount; @@ -1227,6 +1239,8 @@ static int task_numa_migrate(struct task_struct *p) if (env.best_cpu == -1) return -EAGAIN; + sched_setnuma(p, env.dst_nid); + if (env.best_task == NULL) { int ret = migrate_task_to(p, env.best_cpu); return ret; @@ -1342,8 +1356,7 @@ static void task_numa_placement(struct task_struct *p) /* Preferred node as the node with the most faults */ if (max_faults && max_nid != p->numa_preferred_nid) { /* Update the preferred nid and migrate task if possible */ - p->numa_preferred_nid = max_nid; - p->numa_migrate_seq = 1; + sched_setnuma(p, max_nid); numa_migrate_preferred(p); } } @@ -1741,6 +1754,14 @@ void task_tick_numa(struct rq *rq, struct task_struct *curr) static void task_tick_numa(struct rq *rq, struct task_struct *curr) { } + +static inline void account_numa_enqueue(struct rq *rq, struct task_struct *p) +{ +} + +static inline void account_numa_dequeue(struct rq *rq, struct task_struct *p) +{ +} #endif /* CONFIG_NUMA_BALANCING */ static void @@ -1750,8 +1771,12 @@ account_entity_enqueue(struct cfs_rq *cfs_rq, struct sched_entity *se) if (!parent_entity(se)) update_load_add(&rq_of(cfs_rq)->load, se->load.weight); #ifdef CONFIG_SMP - if (entity_is_task(se)) - list_add(&se->group_node, &rq_of(cfs_rq)->cfs_tasks); + if (entity_is_task(se)) { + struct rq *rq = rq_of(cfs_rq); + + account_numa_enqueue(rq, task_of(se)); + list_add(&se->group_node, &rq->cfs_tasks); + } #endif cfs_rq->nr_running++; } @@ -1762,8 +1787,10 @@ account_entity_dequeue(struct cfs_rq *cfs_rq, struct sched_entity *se) update_load_sub(&cfs_rq->load, se->load.weight); if (!parent_entity(se)) update_load_sub(&rq_of(cfs_rq)->load, se->load.weight); - if (entity_is_task(se)) + if (entity_is_task(se)) { + account_numa_dequeue(rq_of(cfs_rq), task_of(se)); list_del_init(&se->group_node); + } cfs_rq->nr_running--; } @@ -4605,6 +4632,8 @@ static bool yield_to_task_fair(struct rq *rq, struct task_struct *p, bool preemp static unsigned long __read_mostly max_load_balance_interval = HZ/10; +enum fbq_type { regular, remote, all }; + #define LBF_ALL_PINNED 0x01 #define LBF_NEED_BREAK 0x02 #define LBF_DST_PINNED 0x04 @@ -4631,6 +4660,8 @@ struct lb_env { unsigned int loop; unsigned int loop_break; unsigned int loop_max; + + enum fbq_type fbq_type; }; /* @@ -5092,6 +5123,10 @@ struct sg_lb_stats { unsigned int group_weight; int group_imb; /* Is there an imbalance in the group ? */ int group_has_capacity; /* Is there extra capacity in the group? */ +#ifdef CONFIG_NUMA_BALANCING + unsigned int nr_numa_running; + unsigned int nr_preferred_running; +#endif }; /* @@ -5409,6 +5444,10 @@ static inline void update_sg_lb_stats(struct lb_env *env, sgs->group_load += load; sgs->sum_nr_running += nr_running; +#ifdef CONFIG_NUMA_BALANCING + sgs->nr_numa_running += rq->nr_numa_running; + sgs->nr_preferred_running += rq->nr_preferred_running; +#endif sgs->sum_weighted_load += weighted_cpuload(i); if (idle_cpu(i)) sgs->idle_cpus++; @@ -5474,14 +5513,43 @@ static bool update_sd_pick_busiest(struct lb_env *env, return false; } +#ifdef CONFIG_NUMA_BALANCING +static inline enum fbq_type fbq_classify_group(struct sg_lb_stats *sgs) +{ + if (sgs->sum_nr_running > sgs->nr_numa_running) + return regular; + if (sgs->sum_nr_running > sgs->nr_preferred_running) + return remote; + return all; +} + +static inline enum fbq_type fbq_classify_rq(struct rq *rq) +{ + if (rq->nr_running > rq->nr_numa_running) + return regular; + if (rq->nr_running > rq->nr_preferred_running) + return remote; + return all; +} +#else +static inline enum fbq_type fbq_classify_group(struct sg_lb_stats *sgs) +{ + return all; +} + +static inline enum fbq_type fbq_classify_rq(struct rq *rq) +{ + return regular; +} +#endif /* CONFIG_NUMA_BALANCING */ + /** * update_sd_lb_stats - Update sched_domain's statistics for load balancing. * @env: The load balancing environment. * @balance: Should we balance. * @sds: variable to hold the statistics for this sched_domain. */ -static inline void update_sd_lb_stats(struct lb_env *env, - struct sd_lb_stats *sds) +static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sds) { struct sched_domain *child = env->sd->child; struct sched_group *sg = env->sd->groups; @@ -5538,6 +5606,9 @@ next_group: sg = sg->next; } while (sg != env->sd->groups); + + if (env->sd->flags & SD_NUMA) + env->fbq_type = fbq_classify_group(&sds->busiest_stat); } /** @@ -5841,15 +5912,39 @@ static struct rq *find_busiest_queue(struct lb_env *env, int i; for_each_cpu_and(i, sched_group_cpus(group), env->cpus) { - unsigned long power = power_of(i); - unsigned long capacity = DIV_ROUND_CLOSEST(power, - SCHED_POWER_SCALE); - unsigned long wl; + unsigned long power, capacity, wl; + enum fbq_type rt; + + rq = cpu_rq(i); + rt = fbq_classify_rq(rq); + /* + * We classify groups/runqueues into three groups: + * - regular: there are !numa tasks + * - remote: there are numa tasks that run on the 'wrong' node + * - all: there is no distinction + * + * In order to avoid migrating ideally placed numa tasks, + * ignore those when there's better options. + * + * If we ignore the actual busiest queue to migrate another + * task, the next balance pass can still reduce the busiest + * queue by moving tasks around inside the node. + * + * If we cannot move enough load due to this classification + * the next pass will adjust the group classification and + * allow migration of more tasks. + * + * Both cases only affect the total convergence complexity. + */ + if (rt > env->fbq_type) + continue; + + power = power_of(i); + capacity = DIV_ROUND_CLOSEST(power, SCHED_POWER_SCALE); if (!capacity) capacity = fix_small_capacity(env->sd, group); - rq = cpu_rq(i); wl = weighted_cpuload(i); /* @@ -5966,6 +6061,7 @@ static int load_balance(int this_cpu, struct rq *this_rq, .idle = idle, .loop_break = sched_nr_migrate_break, .cpus = cpus, + .fbq_type = all, }; /* diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index eeb1923..d69cb32 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -409,6 +409,10 @@ struct rq { * remote CPUs use both these fields when doing load calculation. */ unsigned int nr_running; +#ifdef CONFIG_NUMA_BALANCING + unsigned int nr_numa_running; + unsigned int nr_preferred_running; +#endif #define CPU_LOAD_IDX_MAX 5 unsigned long cpu_load[CPU_LOAD_IDX_MAX]; unsigned long last_load_update_tick; @@ -557,6 +561,7 @@ static inline u64 rq_clock_task(struct rq *rq) } #ifdef CONFIG_NUMA_BALANCING +extern void sched_setnuma(struct task_struct *p, int node); extern int migrate_task_to(struct task_struct *p, int cpu); extern int migrate_swap(struct task_struct *, struct task_struct *); #endif /* CONFIG_NUMA_BALANCING */ -- cgit v0.10.2 From dabe1d992414a6456e60e41f1d1ad8affc6d444d Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Mon, 7 Oct 2013 11:29:34 +0100 Subject: sched/numa: Be more careful about joining numa groups Due to the way the pid is truncated, and tasks are moved between CPUs by the scheduler, it is possible for the current task_numa_fault to group together tasks that do not actually share memory together. This patch adds a few easy sanity checks to task_numa_fault, joining tasks together if they share the same tsk->mm, or if the fault was on a page with an elevated mapcount, in a shared VMA. Signed-off-by: Rik van Riel Signed-off-by: Mel Gorman Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-57-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/include/linux/sched.h b/include/linux/sched.h index 1127a46..59f953b 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1454,6 +1454,7 @@ struct task_struct { #define TNF_MIGRATED 0x01 #define TNF_NO_GROUP 0x02 +#define TNF_SHARED 0x04 #ifdef CONFIG_NUMA_BALANCING extern void task_numa_fault(int last_node, int node, int pages, int flags); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 5166b9b..222c2d0 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1381,7 +1381,7 @@ static void double_lock(spinlock_t *l1, spinlock_t *l2) spin_lock_nested(l2, SINGLE_DEPTH_NESTING); } -static void task_numa_group(struct task_struct *p, int cpupid) +static void task_numa_group(struct task_struct *p, int cpupid, int flags) { struct numa_group *grp, *my_grp; struct task_struct *tsk; @@ -1439,10 +1439,16 @@ static void task_numa_group(struct task_struct *p, int cpupid) if (my_grp->nr_tasks == grp->nr_tasks && my_grp > grp) goto unlock; - if (!get_numa_group(grp)) - goto unlock; + /* Always join threads in the same process. */ + if (tsk->mm == current->mm) + join = true; + + /* Simple filter to avoid false positives due to PID collisions */ + if (flags & TNF_SHARED) + join = true; - join = true; + if (join && !get_numa_group(grp)) + join = false; unlock: rcu_read_unlock(); @@ -1539,7 +1545,7 @@ void task_numa_fault(int last_cpupid, int node, int pages, int flags) } else { priv = cpupid_match_pid(p, last_cpupid); if (!priv && !(flags & TNF_NO_GROUP)) - task_numa_group(p, last_cpupid); + task_numa_group(p, last_cpupid, flags); } /* diff --git a/mm/memory.c b/mm/memory.c index 9898eeb..823720c 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -3584,6 +3584,13 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, if (!pte_write(pte)) flags |= TNF_NO_GROUP; + /* + * Flag if the page is shared between multiple address spaces. This + * is later used when determining whether to group tasks together + */ + if (page_mapcount(page) > 1 && (vma->vm_flags & VM_SHARED)) + flags |= TNF_SHARED; + last_cpupid = page_cpupid_last(page); page_nid = page_to_nid(page); target_nid = numa_migrate_prep(page, vma, addr, page_nid); -- cgit v0.10.2 From 3e6a9418cf05638b103e34f5d13be0321872e623 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:29:35 +0100 Subject: sched/numa: Take false sharing into account when adapting scan rate Scan rate is altered based on whether shared/private faults dominated. task_numa_group() may detect false sharing but that information is not taken into account when adapting the scan rate. Take it into account. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-58-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 222c2d0..d26a16e 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1381,7 +1381,8 @@ static void double_lock(spinlock_t *l1, spinlock_t *l2) spin_lock_nested(l2, SINGLE_DEPTH_NESTING); } -static void task_numa_group(struct task_struct *p, int cpupid, int flags) +static void task_numa_group(struct task_struct *p, int cpupid, int flags, + int *priv) { struct numa_group *grp, *my_grp; struct task_struct *tsk; @@ -1447,6 +1448,9 @@ static void task_numa_group(struct task_struct *p, int cpupid, int flags) if (flags & TNF_SHARED) join = true; + /* Update priv based on whether false sharing was detected */ + *priv = !join; + if (join && !get_numa_group(grp)) join = false; @@ -1545,7 +1549,7 @@ void task_numa_fault(int last_cpupid, int node, int pages, int flags) } else { priv = cpupid_match_pid(p, last_cpupid); if (!priv && !(flags & TNF_NO_GROUP)) - task_numa_group(p, last_cpupid, flags); + task_numa_group(p, last_cpupid, flags, &priv); } /* -- cgit v0.10.2 From 04bb2f9475054298f0c67a89ca92cade42d3fe5e Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Mon, 7 Oct 2013 11:29:36 +0100 Subject: sched/numa: Adjust scan rate in task_numa_placement Adjust numa_scan_period in task_numa_placement, depending on how much useful work the numa code can do. The more local faults there are in a given scan window the longer the period (and hence the slower the scan rate) during the next window. If there are excessive shared faults then the scan period will decrease with the amount of scaling depending on whether the ratio of shared/private faults. If the preferred node changes then the scan rate is reset to recheck if the task is properly placed. Signed-off-by: Rik van Riel Signed-off-by: Mel Gorman Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-59-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/include/linux/sched.h b/include/linux/sched.h index 59f953b..2292f6c 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1365,6 +1365,14 @@ struct task_struct { */ unsigned long *numa_faults_buffer; + /* + * numa_faults_locality tracks if faults recorded during the last + * scan window were remote/local. The task scan period is adapted + * based on the locality of the faults with different weights + * depending on whether they were shared or private faults + */ + unsigned long numa_faults_locality[2]; + int numa_preferred_nid; unsigned long numa_pages_migrated; #endif /* CONFIG_NUMA_BALANCING */ @@ -1455,6 +1463,7 @@ struct task_struct { #define TNF_MIGRATED 0x01 #define TNF_NO_GROUP 0x02 #define TNF_SHARED 0x04 +#define TNF_FAULT_LOCAL 0x08 #ifdef CONFIG_NUMA_BALANCING extern void task_numa_fault(int last_node, int node, int pages, int flags); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index d26a16e..66237ff 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1241,6 +1241,12 @@ static int task_numa_migrate(struct task_struct *p) sched_setnuma(p, env.dst_nid); + /* + * Reset the scan period if the task is being rescheduled on an + * alternative node to recheck if the tasks is now properly placed. + */ + p->numa_scan_period = task_scan_min(p); + if (env.best_task == NULL) { int ret = migrate_task_to(p, env.best_cpu); return ret; @@ -1276,10 +1282,86 @@ static void numa_migrate_preferred(struct task_struct *p) p->numa_migrate_retry = jiffies + HZ*5; } +/* + * When adapting the scan rate, the period is divided into NUMA_PERIOD_SLOTS + * increments. The more local the fault statistics are, the higher the scan + * period will be for the next scan window. If local/remote ratio is below + * NUMA_PERIOD_THRESHOLD (where range of ratio is 1..NUMA_PERIOD_SLOTS) the + * scan period will decrease + */ +#define NUMA_PERIOD_SLOTS 10 +#define NUMA_PERIOD_THRESHOLD 3 + +/* + * Increase the scan period (slow down scanning) if the majority of + * our memory is already on our local node, or if the majority of + * the page accesses are shared with other processes. + * Otherwise, decrease the scan period. + */ +static void update_task_scan_period(struct task_struct *p, + unsigned long shared, unsigned long private) +{ + unsigned int period_slot; + int ratio; + int diff; + + unsigned long remote = p->numa_faults_locality[0]; + unsigned long local = p->numa_faults_locality[1]; + + /* + * If there were no record hinting faults then either the task is + * completely idle or all activity is areas that are not of interest + * to automatic numa balancing. Scan slower + */ + if (local + shared == 0) { + p->numa_scan_period = min(p->numa_scan_period_max, + p->numa_scan_period << 1); + + p->mm->numa_next_scan = jiffies + + msecs_to_jiffies(p->numa_scan_period); + + return; + } + + /* + * Prepare to scale scan period relative to the current period. + * == NUMA_PERIOD_THRESHOLD scan period stays the same + * < NUMA_PERIOD_THRESHOLD scan period decreases (scan faster) + * >= NUMA_PERIOD_THRESHOLD scan period increases (scan slower) + */ + period_slot = DIV_ROUND_UP(p->numa_scan_period, NUMA_PERIOD_SLOTS); + ratio = (local * NUMA_PERIOD_SLOTS) / (local + remote); + if (ratio >= NUMA_PERIOD_THRESHOLD) { + int slot = ratio - NUMA_PERIOD_THRESHOLD; + if (!slot) + slot = 1; + diff = slot * period_slot; + } else { + diff = -(NUMA_PERIOD_THRESHOLD - ratio) * period_slot; + + /* + * Scale scan rate increases based on sharing. There is an + * inverse relationship between the degree of sharing and + * the adjustment made to the scanning period. Broadly + * speaking the intent is that there is little point + * scanning faster if shared accesses dominate as it may + * simply bounce migrations uselessly + */ + period_slot = DIV_ROUND_UP(diff, NUMA_PERIOD_SLOTS); + ratio = DIV_ROUND_UP(private * NUMA_PERIOD_SLOTS, (private + shared)); + diff = (diff * ratio) / NUMA_PERIOD_SLOTS; + } + + p->numa_scan_period = clamp(p->numa_scan_period + diff, + task_scan_min(p), task_scan_max(p)); + memset(p->numa_faults_locality, 0, sizeof(p->numa_faults_locality)); +} + static void task_numa_placement(struct task_struct *p) { int seq, nid, max_nid = -1, max_group_nid = -1; unsigned long max_faults = 0, max_group_faults = 0; + unsigned long fault_types[2] = { 0, 0 }; spinlock_t *group_lock = NULL; seq = ACCESS_ONCE(p->mm->numa_scan_seq); @@ -1309,6 +1391,7 @@ static void task_numa_placement(struct task_struct *p) /* Decay existing window, copy faults since last scan */ p->numa_faults[i] >>= 1; p->numa_faults[i] += p->numa_faults_buffer[i]; + fault_types[priv] += p->numa_faults_buffer[i]; p->numa_faults_buffer[i] = 0; faults += p->numa_faults[i]; @@ -1333,6 +1416,8 @@ static void task_numa_placement(struct task_struct *p) } } + update_task_scan_period(p, fault_types[0], fault_types[1]); + if (p->numa_group) { /* * If the preferred task and group nids are different, @@ -1538,6 +1623,7 @@ void task_numa_fault(int last_cpupid, int node, int pages, int flags) BUG_ON(p->numa_faults_buffer); p->numa_faults_buffer = p->numa_faults + (2 * nr_node_ids); p->total_numa_faults = 0; + memset(p->numa_faults_locality, 0, sizeof(p->numa_faults_locality)); } /* @@ -1552,19 +1638,6 @@ void task_numa_fault(int last_cpupid, int node, int pages, int flags) task_numa_group(p, last_cpupid, flags, &priv); } - /* - * If pages are properly placed (did not migrate) then scan slower. - * This is reset periodically in case of phase changes - */ - if (!migrated) { - /* Initialise if necessary */ - if (!p->numa_scan_period_max) - p->numa_scan_period_max = task_scan_max(p); - - p->numa_scan_period = min(p->numa_scan_period_max, - p->numa_scan_period + 10); - } - task_numa_placement(p); /* Retry task to preferred node migration if it previously failed */ @@ -1575,6 +1648,7 @@ void task_numa_fault(int last_cpupid, int node, int pages, int flags) p->numa_pages_migrated += pages; p->numa_faults_buffer[task_faults_idx(node, priv)] += pages; + p->numa_faults_locality[!!(flags & TNF_FAULT_LOCAL)] += pages; } static void reset_ptenuma_scan(struct task_struct *p) @@ -1702,18 +1776,6 @@ void task_numa_work(struct callback_head *work) out: /* - * If the whole process was scanned without updates then no NUMA - * hinting faults are being recorded and scan rate should be lower. - */ - if (mm->numa_scan_offset == 0 && !nr_pte_updates) { - p->numa_scan_period = min(p->numa_scan_period_max, - p->numa_scan_period << 1); - - next_scan = now + msecs_to_jiffies(p->numa_scan_period); - mm->numa_next_scan = next_scan; - } - - /* * It is possible to reach the end of the VMA list but the last few * VMAs are not guaranteed to the vma_migratable. If they are not, we * would find the !migratable VMA on the next scan but not reset the diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 7ab4e32..1be2a1f 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -1296,8 +1296,10 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, page_nid = page_to_nid(page); last_cpupid = page_cpupid_last(page); count_vm_numa_event(NUMA_HINT_FAULTS); - if (page_nid == this_nid) + if (page_nid == this_nid) { count_vm_numa_event(NUMA_HINT_FAULTS_LOCAL); + flags |= TNF_FAULT_LOCAL; + } /* * Avoid grouping on DSO/COW pages in specific and RO pages diff --git a/mm/memory.c b/mm/memory.c index 823720c..1c7501f 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -3527,13 +3527,16 @@ static int do_nonlinear_fault(struct mm_struct *mm, struct vm_area_struct *vma, } int numa_migrate_prep(struct page *page, struct vm_area_struct *vma, - unsigned long addr, int page_nid) + unsigned long addr, int page_nid, + int *flags) { get_page(page); count_vm_numa_event(NUMA_HINT_FAULTS); - if (page_nid == numa_node_id()) + if (page_nid == numa_node_id()) { count_vm_numa_event(NUMA_HINT_FAULTS_LOCAL); + *flags |= TNF_FAULT_LOCAL; + } return mpol_misplaced(page, vma, addr); } @@ -3593,7 +3596,7 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, last_cpupid = page_cpupid_last(page); page_nid = page_to_nid(page); - target_nid = numa_migrate_prep(page, vma, addr, page_nid); + target_nid = numa_migrate_prep(page, vma, addr, page_nid, &flags); pte_unmap_unlock(ptep, ptl); if (target_nid == -1) { put_page(page); -- cgit v0.10.2 From 930aa174fcc8b0efaad102fd80f677b92f35eaa2 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:29:37 +0100 Subject: sched/numa: Remove the numa_balancing_scan_period_reset sysctl With scan rate adaptions based on whether the workload has properly converged or not there should be no need for the scan period reset hammer. Get rid of it. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-60-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index d48bca4..84f1780 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -374,15 +374,13 @@ guarantee. If the target workload is already bound to NUMA nodes then this feature should be disabled. Otherwise, if the system overhead from the feature is too high then the rate the kernel samples for NUMA hinting faults may be controlled by the numa_balancing_scan_period_min_ms, -numa_balancing_scan_delay_ms, numa_balancing_scan_period_reset, -numa_balancing_scan_period_max_ms, numa_balancing_scan_size_mb and -numa_balancing_settle_count sysctls. +numa_balancing_scan_delay_ms, numa_balancing_scan_period_max_ms, +numa_balancing_scan_size_mb and numa_balancing_settle_count sysctls. ============================================================== numa_balancing_scan_period_min_ms, numa_balancing_scan_delay_ms, -numa_balancing_scan_period_max_ms, numa_balancing_scan_period_reset, -numa_balancing_scan_size_mb +numa_balancing_scan_period_max_ms, numa_balancing_scan_size_mb Automatic NUMA balancing scans tasks address space and unmaps pages to detect if pages are properly placed or if the data should be migrated to a @@ -418,9 +416,6 @@ rate for each task. numa_balancing_scan_size_mb is how many megabytes worth of pages are scanned for a given scan. -numa_balancing_scan_period_reset is a blunt instrument that controls how -often a tasks scan delay is reset to detect sudden changes in task behaviour. - numa_balancing_settle_count is how many scan periods must complete before the schedule balancer stops pushing the task towards a preferred node. This gives the scheduler a chance to place the task on an alternative node if the diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index a30f9ca..a3198e5 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -420,9 +420,6 @@ struct mm_struct { */ unsigned long numa_next_scan; - /* numa_next_reset is when the PTE scanner period will be reset */ - unsigned long numa_next_reset; - /* Restart point for scanning and setting pte_numa */ unsigned long numa_scan_offset; diff --git a/include/linux/sched/sysctl.h b/include/linux/sched/sysctl.h index bf8086b..10d16c4f 100644 --- a/include/linux/sched/sysctl.h +++ b/include/linux/sched/sysctl.h @@ -47,7 +47,6 @@ extern enum sched_tunable_scaling sysctl_sched_tunable_scaling; extern unsigned int sysctl_numa_balancing_scan_delay; extern unsigned int sysctl_numa_balancing_scan_period_min; extern unsigned int sysctl_numa_balancing_scan_period_max; -extern unsigned int sysctl_numa_balancing_scan_period_reset; extern unsigned int sysctl_numa_balancing_scan_size; extern unsigned int sysctl_numa_balancing_settle_count; diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 8cfd51f..89c5ae8 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -1721,7 +1721,6 @@ static void __sched_fork(unsigned long clone_flags, struct task_struct *p) #ifdef CONFIG_NUMA_BALANCING if (p->mm && atomic_read(&p->mm->mm_users) == 1) { p->mm->numa_next_scan = jiffies + msecs_to_jiffies(sysctl_numa_balancing_scan_delay); - p->mm->numa_next_reset = jiffies + msecs_to_jiffies(sysctl_numa_balancing_scan_period_reset); p->mm->numa_scan_seq = 0; } diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 66237ff..da6fa22 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -826,7 +826,6 @@ update_stats_curr_start(struct cfs_rq *cfs_rq, struct sched_entity *se) */ unsigned int sysctl_numa_balancing_scan_period_min = 1000; unsigned int sysctl_numa_balancing_scan_period_max = 60000; -unsigned int sysctl_numa_balancing_scan_period_reset = 60000; /* Portion of address space to scan in MB */ unsigned int sysctl_numa_balancing_scan_size = 256; @@ -1685,24 +1684,9 @@ void task_numa_work(struct callback_head *work) if (p->flags & PF_EXITING) return; - if (!mm->numa_next_reset || !mm->numa_next_scan) { + if (!mm->numa_next_scan) { mm->numa_next_scan = now + msecs_to_jiffies(sysctl_numa_balancing_scan_delay); - mm->numa_next_reset = now + - msecs_to_jiffies(sysctl_numa_balancing_scan_period_reset); - } - - /* - * Reset the scan period if enough time has gone by. Objective is that - * scanning will be reduced if pages are properly placed. As tasks - * can enter different phases this needs to be re-examined. Lacking - * proper tracking of reference behaviour, this blunt hammer is used. - */ - migrate = mm->numa_next_reset; - if (time_after(now, migrate)) { - p->numa_scan_period = task_scan_min(p); - next_scan = now + msecs_to_jiffies(sysctl_numa_balancing_scan_period_reset); - xchg(&mm->numa_next_reset, next_scan); } /* diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 42f616a..e509b90 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -371,13 +371,6 @@ static struct ctl_table kern_table[] = { .proc_handler = proc_dointvec, }, { - .procname = "numa_balancing_scan_period_reset", - .data = &sysctl_numa_balancing_scan_period_reset, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = proc_dointvec, - }, - { .procname = "numa_balancing_scan_period_max_ms", .data = &sysctl_numa_balancing_scan_period_max, .maxlen = sizeof(unsigned int), -- cgit v0.10.2 From 1e3646ffc64b232cb14a5ef01d7b98997c1b73f9 Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Mon, 7 Oct 2013 11:29:38 +0100 Subject: mm: numa: Revert temporarily disabling of NUMA migration With the scan rate code working (at least for multi-instance specjbb), the large hammer that is "sched: Do not migrate memory immediately after switching node" can be replaced with something smarter. Revert temporarily migration disabling and all traces of numa_migrate_seq. Signed-off-by: Rik van Riel Signed-off-by: Mel Gorman Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-61-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/include/linux/sched.h b/include/linux/sched.h index 2292f6c..d24f70f 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1340,7 +1340,6 @@ struct task_struct { #endif #ifdef CONFIG_NUMA_BALANCING int numa_scan_seq; - int numa_migrate_seq; unsigned int numa_scan_period; unsigned int numa_scan_period_max; unsigned long numa_migrate_retry; diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 89c5ae8..0c3feeb 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -1731,7 +1731,6 @@ static void __sched_fork(unsigned long clone_flags, struct task_struct *p) p->node_stamp = 0ULL; p->numa_scan_seq = p->mm ? p->mm->numa_scan_seq : 0; - p->numa_migrate_seq = 1; p->numa_scan_period = sysctl_numa_balancing_scan_delay; p->numa_work.next = &p->numa_work; p->numa_faults = NULL; @@ -4488,7 +4487,6 @@ void sched_setnuma(struct task_struct *p, int nid) p->sched_class->put_prev_task(rq, p); p->numa_preferred_nid = nid; - p->numa_migrate_seq = 1; if (running) p->sched_class->set_curr_task(rq); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index da6fa22..8454c38 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1261,16 +1261,8 @@ static void numa_migrate_preferred(struct task_struct *p) { /* Success if task is already running on preferred CPU */ p->numa_migrate_retry = 0; - if (cpu_to_node(task_cpu(p)) == p->numa_preferred_nid) { - /* - * If migration is temporarily disabled due to a task migration - * then re-enable it now as the task is running on its - * preferred node and memory should migrate locally - */ - if (!p->numa_migrate_seq) - p->numa_migrate_seq++; + if (cpu_to_node(task_cpu(p)) == p->numa_preferred_nid) return; - } /* This task has no NUMA fault statistics yet */ if (unlikely(p->numa_preferred_nid == -1)) @@ -1367,7 +1359,6 @@ static void task_numa_placement(struct task_struct *p) if (p->numa_scan_seq == seq) return; p->numa_scan_seq = seq; - p->numa_migrate_seq++; p->numa_scan_period_max = task_scan_max(p); /* If the task is part of a group prevent parallel updates to group stats */ @@ -4730,20 +4721,6 @@ static void move_task(struct task_struct *p, struct lb_env *env) set_task_cpu(p, env->dst_cpu); activate_task(env->dst_rq, p, 0); check_preempt_curr(env->dst_rq, p, 0); -#ifdef CONFIG_NUMA_BALANCING - if (p->numa_preferred_nid != -1) { - int src_nid = cpu_to_node(env->src_cpu); - int dst_nid = cpu_to_node(env->dst_cpu); - - /* - * If the load balancer has moved the task then limit - * migrations from taking place in the short term in - * case this is a short-lived migration. - */ - if (src_nid != dst_nid && dst_nid != p->numa_preferred_nid) - p->numa_migrate_seq = 0; - } -#endif } /* diff --git a/mm/mempolicy.c b/mm/mempolicy.c index a5867ef..2929c24 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -2404,18 +2404,6 @@ int mpol_misplaced(struct page *page, struct vm_area_struct *vma, unsigned long last_cpupid = page_cpupid_xchg_last(page, this_cpupid); if (!cpupid_pid_unset(last_cpupid) && cpupid_to_nid(last_cpupid) != thisnid) goto out; - -#ifdef CONFIG_NUMA_BALANCING - /* - * If the scheduler has just moved us away from our - * preferred node, do not bother migrating pages yet. - * This way a short and temporary process migration will - * not cause excessive memory migration. - */ - if (thisnid != current->numa_preferred_nid && - !current->numa_migrate_seq) - goto out; -#endif } if (curnid != polnid) -- cgit v0.10.2 From de1c9ce6f07fec0381a39a9d0b379ea35aa1167f Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Mon, 7 Oct 2013 11:29:39 +0100 Subject: sched/numa: Skip some page migrations after a shared fault Shared faults can lead to lots of unnecessary page migrations, slowing down the system, and causing private faults to hit the per-pgdat migration ratelimit. This patch adds sysctl numa_balancing_migrate_deferred, which specifies how many shared page migrations to skip unconditionally, after each page migration that is skipped because it is a shared fault. This reduces the number of page migrations back and forth in shared fault situations. It also gives a strong preference to the tasks that are already running where most of the memory is, and to moving the other tasks to near the memory. Testing this with a much higher scan rate than the default still seems to result in fewer page migrations than before. Memory seems to be somewhat better consolidated than previously, with multi-instance specjbb runs on a 4 node system. Signed-off-by: Rik van Riel Signed-off-by: Mel Gorman Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-62-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index 84f1780..4273b2d 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -375,7 +375,8 @@ feature should be disabled. Otherwise, if the system overhead from the feature is too high then the rate the kernel samples for NUMA hinting faults may be controlled by the numa_balancing_scan_period_min_ms, numa_balancing_scan_delay_ms, numa_balancing_scan_period_max_ms, -numa_balancing_scan_size_mb and numa_balancing_settle_count sysctls. +numa_balancing_scan_size_mb, numa_balancing_settle_count sysctls and +numa_balancing_migrate_deferred. ============================================================== @@ -421,6 +422,13 @@ the schedule balancer stops pushing the task towards a preferred node. This gives the scheduler a chance to place the task on an alternative node if the preferred node is overloaded. +numa_balancing_migrate_deferred is how many page migrations get skipped +unconditionally, after a page migration is skipped because a page is shared +with other tasks. This reduces page migration overhead, and determines +how much stronger the "move task near its memory" policy scheduler becomes, +versus the "move memory near its task" memory management policy, for workloads +with shared memory. + ============================================================== osrelease, ostype & version: diff --git a/include/linux/sched.h b/include/linux/sched.h index d24f70f..833eed5 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1342,6 +1342,8 @@ struct task_struct { int numa_scan_seq; unsigned int numa_scan_period; unsigned int numa_scan_period_max; + int numa_preferred_nid; + int numa_migrate_deferred; unsigned long numa_migrate_retry; u64 node_stamp; /* migration stamp */ struct callback_head numa_work; @@ -1372,7 +1374,6 @@ struct task_struct { */ unsigned long numa_faults_locality[2]; - int numa_preferred_nid; unsigned long numa_pages_migrated; #endif /* CONFIG_NUMA_BALANCING */ @@ -1469,6 +1470,8 @@ extern void task_numa_fault(int last_node, int node, int pages, int flags); extern pid_t task_numa_group_id(struct task_struct *p); extern void set_numabalancing_state(bool enabled); extern void task_numa_free(struct task_struct *p); + +extern unsigned int sysctl_numa_balancing_migrate_deferred; #else static inline void task_numa_fault(int last_node, int node, int pages, int flags) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 8454c38..e7884dc 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -833,6 +833,14 @@ unsigned int sysctl_numa_balancing_scan_size = 256; /* Scan @scan_size MB every @scan_period after an initial @scan_delay in ms */ unsigned int sysctl_numa_balancing_scan_delay = 1000; +/* + * After skipping a page migration on a shared page, skip N more numa page + * migrations unconditionally. This reduces the number of NUMA migrations + * in shared memory workloads, and has the effect of pulling tasks towards + * where their memory lives, over pulling the memory towards the task. + */ +unsigned int sysctl_numa_balancing_migrate_deferred = 16; + static unsigned int task_nr_scan_windows(struct task_struct *p) { unsigned long rss = 0; diff --git a/kernel/sysctl.c b/kernel/sysctl.c index e509b90..a159e1f 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -391,6 +391,13 @@ static struct ctl_table kern_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, + { + .procname = "numa_balancing_migrate_deferred", + .data = &sysctl_numa_balancing_migrate_deferred, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, #endif /* CONFIG_NUMA_BALANCING */ #endif /* CONFIG_SCHED_DEBUG */ { diff --git a/mm/mempolicy.c b/mm/mempolicy.c index 2929c24..71cb253 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -2301,6 +2301,35 @@ static void sp_free(struct sp_node *n) kmem_cache_free(sn_cache, n); } +#ifdef CONFIG_NUMA_BALANCING +static bool numa_migrate_deferred(struct task_struct *p, int last_cpupid) +{ + /* Never defer a private fault */ + if (cpupid_match_pid(p, last_cpupid)) + return false; + + if (p->numa_migrate_deferred) { + p->numa_migrate_deferred--; + return true; + } + return false; +} + +static inline void defer_numa_migrate(struct task_struct *p) +{ + p->numa_migrate_deferred = sysctl_numa_balancing_migrate_deferred; +} +#else +static inline bool numa_migrate_deferred(struct task_struct *p, int last_cpupid) +{ + return false; +} + +static inline void defer_numa_migrate(struct task_struct *p) +{ +} +#endif /* CONFIG_NUMA_BALANCING */ + /** * mpol_misplaced - check whether current page node is valid in policy * @@ -2402,7 +2431,24 @@ int mpol_misplaced(struct page *page, struct vm_area_struct *vma, unsigned long * relation. */ last_cpupid = page_cpupid_xchg_last(page, this_cpupid); - if (!cpupid_pid_unset(last_cpupid) && cpupid_to_nid(last_cpupid) != thisnid) + if (!cpupid_pid_unset(last_cpupid) && cpupid_to_nid(last_cpupid) != thisnid) { + + /* See sysctl_numa_balancing_migrate_deferred comment */ + if (!cpupid_match_pid(current, last_cpupid)) + defer_numa_migrate(current); + + goto out; + } + + /* + * The quadratic filter above reduces extraneous migration + * of shared pages somewhat. This code reduces it even more, + * reducing the overhead of page migrations of shared pages. + * This makes workloads with shared pages rely more on + * "move task near its memory", and less on "move memory + * towards its task", which is exactly what we want. + */ + if (numa_migrate_deferred(current, last_cpupid)) goto out; } -- cgit v0.10.2 From 989348b5fc2367d6880d23a1c779a90bbb6f9baf Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 7 Oct 2013 11:29:40 +0100 Subject: sched/numa: Use unsigned longs for numa group fault stats As Peter says "If you're going to hold locks you can also do away with all that atomic_long_*() nonsense". Lock aquisition moved slightly to protect the updates. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-63-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index e7884dc..5b2208e 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -916,8 +916,8 @@ struct numa_group { struct list_head task_list; struct rcu_head rcu; - atomic_long_t total_faults; - atomic_long_t faults[0]; + unsigned long total_faults; + unsigned long faults[0]; }; pid_t task_numa_group_id(struct task_struct *p) @@ -944,8 +944,7 @@ static inline unsigned long group_faults(struct task_struct *p, int nid) if (!p->numa_group) return 0; - return atomic_long_read(&p->numa_group->faults[2*nid]) + - atomic_long_read(&p->numa_group->faults[2*nid+1]); + return p->numa_group->faults[2*nid] + p->numa_group->faults[2*nid+1]; } /* @@ -971,17 +970,10 @@ static inline unsigned long task_weight(struct task_struct *p, int nid) static inline unsigned long group_weight(struct task_struct *p, int nid) { - unsigned long total_faults; - - if (!p->numa_group) - return 0; - - total_faults = atomic_long_read(&p->numa_group->total_faults); - - if (!total_faults) + if (!p->numa_group || !p->numa_group->total_faults) return 0; - return 1000 * group_faults(p, nid) / total_faults; + return 1000 * group_faults(p, nid) / p->numa_group->total_faults; } static unsigned long weighted_cpuload(const int cpu); @@ -1397,9 +1389,9 @@ static void task_numa_placement(struct task_struct *p) p->total_numa_faults += diff; if (p->numa_group) { /* safe because we can only change our own group */ - atomic_long_add(diff, &p->numa_group->faults[i]); - atomic_long_add(diff, &p->numa_group->total_faults); - group_faults += atomic_long_read(&p->numa_group->faults[i]); + p->numa_group->faults[i] += diff; + p->numa_group->total_faults += diff; + group_faults += p->numa_group->faults[i]; } } @@ -1475,7 +1467,7 @@ static void task_numa_group(struct task_struct *p, int cpupid, int flags, if (unlikely(!p->numa_group)) { unsigned int size = sizeof(struct numa_group) + - 2*nr_node_ids*sizeof(atomic_long_t); + 2*nr_node_ids*sizeof(unsigned long); grp = kzalloc(size, GFP_KERNEL | __GFP_NOWARN); if (!grp) @@ -1487,9 +1479,9 @@ static void task_numa_group(struct task_struct *p, int cpupid, int flags, grp->gid = p->pid; for (i = 0; i < 2*nr_node_ids; i++) - atomic_long_set(&grp->faults[i], p->numa_faults[i]); + grp->faults[i] = p->numa_faults[i]; - atomic_long_set(&grp->total_faults, p->total_numa_faults); + grp->total_faults = p->total_numa_faults; list_add(&p->numa_entry, &grp->task_list); grp->nr_tasks++; @@ -1543,14 +1535,14 @@ unlock: if (!join) return; + double_lock(&my_grp->lock, &grp->lock); + for (i = 0; i < 2*nr_node_ids; i++) { - atomic_long_sub(p->numa_faults[i], &my_grp->faults[i]); - atomic_long_add(p->numa_faults[i], &grp->faults[i]); + my_grp->faults[i] -= p->numa_faults[i]; + grp->faults[i] += p->numa_faults[i]; } - atomic_long_sub(p->total_numa_faults, &my_grp->total_faults); - atomic_long_add(p->total_numa_faults, &grp->total_faults); - - double_lock(&my_grp->lock, &grp->lock); + my_grp->total_faults -= p->total_numa_faults; + grp->total_faults += p->total_numa_faults; list_move(&p->numa_entry, &grp->task_list); my_grp->nr_tasks--; @@ -1571,12 +1563,11 @@ void task_numa_free(struct task_struct *p) void *numa_faults = p->numa_faults; if (grp) { + spin_lock(&grp->lock); for (i = 0; i < 2*nr_node_ids; i++) - atomic_long_sub(p->numa_faults[i], &grp->faults[i]); - - atomic_long_sub(p->total_numa_faults, &grp->total_faults); + grp->faults[i] -= p->numa_faults[i]; + grp->total_faults -= p->total_numa_faults; - spin_lock(&grp->lock); list_del(&p->numa_entry); grp->nr_tasks--; spin_unlock(&grp->lock); -- cgit v0.10.2 From 2739d3eef3a93a92c366a3a0bb85a0afe09e8b8c Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Mon, 7 Oct 2013 11:29:41 +0100 Subject: sched/numa: Retry task_numa_migrate() periodically Short spikes of CPU load can lead to a task being migrated away from its preferred node for temporary reasons. It is important that the task is migrated back to where it belongs, in order to avoid migrating too much memory to its new location, and generally disturbing a task's NUMA location. This patch fixes NUMA placement for 4 specjbb instances on a 4 node system. Without this patch, things take longer to converge, and processes are not always completely on their own node. Signed-off-by: Rik van Riel Signed-off-by: Mel Gorman Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Srikar Dronamraju Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1381141781-10992-64-git-send-email-mgorman@suse.de Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 5b2208e..e914930 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1259,18 +1259,19 @@ static int task_numa_migrate(struct task_struct *p) /* Attempt to migrate a task to a CPU on the preferred node. */ static void numa_migrate_preferred(struct task_struct *p) { - /* Success if task is already running on preferred CPU */ - p->numa_migrate_retry = 0; - if (cpu_to_node(task_cpu(p)) == p->numa_preferred_nid) + /* This task has no NUMA fault statistics yet */ + if (unlikely(p->numa_preferred_nid == -1 || !p->numa_faults)) return; - /* This task has no NUMA fault statistics yet */ - if (unlikely(p->numa_preferred_nid == -1)) + /* Periodically retry migrating the task to the preferred node */ + p->numa_migrate_retry = jiffies + HZ; + + /* Success if task is already running on preferred CPU */ + if (cpu_to_node(task_cpu(p)) == p->numa_preferred_nid) return; /* Otherwise, try migrate to a CPU on the preferred node */ - if (task_numa_migrate(p) != 0) - p->numa_migrate_retry = jiffies + HZ*5; + task_numa_migrate(p); } /* @@ -1629,8 +1630,11 @@ void task_numa_fault(int last_cpupid, int node, int pages, int flags) task_numa_placement(p); - /* Retry task to preferred node migration if it previously failed */ - if (p->numa_migrate_retry && time_after(jiffies, p->numa_migrate_retry)) + /* + * Retry task to preferred node migration periodically, in case it + * case it previously failed, or the scheduler moved us. + */ + if (time_after(jiffies, p->numa_migrate_retry)) numa_migrate_preferred(p); if (migrated) -- cgit v0.10.2 From 3354781a2184380046c8dd19144628d3c33991e6 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 9 Oct 2013 10:24:48 +0200 Subject: sched/numa: Reflow task_numa_group() to avoid a compiler warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reflow the function a bit because GCC gets confused: kernel/sched/fair.c: In function ‘task_numa_fault’: kernel/sched/fair.c:1448:3: warning: ‘my_grp’ may be used uninitialized in this function [-Wmaybe-uninitialized] kernel/sched/fair.c:1463:27: note: ‘my_grp’ was declared here Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-6ebt6x7u64pbbonq1khqu2z9@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index e914930..803e343 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1493,28 +1493,28 @@ static void task_numa_group(struct task_struct *p, int cpupid, int flags, tsk = ACCESS_ONCE(cpu_rq(cpu)->curr); if (!cpupid_match_pid(tsk, cpupid)) - goto unlock; + goto no_join; grp = rcu_dereference(tsk->numa_group); if (!grp) - goto unlock; + goto no_join; my_grp = p->numa_group; if (grp == my_grp) - goto unlock; + goto no_join; /* * Only join the other group if its bigger; if we're the bigger group, * the other task will join us. */ if (my_grp->nr_tasks > grp->nr_tasks) - goto unlock; + goto no_join; /* * Tie-break on the grp address. */ if (my_grp->nr_tasks == grp->nr_tasks && my_grp > grp) - goto unlock; + goto no_join; /* Always join threads in the same process. */ if (tsk->mm == current->mm) @@ -1528,9 +1528,8 @@ static void task_numa_group(struct task_struct *p, int cpupid, int flags, *priv = !join; if (join && !get_numa_group(grp)) - join = false; + goto no_join; -unlock: rcu_read_unlock(); if (!join) @@ -1555,6 +1554,11 @@ unlock: rcu_assign_pointer(p->numa_group, grp); put_numa_group(my_grp); + return; + +no_join: + rcu_read_unlock(); + return; } void task_numa_free(struct task_struct *p) -- cgit v0.10.2 From 651e013e3ce6c0646c39a07e22bebad75a207209 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 8 Oct 2013 18:37:36 +0100 Subject: regmap: Don't generate gather writes for single register raw writes Since it is quite common for single register raw or async writes to be generated by rbtree cache syncs or firmware downloads and essentially all hardware will be faster with only a single transfer optimise this case by copying single values into the internal scratch buffer before sending. Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 742f300..d27758c 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1116,6 +1116,16 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, u8[0] |= map->write_flag_mask; + /* + * Essentially all I/O mechanisms will be faster with a single + * buffer to write. Since register syncs often generate raw + * writes of single registers optimise that case. + */ + if (val != work_val && val_len == map->format.val_bytes) { + memcpy(work_val, val, map->format.val_bytes); + val = work_val; + } + if (async && map->bus->async_write) { struct regmap_async *async; -- cgit v0.10.2 From 0a8198094da895c8d5db95812fe9de7027d808e4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 9 Oct 2013 12:28:52 +0100 Subject: regmap: Simplify the initiation of async I/O Rather than passing a flag around through the entire call stack store it in the regmap struct and read it when required. This minimises the visibility of the feature through the API, minimising the code updates needed to use it more widely. Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 793ebe2..6873b4c 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -63,6 +63,7 @@ struct regmap { void *bus_context; const char *name; + bool async; spinlock_t async_lock; wait_queue_head_t async_waitq; struct list_head async_list; @@ -218,7 +219,7 @@ bool regcache_set_val(struct regmap *map, void *base, unsigned int idx, int regcache_lookup_reg(struct regmap *map, unsigned int reg); int _regmap_raw_write(struct regmap *map, unsigned int reg, - const void *val, size_t val_len, bool async); + const void *val, size_t val_len); void regmap_async_complete_cb(struct regmap_async *async, int ret); diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index d6c2d69..a36112a 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -631,8 +631,7 @@ static int regcache_sync_block_raw_flush(struct regmap *map, const void **data, map->cache_bypass = 1; - ret = _regmap_raw_write(map, base, *data, count * val_bytes, - false); + ret = _regmap_raw_write(map, base, *data, count * val_bytes); map->cache_bypass = 0; diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index d27758c..268fb71 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1041,7 +1041,7 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg, } int _regmap_raw_write(struct regmap *map, unsigned int reg, - const void *val, size_t val_len, bool async) + const void *val, size_t val_len) { struct regmap_range_node *range; unsigned long flags; @@ -1093,7 +1093,7 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, dev_dbg(map->dev, "Writing window %d/%zu\n", win_residue, val_len / map->format.val_bytes); ret = _regmap_raw_write(map, reg, val, win_residue * - map->format.val_bytes, async); + map->format.val_bytes); if (ret != 0) return ret; @@ -1126,7 +1126,7 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, val = work_val; } - if (async && map->bus->async_write) { + if (map->async && map->bus->async_write) { struct regmap_async *async; trace_regmap_async_write_start(map->dev, reg, val_len); @@ -1273,7 +1273,7 @@ static int _regmap_bus_raw_write(void *context, unsigned int reg, map->work_buf + map->format.reg_bytes + map->format.pad_bytes, - map->format.val_bytes, false); + map->format.val_bytes); } static inline void *_regmap_map_get_context(struct regmap *map) @@ -1365,7 +1365,7 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, map->lock(map->lock_arg); - ret = _regmap_raw_write(map, reg, val, val_len, false); + ret = _regmap_raw_write(map, reg, val, val_len); map->unlock(map->lock_arg); @@ -1446,8 +1446,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, return ret; } } else { - ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count, - false); + ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count); } if (val_bytes != 1) @@ -1493,7 +1492,11 @@ int regmap_raw_write_async(struct regmap *map, unsigned int reg, map->lock(map->lock_arg); - ret = _regmap_raw_write(map, reg, val, val_len, true); + map->async = true; + + ret = _regmap_raw_write(map, reg, val, val_len); + + map->async = false; map->unlock(map->lock_arg); -- cgit v0.10.2 From 915f441b6f31b1a8ee01e9263a4e2d44c434d832 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 9 Oct 2013 13:30:10 +0100 Subject: regmap: Provide asynchronous write and update bits operations Make it easier for drivers to include single register writes in asynchronous sequences by providing async versions of the write and update bits operations. The update bits operations are only likely to be effective when used with devices that have caches but this is common enough to be useful. Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 268fb71..0503d86 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1338,6 +1338,37 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val) EXPORT_SYMBOL_GPL(regmap_write); /** + * regmap_write_async(): Write a value to a single register asynchronously + * + * @map: Register map to write to + * @reg: Register to write to + * @val: Value to be written + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_write_async(struct regmap *map, unsigned int reg, unsigned int val) +{ + int ret; + + if (reg % map->reg_stride) + return -EINVAL; + + map->lock(map->lock_arg); + + map->async = true; + + ret = _regmap_write(map, reg, val); + + map->async = false; + + map->unlock(map->lock_arg); + + return ret; +} +EXPORT_SYMBOL_GPL(regmap_write_async); + +/** * regmap_raw_write(): Write raw values to one or more registers * * @map: Register map to write to @@ -1811,6 +1842,41 @@ int regmap_update_bits(struct regmap *map, unsigned int reg, EXPORT_SYMBOL_GPL(regmap_update_bits); /** + * regmap_update_bits_async: Perform a read/modify/write cycle on the register + * map asynchronously + * + * @map: Register map to update + * @reg: Register to update + * @mask: Bitmask to change + * @val: New value for bitmask + * + * With most buses the read must be done synchronously so this is most + * useful for devices with a cache which do not need to interact with + * the hardware to determine the current register value. + * + * Returns zero for success, a negative number on error. + */ +int regmap_update_bits_async(struct regmap *map, unsigned int reg, + unsigned int mask, unsigned int val) +{ + bool change; + int ret; + + map->lock(map->lock_arg); + + map->async = true; + + ret = _regmap_update_bits(map, reg, mask, val, &change); + + map->async = false; + + map->unlock(map->lock_arg); + + return ret; +} +EXPORT_SYMBOL_GPL(regmap_update_bits_async); + +/** * regmap_update_bits_check: Perform a read/modify/write cycle on the * register map and report if updated * @@ -1835,6 +1901,43 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg, } EXPORT_SYMBOL_GPL(regmap_update_bits_check); +/** + * regmap_update_bits_check_async: Perform a read/modify/write cycle on the + * register map asynchronously and report if + * updated + * + * @map: Register map to update + * @reg: Register to update + * @mask: Bitmask to change + * @val: New value for bitmask + * @change: Boolean indicating if a write was done + * + * With most buses the read must be done synchronously so this is most + * useful for devices with a cache which do not need to interact with + * the hardware to determine the current register value. + * + * Returns zero for success, a negative number on error. + */ +int regmap_update_bits_check_async(struct regmap *map, unsigned int reg, + unsigned int mask, unsigned int val, + bool *change) +{ + int ret; + + map->lock(map->lock_arg); + + map->async = true; + + ret = _regmap_update_bits(map, reg, mask, val, change); + + map->async = false; + + map->unlock(map->lock_arg); + + return ret; +} +EXPORT_SYMBOL_GPL(regmap_update_bits_check_async); + void regmap_async_complete_cb(struct regmap_async *async, int ret) { struct regmap *map = async->map; diff --git a/include/linux/regmap.h b/include/linux/regmap.h index a10380b..114565b 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -374,6 +374,7 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config); struct regmap *dev_get_regmap(struct device *dev, const char *name); int regmap_write(struct regmap *map, unsigned int reg, unsigned int val); +int regmap_write_async(struct regmap *map, unsigned int reg, unsigned int val); int regmap_raw_write(struct regmap *map, unsigned int reg, const void *val, size_t val_len); int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, @@ -387,9 +388,14 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, size_t val_count); int regmap_update_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val); +int regmap_update_bits_async(struct regmap *map, unsigned int reg, + unsigned int mask, unsigned int val); int regmap_update_bits_check(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, bool *change); +int regmap_update_bits_check_async(struct regmap *map, unsigned int reg, + unsigned int mask, unsigned int val, + bool *change); int regmap_get_val_bytes(struct regmap *map); int regmap_async_complete(struct regmap *map); bool regmap_can_raw_write(struct regmap *map); @@ -527,6 +533,13 @@ static inline int regmap_write(struct regmap *map, unsigned int reg, return -EINVAL; } +static inline int regmap_write_async(struct regmap *map, unsigned int reg, + unsigned int val) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + static inline int regmap_raw_write(struct regmap *map, unsigned int reg, const void *val, size_t val_len) { @@ -576,6 +589,14 @@ static inline int regmap_update_bits(struct regmap *map, unsigned int reg, return -EINVAL; } +static inline int regmap_update_bits_async(struct regmap *map, + unsigned int reg, + unsigned int mask, unsigned int val) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + static inline int regmap_update_bits_check(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, @@ -585,6 +606,16 @@ static inline int regmap_update_bits_check(struct regmap *map, return -EINVAL; } +static inline int regmap_update_bits_check_async(struct regmap *map, + unsigned int reg, + unsigned int mask, + unsigned int val, + bool *change) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + static inline int regmap_get_val_bytes(struct regmap *map) { WARN_ONCE(1, "regmap API is disabled"); -- cgit v0.10.2 From 01533e9720c8527faf0bc6e476c4c911a488e268 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 3 Sep 2013 12:20:12 -0300 Subject: perf trace: Put syscall formatter parms into struct So that we can add more state to formatters without having to modify all of them. Example is to pass a table to a generic string formatter, like for setitimer 'which' arg. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-zyi2esmas5wfrxznh0x0fkiz@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 71aa3e3..939426c 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -33,22 +33,24 @@ # define MADV_UNMERGEABLE 13 #endif +struct syscall_arg { + unsigned long val; + u8 idx; + u8 mask; +}; + static size_t syscall_arg__scnprintf_hex(char *bf, size_t size, - unsigned long arg, - u8 arg_idx __maybe_unused, - u8 *arg_mask __maybe_unused) + struct syscall_arg *arg) { - return scnprintf(bf, size, "%#lx", arg); + return scnprintf(bf, size, "%#lx", arg->val); } #define SCA_HEX syscall_arg__scnprintf_hex static size_t syscall_arg__scnprintf_whence(char *bf, size_t size, - unsigned long arg, - u8 arg_idx __maybe_unused, - u8 *arg_mask __maybe_unused) + struct syscall_arg *arg) { - int whence = arg; + int whence = arg->val; switch (whence) { #define P_WHENCE(n) case SEEK_##n: return scnprintf(bf, size, #n) @@ -71,11 +73,9 @@ static size_t syscall_arg__scnprintf_whence(char *bf, size_t size, #define SCA_WHENCE syscall_arg__scnprintf_whence static size_t syscall_arg__scnprintf_mmap_prot(char *bf, size_t size, - unsigned long arg, - u8 arg_idx __maybe_unused, - u8 *arg_mask __maybe_unused) + struct syscall_arg *arg) { - int printed = 0, prot = arg; + int printed = 0, prot = arg->val; if (prot == PROT_NONE) return scnprintf(bf, size, "NONE"); @@ -104,10 +104,9 @@ static size_t syscall_arg__scnprintf_mmap_prot(char *bf, size_t size, #define SCA_MMAP_PROT syscall_arg__scnprintf_mmap_prot static size_t syscall_arg__scnprintf_mmap_flags(char *bf, size_t size, - unsigned long arg, u8 arg_idx __maybe_unused, - u8 *arg_mask __maybe_unused) + struct syscall_arg *arg) { - int printed = 0, flags = arg; + int printed = 0, flags = arg->val; #define P_MMAP_FLAG(n) \ if (flags & MAP_##n) { \ @@ -148,10 +147,9 @@ static size_t syscall_arg__scnprintf_mmap_flags(char *bf, size_t size, #define SCA_MMAP_FLAGS syscall_arg__scnprintf_mmap_flags static size_t syscall_arg__scnprintf_madvise_behavior(char *bf, size_t size, - unsigned long arg, u8 arg_idx __maybe_unused, - u8 *arg_mask __maybe_unused) + struct syscall_arg *arg) { - int behavior = arg; + int behavior = arg->val; switch (behavior) { #define P_MADV_BHV(n) case MADV_##n: return scnprintf(bf, size, #n) @@ -190,8 +188,7 @@ static size_t syscall_arg__scnprintf_madvise_behavior(char *bf, size_t size, #define SCA_MADV_BHV syscall_arg__scnprintf_madvise_behavior -static size_t syscall_arg__scnprintf_futex_op(char *bf, size_t size, unsigned long arg, - u8 arg_idx __maybe_unused, u8 *arg_mask) +static size_t syscall_arg__scnprintf_futex_op(char *bf, size_t size, struct syscall_arg *arg) { enum syscall_futex_args { SCF_UADDR = (1 << 0), @@ -201,24 +198,24 @@ static size_t syscall_arg__scnprintf_futex_op(char *bf, size_t size, unsigned lo SCF_UADDR2 = (1 << 4), SCF_VAL3 = (1 << 5), }; - int op = arg; + int op = arg->val; int cmd = op & FUTEX_CMD_MASK; size_t printed = 0; switch (cmd) { #define P_FUTEX_OP(n) case FUTEX_##n: printed = scnprintf(bf, size, #n); - P_FUTEX_OP(WAIT); *arg_mask |= SCF_VAL3|SCF_UADDR2; break; - P_FUTEX_OP(WAKE); *arg_mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break; - P_FUTEX_OP(FD); *arg_mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break; - P_FUTEX_OP(REQUEUE); *arg_mask |= SCF_VAL3|SCF_TIMEOUT; break; - P_FUTEX_OP(CMP_REQUEUE); *arg_mask |= SCF_TIMEOUT; break; - P_FUTEX_OP(CMP_REQUEUE_PI); *arg_mask |= SCF_TIMEOUT; break; + P_FUTEX_OP(WAIT); arg->mask |= SCF_VAL3|SCF_UADDR2; break; + P_FUTEX_OP(WAKE); arg->mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break; + P_FUTEX_OP(FD); arg->mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break; + P_FUTEX_OP(REQUEUE); arg->mask |= SCF_VAL3|SCF_TIMEOUT; break; + P_FUTEX_OP(CMP_REQUEUE); arg->mask |= SCF_TIMEOUT; break; + P_FUTEX_OP(CMP_REQUEUE_PI); arg->mask |= SCF_TIMEOUT; break; P_FUTEX_OP(WAKE_OP); break; - P_FUTEX_OP(LOCK_PI); *arg_mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break; - P_FUTEX_OP(UNLOCK_PI); *arg_mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break; - P_FUTEX_OP(TRYLOCK_PI); *arg_mask |= SCF_VAL3|SCF_UADDR2; break; - P_FUTEX_OP(WAIT_BITSET); *arg_mask |= SCF_UADDR2; break; - P_FUTEX_OP(WAKE_BITSET); *arg_mask |= SCF_UADDR2; break; + P_FUTEX_OP(LOCK_PI); arg->mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break; + P_FUTEX_OP(UNLOCK_PI); arg->mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break; + P_FUTEX_OP(TRYLOCK_PI); arg->mask |= SCF_VAL3|SCF_UADDR2; break; + P_FUTEX_OP(WAIT_BITSET); arg->mask |= SCF_UADDR2; break; + P_FUTEX_OP(WAKE_BITSET); arg->mask |= SCF_UADDR2; break; P_FUTEX_OP(WAIT_REQUEUE_PI); break; default: printed = scnprintf(bf, size, "%#x", cmd); break; } @@ -235,13 +232,12 @@ static size_t syscall_arg__scnprintf_futex_op(char *bf, size_t size, unsigned lo #define SCA_FUTEX_OP syscall_arg__scnprintf_futex_op static size_t syscall_arg__scnprintf_open_flags(char *bf, size_t size, - unsigned long arg, - u8 arg_idx, u8 *arg_mask) + struct syscall_arg *arg) { - int printed = 0, flags = arg; + int printed = 0, flags = arg->val; if (!(flags & O_CREAT)) - *arg_mask |= 1 << (arg_idx + 1); /* Mask the mode parm */ + arg->mask |= 1 << (arg->idx + 1); /* Mask the mode parm */ if (flags == 0) return scnprintf(bf, size, "RDONLY"); @@ -294,7 +290,7 @@ static size_t syscall_arg__scnprintf_open_flags(char *bf, size_t size, static struct syscall_fmt { const char *name; const char *alias; - size_t (*arg_scnprintf[6])(char *bf, size_t size, unsigned long arg, u8 arg_idx, u8 *arg_mask); + size_t (*arg_scnprintf[6])(char *bf, size_t size, struct syscall_arg *arg); bool errmsg; bool timeout; bool hexret; @@ -364,8 +360,7 @@ struct syscall { const char *name; bool filtered; struct syscall_fmt *fmt; - size_t (**arg_scnprintf)(char *bf, size_t size, - unsigned long arg, u8 arg_idx, u8 *args_mask); + size_t (**arg_scnprintf)(char *bf, size_t size, struct syscall_arg *arg); }; static size_t fprintf_duration(unsigned long t, FILE *fp) @@ -605,30 +600,35 @@ static int trace__read_syscall_info(struct trace *trace, int id) static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, unsigned long *args) { - int i = 0; size_t printed = 0; if (sc->tp_format != NULL) { struct format_field *field; - u8 mask = 0, bit = 1; + u8 bit = 1; + struct syscall_arg arg = { + .idx = 0, + .mask = 0, + }; for (field = sc->tp_format->format.fields->next; field; - field = field->next, ++i, bit <<= 1) { - if (mask & bit) + field = field->next, ++arg.idx, bit <<= 1) { + if (arg.mask & bit) continue; printed += scnprintf(bf + printed, size - printed, "%s%s: ", printed ? ", " : "", field->name); - - if (sc->arg_scnprintf && sc->arg_scnprintf[i]) { - printed += sc->arg_scnprintf[i](bf + printed, size - printed, - args[i], i, &mask); + if (sc->arg_scnprintf && sc->arg_scnprintf[arg.idx]) { + arg.val = args[arg.idx]; + printed += sc->arg_scnprintf[arg.idx](bf + printed, + size - printed, &arg); } else { printed += scnprintf(bf + printed, size - printed, - "%ld", args[i]); + "%ld", args[arg.idx]); } } } else { + int i = 0; + while (i < 6) { printed += scnprintf(bf + printed, size - printed, "%sarg%d: %ld", -- cgit v0.10.2 From 1f115cb72e44391fac3ab1c562a77b421469ac2d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 3 Sep 2013 15:50:28 -0300 Subject: perf trace: Allow passing parms to arg formatters So that we can have generic formatters that act upon specific parameters. Start using them with a simple string table that assumes entries will be indexes to a string table, like with the 'which' parm for the set and getitimer syscalls Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-r0dqhapr8j6150v1wctgg340@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 939426c..386ca20 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -35,10 +35,35 @@ struct syscall_arg { unsigned long val; + void *parm; u8 idx; u8 mask; }; +struct strarray { + int nr_entries; + const char **entries; +}; + +#define DEFINE_STRARRAY(array) struct strarray strarray__##array = { \ + .nr_entries = ARRAY_SIZE(array), \ + .entries = array, \ +} + +static size_t syscall_arg__scnprintf_strarray(char *bf, size_t size, + struct syscall_arg *arg) +{ + int idx = arg->val; + struct strarray *sa = arg->parm; + + if (idx < 0 || idx >= sa->nr_entries) + return scnprintf(bf, size, "%d", idx); + + return scnprintf(bf, size, "%s", sa->entries[idx]); +} + +#define SCA_STRARRAY syscall_arg__scnprintf_strarray + static size_t syscall_arg__scnprintf_hex(char *bf, size_t size, struct syscall_arg *arg) { @@ -229,6 +254,9 @@ static size_t syscall_arg__scnprintf_futex_op(char *bf, size_t size, struct sysc return printed; } +static const char *itimers[] = { "REAL", "VIRTUAL", "PROF", }; +static DEFINE_STRARRAY(itimers); + #define SCA_FUTEX_OP syscall_arg__scnprintf_futex_op static size_t syscall_arg__scnprintf_open_flags(char *bf, size_t size, @@ -291,6 +319,7 @@ static struct syscall_fmt { const char *name; const char *alias; size_t (*arg_scnprintf[6])(char *bf, size_t size, struct syscall_arg *arg); + void *arg_parm[6]; bool errmsg; bool timeout; bool hexret; @@ -305,6 +334,9 @@ static struct syscall_fmt { { .name = "fstatat", .errmsg = true, .alias = "newfstatat", }, { .name = "futex", .errmsg = true, .arg_scnprintf = { [1] = SCA_FUTEX_OP, /* op */ }, }, + { .name = "getitimer", .errmsg = true, + .arg_scnprintf = { [0] = SCA_STRARRAY, /* which */ }, + .arg_parm = { [0] = &strarray__itimers, /* which */ }, }, { .name = "ioctl", .errmsg = true, .arg_scnprintf = { [2] = SCA_HEX, /* arg */ }, }, { .name = "lseek", .errmsg = true, @@ -338,6 +370,9 @@ static struct syscall_fmt { { .name = "read", .errmsg = true, }, { .name = "recvfrom", .errmsg = true, }, { .name = "select", .errmsg = true, .timeout = true, }, + { .name = "setitimer", .errmsg = true, + .arg_scnprintf = { [0] = SCA_STRARRAY, /* which */ }, + .arg_parm = { [0] = &strarray__itimers, /* which */ }, }, { .name = "socket", .errmsg = true, }, { .name = "stat", .errmsg = true, .alias = "newstat", }, { .name = "uname", .errmsg = true, .alias = "newuname", }, @@ -361,6 +396,7 @@ struct syscall { bool filtered; struct syscall_fmt *fmt; size_t (**arg_scnprintf)(char *bf, size_t size, struct syscall_arg *arg); + void **arg_parm; }; static size_t fprintf_duration(unsigned long t, FILE *fp) @@ -528,6 +564,9 @@ static int syscall__set_arg_fmts(struct syscall *sc) if (sc->arg_scnprintf == NULL) return -1; + if (sc->fmt) + sc->arg_parm = sc->fmt->arg_parm; + for (field = sc->tp_format->format.fields->next; field; field = field->next) { if (sc->fmt && sc->fmt->arg_scnprintf[idx]) sc->arg_scnprintf[idx] = sc->fmt->arg_scnprintf[idx]; @@ -619,6 +658,8 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, "%s%s: ", printed ? ", " : "", field->name); if (sc->arg_scnprintf && sc->arg_scnprintf[arg.idx]) { arg.val = args[arg.idx]; + if (sc->arg_parm) + arg.parm = sc->arg_parm[arg.idx]; printed += sc->arg_scnprintf[arg.idx](bf + printed, size - printed, &arg); } else { -- cgit v0.10.2 From efe6b882cda2a9967a629fa14b6106b6a9a558a2 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 3 Sep 2013 16:15:12 -0300 Subject: perf trace: Use strarray for ltrace's whence arg Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-5f9jhbq8my4ojarhtlygveox@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 386ca20..7cb6052 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -72,31 +72,6 @@ static size_t syscall_arg__scnprintf_hex(char *bf, size_t size, #define SCA_HEX syscall_arg__scnprintf_hex -static size_t syscall_arg__scnprintf_whence(char *bf, size_t size, - struct syscall_arg *arg) -{ - int whence = arg->val; - - switch (whence) { -#define P_WHENCE(n) case SEEK_##n: return scnprintf(bf, size, #n) - P_WHENCE(SET); - P_WHENCE(CUR); - P_WHENCE(END); -#ifdef SEEK_DATA - P_WHENCE(DATA); -#endif -#ifdef SEEK_HOLE - P_WHENCE(HOLE); -#endif -#undef P_WHENCE - default: break; - } - - return scnprintf(bf, size, "%#x", whence); -} - -#define SCA_WHENCE syscall_arg__scnprintf_whence - static size_t syscall_arg__scnprintf_mmap_prot(char *bf, size_t size, struct syscall_arg *arg) { @@ -254,10 +229,20 @@ static size_t syscall_arg__scnprintf_futex_op(char *bf, size_t size, struct sysc return printed; } +#define SCA_FUTEX_OP syscall_arg__scnprintf_futex_op + static const char *itimers[] = { "REAL", "VIRTUAL", "PROF", }; static DEFINE_STRARRAY(itimers); -#define SCA_FUTEX_OP syscall_arg__scnprintf_futex_op +static const char *whences[] = { "SET", "CUR", "END", +#ifdef SEEK_DATA +"DATA", +#endif +#ifdef SEEK_HOLE +"HOLE", +#endif +}; +static DEFINE_STRARRAY(whences); static size_t syscall_arg__scnprintf_open_flags(char *bf, size_t size, struct syscall_arg *arg) @@ -340,7 +325,8 @@ static struct syscall_fmt { { .name = "ioctl", .errmsg = true, .arg_scnprintf = { [2] = SCA_HEX, /* arg */ }, }, { .name = "lseek", .errmsg = true, - .arg_scnprintf = { [2] = SCA_WHENCE, /* whence */ }, }, + .arg_scnprintf = { [2] = SCA_STRARRAY, /* whence */ }, + .arg_parm = { [2] = &strarray__whences, /* whence */ }, }, { .name = "lstat", .errmsg = true, .alias = "newlstat", }, { .name = "madvise", .errmsg = true, .arg_scnprintf = { [0] = SCA_HEX, /* start */ -- cgit v0.10.2 From 80f587d5f9424ebdf93abbe79ab87e7fb7d4699d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 3 Sep 2013 16:28:58 -0300 Subject: perf trace: Beautify fcntl 'cmd' arg This is just for the low hanging fruit 'cmd' arg, a proper beautifier will as well use arg->mask to ignore the third arg for some of the cmds. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-phhvcyi9vdnxw9l11tbquvru@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 7cb6052..8455a0a 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -244,6 +244,14 @@ static const char *whences[] = { "SET", "CUR", "END", }; static DEFINE_STRARRAY(whences); +static const char *fcntl_cmds[] = { + "DUPFD", "GETFD", "SETFD", "GETFL", "SETFL", "GETLK", "SETLK", + "SETLKW", "SETOWN", "GETOWN", "SETSIG", "GETSIG", "F_GETLK64", + "F_SETLK64", "F_SETLKW64", "F_SETOWN_EX", "F_GETOWN_EX", + "F_GETOWNER_UIDS", +}; +static DEFINE_STRARRAY(fcntl_cmds); + static size_t syscall_arg__scnprintf_open_flags(char *bf, size_t size, struct syscall_arg *arg) { @@ -315,6 +323,9 @@ static struct syscall_fmt { .arg_scnprintf = { [0] = SCA_HEX, /* brk */ }, }, { .name = "mmap", .hexret = true, }, { .name = "connect", .errmsg = true, }, + { .name = "fcntl", .errmsg = true, + .arg_scnprintf = { [1] = SCA_STRARRAY, /* cmd */ }, + .arg_parm = { [1] = &strarray__fcntl_cmds, /* cmd */ }, }, { .name = "fstat", .errmsg = true, .alias = "newfstat", }, { .name = "fstatat", .errmsg = true, .alias = "newfstatat", }, { .name = "futex", .errmsg = true, -- cgit v0.10.2 From eb5b1b1475d5ce6ebe84fd4c641545a41839d5f9 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 3 Sep 2013 16:37:46 -0300 Subject: perf trace: Beautify rt_sigprocmask 'how' arg Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-e2epkc38e3x0uqmi1xie4tgc@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 8455a0a..ecc83ce 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -252,6 +252,9 @@ static const char *fcntl_cmds[] = { }; static DEFINE_STRARRAY(fcntl_cmds); +static const char *sighow[] = { "BLOCK", "UNBLOCK", "SETMASK", }; +static DEFINE_STRARRAY(sighow); + static size_t syscall_arg__scnprintf_open_flags(char *bf, size_t size, struct syscall_arg *arg) { @@ -366,6 +369,9 @@ static struct syscall_fmt { { .name = "pwrite", .errmsg = true, .alias = "pwrite64", }, { .name = "read", .errmsg = true, }, { .name = "recvfrom", .errmsg = true, }, + { .name = "rt_sigprocmask", .errmsg = true, + .arg_scnprintf = { [0] = SCA_STRARRAY, /* how */ }, + .arg_parm = { [0] = &strarray__sighow, /* how */ }, }, { .name = "select", .errmsg = true, .timeout = true, }, { .name = "setitimer", .errmsg = true, .arg_scnprintf = { [0] = SCA_STRARRAY, /* which */ }, -- cgit v0.10.2 From 8bad5b0abfdbd0866c2b0445fdee8a8c2c38865b Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 3 Sep 2013 17:17:15 -0300 Subject: perf trace: Beautify signal number arg in several syscalls Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-ek8w714ramabyl5jqqvjlbyb@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index ecc83ce..eb0bbff 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -311,6 +311,51 @@ static size_t syscall_arg__scnprintf_open_flags(char *bf, size_t size, #define SCA_OPEN_FLAGS syscall_arg__scnprintf_open_flags +static size_t syscall_arg__scnprintf_signum(char *bf, size_t size, struct syscall_arg *arg) +{ + int sig = arg->val; + + switch (sig) { +#define P_SIGNUM(n) case SIG##n: return scnprintf(bf, size, #n) + P_SIGNUM(HUP); + P_SIGNUM(INT); + P_SIGNUM(QUIT); + P_SIGNUM(ILL); + P_SIGNUM(TRAP); + P_SIGNUM(ABRT); + P_SIGNUM(BUS); + P_SIGNUM(FPE); + P_SIGNUM(KILL); + P_SIGNUM(USR1); + P_SIGNUM(SEGV); + P_SIGNUM(USR2); + P_SIGNUM(PIPE); + P_SIGNUM(ALRM); + P_SIGNUM(TERM); + P_SIGNUM(STKFLT); + P_SIGNUM(CHLD); + P_SIGNUM(CONT); + P_SIGNUM(STOP); + P_SIGNUM(TSTP); + P_SIGNUM(TTIN); + P_SIGNUM(TTOU); + P_SIGNUM(URG); + P_SIGNUM(XCPU); + P_SIGNUM(XFSZ); + P_SIGNUM(VTALRM); + P_SIGNUM(PROF); + P_SIGNUM(WINCH); + P_SIGNUM(IO); + P_SIGNUM(PWR); + P_SIGNUM(SYS); + default: break; + } + + return scnprintf(bf, size, "%#x", sig); +} + +#define SCA_SIGNUM syscall_arg__scnprintf_signum + static struct syscall_fmt { const char *name; const char *alias; @@ -338,6 +383,8 @@ static struct syscall_fmt { .arg_parm = { [0] = &strarray__itimers, /* which */ }, }, { .name = "ioctl", .errmsg = true, .arg_scnprintf = { [2] = SCA_HEX, /* arg */ }, }, + { .name = "kill", .errmsg = true, + .arg_scnprintf = { [1] = SCA_SIGNUM, /* sig */ }, }, { .name = "lseek", .errmsg = true, .arg_scnprintf = { [2] = SCA_STRARRAY, /* whence */ }, .arg_parm = { [2] = &strarray__whences, /* whence */ }, }, @@ -369,15 +416,25 @@ static struct syscall_fmt { { .name = "pwrite", .errmsg = true, .alias = "pwrite64", }, { .name = "read", .errmsg = true, }, { .name = "recvfrom", .errmsg = true, }, + { .name = "rt_sigaction", .errmsg = true, + .arg_scnprintf = { [0] = SCA_SIGNUM, /* sig */ }, }, { .name = "rt_sigprocmask", .errmsg = true, .arg_scnprintf = { [0] = SCA_STRARRAY, /* how */ }, .arg_parm = { [0] = &strarray__sighow, /* how */ }, }, + { .name = "rt_sigqueueinfo", .errmsg = true, + .arg_scnprintf = { [1] = SCA_SIGNUM, /* sig */ }, }, + { .name = "rt_tgsigqueueinfo", .errmsg = true, + .arg_scnprintf = { [2] = SCA_SIGNUM, /* sig */ }, }, { .name = "select", .errmsg = true, .timeout = true, }, { .name = "setitimer", .errmsg = true, .arg_scnprintf = { [0] = SCA_STRARRAY, /* which */ }, .arg_parm = { [0] = &strarray__itimers, /* which */ }, }, { .name = "socket", .errmsg = true, }, { .name = "stat", .errmsg = true, .alias = "newstat", }, + { .name = "tgkill", .errmsg = true, + .arg_scnprintf = { [2] = SCA_SIGNUM, /* sig */ }, }, + { .name = "tkill", .errmsg = true, + .arg_scnprintf = { [1] = SCA_SIGNUM, /* sig */ }, }, { .name = "uname", .errmsg = true, .alias = "newuname", }, }; -- cgit v0.10.2 From e10bce815d5c0e7eb793231e4334839b2cd0d81f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 4 Sep 2013 10:27:41 -0300 Subject: perf trace: Beautify socket 'family' arg Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-8xuaupgmy82v7sha3l09oaux@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index eb0bbff..6280166 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -255,6 +255,16 @@ static DEFINE_STRARRAY(fcntl_cmds); static const char *sighow[] = { "BLOCK", "UNBLOCK", "SETMASK", }; static DEFINE_STRARRAY(sighow); +static const char *socket_families[] = { + "UNSPEC", "LOCAL", "INET", "AX25", "IPX", "APPLETALK", "NETROM", + "BRIDGE", "ATMPVC", "X25", "INET6", "ROSE", "DECnet", "NETBEUI", + "SECURITY", "KEY", "NETLINK", "PACKET", "ASH", "ECONET", "ATMSVC", + "RDS", "SNA", "IRDA", "PPPOX", "WANPIPE", "LLC", "IB", "CAN", "TIPC", + "BLUETOOTH", "IUCV", "RXRPC", "ISDN", "PHONET", "IEEE802154", "CAIF", + "ALG", "NFC", "VSOCK", +}; +static DEFINE_STRARRAY(socket_families); + static size_t syscall_arg__scnprintf_open_flags(char *bf, size_t size, struct syscall_arg *arg) { @@ -429,7 +439,9 @@ static struct syscall_fmt { { .name = "setitimer", .errmsg = true, .arg_scnprintf = { [0] = SCA_STRARRAY, /* which */ }, .arg_parm = { [0] = &strarray__itimers, /* which */ }, }, - { .name = "socket", .errmsg = true, }, + { .name = "socket", .errmsg = true, + .arg_scnprintf = { [0] = SCA_STRARRAY, /* family */ }, + .arg_parm = { [0] = &strarray__socket_families, /* family */ }, }, { .name = "stat", .errmsg = true, .alias = "newstat", }, { .name = "tgkill", .errmsg = true, .arg_scnprintf = { [2] = SCA_SIGNUM, /* sig */ }, }, -- cgit v0.10.2 From a28b24b27869d7a2fc0e6a0cae714641c83791a2 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 4 Sep 2013 11:00:44 -0300 Subject: perf trace: Beautify socket 'type' arg Taking into account the fact that the SOCK_ types can be overriden for ABI reasons on MIPS and also masking and interpreting the socket flags (NONBLOCK and CLOEXEC), printing whatever is left in the flags bits as an hex number, or'ed. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-cbn57082gq9v0sbsd67edwjq@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 6280166..2b19aef 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -265,6 +265,53 @@ static const char *socket_families[] = { }; static DEFINE_STRARRAY(socket_families); +#ifndef SOCK_TYPE_MASK +#define SOCK_TYPE_MASK 0xf +#endif + +static size_t syscall_arg__scnprintf_socket_type(char *bf, size_t size, + struct syscall_arg *arg) +{ + size_t printed; + int type = arg->val, + flags = type & ~SOCK_TYPE_MASK; + + type &= SOCK_TYPE_MASK; + /* + * Can't use a strarray, MIPS may override for ABI reasons. + */ + switch (type) { +#define P_SK_TYPE(n) case SOCK_##n: printed = scnprintf(bf, size, #n); break; + P_SK_TYPE(STREAM); + P_SK_TYPE(DGRAM); + P_SK_TYPE(RAW); + P_SK_TYPE(RDM); + P_SK_TYPE(SEQPACKET); + P_SK_TYPE(DCCP); + P_SK_TYPE(PACKET); +#undef P_SK_TYPE + default: + printed = scnprintf(bf, size, "%#x", type); + } + +#define P_SK_FLAG(n) \ + if (flags & SOCK_##n) { \ + printed += scnprintf(bf + printed, size - printed, "|%s", #n); \ + flags &= ~SOCK_##n; \ + } + + P_SK_FLAG(CLOEXEC); + P_SK_FLAG(NONBLOCK); +#undef P_SK_FLAG + + if (flags) + printed += scnprintf(bf + printed, size - printed, "|%#x", flags); + + return printed; +} + +#define SCA_SK_TYPE syscall_arg__scnprintf_socket_type + static size_t syscall_arg__scnprintf_open_flags(char *bf, size_t size, struct syscall_arg *arg) { @@ -440,7 +487,8 @@ static struct syscall_fmt { .arg_scnprintf = { [0] = SCA_STRARRAY, /* which */ }, .arg_parm = { [0] = &strarray__itimers, /* which */ }, }, { .name = "socket", .errmsg = true, - .arg_scnprintf = { [0] = SCA_STRARRAY, /* family */ }, + .arg_scnprintf = { [0] = SCA_STRARRAY, /* family */ + [1] = SCA_SK_TYPE, /* type */ }, .arg_parm = { [0] = &strarray__socket_families, /* family */ }, }, { .name = "stat", .errmsg = true, .alias = "newstat", }, { .name = "tgkill", .errmsg = true, -- cgit v0.10.2 From 511089994e33c9dbfe2c6e1fb2fb00b5e53866e8 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 4 Sep 2013 11:42:27 -0300 Subject: perf trace: Beautify access 'mode' arg Removing the _OK suffix and using RWX when all three bits are set, for instance. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-ypaz9k43lyqy94679feqnv8x@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 2b19aef..7aa6bca 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -312,6 +312,33 @@ static size_t syscall_arg__scnprintf_socket_type(char *bf, size_t size, #define SCA_SK_TYPE syscall_arg__scnprintf_socket_type +static size_t syscall_arg__scnprintf_access_mode(char *bf, size_t size, + struct syscall_arg *arg) +{ + size_t printed = 0; + int mode = arg->val; + + if (mode == F_OK) /* 0 */ + return scnprintf(bf, size, "F"); +#define P_MODE(n) \ + if (mode & n##_OK) { \ + printed += scnprintf(bf + printed, size - printed, "%s", #n); \ + mode &= ~n##_OK; \ + } + + P_MODE(R); + P_MODE(W); + P_MODE(X); +#undef P_MODE + + if (mode) + printed += scnprintf(bf + printed, size - printed, "|%#x", mode); + + return printed; +} + +#define SCA_ACCMODE syscall_arg__scnprintf_access_mode + static size_t syscall_arg__scnprintf_open_flags(char *bf, size_t size, struct syscall_arg *arg) { @@ -422,7 +449,8 @@ static struct syscall_fmt { bool timeout; bool hexret; } syscall_fmts[] = { - { .name = "access", .errmsg = true, }, + { .name = "access", .errmsg = true, + .arg_scnprintf = { [1] = SCA_ACCMODE, /* mode */ }, }, { .name = "arch_prctl", .errmsg = true, .alias = "prctl", }, { .name = "brk", .hexret = true, .arg_scnprintf = { [0] = SCA_HEX, /* brk */ }, }, -- cgit v0.10.2 From c045bf02e47664af67f8970a6a67065ff51acf62 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 4 Sep 2013 11:52:33 -0300 Subject: perf trace: Beautify rlmimit resources On the getrlimit, setrlimit and prlimit64 syscalls. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-pups75313afhn7p96qwhzs9v@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 7aa6bca..7ce036e 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -252,6 +252,13 @@ static const char *fcntl_cmds[] = { }; static DEFINE_STRARRAY(fcntl_cmds); +static const char *rlimit_resources[] = { + "CPU", "FSIZE", "DATA", "STACK", "CORE", "RSS", "NPROC", "NOFILE", + "MEMLOCK", "AS", "LOCKS", "SIGPENDING", "MSGQUEUE", "NICE", "RTPRIO", + "RTTIME", +}; +static DEFINE_STRARRAY(rlimit_resources); + static const char *sighow[] = { "BLOCK", "UNBLOCK", "SETMASK", }; static DEFINE_STRARRAY(sighow); @@ -466,6 +473,9 @@ static struct syscall_fmt { { .name = "getitimer", .errmsg = true, .arg_scnprintf = { [0] = SCA_STRARRAY, /* which */ }, .arg_parm = { [0] = &strarray__itimers, /* which */ }, }, + { .name = "getrlimit", .errmsg = true, + .arg_scnprintf = { [0] = SCA_STRARRAY, /* resource */ }, + .arg_parm = { [0] = &strarray__rlimit_resources, /* resource */ }, }, { .name = "ioctl", .errmsg = true, .arg_scnprintf = { [2] = SCA_HEX, /* arg */ }, }, { .name = "kill", .errmsg = true, @@ -498,6 +508,9 @@ static struct syscall_fmt { { .name = "poll", .errmsg = true, .timeout = true, }, { .name = "ppoll", .errmsg = true, .timeout = true, }, { .name = "pread", .errmsg = true, .alias = "pread64", }, + { .name = "prlimit64", .errmsg = true, + .arg_scnprintf = { [1] = SCA_STRARRAY, /* resource */ }, + .arg_parm = { [1] = &strarray__rlimit_resources, /* resource */ }, }, { .name = "pwrite", .errmsg = true, .alias = "pwrite64", }, { .name = "read", .errmsg = true, }, { .name = "recvfrom", .errmsg = true, }, @@ -514,6 +527,9 @@ static struct syscall_fmt { { .name = "setitimer", .errmsg = true, .arg_scnprintf = { [0] = SCA_STRARRAY, /* which */ }, .arg_parm = { [0] = &strarray__itimers, /* which */ }, }, + { .name = "setrlimit", .errmsg = true, + .arg_scnprintf = { [0] = SCA_STRARRAY, /* resource */ }, + .arg_parm = { [0] = &strarray__rlimit_resources, /* resource */ }, }, { .name = "socket", .errmsg = true, .arg_scnprintf = { [0] = SCA_STRARRAY, /* family */ [1] = SCA_SK_TYPE, /* type */ }, -- cgit v0.10.2 From 4bb09192d38ef08f0619667527cabb26354fff89 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 4 Sep 2013 12:37:43 -0600 Subject: perf trace: Add option to show full timestamp Current timestamp shown for output is time relative to firt sample. This patch adds an option to show the absolute perf_clock timestamp which is useful when comparing output across commands (e.g., perf-trace to perf-script). Signed-off-by: David Ahern Cc: Adrian Hunter Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1378319865-55695-1-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index daccd2c..a93e91a 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt @@ -78,6 +78,10 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs. --input Process events from a given perf data file. +-T +--time + Print full timestamp rather time relative to first sample. + SEE ALSO -------- linkperf:perf-record[1], linkperf:perf-script[1] diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 7ce036e..bc21140 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -624,6 +624,7 @@ struct trace { struct perf_record_opts opts; struct machine host; u64 base_time; + bool full_time; FILE *output; unsigned long nr_events; struct strlist *ev_qualifier; @@ -1066,7 +1067,7 @@ static int trace__process_sample(struct perf_tool *tool, if (skip_sample(trace, sample)) return 0; - if (trace->base_time == 0) + if (!trace->full_time && trace->base_time == 0) trace->base_time = sample->time; if (handler) @@ -1195,7 +1196,7 @@ again: continue; } - if (trace->base_time == 0) + if (!trace->full_time && trace->base_time == 0) trace->base_time = sample.time; if (type != PERF_RECORD_SAMPLE) { @@ -1433,6 +1434,8 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) trace__set_duration), OPT_BOOLEAN(0, "sched", &trace.sched, "show blocking scheduler events"), OPT_INCR('v', "verbose", &verbose, "be more verbose"), + OPT_BOOLEAN('T', "time", &trace.full_time, + "Show full timestamp, not time relative to first start"), OPT_END() }; int err; -- cgit v0.10.2 From 770480592084d2c114adedfbe6740e345aaf2279 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 4 Sep 2013 12:37:44 -0600 Subject: perf trace: Remove duplicate mmap entry in syscall_fmts array Entries in syscall_fmts need to be in alphabetical order, and the duplicate entry breaks bsearch on new entries around this duplicate entry. Signed-off-by: David Ahern Cc: Adrian Hunter Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1378319865-55695-2-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index bc21140..903416c 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -461,7 +461,6 @@ static struct syscall_fmt { { .name = "arch_prctl", .errmsg = true, .alias = "prctl", }, { .name = "brk", .hexret = true, .arg_scnprintf = { [0] = SCA_HEX, /* brk */ }, }, - { .name = "mmap", .hexret = true, }, { .name = "connect", .errmsg = true, }, { .name = "fcntl", .errmsg = true, .arg_scnprintf = { [1] = SCA_STRARRAY, /* cmd */ }, -- cgit v0.10.2 From 22ae5cf1c9b4e27a3efe96d8fa6d038560a2cf23 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 12 Sep 2013 11:27:34 -0300 Subject: perf trace: Don't print zeroed args This way we make the output more compact. If somebody complain (and provide a sane reason why we would like to see zeroes) we can make it an optional, ~/.perfconfig configurable knob. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-myqozw43hk8z2r5hsupzdk82@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 903416c..516f6b3 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -818,6 +818,9 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, if (arg.mask & bit) continue; + if (args[arg.idx] == 0) + continue; + printed += scnprintf(bf + printed, size - printed, "%s%s: ", printed ? ", " : "", field->name); if (sc->arg_scnprintf && sc->arg_scnprintf[arg.idx]) { -- cgit v0.10.2 From b2cc99fdaad20b6e787f0864a1ac7b14391f9fe6 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 12 Sep 2013 11:54:48 -0300 Subject: perf trace: Beautify send/recv syscall 'flags' arg [root@sandy ~]# perf trace -a -e recvmmsg,recvmsg,recvfrom,sendto,sendmsg,sendmmsg 6.901 (0.002 ms): 589 recvmsg(fd: 51, msg: 0x7fff35673420, flags: CMSG_CLOEXEC) = -1 EAGAIN Resource temporarily unavailable 6.966 (0.008 ms): 589 sendmsg(fd: 50, msg: 0x7fff35673230, flags: NOSIGNAL ) = 961 6.984 (0.004 ms): 979 sendmsg(fd: 3, msg: 0x7fff5b484940, flags: NOSIGNAL ) = 945 Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-h25k5k50nac0ej5cl5iwgvae@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 516f6b3..c400fbe 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -319,6 +319,60 @@ static size_t syscall_arg__scnprintf_socket_type(char *bf, size_t size, #define SCA_SK_TYPE syscall_arg__scnprintf_socket_type +#ifndef MSG_PROBE +#define MSG_PROBE 0x10 +#endif +#ifndef MSG_SENDPAGE_NOTLAST +#define MSG_SENDPAGE_NOTLAST 0x20000 +#endif +#ifndef MSG_FASTOPEN +#define MSG_FASTOPEN 0x20000000 +#endif + +static size_t syscall_arg__scnprintf_msg_flags(char *bf, size_t size, + struct syscall_arg *arg) +{ + int printed = 0, flags = arg->val; + + if (flags == 0) + return scnprintf(bf, size, "NONE"); +#define P_MSG_FLAG(n) \ + if (flags & MSG_##n) { \ + printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \ + flags &= ~MSG_##n; \ + } + + P_MSG_FLAG(OOB); + P_MSG_FLAG(PEEK); + P_MSG_FLAG(DONTROUTE); + P_MSG_FLAG(TRYHARD); + P_MSG_FLAG(CTRUNC); + P_MSG_FLAG(PROBE); + P_MSG_FLAG(TRUNC); + P_MSG_FLAG(DONTWAIT); + P_MSG_FLAG(EOR); + P_MSG_FLAG(WAITALL); + P_MSG_FLAG(FIN); + P_MSG_FLAG(SYN); + P_MSG_FLAG(CONFIRM); + P_MSG_FLAG(RST); + P_MSG_FLAG(ERRQUEUE); + P_MSG_FLAG(NOSIGNAL); + P_MSG_FLAG(MORE); + P_MSG_FLAG(WAITFORONE); + P_MSG_FLAG(SENDPAGE_NOTLAST); + P_MSG_FLAG(FASTOPEN); + P_MSG_FLAG(CMSG_CLOEXEC); +#undef P_MSG_FLAG + + if (flags) + printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags); + + return printed; +} + +#define SCA_MSG_FLAGS syscall_arg__scnprintf_msg_flags + static size_t syscall_arg__scnprintf_access_mode(char *bf, size_t size, struct syscall_arg *arg) { @@ -512,7 +566,12 @@ static struct syscall_fmt { .arg_parm = { [1] = &strarray__rlimit_resources, /* resource */ }, }, { .name = "pwrite", .errmsg = true, .alias = "pwrite64", }, { .name = "read", .errmsg = true, }, - { .name = "recvfrom", .errmsg = true, }, + { .name = "recvfrom", .errmsg = true, + .arg_scnprintf = { [3] = SCA_MSG_FLAGS, /* flags */ }, }, + { .name = "recvmmsg", .errmsg = true, + .arg_scnprintf = { [3] = SCA_MSG_FLAGS, /* flags */ }, }, + { .name = "recvmsg", .errmsg = true, + .arg_scnprintf = { [2] = SCA_MSG_FLAGS, /* flags */ }, }, { .name = "rt_sigaction", .errmsg = true, .arg_scnprintf = { [0] = SCA_SIGNUM, /* sig */ }, }, { .name = "rt_sigprocmask", .errmsg = true, @@ -523,6 +582,12 @@ static struct syscall_fmt { { .name = "rt_tgsigqueueinfo", .errmsg = true, .arg_scnprintf = { [2] = SCA_SIGNUM, /* sig */ }, }, { .name = "select", .errmsg = true, .timeout = true, }, + { .name = "sendmmsg", .errmsg = true, + .arg_scnprintf = { [3] = SCA_MSG_FLAGS, /* flags */ }, }, + { .name = "sendmsg", .errmsg = true, + .arg_scnprintf = { [2] = SCA_MSG_FLAGS, /* flags */ }, }, + { .name = "sendto", .errmsg = true, + .arg_scnprintf = { [3] = SCA_MSG_FLAGS, /* flags */ }, }, { .name = "setitimer", .errmsg = true, .arg_scnprintf = { [0] = SCA_STRARRAY, /* which */ }, .arg_parm = { [0] = &strarray__itimers, /* which */ }, }, -- cgit v0.10.2 From 49af9e93adfa11d50435aa079299a765843532fc Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 12 Sep 2013 12:18:56 -0300 Subject: perf trace: Beautify eventfd2 'flags' arg 61.168 ( 0.004 ms): 24267 eventfd2(flags: CLOEXEC|NONBLOCK) = 9 Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-3hg8eajdzil077501c8f5jkw@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index c400fbe..8a09ba3 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -456,6 +457,32 @@ static size_t syscall_arg__scnprintf_open_flags(char *bf, size_t size, #define SCA_OPEN_FLAGS syscall_arg__scnprintf_open_flags +static size_t syscall_arg__scnprintf_eventfd_flags(char *bf, size_t size, + struct syscall_arg *arg) +{ + int printed = 0, flags = arg->val; + + if (flags == 0) + return scnprintf(bf, size, "NONE"); +#define P_FLAG(n) \ + if (flags & EFD_##n) { \ + printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \ + flags &= ~EFD_##n; \ + } + + P_FLAG(SEMAPHORE); + P_FLAG(CLOEXEC); + P_FLAG(NONBLOCK); +#undef P_FLAG + + if (flags) + printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags); + + return printed; +} + +#define SCA_EFD_FLAGS syscall_arg__scnprintf_eventfd_flags + static size_t syscall_arg__scnprintf_signum(char *bf, size_t size, struct syscall_arg *arg) { int sig = arg->val; @@ -516,6 +543,8 @@ static struct syscall_fmt { { .name = "brk", .hexret = true, .arg_scnprintf = { [0] = SCA_HEX, /* brk */ }, }, { .name = "connect", .errmsg = true, }, + { .name = "eventfd2", .errmsg = true, + .arg_scnprintf = { [1] = SCA_EFD_FLAGS, /* flags */ }, }, { .name = "fcntl", .errmsg = true, .arg_scnprintf = { [1] = SCA_STRARRAY, /* cmd */ }, .arg_parm = { [1] = &strarray__fcntl_cmds, /* cmd */ }, }, -- cgit v0.10.2 From 50c95cbd70808aa2e5ba8d79e503456f1da37aeb Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 12 Sep 2013 12:35:21 -0300 Subject: perf trace: Add option to show process COMM Enabled by default, disable with --no-comm, e.g.: 181.821 (0.001 ms): deja-dup-monit/10784 recvmsg(fd: 8, msg: 0x7fff4342baf0, flags: PEEK|TRUNC|CMSG_CLOEXEC ) = 20 181.824 (0.001 ms): deja-dup-monit/10784 geteuid( ) = 1000 181.825 (0.001 ms): deja-dup-monit/10784 getegid( ) = 1000 181.834 (0.002 ms): deja-dup-monit/10784 recvmsg(fd: 8, msg: 0x7fff4342baf0, flags: CMSG_CLOEXEC ) = 20 181.836 (0.001 ms): deja-dup-monit/10784 geteuid( ) = 1000 181.838 (0.001 ms): deja-dup-monit/10784 getegid( ) = 1000 181.705 (0.003 ms): evolution-addr/10924 recvmsg(fd: 10, msg: 0x7fff17dc6990, flags: PEEK|TRUNC|CMSG_CLOEXEC) = 1256 181.710 (0.002 ms): evolution-addr/10924 geteuid( ) = 1000 181.712 (0.001 ms): evolution-addr/10924 getegid( ) = 1000 181.727 (0.003 ms): evolution-addr/10924 recvmsg(fd: 10, msg: 0x7fff17dc6990, flags: CMSG_CLOEXEC ) = 1256 181.731 (0.001 ms): evolution-addr/10924 geteuid( ) = 1000 181.734 (0.001 ms): evolution-addr/10924 getegid( ) = 1000 181.908 (0.002 ms): evolution-addr/10924 recvmsg(fd: 10, msg: 0x7fff17dc6990, flags: PEEK|TRUNC|CMSG_CLOEXEC) = 20 181.913 (0.001 ms): evolution-addr/10924 geteuid( ) = 1000 181.915 (0.001 ms): evolution-addr/10924 getegid( ) = 1000 181.930 (0.003 ms): evolution-addr/10924 recvmsg(fd: 10, msg: 0x7fff17dc6990, flags: CMSG_CLOEXEC ) = 20 181.934 (0.001 ms): evolution-addr/10924 geteuid( ) = 1000 181.937 (0.001 ms): evolution-addr/10924 getegid( ) = 1000 220.718 (0.010 ms): at-spi2-regist/10715 sendmsg(fd: 3, msg: 0x7fffdb8756c0, flags: NOSIGNAL ) = 200 220.741 (0.000 ms): dbus-daemon/10711 ... [continued]: epoll_wait()) = 1 220.759 (0.004 ms): dbus-daemon/10711 recvmsg(fd: 11, msg: 0x7ffff94594d0, flags: CMSG_CLOEXEC ) = 200 220.780 (0.002 ms): dbus-daemon/10711 recvmsg(fd: 11, msg: 0x7ffff94594d0, flags: CMSG_CLOEXEC ) = 200 220.788 (0.001 ms): dbus-daemon/10711 recvmsg(fd: 11, msg: 0x7ffff94594d0, flags: CMSG_CLOEXEC ) = -1 EAGAIN Resource temporarily unavailable 220.760 (0.004 ms): at-spi2-regist/10715 sendmsg(fd: 3, msg: 0x7fffdb8756c0, flags: NOSIGNAL ) = 200 220.771 (0.023 ms): perf/26347 open(filename: 0xf2e780, mode: 15918976 ) = 19 220.850 (0.002 ms): perf/26347 close(fd: 19 ) = 0 Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-6be5jvnkdzjptdrebfn5263n@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index a93e91a..3777385 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt @@ -82,6 +82,9 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs. --time Print full timestamp rather time relative to first sample. +--comm:: + Show process COMM right beside its ID, on by default, disable with --no-comm. + SEE ALSO -------- linkperf:perf-record[1], linkperf:perf-script[1] diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 8a09ba3..be65843 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -726,6 +726,7 @@ struct trace { struct intlist *pid_list; bool sched; bool multiple_threads; + bool show_comm; double duration_filter; double runtime_ms; }; @@ -755,8 +756,11 @@ static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thre size_t printed = trace__fprintf_tstamp(trace, tstamp, fp); printed += fprintf_duration(duration, fp); - if (trace->multiple_threads) + if (trace->multiple_threads) { + if (trace->show_comm) + printed += fprintf(fp, "%.14s/", thread->comm); printed += fprintf(fp, "%d ", thread->tid); + } return printed; } @@ -1503,10 +1507,13 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) .mmap_pages = 1024, }, .output = stdout, + .show_comm = true, }; const char *output_name = NULL; const char *ev_qualifier_str = NULL; const struct option trace_options[] = { + OPT_BOOLEAN(0, "comm", &trace.show_comm, + "show the thread COMM next to its id"), OPT_STRING('e', "expr", &ev_qualifier_str, "expr", "list of events to trace"), OPT_STRING('o', "output", &output_name, "file", "output file name"), -- cgit v0.10.2 From 1ba6e01782fd2a94481e18b91b363636f8171565 Mon Sep 17 00:00:00 2001 From: Ramkumar Ramachandra Date: Thu, 4 Jul 2013 18:11:25 +0530 Subject: perf completion: Don't dictate perf install location The statement have perf limits the locations in which to look for the perf program. Moreover, it depends on the bash-completion package to be installed. Replace it with a call to `type perf`. Signed-off-by: Ramkumar Ramachandra Cc: Frederic Weisbecker Link: http://lkml.kernel.org/r/1372941691-14684-2-git-send-email-artagnon@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/bash_completion b/tools/perf/bash_completion index 56e6a12..50540cf 100644 --- a/tools/perf/bash_completion +++ b/tools/perf/bash_completion @@ -19,7 +19,7 @@ __ltrim_colon_completions() fi } -have perf && +type perf &>/dev/null && _perf() { local cur prev cmd -- cgit v0.10.2 From 30079d1d5ebcb8d706c6e05cacebb7facc60cd95 Mon Sep 17 00:00:00 2001 From: Ramkumar Ramachandra Date: Thu, 4 Jul 2013 18:11:26 +0530 Subject: perf completion: Update __ltrim_colon_completions The function is taken from the bash-completion package; update it to use the latest version where colon_word doesn't miss quoting. Signed-off-by: Ramkumar Ramachandra Cc: Frederic Weisbecker Link: http://lkml.kernel.org/r/1372941691-14684-3-git-send-email-artagnon@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/bash_completion b/tools/perf/bash_completion index 50540cf..b0cdd12 100644 --- a/tools/perf/bash_completion +++ b/tools/perf/bash_completion @@ -11,7 +11,7 @@ __ltrim_colon_completions() { if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then # Remove colon-word prefix from COMPREPLY items - local colon_word=${1%${1##*:}} + local colon_word=${1%"${1##*:}"} local i=${#COMPREPLY[*]} while [[ $((--i)) -ge 0 ]]; do COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"} -- cgit v0.10.2 From 7b6c48e16e5d312b0ebae78acfb3ff4f9c8c083c Mon Sep 17 00:00:00 2001 From: Ramkumar Ramachandra Date: Thu, 4 Jul 2013 18:11:27 +0530 Subject: perf completion: Strip dependency on _filedir _filedir is defined in the bash-completion package, but there is no need to depend on it. Instead, call complete with multiple -o arguments before the -F argument like in git.git's completion script. Signed-off-by: Ramkumar Ramachandra Cc: Frederic Weisbecker Link: http://lkml.kernel.org/r/1372941691-14684-4-git-send-email-artagnon@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/bash_completion b/tools/perf/bash_completion index b0cdd12..d2598be 100644 --- a/tools/perf/bash_completion +++ b/tools/perf/bash_completion @@ -54,9 +54,8 @@ _perf() subcmd=${COMP_WORDS[1]} opts=$($cmd $subcmd --list-opts) COMPREPLY=( $( compgen -W '$opts' -- "$cur" ) ) - # Fall down to list regular files - else - _filedir fi } && -complete -F _perf perf + +complete -o bashdefault -o default -o nospace -F _perf perf 2>/dev/null \ + || complete -o default -o nospace -F _perf perf -- cgit v0.10.2 From 4685a6cfaeac6d2fe8ed10a9aa0cbff5026529cb Mon Sep 17 00:00:00 2001 From: Ramkumar Ramachandra Date: Thu, 4 Jul 2013 18:11:29 +0530 Subject: perf completion: Strip function_exists () Use "type" to check existence consistently. Signed-off-by: Ramkumar Ramachandra Cc: Frederic Weisbecker Link: http://lkml.kernel.org/r/1372941691-14684-6-git-send-email-artagnon@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/bash_completion b/tools/perf/bash_completion index d2598be..35fdda1 100644 --- a/tools/perf/bash_completion +++ b/tools/perf/bash_completion @@ -1,12 +1,6 @@ # perf completion -function_exists() -{ - declare -F $1 > /dev/null - return $? -} - -function_exists __ltrim_colon_completions || +type __ltrim_colon_completions &>/dev/null || __ltrim_colon_completions() { if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then -- cgit v0.10.2 From c3fb6717e90049b93d0f5f5714a4d878799d89c2 Mon Sep 17 00:00:00 2001 From: Ramkumar Ramachandra Date: Thu, 4 Jul 2013 18:11:30 +0530 Subject: perf completion: Strip dependency on bash-completion The bash-completion package defines the _get_comp_words_by_ref function. There is no need to depend on it, as we can reimplement it like git.git has. Signed-off-by: Ramkumar Ramachandra Cc: Frederic Weisbecker Cc: Namhyung Kim Link: http://lkml.kernel.org/r/1372941691-14684-7-git-send-email-artagnon@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/bash_completion b/tools/perf/bash_completion index 35fdda1..ee9c6d8 100644 --- a/tools/perf/bash_completion +++ b/tools/perf/bash_completion @@ -1,5 +1,81 @@ # perf completion +# Taken from git.git's completion script. +__my_reassemble_comp_words_by_ref() +{ + local exclude i j first + # Which word separators to exclude? + exclude="${1//[^$COMP_WORDBREAKS]}" + cword_=$COMP_CWORD + if [ -z "$exclude" ]; then + words_=("${COMP_WORDS[@]}") + return + fi + # List of word completion separators has shrunk; + # re-assemble words to complete. + for ((i=0, j=0; i < ${#COMP_WORDS[@]}; i++, j++)); do + # Append each nonempty word consisting of just + # word separator characters to the current word. + first=t + while + [ $i -gt 0 ] && + [ -n "${COMP_WORDS[$i]}" ] && + # word consists of excluded word separators + [ "${COMP_WORDS[$i]//[^$exclude]}" = "${COMP_WORDS[$i]}" ] + do + # Attach to the previous token, + # unless the previous token is the command name. + if [ $j -ge 2 ] && [ -n "$first" ]; then + ((j--)) + fi + first= + words_[$j]=${words_[j]}${COMP_WORDS[i]} + if [ $i = $COMP_CWORD ]; then + cword_=$j + fi + if (($i < ${#COMP_WORDS[@]} - 1)); then + ((i++)) + else + # Done. + return + fi + done + words_[$j]=${words_[j]}${COMP_WORDS[i]} + if [ $i = $COMP_CWORD ]; then + cword_=$j + fi + done +} + +type _get_comp_words_by_ref &>/dev/null || +_get_comp_words_by_ref() +{ + local exclude cur_ words_ cword_ + if [ "$1" = "-n" ]; then + exclude=$2 + shift 2 + fi + __my_reassemble_comp_words_by_ref "$exclude" + cur_=${words_[cword_]} + while [ $# -gt 0 ]; do + case "$1" in + cur) + cur=$cur_ + ;; + prev) + prev=${words_[$cword_-1]} + ;; + words) + words=("${words_[@]}") + ;; + cword) + cword=$cword_ + ;; + esac + shift + done +} + type __ltrim_colon_completions &>/dev/null || __ltrim_colon_completions() { @@ -19,12 +95,7 @@ _perf() local cur prev cmd COMPREPLY=() - if function_exists _get_comp_words_by_ref; then - _get_comp_words_by_ref -n : cur prev - else - cur=$(_get_cword :) - prev=${COMP_WORDS[COMP_CWORD-1]} - fi + _get_comp_words_by_ref -n : cur prev cmd=${COMP_WORDS[0]} -- cgit v0.10.2 From 6e0dc374a2c912a8a967ea8a4f9696dd4b0a6d3e Mon Sep 17 00:00:00 2001 From: Ramkumar Ramachandra Date: Thu, 4 Jul 2013 18:11:31 +0530 Subject: perf completion: Use more comp words The completion words $words and $cword are available, so we might as well use them instead of directly accessing COMP_WORDS. Signed-off-by: Ramkumar Ramachandra Cc: Frederic Weisbecker Link: http://lkml.kernel.org/r/1372941691-14684-8-git-send-email-artagnon@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/bash_completion b/tools/perf/bash_completion index ee9c6d8..62e157db 100644 --- a/tools/perf/bash_completion +++ b/tools/perf/bash_completion @@ -92,15 +92,15 @@ __ltrim_colon_completions() type perf &>/dev/null && _perf() { - local cur prev cmd + local cur words cword prev cmd COMPREPLY=() - _get_comp_words_by_ref -n : cur prev + _get_comp_words_by_ref -n =: cur words cword prev - cmd=${COMP_WORDS[0]} + cmd=${words[0]} # List perf subcommands or long options - if [ $COMP_CWORD -eq 1 ]; then + if [ $cword -eq 1 ]; then if [[ $cur == --* ]]; then COMPREPLY=( $( compgen -W '--help --version \ --exec-path --html-path --paginate --no-pager \ @@ -110,13 +110,13 @@ _perf() COMPREPLY=( $( compgen -W '$cmds' -- "$cur" ) ) fi # List possible events for -e option - elif [[ $prev == "-e" && "${COMP_WORDS[1]}" == @(record|stat|top) ]]; then + elif [[ $prev == "-e" && "${words[1]}" == @(record|stat|top) ]]; then evts=$($cmd list --raw-dump) COMPREPLY=( $( compgen -W '$evts' -- "$cur" ) ) __ltrim_colon_completions $cur # List long option names elif [[ $cur == --* ]]; then - subcmd=${COMP_WORDS[1]} + subcmd=${words[1]} opts=$($cmd $subcmd --list-opts) COMPREPLY=( $( compgen -W '$opts' -- "$cur" ) ) fi -- cgit v0.10.2 From 9cd00941f8c274e6ca03ed238d96ddb0be474b86 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Wed, 18 Sep 2013 15:56:14 +0200 Subject: perf symbols: Support for Openembedded/Yocto -dbg packages On OpenEmbedded the symbol files are located under a .debug folder on the same folder as the binary file. This patch adds support for such files. Without this patch on perf top you can see: no symbols found in /usr/lib/gstreamer-1.0/libtheoraenc.so.1.1.2, maybe install a debug package? 84.56% libtheoraenc.so.1.1.2 [.] 0x000000000000b346 With this patch symbols are shown: 19.06% libtheoraenc.so.1.1.2 [.] oc_int_frag_satd_thresh_mmxext 9.76% libtheoraenc.so.1.1.2 [.] oc_analyze_mb_mode_luma 5.58% libtheoraenc.so.1.1.2 [.] oc_qii_state_advance 4.84% libtheoraenc.so.1.1.2 [.] oc_enc_tokenize_ac ... Signed-off-by: Ricardo Ribalda Delgado Acked-by: Ingo Molnar Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Waiman Long Link: http://lkml.kernel.org/r/1379512574-25912-1-git-send-email-ricardo.ribalda@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index e3c1ff8..6bfc8aa 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -7,19 +7,20 @@ char dso__symtab_origin(const struct dso *dso) { static const char origin[] = { - [DSO_BINARY_TYPE__KALLSYMS] = 'k', - [DSO_BINARY_TYPE__VMLINUX] = 'v', - [DSO_BINARY_TYPE__JAVA_JIT] = 'j', - [DSO_BINARY_TYPE__DEBUGLINK] = 'l', - [DSO_BINARY_TYPE__BUILD_ID_CACHE] = 'B', - [DSO_BINARY_TYPE__FEDORA_DEBUGINFO] = 'f', - [DSO_BINARY_TYPE__UBUNTU_DEBUGINFO] = 'u', - [DSO_BINARY_TYPE__BUILDID_DEBUGINFO] = 'b', - [DSO_BINARY_TYPE__SYSTEM_PATH_DSO] = 'd', - [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K', - [DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g', - [DSO_BINARY_TYPE__GUEST_KMODULE] = 'G', - [DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V', + [DSO_BINARY_TYPE__KALLSYMS] = 'k', + [DSO_BINARY_TYPE__VMLINUX] = 'v', + [DSO_BINARY_TYPE__JAVA_JIT] = 'j', + [DSO_BINARY_TYPE__DEBUGLINK] = 'l', + [DSO_BINARY_TYPE__BUILD_ID_CACHE] = 'B', + [DSO_BINARY_TYPE__FEDORA_DEBUGINFO] = 'f', + [DSO_BINARY_TYPE__UBUNTU_DEBUGINFO] = 'u', + [DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO] = 'o', + [DSO_BINARY_TYPE__BUILDID_DEBUGINFO] = 'b', + [DSO_BINARY_TYPE__SYSTEM_PATH_DSO] = 'd', + [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K', + [DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g', + [DSO_BINARY_TYPE__GUEST_KMODULE] = 'G', + [DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V', }; if (dso == NULL || dso->symtab_type == DSO_BINARY_TYPE__NOT_FOUND) @@ -64,6 +65,28 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, symbol_conf.symfs, dso->long_name); break; + case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO: + { + char *last_slash; + size_t len; + size_t dir_size; + + last_slash = dso->long_name + dso->long_name_len; + while (last_slash != dso->long_name && *last_slash != '/') + last_slash--; + + len = scnprintf(file, size, "%s", symbol_conf.symfs); + dir_size = last_slash - dso->long_name + 2; + if (dir_size > (size - len)) { + ret = -1; + break; + } + len += scnprintf(file + len, dir_size, "%s", dso->long_name); + len += scnprintf(file + len , size - len, ".debug%s", + last_slash); + break; + } + case DSO_BINARY_TYPE__BUILDID_DEBUGINFO: if (!dso->has_build_id) { ret = -1; diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index b793053..dbd9241 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -23,6 +23,7 @@ enum dso_binary_type { DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, DSO_BINARY_TYPE__KCORE, DSO_BINARY_TYPE__GUEST_KCORE, + DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, DSO_BINARY_TYPE__NOT_FOUND, }; diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 7eb0362..cd1dcc4 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -51,6 +51,7 @@ static enum dso_binary_type binary_type_symtab[] = { DSO_BINARY_TYPE__SYSTEM_PATH_DSO, DSO_BINARY_TYPE__GUEST_KMODULE, DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, + DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, DSO_BINARY_TYPE__NOT_FOUND, }; -- cgit v0.10.2 From 5283ec23a02e8afdc984c7f5f07e2a2662d4934a Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 12 Sep 2013 18:39:34 +0200 Subject: perf tools: Remove unused trace-event-* code Removing unused trace-event-* code. Acked-by: Namhyung Kim Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1379003976-5839-3-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index e9e1c03..6681f71 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -120,42 +120,6 @@ raw_field_value(struct event_format *event, const char *name, void *data) return val; } -void *raw_field_ptr(struct event_format *event, const char *name, void *data) -{ - struct format_field *field; - - field = pevent_find_any_field(event, name); - if (!field) - return NULL; - - if (field->flags & FIELD_IS_DYNAMIC) { - int offset; - - offset = *(int *)(data + field->offset); - offset &= 0xffff; - - return data + offset; - } - - return data + field->offset; -} - -int trace_parse_common_type(struct pevent *pevent, void *data) -{ - struct pevent_record record; - - record.data = data; - return pevent_data_type(pevent, &record); -} - -int trace_parse_common_pid(struct pevent *pevent, void *data) -{ - struct pevent_record record; - - record.data = data; - return pevent_data_pid(pevent, &record); -} - unsigned long long read_size(struct event_format *event, void *ptr, int size) { return pevent_read_number(event->pevent, ptr, size); diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index fafe1a4..04df631 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -11,8 +11,6 @@ union perf_event; struct perf_tool; struct thread; -extern struct pevent *perf_pevent; - int bigendian(void); struct pevent *read_trace_init(int file_bigendian, int host_bigendian); @@ -23,26 +21,19 @@ int parse_ftrace_file(struct pevent *pevent, char *buf, unsigned long size); int parse_event_file(struct pevent *pevent, char *buf, unsigned long size, char *sys); -struct pevent_record *trace_peek_data(struct pevent *pevent, int cpu); - unsigned long long raw_field_value(struct event_format *event, const char *name, void *data); -void *raw_field_ptr(struct event_format *event, const char *name, void *data); void parse_proc_kallsyms(struct pevent *pevent, char *file, unsigned int size); void parse_ftrace_printk(struct pevent *pevent, char *file, unsigned int size); ssize_t trace_report(int fd, struct pevent **pevent, bool repipe); -int trace_parse_common_type(struct pevent *pevent, void *data); -int trace_parse_common_pid(struct pevent *pevent, void *data); - struct event_format *trace_find_next_event(struct pevent *pevent, struct event_format *event); unsigned long long read_size(struct event_format *event, void *ptr, int size); unsigned long long eval_flag(const char *flag); -struct pevent_record *trace_read_data(struct pevent *pevent, int cpu); int read_tracing_data(int fd, struct list_head *pattrs); struct tracing_data { -- cgit v0.10.2 From 918512b435e15fefe609d236e0ecd62cb8f389c9 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 12 Sep 2013 18:39:35 +0200 Subject: perf tools: Unify page_size usage Making page_size global from the util object. Removing the not needed one. Signed-off-by: Jiri Olsa Acked-by: Namhyung Kim Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1379003976-5839-4-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index cf36ba2..0aacd62 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -70,7 +70,6 @@ struct perf_record { struct perf_session *session; const char *progname; int output; - unsigned int page_size; int realtime_prio; bool no_buildid; bool no_buildid_cache; @@ -119,7 +118,7 @@ static int perf_record__mmap_read(struct perf_record *rec, { unsigned int head = perf_mmap__read_head(md); unsigned int old = md->prev; - unsigned char *data = md->base + rec->page_size; + unsigned char *data = md->base + page_size; unsigned long size; void *buf; int rc = 0; @@ -360,8 +359,6 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) rec->progname = argv[0]; - rec->page_size = sysconf(_SC_PAGE_SIZE); - on_exit(perf_record__sig_exit, rec); signal(SIGCHLD, sig_handler); signal(SIGINT, sig_handler); diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 245020c..6265778 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -456,6 +456,7 @@ int main(int argc, const char **argv) { const char *cmd; + /* The page_size is placed in util object. */ page_size = sysconf(_SC_PAGE_SIZE); cmd = perf_extract_argv0_path(argv[0]); diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 71b5412..a24ce0a 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -1036,6 +1036,7 @@ PyMODINIT_FUNC initperf(void) pyrf_cpu_map__setup_types() < 0) return; + /* The page_size is placed in util object. */ page_size = sysconf(_SC_PAGE_SIZE); Py_INCREF(&pyrf_evlist__type); -- cgit v0.10.2 From 02ad0702e54f9b82b697718e7e8662eb3f2266ee Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Sun, 8 Sep 2013 19:19:13 -0700 Subject: perf lock: Remove dead code No need for break statements after goto jumps. Signed-off-by: Davidlohr Bueso Cc: Aswin Chandramouleeswaran Cc: Frederic Weisbecker Cc: Hitoshi Mitake Cc: Ingo Molnar Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378693159-8747-2-git-send-email-davidlohr@hp.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index ee33ba2..148f7e2 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -446,7 +446,6 @@ broken: list_del(&seq->list); free(seq); goto end; - break; default: BUG_ON("Unknown state of lock sequence found!\n"); break; @@ -508,8 +507,6 @@ static int report_lock_acquired_event(struct perf_evsel *evsel, list_del(&seq->list); free(seq); goto end; - break; - default: BUG_ON("Unknown state of lock sequence found!\n"); break; @@ -564,7 +561,6 @@ static int report_lock_contended_event(struct perf_evsel *evsel, list_del(&seq->list); free(seq); goto end; - break; default: BUG_ON("Unknown state of lock sequence found!\n"); break; @@ -606,7 +602,6 @@ static int report_lock_release_event(struct perf_evsel *evsel, switch (seq->state) { case SEQ_STATE_UNINITIALIZED: goto end; - break; case SEQ_STATE_ACQUIRED: break; case SEQ_STATE_READ_ACQUIRED: @@ -624,7 +619,6 @@ static int report_lock_release_event(struct perf_evsel *evsel, ls->discard = 1; bad_hist[BROKEN_RELEASE]++; goto free_seq; - break; default: BUG_ON("Unknown state of lock sequence found!\n"); break; -- cgit v0.10.2 From b33492ade49a223a666e582d0c63566609e7014b Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Sun, 8 Sep 2013 19:19:14 -0700 Subject: perf lock: Return proper code in report_lock_*_event The report_lock_*_event() functions return -1 when lock_stat_findnew(), thread_stat_findnew() or get_seq() return NULL. These functions only return this value when failing to allocate memory, this return -ENOMEM instead. Signed-off-by: Davidlohr Bueso Cc: Aswin Chandramouleeswaran Cc: Frederic Weisbecker Cc: Hitoshi Mitake Cc: Ingo Molnar Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378693159-8747-3-git-send-email-davidlohr@hp.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 148f7e2..d318862 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -400,17 +400,17 @@ static int report_lock_acquire_event(struct perf_evsel *evsel, ls = lock_stat_findnew(addr, name); if (!ls) - return -1; + return -ENOMEM; if (ls->discard) return 0; ts = thread_stat_findnew(sample->tid); if (!ts) - return -1; + return -ENOMEM; seq = get_seq(ts, addr); if (!seq) - return -1; + return -ENOMEM; switch (seq->state) { case SEQ_STATE_UNINITIALIZED: @@ -472,17 +472,17 @@ static int report_lock_acquired_event(struct perf_evsel *evsel, ls = lock_stat_findnew(addr, name); if (!ls) - return -1; + return -ENOMEM; if (ls->discard) return 0; ts = thread_stat_findnew(sample->tid); if (!ts) - return -1; + return -ENOMEM; seq = get_seq(ts, addr); if (!seq) - return -1; + return -ENOMEM; switch (seq->state) { case SEQ_STATE_UNINITIALIZED: @@ -533,17 +533,17 @@ static int report_lock_contended_event(struct perf_evsel *evsel, ls = lock_stat_findnew(addr, name); if (!ls) - return -1; + return -ENOMEM; if (ls->discard) return 0; ts = thread_stat_findnew(sample->tid); if (!ts) - return -1; + return -ENOMEM; seq = get_seq(ts, addr); if (!seq) - return -1; + return -ENOMEM; switch (seq->state) { case SEQ_STATE_UNINITIALIZED: @@ -587,17 +587,17 @@ static int report_lock_release_event(struct perf_evsel *evsel, ls = lock_stat_findnew(addr, name); if (!ls) - return -1; + return -ENOMEM; if (ls->discard) return 0; ts = thread_stat_findnew(sample->tid); if (!ts) - return -1; + return -ENOMEM; seq = get_seq(ts, addr); if (!seq) - return -1; + return -ENOMEM; switch (seq->state) { case SEQ_STATE_UNINITIALIZED: -- cgit v0.10.2 From 0a98c7febf55325ebac4f28289a9433f4b66ed0e Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Sun, 8 Sep 2013 19:19:15 -0700 Subject: perf lock: Plug some memleaks Address some trivial leaks. Signed-off-by: Davidlohr Bueso Cc: Aswin Chandramouleeswaran Cc: Frederic Weisbecker Cc: Hitoshi Mitake Cc: Ingo Molnar Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378693159-8747-4-git-send-email-davidlohr@hp.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index d318862..7784347 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -321,10 +321,12 @@ static struct lock_stat *lock_stat_findnew(void *addr, const char *name) new->addr = addr; new->name = zalloc(sizeof(char) * strlen(name) + 1); - if (!new->name) + if (!new->name) { + free(new); goto alloc_failed; - strcpy(new->name, name); + } + strcpy(new->name, name); new->wait_time_min = ULLONG_MAX; list_add(&new->hash_entry, entry); @@ -875,7 +877,7 @@ static int __cmd_record(int argc, const char **argv) const char *record_args[] = { "record", "-R", "-m", "1024", "-c", "1", }; - unsigned int rec_argc, i, j; + unsigned int rec_argc, i, j, ret; const char **rec_argv; for (i = 0; i < ARRAY_SIZE(lock_tracepoints); i++) { @@ -892,7 +894,7 @@ static int __cmd_record(int argc, const char **argv) rec_argc += 2 * ARRAY_SIZE(lock_tracepoints); rec_argv = calloc(rec_argc + 1, sizeof(char *)); - if (rec_argv == NULL) + if (!rec_argv) return -ENOMEM; for (i = 0; i < ARRAY_SIZE(record_args); i++) @@ -908,7 +910,9 @@ static int __cmd_record(int argc, const char **argv) BUG_ON(i != rec_argc); - return cmd_record(i, rec_argv, NULL); + ret = cmd_record(i, rec_argv, NULL); + free(rec_argv); + return ret; } int cmd_lock(int argc, const char **argv, const char *prefix __maybe_unused) -- cgit v0.10.2 From 375eb2be5584b8182a917124ca217b74e43d2dc4 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Sun, 8 Sep 2013 19:19:16 -0700 Subject: perf lock: Redo __cmd_report This function should be straightforward, and we can remove some trivial logic by moving the functionality of read_events() into __cmd_report() - thus allowing a new session to be properly deleted. Since the 'info' subcommand also needs to process the recorded events, add a 'display_info' flag to differentiate between report and info commands. Furthermore, this patch also calls perf_session__has_traces(), making sure that we don't compare apples and oranges, fixing a segfault when using an perf.data file generated by a different subcommand. ie: ./perf mem record sleep 1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.017 MB perf.data (~724 samples) ] ./perf lock report Segmentation fault (core dumped) Signed-off-by: Davidlohr Bueso Cc: Aswin Chandramouleeswaran Cc: Frederic Weisbecker Cc: Hitoshi Mitake Cc: Ingo Molnar Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378693159-8747-5-git-send-email-davidlohr@hp.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 7784347..780484f 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -818,6 +818,18 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, return 0; } +static void sort_result(void) +{ + unsigned int i; + struct lock_stat *st; + + for (i = 0; i < LOCKHASH_SIZE; i++) { + list_for_each_entry(st, &lockhash_table[i], hash_entry) { + insert_to_result(st, compare); + } + } +} + static const struct perf_evsel_str_handler lock_tracepoints[] = { { "lock:lock_acquire", perf_evsel__process_lock_acquire, }, /* CONFIG_LOCKDEP */ { "lock:lock_acquired", perf_evsel__process_lock_acquired, }, /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */ @@ -825,51 +837,47 @@ static const struct perf_evsel_str_handler lock_tracepoints[] = { { "lock:lock_release", perf_evsel__process_lock_release, }, /* CONFIG_LOCKDEP */ }; -static int read_events(void) +static int __cmd_report(bool display_info) { + int err = -EINVAL; struct perf_tool eops = { .sample = process_sample_event, .comm = perf_event__process_comm, .ordered_samples = true, }; + session = perf_session__new(input_name, O_RDONLY, 0, false, &eops); if (!session) { pr_err("Initializing perf session failed\n"); - return -1; + return -ENOMEM; } + if (!perf_session__has_traces(session, "lock record")) + goto out_delete; + if (perf_session__set_tracepoints_handlers(session, lock_tracepoints)) { pr_err("Initializing perf session tracepoint handlers failed\n"); - return -1; + goto out_delete; } - return perf_session__process_events(session, &eops); -} + if (select_key()) + goto out_delete; -static void sort_result(void) -{ - unsigned int i; - struct lock_stat *st; - - for (i = 0; i < LOCKHASH_SIZE; i++) { - list_for_each_entry(st, &lockhash_table[i], hash_entry) { - insert_to_result(st, compare); - } - } -} + err = perf_session__process_events(session, &eops); + if (err) + goto out_delete; -static int __cmd_report(void) -{ setup_pager(); + if (display_info) /* used for info subcommand */ + err = dump_info(); + else { + sort_result(); + print_result(); + } - if ((select_key() != 0) || - (read_events() != 0)) - return -1; - - sort_result(); - print_result(); - - return 0; +out_delete: + perf_session__delete(session); + return err; } static int __cmd_record(int argc, const char **argv) @@ -970,7 +978,7 @@ int cmd_lock(int argc, const char **argv, const char *prefix __maybe_unused) if (argc) usage_with_options(report_usage, report_options); } - __cmd_report(); + rc = __cmd_report(false); } else if (!strcmp(argv[0], "script")) { /* Aliased to 'perf script' */ return cmd_script(argc, argv, prefix); @@ -983,11 +991,7 @@ int cmd_lock(int argc, const char **argv, const char *prefix __maybe_unused) } /* recycling report_lock_ops */ trace_handler = &report_lock_ops; - setup_pager(); - if (read_events() != 0) - rc = -1; - else - rc = dump_info(); + rc = __cmd_report(true); } else { usage_with_options(lock_usage, lock_options); } -- cgit v0.10.2 From 60a25cbc4a167fc0129296c3c640d8506a57acc5 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Sun, 8 Sep 2013 19:19:18 -0700 Subject: perf lock: Limit bad rate precision Two decimal precision should be enough for this. Signed-off-by: Davidlohr Bueso Cc: Aswin Chandramouleeswaran Cc: Frederic Weisbecker Cc: Hitoshi Mitake Cc: Ingo Molnar Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378693159-8747-7-git-send-email-davidlohr@hp.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 780484f..972310c 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -686,7 +686,7 @@ static void print_bad_events(int bad, int total) pr_info("\n=== output for debug===\n\n"); pr_info("bad: %d, total: %d\n", bad, total); - pr_info("bad rate: %f %%\n", (double)bad / (double)total * 100); + pr_info("bad rate: %.2f %%\n", (double)bad / (double)total * 100); pr_info("histogram of events caused bad sequence\n"); for (i = 0; i < BROKEN_MAX; i++) pr_info(" %10s: %d\n", name[i], bad_hist[i]); -- cgit v0.10.2 From f37376cd721a539ac398cbb7718b72fce83cd49b Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Sun, 8 Sep 2013 19:19:19 -0700 Subject: perf lock: Account for lock average wait time While perf-lock currently reports both the total wait time and the number of contentions, it doesn't explicitly show the average wait time. Having this value immediately in the report can be quite useful when looking into performance issues. Furthermore, allowing report to sort by averages is another handy feature to have - and thus do not only print the value, but add it to the lock_stat structure. Signed-off-by: Davidlohr Bueso Cc: Aswin Chandramouleeswaran Cc: Frederic Weisbecker Cc: Hitoshi Mitake Cc: Ingo Molnar Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378693159-8747-8-git-send-email-davidlohr@hp.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Documentation/perf-lock.txt b/tools/perf/Documentation/perf-lock.txt index c7f5f55..ab25be2 100644 --- a/tools/perf/Documentation/perf-lock.txt +++ b/tools/perf/Documentation/perf-lock.txt @@ -48,7 +48,7 @@ REPORT OPTIONS -k:: --key=:: Sorting key. Possible values: acquired (default), contended, - wait_total, wait_max, wait_min. + avg_wait, wait_total, wait_max, wait_min. INFO OPTIONS ------------ diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 972310c..6a9076f 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -56,7 +56,9 @@ struct lock_stat { unsigned int nr_readlock; unsigned int nr_trylock; + /* these times are in nano sec. */ + u64 avg_wait_time; u64 wait_time_total; u64 wait_time_min; u64 wait_time_max; @@ -208,6 +210,7 @@ static struct thread_stat *thread_stat_findnew_first(u32 tid) SINGLE_KEY(nr_acquired) SINGLE_KEY(nr_contended) +SINGLE_KEY(avg_wait_time) SINGLE_KEY(wait_time_total) SINGLE_KEY(wait_time_max) @@ -244,6 +247,7 @@ static struct rb_root result; /* place to store sorted data */ struct lock_key keys[] = { DEF_KEY_LOCK(acquired, nr_acquired), DEF_KEY_LOCK(contended, nr_contended), + DEF_KEY_LOCK(avg_wait, avg_wait_time), DEF_KEY_LOCK(wait_total, wait_time_total), DEF_KEY_LOCK(wait_min, wait_time_min), DEF_KEY_LOCK(wait_max, wait_time_max), @@ -516,6 +520,7 @@ static int report_lock_acquired_event(struct perf_evsel *evsel, seq->state = SEQ_STATE_ACQUIRED; ls->nr_acquired++; + ls->avg_wait_time = ls->nr_contended ? ls->wait_time_total/ls->nr_contended : 0; seq->prev_event_time = sample->time; end: return 0; @@ -570,6 +575,7 @@ static int report_lock_contended_event(struct perf_evsel *evsel, seq->state = SEQ_STATE_CONTENDED; ls->nr_contended++; + ls->avg_wait_time = ls->wait_time_total/ls->nr_contended; seq->prev_event_time = sample->time; end: return 0; @@ -703,6 +709,7 @@ static void print_result(void) pr_info("%10s ", "acquired"); pr_info("%10s ", "contended"); + pr_info("%15s ", "avg wait (ns)"); pr_info("%15s ", "total wait (ns)"); pr_info("%15s ", "max wait (ns)"); pr_info("%15s ", "min wait (ns)"); @@ -734,6 +741,7 @@ static void print_result(void) pr_info("%10u ", st->nr_acquired); pr_info("%10u ", st->nr_contended); + pr_info("%15" PRIu64 " ", st->avg_wait_time); pr_info("%15" PRIu64 " ", st->wait_time_total); pr_info("%15" PRIu64 " ", st->wait_time_max); pr_info("%15" PRIu64 " ", st->wait_time_min == ULLONG_MAX ? @@ -940,7 +948,7 @@ int cmd_lock(int argc, const char **argv, const char *prefix __maybe_unused) }; const struct option report_options[] = { OPT_STRING('k', "key", &sort_key, "acquired", - "key for sorting (acquired / contended / wait_total / wait_max / wait_min)"), + "key for sorting (acquired / contended / avg_wait / wait_total / wait_max / wait_min)"), /* TODO: type */ OPT_END() }; -- cgit v0.10.2 From 994a1f78b191df0c9d6caca3f3afb03e247aff26 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 1 Sep 2013 12:36:12 +0200 Subject: perf tools: Check mmap pages value early Move the check of the mmap_pages value to the options parsing time, so we could rely on this value on other parts of code. Related changes come in the next patches. Also changes perf_evlist::mmap_len to proper size_t type. Signed-off-by: Jiri Olsa Cc: Andi Kleen Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378031796-17892-2-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 935d522..cfa0b79 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -1426,8 +1426,9 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, const struct option live_options[] = { OPT_STRING('p', "pid", &kvm->opts.target.pid, "pid", "record events on existing process id"), - OPT_UINTEGER('m', "mmap-pages", &kvm->opts.mmap_pages, - "number of mmap data pages"), + OPT_CALLBACK('m', "mmap-pages", &kvm->opts.mmap_pages, "pages", + "number of mmap data pages", + perf_evlist__parse_mmap_pages), OPT_INCR('v', "verbose", &verbose, "be more verbose (show counter open errors, etc)"), OPT_BOOLEAN('a', "all-cpus", &kvm->opts.target.system_wide, diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 0aacd62..92ca541 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -233,10 +233,6 @@ try_again: "or try again with a smaller value of -m/--mmap_pages.\n" "(current value: %d)\n", opts->mmap_pages); rc = -errno; - } else if (!is_power_of_2(opts->mmap_pages) && - (opts->mmap_pages != UINT_MAX)) { - pr_err("--mmap_pages/-m value must be a power of two."); - rc = -EINVAL; } else { pr_err("failed to mmap with %d (%s)\n", errno, strerror(errno)); rc = -errno; @@ -854,8 +850,9 @@ const struct option record_options[] = { OPT_BOOLEAN('i', "no-inherit", &record.opts.no_inherit, "child tasks do not inherit counters"), OPT_UINTEGER('F', "freq", &record.opts.user_freq, "profile at this frequency"), - OPT_UINTEGER('m', "mmap-pages", &record.opts.mmap_pages, - "number of mmap data pages"), + OPT_CALLBACK('m', "mmap-pages", &record.opts.mmap_pages, "pages", + "number of mmap data pages", + perf_evlist__parse_mmap_pages), OPT_BOOLEAN(0, "group", &record.opts.group, "put the counters into a counter group"), OPT_CALLBACK_DEFAULT('g', "call-graph", &record.opts, diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index b3e0229..e846695 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1075,8 +1075,9 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) "file", "vmlinux pathname"), OPT_BOOLEAN('K', "hide_kernel_symbols", &top.hide_kernel_symbols, "hide kernel symbols"), - OPT_UINTEGER('m', "mmap-pages", &opts->mmap_pages, - "number of mmap data pages"), + OPT_CALLBACK('m', "mmap-pages", &opts->mmap_pages, "pages", + "number of mmap data pages", + perf_evlist__parse_mmap_pages), OPT_INTEGER('r', "realtime", &top.realtime_prio, "collect data with this RT SCHED_FIFO priority"), OPT_INTEGER('d', "delay", &top.delay_secs, diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index be65843..f3ddbb0 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1528,8 +1528,9 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) "list of cpus to monitor"), OPT_BOOLEAN(0, "no-inherit", &trace.opts.no_inherit, "child tasks do not inherit counters"), - OPT_UINTEGER('m', "mmap-pages", &trace.opts.mmap_pages, - "number of mmap data pages"), + OPT_CALLBACK('m', "mmap-pages", &trace.opts.mmap_pages, "pages", + "number of mmap data pages", + perf_evlist__parse_mmap_pages), OPT_STRING('u', "uid", &trace.opts.target.uid_str, "user", "user to profile"), OPT_CALLBACK(0, "duration", &trace, "float", diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index f9f77be..4283f49 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -18,6 +18,7 @@ #include #include "parse-events.h" +#include "parse-options.h" #include @@ -672,6 +673,40 @@ out_unmap: return -1; } +static size_t perf_evlist__mmap_size(unsigned long pages) +{ + /* 512 kiB: default amount of unprivileged mlocked memory */ + if (pages == UINT_MAX) + pages = (512 * 1024) / page_size; + else if (!is_power_of_2(pages)) + return 0; + + return (pages + 1) * page_size; +} + +int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, + int unset __maybe_unused) +{ + unsigned int pages, *mmap_pages = opt->value; + size_t size; + char *eptr; + + pages = strtoul(str, &eptr, 10); + if (*eptr != '\0') { + pr_err("failed to parse --mmap_pages/-m value\n"); + return -1; + } + + size = perf_evlist__mmap_size(pages); + if (!size) { + pr_err("--mmap_pages/-m value must be a power of two."); + return -1; + } + + *mmap_pages = pages; + return 0; +} + /** perf_evlist__mmap - Create per cpu maps to receive events * * @evlist - list of events @@ -695,14 +730,6 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, const struct thread_map *threads = evlist->threads; int prot = PROT_READ | (overwrite ? 0 : PROT_WRITE), mask; - /* 512 kiB: default amount of unprivileged mlocked memory */ - if (pages == UINT_MAX) - pages = (512 * 1024) / page_size; - else if (!is_power_of_2(pages)) - return -EINVAL; - - mask = pages * page_size - 1; - if (evlist->mmap == NULL && perf_evlist__alloc_mmap(evlist) < 0) return -ENOMEM; @@ -710,7 +737,8 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, return -ENOMEM; evlist->overwrite = overwrite; - evlist->mmap_len = (pages + 1) * page_size; + evlist->mmap_len = perf_evlist__mmap_size(pages); + mask = evlist->mmap_len - page_size - 1; list_for_each_entry(evsel, &evlist->entries, node) { if ((evsel->attr.read_format & PERF_FORMAT_ID) && diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 880d713..4edb500 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -31,7 +31,7 @@ struct perf_evlist { int nr_groups; int nr_fds; int nr_mmaps; - int mmap_len; + size_t mmap_len; int id_pos; int is_pos; u64 combined_sample_type; @@ -103,6 +103,10 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, bool want_signal); int perf_evlist__start_workload(struct perf_evlist *evlist); +int perf_evlist__parse_mmap_pages(const struct option *opt, + const char *str, + int unset); + int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, bool overwrite); void perf_evlist__munmap(struct perf_evlist *evlist); -- cgit v0.10.2 From 27050f530dc4fd88dc93d85c177e000efe970d12 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 1 Sep 2013 12:36:13 +0200 Subject: perf tools: Add possibility to specify mmap size Adding possibility to specify mmap size via -m/--mmap-pages by appending unit size character (B/K/M/G) to the number, like: $ perf record -m 8K ls $ perf record -m 2M ls The size is rounded up appropriately to follow perf mmap restrictions. If no unit is specified the number provides pages as of now, like: $ perf record -m 8 ls Signed-off-by: Jiri Olsa Cc: Andi Kleen Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378031796-17892-3-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Documentation/perf-kvm.txt b/tools/perf/Documentation/perf-kvm.txt index ac84db2..6a06cef 100644 --- a/tools/perf/Documentation/perf-kvm.txt +++ b/tools/perf/Documentation/perf-kvm.txt @@ -109,7 +109,9 @@ STAT LIVE OPTIONS -m:: --mmap-pages=:: - Number of mmap data pages. Must be a power of two. + Number of mmap data pages (must be a power of two) or size + specification with appended unit character - B/K/M/G. The + size is rounded up to have nearest pages power of two value. -a:: --all-cpus:: diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index f732eaa..f10ab63 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -87,7 +87,9 @@ OPTIONS -m:: --mmap-pages=:: - Number of mmap data pages. Must be a power of two. + Number of mmap data pages (must be a power of two) or size + specification with appended unit character - B/K/M/G. The + size is rounded up to have nearest pages power of two value. -g:: --call-graph:: diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index 6d70fbf..f65777c 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt @@ -68,7 +68,9 @@ Default is to monitor all CPUS. -m :: --mmap-pages=:: - Number of mmapped data pages. + Number of mmap data pages (must be a power of two) or size + specification with appended unit character - B/K/M/G. The + size is rounded up to have nearest pages power of two value. -p :: --pid=:: diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index 3777385..7f70d36 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt @@ -59,7 +59,9 @@ OPTIONS -m:: --mmap-pages=:: - Number of mmap data pages. Must be a power of two. + Number of mmap data pages (must be a power of two) or size + specification with appended unit character - B/K/M/G. The + size is rounded up to have nearest pages power of two value. -C:: --cpu:: diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 4283f49..d21ab08 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -687,14 +687,33 @@ static size_t perf_evlist__mmap_size(unsigned long pages) int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, int unset __maybe_unused) { - unsigned int pages, *mmap_pages = opt->value; + unsigned int pages, val, *mmap_pages = opt->value; size_t size; - char *eptr; + static struct parse_tag tags[] = { + { .tag = 'B', .mult = 1 }, + { .tag = 'K', .mult = 1 << 10 }, + { .tag = 'M', .mult = 1 << 20 }, + { .tag = 'G', .mult = 1 << 30 }, + { .tag = 0 }, + }; - pages = strtoul(str, &eptr, 10); - if (*eptr != '\0') { - pr_err("failed to parse --mmap_pages/-m value\n"); - return -1; + val = parse_tag_value(str, tags); + if (val != (unsigned int) -1) { + /* we got file size value */ + pages = PERF_ALIGN(val, page_size) / page_size; + if (!is_power_of_2(pages)) { + pages = next_pow2(pages); + pr_info("rounding mmap pages size to %u (%u pages)\n", + pages * page_size, pages); + } + } else { + /* we got pages count value */ + char *eptr; + pages = strtoul(str, &eptr, 10); + if (*eptr != '\0') { + pr_err("failed to parse --mmap_pages/-m value\n"); + return -1; + } } size = perf_evlist__mmap_size(pages); @@ -738,6 +757,7 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, evlist->overwrite = overwrite; evlist->mmap_len = perf_evlist__mmap_size(pages); + pr_debug("mmap size %luB\n", evlist->mmap_len); mask = evlist->mmap_len - page_size - 1; list_for_each_entry(evsel, &evlist->entries, node) { diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index ccfdeb6..ab71d62 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -361,3 +361,28 @@ int parse_nsec_time(const char *str, u64 *ptime) *ptime = time_sec * NSEC_PER_SEC + time_nsec; return 0; } + +unsigned long parse_tag_value(const char *str, struct parse_tag *tags) +{ + struct parse_tag *i = tags; + + while (i->tag) { + char *s; + + s = strchr(str, i->tag); + if (s) { + unsigned long int value; + char *endptr; + + value = strtoul(str, &endptr, 10); + if (s != endptr) + break; + + value *= i->mult; + return value; + } + i++; + } + + return (unsigned long) -1; +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index a535359..c29ecaa 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -270,6 +270,13 @@ bool is_power_of_2(unsigned long n) return (n != 0 && ((n & (n - 1)) == 0)); } +static inline unsigned next_pow2(unsigned x) +{ + if (!x) + return 1; + return 1ULL << (32 - __builtin_clz(x - 1)); +} + size_t hex_width(u64 v); int hex2u64(const char *ptr, u64 *val); @@ -281,4 +288,11 @@ void dump_stack(void); extern unsigned int page_size; void get_term_dimensions(struct winsize *ws); + +struct parse_tag { + char tag; + int mult; +}; + +unsigned long parse_tag_value(const char *str, struct parse_tag *tags); #endif /* GIT_COMPAT_UTIL_H */ -- cgit v0.10.2 From b22d54b09a5448d3706929c6f0eae36429f4ec5d Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 1 Sep 2013 12:36:14 +0200 Subject: perf evlist: Introduce perf_evlist__new_default function Adding new common function to create evlist with default event. It spares some code lines in automated tests. Signed-off-by: Jiri Olsa Cc: Andi Kleen Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378031796-17892-4-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c index b8a7056..82ac715 100644 --- a/tools/perf/tests/perf-record.c +++ b/tools/perf/tests/perf-record.c @@ -45,7 +45,7 @@ int test__PERF_RECORD(void) }; cpu_set_t cpu_mask; size_t cpu_mask_size = sizeof(cpu_mask); - struct perf_evlist *evlist = perf_evlist__new(); + struct perf_evlist *evlist = perf_evlist__new_default(); struct perf_evsel *evsel; struct perf_sample sample; const char *cmd = "sleep"; @@ -66,16 +66,6 @@ int test__PERF_RECORD(void) } /* - * We need at least one evsel in the evlist, use the default - * one: "cycles". - */ - err = perf_evlist__add_default(evlist); - if (err < 0) { - pr_debug("Not enough memory to create evsel\n"); - goto out_delete_evlist; - } - - /* * Create maps of threads and cpus to monitor. In this case * we start with all threads and cpus (-1, -1) but then in * perf_evlist__prepare_workload we'll fill in the only thread diff --git a/tools/perf/tests/task-exit.c b/tools/perf/tests/task-exit.c index 28fe589..b07f8a1 100644 --- a/tools/perf/tests/task-exit.c +++ b/tools/perf/tests/task-exit.c @@ -37,20 +37,11 @@ int test__task_exit(void) signal(SIGCHLD, sig_handler); signal(SIGUSR1, sig_handler); - evlist = perf_evlist__new(); + evlist = perf_evlist__new_default(); if (evlist == NULL) { - pr_debug("perf_evlist__new\n"); + pr_debug("perf_evlist__new_default\n"); return -1; } - /* - * We need at least one evsel in the evlist, use the default - * one: "cycles". - */ - err = perf_evlist__add_default(evlist); - if (err < 0) { - pr_debug("Not enough memory to create evsel\n"); - goto out_free_evlist; - } /* * Create maps of threads and cpus to monitor. In this case @@ -117,7 +108,6 @@ out_close_evlist: perf_evlist__close(evlist); out_delete_maps: perf_evlist__delete_maps(evlist); -out_free_evlist: perf_evlist__delete(evlist); return err; } diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index d21ab08..f0d71a9 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -50,6 +50,18 @@ struct perf_evlist *perf_evlist__new(void) return evlist; } +struct perf_evlist *perf_evlist__new_default(void) +{ + struct perf_evlist *evlist = perf_evlist__new(); + + if (evlist && perf_evlist__add_default(evlist)) { + perf_evlist__delete(evlist); + evlist = NULL; + } + + return evlist; +} + /** * perf_evlist__set_id_pos - set the positions of event ids. * @evlist: selected event list diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 4edb500..871b55a 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -53,6 +53,7 @@ struct perf_evsel_str_handler { }; struct perf_evlist *perf_evlist__new(void); +struct perf_evlist *perf_evlist__new_default(void); void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, struct thread_map *threads); void perf_evlist__exit(struct perf_evlist *evlist); -- cgit v0.10.2 From dd96c46b5c765a779d8c35cc7d1df7515b4c7baf Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 1 Sep 2013 12:36:15 +0200 Subject: perf tools: Adding throttle event data struct support Moving 'struct throttle_event' out of python code and making it global as any other event. There's no usage of throttling events in any perf commands so far (besides python support), but we'll need this event data backup for upcoming test. Signed-off-by: Jiri Olsa Cc: Andi Kleen Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378031796-17892-5-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 17d9e16..9b7d4d3 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -61,6 +61,12 @@ struct read_event { u64 id; }; +struct throttle_event { + struct perf_event_header header; + u64 time; + u64 id; + u64 stream_id; +}; #define PERF_SAMPLE_MASK \ (PERF_SAMPLE_IP | PERF_SAMPLE_TID | \ @@ -178,6 +184,7 @@ union perf_event { struct fork_event fork; struct lost_event lost; struct read_event read; + struct throttle_event throttle; struct sample_event sample; struct attr_event attr; struct event_type_event event_type; diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index a24ce0a..06efd02 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -33,13 +33,6 @@ int eprintf(int level, const char *fmt, ...) # define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size, #endif -struct throttle_event { - struct perf_event_header header; - u64 time; - u64 id; - u64 stream_id; -}; - PyMODINIT_FUNC initperf(void); #define member_def(type, member, ptype, help) \ diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index b97f468..d1e4495 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -397,6 +397,17 @@ static void perf_event__read_swap(union perf_event *event, bool sample_id_all) swap_sample_id_all(event, &event->read + 1); } +static void perf_event__throttle_swap(union perf_event *event, + bool sample_id_all) +{ + event->throttle.time = bswap_64(event->throttle.time); + event->throttle.id = bswap_64(event->throttle.id); + event->throttle.stream_id = bswap_64(event->throttle.stream_id); + + if (sample_id_all) + swap_sample_id_all(event, &event->throttle + 1); +} + static u8 revbyte(u8 b) { int rev = (b >> 4) | ((b & 0xf) << 4); @@ -482,6 +493,8 @@ static perf_event__swap_op perf_event__swap_ops[] = { [PERF_RECORD_EXIT] = perf_event__task_swap, [PERF_RECORD_LOST] = perf_event__all64_swap, [PERF_RECORD_READ] = perf_event__read_swap, + [PERF_RECORD_THROTTLE] = perf_event__throttle_swap, + [PERF_RECORD_UNTHROTTLE] = perf_event__throttle_swap, [PERF_RECORD_SAMPLE] = perf_event__all64_swap, [PERF_RECORD_HEADER_ATTR] = perf_event__hdr_attr_swap, [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap, -- cgit v0.10.2 From fc2be6968e99b5314f20e938a547d44dcb1c40eb Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Sat, 14 Sep 2013 10:32:59 +0200 Subject: perf symbols: Add new option --ignore-vmlinux for perf top Running "perf top" on a machine with possibly invalid or non-matching vmlinux at the various places results in no symbol resolving despite /proc/kallsyms being present and valid. Add a new option --ignore-vmlinux to explicitly indicate that we do not want to use these kernels and just use what we have (kallsyms). Signed-off-by: Willy Tarreau Cc: Ingo Molnar Link: http://lkml.kernel.org/r/20130914083259.GA3418@1wt.eu Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index e846695..65c49b2 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1073,6 +1073,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) "list of cpus to monitor"), OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, "file", "vmlinux pathname"), + OPT_BOOLEAN(0, "ignore-vmlinux", &symbol_conf.ignore_vmlinux, + "don't load vmlinux even if found"), OPT_BOOLEAN('K', "hide_kernel_symbols", &top.hide_kernel_symbols, "hide kernel symbols"), OPT_CALLBACK('m', "mmap-pages", &opts->mmap_pages, "pages", diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index cd1dcc4..48c3879 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1215,7 +1215,7 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map, goto do_kallsyms; } - if (symbol_conf.vmlinux_name != NULL) { + if (!symbol_conf.ignore_vmlinux && symbol_conf.vmlinux_name != NULL) { err = dso__load_vmlinux(dso, map, symbol_conf.vmlinux_name, filter); if (err > 0) { @@ -1227,7 +1227,7 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map, return err; } - if (vmlinux_path != NULL) { + if (!symbol_conf.ignore_vmlinux && vmlinux_path != NULL) { err = dso__load_vmlinux_path(dso, map, filter); if (err > 0) return err; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 2a97bb1..9b8b213 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -85,6 +85,7 @@ struct symbol_conf { unsigned short priv_size; unsigned short nr_events; bool try_vmlinux_path, + ignore_vmlinux, show_kernel_path, use_modules, sort_by_name, -- cgit v0.10.2 From 312717f15ac7095c3ea98d487276781535ca7c0b Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Sun, 25 Aug 2013 18:29:51 -0500 Subject: c6x: use boot_command_line instead of private c6x_command_line Save some pointless copying of the kernel command line and just use boot_command_line instead. Also remove default_command_line as it is not referenced anywhere, and the DT code already handles the default command line. Signed-off-by: Rob Herring Tested-by: Mark Salter Acked-by: Mark Salter Cc: Aurelien Jacquiot Cc: linux-c6x-dev@linux-c6x.org Reviewed-by: Grant Likely diff --git a/arch/c6x/include/asm/setup.h b/arch/c6x/include/asm/setup.h index ecead15..6968044 100644 --- a/arch/c6x/include/asm/setup.h +++ b/arch/c6x/include/asm/setup.h @@ -14,8 +14,6 @@ #include #ifndef __ASSEMBLY__ -extern char c6x_command_line[COMMAND_LINE_SIZE]; - extern int c6x_add_memory(phys_addr_t start, unsigned long size); extern unsigned long ram_start; diff --git a/arch/c6x/kernel/devicetree.c b/arch/c6x/kernel/devicetree.c index 9e15ab9..5e8c838 100644 --- a/arch/c6x/kernel/devicetree.c +++ b/arch/c6x/kernel/devicetree.c @@ -24,7 +24,7 @@ void __init early_init_devtree(void *params) * device-tree, including the platform type, initrd location and * size and more ... */ - of_scan_flat_dt(early_init_dt_scan_chosen, c6x_command_line); + of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line); /* Scan memory nodes and rebuild MEMBLOCKs */ of_scan_flat_dt(early_init_dt_scan_root, NULL); diff --git a/arch/c6x/kernel/setup.c b/arch/c6x/kernel/setup.c index f4e72bd..0e5a812 100644 --- a/arch/c6x/kernel/setup.c +++ b/arch/c6x/kernel/setup.c @@ -68,13 +68,6 @@ unsigned long ram_end; static unsigned long dma_start __initdata; static unsigned long dma_size __initdata; -char c6x_command_line[COMMAND_LINE_SIZE]; - -#if defined(CONFIG_CMDLINE_BOOL) -static const char default_command_line[COMMAND_LINE_SIZE] __section(.cmdline) = - CONFIG_CMDLINE; -#endif - struct cpuinfo_c6x { const char *cpu_name; const char *cpu_voltage; @@ -296,8 +289,6 @@ notrace void __init machine_init(unsigned long dt_ptr) /* Do some early initialization based on the flat device tree */ early_init_devtree(fdt); - /* parse_early_param needs a boot_command_line */ - strlcpy(boot_command_line, c6x_command_line, COMMAND_LINE_SIZE); parse_early_param(); } @@ -309,7 +300,7 @@ void __init setup_arch(char **cmdline_p) printk(KERN_INFO "Initializing kernel\n"); /* Initialize command line */ - *cmdline_p = c6x_command_line; + *cmdline_p = boot_command_line; memory_end = ram_end; memory_end &= ~(PAGE_SIZE - 1); diff --git a/arch/c6x/kernel/vmlinux.lds.S b/arch/c6x/kernel/vmlinux.lds.S index 279d807..5a6e141 100644 --- a/arch/c6x/kernel/vmlinux.lds.S +++ b/arch/c6x/kernel/vmlinux.lds.S @@ -37,12 +37,6 @@ SECTIONS _vectors_end = .; } - . = ALIGN(0x1000); - .cmdline : - { - *(.cmdline) - } - /* * This section contains data which may be shared with other * cores. It needs to be a fixed offset from PAGE_OFFSET -- cgit v0.10.2 From bbf28b505ac2dd923b9051ca5031cf87bf7c40cc Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 26 Aug 2013 11:28:50 -0500 Subject: openrisc: use boot_command_line instead of private cmd_line Save some pointless copying of the kernel command line and just use boot_command_line instead. The DT code already handles CONFIG_CMDLINE, so a separate copy is not needed. Signed-off-by: Rob Herring Cc: Jonas Bonn Cc: linux@lists.openrisc.net Reviewed-by: Grant Likely diff --git a/arch/openrisc/kernel/prom.c b/arch/openrisc/kernel/prom.c index a63e768..bf3fd05 100644 --- a/arch/openrisc/kernel/prom.c +++ b/arch/openrisc/kernel/prom.c @@ -47,8 +47,6 @@ #include #include -extern char cmd_line[COMMAND_LINE_SIZE]; - void __init early_init_dt_add_memory_arch(u64 base, u64 size) { size &= PAGE_MASK; @@ -67,15 +65,12 @@ void __init early_init_devtree(void *params) * device-tree, including the platform type, initrd location and * size, TCE reserve, and more ... */ - of_scan_flat_dt(early_init_dt_scan_chosen, cmd_line); + of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line); /* Scan memory nodes and rebuild MEMBLOCKs */ of_scan_flat_dt(early_init_dt_scan_root, NULL); of_scan_flat_dt(early_init_dt_scan_memory, NULL); - /* Save command line for /proc/cmdline and then parse parameters */ - strlcpy(boot_command_line, cmd_line, COMMAND_LINE_SIZE); - memblock_allow_resize(); /* We must copy the flattend device tree from init memory to regular diff --git a/arch/openrisc/kernel/setup.c b/arch/openrisc/kernel/setup.c index d7359ff..719c5c8 100644 --- a/arch/openrisc/kernel/setup.c +++ b/arch/openrisc/kernel/setup.c @@ -50,8 +50,6 @@ #include "vmlinux.h" -char __initdata cmd_line[COMMAND_LINE_SIZE] = CONFIG_CMDLINE; - static unsigned long __init setup_memory(void) { unsigned long bootmap_size; @@ -316,7 +314,7 @@ void __init setup_arch(char **cmdline_p) conswitchp = &dummy_con; #endif - *cmdline_p = cmd_line; + *cmdline_p = boot_command_line; printk(KERN_INFO "OpenRISC Linux -- http://openrisc.net\n"); } -- cgit v0.10.2 From a8bf7527a2e17ccf1366e67f6ac728327ca34c40 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 26 Aug 2013 11:22:45 -0500 Subject: of: create unflatten_and_copy_device_tree Several architectures using DT support built-in dtb's in the init section. These platforms need to copy the dtb from init since the strings are referenced after unflattening. Every arch has their own copying routine which do the same thing. Create a common function, unflatten_and_copy_device_tree, to copy the dtb when unflattening the dtb. Signed-off-by: Rob Herring Acked-by: Grant Likely diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 229dd9d..5177616 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -802,6 +802,30 @@ void __init unflatten_device_tree(void) of_alias_scan(early_init_dt_alloc_memory_arch); } +/** + * unflatten_and_copy_device_tree - copy and create tree of device_nodes from flat blob + * + * Copies and unflattens the device-tree passed by the firmware, creating the + * tree of struct device_node. It also fills the "name" and "type" + * pointers of the nodes so the normal device-tree walking functions + * can be used. This should only be used when the FDT memory has not been + * reserved such is the case when the FDT is built-in to the kernel init + * section. If the FDT memory is reserved already then unflatten_device_tree + * should be used instead. + */ +void __init unflatten_and_copy_device_tree(void) +{ + int size = __be32_to_cpu(initial_boot_params->totalsize); + void *dt = early_init_dt_alloc_memory_arch(size, + __alignof__(struct boot_param_header)); + + if (dt) { + memcpy(dt, initial_boot_params, size); + initial_boot_params = dt; + } + unflatten_device_tree(); +} + #endif /* CONFIG_OF_EARLY_FLATTREE */ /* Feed entire flattened device tree into the random pool */ diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index a478c62..58c28a8 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -118,9 +118,11 @@ extern int early_init_dt_scan_root(unsigned long node, const char *uname, /* Other Prototypes */ extern void unflatten_device_tree(void); +extern void unflatten_and_copy_device_tree(void); extern void early_init_devtree(void *); #else /* CONFIG_OF_FLATTREE */ static inline void unflatten_device_tree(void) {} +static inline void unflatten_and_copy_device_tree(void) {} #endif /* CONFIG_OF_FLATTREE */ #endif /* __ASSEMBLY__ */ -- cgit v0.10.2 From 1efc959e0b28942d69d3084ecd4e9bde3beb9866 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 26 Aug 2013 11:23:27 -0500 Subject: arc: use unflatten_and_copy_device_tree Use the common unflatten_and_copy_device_tree to copy the built-in FDT out of init section. Signed-off-by: Rob Herring Acked-by: Vineet Gupta Acked-by: Grant Likely diff --git a/arch/arc/include/asm/mach_desc.h b/arch/arc/include/asm/mach_desc.h index 9998dc8..d3e9c0a 100644 --- a/arch/arc/include/asm/mach_desc.h +++ b/arch/arc/include/asm/mach_desc.h @@ -82,6 +82,5 @@ __attribute__((__section__(".arch.info.init"))) = { \ }; extern struct machine_desc *setup_machine_fdt(void *dt); -extern void __init copy_devtree(void); #endif diff --git a/arch/arc/kernel/devtree.c b/arch/arc/kernel/devtree.c index 2340af0..eeb613a 100644 --- a/arch/arc/kernel/devtree.c +++ b/arch/arc/kernel/devtree.c @@ -100,18 +100,3 @@ struct machine_desc * __init setup_machine_fdt(void *dt) return mdesc_best; } - -/* - * Copy the flattened DT out of .init since unflattening doesn't copy strings - * and the normal DT APIs refs them from orig flat DT - */ -void __init copy_devtree(void) -{ - void *alloc = early_init_dt_alloc_memory_arch( - be32_to_cpu(initial_boot_params->totalsize), 64); - if (alloc) { - memcpy(alloc, initial_boot_params, - be32_to_cpu(initial_boot_params->totalsize)); - initial_boot_params = alloc; - } -} diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c index 2c68bc7e..710bf89 100644 --- a/arch/arc/kernel/setup.c +++ b/arch/arc/kernel/setup.c @@ -346,8 +346,7 @@ void __init setup_arch(char **cmdline_p) setup_arch_memory(); /* copy flat DT out of .init and then unflatten it */ - copy_devtree(); - unflatten_device_tree(); + unflatten_and_copy_device_tree(); /* Can be issue if someone passes cmd line arg "ro" * But that is unlikely so keeping it as it is -- cgit v0.10.2 From 3486892f998fea5b4f1a0a4d093f09b7d0024696 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 26 Aug 2013 11:23:50 -0500 Subject: openrisc: use unflatten_and_copy_device_tree Use the common unflatten_and_copy_device_tree to copy the built-in FDT out of init section. This moves the copy later in the boot, but there do not appear to be any references to strings in the FDT before the copy. Signed-off-by: Rob Herring Cc: Jonas Bonn Cc: linux@lists.openrisc.net diff --git a/arch/openrisc/kernel/prom.c b/arch/openrisc/kernel/prom.c index bf3fd05..3b94972 100644 --- a/arch/openrisc/kernel/prom.c +++ b/arch/openrisc/kernel/prom.c @@ -55,8 +55,6 @@ void __init early_init_dt_add_memory_arch(u64 base, u64 size) void __init early_init_devtree(void *params) { - void *alloc; - /* Setup flat device-tree pointer */ initial_boot_params = params; @@ -72,17 +70,6 @@ void __init early_init_devtree(void *params) of_scan_flat_dt(early_init_dt_scan_memory, NULL); memblock_allow_resize(); - - /* We must copy the flattend device tree from init memory to regular - * memory because the device tree references the strings in it - * directly. - */ - - alloc = __va(memblock_alloc(initial_boot_params->totalsize, PAGE_SIZE)); - - memcpy(alloc, initial_boot_params, initial_boot_params->totalsize); - - initial_boot_params = alloc; } #ifdef CONFIG_BLK_DEV_INITRD diff --git a/arch/openrisc/kernel/setup.c b/arch/openrisc/kernel/setup.c index 719c5c8..09a769b 100644 --- a/arch/openrisc/kernel/setup.c +++ b/arch/openrisc/kernel/setup.c @@ -283,7 +283,7 @@ void __init setup_arch(char **cmdline_p) { unsigned long max_low_pfn; - unflatten_device_tree(); + unflatten_and_copy_device_tree(); setup_cpuinfo(); -- cgit v0.10.2 From 21c561bda17a67afe923866cf88653637abf6e7a Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 28 Aug 2013 09:57:57 -0500 Subject: x86: use unflatten_and_copy_device_tree Use the common unflatten_and_copy_device_tree to copy the built-in FDT out of init section. Signed-off-by: Rob Herring Cc: Thomas Gleixner Cc: Ingo Molnar Cc: "H. Peter Anvin" Cc: x86@kernel.org diff --git a/arch/x86/kernel/devicetree.c b/arch/x86/kernel/devicetree.c index 376dc78..0db805c 100644 --- a/arch/x86/kernel/devicetree.c +++ b/arch/x86/kernel/devicetree.c @@ -230,7 +230,7 @@ static void __init dtb_apic_setup(void) static void __init x86_flattree_get_config(void) { u32 size, map_len; - void *new_dtb; + struct boot_param_header *dt; if (!initial_dtb) return; @@ -238,24 +238,17 @@ static void __init x86_flattree_get_config(void) map_len = max(PAGE_SIZE - (initial_dtb & ~PAGE_MASK), (u64)sizeof(struct boot_param_header)); - initial_boot_params = early_memremap(initial_dtb, map_len); - size = be32_to_cpu(initial_boot_params->totalsize); + dt = early_memremap(initial_dtb, map_len); + size = be32_to_cpu(dt->totalsize); if (map_len < size) { - early_iounmap(initial_boot_params, map_len); - initial_boot_params = early_memremap(initial_dtb, size); + early_iounmap(dt, map_len); + dt = early_memremap(initial_dtb, size); map_len = size; } - new_dtb = alloc_bootmem(size); - memcpy(new_dtb, initial_boot_params, size); - early_iounmap(initial_boot_params, map_len); - - initial_boot_params = new_dtb; - - /* root level address cells */ - of_scan_flat_dt(early_init_dt_scan_root, NULL); - - unflatten_device_tree(); + initial_boot_params = dt; + unflatten_and_copy_device_tree(); + early_iounmap(dt, map_len); } #else static inline void x86_flattree_get_config(void) { } -- cgit v0.10.2 From 3104021c12747df63843fedf3ceb6a54410e2c18 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 26 Aug 2013 11:24:11 -0500 Subject: xtensa: use unflatten_and_copy_device_tree Use the common unflatten_and_copy_device_tree to copy the built-in FDT out of init section. Signed-off-by: Rob Herring Cc: Chris Zankel Acked-by: Max Filippov Cc: linux-xtensa@linux-xtensa.org Acked-by: Grant Likely diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index 946fb8d..fc31ec1 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -21,11 +21,8 @@ #include #include #include - -#ifdef CONFIG_OF #include #include -#endif #if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE) # include @@ -252,17 +249,6 @@ void __init early_init_devtree(void *params) of_scan_flat_dt(early_init_dt_scan_memory, NULL); } -static void __init copy_devtree(void) -{ - void *alloc = early_init_dt_alloc_memory_arch( - be32_to_cpu(initial_boot_params->totalsize), 8); - if (alloc) { - memcpy(alloc, initial_boot_params, - be32_to_cpu(initial_boot_params->totalsize)); - initial_boot_params = alloc; - } -} - static int __init xtensa_device_probe(void) { of_platform_populate(NULL, NULL, NULL, NULL); @@ -525,10 +511,7 @@ void __init setup_arch(char **cmdline_p) bootmem_init(); -#ifdef CONFIG_OF - copy_devtree(); - unflatten_device_tree(); -#endif + unflatten_and_copy_device_tree(); platform_setup(cmdline_p); -- cgit v0.10.2 From 08854f41809293b283b96c642562cd4119d6b5db Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 27 Aug 2013 21:39:46 -0500 Subject: metag: use unflatten_and_copy_device_tree Use the common unflatten_and_copy_device_tree to copy the built-in FDT out of init section. Signed-off-by: Rob Herring Cc: James Hogan diff --git a/arch/metag/kernel/devtree.c b/arch/metag/kernel/devtree.c index 7cd0252..049af56 100644 --- a/arch/metag/kernel/devtree.c +++ b/arch/metag/kernel/devtree.c @@ -94,21 +94,5 @@ struct machine_desc * __init setup_machine_fdt(void *dt) of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line); return mdesc_best; -} -/** - * copy_fdt - Copy device tree into non-init memory. - * - * We must copy the flattened device tree blob into non-init memory because the - * unflattened device tree will reference the strings in it directly. - */ -void __init copy_fdt(void) -{ - void *alloc = early_init_dt_alloc_memory_arch( - be32_to_cpu(initial_boot_params->totalsize), 0x40); - if (alloc) { - memcpy(alloc, initial_boot_params, - be32_to_cpu(initial_boot_params->totalsize)); - initial_boot_params = alloc; - } } diff --git a/arch/metag/kernel/setup.c b/arch/metag/kernel/setup.c index c396cd0..2c697d3 100644 --- a/arch/metag/kernel/setup.c +++ b/arch/metag/kernel/setup.c @@ -408,9 +408,7 @@ void __init setup_arch(char **cmdline_p) cpu_2_hwthread_id[smp_processor_id()] = hard_processor_id(); hwthread_id_2_cpu[hard_processor_id()] = smp_processor_id(); - /* Copy device tree blob into non-init memory before unflattening */ - copy_fdt(); - unflatten_device_tree(); + unflatten_and_copy_device_tree(); #ifdef CONFIG_SMP smp_init_cpus(); -- cgit v0.10.2 From 0288ffcbfdf9b8656e7320c24caa1e4c1d498287 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 26 Aug 2013 09:47:40 -0500 Subject: of: Introduce common early_init_dt_scan Most architectures scan the all the same items early in the FDT and none are really architecture specific. Create a common early_init_dt_scan to unify the early scan of root, memory, and chosen nodes in the flattened DT. Signed-off-by: Rob Herring Acked-by: Grant Likely diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 5177616..bfbfda5 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -785,6 +785,32 @@ void * __init __weak early_init_dt_alloc_memory_arch(u64 size, u64 align) } #endif +bool __init early_init_dt_scan(void *params) +{ + if (!params) + return false; + + /* Setup flat device-tree pointer */ + initial_boot_params = params; + + /* check device tree validity */ + if (be32_to_cpu(initial_boot_params->magic) != OF_DT_HEADER) { + initial_boot_params = NULL; + return false; + } + + /* Retrieve various information from the /chosen node */ + of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line); + + /* Initialize {size,address}-cells info */ + of_scan_flat_dt(early_init_dt_scan_root, NULL); + + /* Setup memory, calling early_init_dt_add_memory_arch */ + of_scan_flat_dt(early_init_dt_scan_memory, NULL); + + return true; +} + /** * unflatten_device_tree - create tree of device_nodes from flat blob * diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index 58c28a8..73e1651 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -116,6 +116,8 @@ extern void early_init_dt_setup_initrd_arch(u64 start, u64 end); extern int early_init_dt_scan_root(unsigned long node, const char *uname, int depth, void *data); +extern bool early_init_dt_scan(void *params); + /* Other Prototypes */ extern void unflatten_device_tree(void); extern void unflatten_and_copy_device_tree(void); -- cgit v0.10.2 From ad81fcb53033be98ab29c6cfb6ef0249a8e2ffaa Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 26 Aug 2013 10:12:22 -0500 Subject: arc: use early_init_dt_scan Convert arc to use new early_init_dt_scan function. Signed-off-by: Rob Herring Acked-by: Vineet Gupta diff --git a/arch/arc/kernel/devtree.c b/arch/arc/kernel/devtree.c index eeb613a..1ab6f35 100644 --- a/arch/arc/kernel/devtree.c +++ b/arch/arc/kernel/devtree.c @@ -27,7 +27,6 @@ */ struct machine_desc * __init setup_machine_fdt(void *dt) { - struct boot_param_header *devtree = dt; struct machine_desc *mdesc = NULL, *mdesc_best = NULL; unsigned int score, mdesc_score = ~1; unsigned long dt_root; @@ -36,11 +35,9 @@ struct machine_desc * __init setup_machine_fdt(void *dt) char manufacturer[16]; unsigned long len; - /* check device tree validity */ - if (be32_to_cpu(devtree->magic) != OF_DT_HEADER) + if (!early_init_dt_scan(dt)) return NULL; - initial_boot_params = devtree; dt_root = of_get_flat_dt_root(); /* @@ -85,15 +82,6 @@ struct machine_desc * __init setup_machine_fdt(void *dt) pr_info("Board \"%s\" from %s (Manufacturer)\n", model, manufacturer); - /* Retrieve various information from the /chosen node */ - of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line); - - /* Initialize {size,address}-cells info */ - of_scan_flat_dt(early_init_dt_scan_root, NULL); - - /* Setup memory, calling early_init_dt_add_memory_arch */ - of_scan_flat_dt(early_init_dt_scan_memory, NULL); - clk = of_get_flat_dt_prop(dt_root, "clock-frequency", &len); if (clk) arc_set_core_freq(of_read_ulong(clk, len/4)); -- cgit v0.10.2 From 56dc1f474add364b07254ad9690f4e244ca65ef4 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 26 Aug 2013 10:13:01 -0500 Subject: arm: use early_init_dt_scan Convert arm to use new early_init_dt_scan function. Signed-off-by: Rob Herring Cc: Russell King Cc: linux-arm-kernel@lists.infradead.org diff --git a/arch/arm/kernel/devtree.c b/arch/arm/kernel/devtree.c index f35906b..e7ce175 100644 --- a/arch/arm/kernel/devtree.c +++ b/arch/arm/kernel/devtree.c @@ -183,7 +183,6 @@ bool arch_match_cpu_phys_id(int cpu, u64 phys_id) */ const struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys) { - struct boot_param_header *devtree; const struct machine_desc *mdesc, *mdesc_best = NULL; unsigned int score, mdesc_score = ~1; unsigned long dt_root; @@ -196,17 +195,11 @@ const struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys) mdesc_best = &__mach_desc_GENERIC_DT; #endif - if (!dt_phys) + if (!dt_phys || !early_init_dt_scan(phys_to_virt(dt_phys))) return NULL; - devtree = phys_to_virt(dt_phys); - - /* check device tree validity */ - if (be32_to_cpu(devtree->magic) != OF_DT_HEADER) - return NULL; /* Search the mdescs for the 'best' compatible value match */ - initial_boot_params = devtree; dt_root = of_get_flat_dt_root(); for_each_machine_desc(mdesc) { score = of_flat_dt_match(dt_root, mdesc->dt_compat); @@ -240,13 +233,6 @@ const struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys) model = ""; pr_info("Machine: %s, model: %s\n", mdesc_best->name, model); - /* Retrieve various information from the /chosen node */ - of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line); - /* Initialize {size,address}-cells info */ - of_scan_flat_dt(early_init_dt_scan_root, NULL); - /* Setup memory, calling early_init_dt_add_memory_arch */ - of_scan_flat_dt(early_init_dt_scan_memory, NULL); - /* Change machine number to match the mdesc we're using */ __machine_arch_type = mdesc_best->nr; -- cgit v0.10.2 From d5189cc57b1d231b6dc745de6b68881901d0c5c4 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 26 Aug 2013 10:14:32 -0500 Subject: arm64: use early_init_dt_scan Convert arm64 to use new early_init_dt_scan function. Signed-off-by: Rob Herring Acked-by: Catalin Marinas Cc: Will Deacon Cc: linux-arm-kernel@lists.infradead.org diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index 055cfb8..4a5f624 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -124,37 +124,19 @@ static void __init setup_processor(void) static void __init setup_machine_fdt(phys_addr_t dt_phys) { - struct boot_param_header *devtree; unsigned long dt_root; - /* Check we have a non-NULL DT pointer */ - if (!dt_phys) { - early_print("\n" - "Error: NULL or invalid device tree blob\n" - "The dtb must be 8-byte aligned and passed in the first 512MB of memory\n" - "\nPlease check your bootloader.\n"); - - while (true) - cpu_relax(); - - } - - devtree = phys_to_virt(dt_phys); - - /* Check device tree validity */ - if (be32_to_cpu(devtree->magic) != OF_DT_HEADER) { + if (!dt_phys || !early_init_dt_scan(phys_to_virt(dt_phys))) { early_print("\n" "Error: invalid device tree blob at physical address 0x%p (virtual address 0x%p)\n" - "Expected 0x%x, found 0x%x\n" + "The dtb must be 8-byte aligned and passed in the first 512MB of memory\n" "\nPlease check your bootloader.\n", - dt_phys, devtree, OF_DT_HEADER, - be32_to_cpu(devtree->magic)); + dt_phys, phys_to_virt(dt_phys)); while (true) cpu_relax(); } - initial_boot_params = devtree; dt_root = of_get_flat_dt_root(); machine_name = of_get_flat_dt_prop(dt_root, "model", NULL); @@ -163,13 +145,6 @@ static void __init setup_machine_fdt(phys_addr_t dt_phys) if (!machine_name) machine_name = ""; pr_info("Machine: %s\n", machine_name); - - /* Retrieve various information from the /chosen node */ - of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line); - /* Initialize {size,address}-cells info */ - of_scan_flat_dt(early_init_dt_scan_root, NULL); - /* Setup memory, calling early_init_dt_add_memory_arch */ - of_scan_flat_dt(early_init_dt_scan_memory, NULL); } void __init early_init_dt_add_memory_arch(u64 base, u64 size) -- cgit v0.10.2 From a8e44636c66d90d177fcdf8e8cfc36747074a4ed Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 26 Aug 2013 10:18:20 -0500 Subject: c6x: use early_init_dt_scan Convert c6x to use new early_init_dt_scan function. Signed-off-by: Rob Herring Acked-by: Mark Salter Cc: Aurelien Jacquiot Cc: linux-c6x-dev@linux-c6x.org diff --git a/arch/c6x/kernel/devicetree.c b/arch/c6x/kernel/devicetree.c index 5e8c838..d28a92f 100644 --- a/arch/c6x/kernel/devicetree.c +++ b/arch/c6x/kernel/devicetree.c @@ -10,28 +10,9 @@ * */ #include -#include -#include #include #include -void __init early_init_devtree(void *params) -{ - /* Setup flat device-tree pointer */ - initial_boot_params = params; - - /* Retrieve various informations from the /chosen node of the - * device-tree, including the platform type, initrd location and - * size and more ... - */ - of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line); - - /* Scan memory nodes and rebuild MEMBLOCKs */ - of_scan_flat_dt(early_init_dt_scan_root, NULL); - of_scan_flat_dt(early_init_dt_scan_memory, NULL); -} - - #ifdef CONFIG_BLK_DEV_INITRD void __init early_init_dt_setup_initrd_arch(u64 start, u64 end) { diff --git a/arch/c6x/kernel/setup.c b/arch/c6x/kernel/setup.c index 0e5a812..731db4b 100644 --- a/arch/c6x/kernel/setup.c +++ b/arch/c6x/kernel/setup.c @@ -287,7 +287,7 @@ notrace void __init machine_init(unsigned long dt_ptr) fdt = dtb; /* Do some early initialization based on the flat device tree */ - early_init_devtree(fdt); + early_init_dt_scan(fdt); parse_early_param(); } -- cgit v0.10.2 From e5d659935e8aa4ee8d72b1cce9ad6d6f0213909e Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 25 Sep 2013 15:46:14 -0500 Subject: metag: use early_init_dt_scan Convert metag to use new early_init_dt_scan function. Signed-off-by: Rob Herring Cc: James Hogan diff --git a/arch/metag/kernel/devtree.c b/arch/metag/kernel/devtree.c index 049af56..68c2fee 100644 --- a/arch/metag/kernel/devtree.c +++ b/arch/metag/kernel/devtree.c @@ -43,18 +43,16 @@ void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) */ struct machine_desc * __init setup_machine_fdt(void *dt) { - struct boot_param_header *devtree = dt; struct machine_desc *mdesc, *mdesc_best = NULL; unsigned int score, mdesc_score = ~1; unsigned long dt_root; const char *model; /* check device tree validity */ - if (be32_to_cpu(devtree->magic) != OF_DT_HEADER) + if (!early_init_dt_scan(dt)) return NULL; /* Search the mdescs for the 'best' compatible value match */ - initial_boot_params = devtree; dt_root = of_get_flat_dt_root(); for_each_machine_desc(mdesc) { @@ -90,9 +88,6 @@ struct machine_desc * __init setup_machine_fdt(void *dt) model = ""; pr_info("Machine: %s, model: %s\n", mdesc_best->name, model); - /* Retrieve various information from the /chosen node */ - of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line); - return mdesc_best; } -- cgit v0.10.2 From dc0b433526773c70a030478212f57c3f402681ae Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 26 Aug 2013 10:19:07 -0500 Subject: microblaze: use early_init_dt_scan Convert microblaze to use new early_init_dt_scan function. Signed-off-by: Rob Herring Tested-by: Michal Simek Cc: microblaze-uclinux@itee.uq.edu.au diff --git a/arch/microblaze/kernel/prom.c b/arch/microblaze/kernel/prom.c index 0c4453f..e13686e 100644 --- a/arch/microblaze/kernel/prom.c +++ b/arch/microblaze/kernel/prom.c @@ -106,21 +106,10 @@ void __init early_init_devtree(void *params) { pr_debug(" -> early_init_devtree(%p)\n", params); - /* Setup flat device-tree pointer */ - initial_boot_params = params; + early_init_dt_scan(params); + if (!strlen(boot_command_line)) + strlcpy(boot_command_line, cmd_line, COMMAND_LINE_SIZE); - /* Retrieve various informations from the /chosen node of the - * device-tree, including the platform type, initrd location and - * size, TCE reserve, and more ... - */ - of_scan_flat_dt(early_init_dt_scan_chosen, cmd_line); - - /* Scan memory nodes and rebuild MEMBLOCKs */ - of_scan_flat_dt(early_init_dt_scan_root, NULL); - of_scan_flat_dt(early_init_dt_scan_memory, NULL); - - /* Save command line for /proc/cmdline and then parse parameters */ - strlcpy(boot_command_line, cmd_line, COMMAND_LINE_SIZE); parse_early_param(); memblock_allow_resize(); diff --git a/arch/microblaze/kernel/setup.c b/arch/microblaze/kernel/setup.c index 0775e03..6c4efba 100644 --- a/arch/microblaze/kernel/setup.c +++ b/arch/microblaze/kernel/setup.c @@ -50,7 +50,7 @@ char cmd_line[COMMAND_LINE_SIZE] __attribute__ ((section(".data"))); void __init setup_arch(char **cmdline_p) { - *cmdline_p = cmd_line; + *cmdline_p = boot_command_line; console_verbose(); -- cgit v0.10.2 From f75813c0127bbef41ac3152f64a72ba212a5514c Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 26 Aug 2013 10:20:26 -0500 Subject: mips: use early_init_dt_scan Convert mips to use new early_init_dt_scan function. Remove early_init_dt_scan_memory_arch Signed-off-by: Rob Herring Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Acked-by: John Crispin diff --git a/arch/mips/include/asm/prom.h b/arch/mips/include/asm/prom.h index 1e7e096..e3dbd0e 100644 --- a/arch/mips/include/asm/prom.h +++ b/arch/mips/include/asm/prom.h @@ -17,9 +17,6 @@ #include #include -extern int early_init_dt_scan_memory_arch(unsigned long node, - const char *uname, int depth, void *data); - extern void device_tree_init(void); static inline unsigned long pci_address_to_pio(phys_addr_t address) diff --git a/arch/mips/kernel/prom.c b/arch/mips/kernel/prom.c index 0fa0b69..67a4c53 100644 --- a/arch/mips/kernel/prom.c +++ b/arch/mips/kernel/prom.c @@ -17,8 +17,6 @@ #include #include #include -#include -#include #include #include @@ -40,13 +38,6 @@ char *mips_get_machine_name(void) } #ifdef CONFIG_OF -int __init early_init_dt_scan_memory_arch(unsigned long node, - const char *uname, int depth, - void *data) -{ - return early_init_dt_scan_memory(node, uname, depth, data); -} - void __init early_init_dt_add_memory_arch(u64 base, u64 size) { return add_memory_region(base, size, BOOT_MEM_RAM); @@ -78,36 +69,12 @@ int __init early_init_dt_scan_model(unsigned long node, const char *uname, return 0; } -void __init early_init_devtree(void *params) -{ - /* Setup flat device-tree pointer */ - initial_boot_params = params; - - /* Retrieve various informations from the /chosen node of the - * device-tree, including the platform type, initrd location and - * size, and more ... - */ - of_scan_flat_dt(early_init_dt_scan_chosen, arcs_cmdline); - - - /* Scan memory nodes */ - of_scan_flat_dt(early_init_dt_scan_root, NULL); - of_scan_flat_dt(early_init_dt_scan_memory_arch, NULL); - - /* try to load the mips machine name */ - of_scan_flat_dt(early_init_dt_scan_model, NULL); -} - void __init __dt_setup_arch(struct boot_param_header *bph) { - if (be32_to_cpu(bph->magic) != OF_DT_HEADER) { - pr_err("DTB has bad magic, ignoring builtin OF DTB\n"); - + if (!early_init_dt_scan(bph)) return; - } - - initial_boot_params = bph; - early_init_devtree(initial_boot_params); + /* try to load the mips machine name */ + of_scan_flat_dt(early_init_dt_scan_model, NULL); } #endif -- cgit v0.10.2 From e94353890aaae1ab81dcacb3cafb449432224d3f Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 26 Aug 2013 10:25:26 -0500 Subject: openrisc: use early_init_dt_scan Convert openrisc to use new early_init_dt_scan function. Signed-off-by: Rob Herring Cc: Jonas Bonn Cc: linux@lists.openrisc.net diff --git a/arch/openrisc/kernel/prom.c b/arch/openrisc/kernel/prom.c index 3b94972..fbed459 100644 --- a/arch/openrisc/kernel/prom.c +++ b/arch/openrisc/kernel/prom.c @@ -55,20 +55,7 @@ void __init early_init_dt_add_memory_arch(u64 base, u64 size) void __init early_init_devtree(void *params) { - /* Setup flat device-tree pointer */ - initial_boot_params = params; - - - /* Retrieve various informations from the /chosen node of the - * device-tree, including the platform type, initrd location and - * size, TCE reserve, and more ... - */ - of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line); - - /* Scan memory nodes and rebuild MEMBLOCKs */ - of_scan_flat_dt(early_init_dt_scan_root, NULL); - of_scan_flat_dt(early_init_dt_scan_memory, NULL); - + early_init_dt_scan(params); memblock_allow_resize(); } -- cgit v0.10.2 From 7745fc1fec6ec9556e4cc36c21b0e02589078902 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 28 Aug 2013 10:05:10 -0500 Subject: xtensa: use early_init_dt_scan Convert xtensa to use new early_init_dt_scan function. Signed-off-by: Rob Herring Acked-by: Max Filippov Cc: Chris Zankel Cc: linux-xtensa@linux-xtensa.org diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index fc31ec1..65974a8 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -219,9 +219,13 @@ static int __init parse_bootparam(const bp_tag_t* tag) } #ifdef CONFIG_OF +bool __initdata dt_memory_scan = false; void __init early_init_dt_add_memory_arch(u64 base, u64 size) { + if (!dt_memory_scan) + return; + size &= PAGE_MASK; add_sysmem_bank(MEMORY_TYPE_CONVENTIONAL, base, base + size); } @@ -233,20 +237,13 @@ void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) void __init early_init_devtree(void *params) { - /* Setup flat device-tree pointer */ - initial_boot_params = params; + if (sysmem.nr_banks == 0) + dt_memory_scan = true; - /* Retrieve various informations from the /chosen node of the - * device-tree, including the platform type, initrd location and - * size, TCE reserve, and more ... - */ - if (!command_line[0]) - of_scan_flat_dt(early_init_dt_scan_chosen, command_line); + early_init_dt_scan(params); - /* Scan memory nodes and rebuild MEMBLOCKs */ - of_scan_flat_dt(early_init_dt_scan_root, NULL); - if (sysmem.nr_banks == 0) - of_scan_flat_dt(early_init_dt_scan_memory, NULL); + if (!command_line[0]) + strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE); } static int __init xtensa_device_probe(void) -- cgit v0.10.2 From 068f6310b965d67d57f89ebf4c539e5933754366 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 24 Sep 2013 22:20:01 -0500 Subject: of: create default early_init_dt_add_memory_arch Create a weak version of early_init_dt_add_memory_arch which uses memblock. This will unify all architectures except ones with custom memory bank structs. Signed-off-by: Rob Herring Acked-by: Catalin Marinas Cc: Will Deacon Cc: Michal Simek Cc: Jonas Bonn Acked-by: Grant Likely Cc: linux-arm-kernel@lists.infradead.org Cc: microblaze-uclinux@itee.uq.edu.au Cc: linux@lists.openrisc.net Cc: devicetree@vger.kernel.org diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index 4a5f624..7feb0c9 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -147,24 +147,6 @@ static void __init setup_machine_fdt(phys_addr_t dt_phys) pr_info("Machine: %s\n", machine_name); } -void __init early_init_dt_add_memory_arch(u64 base, u64 size) -{ - base &= PAGE_MASK; - size &= PAGE_MASK; - if (base + size < PHYS_OFFSET) { - pr_warning("Ignoring memory block 0x%llx - 0x%llx\n", - base, base + size); - return; - } - if (base < PHYS_OFFSET) { - pr_warning("Ignoring memory range 0x%llx - 0x%llx\n", - base, PHYS_OFFSET); - size -= PHYS_OFFSET - base; - base = PHYS_OFFSET; - } - memblock_add(base, size); -} - /* * Limit the memory size that was specified via FDT. */ diff --git a/arch/microblaze/kernel/prom.c b/arch/microblaze/kernel/prom.c index e13686e..951e4d6 100644 --- a/arch/microblaze/kernel/prom.c +++ b/arch/microblaze/kernel/prom.c @@ -41,11 +41,6 @@ #include #include -void __init early_init_dt_add_memory_arch(u64 base, u64 size) -{ - memblock_add(base, size); -} - #ifdef CONFIG_EARLY_PRINTK static char *stdout; diff --git a/arch/openrisc/kernel/prom.c b/arch/openrisc/kernel/prom.c index fbed459..6dbcaa8 100644 --- a/arch/openrisc/kernel/prom.c +++ b/arch/openrisc/kernel/prom.c @@ -47,12 +47,6 @@ #include #include -void __init early_init_dt_add_memory_arch(u64 base, u64 size) -{ - size &= PAGE_MASK; - memblock_add(base, size); -} - void __init early_init_devtree(void *params) { early_init_dt_scan(params); diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index bfbfda5..5bc55b6 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -775,6 +775,25 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, } #ifdef CONFIG_HAVE_MEMBLOCK +void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size) +{ + const u64 phys_offset = __pa(PAGE_OFFSET); + base &= PAGE_MASK; + size &= PAGE_MASK; + if (base + size < phys_offset) { + pr_warning("Ignoring memory block 0x%llx - 0x%llx\n", + base, base + size); + return; + } + if (base < phys_offset) { + pr_warning("Ignoring memory range 0x%llx - 0x%llx\n", + base, phys_offset); + size -= phys_offset - base; + base = phys_offset; + } + memblock_add(base, size); +} + /* * called from unflatten_device_tree() to bootstrap devicetree itself * Architectures can override this definition if memblock isn't used -- cgit v0.10.2 From 65939301acdb1e593fdb424bd356f0b9bc7ba5be Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 30 Aug 2013 10:54:26 -0500 Subject: arm: set initrd_start/initrd_end for fdt scan In order to unify the initrd scanning for DT across architectures, make arm set initrd_start and initrd_end instead of the physical addresses. This is aligned with all other architectures. Signed-off-by: Rob Herring Cc: Russell King Cc: linux-arm-kernel@lists.infradead.org Acked-by: Grant Likely diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index febaee7..9eeb1cd 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -77,11 +77,11 @@ static int __init parse_tag_initrd2(const struct tag *tag) __tagtable(ATAG_INITRD2, parse_tag_initrd2); -#ifdef CONFIG_OF_FLATTREE +#if defined(CONFIG_OF_FLATTREE) && defined(CONFIG_BLK_DEV_INITRD) void __init early_init_dt_setup_initrd_arch(u64 start, u64 end) { - phys_initrd_start = start; - phys_initrd_size = end - start; + initrd_start = (unsigned long)__va(start); + initrd_end = (unsigned long)__va(end); } #endif /* CONFIG_OF_FLATTREE */ @@ -351,6 +351,11 @@ void __init arm_memblock_init(struct meminfo *mi, memblock_reserve(__pa(_stext), _end - _stext); #endif #ifdef CONFIG_BLK_DEV_INITRD + /* FDT scan will populate initrd_start */ + if (initrd_start) { + phys_initrd_start = __virt_to_phys(initrd_start); + phys_initrd_size = initrd_end - initrd_start; + } if (phys_initrd_size && !memblock_is_region_memory(phys_initrd_start, phys_initrd_size)) { pr_err("INITRD: 0x%08llx+0x%08lx is not a memory region - disabling initrd\n", -- cgit v0.10.2 From ec2eaa73b3d21776f46797a2eef983d7be09a964 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Sun, 25 Aug 2013 16:47:48 -0500 Subject: arm64: set initrd_start/initrd_end for fdt scan In order to unify the initrd scanning for DT across architectures, make arm64 use initrd_start and initrd_end instead of the physical addresses. Signed-off-by: Rob Herring Acked-by: Catalin Marinas Cc: Will Deacon Cc: linux-arm-kernel@lists.infradead.org diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c index de2de5d..8261f4e 100644 --- a/arch/arm64/mm/init.c +++ b/arch/arm64/mm/init.c @@ -39,17 +39,9 @@ #include "mm.h" -static unsigned long phys_initrd_start __initdata = 0; -static unsigned long phys_initrd_size __initdata = 0; - phys_addr_t memstart_addr __read_mostly = 0; -void __init early_init_dt_setup_initrd_arch(u64 start, u64 end) -{ - phys_initrd_start = start; - phys_initrd_size = end - start; -} - +#ifdef CONFIG_BLK_DEV_INITRD static int __init early_initrd(char *p) { unsigned long start, size; @@ -59,12 +51,13 @@ static int __init early_initrd(char *p) if (*endp == ',') { size = memparse(endp + 1, NULL); - phys_initrd_start = start; - phys_initrd_size = size; + initrd_start = (unsigned long)__va(start); + initrd_end = (unsigned long)__va(start + size); } return 0; } early_param("initrd", early_initrd); +#endif #define MAX_DMA32_PFN ((4UL * 1024 * 1024 * 1024) >> PAGE_SHIFT) @@ -137,13 +130,8 @@ void __init arm64_memblock_init(void) /* Register the kernel text, kernel data and initrd with memblock */ memblock_reserve(__pa(_text), _end - _text); #ifdef CONFIG_BLK_DEV_INITRD - if (phys_initrd_size) { - memblock_reserve(phys_initrd_start, phys_initrd_size); - - /* Now convert initrd to virtual addresses */ - initrd_start = __phys_to_virt(phys_initrd_start); - initrd_end = initrd_start + phys_initrd_size; - } + if (initrd_start) + memblock_reserve(__virt_to_phys(initrd_start), initrd_end - initrd_start); #endif /* -- cgit v0.10.2 From 29eb45a9ab4839a1e9cef2bcf369b918c9c4fcad Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 30 Aug 2013 17:06:53 -0500 Subject: of: remove early_init_dt_setup_initrd_arch All arches do essentially the same thing now for early_init_dt_setup_initrd_arch, so it can now be removed. Signed-off-by: Rob Herring Acked-by: Vineet Gupta Cc: Russell King Cc: Mark Salter Cc: Aurelien Jacquiot Cc: James Hogan Cc: Michal Simek Cc: Ralf Baechle Cc: Jonas Bonn Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Thomas Gleixner Cc: Ingo Molnar Cc: "H. Peter Anvin" Cc: x86@kernel.org Cc: Chris Zankel Cc: Max Filippov Acked-by: Grant Likely diff --git a/arch/arc/mm/init.c b/arch/arc/mm/init.c index 81279ec..55e0a85 100644 --- a/arch/arc/mm/init.c +++ b/arch/arc/mm/init.c @@ -125,10 +125,3 @@ void __init free_initrd_mem(unsigned long start, unsigned long end) free_reserved_area((void *)start, (void *)end, -1, "initrd"); } #endif - -#ifdef CONFIG_OF_FLATTREE -void __init early_init_dt_setup_initrd_arch(u64 start, u64 end) -{ - pr_err("%s(%llx, %llx)\n", __func__, start, end); -} -#endif /* CONFIG_OF_FLATTREE */ diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 9eeb1cd..9d0b91d 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -77,14 +77,6 @@ static int __init parse_tag_initrd2(const struct tag *tag) __tagtable(ATAG_INITRD2, parse_tag_initrd2); -#if defined(CONFIG_OF_FLATTREE) && defined(CONFIG_BLK_DEV_INITRD) -void __init early_init_dt_setup_initrd_arch(u64 start, u64 end) -{ - initrd_start = (unsigned long)__va(start); - initrd_end = (unsigned long)__va(end); -} -#endif /* CONFIG_OF_FLATTREE */ - /* * This keeps memory configuration data used by a couple memory * initialization functions, as well as show_mem() for the skipping diff --git a/arch/c6x/kernel/devicetree.c b/arch/c6x/kernel/devicetree.c index d28a92f..fa3e574 100644 --- a/arch/c6x/kernel/devicetree.c +++ b/arch/c6x/kernel/devicetree.c @@ -10,18 +10,8 @@ * */ #include -#include #include -#ifdef CONFIG_BLK_DEV_INITRD -void __init early_init_dt_setup_initrd_arch(u64 start, u64 end) -{ - initrd_start = (unsigned long)__va(start); - initrd_end = (unsigned long)__va(end); - initrd_below_start_ok = 1; -} -#endif - void __init early_init_dt_add_memory_arch(u64 base, u64 size) { c6x_add_memory(base, size); diff --git a/arch/metag/mm/init.c b/arch/metag/mm/init.c index 1239195..249fff6 100644 --- a/arch/metag/mm/init.c +++ b/arch/metag/mm/init.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include @@ -405,11 +404,3 @@ void free_initrd_mem(unsigned long start, unsigned long end) "initrd"); } #endif - -#ifdef CONFIG_OF_FLATTREE -void __init early_init_dt_setup_initrd_arch(u64 start, u64 end) -{ - pr_err("%s(%llx, %llx)\n", - __func__, start, end); -} -#endif /* CONFIG_OF_FLATTREE */ diff --git a/arch/microblaze/kernel/prom.c b/arch/microblaze/kernel/prom.c index 951e4d6..cab6dc3 100644 --- a/arch/microblaze/kernel/prom.c +++ b/arch/microblaze/kernel/prom.c @@ -114,15 +114,6 @@ void __init early_init_devtree(void *params) pr_debug(" <- early_init_devtree()\n"); } -#ifdef CONFIG_BLK_DEV_INITRD -void __init early_init_dt_setup_initrd_arch(u64 start, u64 end) -{ - initrd_start = (unsigned long)__va(start); - initrd_end = (unsigned long)__va(end); - initrd_below_start_ok = 1; -} -#endif - /******* * * New implementation of the OF "find" APIs, return a refcounted diff --git a/arch/mips/kernel/prom.c b/arch/mips/kernel/prom.c index 67a4c53..0b2485f 100644 --- a/arch/mips/kernel/prom.c +++ b/arch/mips/kernel/prom.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -48,15 +47,6 @@ void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) return __alloc_bootmem(size, align, __pa(MAX_DMA_ADDRESS)); } -#ifdef CONFIG_BLK_DEV_INITRD -void __init early_init_dt_setup_initrd_arch(u64 start, u64 end) -{ - initrd_start = (unsigned long)__va(start); - initrd_end = (unsigned long)__va(end); - initrd_below_start_ok = 1; -} -#endif - int __init early_init_dt_scan_model(unsigned long node, const char *uname, int depth, void *data) { diff --git a/arch/openrisc/kernel/prom.c b/arch/openrisc/kernel/prom.c index 6dbcaa8..2aae474 100644 --- a/arch/openrisc/kernel/prom.c +++ b/arch/openrisc/kernel/prom.c @@ -52,12 +52,3 @@ void __init early_init_devtree(void *params) early_init_dt_scan(params); memblock_allow_resize(); } - -#ifdef CONFIG_BLK_DEV_INITRD -void __init early_init_dt_setup_initrd_arch(u64 start, u64 end) -{ - initrd_start = (unsigned long)__va(start); - initrd_end = (unsigned long)__va(end); - initrd_below_start_ok = 1; -} -#endif diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index b7634ce..a089468 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -546,15 +546,6 @@ void __init early_init_dt_add_memory_arch(u64 base, u64 size) memblock_add(base, size); } -#ifdef CONFIG_BLK_DEV_INITRD -void __init early_init_dt_setup_initrd_arch(u64 start, u64 end) -{ - initrd_start = (unsigned long)__va(start); - initrd_end = (unsigned long)__va(end); - initrd_below_start_ok = 1; -} -#endif - static void __init early_reserve_mem_dt(void) { unsigned long i, len, dt_root; diff --git a/arch/x86/kernel/devicetree.c b/arch/x86/kernel/devicetree.c index 0db805c..0e1f95b 100644 --- a/arch/x86/kernel/devicetree.c +++ b/arch/x86/kernel/devicetree.c @@ -51,15 +51,6 @@ void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) return __alloc_bootmem(size, align, __pa(MAX_DMA_ADDRESS)); } -#ifdef CONFIG_BLK_DEV_INITRD -void __init early_init_dt_setup_initrd_arch(u64 start, u64 end) -{ - initrd_start = (unsigned long)__va(start); - initrd_end = (unsigned long)__va(end); - initrd_below_start_ok = 1; -} -#endif - void __init add_dtb(u64 data) { initial_dtb = data + offsetof(struct setup_data, data); diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index 65974a8..6e2b663 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -61,8 +61,8 @@ extern struct rtc_ops no_rtc_ops; struct rtc_ops *rtc_ops; #ifdef CONFIG_BLK_DEV_INITRD -extern void *initrd_start; -extern void *initrd_end; +extern unsigned long initrd_start; +extern unsigned long initrd_end; int initrd_is_mapped = 0; extern int initrd_below_start_ok; #endif @@ -149,8 +149,8 @@ static int __init parse_tag_initrd(const bp_tag_t* tag) { meminfo_t* mi; mi = (meminfo_t*)(tag->data); - initrd_start = __va(mi->start); - initrd_end = __va(mi->end); + initrd_start = (unsigned long)__va(mi->start); + initrd_end = (unsigned long)__va(mi->end); return 0; } @@ -167,13 +167,6 @@ static int __init parse_tag_fdt(const bp_tag_t *tag) __tagtable(BP_TAG_FDT, parse_tag_fdt); -void __init early_init_dt_setup_initrd_arch(u64 start, u64 end) -{ - initrd_start = (void *)__va(start); - initrd_end = (void *)__va(end); - initrd_below_start_ok = 1; -} - #endif /* CONFIG_OF */ #endif /* CONFIG_BLK_DEV_INITRD */ diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 5bc55b6..5f4cc88 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -624,7 +624,7 @@ int __init of_scan_flat_dt_by_path(const char *path, * early_init_dt_check_for_initrd - Decode initrd location from flat tree * @node: reference to node containing initrd location ('chosen') */ -void __init early_init_dt_check_for_initrd(unsigned long node) +static void __init early_init_dt_check_for_initrd(unsigned long node) { u64 start, end; unsigned long len; @@ -642,12 +642,15 @@ void __init early_init_dt_check_for_initrd(unsigned long node) return; end = of_read_number(prop, len/4); - early_init_dt_setup_initrd_arch(start, end); + initrd_start = (unsigned long)__va(start); + initrd_end = (unsigned long)__va(end); + initrd_below_start_ok = 1; + pr_debug("initrd_start=0x%llx initrd_end=0x%llx\n", (unsigned long long)start, (unsigned long long)end); } #else -inline void early_init_dt_check_for_initrd(unsigned long node) +static inline void early_init_dt_check_for_initrd(unsigned long node) { } #endif /* CONFIG_BLK_DEV_INITRD */ diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index 73e1651..b365f5a 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -96,22 +96,12 @@ extern int of_scan_flat_dt_by_path(const char *path, extern int early_init_dt_scan_chosen(unsigned long node, const char *uname, int depth, void *data); -extern void early_init_dt_check_for_initrd(unsigned long node); extern int early_init_dt_scan_memory(unsigned long node, const char *uname, int depth, void *data); extern void early_init_dt_add_memory_arch(u64 base, u64 size); extern void * early_init_dt_alloc_memory_arch(u64 size, u64 align); extern u64 dt_mem_next_cell(int s, __be32 **cellp); -/* - * If BLK_DEV_INITRD, the fdt early init code will call this function, - * to be provided by the arch code. start and end are specified as - * physical addresses. - */ -#ifdef CONFIG_BLK_DEV_INITRD -extern void early_init_dt_setup_initrd_arch(u64 start, u64 end); -#endif - /* Early flat tree scan hooks */ extern int early_init_dt_scan_root(unsigned long node, const char *uname, int depth, void *data); -- cgit v0.10.2 From 9b7d9f27f6cf418a79b81d99688c2f92bf93fc49 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 26 Aug 2013 13:38:37 -0500 Subject: openrisc: remove unnecessary prom.c includes It appears openrisc prom.c was just copied from another arch with a bunch of unnecessary includes. Remove all the unecessary ones. Signed-off-by: Rob Herring Cc: Jonas Bonn Cc: linux@lists.openrisc.net diff --git a/arch/openrisc/kernel/prom.c b/arch/openrisc/kernel/prom.c index 2aae474..6a44340 100644 --- a/arch/openrisc/kernel/prom.c +++ b/arch/openrisc/kernel/prom.c @@ -18,34 +18,12 @@ * */ -#include -#include -#include #include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include -#include #include -#include -#include -#include -#include -#include -#include -#include void __init early_init_devtree(void *params) { -- cgit v0.10.2 From 4174a7a4f763ed51809a4dbca06de054d31c1d38 Mon Sep 17 00:00:00 2001 From: Anthony Olech Date: Wed, 9 Oct 2013 17:44:38 +0100 Subject: regmap: Fix regmap_bulk_write single-rw mutex deadlock When regmap_bulk_write() is called with the map->use_single_rw flag set an immediate mutex deadlock happens because regmap_raw_write() is called after obtaining the mutex and regmap_raw_write() itself then tries to obtain the mutex as well. It is obvious that no one other than myself tried it with a real device. I did, but only for the purposes of an experiment and demonstration. But even if this situation will never ever happen with a real device, it is a bug and therefore should be fixed. Signed-off-by: Anthony Olech Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7d689a1..c1245ca 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1418,10 +1418,11 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, */ if (map->use_single_rw) { for (i = 0; i < val_count; i++) { - ret = regmap_raw_write(map, - reg + (i * map->reg_stride), - val + (i * val_bytes), - val_bytes); + ret = _regmap_raw_write(map, + reg + (i * map->reg_stride), + val + (i * val_bytes), + val_bytes, + false); if (ret != 0) return ret; } -- cgit v0.10.2 From 4c850ead98ec52a00af5bedb3ef75963d914e844 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 8 Oct 2013 19:31:02 +0530 Subject: regulator: tps65090: get regulators node from parent node only As per the devicetree binding document of TPS65090, the "regulators" subnode should be under the parent node, not outside of parent node. Hence to get the regulator node, the correct call is of_get_child_by_name() rather than of_find_node_by_name() which searches the "regulators" node from the parent node to end of DTS file. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c index c8e7045..f302d31 100644 --- a/drivers/regulator/tps65090-regulator.c +++ b/drivers/regulator/tps65090-regulator.c @@ -180,7 +180,7 @@ static struct tps65090_platform_data *tps65090_parse_dt_reg_data( return ERR_PTR(-ENOMEM); } - regulators = of_find_node_by_name(np, "regulators"); + regulators = of_get_child_by_name(np, "regulators"); if (!regulators) { dev_err(&pdev->dev, "regulator node not found\n"); return ERR_PTR(-ENODEV); -- cgit v0.10.2 From 712c967fec612f572d9eea5d031278512f642cf8 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 8 Oct 2013 19:31:03 +0530 Subject: regulator: tps6586x: get regulators node from parent node only As per the devicetree binding document of TPS6586x, the "regulators" subnode should be under the parent node, not outside of parent node. Hence to get the regulator node, the correct call is of_get_child_by_name() rather than of_find_node_by_name() which searches the "regulators" node from the parent node to end of DTS file. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c index 2c9155b..c8e9742 100644 --- a/drivers/regulator/tps6586x-regulator.c +++ b/drivers/regulator/tps6586x-regulator.c @@ -298,7 +298,7 @@ static struct tps6586x_platform_data *tps6586x_parse_regulator_dt( struct tps6586x_platform_data *pdata; int err; - regs = of_find_node_by_name(np, "regulators"); + regs = of_get_child_by_name(np, "regulators"); if (!regs) { dev_err(&pdev->dev, "regulator node not found\n"); return NULL; -- cgit v0.10.2 From 4ae1ff7fe8ad70dfe984ea7586ced00294359b47 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 8 Oct 2013 19:31:04 +0530 Subject: regulator: tps65910: get regulators node from parent node only As per the devicetree binding document of TPS65910, the "regulators" subnode should be under the parent node, not outside of parent node. Hence to get the regulator node, the correct call is of_get_child_by_name() rather than of_find_node_by_name() which searches the "regulators" node from the parent node to end of DTS file. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index 45c1644..95ebc67 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -982,7 +982,7 @@ static struct tps65910_board *tps65910_parse_dt_reg_data( } np = of_node_get(pdev->dev.parent->of_node); - regulators = of_find_node_by_name(np, "regulators"); + regulators = of_get_child_by_name(np, "regulators"); if (!regulators) { dev_err(&pdev->dev, "regulator node not found\n"); return NULL; -- cgit v0.10.2 From 5db542ed85b5e88346675f672dbd2eefa52005f4 Mon Sep 17 00:00:00 2001 From: Illia Smyrnov Date: Wed, 9 Oct 2013 15:05:08 +0300 Subject: spi: omap2-mcspi: Fix FIFO support for transmit-and-receive mode This patch fixes MCSPI FIFO buffer support when transmit-and-receive (full duplex) mode is used. In this mode FIFO can be used for RX or for TX or for both directions. If FIFO used for both directions the buffer is split into two 32-byte buffers - one for each direction. Also for full duplex mode both AEL and AFL need to be set in CHCONF0 register. Signed-off-by: Illia Smyrnov Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index ed4af47..32dca0c 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -276,7 +276,7 @@ static void omap2_mcspi_set_fifo(const struct spi_device *spi, struct omap2_mcspi_cs *cs = spi->controller_state; struct omap2_mcspi *mcspi; unsigned int wcnt; - int fifo_depth, bytes_per_word; + int max_fifo_depth, fifo_depth, bytes_per_word; u32 chconf, xferlevel; mcspi = spi_master_get_devdata(master); @@ -287,7 +287,12 @@ static void omap2_mcspi_set_fifo(const struct spi_device *spi, if (t->len % bytes_per_word != 0) goto disable_fifo; - fifo_depth = gcd(t->len, OMAP2_MCSPI_MAX_FIFODEPTH); + if (t->rx_buf != NULL && t->tx_buf != NULL) + max_fifo_depth = OMAP2_MCSPI_MAX_FIFODEPTH / 2; + else + max_fifo_depth = OMAP2_MCSPI_MAX_FIFODEPTH; + + fifo_depth = gcd(t->len, max_fifo_depth); if (fifo_depth < 2 || fifo_depth % bytes_per_word != 0) goto disable_fifo; @@ -299,7 +304,8 @@ static void omap2_mcspi_set_fifo(const struct spi_device *spi, if (t->rx_buf != NULL) { chconf |= OMAP2_MCSPI_CHCONF_FFER; xferlevel |= (fifo_depth - 1) << 8; - } else { + } + if (t->tx_buf != NULL) { chconf |= OMAP2_MCSPI_CHCONF_FFET; xferlevel |= fifo_depth - 1; } -- cgit v0.10.2 From fc67297b16da335d610af2fac96233d51146300a Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 13 Sep 2013 15:27:43 +0900 Subject: perf tools: Separate out GTK codes to libperf-gtk.so Separate out GTK codes to a shared object called libperf-gtk.so. This time only GTK codes are built with -fPIC and libperf remains as is. Now run GTK hist and annotation browser using libdl. Signed-off-by: Namhyung Kim Reviewed-by: Pekka Enberg Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Pekka Enberg Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1379053663-13706-1-git-send-email-namhyung@kernel.org [ Fix it up wrt Ingo's tools/perf build speedups ] Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index a24f6c2..40c39c3 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -114,6 +114,7 @@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__ BUILTIN_OBJS = LIB_H = LIB_OBJS = +GTK_OBJS = PYRF_OBJS = SCRIPT_SH = @@ -490,13 +491,19 @@ ifndef NO_SLANG endif ifndef NO_GTK2 - LIB_OBJS += $(OUTPUT)ui/gtk/browser.o - LIB_OBJS += $(OUTPUT)ui/gtk/hists.o - LIB_OBJS += $(OUTPUT)ui/gtk/setup.o - LIB_OBJS += $(OUTPUT)ui/gtk/util.o - LIB_OBJS += $(OUTPUT)ui/gtk/helpline.o - LIB_OBJS += $(OUTPUT)ui/gtk/progress.o - LIB_OBJS += $(OUTPUT)ui/gtk/annotate.o + ALL_PROGRAMS += $(OUTPUT)libperf-gtk.so + + GTK_OBJS += $(OUTPUT)ui/gtk/browser.o + GTK_OBJS += $(OUTPUT)ui/gtk/hists.o + GTK_OBJS += $(OUTPUT)ui/gtk/setup.o + GTK_OBJS += $(OUTPUT)ui/gtk/util.o + GTK_OBJS += $(OUTPUT)ui/gtk/helpline.o + GTK_OBJS += $(OUTPUT)ui/gtk/progress.o + GTK_OBJS += $(OUTPUT)ui/gtk/annotate.o + +install-gtk: $(OUTPUT)libperf-gtk.so + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(libdir_SQ)' + $(INSTALL) $(OUTPUT)libperf-gtk.so '$(DESTDIR_SQ)$(libdir_SQ)' endif ifndef NO_LIBPERL @@ -550,6 +557,12 @@ $(OUTPUT)perf: $(OUTPUT)perf.o $(BUILTIN_OBJS) $(PERFLIBS) $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $(OUTPUT)perf.o \ $(BUILTIN_OBJS) $(LIBS) -o $@ +$(GTK_OBJS): $(OUTPUT)%.o: %.c $(LIB_H) + $(QUIET_CC)$(CC) -o $@ -c -fPIC $(CFLAGS) $(GTK_CFLAGS) $< + +$(OUTPUT)libperf-gtk.so: $(GTK_OBJS) $(PERFLIBS) + $(QUIET_LINK)$(CC) -o $@ -shared $(ALL_LDFLAGS) $(filter %.o,$^) $(GTK_LIBS) + $(OUTPUT)builtin-help.o: builtin-help.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) \ '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ @@ -632,6 +645,9 @@ $(OUTPUT)tests/python-use.o: tests/python-use.c $(OUTPUT)PERF-CFLAGS $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< +$(OUTPUT)ui/setup.o: ui/setup.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DLIBDIR='"$(libdir_SQ)"' $< + $(OUTPUT)ui/browser.o: ui/browser.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DENABLE_SLFUTURE_CONST $< @@ -673,7 +689,8 @@ $(patsubst perf-%,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) # we compile into subdirectories. if the target directory is not the source directory, they might not exists. So # we depend the various files onto their directories. -DIRECTORY_DEPS = $(LIB_OBJS) $(BUILTIN_OBJS) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h +DIRECTORY_DEPS = $(LIB_OBJS) $(BUILTIN_OBJS) $(GTK_OBJS) +DIRECTORY_DEPS += $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h $(DIRECTORY_DEPS): | $(sort $(dir $(DIRECTORY_DEPS))) # In the second step, we make a rule to actually create these directories $(sort $(dir $(DIRECTORY_DEPS))): @@ -786,7 +803,9 @@ check: $(OUTPUT)common-cmds.h ### Installation rules -install-bin: all +install-gtk: + +install-bin: all install-gtk $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)' $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' @@ -831,7 +850,8 @@ config-clean: @$(MAKE) -C config/feature-checks clean clean: $(LIBTRACEEVENT)-clean $(LIBLK)-clean config-clean - $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS) + $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(GTK_OBJS) + $(RM) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS) $(RM) $(ALL_PROGRAMS) perf $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean @@ -850,7 +870,7 @@ else GIT-HEAD-PHONY = endif -.PHONY: all install clean config-clean strip +.PHONY: all install clean config-clean strip install-gtk .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell .PHONY: $(GIT-HEAD-PHONY) TAGS tags cscope .FORCE-PERF-CFLAGS diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 0393d98..94f9a8e 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -30,6 +30,7 @@ #include "util/tool.h" #include "arch/common.h" +#include #include struct perf_annotate { @@ -142,8 +143,18 @@ find_next: if (use_browser == 2) { int ret; + int (*annotate)(struct hist_entry *he, + struct perf_evsel *evsel, + struct hist_browser_timer *hbt); + + annotate = dlsym(perf_gtk_handle, + "hist_entry__gtk_annotate"); + if (annotate == NULL) { + ui__error("GTK browser not found!\n"); + return; + } - ret = hist_entry__gtk_annotate(he, evsel, NULL); + ret = annotate(he, evsel, NULL); if (!ret || !ann->skip_missing) return; @@ -247,8 +258,17 @@ static int __cmd_annotate(struct perf_annotate *ann) goto out_delete; } - if (use_browser == 2) - perf_gtk__show_annotations(); + if (use_browser == 2) { + void (*show_annotations)(void); + + show_annotations = dlsym(perf_gtk_handle, + "perf_gtk__show_annotations"); + if (show_annotations == NULL) { + ui__error("GTK browser not found!\n"); + goto out_delete; + } + show_annotations(); + } out_delete: /* diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 06e1abe..21b5c2f 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -35,6 +35,7 @@ #include "util/hist.h" #include "arch/common.h" +#include #include struct perf_report { @@ -591,8 +592,19 @@ static int __cmd_report(struct perf_report *rep) ret = 0; } else if (use_browser == 2) { - perf_evlist__gtk_browse_hists(session->evlist, help, - NULL, rep->min_percent); + int (*hist_browser)(struct perf_evlist *, + const char *, + struct hist_browser_timer *, + float min_pcnt); + + hist_browser = dlsym(perf_gtk_handle, + "perf_evlist__gtk_browse_hists"); + if (hist_browser == NULL) { + ui__error("GTK browser not found!\n"); + return ret; + } + hist_browser(session->evlist, help, NULL, + rep->min_percent); } } else perf_evlist__tty_browse_hists(session->evlist, rep, help); diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index f5d661f..d9bba8d 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -377,11 +377,11 @@ ifndef NO_GTK2 NO_GTK2 := 1 else ifeq ($(feature-gtk2-infobar), 1) - CFLAGS += -DHAVE_GTK_INFO_BAR_SUPPORT + GTK_CFLAGS := -DHAVE_GTK_INFO_BAR_SUPPORT endif CFLAGS += -DHAVE_GTK2_SUPPORT - CFLAGS += $(shell pkg-config --cflags gtk+-2.0 2>/dev/null) - EXTLIBS += $(shell pkg-config --libs gtk+-2.0 2>/dev/null) + GTK_CFLAGS += $(shell pkg-config --cflags gtk+-2.0 2>/dev/null) + GTK_LIBS := $(shell pkg-config --libs gtk+-2.0 2>/dev/null) endif endif @@ -554,7 +554,12 @@ else sysconfdir = $(prefix)/etc ETC_PERFCONFIG = etc/perfconfig endif +ifeq ($(IS_X86_64),1) +lib = lib64 +else lib = lib +endif +libdir = $(prefix)/$(lib) # Shell quote (do not use $(call) to accommodate ancient setups); ETC_PERFCONFIG_SQ = $(subst ','\'',$(ETC_PERFCONFIG)) @@ -567,6 +572,7 @@ template_dir_SQ = $(subst ','\'',$(template_dir)) htmldir_SQ = $(subst ','\'',$(htmldir)) prefix_SQ = $(subst ','\'',$(prefix)) sysconfdir_SQ = $(subst ','\'',$(sysconfdir)) +libdir_SQ = $(subst ','\'',$(libdir)) ifneq ($(filter /%,$(firstword $(perfexecdir))),) perfexec_instdir = $(perfexecdir) diff --git a/tools/perf/ui/gtk/annotate.c b/tools/perf/ui/gtk/annotate.c index f538794..9c7ff8d 100644 --- a/tools/perf/ui/gtk/annotate.c +++ b/tools/perf/ui/gtk/annotate.c @@ -154,9 +154,9 @@ static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym, return 0; } -int symbol__gtk_annotate(struct symbol *sym, struct map *map, - struct perf_evsel *evsel, - struct hist_browser_timer *hbt) +static int symbol__gtk_annotate(struct symbol *sym, struct map *map, + struct perf_evsel *evsel, + struct hist_browser_timer *hbt) { GtkWidget *window; GtkWidget *notebook; @@ -226,6 +226,13 @@ int symbol__gtk_annotate(struct symbol *sym, struct map *map, return 0; } +int hist_entry__gtk_annotate(struct hist_entry *he, + struct perf_evsel *evsel, + struct hist_browser_timer *hbt) +{ + return symbol__gtk_annotate(he->ms.sym, he->ms.map, evsel, hbt); +} + void perf_gtk__show_annotations(void) { GtkWidget *window; diff --git a/tools/perf/ui/gtk/gtk.h b/tools/perf/ui/gtk/gtk.h index a72acbc..8576cf1 100644 --- a/tools/perf/ui/gtk/gtk.h +++ b/tools/perf/ui/gtk/gtk.h @@ -20,6 +20,9 @@ struct perf_gtk_context { guint statbar_ctx_id; }; +int perf_gtk__init(void); +void perf_gtk__exit(bool wait_for_ok); + extern struct perf_gtk_context *pgctx; static inline bool perf_gtk__is_active_context(struct perf_gtk_context *ctx) @@ -48,4 +51,17 @@ static inline GtkWidget *perf_gtk__setup_info_bar(void) } #endif +struct perf_evsel; +struct perf_evlist; +struct hist_entry; +struct hist_browser_timer; + +int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help, + struct hist_browser_timer *hbt, + float min_pcnt); +int hist_entry__gtk_annotate(struct hist_entry *he, + struct perf_evsel *evsel, + struct hist_browser_timer *hbt); +void perf_gtk__show_annotations(void); + #endif /* _PERF_GTK_H_ */ diff --git a/tools/perf/ui/setup.c b/tools/perf/ui/setup.c index 47d9a57..5df5140 100644 --- a/tools/perf/ui/setup.c +++ b/tools/perf/ui/setup.c @@ -1,10 +1,64 @@ #include +#include #include "../util/cache.h" #include "../util/debug.h" #include "../util/hist.h" pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER; +void *perf_gtk_handle; + +#ifdef HAVE_GTK2_SUPPORT +static int setup_gtk_browser(void) +{ + int (*perf_ui_init)(void); + + if (perf_gtk_handle) + return 0; + + perf_gtk_handle = dlopen(PERF_GTK_DSO, RTLD_LAZY); + if (perf_gtk_handle == NULL) { + char buf[PATH_MAX]; + scnprintf(buf, sizeof(buf), "%s/%s", LIBDIR, PERF_GTK_DSO); + perf_gtk_handle = dlopen(buf, RTLD_LAZY); + } + if (perf_gtk_handle == NULL) + return -1; + + perf_ui_init = dlsym(perf_gtk_handle, "perf_gtk__init"); + if (perf_ui_init == NULL) + goto out_close; + + if (perf_ui_init() == 0) + return 0; + +out_close: + dlclose(perf_gtk_handle); + return -1; +} + +static void exit_gtk_browser(bool wait_for_ok) +{ + void (*perf_ui_exit)(bool); + + if (perf_gtk_handle == NULL) + return; + + perf_ui_exit = dlsym(perf_gtk_handle, "perf_gtk__exit"); + if (perf_ui_exit == NULL) + goto out_close; + + perf_ui_exit(wait_for_ok); + +out_close: + dlclose(perf_gtk_handle); + + perf_gtk_handle = NULL; +} +#else +static inline int setup_gtk_browser(void) { return -1; } +static inline void exit_gtk_browser(bool wait_for_ok __maybe_unused) {} +#endif void setup_browser(bool fallback_to_pager) { @@ -17,8 +71,11 @@ void setup_browser(bool fallback_to_pager) switch (use_browser) { case 2: - if (perf_gtk__init() == 0) + if (setup_gtk_browser() == 0) break; + printf("GTK browser requested but could not find %s\n", + PERF_GTK_DSO); + sleep(1); /* fall through */ case 1: use_browser = 1; @@ -39,7 +96,7 @@ void exit_browser(bool wait_for_ok) { switch (use_browser) { case 2: - perf_gtk__exit(wait_for_ok); + exit_gtk_browser(wait_for_ok); break; case 1: diff --git a/tools/perf/ui/ui.h b/tools/perf/ui/ui.h index 1349d14..ab88383 100644 --- a/tools/perf/ui/ui.h +++ b/tools/perf/ui/ui.h @@ -6,6 +6,7 @@ #include extern pthread_mutex_t ui__lock; +extern void *perf_gtk_handle; extern int use_browser; @@ -23,17 +24,6 @@ static inline int ui__init(void) static inline void ui__exit(bool wait_for_ok __maybe_unused) {} #endif -#ifdef HAVE_GTK2_SUPPORT -int perf_gtk__init(void); -void perf_gtk__exit(bool wait_for_ok); -#else -static inline int perf_gtk__init(void) -{ - return -1; -} -static inline void perf_gtk__exit(bool wait_for_ok __maybe_unused) {} -#endif - void ui__refresh_dimensions(bool force); #endif /* _PERF_UI_H_ */ diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index f0699e9..834b7b5 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -165,30 +165,6 @@ static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused, } #endif -#ifdef HAVE_GTK2_SUPPORT -int symbol__gtk_annotate(struct symbol *sym, struct map *map, - struct perf_evsel *evsel, - struct hist_browser_timer *hbt); - -static inline int hist_entry__gtk_annotate(struct hist_entry *he, - struct perf_evsel *evsel, - struct hist_browser_timer *hbt) -{ - return symbol__gtk_annotate(he->ms.sym, he->ms.map, evsel, hbt); -} - -void perf_gtk__show_annotations(void); -#else -static inline int hist_entry__gtk_annotate(struct hist_entry *he __maybe_unused, - struct perf_evsel *evsel __maybe_unused, - struct hist_browser_timer *hbt __maybe_unused) -{ - return 0; -} - -static inline void perf_gtk__show_annotations(void) {} -#endif - extern const char *disassembler_style; #endif /* __PERF_ANNOTATE_H */ diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index ed4f90e..20b1758 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -228,20 +228,5 @@ static inline int script_browse(const char *script_opt __maybe_unused) #define K_SWITCH_INPUT_DATA -3000 #endif -#ifdef HAVE_GTK2_SUPPORT -int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help, - struct hist_browser_timer *hbt __maybe_unused, - float min_pcnt); -#else -static inline -int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __maybe_unused, - const char *help __maybe_unused, - struct hist_browser_timer *hbt __maybe_unused, - float min_pcnt __maybe_unused) -{ - return 0; -} -#endif - unsigned int hists__sort_list_width(struct hists *self); #endif /* __PERF_HIST_H */ diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index c29ecaa..7fd840b 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -128,6 +128,8 @@ void put_tracing_file(char *file); #endif #endif +#define PERF_GTK_DSO "libperf-gtk.so" + /* General helper functions */ extern void usage(const char *err) NORETURN; extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2))); -- cgit v0.10.2 From 963ba5fd5d04f36d6a5c9a94562484a4f270c1de Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 11 Sep 2013 14:09:25 +0900 Subject: perf sort: Fix a memory leak on srcline In the hist_entry__srcline_snprintf(), path and self->srcline are pointing the same memory region, but they are doubly allocated. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378876173-13363-2-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index b4ecc0e..97cf3ef 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -269,10 +269,7 @@ static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, if (!fp) goto out_ip; - if (getline(&path, &line_len, fp) < 0 || !line_len) - goto out_ip; - self->srcline = strdup(path); - if (self->srcline == NULL) + if (getline(&self->srcline, &line_len, fp) < 0 || !line_len) goto out_ip; nl = strchr(self->srcline, '\n'); -- cgit v0.10.2 From 89da393c171926d3372f573d752be5ced98038eb Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 11 Sep 2013 14:09:26 +0900 Subject: perf annotate: Reuse path from the result of addr2line In the symbol__get_source_line(), path and src_line->path will have same value, but they were allocated separately, and leaks one. Just share path to src_line->path. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378876173-13363-3-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 7eae548..c6fd187 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -1143,11 +1143,7 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, if (getline(&path, &line_len, fp) < 0 || !line_len) goto next_close; - src_line->path = malloc(sizeof(char) * line_len + 1); - if (!src_line->path) - goto next_close; - - strcpy(src_line->path, path); + src_line->path = path; insert_source_line(&tmp_root, src_line); next_close: -- cgit v0.10.2 From 909b143162de7af310d2a9351220030260ebe728 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 11 Sep 2013 14:09:27 +0900 Subject: perf hists: Free srcline when freeing hist_entry We've been leaked srcline of hist_entry, it should be freed also. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378876173-13363-4-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index f3278a3..e6fc38a 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -535,6 +535,7 @@ void hist_entry__free(struct hist_entry *he) { free(he->branch_info); free(he->mem_info); + free(he->srcline); free(he); } -- cgit v0.10.2 From f048d548f803b57ee1dbf66702f398ba69657450 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 11 Sep 2013 14:09:28 +0900 Subject: perf annotate: Factor out get/free_srcline() Currently external addr2line tool is used for srcline sort key and annotate with srcline info. Separate the common code to prepare upcoming enhancements. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378876173-13363-5-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 40c39c3..1f13615 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -363,6 +363,7 @@ LIB_OBJS += $(OUTPUT)util/intlist.o LIB_OBJS += $(OUTPUT)util/vdso.o LIB_OBJS += $(OUTPUT)util/stat.o LIB_OBJS += $(OUTPUT)util/record.o +LIB_OBJS += $(OUTPUT)util/srcline.o LIB_OBJS += $(OUTPUT)ui/setup.o LIB_OBJS += $(OUTPUT)ui/helpline.o diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index c6fd187..d48297d 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -1070,7 +1070,7 @@ static void symbol__free_source_line(struct symbol *sym, int len) (sizeof(src_line->p) * (src_line->nr_pcnt - 1)); for (i = 0; i < len; i++) { - free(src_line->path); + free_srcline(src_line->path); src_line = (void *)src_line + sizeof_src_line; } @@ -1087,7 +1087,6 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, u64 start; int i, k; int evidx = evsel->idx; - char cmd[PATH_MAX * 2]; struct source_line *src_line; struct annotation *notes = symbol__annotation(sym); struct sym_hist *h = annotation__histogram(notes, evidx); @@ -1115,10 +1114,7 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, start = map__rip_2objdump(map, sym->start); for (i = 0; i < len; i++) { - char *path = NULL; - size_t line_len; u64 offset; - FILE *fp; double percent_max = 0.0; src_line->nr_pcnt = nr_pcnt; @@ -1135,19 +1131,9 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, goto next; offset = start + i; - sprintf(cmd, "addr2line -e %s %016" PRIx64, filename, offset); - fp = popen(cmd, "r"); - if (!fp) - goto next; - - if (getline(&path, &line_len, fp) < 0 || !line_len) - goto next_close; - - src_line->path = path; + src_line->path = get_srcline(filename, offset); insert_source_line(&tmp_root, src_line); - next_close: - pclose(fp); next: src_line = (void *)src_line + sizeof_src_line; } @@ -1188,7 +1174,7 @@ static void print_summary(struct rb_root *root, const char *filename) path = src_line->path; color = get_percent_color(percent_max); - color_fprintf(stdout, color, " %s", path); + color_fprintf(stdout, color, " %s\n", path); node = rb_next(node); } diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index e6fc38a..cca03831 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -535,7 +535,7 @@ void hist_entry__free(struct hist_entry *he) { free(he->branch_info); free(he->mem_info); - free(he->srcline); + free_srcline(he->srcline); free(he); } diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 97cf3ef..b7e0ef0 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -251,8 +251,7 @@ static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, unsigned int width __maybe_unused) { FILE *fp = NULL; - char cmd[PATH_MAX + 2], *path = self->srcline, *nl; - size_t line_len; + char *path = self->srcline; if (path != NULL) goto out_path; @@ -263,19 +262,9 @@ static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, if (!strncmp(self->ms.map->dso->long_name, "/tmp/perf-", 10)) goto out_ip; - snprintf(cmd, sizeof(cmd), "addr2line -e %s %016" PRIx64, - self->ms.map->dso->long_name, self->ip); - fp = popen(cmd, "r"); - if (!fp) - goto out_ip; - - if (getline(&self->srcline, &line_len, fp) < 0 || !line_len) - goto out_ip; + path = get_srcline(self->ms.map->dso->long_name, self->ip); + self->srcline = path; - nl = strchr(self->srcline, '\n'); - if (nl != NULL) - *nl = '\0'; - path = self->srcline; out_path: if (fp) pclose(fp); diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c new file mode 100644 index 0000000..7e92cca --- /dev/null +++ b/tools/perf/util/srcline.c @@ -0,0 +1,83 @@ +#include +#include +#include + +#include + +#include "util/util.h" +#include "util/debug.h" + +static int addr2line(const char *dso_name, unsigned long addr, + char **file, unsigned int *line_nr) +{ + FILE *fp; + char cmd[PATH_MAX]; + char *filename = NULL; + size_t len; + char *sep; + int ret = 0; + + scnprintf(cmd, sizeof(cmd), "addr2line -e %s %016"PRIx64, + dso_name, addr); + + fp = popen(cmd, "r"); + if (fp == NULL) { + pr_warning("popen failed for %s\n", dso_name); + return 0; + } + + if (getline(&filename, &len, fp) < 0 || !len) { + pr_warning("addr2line has no output for %s\n", dso_name); + goto out; + } + + sep = strchr(filename, '\n'); + if (sep) + *sep = '\0'; + + if (!strcmp(filename, "??:0")) { + pr_debug("no debugging info in %s\n", dso_name); + free(filename); + goto out; + } + + sep = strchr(filename, ':'); + if (sep) { + *sep++ = '\0'; + *file = filename; + *line_nr = strtoul(sep, NULL, 0); + ret = 1; + } +out: + pclose(fp); + return ret; +} + +char *get_srcline(const char *dso_name, unsigned long addr) +{ + char *file; + unsigned line; + char *srcline; + size_t size; + + if (!addr2line(dso_name, addr, &file, &line)) + return SRCLINE_UNKNOWN; + + /* just calculate actual length */ + size = snprintf(NULL, 0, "%s:%u", file, line) + 1; + + srcline = malloc(size); + if (srcline) + snprintf(srcline, size, "%s:%u", file, line); + else + srcline = SRCLINE_UNKNOWN; + + free(file); + return srcline; +} + +void free_srcline(char *srcline) +{ + if (srcline && strcmp(srcline, SRCLINE_UNKNOWN) != 0) + free(srcline); +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 7fd840b..7c8b43f 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -297,4 +297,9 @@ struct parse_tag { }; unsigned long parse_tag_value(const char *str, struct parse_tag *tags); + +#define SRCLINE_UNKNOWN ((char *) "??:0") + +char *get_srcline(const char *dso_name, unsigned long addr); +void free_srcline(char *srcline); #endif /* GIT_COMPAT_UTIL_H */ -- cgit v0.10.2 From 58d91a0068694a5ba3efc99e88ce6b4b0dd0d085 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 11 Sep 2013 14:09:29 +0900 Subject: perf tools: Do not try to call addr2line on non-binary files No need to call addr2line since they don't have such information. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378876173-13363-6-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index b7e0ef0..d443593 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -259,9 +259,6 @@ static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, if (!self->ms.map) goto out_ip; - if (!strncmp(self->ms.map->dso->long_name, "/tmp/perf-", 10)) - goto out_ip; - path = get_srcline(self->ms.map->dso->long_name, self->ip); self->srcline = path; diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index 7e92cca..777f918 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c @@ -57,11 +57,17 @@ char *get_srcline(const char *dso_name, unsigned long addr) { char *file; unsigned line; - char *srcline; + char *srcline = SRCLINE_UNKNOWN; size_t size; + if (dso_name[0] == '[') + goto out; + + if (!strncmp(dso_name, "/tmp/perf-", 10)) + goto out; + if (!addr2line(dso_name, addr, &file, &line)) - return SRCLINE_UNKNOWN; + goto out; /* just calculate actual length */ size = snprintf(NULL, 0, "%s:%u", file, line) + 1; @@ -73,6 +79,7 @@ char *get_srcline(const char *dso_name, unsigned long addr) srcline = SRCLINE_UNKNOWN; free(file); +out: return srcline; } -- cgit v0.10.2 From 86c98cab5a3137376ea7df5ffa5bd52e545fee95 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 11 Sep 2013 14:09:30 +0900 Subject: perf annotate: Pass dso instead of dso_name to get_srcline() This is a preparation of next change. No functional changes are intended. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378876173-13363-7-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index d48297d..d73e800 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -1081,8 +1081,7 @@ static void symbol__free_source_line(struct symbol *sym, int len) /* Get the filename:line for the colored entries */ static int symbol__get_source_line(struct symbol *sym, struct map *map, struct perf_evsel *evsel, - struct rb_root *root, int len, - const char *filename) + struct rb_root *root, int len) { u64 start; int i, k; @@ -1131,7 +1130,7 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, goto next; offset = start + i; - src_line->path = get_srcline(filename, offset); + src_line->path = get_srcline(map->dso, offset); insert_source_line(&tmp_root, src_line); next: @@ -1338,7 +1337,6 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, bool full_paths, int min_pcnt, int max_lines) { struct dso *dso = map->dso; - const char *filename = dso->long_name; struct rb_root source_line = RB_ROOT; u64 len; @@ -1348,9 +1346,8 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, len = symbol__size(sym); if (print_lines) { - symbol__get_source_line(sym, map, evsel, &source_line, - len, filename); - print_summary(&source_line, filename); + symbol__get_source_line(sym, map, evsel, &source_line, len); + print_summary(&source_line, dso->long_name); } symbol__annotate_printf(sym, map, evsel, full_paths, diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index dbd9241..72eedd6 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -6,6 +6,7 @@ #include #include "types.h" #include "map.h" +#include "build-id.h" enum dso_binary_type { DSO_BINARY_TYPE__KALLSYMS = 0, diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index d443593..f732120 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -259,7 +259,7 @@ static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, if (!self->ms.map) goto out_ip; - path = get_srcline(self->ms.map->dso->long_name, self->ip); + path = get_srcline(self->ms.map->dso, self->ip); self->srcline = path; out_path: diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index 777f918..c736d94 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c @@ -4,6 +4,7 @@ #include +#include "util/dso.h" #include "util/util.h" #include "util/debug.h" @@ -53,11 +54,12 @@ out: return ret; } -char *get_srcline(const char *dso_name, unsigned long addr) +char *get_srcline(struct dso *dso, unsigned long addr) { char *file; unsigned line; char *srcline = SRCLINE_UNKNOWN; + char *dso_name = dso->long_name; size_t size; if (dso_name[0] == '[') diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 7c8b43f..1f06ba4 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -300,6 +300,8 @@ unsigned long parse_tag_value(const char *str, struct parse_tag *tags); #define SRCLINE_UNKNOWN ((char *) "??:0") -char *get_srcline(const char *dso_name, unsigned long addr); +struct dso; + +char *get_srcline(struct dso *dso, unsigned long addr); void free_srcline(char *srcline); #endif /* GIT_COMPAT_UTIL_H */ -- cgit v0.10.2 From 2cc9d0ef577975abb3ebce7d5978559ec1c73633 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 11 Sep 2013 14:09:31 +0900 Subject: perf tools: Save failed result of get_srcline() Some dso's lack srcline info, so there's no point to keep trying on them. Just save failture status and skip them. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378876173-13363-8-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 6bfc8aa..af4c687c 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -450,6 +450,7 @@ struct dso *dso__new(const char *name) dso->rel = 0; dso->sorted_by_name = 0; dso->has_build_id = 0; + dso->has_srcline = 1; dso->kernel = DSO_TYPE_USER; dso->needs_swap = DSO_SWAP__UNSET; INIT_LIST_HEAD(&dso->node); diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index 72eedd6..9ac666a 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -83,6 +83,7 @@ struct dso { enum dso_binary_type data_type; u8 adjust_symbols:1; u8 has_build_id:1; + u8 has_srcline:1; u8 hit:1; u8 annotate_warned:1; u8 sname_alloc:1; diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index c736d94..dcff10b 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c @@ -58,10 +58,13 @@ char *get_srcline(struct dso *dso, unsigned long addr) { char *file; unsigned line; - char *srcline = SRCLINE_UNKNOWN; + char *srcline; char *dso_name = dso->long_name; size_t size; + if (!dso->has_srcline) + return SRCLINE_UNKNOWN; + if (dso_name[0] == '[') goto out; @@ -81,8 +84,11 @@ char *get_srcline(struct dso *dso, unsigned long addr) srcline = SRCLINE_UNKNOWN; free(file); -out: return srcline; + +out: + dso->has_srcline = 0; + return SRCLINE_UNKNOWN; } void free_srcline(char *srcline) -- cgit v0.10.2 From 2f48fcd84e9e68392e29c59204a4a434311d49e9 Mon Sep 17 00:00:00 2001 From: Roberto Vitillo Date: Wed, 11 Sep 2013 14:09:32 +0900 Subject: perf tools: Implement addr2line directly using libbfd When the srcline sort key is used , the external addr2line utility needs to be run for each hist entry to get the srcline info. This can consume quite a time if one has a huge perf.data file. So rather than executing the external utility, implement it internally and just call it. We can do it since we've linked with libbfd already. Signed-off-by: Roberto Agostino Vitillo Reviewed-by: Jiri Olsa Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378876173-13363-9-git-send-email-namhyung@kernel.org [ Use a2l_data struct instead of static globals ] Signed-off-by: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index d9bba8d..cf6ad5d 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -523,6 +523,10 @@ ifndef NO_LIBNUMA endif endif +ifndef ($(filter -lbfd,$(EXTLIBS)),) + CFLAGS += -DHAVE_LIBBFD_SUPPORT +endif + # Among the variables below, these: # perfexecdir # template_dir diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index dcff10b..3735319 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c @@ -8,6 +8,172 @@ #include "util/util.h" #include "util/debug.h" +#ifdef HAVE_LIBBFD_SUPPORT + +/* + * Implement addr2line using libbfd. + */ +#define PACKAGE "perf" +#include + +struct a2l_data { + const char *input; + unsigned long addr; + + bool found; + const char *filename; + const char *funcname; + unsigned line; + + bfd *abfd; + asymbol **syms; +}; + +static int bfd_error(const char *string) +{ + const char *errmsg; + + errmsg = bfd_errmsg(bfd_get_error()); + fflush(stdout); + + if (string) + pr_debug("%s: %s\n", string, errmsg); + else + pr_debug("%s\n", errmsg); + + return -1; +} + +static int slurp_symtab(bfd *abfd, struct a2l_data *a2l) +{ + long storage; + long symcount; + asymbol **syms; + bfd_boolean dynamic = FALSE; + + if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0) + return bfd_error(bfd_get_filename(abfd)); + + storage = bfd_get_symtab_upper_bound(abfd); + if (storage == 0L) { + storage = bfd_get_dynamic_symtab_upper_bound(abfd); + dynamic = TRUE; + } + if (storage < 0L) + return bfd_error(bfd_get_filename(abfd)); + + syms = malloc(storage); + if (dynamic) + symcount = bfd_canonicalize_dynamic_symtab(abfd, syms); + else + symcount = bfd_canonicalize_symtab(abfd, syms); + + if (symcount < 0) { + free(syms); + return bfd_error(bfd_get_filename(abfd)); + } + + a2l->syms = syms; + return 0; +} + +static void find_address_in_section(bfd *abfd, asection *section, void *data) +{ + bfd_vma pc, vma; + bfd_size_type size; + struct a2l_data *a2l = data; + + if (a2l->found) + return; + + if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0) + return; + + pc = a2l->addr; + vma = bfd_get_section_vma(abfd, section); + size = bfd_get_section_size(section); + + if (pc < vma || pc >= vma + size) + return; + + a2l->found = bfd_find_nearest_line(abfd, section, a2l->syms, pc - vma, + &a2l->filename, &a2l->funcname, + &a2l->line); +} + +static struct a2l_data *addr2line_init(const char *path) +{ + bfd *abfd; + struct a2l_data *a2l = NULL; + + abfd = bfd_openr(path, NULL); + if (abfd == NULL) + return NULL; + + if (!bfd_check_format(abfd, bfd_object)) + goto out; + + a2l = zalloc(sizeof(*a2l)); + if (a2l == NULL) + goto out; + + a2l->abfd = abfd; + a2l->input = strdup(path); + if (a2l->input == NULL) + goto out; + + if (slurp_symtab(abfd, a2l)) + goto out; + + return a2l; + +out: + if (a2l) { + free((void *)a2l->input); + free(a2l); + } + bfd_close(abfd); + return NULL; +} + +static void addr2line_cleanup(struct a2l_data *a2l) +{ + if (a2l->abfd) + bfd_close(a2l->abfd); + free((void *)a2l->input); + free(a2l->syms); + free(a2l); +} + +static int addr2line(const char *dso_name, unsigned long addr, + char **file, unsigned int *line) +{ + int ret = 0; + struct a2l_data *a2l; + + a2l = addr2line_init(dso_name); + if (a2l == NULL) { + pr_warning("addr2line_init failed for %s\n", dso_name); + return 0; + } + + a2l->addr = addr; + bfd_map_over_sections(a2l->abfd, find_address_in_section, a2l); + + if (a2l->found && a2l->filename) { + *file = strdup(a2l->filename); + *line = a2l->line; + + if (*file) + ret = 1; + } + + addr2line_cleanup(a2l); + return ret; +} + +#else /* HAVE_LIBBFD_SUPPORT */ + static int addr2line(const char *dso_name, unsigned long addr, char **file, unsigned int *line_nr) { @@ -53,6 +219,7 @@ out: pclose(fp); return ret; } +#endif /* HAVE_LIBBFD_SUPPORT */ char *get_srcline(struct dso *dso, unsigned long addr) { -- cgit v0.10.2 From 4adcc43003024354b45edadfad4ea2fa205f135f Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 11 Sep 2013 14:09:33 +0900 Subject: perf tools: Fix srcline sort key behavior Currently the srcline sort key compares ip rather than srcline info. I guess this was due to a performance reason to run external addr2line utility. Now we have implemented the functionality inside, use the srcline info when comparing hist entries. Also constantly print "??:0" string for unknown srcline rather than printing ip. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378876173-13363-10-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index f732120..32c5637 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -243,33 +243,32 @@ struct sort_entry sort_sym = { static int64_t sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right) { - return (int64_t)(right->ip - left->ip); + if (!left->srcline) { + if (!left->ms.map) + left->srcline = SRCLINE_UNKNOWN; + else { + struct map *map = left->ms.map; + left->srcline = get_srcline(map->dso, + map__rip_2objdump(map, left->ip)); + } + } + if (!right->srcline) { + if (!right->ms.map) + right->srcline = SRCLINE_UNKNOWN; + else { + struct map *map = right->ms.map; + right->srcline = get_srcline(map->dso, + map__rip_2objdump(map, right->ip)); + } + } + return strcmp(left->srcline, right->srcline); } static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, size_t size, unsigned int width __maybe_unused) { - FILE *fp = NULL; - char *path = self->srcline; - - if (path != NULL) - goto out_path; - - if (!self->ms.map) - goto out_ip; - - path = get_srcline(self->ms.map->dso, self->ip); - self->srcline = path; - -out_path: - if (fp) - pclose(fp); - return repsep_snprintf(bf, size, "%s", path); -out_ip: - if (fp) - pclose(fp); - return repsep_snprintf(bf, size, "%-#*llx", BITS_PER_LONG / 4, self->ip); + return repsep_snprintf(bf, size, "%s", self->srcline); } struct sort_entry sort_srcline = { -- cgit v0.10.2 From eac032c54be48e899a7be2b8db4b746be1b51748 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 20 Sep 2013 11:27:32 -0300 Subject: perf trace: Beautify epoll_ctl 'op' arg [root@sandy ~]# perf trace -e epoll_ctl 2.490 (0.003 ms): systemd-logind/586 epoll_ctl(epfd: 10, op: ADD, fd: 24, event: 0x7fff22314ef0) = 0 2.621 (0.003 ms): systemd-logind/586 epoll_ctl(epfd: 10, op: DEL, fd: 24 ) = 0 2.833 (0.010 ms): systemd-logind/586 epoll_ctl(epfd: 10, op: ADD, fd: 24, event: 0x7fff22314cd0) = 0 2.953 (0.002 ms): systemd-logind/586 epoll_ctl(epfd: 10, op: DEL, fd: 24 ) = 0 3.118 (0.002 ms): systemd-logind/586 epoll_ctl(epfd: 10, op: ADD, fd: 24, event: 0x7fff22314d20) = 0 4.762 (0.002 ms): systemd-logind/586 epoll_ctl(epfd: 10, op: DEL, fd: 24 ) = 0 ^C[root@sandy ~]# Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-88xz9phc8cbicnxonud6if8h@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index f3ddbb0..807e542 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -232,6 +232,9 @@ static size_t syscall_arg__scnprintf_futex_op(char *bf, size_t size, struct sysc #define SCA_FUTEX_OP syscall_arg__scnprintf_futex_op +static const char *epoll_ctl_ops[] = { [1] = "ADD", "DEL", "MOD", }; +static DEFINE_STRARRAY(epoll_ctl_ops); + static const char *itimers[] = { "REAL", "VIRTUAL", "PROF", }; static DEFINE_STRARRAY(itimers); @@ -543,6 +546,9 @@ static struct syscall_fmt { { .name = "brk", .hexret = true, .arg_scnprintf = { [0] = SCA_HEX, /* brk */ }, }, { .name = "connect", .errmsg = true, }, + { .name = "epoll_ctl", .errmsg = true, + .arg_scnprintf = { [1] = SCA_STRARRAY, /* op */ }, + .arg_parm = { [1] = &strarray__epoll_ctl_ops, /* op */ }, }, { .name = "eventfd2", .errmsg = true, .arg_scnprintf = { [1] = SCA_EFD_FLAGS, /* flags */ }, }, { .name = "fcntl", .errmsg = true, -- cgit v0.10.2 From 5cea6ff265e6979226b751f5ebfdda4594322523 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 20 Sep 2013 11:49:50 -0300 Subject: perf trace: Beautify flock 'cmd' arg 4735.638 ( 0.003 ms): man/19881 flock(fd: 3, cmd: SH|NB) = 0 4735.832 ( 0.002 ms): man/19881 flock(fd: 3, cmd: UN ) = 0 Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-amh3y88kh1nmclpwezqlarl8@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 807e542..2007c8c 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -189,6 +189,37 @@ static size_t syscall_arg__scnprintf_madvise_behavior(char *bf, size_t size, #define SCA_MADV_BHV syscall_arg__scnprintf_madvise_behavior +static size_t syscall_arg__scnprintf_flock(char *bf, size_t size, + struct syscall_arg *arg) +{ + int printed = 0, op = arg->val; + + if (op == 0) + return scnprintf(bf, size, "NONE"); +#define P_CMD(cmd) \ + if ((op & LOCK_##cmd) == LOCK_##cmd) { \ + printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #cmd); \ + op &= ~LOCK_##cmd; \ + } + + P_CMD(SH); + P_CMD(EX); + P_CMD(NB); + P_CMD(UN); + P_CMD(MAND); + P_CMD(RW); + P_CMD(READ); + P_CMD(WRITE); +#undef P_OP + + if (op) + printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", op); + + return printed; +} + +#define SCA_FLOCK syscall_arg__scnprintf_flock + static size_t syscall_arg__scnprintf_futex_op(char *bf, size_t size, struct syscall_arg *arg) { enum syscall_futex_args { @@ -554,6 +585,8 @@ static struct syscall_fmt { { .name = "fcntl", .errmsg = true, .arg_scnprintf = { [1] = SCA_STRARRAY, /* cmd */ }, .arg_parm = { [1] = &strarray__fcntl_cmds, /* cmd */ }, }, + { .name = "flock", .errmsg = true, + .arg_scnprintf = { [1] = SCA_FLOCK, /* cmd */ }, }, { .name = "fstat", .errmsg = true, .alias = "newfstat", }, { .name = "fstatat", .errmsg = true, .alias = "newfstatat", }, { .name = "futex", .errmsg = true, -- cgit v0.10.2 From 453350dd0f0245b91b1e43310f5966fb1c51e7bd Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 20 Sep 2013 12:13:37 -0300 Subject: perf trace: Add helper for syscalls with a single strarray arg In such cases just stating the (arg, name, array) is enough, reducing the size of the syscall formatters table. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-3k53p6dv2sh4ydsc5k5otoia@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 2007c8c..8855207 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -562,6 +562,10 @@ static size_t syscall_arg__scnprintf_signum(char *bf, size_t size, struct syscal #define SCA_SIGNUM syscall_arg__scnprintf_signum +#define STRARRAY(arg, name, array) \ + .arg_scnprintf = { [arg] = SCA_STRARRAY, }, \ + .arg_parm = { [arg] = &strarray__##array, } + static struct syscall_fmt { const char *name; const char *alias; @@ -577,33 +581,23 @@ static struct syscall_fmt { { .name = "brk", .hexret = true, .arg_scnprintf = { [0] = SCA_HEX, /* brk */ }, }, { .name = "connect", .errmsg = true, }, - { .name = "epoll_ctl", .errmsg = true, - .arg_scnprintf = { [1] = SCA_STRARRAY, /* op */ }, - .arg_parm = { [1] = &strarray__epoll_ctl_ops, /* op */ }, }, + { .name = "epoll_ctl", .errmsg = true, STRARRAY(1, op, epoll_ctl_ops), }, { .name = "eventfd2", .errmsg = true, .arg_scnprintf = { [1] = SCA_EFD_FLAGS, /* flags */ }, }, - { .name = "fcntl", .errmsg = true, - .arg_scnprintf = { [1] = SCA_STRARRAY, /* cmd */ }, - .arg_parm = { [1] = &strarray__fcntl_cmds, /* cmd */ }, }, + { .name = "fcntl", .errmsg = true, STRARRAY(1, cmd, fcntl_cmds), }, { .name = "flock", .errmsg = true, .arg_scnprintf = { [1] = SCA_FLOCK, /* cmd */ }, }, { .name = "fstat", .errmsg = true, .alias = "newfstat", }, { .name = "fstatat", .errmsg = true, .alias = "newfstatat", }, { .name = "futex", .errmsg = true, .arg_scnprintf = { [1] = SCA_FUTEX_OP, /* op */ }, }, - { .name = "getitimer", .errmsg = true, - .arg_scnprintf = { [0] = SCA_STRARRAY, /* which */ }, - .arg_parm = { [0] = &strarray__itimers, /* which */ }, }, - { .name = "getrlimit", .errmsg = true, - .arg_scnprintf = { [0] = SCA_STRARRAY, /* resource */ }, - .arg_parm = { [0] = &strarray__rlimit_resources, /* resource */ }, }, + { .name = "getitimer", .errmsg = true, STRARRAY(0, which, itimers), }, + { .name = "getrlimit", .errmsg = true, STRARRAY(0, resource, rlimit_resources), }, { .name = "ioctl", .errmsg = true, .arg_scnprintf = { [2] = SCA_HEX, /* arg */ }, }, { .name = "kill", .errmsg = true, .arg_scnprintf = { [1] = SCA_SIGNUM, /* sig */ }, }, - { .name = "lseek", .errmsg = true, - .arg_scnprintf = { [2] = SCA_STRARRAY, /* whence */ }, - .arg_parm = { [2] = &strarray__whences, /* whence */ }, }, + { .name = "lseek", .errmsg = true, STRARRAY(2, whence, whences), }, { .name = "lstat", .errmsg = true, .alias = "newlstat", }, { .name = "madvise", .errmsg = true, .arg_scnprintf = { [0] = SCA_HEX, /* start */ @@ -629,9 +623,7 @@ static struct syscall_fmt { { .name = "poll", .errmsg = true, .timeout = true, }, { .name = "ppoll", .errmsg = true, .timeout = true, }, { .name = "pread", .errmsg = true, .alias = "pread64", }, - { .name = "prlimit64", .errmsg = true, - .arg_scnprintf = { [1] = SCA_STRARRAY, /* resource */ }, - .arg_parm = { [1] = &strarray__rlimit_resources, /* resource */ }, }, + { .name = "prlimit64", .errmsg = true, STRARRAY(1, resource, rlimit_resources), }, { .name = "pwrite", .errmsg = true, .alias = "pwrite64", }, { .name = "read", .errmsg = true, }, { .name = "recvfrom", .errmsg = true, @@ -642,9 +634,7 @@ static struct syscall_fmt { .arg_scnprintf = { [2] = SCA_MSG_FLAGS, /* flags */ }, }, { .name = "rt_sigaction", .errmsg = true, .arg_scnprintf = { [0] = SCA_SIGNUM, /* sig */ }, }, - { .name = "rt_sigprocmask", .errmsg = true, - .arg_scnprintf = { [0] = SCA_STRARRAY, /* how */ }, - .arg_parm = { [0] = &strarray__sighow, /* how */ }, }, + { .name = "rt_sigprocmask", .errmsg = true, STRARRAY(0, how, sighow), }, { .name = "rt_sigqueueinfo", .errmsg = true, .arg_scnprintf = { [1] = SCA_SIGNUM, /* sig */ }, }, { .name = "rt_tgsigqueueinfo", .errmsg = true, @@ -656,12 +646,8 @@ static struct syscall_fmt { .arg_scnprintf = { [2] = SCA_MSG_FLAGS, /* flags */ }, }, { .name = "sendto", .errmsg = true, .arg_scnprintf = { [3] = SCA_MSG_FLAGS, /* flags */ }, }, - { .name = "setitimer", .errmsg = true, - .arg_scnprintf = { [0] = SCA_STRARRAY, /* which */ }, - .arg_parm = { [0] = &strarray__itimers, /* which */ }, }, - { .name = "setrlimit", .errmsg = true, - .arg_scnprintf = { [0] = SCA_STRARRAY, /* resource */ }, - .arg_parm = { [0] = &strarray__rlimit_resources, /* resource */ }, }, + { .name = "setitimer", .errmsg = true, STRARRAY(0, which, itimers), }, + { .name = "setrlimit", .errmsg = true, STRARRAY(0, resource, rlimit_resources), }, { .name = "socket", .errmsg = true, .arg_scnprintf = { [0] = SCA_STRARRAY, /* family */ [1] = SCA_SK_TYPE, /* type */ }, -- cgit v0.10.2 From 4aa5823225c70efba758750029504807cd30b925 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 20 Sep 2013 12:19:41 -0300 Subject: perf trace: Don't supress zeroed args when there is an strarray entry for it Case in hand: 9.682 ( 0.001 ms): Xorg/13079 setitimer(which: REAL, value: 0x7fffede42470) = 0 ITIMER_REAL is zero. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-6hnoqsjh99t4hxi3xu2nlwep@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 8855207..27b0ec8 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -940,8 +940,15 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, field = field->next, ++arg.idx, bit <<= 1) { if (arg.mask & bit) continue; - - if (args[arg.idx] == 0) + /* + * Suppress this argument if its value is zero and + * and we don't have a string associated in an + * strarray for it. + */ + if (args[arg.idx] == 0 && + !(sc->arg_scnprintf && + sc->arg_scnprintf[arg.idx] == SCA_STRARRAY && + sc->arg_parm[arg.idx])) continue; printed += scnprintf(bf + printed, size - printed, -- cgit v0.10.2 From 07120aa5d5a241895bcca8b6e5b5b8d7b1df1f2f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 20 Sep 2013 12:24:20 -0300 Subject: perf trace: Use socket's beautifiers in socketpair For the address family and socket type. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-3a6cwwskobvan823pau76cm4@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 27b0ec8..bf74217 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -652,6 +652,10 @@ static struct syscall_fmt { .arg_scnprintf = { [0] = SCA_STRARRAY, /* family */ [1] = SCA_SK_TYPE, /* type */ }, .arg_parm = { [0] = &strarray__socket_families, /* family */ }, }, + { .name = "socketpair", .errmsg = true, + .arg_scnprintf = { [0] = SCA_STRARRAY, /* family */ + [1] = SCA_SK_TYPE, /* type */ }, + .arg_parm = { [0] = &strarray__socket_families, /* family */ }, }, { .name = "stat", .errmsg = true, .alias = "newstat", }, { .name = "tgkill", .errmsg = true, .arg_scnprintf = { [2] = SCA_SIGNUM, /* sig */ }, }, -- cgit v0.10.2 From 46cce19ba8cc8231d3c949c128e4bdaf420228a2 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 23 Sep 2013 12:52:04 -0300 Subject: perf trace: Beautify pipe2 'flags' arg 4.234 (0.005 ms): fetchmail/3224 pipe2(fildes: 0x7fffc72bcee0, flags: CLOEXEC) = 0 Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-9e1jz78i6q6e0xr9fsitqbpe@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index bf74217..76d9427 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -517,6 +517,29 @@ static size_t syscall_arg__scnprintf_eventfd_flags(char *bf, size_t size, #define SCA_EFD_FLAGS syscall_arg__scnprintf_eventfd_flags +static size_t syscall_arg__scnprintf_pipe_flags(char *bf, size_t size, + struct syscall_arg *arg) +{ + int printed = 0, flags = arg->val; + +#define P_FLAG(n) \ + if (flags & O_##n) { \ + printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \ + flags &= ~O_##n; \ + } + + P_FLAG(CLOEXEC); + P_FLAG(NONBLOCK); +#undef P_FLAG + + if (flags) + printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags); + + return printed; +} + +#define SCA_PIPE_FLAGS syscall_arg__scnprintf_pipe_flags + static size_t syscall_arg__scnprintf_signum(char *bf, size_t size, struct syscall_arg *arg) { int sig = arg->val; @@ -620,6 +643,8 @@ static struct syscall_fmt { .arg_scnprintf = { [2] = SCA_OPEN_FLAGS, /* flags */ }, }, { .name = "openat", .errmsg = true, .arg_scnprintf = { [2] = SCA_OPEN_FLAGS, /* flags */ }, }, + { .name = "pipe2", .errmsg = true, + .arg_scnprintf = { [1] = SCA_PIPE_FLAGS, /* flags */ }, }, { .name = "poll", .errmsg = true, .timeout = true, }, { .name = "ppoll", .errmsg = true, .timeout = true, }, { .name = "pread", .errmsg = true, .alias = "pread64", }, -- cgit v0.10.2 From 4f8c1b74c5fdac35ee4480685d42030446724848 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Sun, 22 Sep 2013 19:45:00 -0600 Subject: perf trace: Add beautifier for clock_gettime's clk_id argument Before: 0.030 ( 0.002 ms): 2571 clock_gettime(which_clock: 1, tp: 0x7f3b45729cd0 ) = 0 After: 0.030 ( 0.002 ms): 2571 clock_gettime(which_clock: MONOTONIC, tp: 0x7f3b45729cd0 ) = 0 v2: Update to use the STRARRAY option Signed-off-by: David Ahern Cc: Adrian Hunter Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1379900700-5186-6-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 76d9427..39a947a 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -297,6 +297,12 @@ static DEFINE_STRARRAY(rlimit_resources); static const char *sighow[] = { "BLOCK", "UNBLOCK", "SETMASK", }; static DEFINE_STRARRAY(sighow); +static const char *clockid[] = { + "REALTIME", "MONOTONIC", "PROCESS_CPUTIME_ID", "THREAD_CPUTIME_ID", + "MONOTONIC_RAW", "REALTIME_COARSE", "MONOTONIC_COARSE", +}; +static DEFINE_STRARRAY(clockid); + static const char *socket_families[] = { "UNSPEC", "LOCAL", "INET", "AX25", "IPX", "APPLETALK", "NETROM", "BRIDGE", "ATMPVC", "X25", "INET6", "ROSE", "DECnet", "NETBEUI", @@ -603,6 +609,7 @@ static struct syscall_fmt { { .name = "arch_prctl", .errmsg = true, .alias = "prctl", }, { .name = "brk", .hexret = true, .arg_scnprintf = { [0] = SCA_HEX, /* brk */ }, }, + { .name = "clock_gettime", .errmsg = true, STRARRAY(0, clk_id, clockid), }, { .name = "connect", .errmsg = true, }, { .name = "epoll_ctl", .errmsg = true, STRARRAY(1, op, epoll_ctl_ops), }, { .name = "eventfd2", .errmsg = true, -- cgit v0.10.2 From b6e8f8f4674be5a32f78027ec6e432f5ea33921e Mon Sep 17 00:00:00 2001 From: David Ahern Date: Sun, 22 Sep 2013 19:44:56 -0600 Subject: perf trace: Handle MSG_WAITFORONE not defined Needed for compiles on Fedora 12 for example. Signed-off-by: David Ahern Link: http://lkml.kernel.org/r/1379900700-5186-2-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 39a947a..3ca6a85 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -363,6 +363,9 @@ static size_t syscall_arg__scnprintf_socket_type(char *bf, size_t size, #ifndef MSG_PROBE #define MSG_PROBE 0x10 #endif +#ifndef MSG_WAITFORONE +#define MSG_WAITFORONE 0x10000 +#endif #ifndef MSG_SENDPAGE_NOTLAST #define MSG_SENDPAGE_NOTLAST 0x20000 #endif -- cgit v0.10.2 From 3d903aa74a98da2836249bce86aaf557781239c9 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 24 Sep 2013 00:09:38 -0300 Subject: perf trace: Beautify mlock & friends 'addr' arg Printing it as an hex number. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-gd68zmnwbbofsv5m6w18intw@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 3ca6a85..12a275d 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -635,6 +635,10 @@ static struct syscall_fmt { { .name = "madvise", .errmsg = true, .arg_scnprintf = { [0] = SCA_HEX, /* start */ [2] = SCA_MADV_BHV, /* behavior */ }, }, + { .name = "mlock", .errmsg = true, + .arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, }, + { .name = "mlockall", .errmsg = true, + .arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, }, { .name = "mmap", .hexret = true, .arg_scnprintf = { [0] = SCA_HEX, /* addr */ [2] = SCA_MMAP_PROT, /* prot */ @@ -645,6 +649,8 @@ static struct syscall_fmt { { .name = "mremap", .hexret = true, .arg_scnprintf = { [0] = SCA_HEX, /* addr */ [4] = SCA_HEX, /* new_addr */ }, }, + { .name = "munlock", .errmsg = true, + .arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, }, { .name = "munmap", .errmsg = true, .arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, }, { .name = "open", .errmsg = true, -- cgit v0.10.2 From 75b757ca90469e990e6901f4a9497fe4161f7f5a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 24 Sep 2013 11:04:32 -0300 Subject: perf trace: Show path associated with fd in live sessions For live sessions we can just access /proc to map an fd to its path, on a best effort way, i.e. sometimes the fd will have gone away when we try to do the mapping, as it is done in a lazy way, only when a reference to such fd is made then the path will be looked up in /proc. This is disabled when processing perf.data files, where we will have to have a way to get getname events, be it via an on-the-fly 'perf probe' event or after a vfs_getname tracepoint is added to the kernel. A first step will be to synthesize such event for the use cases where the threads in the monitored workload exist already. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-1r1ti33ye1666jezu2d8q1c3@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 12a275d..fcc157f 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -36,6 +36,8 @@ struct syscall_arg { unsigned long val; + struct thread *thread; + struct trace *trace; void *parm; u8 idx; u8 mask; @@ -65,6 +67,29 @@ static size_t syscall_arg__scnprintf_strarray(char *bf, size_t size, #define SCA_STRARRAY syscall_arg__scnprintf_strarray +static size_t syscall_arg__scnprintf_fd(char *bf, size_t size, + struct syscall_arg *arg); + +#define SCA_FD syscall_arg__scnprintf_fd + +static size_t syscall_arg__scnprintf_fd_at(char *bf, size_t size, + struct syscall_arg *arg) +{ + int fd = arg->val; + + if (fd == AT_FDCWD) + return scnprintf(bf, size, "CWD"); + + return syscall_arg__scnprintf_fd(bf, size, arg); +} + +#define SCA_FDAT syscall_arg__scnprintf_fd_at + +static size_t syscall_arg__scnprintf_close_fd(char *bf, size_t size, + struct syscall_arg *arg); + +#define SCA_CLOSE_FD syscall_arg__scnprintf_close_fd + static size_t syscall_arg__scnprintf_hex(char *bf, size_t size, struct syscall_arg *arg) { @@ -613,28 +638,84 @@ static struct syscall_fmt { { .name = "brk", .hexret = true, .arg_scnprintf = { [0] = SCA_HEX, /* brk */ }, }, { .name = "clock_gettime", .errmsg = true, STRARRAY(0, clk_id, clockid), }, + { .name = "close", .errmsg = true, + .arg_scnprintf = { [0] = SCA_CLOSE_FD, /* fd */ }, }, { .name = "connect", .errmsg = true, }, + { .name = "dup", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, + { .name = "dup2", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, + { .name = "dup3", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, { .name = "epoll_ctl", .errmsg = true, STRARRAY(1, op, epoll_ctl_ops), }, { .name = "eventfd2", .errmsg = true, .arg_scnprintf = { [1] = SCA_EFD_FLAGS, /* flags */ }, }, - { .name = "fcntl", .errmsg = true, STRARRAY(1, cmd, fcntl_cmds), }, + { .name = "faccessat", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, }, + { .name = "fadvise64", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, + { .name = "fallocate", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, + { .name = "fchdir", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, + { .name = "fchmod", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, + { .name = "fchmodat", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, }, + { .name = "fchown", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, + { .name = "fchownat", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, }, + { .name = "fcntl", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FD, /* fd */ + [1] = SCA_STRARRAY, /* cmd */ }, + .arg_parm = { [1] = &strarray__fcntl_cmds, /* cmd */ }, }, + { .name = "fdatasync", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, { .name = "flock", .errmsg = true, - .arg_scnprintf = { [1] = SCA_FLOCK, /* cmd */ }, }, - { .name = "fstat", .errmsg = true, .alias = "newfstat", }, - { .name = "fstatat", .errmsg = true, .alias = "newfstatat", }, + .arg_scnprintf = { [0] = SCA_FD, /* fd */ + [1] = SCA_FLOCK, /* cmd */ }, }, + { .name = "fsetxattr", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, + { .name = "fstat", .errmsg = true, .alias = "newfstat", + .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, + { .name = "fstatat", .errmsg = true, .alias = "newfstatat", + .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, }, + { .name = "fstatfs", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, + { .name = "fsync", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, + { .name = "ftruncate", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, { .name = "futex", .errmsg = true, .arg_scnprintf = { [1] = SCA_FUTEX_OP, /* op */ }, }, + { .name = "futimesat", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, }, + { .name = "getdents", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, + { .name = "getdents64", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, { .name = "getitimer", .errmsg = true, STRARRAY(0, which, itimers), }, { .name = "getrlimit", .errmsg = true, STRARRAY(0, resource, rlimit_resources), }, { .name = "ioctl", .errmsg = true, - .arg_scnprintf = { [2] = SCA_HEX, /* arg */ }, }, + .arg_scnprintf = { [0] = SCA_FD, /* fd */ + [2] = SCA_HEX, /* arg */ }, }, { .name = "kill", .errmsg = true, .arg_scnprintf = { [1] = SCA_SIGNUM, /* sig */ }, }, - { .name = "lseek", .errmsg = true, STRARRAY(2, whence, whences), }, + { .name = "linkat", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, }, + { .name = "lseek", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FD, /* fd */ + [2] = SCA_STRARRAY, /* whence */ }, + .arg_parm = { [2] = &strarray__whences, /* whence */ }, }, { .name = "lstat", .errmsg = true, .alias = "newlstat", }, { .name = "madvise", .errmsg = true, .arg_scnprintf = { [0] = SCA_HEX, /* start */ [2] = SCA_MADV_BHV, /* behavior */ }, }, + { .name = "mkdirat", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, }, + { .name = "mknodat", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, }, { .name = "mlock", .errmsg = true, .arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, }, { .name = "mlockall", .errmsg = true, @@ -653,26 +734,45 @@ static struct syscall_fmt { .arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, }, { .name = "munmap", .errmsg = true, .arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, }, + { .name = "name_to_handle_at", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, }, + { .name = "newfstatat", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, }, { .name = "open", .errmsg = true, .arg_scnprintf = { [1] = SCA_OPEN_FLAGS, /* flags */ }, }, { .name = "open_by_handle_at", .errmsg = true, - .arg_scnprintf = { [2] = SCA_OPEN_FLAGS, /* flags */ }, }, + .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ + [2] = SCA_OPEN_FLAGS, /* flags */ }, }, { .name = "openat", .errmsg = true, - .arg_scnprintf = { [2] = SCA_OPEN_FLAGS, /* flags */ }, }, + .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ + [2] = SCA_OPEN_FLAGS, /* flags */ }, }, { .name = "pipe2", .errmsg = true, .arg_scnprintf = { [1] = SCA_PIPE_FLAGS, /* flags */ }, }, { .name = "poll", .errmsg = true, .timeout = true, }, { .name = "ppoll", .errmsg = true, .timeout = true, }, - { .name = "pread", .errmsg = true, .alias = "pread64", }, + { .name = "pread", .errmsg = true, .alias = "pread64", + .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, + { .name = "preadv", .errmsg = true, .alias = "pread", + .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, { .name = "prlimit64", .errmsg = true, STRARRAY(1, resource, rlimit_resources), }, - { .name = "pwrite", .errmsg = true, .alias = "pwrite64", }, - { .name = "read", .errmsg = true, }, + { .name = "pwrite", .errmsg = true, .alias = "pwrite64", + .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, + { .name = "pwritev", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, + { .name = "read", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, + { .name = "readlinkat", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, }, + { .name = "readv", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, { .name = "recvfrom", .errmsg = true, .arg_scnprintf = { [3] = SCA_MSG_FLAGS, /* flags */ }, }, { .name = "recvmmsg", .errmsg = true, .arg_scnprintf = { [3] = SCA_MSG_FLAGS, /* flags */ }, }, { .name = "recvmsg", .errmsg = true, .arg_scnprintf = { [2] = SCA_MSG_FLAGS, /* flags */ }, }, + { .name = "renameat", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, }, { .name = "rt_sigaction", .errmsg = true, .arg_scnprintf = { [0] = SCA_SIGNUM, /* sig */ }, }, { .name = "rt_sigprocmask", .errmsg = true, STRARRAY(0, how, sighow), }, @@ -689,6 +789,8 @@ static struct syscall_fmt { .arg_scnprintf = { [3] = SCA_MSG_FLAGS, /* flags */ }, }, { .name = "setitimer", .errmsg = true, STRARRAY(0, which, itimers), }, { .name = "setrlimit", .errmsg = true, STRARRAY(0, resource, rlimit_resources), }, + { .name = "shutdown", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, { .name = "socket", .errmsg = true, .arg_scnprintf = { [0] = SCA_STRARRAY, /* family */ [1] = SCA_SK_TYPE, /* type */ }, @@ -698,11 +800,21 @@ static struct syscall_fmt { [1] = SCA_SK_TYPE, /* type */ }, .arg_parm = { [0] = &strarray__socket_families, /* family */ }, }, { .name = "stat", .errmsg = true, .alias = "newstat", }, + { .name = "symlinkat", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, }, { .name = "tgkill", .errmsg = true, .arg_scnprintf = { [2] = SCA_SIGNUM, /* sig */ }, }, { .name = "tkill", .errmsg = true, .arg_scnprintf = { [1] = SCA_SIGNUM, /* sig */ }, }, { .name = "uname", .errmsg = true, .alias = "newuname", }, + { .name = "unlinkat", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, }, + { .name = "utimensat", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FDAT, /* dirfd */ }, }, + { .name = "write", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, + { .name = "writev", .errmsg = true, + .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, }; static int syscall_fmt__cmp(const void *name, const void *fmtp) @@ -747,11 +859,20 @@ struct thread_trace { unsigned long nr_events; char *entry_str; double runtime_ms; + struct { + int max; + char **table; + } paths; }; static struct thread_trace *thread_trace__new(void) { - return zalloc(sizeof(struct thread_trace)); + struct thread_trace *ttrace = zalloc(sizeof(struct thread_trace)); + + if (ttrace) + ttrace->paths.max = -1; + + return ttrace; } static struct thread_trace *thread__trace(struct thread *thread, FILE *fp) @@ -792,6 +913,7 @@ struct trace { unsigned long nr_events; struct strlist *ev_qualifier; bool not_ev_qualifier; + bool live; struct intlist *tid_list; struct intlist *pid_list; bool sched; @@ -801,6 +923,98 @@ struct trace { double runtime_ms; }; +static int thread__read_fd_path(struct thread *thread, int fd) +{ + struct thread_trace *ttrace = thread->priv; + char linkname[PATH_MAX], pathname[PATH_MAX]; + struct stat st; + int ret; + + if (thread->pid_ == thread->tid) { + scnprintf(linkname, sizeof(linkname), + "/proc/%d/fd/%d", thread->pid_, fd); + } else { + scnprintf(linkname, sizeof(linkname), + "/proc/%d/task/%d/fd/%d", thread->pid_, thread->tid, fd); + } + + if (lstat(linkname, &st) < 0 || st.st_size + 1 > (off_t)sizeof(pathname)) + return -1; + + ret = readlink(linkname, pathname, sizeof(pathname)); + + if (ret < 0 || ret > st.st_size) + return -1; + + pathname[ret] = '\0'; + + if (fd > ttrace->paths.max) { + char **npath = realloc(ttrace->paths.table, (fd + 1) * sizeof(char *)); + + if (npath == NULL) + return -1; + + if (ttrace->paths.max != -1) { + memset(npath + ttrace->paths.max + 1, 0, + (fd - ttrace->paths.max) * sizeof(char *)); + } else { + memset(npath, 0, (fd + 1) * sizeof(char *)); + } + + ttrace->paths.table = npath; + ttrace->paths.max = fd; + } + + ttrace->paths.table[fd] = strdup(pathname); + + return ttrace->paths.table[fd] != NULL ? 0 : -1; +} + +static const char *thread__fd_path(struct thread *thread, int fd, bool live) +{ + struct thread_trace *ttrace = thread->priv; + + if (ttrace == NULL) + return NULL; + + if (fd < 0) + return NULL; + + if ((fd > ttrace->paths.max || ttrace->paths.table[fd] == NULL) && + (!live || thread__read_fd_path(thread, fd))) + return NULL; + + return ttrace->paths.table[fd]; +} + +static size_t syscall_arg__scnprintf_fd(char *bf, size_t size, + struct syscall_arg *arg) +{ + int fd = arg->val; + size_t printed = scnprintf(bf, size, "%d", fd); + const char *path = thread__fd_path(arg->thread, fd, arg->trace->live); + + if (path) + printed += scnprintf(bf + printed, size - printed, "<%s>", path); + + return printed; +} + +static size_t syscall_arg__scnprintf_close_fd(char *bf, size_t size, + struct syscall_arg *arg) +{ + int fd = arg->val; + size_t printed = syscall_arg__scnprintf_fd(bf, size, arg); + struct thread_trace *ttrace = arg->thread->priv; + + if (ttrace && fd >= 0 && fd <= ttrace->paths.max) { + free(ttrace->paths.table[fd]); + ttrace->paths.table[fd] = NULL; + } + + return printed; +} + static bool trace__filter_duration(struct trace *trace, double t) { return t < (trace->duration_filter * NSEC_PER_MSEC); @@ -969,7 +1183,8 @@ static int trace__read_syscall_info(struct trace *trace, int id) } static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, - unsigned long *args) + unsigned long *args, struct trace *trace, + struct thread *thread) { size_t printed = 0; @@ -977,8 +1192,10 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, struct format_field *field; u8 bit = 1; struct syscall_arg arg = { - .idx = 0, - .mask = 0, + .idx = 0, + .mask = 0, + .trace = trace, + .thread = thread, }; for (field = sc->tp_format->format.fields->next; field; @@ -1111,7 +1328,8 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, msg = ttrace->entry_str; printed += scnprintf(msg + printed, 1024 - printed, "%s(", sc->name); - printed += syscall__scnprintf_args(sc, msg + printed, 1024 - printed, args); + printed += syscall__scnprintf_args(sc, msg + printed, 1024 - printed, + args, trace, thread); if (!strcmp(sc->name, "exit_group") || !strcmp(sc->name, "exit")) { if (!trace->duration_filter) { @@ -1292,6 +1510,8 @@ static int trace__run(struct trace *trace, int argc, const char **argv) unsigned long before; const bool forks = argc > 0; + trace->live = true; + if (evlist == NULL) { fprintf(trace->output, "Not enough memory to run!\n"); goto out; @@ -1423,6 +1643,7 @@ out_delete_maps: out_delete_evlist: perf_evlist__delete(evlist); out: + trace->live = false; return err; } -- cgit v0.10.2 From 65cd4f6c99c1170bd0114dbd71b978012ea44d28 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 18 Jul 2013 16:21:18 -0700 Subject: arch_timer: Move to generic sched_clock framework Register with the generic sched_clock framework now that it supports 64 bits. This fixes two problems with the current sched_clock support for machines using the architected timers. First off, we don't subtract the start value from subsequent sched_clock calls so we can potentially start off with sched_clock returning gigantic numbers. Second, there is no support for suspend/resume handling so problems such as discussed in 6a4dae5 (ARM: 7565/1: sched: stop sched_clock() during suspend, 2012-10-23) can happen without this patch. Finally, it allows us to move the sched_clock setup into drivers clocksource out of the arch ports. Cc: Christopher Covington Cc: Catalin Marinas Acked-by: Will Deacon Signed-off-by: Stephen Boyd Signed-off-by: John Stultz diff --git a/arch/arm/kernel/arch_timer.c b/arch/arm/kernel/arch_timer.c index 221f07b..1791f12 100644 --- a/arch/arm/kernel/arch_timer.c +++ b/arch/arm/kernel/arch_timer.c @@ -11,7 +11,6 @@ #include #include #include -#include #include @@ -22,13 +21,6 @@ static unsigned long arch_timer_read_counter_long(void) return arch_timer_read_counter(); } -static u32 sched_clock_mult __read_mostly; - -static unsigned long long notrace arch_timer_sched_clock(void) -{ - return arch_timer_read_counter() * sched_clock_mult; -} - static struct delay_timer arch_delay_timer; static void __init arch_timer_delay_timer_register(void) @@ -48,11 +40,5 @@ int __init arch_timer_arch_init(void) arch_timer_delay_timer_register(); - /* Cache the sched_clock multiplier to save a divide in the hot path. */ - sched_clock_mult = NSEC_PER_SEC / arch_timer_rate; - sched_clock_func = arch_timer_sched_clock; - pr_info("sched_clock: ARM arch timer >56 bits at %ukHz, resolution %uns\n", - arch_timer_rate / 1000, sched_clock_mult); - return 0; } diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index c044548..35fd0eb 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -14,6 +14,7 @@ config ARM64 select GENERIC_IOMAP select GENERIC_IRQ_PROBE select GENERIC_IRQ_SHOW + select GENERIC_SCHED_CLOCK select GENERIC_SMP_IDLE_THREAD select GENERIC_TIME_VSYSCALL select HARDIRQS_SW_RESEND diff --git a/arch/arm64/kernel/time.c b/arch/arm64/kernel/time.c index 03dc371..29c39d5 100644 --- a/arch/arm64/kernel/time.c +++ b/arch/arm64/kernel/time.c @@ -61,13 +61,6 @@ unsigned long profile_pc(struct pt_regs *regs) EXPORT_SYMBOL(profile_pc); #endif -static u64 sched_clock_mult __read_mostly; - -unsigned long long notrace sched_clock(void) -{ - return arch_timer_read_counter() * sched_clock_mult; -} - void __init time_init(void) { u32 arch_timer_rate; @@ -78,9 +71,6 @@ void __init time_init(void) if (!arch_timer_rate) panic("Unable to initialise architected timer.\n"); - /* Cache the sched_clock multiplier to save a divide in the hot path. */ - sched_clock_mult = NSEC_PER_SEC / arch_timer_rate; - /* Calibrate the delay loop directly */ lpj_fine = arch_timer_rate / HZ; } diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index fbd9ccd..5d52789 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -471,6 +472,15 @@ static int __init arch_timer_register(void) goto out; } + clocksource_register_hz(&clocksource_counter, arch_timer_rate); + cyclecounter.mult = clocksource_counter.mult; + cyclecounter.shift = clocksource_counter.shift; + timecounter_init(&timecounter, &cyclecounter, + arch_counter_get_cntvct()); + + /* 56 bits minimum, so we assume worst case rollover */ + sched_clock_register(arch_timer_read_counter, 56, arch_timer_rate); + if (arch_timer_use_virtual) { ppi = arch_timer_ppi[VIRT_PPI]; err = request_percpu_irq(ppi, arch_timer_handler_virt, -- cgit v0.10.2 From b4042ceaabbd913bc5b397ddd1e396eeb312d72f Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 18 Jul 2013 16:21:19 -0700 Subject: sched_clock: Remove sched_clock_func() hook Nobody is using sched_clock_func() anymore now that sched_clock supports up to 64 bits. Remove the hook so that new code only uses sched_clock_register(). Signed-off-by: Stephen Boyd Signed-off-by: John Stultz diff --git a/include/linux/sched_clock.h b/include/linux/sched_clock.h index eca7abe..cddf0c2 100644 --- a/include/linux/sched_clock.h +++ b/include/linux/sched_clock.h @@ -18,6 +18,4 @@ extern void setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate); extern void sched_clock_register(u64 (*read)(void), int bits, unsigned long rate); -extern unsigned long long (*sched_clock_func)(void); - #endif diff --git a/kernel/time/sched_clock.c b/kernel/time/sched_clock.c index f388bae..68b7993 100644 --- a/kernel/time/sched_clock.c +++ b/kernel/time/sched_clock.c @@ -63,7 +63,7 @@ static inline u64 notrace cyc_to_ns(u64 cyc, u32 mult, u32 shift) return (cyc * mult) >> shift; } -static unsigned long long notrace sched_clock_32(void) +unsigned long long notrace sched_clock(void) { u64 epoch_ns; u64 epoch_cyc; @@ -170,13 +170,6 @@ void __init setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate) sched_clock_register(read_sched_clock_32_wrapper, bits, rate); } -unsigned long long __read_mostly (*sched_clock_func)(void) = sched_clock_32; - -unsigned long long notrace sched_clock(void) -{ - return sched_clock_func(); -} - void __init sched_clock_postinit(void) { /* -- cgit v0.10.2 From 6a903a2551ef778d60ce4341722d611144251398 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 27 Aug 2013 21:41:56 -0500 Subject: of: introduce common FDT machine related functions Introduce common of_flat_dt_match_machine and of_flat_dt_get_machine_name functions to unify architectures' handling of machine level model and compatible properties. Several architectures match the root compatible string with an arch specific list of machine descriptors duplicating the same search algorithm. Create a common implementation with a simple architecture specific hook to iterate over each machine's match table. Signed-off-by: Rob Herring Acked-by: Grant Likely diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 5f4cc88..5c47910 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -619,6 +619,66 @@ int __init of_scan_flat_dt_by_path(const char *path, return ret; } +const char * __init of_flat_dt_get_machine_name(void) +{ + const char *name; + unsigned long dt_root = of_get_flat_dt_root(); + + name = of_get_flat_dt_prop(dt_root, "model", NULL); + if (!name) + name = of_get_flat_dt_prop(dt_root, "compatible", NULL); + return name; +} + +/** + * of_flat_dt_match_machine - Iterate match tables to find matching machine. + * + * @default_match: A machine specific ptr to return in case of no match. + * @get_next_compat: callback function to return next compatible match table. + * + * Iterate through machine match tables to find the best match for the machine + * compatible string in the FDT. + */ +const void * __init of_flat_dt_match_machine(const void *default_match, + const void * (*get_next_compat)(const char * const**)) +{ + const void *data = NULL; + const void *best_data = default_match; + const char *const *compat; + unsigned long dt_root; + unsigned int best_score = ~1, score = 0; + + dt_root = of_get_flat_dt_root(); + while ((data = get_next_compat(&compat))) { + score = of_flat_dt_match(dt_root, compat); + if (score > 0 && score < best_score) { + best_data = data; + best_score = score; + } + } + if (!best_data) { + const char *prop; + long size; + + pr_err("\n unrecognized device tree list:\n[ "); + + prop = of_get_flat_dt_prop(dt_root, "compatible", &size); + if (prop) { + while (size > 0) { + printk("'%s' ", prop); + size -= strlen(prop) + 1; + prop += strlen(prop) + 1; + } + } + printk("]\n\n"); + return NULL; + } + + pr_info("Machine model: %s\n", of_flat_dt_get_machine_name()); + + return best_data; +} + #ifdef CONFIG_BLK_DEV_INITRD /** * early_init_dt_check_for_initrd - Decode initrd location from flat tree diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index b365f5a..0beaee9 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -108,11 +108,16 @@ extern int early_init_dt_scan_root(unsigned long node, const char *uname, extern bool early_init_dt_scan(void *params); +extern const char *of_flat_dt_get_machine_name(void); +extern const void *of_flat_dt_match_machine(const void *default_match, + const void * (*get_next_compat)(const char * const**)); + /* Other Prototypes */ extern void unflatten_device_tree(void); extern void unflatten_and_copy_device_tree(void); extern void early_init_devtree(void *); #else /* CONFIG_OF_FLATTREE */ +static inline const char *of_flat_dt_get_machine_name(void) { return NULL; } static inline void unflatten_device_tree(void) {} static inline void unflatten_and_copy_device_tree(void) {} #endif /* CONFIG_OF_FLATTREE */ -- cgit v0.10.2 From 880beb8807814c7785680bcbdc81dcf076f585db Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 27 Aug 2013 21:43:12 -0500 Subject: arc: use common of_flat_dt_match_machine Convert arc to use the common of_flat_dt_match_machine function. Signed-off-by: Rob Herring Acked-by: Vineet Gupta diff --git a/arch/arc/include/asm/mach_desc.h b/arch/arc/include/asm/mach_desc.h index d3e9c0a..e8993a2 100644 --- a/arch/arc/include/asm/mach_desc.h +++ b/arch/arc/include/asm/mach_desc.h @@ -51,22 +51,12 @@ struct machine_desc { /* * Current machine - only accessible during boot. */ -extern struct machine_desc *machine_desc; +extern const struct machine_desc *machine_desc; /* * Machine type table - also only accessible during boot */ -extern struct machine_desc __arch_info_begin[], __arch_info_end[]; -#define for_each_machine_desc(p) \ - for (p = __arch_info_begin; p < __arch_info_end; p++) - -static inline struct machine_desc *default_machine_desc(void) -{ - /* the default machine is the last one linked in */ - if (__arch_info_end - 1 < __arch_info_begin) - return NULL; - return __arch_info_end - 1; -} +extern const struct machine_desc __arch_info_begin[], __arch_info_end[]; /* * Set of macros to define architecture features. @@ -81,6 +71,6 @@ __attribute__((__section__(".arch.info.init"))) = { \ #define MACHINE_END \ }; -extern struct machine_desc *setup_machine_fdt(void *dt); +extern const struct machine_desc *setup_machine_fdt(void *dt); #endif diff --git a/arch/arc/kernel/devtree.c b/arch/arc/kernel/devtree.c index 1ab6f35..d7b5322 100644 --- a/arch/arc/kernel/devtree.c +++ b/arch/arc/kernel/devtree.c @@ -18,6 +18,19 @@ #include #include +static const void * __init arch_get_next_mach(const char *const **match) +{ + static const struct machine_desc *mdesc = __arch_info_begin; + const struct machine_desc *m = mdesc; + + if (m >= __arch_info_end) + return NULL; + + mdesc++; + *match = m->dt_compat; + return m; +} + /** * setup_machine_fdt - Machine setup when an dtb was passed to the kernel * @dt: virtual address pointer to dt blob @@ -25,66 +38,24 @@ * If a dtb was passed to the kernel, then use it to choose the correct * machine_desc and to setup the system. */ -struct machine_desc * __init setup_machine_fdt(void *dt) +const struct machine_desc * __init setup_machine_fdt(void *dt) { - struct machine_desc *mdesc = NULL, *mdesc_best = NULL; - unsigned int score, mdesc_score = ~1; + const struct machine_desc *mdesc; unsigned long dt_root; - const char *model, *compat; void *clk; - char manufacturer[16]; unsigned long len; if (!early_init_dt_scan(dt)) return NULL; - dt_root = of_get_flat_dt_root(); - - /* - * The kernel could be multi-platform enabled, thus could have many - * "baked-in" machine descriptors. Search thru all for the best - * "compatible" string match. - */ - for_each_machine_desc(mdesc) { - score = of_flat_dt_match(dt_root, mdesc->dt_compat); - if (score > 0 && score < mdesc_score) { - mdesc_best = mdesc; - mdesc_score = score; - } - } - if (!mdesc_best) { - const char *prop; - long size; - - pr_err("\n unrecognized device tree list:\n[ "); - - prop = of_get_flat_dt_prop(dt_root, "compatible", &size); - if (prop) { - while (size > 0) { - printk("'%s' ", prop); - size -= strlen(prop) + 1; - prop += strlen(prop) + 1; - } - } - printk("]\n\n"); - + mdesc = of_flat_dt_match_machine(NULL, arch_get_next_mach); + if (!mdesc) machine_halt(); - } - - /* compat = "," */ - compat = mdesc_best->dt_compat[0]; - - model = strchr(compat, ','); - if (model) - model++; - - strlcpy(manufacturer, compat, model ? model - compat : strlen(compat)); - - pr_info("Board \"%s\" from %s (Manufacturer)\n", model, manufacturer); + dt_root = of_get_flat_dt_root(); clk = of_get_flat_dt_prop(dt_root, "clock-frequency", &len); if (clk) arc_set_core_freq(of_read_ulong(clk, len/4)); - return mdesc_best; + return mdesc; } diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c index 710bf89..8a9cf4e 100644 --- a/arch/arc/kernel/setup.c +++ b/arch/arc/kernel/setup.c @@ -31,7 +31,7 @@ int running_on_hw = 1; /* vs. on ISS */ char __initdata command_line[COMMAND_LINE_SIZE]; -struct machine_desc *machine_desc; +const struct machine_desc *machine_desc; struct task_struct *_current_task[NR_CPUS]; /* For stack switching */ -- cgit v0.10.2 From 6d67a9f672f4718a3a9060a96f859d0465663b55 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 27 Aug 2013 21:43:49 -0500 Subject: arm: use common of_flat_dt_match_machine Convert arm to use the common of_flat_dt_match_machine function. Signed-off-by: Rob Herring Cc: Russell King Cc: linux-arm-kernel@lists.infradead.org diff --git a/arch/arm/kernel/devtree.c b/arch/arm/kernel/devtree.c index e7ce175..739c3df 100644 --- a/arch/arm/kernel/devtree.c +++ b/arch/arm/kernel/devtree.c @@ -174,6 +174,19 @@ bool arch_match_cpu_phys_id(int cpu, u64 phys_id) return (phys_id & MPIDR_HWID_BITMASK) == cpu_logical_map(cpu); } +static const void * __init arch_get_next_mach(const char *const **match) +{ + static const struct machine_desc *mdesc = __arch_info_begin; + const struct machine_desc *m = mdesc; + + if (m >= __arch_info_end) + return NULL; + + mdesc++; + *match = m->dt_compat; + return m; +} + /** * setup_machine_fdt - Machine setup when an dtb was passed to the kernel * @dt_phys: physical address of dt blob @@ -184,9 +197,6 @@ bool arch_match_cpu_phys_id(int cpu, u64 phys_id) const struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys) { const struct machine_desc *mdesc, *mdesc_best = NULL; - unsigned int score, mdesc_score = ~1; - unsigned long dt_root; - const char *model; #ifdef CONFIG_ARCH_MULTIPLATFORM DT_MACHINE_START(GENERIC_DT, "Generic DT based system") @@ -198,23 +208,17 @@ const struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys) if (!dt_phys || !early_init_dt_scan(phys_to_virt(dt_phys))) return NULL; + mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach); - /* Search the mdescs for the 'best' compatible value match */ - dt_root = of_get_flat_dt_root(); - for_each_machine_desc(mdesc) { - score = of_flat_dt_match(dt_root, mdesc->dt_compat); - if (score > 0 && score < mdesc_score) { - mdesc_best = mdesc; - mdesc_score = score; - } - } - if (!mdesc_best) { + if (!mdesc) { const char *prop; long size; + unsigned long dt_root; early_print("\nError: unrecognized/unsupported " "device tree compatible list:\n[ "); + dt_root = of_get_flat_dt_root(); prop = of_get_flat_dt_prop(dt_root, "compatible", &size); while (size > 0) { early_print("'%s' ", prop); @@ -226,15 +230,8 @@ const struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys) dump_machine_table(); /* does not return */ } - model = of_get_flat_dt_prop(dt_root, "model", NULL); - if (!model) - model = of_get_flat_dt_prop(dt_root, "compatible", NULL); - if (!model) - model = ""; - pr_info("Machine: %s, model: %s\n", mdesc_best->name, model); - /* Change machine number to match the mdesc we're using */ - __machine_arch_type = mdesc_best->nr; + __machine_arch_type = mdesc->nr; - return mdesc_best; + return mdesc; } -- cgit v0.10.2 From f2b99bccae87de82a5d03838abe85d604ee7e525 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 27 Aug 2013 21:44:37 -0500 Subject: arm64: use common of_flat_dt_get_machine_name Convert arm64 to use the common of_flat_dt_get_machine_name function. Signed-off-by: Rob Herring Acked-by: Catalin Marinas Cc: Will Deacon Cc: linux-arm-kernel@lists.infradead.org diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index 7feb0c9..a4ed2d3 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -124,8 +124,6 @@ static void __init setup_processor(void) static void __init setup_machine_fdt(phys_addr_t dt_phys) { - unsigned long dt_root; - if (!dt_phys || !early_init_dt_scan(phys_to_virt(dt_phys))) { early_print("\n" "Error: invalid device tree blob at physical address 0x%p (virtual address 0x%p)\n" @@ -137,14 +135,7 @@ static void __init setup_machine_fdt(phys_addr_t dt_phys) cpu_relax(); } - dt_root = of_get_flat_dt_root(); - - machine_name = of_get_flat_dt_prop(dt_root, "model", NULL); - if (!machine_name) - machine_name = of_get_flat_dt_prop(dt_root, "compatible", NULL); - if (!machine_name) - machine_name = ""; - pr_info("Machine: %s\n", machine_name); + machine_name = of_flat_dt_get_machine_name(); } /* -- cgit v0.10.2 From 986c494b3c05bac5eace21f8e3a42f2afbfcbd34 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 25 Sep 2013 15:49:28 -0500 Subject: metag: use common of_flat_dt_match_machine Convert metag to use the common of_flat_dt_get_machine_name function. Signed-off-by: Rob Herring [james.hogan: fix missing arch_get_next_mach and const mismatch] Reported-by: Guenter Roeck Signed-off-by: James Hogan diff --git a/arch/metag/include/asm/mach/arch.h b/arch/metag/include/asm/mach/arch.h index 12c5664..433f946 100644 --- a/arch/metag/include/asm/mach/arch.h +++ b/arch/metag/include/asm/mach/arch.h @@ -53,7 +53,7 @@ struct machine_desc { /* * Current machine - only accessible during boot. */ -extern struct machine_desc *machine_desc; +extern const struct machine_desc *machine_desc; /* * Machine type table - also only accessible during boot diff --git a/arch/metag/include/asm/prom.h b/arch/metag/include/asm/prom.h index d2aa35d..9f67cbf 100644 --- a/arch/metag/include/asm/prom.h +++ b/arch/metag/include/asm/prom.h @@ -17,7 +17,7 @@ #include #define HAVE_ARCH_DEVTREE_FIXUPS -extern struct machine_desc *setup_machine_fdt(void *dt); +extern const struct machine_desc *setup_machine_fdt(void *dt); extern void copy_fdt(void); #endif /* __ASM_METAG_PROM_H */ diff --git a/arch/metag/kernel/devtree.c b/arch/metag/kernel/devtree.c index 68c2fee..18dd7ae 100644 --- a/arch/metag/kernel/devtree.c +++ b/arch/metag/kernel/devtree.c @@ -34,6 +34,19 @@ void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) return alloc_bootmem_align(size, align); } +static const void * __init arch_get_next_mach(const char *const **match) +{ + static const struct machine_desc *mdesc = __arch_info_begin; + const struct machine_desc *m = mdesc; + + if (m >= __arch_info_end) + return NULL; + + mdesc++; + *match = m->dt_compat; + return m; +} + /** * setup_machine_fdt - Machine setup when an dtb was passed to the kernel * @dt: virtual address pointer to dt blob @@ -41,53 +54,18 @@ void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) * If a dtb was passed to the kernel, then use it to choose the correct * machine_desc and to setup the system. */ -struct machine_desc * __init setup_machine_fdt(void *dt) +const struct machine_desc * __init setup_machine_fdt(void *dt) { - struct machine_desc *mdesc, *mdesc_best = NULL; - unsigned int score, mdesc_score = ~1; - unsigned long dt_root; - const char *model; + const struct machine_desc *mdesc; /* check device tree validity */ if (!early_init_dt_scan(dt)) return NULL; - /* Search the mdescs for the 'best' compatible value match */ - dt_root = of_get_flat_dt_root(); - - for_each_machine_desc(mdesc) { - score = of_flat_dt_match(dt_root, mdesc->dt_compat); - if (score > 0 && score < mdesc_score) { - mdesc_best = mdesc; - mdesc_score = score; - } - } - if (!mdesc_best) { - const char *prop; - long size; - - pr_err("\nError: unrecognized/unsupported device tree compatible list:\n[ "); - - prop = of_get_flat_dt_prop(dt_root, "compatible", &size); - if (prop) { - while (size > 0) { - printk("'%s' ", prop); - size -= strlen(prop) + 1; - prop += strlen(prop) + 1; - } - } - printk("]\n\n"); - + mdesc = of_flat_dt_match_machine(NULL, arch_get_next_mach); + if (!mdesc) dump_machine_table(); /* does not return */ - } - - model = of_get_flat_dt_prop(dt_root, "model", NULL); - if (!model) - model = of_get_flat_dt_prop(dt_root, "compatible", NULL); - if (!model) - model = ""; - pr_info("Machine: %s, model: %s\n", mdesc_best->name, model); - - return mdesc_best; + pr_info("Machine name: %s\n", mdesc->name); + return mdesc; } diff --git a/arch/metag/kernel/setup.c b/arch/metag/kernel/setup.c index 2c697d3..92cc119 100644 --- a/arch/metag/kernel/setup.c +++ b/arch/metag/kernel/setup.c @@ -115,7 +115,7 @@ extern u32 __dtb_start[]; extern struct console dash_console; #endif -struct machine_desc *machine_desc __initdata; +const struct machine_desc *machine_desc __initdata; /* * Map a Linux CPU number to a hardware thread ID -- cgit v0.10.2 From 97e7f455434704b4d42f918ac3de76f593e642c1 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 28 Aug 2013 09:56:40 -0500 Subject: mips: use common of_flat_dt_get_machine_name Convert mips to use the common of_flat_dt_get_machine_name function. Signed-off-by: Rob Herring Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Acked-by: John Crispin diff --git a/arch/mips/kernel/prom.c b/arch/mips/kernel/prom.c index 0b2485f..3c3b0df 100644 --- a/arch/mips/kernel/prom.c +++ b/arch/mips/kernel/prom.c @@ -47,24 +47,11 @@ void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) return __alloc_bootmem(size, align, __pa(MAX_DMA_ADDRESS)); } -int __init early_init_dt_scan_model(unsigned long node, const char *uname, - int depth, void *data) -{ - if (!depth) { - char *model = of_get_flat_dt_prop(node, "model", NULL); - - if (model) - mips_set_machine_name(model); - } - return 0; -} - void __init __dt_setup_arch(struct boot_param_header *bph) { if (!early_init_dt_scan(bph)) return; - /* try to load the mips machine name */ - of_scan_flat_dt(early_init_dt_scan_model, NULL); + mips_set_machine_name(of_flat_dt_get_machine_name()); } #endif -- cgit v0.10.2 From fc84b0c3dc5dd42c6d040dcd11571c10b690d318 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Sat, 7 Sep 2013 16:35:13 -0500 Subject: drivers: remove unnecessary prom.h includes Remove unnecessary prom.h includes in preparation to remove implicit includes of prom.h. Signed-off-by: Rob Herring Acked-by: Marc Kleine-Budde Acked-by: Grant Likely Cc: Wolfgang Grandegger Cc: linux-can@vger.kernel.org Cc: netdev@vger.kernel.org diff --git a/drivers/net/can/grcan.c b/drivers/net/can/grcan.c index 6aa737a..ab506d6 100644 --- a/drivers/net/can/grcan.c +++ b/drivers/net/can/grcan.c @@ -34,10 +34,7 @@ #include #include #include - #include -#include - #include #include diff --git a/drivers/net/can/sja1000/sja1000_of_platform.c b/drivers/net/can/sja1000/sja1000_of_platform.c index 31ad339..047accd 100644 --- a/drivers/net/can/sja1000/sja1000_of_platform.c +++ b/drivers/net/can/sja1000/sja1000_of_platform.c @@ -44,7 +44,6 @@ #include #include #include -#include #include "sja1000.h" -- cgit v0.10.2 From 9cae09c5a9940b9e02474be0639f0b49bd592a33 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 9 Sep 2013 18:13:46 -0500 Subject: of: remove unnecessary prom.h includes Remove unnecessary prom.h includes in preparation to make prom.h optional. Signed-off-by: Rob Herring Acked-by: Grant Likely diff --git a/drivers/of/of_pci_irq.c b/drivers/of/of_pci_irq.c index 6770538..d860764 100644 --- a/drivers/of/of_pci_irq.c +++ b/drivers/of/of_pci_irq.c @@ -2,7 +2,6 @@ #include #include #include -#include /** * of_irq_map_pci - Resolve the interrupt for a PCI device diff --git a/drivers/of/pdt.c b/drivers/of/pdt.c index 4ec19cb..7b66673 100644 --- a/drivers/of/pdt.c +++ b/drivers/of/pdt.c @@ -22,7 +22,6 @@ #include #include #include -#include static struct of_pdt_ops *of_pdt_prom_ops __initdata; -- cgit v0.10.2 From c0c376687ba979a6c2f2626d5a1eb94ece44f7b4 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Sun, 8 Sep 2013 14:28:38 -0500 Subject: ARM: keystone: remove unnecessary prom.h include Remove unnecessary prom.h include in preparation to make prom.h optional. Signed-off-by: Rob Herring Acked-by: Grant Likely Cc: Santosh Shilimkar diff --git a/arch/arm/mach-keystone/platsmp.c b/arch/arm/mach-keystone/platsmp.c index c1229615..5cf0683 100644 --- a/arch/arm/mach-keystone/platsmp.c +++ b/arch/arm/mach-keystone/platsmp.c @@ -17,7 +17,6 @@ #include #include -#include #include "keystone.h" -- cgit v0.10.2 From e2d1c994f7194e933236bd874b2d7f31b678bb94 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Sat, 7 Sep 2013 14:57:42 -0500 Subject: arm64: remove unnecessary prom.h include Remove unnecessary prom.h include in preparation to make prom.h optional. Signed-off-by: Rob Herring Acked-by: Catalin Marinas Acked-by: Grant Likely Cc: Will Deacon diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c index 8261f4e..0cb8742 100644 --- a/arch/arm64/mm/init.c +++ b/arch/arm64/mm/init.c @@ -31,7 +31,6 @@ #include #include -#include #include #include #include -- cgit v0.10.2 From be5973117bd48624d9e7d7728d754f4c9a322193 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Sun, 8 Sep 2013 14:26:28 -0500 Subject: arc: remove unnecessary prom.h includes Remove unnecessary prom.h includes in preparation to remove prom.h. Signed-off-by: Rob Herring Acked-by: Vineet Gupta Acked-by: Grant Likely diff --git a/arch/arc/kernel/devtree.c b/arch/arc/kernel/devtree.c index d7b5322..b6dc4e2 100644 --- a/arch/arc/kernel/devtree.c +++ b/arch/arc/kernel/devtree.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c index 8a9cf4e..f7d4a41 100644 --- a/arch/arc/kernel/setup.c +++ b/arch/arc/kernel/setup.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include -- cgit v0.10.2 From 0b81430590882954ed60a138ac2bad1149303165 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Sun, 8 Sep 2013 14:27:41 -0500 Subject: hexagon: remove unnecessary prom.h includes Hexagon does not have a prom.h header, so it is probably broken with CONFIG_OF enabled. In any case, remove unnecessary prom.h include in preparation to make prom.h optional. Signed-off-by: Rob Herring Acked-by: Richard Kuo Acked-by: Grant Likely Cc: linux-hexagon@vger.kernel.org diff --git a/arch/hexagon/kernel/setup.c b/arch/hexagon/kernel/setup.c index 29d1f1b..0e7c1db 100644 --- a/arch/hexagon/kernel/setup.c +++ b/arch/hexagon/kernel/setup.c @@ -32,9 +32,6 @@ #include #include #include -#ifdef CONFIG_OF -#include -#endif char cmd_line[COMMAND_LINE_SIZE]; static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE; -- cgit v0.10.2 From 7b009939af9bc7b698d7ddec03d53604efff881c Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Sun, 8 Sep 2013 14:27:53 -0500 Subject: metag: move setup_machine_fdt declaration from prom.h Move setup_machine_fdt out of prom.h and into asm/setup.h in preparation to remove prom.h. Signed-off-by: Rob Herring Acked-by: Grant Likely Cc: James Hogan Cc: linux-metag@vger.kernel.org diff --git a/arch/metag/include/asm/prom.h b/arch/metag/include/asm/prom.h index 9f67cbf..e19022c 100644 --- a/arch/metag/include/asm/prom.h +++ b/arch/metag/include/asm/prom.h @@ -14,10 +14,8 @@ #ifndef __ASM_METAG_PROM_H #define __ASM_METAG_PROM_H -#include #define HAVE_ARCH_DEVTREE_FIXUPS -extern const struct machine_desc *setup_machine_fdt(void *dt); extern void copy_fdt(void); #endif /* __ASM_METAG_PROM_H */ diff --git a/arch/metag/include/asm/setup.h b/arch/metag/include/asm/setup.h index e13083b..e9fdee9 100644 --- a/arch/metag/include/asm/setup.h +++ b/arch/metag/include/asm/setup.h @@ -3,6 +3,7 @@ #include +extern const struct machine_desc *setup_machine_fdt(void *dt); void per_cpu_trap_init(unsigned long); extern void __init dump_machine_table(void); #endif /* _ASM_METAG_SETUP_H */ diff --git a/arch/metag/kernel/setup.c b/arch/metag/kernel/setup.c index 92cc119..8c4b397 100644 --- a/arch/metag/kernel/setup.c +++ b/arch/metag/kernel/setup.c @@ -42,7 +42,6 @@ #include #include #include -#include #include #include #include -- cgit v0.10.2 From 5c9f303e996516dd0dcc2ce8c2b95504d3137b19 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Sat, 7 Sep 2013 14:05:10 -0500 Subject: microblaze: clean-up prom.h implicit includes While powerpc is a mess of implicit includes by prom.h, microblaze just copied this and is easily fixed. Add the necessary explicit includes and remove unnecessary includes and other parts from prom.h Signed-off-by: Rob Herring Acked-by: Grant Likely Cc: Michal Simek Cc: microblaze-uclinux@itee.uq.edu.au Cc: netdev@vger.kernel.org diff --git a/arch/microblaze/include/asm/prom.h b/arch/microblaze/include/asm/prom.h index 9977816..f05bedc 100644 --- a/arch/microblaze/include/asm/prom.h +++ b/arch/microblaze/include/asm/prom.h @@ -11,17 +11,10 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ - -#include /* linux/of.h gets to determine #include ordering */ - #ifndef _ASM_MICROBLAZE_PROM_H #define _ASM_MICROBLAZE_PROM_H -#ifdef __KERNEL__ -#ifndef __ASSEMBLY__ -#include -#include -#include +#include #define HAVE_ARCH_DEVTREE_FIXUPS @@ -42,23 +35,4 @@ extern unsigned long pci_address_to_pio(phys_addr_t address); #define pci_address_to_pio pci_address_to_pio #endif /* CONFIG_PCI */ -/* Parse the ibm,dma-window property of an OF node into the busno, phys and - * size parameters. - */ -void of_parse_dma_window(struct device_node *dn, const void *dma_window_prop, - unsigned long *busno, unsigned long *phys, unsigned long *size); - -extern void kdump_move_device_tree(void); - -#endif /* __ASSEMBLY__ */ -#endif /* __KERNEL__ */ - -/* These includes are put at the bottom because they may contain things - * that are overridden by this file. Ideally they shouldn't be included - * by this file, but there are a bunch of .c files that currently depend - * on it. Eventually they will be cleaned up. */ -#include -#include -#include - #endif /* _ASM_MICROBLAZE_PROM_H */ diff --git a/arch/microblaze/kernel/prom.c b/arch/microblaze/kernel/prom.c index cab6dc3..abdfb10 100644 --- a/arch/microblaze/kernel/prom.c +++ b/arch/microblaze/kernel/prom.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include diff --git a/arch/microblaze/kernel/setup.c b/arch/microblaze/kernel/setup.c index 6c4efba..8de8ebc 100644 --- a/arch/microblaze/kernel/setup.c +++ b/arch/microblaze/kernel/setup.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include diff --git a/arch/microblaze/kernel/timer.c b/arch/microblaze/kernel/timer.c index e4b3f33..827df4d 100644 --- a/arch/microblaze/kernel/timer.c +++ b/arch/microblaze/kernel/timer.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include diff --git a/arch/microblaze/pci/pci-common.c b/arch/microblaze/pci/pci-common.c index 1b93bf0..7c8a352 100644 --- a/arch/microblaze/pci/pci-common.c +++ b/arch/microblaze/pci/pci-common.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c index b88121f..22478d5 100644 --- a/drivers/net/ethernet/xilinx/ll_temac_main.c +++ b/drivers/net/ethernet/xilinx/ll_temac_main.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include -- cgit v0.10.2 From 5af5073004071cedd0343eee51d77955037ec6f3 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 17 Sep 2013 14:28:33 -0500 Subject: drivers: clean-up prom.h implicit includes Powerpc is a mess of implicit includes by prom.h. Add the necessary explicit includes to drivers in preparation of prom.h cleanup. Signed-off-by: Rob Herring Acked-by: Grant Likely diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index 851bd3f..fb0b40a 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include static unsigned int intr_coalescing_count; diff --git a/drivers/char/bsr.c b/drivers/char/bsr.c index 0671e45..8fedbc2 100644 --- a/drivers/char/bsr.c +++ b/drivers/char/bsr.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include diff --git a/drivers/char/hw_random/pasemi-rng.c b/drivers/char/hw_random/pasemi-rng.c index c6df5b2..c66279b 100644 --- a/drivers/char/hw_random/pasemi-rng.c +++ b/drivers/char/hw_random/pasemi-rng.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include diff --git a/drivers/cpufreq/pasemi-cpufreq.c b/drivers/cpufreq/pasemi-cpufreq.c index 534e43a..f4ec814 100644 --- a/drivers/cpufreq/pasemi-cpufreq.c +++ b/drivers/cpufreq/pasemi-cpufreq.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c index b010d42..3054870 100644 --- a/drivers/crypto/caam/ctrl.c +++ b/drivers/crypto/caam/ctrl.c @@ -5,6 +5,9 @@ * Copyright 2008-2012 Freescale Semiconductor, Inc. */ +#include +#include + #include "compat.h" #include "regs.h" #include "intern.h" diff --git a/drivers/crypto/caam/jr.c b/drivers/crypto/caam/jr.c index 105ba4d..94b80a9 100644 --- a/drivers/crypto/caam/jr.c +++ b/drivers/crypto/caam/jr.c @@ -5,6 +5,8 @@ * Copyright 2008-2012 Freescale Semiconductor, Inc. */ +#include + #include "compat.h" #include "regs.h" #include "jr.h" diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index 661dc3e..6cd0e60 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/drivers/dma/bestcomm/sram.c b/drivers/dma/bestcomm/sram.c index 5e2ed30..2074e0e 100644 --- a/drivers/dma/bestcomm/sram.c +++ b/drivers/dma/bestcomm/sram.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index b3f3e90..61517dd 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include "dmaengine.h" diff --git a/drivers/dma/mpc512x_dma.c b/drivers/dma/mpc512x_dma.c index 2fe4353..448750d 100644 --- a/drivers/dma/mpc512x_dma.c +++ b/drivers/dma/mpc512x_dma.c @@ -39,7 +39,9 @@ #include #include #include +#include #include +#include #include #include diff --git a/drivers/edac/cell_edac.c b/drivers/edac/cell_edac.c index c2eaf33..9ee1c76 100644 --- a/drivers/edac/cell_edac.c +++ b/drivers/edac/cell_edac.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index a0b33a2..de9630b 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c index ff3caa0..f744410 100644 --- a/drivers/i2c/busses/i2c-ibm_iic.c +++ b/drivers/i2c/busses/i2c-ibm_iic.c @@ -41,6 +41,8 @@ #include #include #include +#include +#include #include #include "i2c-ibm_iic.h" diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index b80c768..b6a741c 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c index 37e8cfa..8c87f4a 100644 --- a/drivers/i2c/busses/i2c-powermac.c +++ b/drivers/i2c/busses/i2c-powermac.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include diff --git a/drivers/input/serio/xilinx_ps2.c b/drivers/input/serio/xilinx_ps2.c index 4b7662a..b90eb4f 100644 --- a/drivers/input/serio/xilinx_ps2.c +++ b/drivers/input/serio/xilinx_ps2.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #define DRIVER_NAME "xilinx_ps2" diff --git a/drivers/macintosh/macio_asic.c b/drivers/macintosh/macio_asic.c index ac5c879..4f12c6f 100644 --- a/drivers/macintosh/macio_asic.c +++ b/drivers/macintosh/macio_asic.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include diff --git a/drivers/macintosh/rack-meter.c b/drivers/macintosh/rack-meter.c index cad0e19..4192901 100644 --- a/drivers/macintosh/rack-meter.c +++ b/drivers/macintosh/rack-meter.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c index b3b2d36..23b4a3b 100644 --- a/drivers/macintosh/smu.c +++ b/drivers/macintosh/smu.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index 283e1b5..dee88e5 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c @@ -46,6 +46,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index fe9898c..6a23223 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/drivers/misc/carma/carma-fpga-program.c b/drivers/misc/carma/carma-fpga-program.c index c6bd7e8..7be8983 100644 --- a/drivers/misc/carma/carma-fpga-program.c +++ b/drivers/misc/carma/carma-fpga-program.c @@ -10,6 +10,8 @@ */ #include +#include +#include #include #include #include diff --git a/drivers/misc/carma/carma-fpga.c b/drivers/misc/carma/carma-fpga.c index 7b56563..08b18f3 100644 --- a/drivers/misc/carma/carma-fpga.c +++ b/drivers/misc/carma/carma-fpga.c @@ -88,6 +88,8 @@ * interrupt source to the GPIO pin. Tada, we hid the interrupt. :) */ +#include +#include #include #include #include diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 2065720..e59c886 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index 317a771..2730c78 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index 04e0725..4d203e8 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c index 3c60a00..439bc38 100644 --- a/drivers/mtd/nand/mpc5121_nfc.c +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -36,7 +36,9 @@ #include #include #include +#include #include +#include #include #include diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index 8e148f1..69eaba6 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c index 5a67082..4d17436 100644 --- a/drivers/mtd/nand/pasemi_nand.c +++ b/drivers/mtd/nand/pasemi_nand.c @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/drivers/net/ethernet/freescale/fs_enet/mac-fcc.c b/drivers/net/ethernet/freescale/fs_enet/mac-fcc.c index 7583a95..005c2e0 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mac-fcc.c +++ b/drivers/net/ethernet/freescale/fs_enet/mac-fcc.c @@ -32,7 +32,9 @@ #include #include #include +#include #include +#include #include #include diff --git a/drivers/net/ethernet/freescale/fs_enet/mac-fec.c b/drivers/net/ethernet/freescale/fs_enet/mac-fec.c index 9ae6cdb..8aac231 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mac-fec.c +++ b/drivers/net/ethernet/freescale/fs_enet/mac-fec.c @@ -31,7 +31,9 @@ #include #include #include +#include #include +#include #include #include diff --git a/drivers/net/ethernet/freescale/fs_enet/mac-scc.c b/drivers/net/ethernet/freescale/fs_enet/mac-scc.c index 22a02a7..e151784 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mac-scc.c +++ b/drivers/net/ethernet/freescale/fs_enet/mac-scc.c @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include #include diff --git a/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c b/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c index 844ecfa..67caaac 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c +++ b/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include diff --git a/drivers/net/ethernet/freescale/fs_enet/mii-fec.c b/drivers/net/ethernet/freescale/fs_enet/mii-fec.c index 2f1c46a..ac5d447 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mii-fec.c +++ b/drivers/net/ethernet/freescale/fs_enet/mii-fec.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index c4eaade..c49abb4 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -78,6 +78,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c index 5930c39..64b329f 100644 --- a/drivers/net/ethernet/freescale/ucc_geth.c +++ b/drivers/net/ethernet/freescale/ucc_geth.c @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/drivers/net/ethernet/freescale/xgmac_mdio.c b/drivers/net/ethernet/freescale/xgmac_mdio.c index c1b6e7e..d449fcb 100644 --- a/drivers/net/ethernet/freescale/xgmac_mdio.c +++ b/drivers/net/ethernet/freescale/xgmac_mdio.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c index 6b5c722..cdf2321 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c @@ -39,6 +39,8 @@ #include #include #include +#include +#include #include #include diff --git a/drivers/net/ethernet/ibm/emac/mal.c b/drivers/net/ethernet/ibm/emac/mal.c index dac564c..4742ba3 100644 --- a/drivers/net/ethernet/ibm/emac/mal.c +++ b/drivers/net/ethernet/ibm/emac/mal.c @@ -27,6 +27,7 @@ #include #include +#include #include "core.h" #include diff --git a/drivers/net/ethernet/ibm/emac/rgmii.c b/drivers/net/ethernet/ibm/emac/rgmii.c index c47e23d..4fb2f96 100644 --- a/drivers/net/ethernet/ibm/emac/rgmii.c +++ b/drivers/net/ethernet/ibm/emac/rgmii.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "emac.h" diff --git a/drivers/net/ethernet/ibm/emac/tah.c b/drivers/net/ethernet/ibm/emac/tah.c index c231a4a..9f24769 100644 --- a/drivers/net/ethernet/ibm/emac/tah.c +++ b/drivers/net/ethernet/ibm/emac/tah.c @@ -18,6 +18,7 @@ * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. */ +#include #include #include "emac.h" diff --git a/drivers/net/ethernet/ibm/emac/zmii.c b/drivers/net/ethernet/ibm/emac/zmii.c index 4cdf286..9ca67a3 100644 --- a/drivers/net/ethernet/ibm/emac/zmii.c +++ b/drivers/net/ethernet/ibm/emac/zmii.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "emac.h" diff --git a/drivers/pcmcia/electra_cf.c b/drivers/pcmcia/electra_cf.c index a007321a..1b206ea 100644 --- a/drivers/pcmcia/electra_cf.c +++ b/drivers/pcmcia/electra_cf.c @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include #include diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c index 9c8f609..dc4f142 100644 --- a/drivers/rtc/rtc-mpc5121.c +++ b/drivers/rtc/rtc-mpc5121.c @@ -14,7 +14,9 @@ #include #include #include +#include #include +#include #include #include #include diff --git a/drivers/spi/spi-fsl-cpm.c b/drivers/spi/spi-fsl-cpm.c index 07971e3..bd5aaf4 100644 --- a/drivers/spi/spi-fsl-cpm.c +++ b/drivers/spi/spi-fsl-cpm.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index b8f1103..c1c936c 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/drivers/spi/spi-mpc512x-psc.c b/drivers/spi/spi-mpc512x-psc.c index dbc5e99..6148b1d 100644 --- a/drivers/spi/spi-mpc512x-psc.c +++ b/drivers/spi/spi-mpc512x-psc.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/tty/ehv_bytechan.c b/drivers/tty/ehv_bytechan.c index 9bffcec..0419b69 100644 --- a/drivers/tty/ehv_bytechan.c +++ b/drivers/tty/ehv_bytechan.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c index 1a535f7..9f7ba6d 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c @@ -41,6 +41,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c b/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c index 18f7957..527a969 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c +++ b/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c @@ -45,6 +45,7 @@ #include #include +#include #include "cpm_uart.h" diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c index f87f1a0..246b4c3 100644 --- a/drivers/tty/serial/pmac_zilog.c +++ b/drivers/tty/serial/pmac_zilog.c @@ -57,6 +57,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c index 8831748..9de1da0 100644 --- a/drivers/tty/serial/ucc_uart.c +++ b/drivers/tty/serial/ucc_uart.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index f3bb363..807127d 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c index 932293f..2b1694e 100644 --- a/drivers/usb/host/ehci-ppc-of.c +++ b/drivers/usb/host/ehci-ppc-of.c @@ -16,6 +16,8 @@ #include #include +#include +#include #include diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c index 0b4654259..0551c0a 100644 --- a/drivers/usb/host/fhci-hcd.c +++ b/drivers/usb/host/fhci-hcd.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c index 75f5a1e..81f3eba 100644 --- a/drivers/usb/host/ohci-ppc-of.c +++ b/drivers/usb/host/ohci-ppc-of.c @@ -14,6 +14,8 @@ */ #include +#include +#include #include #include diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index 6dd7225..b047ec5 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include #include diff --git a/drivers/virt/fsl_hypervisor.c b/drivers/virt/fsl_hypervisor.c index d294f67..32c8fc5 100644 --- a/drivers/virt/fsl_hypervisor.c +++ b/drivers/virt/fsl_hypervisor.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/watchdog/gef_wdt.c b/drivers/watchdog/gef_wdt.c index 257cfba..3755833 100644 --- a/drivers/watchdog/gef_wdt.c +++ b/drivers/watchdog/gef_wdt.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/watchdog/mpc8xxx_wdt.c b/drivers/watchdog/mpc8xxx_wdt.c index da27520..d0ebeba 100644 --- a/drivers/watchdog/mpc8xxx_wdt.c +++ b/drivers/watchdog/mpc8xxx_wdt.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/aoa/core/gpio-feature.c b/sound/aoa/core/gpio-feature.c index faa3174..f341539 100644 --- a/sound/aoa/core/gpio-feature.c +++ b/sound/aoa/core/gpio-feature.c @@ -10,8 +10,9 @@ * registers. */ -#include +#include #include +#include #include "../aoa.h" /* TODO: these are lots of global variables diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c index 15e7613..4678360 100644 --- a/sound/aoa/soundbus/i2sbus/core.c +++ b/sound/aoa/soundbus/i2sbus/core.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index c93fbbb..7a43c0c 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include "pmac.h" #include diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c index b23354a..b9ffc17 100644 --- a/sound/ppc/tumbler.c +++ b/sound/ppc/tumbler.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index 9cc5c1f..d1b111e 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index 2a847ca..161e5055 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c index 3ef7a0c..24eafa2 100644 --- a/sound/soc/fsl/mpc5200_psc_ac97.c +++ b/sound/soc/fsl/mpc5200_psc_ac97.c @@ -291,7 +291,7 @@ static int psc_ac97_of_probe(struct platform_device *op) rc = snd_soc_set_ac97_ops(&psc_ac97_ops); if (rc != 0) { - dev_err(&op->dev, "Failed to set AC'97 ops: %d\n", ret); + dev_err(&op->dev, "Failed to set AC'97 ops: %d\n", rc); return rc; } diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c index 228c52e..fa756d0 100644 --- a/sound/soc/fsl/mpc8610_hpcd.c +++ b/sound/soc/fsl/mpc8610_hpcd.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c index ba59c23..f75c3cf 100644 --- a/sound/soc/fsl/p1022_ds.c +++ b/sound/soc/fsl/p1022_ds.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include diff --git a/sound/soc/fsl/p1022_rdk.c b/sound/soc/fsl/p1022_rdk.c index f215519..9d89bb0 100644 --- a/sound/soc/fsl/p1022_rdk.c +++ b/sound/soc/fsl/p1022_rdk.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include -- cgit v0.10.2 From 089a49b68ce9bfe078d509d96c78a94e5f865ca9 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Sat, 7 Sep 2013 14:58:54 -0500 Subject: mips: add explicit includes of prom.h In preparation of removing prom.h include by of.h, add explicit includes. Signed-off-by: Rob Herring Acked-by: Grant Likely Cc: Ralf Baechle Cc: linux-mips@linux-mips.org diff --git a/arch/mips/lantiq/prom.c b/arch/mips/lantiq/prom.c index 49c4603..19686c5 100644 --- a/arch/mips/lantiq/prom.c +++ b/arch/mips/lantiq/prom.c @@ -14,6 +14,7 @@ #include #include +#include #include diff --git a/arch/mips/mti-sead3/sead3-setup.c b/arch/mips/mti-sead3/sead3-setup.c index b5059dc..928ba84 100644 --- a/arch/mips/mti-sead3/sead3-setup.c +++ b/arch/mips/mti-sead3/sead3-setup.c @@ -10,6 +10,8 @@ #include #include +#include + #include const char *get_system_type(void) diff --git a/arch/mips/ralink/of.c b/arch/mips/ralink/of.c index ce38d11..58c4fd52 100644 --- a/arch/mips/ralink/of.c +++ b/arch/mips/ralink/of.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "common.h" -- cgit v0.10.2 From ba904f0649357a3cbd67e752d30898c42b1fc91a Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 9 Sep 2013 18:13:22 -0500 Subject: x86: add necessary includes for prom.h Once prom.h is no longer implicitly included, we need to include setup.h to get COMMAND_LINE_SIZE. Signed-off-by: Rob Herring Acked-by: Grant Likely Cc: Thomas Gleixner Cc: Ingo Molnar Cc: "H. Peter Anvin" Cc: x86@kernel.org diff --git a/arch/x86/kernel/devicetree.c b/arch/x86/kernel/devicetree.c index 0e1f95b..679d676 100644 --- a/arch/x86/kernel/devicetree.c +++ b/arch/x86/kernel/devicetree.c @@ -20,6 +20,7 @@ #include #include #include +#include __initdata u64 initial_dtb; char __initdata cmd_line[COMMAND_LINE_SIZE]; -- cgit v0.10.2 From 25ff79443cbfa924b8df1d4a8a0fbff83816938a Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Sat, 7 Sep 2013 14:07:11 -0500 Subject: of: implement pci_address_to_pio as weak function Implement pci_address_to_pio as weak function to remove the dependency on asm/prom.h. This is in preparation to make prom.h optional. Signed-off-by: Rob Herring Acked-by: Grant Likely Cc: Michal Simek Cc: Ralf Baechle Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Thomas Gleixner Cc: Ingo Molnar Cc: "H. Peter Anvin" Cc: x86@kernel.org Cc: Grant Likely diff --git a/arch/microblaze/include/asm/prom.h b/arch/microblaze/include/asm/prom.h index f05bedc..0ebd924 100644 --- a/arch/microblaze/include/asm/prom.h +++ b/arch/microblaze/include/asm/prom.h @@ -26,13 +26,4 @@ enum early_consoles { extern int of_early_console(void *version); -/* - * OF address retreival & translation - */ - -#ifdef CONFIG_PCI -extern unsigned long pci_address_to_pio(phys_addr_t address); -#define pci_address_to_pio pci_address_to_pio -#endif /* CONFIG_PCI */ - #endif /* _ASM_MICROBLAZE_PROM_H */ diff --git a/arch/mips/include/asm/prom.h b/arch/mips/include/asm/prom.h index e3dbd0e..ccd2b75 100644 --- a/arch/mips/include/asm/prom.h +++ b/arch/mips/include/asm/prom.h @@ -19,17 +19,6 @@ extern void device_tree_init(void); -static inline unsigned long pci_address_to_pio(phys_addr_t address) -{ - /* - * The ioport address can be directly used by inX() / outX() - */ - BUG_ON(address > IO_SPACE_LIMIT); - - return (unsigned long) address; -} -#define pci_address_to_pio pci_address_to_pio - struct boot_param_header; extern void __dt_setup_arch(struct boot_param_header *bph); diff --git a/arch/powerpc/include/asm/prom.h b/arch/powerpc/include/asm/prom.h index 7d0c7f3..bd215f7 100644 --- a/arch/powerpc/include/asm/prom.h +++ b/arch/powerpc/include/asm/prom.h @@ -30,11 +30,6 @@ extern u64 of_translate_dma_address(struct device_node *dev, const __be32 *in_addr); -#ifdef CONFIG_PCI -extern unsigned long pci_address_to_pio(phys_addr_t address); -#define pci_address_to_pio pci_address_to_pio -#endif /* CONFIG_PCI */ - /* Parse the ibm,dma-window property of an OF node into the busno, phys and * size parameters. */ diff --git a/arch/x86/include/asm/prom.h b/arch/x86/include/asm/prom.h index bade6ac..8ef2ec7 100644 --- a/arch/x86/include/asm/prom.h +++ b/arch/x86/include/asm/prom.h @@ -39,9 +39,6 @@ static inline void x86_dtb_init(void) { } extern char cmd_line[COMMAND_LINE_SIZE]; -#define pci_address_to_pio pci_address_to_pio -unsigned long pci_address_to_pio(phys_addr_t addr); - #define HAVE_ARCH_DEVTREE_FIXUPS #endif /* __ASSEMBLY__ */ diff --git a/arch/x86/kernel/devicetree.c b/arch/x86/kernel/devicetree.c index 679d676..cffd073 100644 --- a/arch/x86/kernel/devicetree.c +++ b/arch/x86/kernel/devicetree.c @@ -27,16 +27,6 @@ char __initdata cmd_line[COMMAND_LINE_SIZE]; int __initdata of_ioapic; -unsigned long pci_address_to_pio(phys_addr_t address) -{ - /* - * The ioport address can be directly used by inX / outX - */ - BUG_ON(address >= (1 << 16)); - return (unsigned long)address; -} -EXPORT_SYMBOL_GPL(pci_address_to_pio); - void __init early_init_dt_scan_chosen_arch(unsigned long node) { BUG(); diff --git a/drivers/of/address.c b/drivers/of/address.c index b55c218..556a7fb 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -626,6 +626,14 @@ const __be32 *of_get_address(struct device_node *dev, int index, u64 *size, } EXPORT_SYMBOL(of_get_address); +unsigned long __weak pci_address_to_pio(phys_addr_t address) +{ + if (address > IO_SPACE_LIMIT) + return (unsigned long)-1; + + return (unsigned long) address; +} + static int __of_address_to_resource(struct device_node *dev, const __be32 *addrp, u64 size, unsigned int flags, const char *name, struct resource *r) diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c index e5ca008..8481996 100644 --- a/drivers/of/of_pci.c +++ b/drivers/of/of_pci.c @@ -2,7 +2,6 @@ #include #include #include -#include static inline int __of_pci_pci_compare(struct device_node *node, unsigned int data) diff --git a/include/linux/of_address.h b/include/linux/of_address.h index 4c2e6f2..f6fc689 100644 --- a/include/linux/of_address.h +++ b/include/linux/of_address.h @@ -52,10 +52,7 @@ extern void __iomem *of_iomap(struct device_node *device, int index); extern const __be32 *of_get_address(struct device_node *dev, int index, u64 *size, unsigned int *flags); -#ifndef pci_address_to_pio -static inline unsigned long pci_address_to_pio(phys_addr_t addr) { return -1; } -#define pci_address_to_pio pci_address_to_pio -#endif +extern unsigned long pci_address_to_pio(phys_addr_t addr); extern int of_pci_range_parser_init(struct of_pci_range_parser *parser, struct device_node *node); -- cgit v0.10.2 From 0c3f061c195ceb891067b6de9e4ecc347c4dea31 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 17 Sep 2013 10:42:50 -0500 Subject: of: implement of_node_to_nid as a weak function Implement of_node_to_nid as weak function to remove the dependency on asm/prom.h. This is in preparation to make prom.h optional. Signed-off-by: Rob Herring Acked-by: Grant Likely diff --git a/arch/powerpc/include/asm/prom.h b/arch/powerpc/include/asm/prom.h index bd215f7..6707c16 100644 --- a/arch/powerpc/include/asm/prom.h +++ b/arch/powerpc/include/asm/prom.h @@ -42,13 +42,6 @@ extern void kdump_move_device_tree(void); /* cache lookup */ struct device_node *of_find_next_cache_node(struct device_node *np); -#ifdef CONFIG_NUMA -extern int of_node_to_nid(struct device_node *device); -#else -static inline int of_node_to_nid(struct device_node *device) { return 0; } -#endif -#define of_node_to_nid of_node_to_nid - extern void of_instantiate_rtc(void); extern int of_get_ibm_chip_id(struct device_node *np); diff --git a/arch/sparc/include/asm/prom.h b/arch/sparc/include/asm/prom.h index 67c6257..60c8d7b 100644 --- a/arch/sparc/include/asm/prom.h +++ b/arch/sparc/include/asm/prom.h @@ -43,10 +43,6 @@ extern int of_getintprop_default(struct device_node *np, const char *name, int def); extern int of_find_in_proplist(const char *list, const char *match, int len); -#ifdef CONFIG_NUMA -extern int of_node_to_nid(struct device_node *dp); -#define of_node_to_nid of_node_to_nid -#endif extern void prom_build_devicetree(void); extern void of_populate_present_mask(void); diff --git a/drivers/of/base.c b/drivers/of/base.c index 865d3f6..ced4c06 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -74,6 +74,13 @@ int of_n_size_cells(struct device_node *np) } EXPORT_SYMBOL(of_n_size_cells); +#ifdef CONFIG_NUMA +int __weak of_node_to_nid(struct device_node *np) +{ + return numa_node_id(); +} +#endif + #if defined(CONFIG_OF_DYNAMIC) /** * of_node_get - Increment refcount of a node diff --git a/include/linux/of.h b/include/linux/of.h index f95aee3..4d294a0 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -534,13 +534,10 @@ static inline const char *of_prop_next_string(struct property *prop, #define of_match_node(_matches, _node) NULL #endif /* CONFIG_OF */ -#ifndef of_node_to_nid -static inline int of_node_to_nid(struct device_node *np) -{ - return numa_node_id(); -} - -#define of_node_to_nid of_node_to_nid +#if defined(CONFIG_OF) && defined(CONFIG_NUMA) +extern int of_node_to_nid(struct device_node *np); +#else +static inline int of_node_to_nid(struct device_node *device) { return 0; } #endif /** -- cgit v0.10.2 From 32df8dca503f82c816f8be85a8d0a394a8b88c2c Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Sat, 7 Sep 2013 14:11:58 -0500 Subject: of: remove HAVE_ARCH_DEVTREE_FIXUPS HAVE_ARCH_DEVTREE_FIXUPS appears to always be needed except for sparc, but it is only used for /proc/device-teee and sparc does not enable /proc/device-tree. So this option is redundant. Remove the option and always enable it. This has the side effect of fixing /proc/device-tree on arches such as arm64 which failed to define this option. Signed-off-by: Rob Herring Acked-by: Vineet Gupta Acked-by: Grant Likely Cc: Russell King Cc: James Hogan Cc: Michal Simek Cc: Jonas Bonn Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Thomas Gleixner Cc: Ingo Molnar Cc: "H. Peter Anvin" Cc: x86@kernel.org Cc: Chris Zankel Cc: Max Filippov diff --git a/arch/arc/include/asm/prom.h b/arch/arc/include/asm/prom.h index 692d0d0..1555549 100644 --- a/arch/arc/include/asm/prom.h +++ b/arch/arc/include/asm/prom.h @@ -9,6 +9,4 @@ #ifndef _ASM_ARC_PROM_H_ #define _ASM_ARC_PROM_H_ -#define HAVE_ARCH_DEVTREE_FIXUPS - #endif diff --git a/arch/arm/include/asm/prom.h b/arch/arm/include/asm/prom.h index 4a2985e..b681575 100644 --- a/arch/arm/include/asm/prom.h +++ b/arch/arm/include/asm/prom.h @@ -11,8 +11,6 @@ #ifndef __ASMARM_PROM_H #define __ASMARM_PROM_H -#define HAVE_ARCH_DEVTREE_FIXUPS - #ifdef CONFIG_OF extern const struct machine_desc *setup_machine_fdt(unsigned int dt_phys); diff --git a/arch/metag/include/asm/prom.h b/arch/metag/include/asm/prom.h index e19022c..d4be144 100644 --- a/arch/metag/include/asm/prom.h +++ b/arch/metag/include/asm/prom.h @@ -14,7 +14,6 @@ #ifndef __ASM_METAG_PROM_H #define __ASM_METAG_PROM_H -#define HAVE_ARCH_DEVTREE_FIXUPS extern void copy_fdt(void); diff --git a/arch/microblaze/include/asm/prom.h b/arch/microblaze/include/asm/prom.h index 0ebd924..2f03ac8 100644 --- a/arch/microblaze/include/asm/prom.h +++ b/arch/microblaze/include/asm/prom.h @@ -16,8 +16,6 @@ #include -#define HAVE_ARCH_DEVTREE_FIXUPS - /* Other Prototypes */ enum early_consoles { UARTLITE = 1, diff --git a/arch/openrisc/include/asm/prom.h b/arch/openrisc/include/asm/prom.h index 93c9980..bec477b 100644 --- a/arch/openrisc/include/asm/prom.h +++ b/arch/openrisc/include/asm/prom.h @@ -17,6 +17,4 @@ #ifndef _ASM_OPENRISC_PROM_H #define _ASM_OPENRISC_PROM_H -#define HAVE_ARCH_DEVTREE_FIXUPS - #endif /* _ASM_OPENRISC_PROM_H */ diff --git a/arch/powerpc/include/asm/prom.h b/arch/powerpc/include/asm/prom.h index 6707c16..43fe002 100644 --- a/arch/powerpc/include/asm/prom.h +++ b/arch/powerpc/include/asm/prom.h @@ -20,8 +20,6 @@ #include #include -#define HAVE_ARCH_DEVTREE_FIXUPS - /* * OF address retreival & translation */ diff --git a/arch/x86/include/asm/prom.h b/arch/x86/include/asm/prom.h index 8ef2ec7..fbeb06e 100644 --- a/arch/x86/include/asm/prom.h +++ b/arch/x86/include/asm/prom.h @@ -39,7 +39,5 @@ static inline void x86_dtb_init(void) { } extern char cmd_line[COMMAND_LINE_SIZE]; -#define HAVE_ARCH_DEVTREE_FIXUPS - #endif /* __ASSEMBLY__ */ #endif diff --git a/arch/xtensa/include/asm/prom.h b/arch/xtensa/include/asm/prom.h index f3d7cd2..2a87a58 100644 --- a/arch/xtensa/include/asm/prom.h +++ b/arch/xtensa/include/asm/prom.h @@ -1,6 +1,4 @@ #ifndef _XTENSA_ASM_PROM_H #define _XTENSA_ASM_PROM_H -#define HAVE_ARCH_DEVTREE_FIXUPS - #endif /* _XTENSA_ASM_PROM_H */ diff --git a/fs/proc/proc_devtree.c b/fs/proc/proc_devtree.c index 106a835..70779b2 100644 --- a/fs/proc/proc_devtree.c +++ b/fs/proc/proc_devtree.c @@ -14,16 +14,13 @@ #include #include #include -#include #include #include "internal.h" static inline void set_node_proc_entry(struct device_node *np, struct proc_dir_entry *de) { -#ifdef HAVE_ARCH_DEVTREE_FIXUPS np->pde = de; -#endif } static struct proc_dir_entry *proc_device_tree; -- cgit v0.10.2 From 4acf4b9cd4534aaa9102004937e1ba79da01d008 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 16 Sep 2013 21:03:24 -0500 Subject: of: move of_address_to_resource and of_iomap declarations from sparc Move of_address_to_resource and of_iomap declarations to common code. These only differ on sparc, but the declarations are the same and don't need to be in arch header. Signed-off-by: Rob Herring Acked-by: Grant Likely Cc: "David S. Miller" Cc: sparclinux@vger.kernel.org diff --git a/arch/sparc/include/asm/prom.h b/arch/sparc/include/asm/prom.h index 60c8d7b..11ebd65 100644 --- a/arch/sparc/include/asm/prom.h +++ b/arch/sparc/include/asm/prom.h @@ -59,13 +59,5 @@ extern char *of_console_options; extern void irq_trans_init(struct device_node *dp); extern char *build_path_component(struct device_node *dp); -/* SPARC has local implementations */ -extern int of_address_to_resource(struct device_node *dev, int index, - struct resource *r); -#define of_address_to_resource of_address_to_resource - -void __iomem *of_iomap(struct device_node *node, int index); -#define of_iomap of_iomap - #endif /* __KERNEL__ */ #endif /* _SPARC_PROM_H */ diff --git a/include/linux/of_address.h b/include/linux/of_address.h index f6fc689..e8a1797 100644 --- a/include/linux/of_address.h +++ b/include/linux/of_address.h @@ -60,13 +60,6 @@ extern struct of_pci_range *of_pci_range_parser_one( struct of_pci_range_parser *parser, struct of_pci_range *range); #else /* CONFIG_OF_ADDRESS */ -#ifndef of_address_to_resource -static inline int of_address_to_resource(struct device_node *dev, int index, - struct resource *r) -{ - return -EINVAL; -} -#endif static inline struct device_node *of_find_matching_node_by_address( struct device_node *from, const struct of_device_id *matches, @@ -74,12 +67,7 @@ static inline struct device_node *of_find_matching_node_by_address( { return NULL; } -#ifndef of_iomap -static inline void __iomem *of_iomap(struct device_node *device, int index) -{ - return NULL; -} -#endif + static inline const __be32 *of_get_address(struct device_node *dev, int index, u64 *size, unsigned int *flags) { @@ -100,6 +88,22 @@ static inline struct of_pci_range *of_pci_range_parser_one( } #endif /* CONFIG_OF_ADDRESS */ +#ifdef CONFIG_OF +extern int of_address_to_resource(struct device_node *dev, int index, + struct resource *r); +void __iomem *of_iomap(struct device_node *node, int index); +#else +static inline int of_address_to_resource(struct device_node *dev, int index, + struct resource *r) +{ + return -EINVAL; +} + +static inline void __iomem *of_iomap(struct device_node *device, int index) +{ + return NULL; +} +#endif #if defined(CONFIG_OF_ADDRESS) && defined(CONFIG_PCI) extern const __be32 *of_get_pci_address(struct device_node *dev, int bar_no, -- cgit v0.10.2 From d0dfa16a600190d142f7538e5909d13c35b60d98 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 16 Sep 2013 21:05:05 -0500 Subject: of: move of_translate_dma_address to of_address.h of_translate_dma_address is implemented in common code, so move the declaration there too. Signed-off-by: Rob Herring Acked-by: Grant Likely Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: linuxppc-dev@lists.ozlabs.org diff --git a/arch/powerpc/include/asm/prom.h b/arch/powerpc/include/asm/prom.h index 43fe002..b8774bd 100644 --- a/arch/powerpc/include/asm/prom.h +++ b/arch/powerpc/include/asm/prom.h @@ -24,10 +24,6 @@ * OF address retreival & translation */ -/* Translate a DMA address from device space to CPU space */ -extern u64 of_translate_dma_address(struct device_node *dev, - const __be32 *in_addr); - /* Parse the ibm,dma-window property of an OF node into the busno, phys and * size parameters. */ diff --git a/include/linux/of_address.h b/include/linux/of_address.h index e8a1797..5f6ed6b 100644 --- a/include/linux/of_address.h +++ b/include/linux/of_address.h @@ -34,6 +34,10 @@ static inline void of_pci_range_to_resource(struct of_pci_range *range, res->name = np->full_name; } +/* Translate a DMA address from device space to CPU space */ +extern u64 of_translate_dma_address(struct device_node *dev, + const __be32 *in_addr); + #ifdef CONFIG_OF_ADDRESS extern u64 of_translate_address(struct device_node *np, const __be32 *addr); extern bool of_can_translate_address(struct device_node *dev); -- cgit v0.10.2 From 5c19c5c6d4f5e1dc0d0e26b683bc820dda01fe06 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 17 Sep 2013 14:34:00 -0500 Subject: powerpc: clean-up include ordering in prom.h Now that the core OF headers don't depend on prom.h, rearrange the includes. There are still lots of implicit includes in the powerpc tree, so the includes of OF headers are still necessary. Signed-off-by: Rob Herring Acked-by: Grant Likely diff --git a/arch/powerpc/include/asm/prom.h b/arch/powerpc/include/asm/prom.h index b8774bd..7687f82 100644 --- a/arch/powerpc/include/asm/prom.h +++ b/arch/powerpc/include/asm/prom.h @@ -1,4 +1,3 @@ -#include /* linux/of.h gets to determine #include ordering */ #ifndef _POWERPC_PROM_H #define _POWERPC_PROM_H #ifdef __KERNEL__ @@ -20,6 +19,13 @@ #include #include +/* These includes should be removed once implicit includes are cleaned up. */ +#include +#include +#include +#include +#include + /* * OF address retreival & translation */ @@ -125,14 +131,5 @@ struct of_drconf_cell { */ extern unsigned char ibm_architecture_vec[]; -/* These includes are put at the bottom because they may contain things - * that are overridden by this file. Ideally they shouldn't be included - * by this file, but there are a bunch of .c files that currently depend - * on it. Eventually they will be cleaned up. */ -#include -#include -#include -#include - #endif /* __KERNEL__ */ #endif /* _POWERPC_PROM_H */ -- cgit v0.10.2 From 26a2056eb21fff26caf99d19ad5448e9403db55d Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 26 Sep 2013 07:40:04 -0500 Subject: powerpc: add explicit OF includes When removing prom.h include by of.h, several OF headers will no longer be implicitly included. Add explicit includes of of_*.h as needed. Signed-off-by: Rob Herring Acked-by: Grant Likely Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Anatolij Gustschin Cc: Kumar Gala Cc: Olof Johansson Cc: linuxppc-dev@lists.ozlabs.org diff --git a/arch/powerpc/kernel/epapr_paravirt.c b/arch/powerpc/kernel/epapr_paravirt.c index 6300c13..7898be9 100644 --- a/arch/powerpc/kernel/epapr_paravirt.c +++ b/arch/powerpc/kernel/epapr_paravirt.c @@ -18,6 +18,7 @@ */ #include +#include #include #include #include diff --git a/arch/powerpc/platforms/512x/clock.c b/arch/powerpc/platforms/512x/clock.c index e504166..fd8a376 100644 --- a/arch/powerpc/platforms/512x/clock.c +++ b/arch/powerpc/platforms/512x/clock.c @@ -24,6 +24,7 @@ #include #include +#include #include #include #include diff --git a/arch/powerpc/platforms/512x/pdm360ng.c b/arch/powerpc/platforms/512x/pdm360ng.c index 24b314d..116f2325 100644 --- a/arch/powerpc/platforms/512x/pdm360ng.c +++ b/arch/powerpc/platforms/512x/pdm360ng.c @@ -14,6 +14,8 @@ #include #include +#include +#include #include #include diff --git a/arch/powerpc/platforms/82xx/mpc8272_ads.c b/arch/powerpc/platforms/82xx/mpc8272_ads.c index 30394b4..6a14cf5 100644 --- a/arch/powerpc/platforms/82xx/mpc8272_ads.c +++ b/arch/powerpc/platforms/82xx/mpc8272_ads.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #include diff --git a/arch/powerpc/platforms/82xx/pq2fads.c b/arch/powerpc/platforms/82xx/pq2fads.c index e1dceee..e5f82ec 100644 --- a/arch/powerpc/platforms/82xx/pq2fads.c +++ b/arch/powerpc/platforms/82xx/pq2fads.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include #include diff --git a/arch/powerpc/platforms/83xx/suspend.c b/arch/powerpc/platforms/83xx/suspend.c index 1d769a2..3d9716c 100644 --- a/arch/powerpc/platforms/83xx/suspend.c +++ b/arch/powerpc/platforms/83xx/suspend.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include diff --git a/arch/powerpc/platforms/85xx/c293pcie.c b/arch/powerpc/platforms/85xx/c293pcie.c index 6208e49..213d5b8 100644 --- a/arch/powerpc/platforms/85xx/c293pcie.c +++ b/arch/powerpc/platforms/85xx/c293pcie.c @@ -11,6 +11,7 @@ #include #include +#include #include #include diff --git a/arch/powerpc/platforms/85xx/common.c b/arch/powerpc/platforms/85xx/common.c index d0861a0..eba78c8 100644 --- a/arch/powerpc/platforms/85xx/common.c +++ b/arch/powerpc/platforms/85xx/common.c @@ -5,6 +5,8 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ + +#include #include #include diff --git a/arch/powerpc/platforms/85xx/socrates_fpga_pic.c b/arch/powerpc/platforms/85xx/socrates_fpga_pic.c index 3bbbf74..55a9682 100644 --- a/arch/powerpc/platforms/85xx/socrates_fpga_pic.c +++ b/arch/powerpc/platforms/85xx/socrates_fpga_pic.c @@ -9,6 +9,8 @@ */ #include +#include +#include #include #include diff --git a/arch/powerpc/platforms/86xx/pic.c b/arch/powerpc/platforms/86xx/pic.c index 9982f57..d5b98c0 100644 --- a/arch/powerpc/platforms/86xx/pic.c +++ b/arch/powerpc/platforms/86xx/pic.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include diff --git a/arch/powerpc/platforms/8xx/ep88xc.c b/arch/powerpc/platforms/8xx/ep88xc.c index 7d9ac60..e621666 100644 --- a/arch/powerpc/platforms/8xx/ep88xc.c +++ b/arch/powerpc/platforms/8xx/ep88xc.c @@ -10,6 +10,8 @@ */ #include +#include +#include #include #include diff --git a/arch/powerpc/platforms/8xx/mpc86xads_setup.c b/arch/powerpc/platforms/8xx/mpc86xads_setup.c index 866feff..6308464 100644 --- a/arch/powerpc/platforms/8xx/mpc86xads_setup.c +++ b/arch/powerpc/platforms/8xx/mpc86xads_setup.c @@ -15,6 +15,8 @@ */ #include +#include +#include #include #include diff --git a/arch/powerpc/platforms/8xx/mpc885ads_setup.c b/arch/powerpc/platforms/8xx/mpc885ads_setup.c index 5d98398..c126258 100644 --- a/arch/powerpc/platforms/8xx/mpc885ads_setup.c +++ b/arch/powerpc/platforms/8xx/mpc885ads_setup.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include diff --git a/arch/powerpc/platforms/8xx/tqm8xx_setup.c b/arch/powerpc/platforms/8xx/tqm8xx_setup.c index 8d21ab7..c9a0d00 100644 --- a/arch/powerpc/platforms/8xx/tqm8xx_setup.c +++ b/arch/powerpc/platforms/8xx/tqm8xx_setup.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include diff --git a/arch/powerpc/platforms/embedded6xx/flipper-pic.c b/arch/powerpc/platforms/embedded6xx/flipper-pic.c index 53d6eee0..4cde8e7 100644 --- a/arch/powerpc/platforms/embedded6xx/flipper-pic.c +++ b/arch/powerpc/platforms/embedded6xx/flipper-pic.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "flipper-pic.h" diff --git a/arch/powerpc/platforms/embedded6xx/hlwd-pic.c b/arch/powerpc/platforms/embedded6xx/hlwd-pic.c index 3006b51..7cab21d 100644 --- a/arch/powerpc/platforms/embedded6xx/hlwd-pic.c +++ b/arch/powerpc/platforms/embedded6xx/hlwd-pic.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include "hlwd-pic.h" diff --git a/arch/powerpc/platforms/pasemi/gpio_mdio.c b/arch/powerpc/platforms/pasemi/gpio_mdio.c index 0237ab7..15adee5 100644 --- a/arch/powerpc/platforms/pasemi/gpio_mdio.c +++ b/arch/powerpc/platforms/pasemi/gpio_mdio.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include diff --git a/arch/powerpc/platforms/powermac/pfunc_base.c b/arch/powerpc/platforms/powermac/pfunc_base.c index f5e3cda..e49d07f 100644 --- a/arch/powerpc/platforms/powermac/pfunc_base.c +++ b/arch/powerpc/platforms/powermac/pfunc_base.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include diff --git a/arch/powerpc/platforms/powernv/opal-lpc.c b/arch/powerpc/platforms/powernv/opal-lpc.c index a7614bb..e7e59e4 100644 --- a/arch/powerpc/platforms/powernv/opal-lpc.c +++ b/arch/powerpc/platforms/powernv/opal-lpc.c @@ -17,6 +17,7 @@ #include #include #include +#include static int opal_lpc_chip_id = -1; diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c index 2911abe..f36ff35 100644 --- a/arch/powerpc/platforms/powernv/opal.c +++ b/arch/powerpc/platforms/powernv/opal.c @@ -13,6 +13,7 @@ #include #include +#include #include #include #include diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index e239dcf..19884b2 100644 --- a/arch/powerpc/platforms/powernv/setup.c +++ b/arch/powerpc/platforms/powernv/setup.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include diff --git a/arch/powerpc/platforms/pseries/hotplug-memory.c b/arch/powerpc/platforms/pseries/hotplug-memory.c index 9a432de..9590dbb 100644 --- a/arch/powerpc/platforms/pseries/hotplug-memory.c +++ b/arch/powerpc/platforms/pseries/hotplug-memory.c @@ -10,12 +10,14 @@ */ #include +#include #include #include #include #include #include +#include #include static unsigned long get_memblock_size(void) diff --git a/arch/powerpc/sysdev/cpm_common.c b/arch/powerpc/sysdev/cpm_common.c index 4dd5341..4f78695 100644 --- a/arch/powerpc/sysdev/cpm_common.c +++ b/arch/powerpc/sysdev/cpm_common.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include diff --git a/arch/powerpc/sysdev/fsl_gtm.c b/arch/powerpc/sysdev/fsl_gtm.c index 0eb871c..1b980ee 100644 --- a/arch/powerpc/sysdev/fsl_gtm.c +++ b/arch/powerpc/sysdev/fsl_gtm.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/arch/powerpc/sysdev/fsl_pmc.c b/arch/powerpc/sysdev/fsl_pmc.c index 592a0f8..8cf4aa0 100644 --- a/arch/powerpc/sysdev/fsl_pmc.c +++ b/arch/powerpc/sysdev/fsl_pmc.c @@ -18,6 +18,7 @@ #include #include #include +#include #include struct pmc_regs { diff --git a/arch/powerpc/sysdev/fsl_rio.c b/arch/powerpc/sysdev/fsl_rio.c index e2fb317..95dd892 100644 --- a/arch/powerpc/sysdev/fsl_rio.c +++ b/arch/powerpc/sysdev/fsl_rio.c @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/arch/powerpc/sysdev/fsl_rmu.c b/arch/powerpc/sysdev/fsl_rmu.c index 14bd522..00e224a 100644 --- a/arch/powerpc/sysdev/fsl_rmu.c +++ b/arch/powerpc/sysdev/fsl_rmu.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 1be54fa..2d30eaf 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -535,7 +535,7 @@ static void __init mpic_scan_ht_pic(struct mpic *mpic, u8 __iomem *devbase, mpic->fixups[irq].data = readl(base + 4) | 0x80000000; } } - + static void __init mpic_scan_ht_pics(struct mpic *mpic) { @@ -1475,7 +1475,7 @@ struct mpic * __init mpic_alloc(struct device_node *node, * as a default instead of the value read from the HW. */ last_irq = (greg_feature & MPIC_GREG_FEATURE_LAST_SRC_MASK) - >> MPIC_GREG_FEATURE_LAST_SRC_SHIFT; + >> MPIC_GREG_FEATURE_LAST_SRC_SHIFT; if (isu_size) last_irq = isu_size * MPIC_MAX_ISU - 1; of_property_read_u32(mpic->node, "last-interrupt-source", &last_irq); @@ -1625,7 +1625,7 @@ void __init mpic_init(struct mpic *mpic) /* start with vector = source number, and masked */ u32 vecpri = MPIC_VECPRI_MASK | i | (8 << MPIC_VECPRI_PRIORITY_SHIFT); - + /* check if protected */ if (mpic->protected && test_bit(i, mpic->protected)) continue; @@ -1634,7 +1634,7 @@ void __init mpic_init(struct mpic *mpic) mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION), 1 << cpu); } } - + /* Init spurious vector */ mpic_write(mpic->gregs, MPIC_INFO(GREG_SPURIOUS), mpic->spurious_vec); diff --git a/arch/powerpc/sysdev/mpic_timer.c b/arch/powerpc/sysdev/mpic_timer.c index c06db92..22d7d57 100644 --- a/arch/powerpc/sysdev/mpic_timer.c +++ b/arch/powerpc/sysdev/mpic_timer.c @@ -19,7 +19,9 @@ #include #include #include +#include #include +#include #include #include #include diff --git a/arch/powerpc/sysdev/of_rtc.c b/arch/powerpc/sysdev/of_rtc.c index c9e803f..6f54b54 100644 --- a/arch/powerpc/sysdev/of_rtc.c +++ b/arch/powerpc/sysdev/of_rtc.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include diff --git a/arch/powerpc/sysdev/ppc4xx_soc.c b/arch/powerpc/sysdev/ppc4xx_soc.c index 0debcc3..5c77c9b 100644 --- a/arch/powerpc/sysdev/ppc4xx_soc.c +++ b/arch/powerpc/sysdev/ppc4xx_soc.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include diff --git a/arch/powerpc/sysdev/xilinx_intc.c b/arch/powerpc/sysdev/xilinx_intc.c index 8d73c3c..f4fdc94 100644 --- a/arch/powerpc/sysdev/xilinx_intc.c +++ b/arch/powerpc/sysdev/xilinx_intc.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include -- cgit v0.10.2 From b5b4bb3f6a11f9c37b6d53138244f2ffe5bacd12 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Sat, 7 Sep 2013 14:08:20 -0500 Subject: of: only include prom.h on sparc The dependency on prom.h by the core DT code is now removed and only sparc needs to include prom.h for the core code. Signed-off-by: Rob Herring Acked-by: Grant Likely diff --git a/include/linux/of.h b/include/linux/of.h index 4d294a0..54017b8 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -136,7 +136,9 @@ static inline unsigned long of_read_ulong(const __be32 *cell, int size) return of_read_number(cell, size); } +#if defined(CONFIG_SPARC) #include +#endif /* Default #address and #size cells. Allow arch asm/prom.h to override */ #if !defined(OF_ROOT_NODE_ADDR_CELLS_DEFAULT) -- cgit v0.10.2 From 5116b1defad7cec648384c2d1bc0b7460d305c93 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Sat, 7 Sep 2013 14:33:54 -0500 Subject: of: remove empty arch prom.h headers Now that prom.h is optional, all the empty prom.h headers can be removed. Signed-off-by: Rob Herring Acked-by: Vineet Gupta Acked-by: Catalin Marinas Acked-by: Grant Likely Cc: Will Deacon Cc: Mark Salter Cc: Aurelien Jacquiot Cc: James Hogan Cc: Jonas Bonn Cc: Chris Zankel Cc: Max Filippov diff --git a/arch/arc/include/asm/prom.h b/arch/arc/include/asm/prom.h deleted file mode 100644 index 1555549..0000000 --- a/arch/arc/include/asm/prom.h +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright (C) 2012 Synopsys, Inc. (www.synopsys.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef _ASM_ARC_PROM_H_ -#define _ASM_ARC_PROM_H_ - -#endif diff --git a/arch/arm64/include/asm/prom.h b/arch/arm64/include/asm/prom.h deleted file mode 100644 index 68b90e6..0000000 --- a/arch/arm64/include/asm/prom.h +++ /dev/null @@ -1 +0,0 @@ -/* Empty for now */ diff --git a/arch/c6x/include/asm/prom.h b/arch/c6x/include/asm/prom.h deleted file mode 100644 index b4ec95f..0000000 --- a/arch/c6x/include/asm/prom.h +++ /dev/null @@ -1 +0,0 @@ -/* dummy prom.h; here to make linux/of.h's #includes happy */ diff --git a/arch/metag/include/asm/prom.h b/arch/metag/include/asm/prom.h deleted file mode 100644 index d4be144..0000000 --- a/arch/metag/include/asm/prom.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * arch/metag/include/asm/prom.h - * - * Copyright (C) 2012 Imagination Technologies Ltd. - * - * Based on ARM version: - * Copyright (C) 2009 Canonical Ltd. - * - * 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 __ASM_METAG_PROM_H -#define __ASM_METAG_PROM_H - - -extern void copy_fdt(void); - -#endif /* __ASM_METAG_PROM_H */ diff --git a/arch/openrisc/include/asm/prom.h b/arch/openrisc/include/asm/prom.h deleted file mode 100644 index bec477b..0000000 --- a/arch/openrisc/include/asm/prom.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * OpenRISC Linux - * - * Linux architectural port borrowing liberally from similar works of - * others. All original copyrights apply as per the original source - * declaration. - * - * OpenRISC implementation: - * Copyright (C) 2010-2011 Jonas Bonn - * et al. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ -#ifndef _ASM_OPENRISC_PROM_H -#define _ASM_OPENRISC_PROM_H - -#endif /* _ASM_OPENRISC_PROM_H */ diff --git a/arch/xtensa/include/asm/prom.h b/arch/xtensa/include/asm/prom.h deleted file mode 100644 index 2a87a58..0000000 --- a/arch/xtensa/include/asm/prom.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef _XTENSA_ASM_PROM_H -#define _XTENSA_ASM_PROM_H - -#endif /* _XTENSA_ASM_PROM_H */ -- cgit v0.10.2 From cd4035e814b04c009ece9939396d06370c53ed44 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Thu, 10 Oct 2013 09:01:25 +0200 Subject: ALSA: hda - Enable surround speakers (when line out is also present) In the case where we have both line out and more than stereo speakers, the speaker DACs will end up in extra_out_nid. In fact, AFAIU, speakers are the only ones that can end up in extra_out_nid, and if we have several of those, they should be surround outputs rather than copy front. BugLink: https://bugs.launchpad.net/bugs/1236965 Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 5b6c4e3..68801ba 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -5395,11 +5395,6 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, snd_hda_codec_setup_stream(codec, mout->hp_out_nid[i], stream_tag, 0, format); - for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) - if (!mout->no_share_stream && mout->extra_out_nid[i]) - snd_hda_codec_setup_stream(codec, - mout->extra_out_nid[i], - stream_tag, 0, format); /* surrounds */ for (i = 1; i < mout->num_dacs; i++) { @@ -5410,6 +5405,20 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 0, format); } + + /* extra surrounds */ + for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) { + int ch = 0; + if (!mout->extra_out_nid[i]) + break; + if (chs >= (i + 1) * 2) + ch = i * 2; + else if (!mout->no_share_stream) + break; + snd_hda_codec_setup_stream(codec, mout->extra_out_nid[i], + stream_tag, ch, format); + } + return 0; } EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_prepare); -- cgit v0.10.2 From 2cb763614c1c5baef58045af9304265075f22d0a Mon Sep 17 00:00:00 2001 From: Dong Zhu Date: Thu, 10 Oct 2013 15:56:18 +0800 Subject: timer stats: Add a 'Collection: active/inactive' line to timer usage statistics We can enable/disable timer statistics collection via: echo [1|0] > /proc/timers_stats and it would be nice if apps had the ability to check what the current collection status is. This patch adds a 'Collection: active/inactive' line to display the current timer collection status. Also bump up the timer stats version to v0.3. Signed-off-by: Dong Zhu Cc: John Stultz Link: http://lkml.kernel.org/r/20131010075618.GH2139@zhudong.nay.redhat.com [ Improved the changelog and the code. ] Signed-off-by: Ingo Molnar diff --git a/kernel/time/timer_stats.c b/kernel/time/timer_stats.c index 0b537f2..1fb08f2 100644 --- a/kernel/time/timer_stats.c +++ b/kernel/time/timer_stats.c @@ -298,15 +298,15 @@ static int tstats_show(struct seq_file *m, void *v) period = ktime_to_timespec(time); ms = period.tv_nsec / 1000000; - seq_puts(m, "Timer Stats Version: v0.2\n"); + seq_puts(m, "Timer Stats Version: v0.3\n"); seq_printf(m, "Sample period: %ld.%03ld s\n", period.tv_sec, ms); if (atomic_read(&overflow_count)) - seq_printf(m, "Overflow: %d entries\n", - atomic_read(&overflow_count)); + seq_printf(m, "Overflow: %d entries\n", atomic_read(&overflow_count)); + seq_printf(m, "Collection: %s\n", timer_stats_active ? "active" : "inactive"); for (i = 0; i < nr_entries; i++) { entry = entries + i; - if (entry->timer_flag & TIMER_STATS_FLAG_DEFERRABLE) { + if (entry->timer_flag & TIMER_STATS_FLAG_DEFERRABLE) { seq_printf(m, "%4luD, %5d %-16s ", entry->count, entry->pid, entry->comm); } else { -- cgit v0.10.2 From cb52c673f8adc4a1cba7b645ff5375b57dae21fa Mon Sep 17 00:00:00 2001 From: Hiep Cao Minh Date: Thu, 10 Oct 2013 17:14:03 +0900 Subject: spi/rspi: Fix 8bit data access, clear buffer The R8A7790 has QSPI module which added into RSPI together. The transmit or receive data should be read from or written to with the longword-, word-, or byte-access width. Modify word- access to byte-access. In 16-bit data register, QSPI send or receive datas access from high 8-bit while RSPI send or receive datas access from low 8-bit on single mode. Modify to reset transmit-receive buffer data and reading dummy after data are transmited. RSPI has a TXMD bit on control register(SPCR) to set transmit-only mode when transmit data or Full-duplex synchronous mode when receive data. In QSPI the TXMD bit is not supported, so after transmit data, dummy should be read and before transmit or receive data the bufer register should be reset. This driver is the implementation of send and receive pio only, DMA is not supported at this time. Without this patch, it will occur error when transmit and receive Signed-off-by: Hiep Cao Minh Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 4c8dbac..58449ad 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -198,6 +198,11 @@ static u16 rspi_read16(struct rspi_data *rspi, u16 offset) /* optional functions */ struct spi_ops { int (*set_config_register)(struct rspi_data *rspi, int access_size); + int (*send_pio)(struct rspi_data *rspi, struct spi_message *mesg, + struct spi_transfer *t); + int (*receive_pio)(struct rspi_data *rspi, struct spi_message *mesg, + struct spi_transfer *t); + }; /* @@ -349,6 +354,43 @@ static int rspi_send_pio(struct rspi_data *rspi, struct spi_message *mesg, return 0; } +static int qspi_send_pio(struct rspi_data *rspi, struct spi_message *mesg, + struct spi_transfer *t) +{ + int remain = t->len; + u8 *data; + + rspi_write8(rspi, SPBFCR_TXRST, QSPI_SPBFCR); + rspi_write8(rspi, 0x00, QSPI_SPBFCR); + + data = (u8 *)t->tx_buf; + while (remain > 0) { + + if (rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE) < 0) { + dev_err(&rspi->master->dev, + "%s: tx empty timeout\n", __func__); + return -ETIMEDOUT; + } + rspi_write8(rspi, *data++, RSPI_SPDR); + + if (rspi_wait_for_interrupt(rspi, SPSR_SPRF, SPCR_SPRIE) < 0) { + dev_err(&rspi->master->dev, + "%s: receive timeout\n", __func__); + return -ETIMEDOUT; + } + rspi_read8(rspi, RSPI_SPDR); + + remain--; + } + + /* Waiting for the last transmition */ + rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE); + + return 0; +} + +#define send_pio(spi, mesg, t) spi->ops->send_pio(spi, mesg, t) + static void rspi_dma_complete(void *arg) { struct rspi_data *rspi = arg; @@ -514,6 +556,51 @@ static int rspi_receive_pio(struct rspi_data *rspi, struct spi_message *mesg, return 0; } +static void qspi_receive_init(struct rspi_data *rspi) +{ + unsigned char spsr; + + spsr = rspi_read8(rspi, RSPI_SPSR); + if (spsr & SPSR_SPRF) + rspi_read8(rspi, RSPI_SPDR); /* dummy read */ + rspi_write8(rspi, SPBFCR_TXRST | SPBFCR_RXRST, QSPI_SPBFCR); + rspi_write8(rspi, 0x00, QSPI_SPBFCR); +} + +static int qspi_receive_pio(struct rspi_data *rspi, struct spi_message *mesg, + struct spi_transfer *t) +{ + int remain = t->len; + u8 *data; + + qspi_receive_init(rspi); + + data = (u8 *)t->rx_buf; + while (remain > 0) { + + if (rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE) < 0) { + dev_err(&rspi->master->dev, + "%s: tx empty timeout\n", __func__); + return -ETIMEDOUT; + } + /* dummy write for generate clock */ + rspi_write8(rspi, 0x00, RSPI_SPDR); + + if (rspi_wait_for_interrupt(rspi, SPSR_SPRF, SPCR_SPRIE) < 0) { + dev_err(&rspi->master->dev, + "%s: receive timeout\n", __func__); + return -ETIMEDOUT; + } + /* SPDR allows 8, 16 or 32-bit access */ + *data++ = rspi_read8(rspi, RSPI_SPDR); + remain--; + } + + return 0; +} + +#define receive_pio(spi, mesg, t) spi->ops->receive_pio(spi, mesg, t) + static int rspi_receive_dma(struct rspi_data *rspi, struct spi_transfer *t) { struct scatterlist sg, sg_dummy; @@ -653,7 +740,7 @@ static void rspi_work(struct work_struct *work) if (rspi_is_dma(rspi, t)) ret = rspi_send_dma(rspi, t); else - ret = rspi_send_pio(rspi, mesg, t); + ret = send_pio(rspi, mesg, t); if (ret < 0) goto error; } @@ -661,7 +748,7 @@ static void rspi_work(struct work_struct *work) if (rspi_is_dma(rspi, t)) ret = rspi_receive_dma(rspi, t); else - ret = rspi_receive_pio(rspi, mesg, t); + ret = receive_pio(rspi, mesg, t); if (ret < 0) goto error; } @@ -918,10 +1005,14 @@ error1: static struct spi_ops rspi_ops = { .set_config_register = rspi_set_config_register, + .send_pio = rspi_send_pio, + .receive_pio = rspi_receive_pio, }; static struct spi_ops qspi_ops = { .set_config_register = qspi_set_config_register, + .send_pio = qspi_send_pio, + .receive_pio = qspi_receive_pio, }; static struct platform_device_id spi_driver_ids[] = { -- cgit v0.10.2 From eb270e98e15b9f4303b074ba5d88ee98110bc451 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 9 Oct 2013 13:52:52 +0100 Subject: ASoC: dapm: Use async I/O for DAPM sequences Within a DAPM sequence we normally don't care about when exactly a register write has completed so long as they happen in the order we requested. This means that we can issue most of the writes we do asynchronously which should maximise the ability of the underlying frameworks to keep the hardware busy, providing a small performance improvement on some systems. We currently ensure that all writes are completed both when changing to a different device and when calling into the regulator and clock frameworks. This should ensure that the previous ordering is maintained. We also ensure that writes are completed prior to calling into widget event functions since some event functions implement delays. This should be improved in future so that widgets can disable this sync in order to add extra writes. Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 9273216..1dbc5f8 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -409,6 +409,12 @@ static inline void soc_widget_unlock(struct snd_soc_dapm_widget *w) mutex_unlock(&w->platform->mutex); } +static void soc_dapm_async_complete(struct snd_soc_dapm_context *dapm) +{ + if (dapm->codec && dapm->codec->using_regmap) + regmap_async_complete(dapm->codec->control_data); +} + static int soc_widget_update_bits_locked(struct snd_soc_dapm_widget *w, unsigned short reg, unsigned int mask, unsigned int value) { @@ -417,8 +423,9 @@ static int soc_widget_update_bits_locked(struct snd_soc_dapm_widget *w, int ret; if (w->codec && w->codec->using_regmap) { - ret = regmap_update_bits_check(w->codec->control_data, - reg, mask, value, &change); + ret = regmap_update_bits_check_async(w->codec->control_data, + reg, mask, value, + &change); if (ret != 0) return ret; } else { @@ -1201,6 +1208,8 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w, { int ret; + soc_dapm_async_complete(w->dapm); + if (SND_SOC_DAPM_EVENT_ON(event)) { if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) { ret = regulator_allow_bypass(w->regulator, false); @@ -1234,6 +1243,8 @@ int dapm_clock_event(struct snd_soc_dapm_widget *w, if (!w->clk) return -EIO; + soc_dapm_async_complete(w->dapm); + #ifdef CONFIG_HAVE_CLK if (SND_SOC_DAPM_EVENT_ON(event)) { return clk_prepare_enable(w->clk); @@ -1426,6 +1437,7 @@ static void dapm_seq_check_event(struct snd_soc_card *card, if (w->event && (w->event_flags & event)) { pop_dbg(w->dapm->dev, card->pop_time, "pop test : %s %s\n", w->name, ev_name); + soc_dapm_async_complete(w->dapm); trace_snd_soc_dapm_widget_event_start(w, event); ret = w->event(w, NULL, event); trace_snd_soc_dapm_widget_event_done(w, event); @@ -1498,6 +1510,7 @@ static void dapm_seq_run(struct snd_soc_card *card, struct list_head *list, int event, bool power_up) { struct snd_soc_dapm_widget *w, *n; + struct snd_soc_dapm_context *d; LIST_HEAD(pending); int cur_sort = -1; int cur_subseq = -1; @@ -1528,6 +1541,9 @@ static void dapm_seq_run(struct snd_soc_card *card, cur_subseq); } + if (cur_dapm && w->dapm != cur_dapm) + soc_dapm_async_complete(cur_dapm); + INIT_LIST_HEAD(&pending); cur_sort = -1; cur_subseq = INT_MIN; @@ -1586,6 +1602,10 @@ static void dapm_seq_run(struct snd_soc_card *card, cur_dapm->seq_notifier(cur_dapm, i, cur_subseq); } + + list_for_each_entry(d, &card->dapm_list, list) { + soc_dapm_async_complete(d); + } } static void dapm_widget_update(struct snd_soc_card *card) -- cgit v0.10.2 From 1dd275b60e5db4d0bb3763490b519176dcfc4308 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 9 Oct 2013 13:56:37 +0100 Subject: ASoC: dapm: Run clock and regulator events separately to other supplies In order to avoid trying to use an external clock or supply for an on-chip supply prior to it being enabled move the clock and regulator supply events to a separate step in DAPM sequencing from normal supply events. This should have minimal practical impact since these widgets are sorted using SND_SOC_NOPM which is a negative value and hence sorted separately to any real register writes, though it may be relevant if supplies have event callbacks only. Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 1dbc5f8..2fb0b72 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -59,31 +59,31 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, /* dapm power sequences - make this per codec in the future */ static int dapm_up_seq[] = { [snd_soc_dapm_pre] = 0, - [snd_soc_dapm_supply] = 1, [snd_soc_dapm_regulator_supply] = 1, [snd_soc_dapm_clock_supply] = 1, - [snd_soc_dapm_micbias] = 2, + [snd_soc_dapm_supply] = 2, + [snd_soc_dapm_micbias] = 3, [snd_soc_dapm_dai_link] = 2, - [snd_soc_dapm_dai_in] = 3, - [snd_soc_dapm_dai_out] = 3, - [snd_soc_dapm_aif_in] = 3, - [snd_soc_dapm_aif_out] = 3, - [snd_soc_dapm_mic] = 4, - [snd_soc_dapm_mux] = 5, - [snd_soc_dapm_virt_mux] = 5, - [snd_soc_dapm_value_mux] = 5, - [snd_soc_dapm_dac] = 6, - [snd_soc_dapm_switch] = 7, - [snd_soc_dapm_mixer] = 7, - [snd_soc_dapm_mixer_named_ctl] = 7, - [snd_soc_dapm_pga] = 8, - [snd_soc_dapm_adc] = 9, - [snd_soc_dapm_out_drv] = 10, - [snd_soc_dapm_hp] = 10, - [snd_soc_dapm_spk] = 10, - [snd_soc_dapm_line] = 10, - [snd_soc_dapm_kcontrol] = 11, - [snd_soc_dapm_post] = 12, + [snd_soc_dapm_dai_in] = 4, + [snd_soc_dapm_dai_out] = 4, + [snd_soc_dapm_aif_in] = 4, + [snd_soc_dapm_aif_out] = 4, + [snd_soc_dapm_mic] = 5, + [snd_soc_dapm_mux] = 6, + [snd_soc_dapm_virt_mux] = 6, + [snd_soc_dapm_value_mux] = 6, + [snd_soc_dapm_dac] = 7, + [snd_soc_dapm_switch] = 8, + [snd_soc_dapm_mixer] = 8, + [snd_soc_dapm_mixer_named_ctl] = 8, + [snd_soc_dapm_pga] = 9, + [snd_soc_dapm_adc] = 10, + [snd_soc_dapm_out_drv] = 11, + [snd_soc_dapm_hp] = 11, + [snd_soc_dapm_spk] = 11, + [snd_soc_dapm_line] = 11, + [snd_soc_dapm_kcontrol] = 12, + [snd_soc_dapm_post] = 13, }; static int dapm_down_seq[] = { @@ -109,10 +109,10 @@ static int dapm_down_seq[] = { [snd_soc_dapm_dai_in] = 10, [snd_soc_dapm_dai_out] = 10, [snd_soc_dapm_dai_link] = 11, - [snd_soc_dapm_clock_supply] = 12, - [snd_soc_dapm_regulator_supply] = 12, [snd_soc_dapm_supply] = 12, - [snd_soc_dapm_post] = 13, + [snd_soc_dapm_clock_supply] = 13, + [snd_soc_dapm_regulator_supply] = 13, + [snd_soc_dapm_post] = 14, }; static void pop_wait(u32 pop_time) -- cgit v0.10.2 From c8ce878206076b159ee9488133aa51314570da38 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 2 Oct 2013 21:20:29 +0530 Subject: pincntrl: add support for ams AS3722 pin control driver The AS3722 is a compact system PMU suitable for mobile phones, tablets etc. Add a driver to support accessing the GPIO, pinmux and pin configuration of 8 GPIO pins found on the ams AS3722 through pin control driver and gpiolib. The driver will register itself as the pincontrol driver and gpio driver. Signed-off-by: Laxman Dewangan Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 21db201..85f5462 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -59,6 +59,17 @@ config PINCTRL_ADI2 future processors. This option is selected automatically when specific machine and arch are selected to build. +config PINCTRL_AS3722 + bool "Pinctrl and GPIO driver for ams AS3722 PMIC" + depends on MFD_AS3722 && GPIOLIB + select PINMUX + select GENERIC_PINCONF + help + AS3722 device supports the configuration of GPIO pins for different + functionality. This driver supports the pinmux, push-pull and + open drain configuration for the GPIO pins of AS3722 devices. It also + supports the GPIO functionality through gpiolib. + config PINCTRL_BF54x def_bool y if BF54x select PINCTRL_ADI2 diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index bbeb980..3d14cb8 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_PINCTRL_AB8540) += pinctrl-ab8540.o obj-$(CONFIG_PINCTRL_AB9540) += pinctrl-ab9540.o obj-$(CONFIG_PINCTRL_AB8505) += pinctrl-ab8505.o obj-$(CONFIG_PINCTRL_ADI2) += pinctrl-adi2.o +obj-$(CONFIG_PINCTRL_AS3722) += pinctrl-as3722.o obj-$(CONFIG_PINCTRL_BF54x) += pinctrl-adi2-bf54x.o obj-$(CONFIG_PINCTRL_BF60x) += pinctrl-adi2-bf60x.o obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o diff --git a/drivers/pinctrl/pinctrl-as3722.c b/drivers/pinctrl/pinctrl-as3722.c new file mode 100644 index 0000000..01bffc1 --- /dev/null +++ b/drivers/pinctrl/pinctrl-as3722.c @@ -0,0 +1,630 @@ +/* + * ams AS3722 pin control and GPIO driver. + * + * Copyright (c) 2013, NVIDIA Corporation. + * + * Author: Laxman Dewangan + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "pinconf.h" +#include "pinctrl-utils.h" + +#define AS3722_PIN_GPIO0 0 +#define AS3722_PIN_GPIO1 1 +#define AS3722_PIN_GPIO2 2 +#define AS3722_PIN_GPIO3 3 +#define AS3722_PIN_GPIO4 4 +#define AS3722_PIN_GPIO5 5 +#define AS3722_PIN_GPIO6 6 +#define AS3722_PIN_GPIO7 7 +#define AS3722_PIN_NUM (AS3722_PIN_GPIO7 + 1) + +#define AS3722_GPIO_MODE_PULL_UP BIT(PIN_CONFIG_BIAS_PULL_UP) +#define AS3722_GPIO_MODE_PULL_DOWN BIT(PIN_CONFIG_BIAS_PULL_DOWN) +#define AS3722_GPIO_MODE_HIGH_IMPED BIT(PIN_CONFIG_BIAS_HIGH_IMPEDANCE) +#define AS3722_GPIO_MODE_OPEN_DRAIN BIT(PIN_CONFIG_DRIVE_OPEN_DRAIN) + +struct as3722_pin_function { + const char *name; + const char * const *groups; + unsigned ngroups; + int mux_option; +}; + +struct as3722_gpio_pin_control { + bool enable_gpio_invert; + unsigned mode_prop; + int io_function; +}; + +struct as3722_pingroup { + const char *name; + const unsigned pins[1]; + unsigned npins; +}; + +struct as3722_pctrl_info { + struct device *dev; + struct pinctrl_dev *pctl; + struct as3722 *as3722; + struct gpio_chip gpio_chip; + int pins_current_opt[AS3722_PIN_NUM]; + const struct as3722_pin_function *functions; + unsigned num_functions; + const struct as3722_pingroup *pin_groups; + int num_pin_groups; + const struct pinctrl_pin_desc *pins; + unsigned num_pins; + struct as3722_gpio_pin_control gpio_control[AS3722_PIN_NUM]; +}; + +static const struct pinctrl_pin_desc as3722_pins_desc[] = { + PINCTRL_PIN(AS3722_PIN_GPIO0, "gpio0"), + PINCTRL_PIN(AS3722_PIN_GPIO1, "gpio1"), + PINCTRL_PIN(AS3722_PIN_GPIO2, "gpio2"), + PINCTRL_PIN(AS3722_PIN_GPIO3, "gpio3"), + PINCTRL_PIN(AS3722_PIN_GPIO4, "gpio4"), + PINCTRL_PIN(AS3722_PIN_GPIO5, "gpio5"), + PINCTRL_PIN(AS3722_PIN_GPIO6, "gpio6"), + PINCTRL_PIN(AS3722_PIN_GPIO7, "gpio7"), +}; + +static const char * const gpio_groups[] = { + "gpio0", + "gpio1", + "gpio2", + "gpio3", + "gpio4", + "gpio5", + "gpio6", + "gpio7", +}; + +enum as3722_pinmux_option { + AS3722_PINMUX_GPIO = 0, + AS3722_PINMUX_INTERRUPT_OUT = 1, + AS3722_PINMUX_VSUB_VBAT_UNDEB_LOW_OUT = 2, + AS3722_PINMUX_GPIO_INTERRUPT = 3, + AS3722_PINMUX_PWM_INPUT = 4, + AS3722_PINMUX_VOLTAGE_IN_STBY = 5, + AS3722_PINMUX_OC_PG_SD0 = 6, + AS3722_PINMUX_PG_OUT = 7, + AS3722_PINMUX_CLK32K_OUT = 8, + AS3722_PINMUX_WATCHDOG_INPUT = 9, + AS3722_PINMUX_SOFT_RESET_IN = 11, + AS3722_PINMUX_PWM_OUTPUT = 12, + AS3722_PINMUX_VSUB_VBAT_LOW_DEB_OUT = 13, + AS3722_PINMUX_OC_PG_SD6 = 14, +}; + +#define FUNCTION_GROUP(fname, mux) \ + { \ + .name = #fname, \ + .groups = gpio_groups, \ + .ngroups = ARRAY_SIZE(gpio_groups), \ + .mux_option = AS3722_PINMUX_##mux, \ + } + +static const struct as3722_pin_function as3722_pin_function[] = { + FUNCTION_GROUP(gpio, GPIO), + FUNCTION_GROUP(interrupt-out, INTERRUPT_OUT), + FUNCTION_GROUP(gpio-in-interrupt, GPIO_INTERRUPT), + FUNCTION_GROUP(vsup-vbat-low-undebounce-out, VSUB_VBAT_UNDEB_LOW_OUT), + FUNCTION_GROUP(vsup-vbat-low-debounce-out, VSUB_VBAT_LOW_DEB_OUT), + FUNCTION_GROUP(voltage-in-standby, VOLTAGE_IN_STBY), + FUNCTION_GROUP(oc-pg-sd0, OC_PG_SD0), + FUNCTION_GROUP(oc-pg-sd6, OC_PG_SD6), + FUNCTION_GROUP(powergood-out, PG_OUT), + FUNCTION_GROUP(pwm-in, PWM_INPUT), + FUNCTION_GROUP(pwm-out, PWM_OUTPUT), + FUNCTION_GROUP(clk32k-out, CLK32K_OUT), + FUNCTION_GROUP(watchdog-in, WATCHDOG_INPUT), + FUNCTION_GROUP(soft-reset-in, SOFT_RESET_IN), +}; + +#define AS3722_PINGROUP(pg_name, pin_id) \ + { \ + .name = #pg_name, \ + .pins = {AS3722_PIN_##pin_id}, \ + .npins = 1, \ + } + +static const struct as3722_pingroup as3722_pingroups[] = { + AS3722_PINGROUP(gpio0, GPIO0), + AS3722_PINGROUP(gpio1, GPIO1), + AS3722_PINGROUP(gpio2, GPIO2), + AS3722_PINGROUP(gpio3, GPIO3), + AS3722_PINGROUP(gpio4, GPIO4), + AS3722_PINGROUP(gpio5, GPIO5), + AS3722_PINGROUP(gpio6, GPIO6), + AS3722_PINGROUP(gpio7, GPIO7), +}; + +static int as3722_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct as3722_pctrl_info *as_pci = pinctrl_dev_get_drvdata(pctldev); + + return as_pci->num_pin_groups; +} + +static const char *as3722_pinctrl_get_group_name(struct pinctrl_dev *pctldev, + unsigned group) +{ + struct as3722_pctrl_info *as_pci = pinctrl_dev_get_drvdata(pctldev); + + return as_pci->pin_groups[group].name; +} + +static int as3722_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned group, const unsigned **pins, unsigned *num_pins) +{ + struct as3722_pctrl_info *as_pci = pinctrl_dev_get_drvdata(pctldev); + + *pins = as_pci->pin_groups[group].pins; + *num_pins = as_pci->pin_groups[group].npins; + return 0; +} + +static const struct pinctrl_ops as3722_pinctrl_ops = { + .get_groups_count = as3722_pinctrl_get_groups_count, + .get_group_name = as3722_pinctrl_get_group_name, + .get_group_pins = as3722_pinctrl_get_group_pins, + .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, + .dt_free_map = pinctrl_utils_dt_free_map, +}; + +static int as3722_pinctrl_get_funcs_count(struct pinctrl_dev *pctldev) +{ + struct as3722_pctrl_info *as_pci = pinctrl_dev_get_drvdata(pctldev); + + return as_pci->num_functions; +} + +static const char *as3722_pinctrl_get_func_name(struct pinctrl_dev *pctldev, + unsigned function) +{ + struct as3722_pctrl_info *as_pci = pinctrl_dev_get_drvdata(pctldev); + + return as_pci->functions[function].name; +} + +static int as3722_pinctrl_get_func_groups(struct pinctrl_dev *pctldev, + unsigned function, const char * const **groups, + unsigned * const num_groups) +{ + struct as3722_pctrl_info *as_pci = pinctrl_dev_get_drvdata(pctldev); + + *groups = as_pci->functions[function].groups; + *num_groups = as_pci->functions[function].ngroups; + return 0; +} + +static int as3722_pinctrl_enable(struct pinctrl_dev *pctldev, unsigned function, + unsigned group) +{ + struct as3722_pctrl_info *as_pci = pinctrl_dev_get_drvdata(pctldev); + int gpio_cntr_reg = AS3722_GPIOn_CONTROL_REG(group); + u8 val = AS3722_GPIO_IOSF_VAL(as_pci->functions[function].mux_option); + int ret; + + dev_dbg(as_pci->dev, "%s(): GPIO %u pin to function %u and val %u\n", + __func__, group, function, val); + + ret = as3722_update_bits(as_pci->as3722, gpio_cntr_reg, + AS3722_GPIO_IOSF_MASK, val); + if (ret < 0) { + dev_err(as_pci->dev, "GPIO%d_CTRL_REG update failed %d\n", + group, ret); + return ret; + } + as_pci->gpio_control[group].io_function = function; + return ret; +} + +static int as3722_pinctrl_gpio_get_mode(unsigned gpio_mode_prop, bool input) +{ + if (gpio_mode_prop & AS3722_GPIO_MODE_HIGH_IMPED) + return -EINVAL; + + if (gpio_mode_prop & AS3722_GPIO_MODE_OPEN_DRAIN) { + if (gpio_mode_prop & AS3722_GPIO_MODE_PULL_UP) + return AS3722_GPIO_MODE_IO_OPEN_DRAIN_PULL_UP; + return AS3722_GPIO_MODE_IO_OPEN_DRAIN; + } + if (input) { + if (gpio_mode_prop & AS3722_GPIO_MODE_PULL_UP) + return AS3722_GPIO_MODE_INPUT_PULL_UP; + else if (gpio_mode_prop & AS3722_GPIO_MODE_PULL_DOWN) + return AS3722_GPIO_MODE_INPUT_PULL_DOWN; + return AS3722_GPIO_MODE_INPUT; + } + if (gpio_mode_prop & AS3722_GPIO_MODE_PULL_DOWN) + return AS3722_GPIO_MODE_OUTPUT_VDDL; + return AS3722_GPIO_MODE_OUTPUT_VDDH; +} + +static int as3722_pinctrl_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, unsigned offset) +{ + struct as3722_pctrl_info *as_pci = pinctrl_dev_get_drvdata(pctldev); + + if (as_pci->gpio_control[offset].io_function) + return -EBUSY; + return 0; +} + +static int as3722_pinctrl_gpio_set_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, unsigned offset, bool input) +{ + struct as3722_pctrl_info *as_pci = pinctrl_dev_get_drvdata(pctldev); + struct as3722 *as3722 = as_pci->as3722; + int mode; + + mode = as3722_pinctrl_gpio_get_mode( + as_pci->gpio_control[offset].mode_prop, input); + if (mode < 0) { + dev_err(as_pci->dev, "%s direction for GPIO %d not supported\n", + (input) ? "Input" : "Output", offset); + return mode; + } + + if (as_pci->gpio_control[offset].enable_gpio_invert) + mode |= AS3722_GPIO_INV; + + return as3722_write(as3722, AS3722_GPIOn_CONTROL_REG(offset), mode); +} + +static const struct pinmux_ops as3722_pinmux_ops = { + .get_functions_count = as3722_pinctrl_get_funcs_count, + .get_function_name = as3722_pinctrl_get_func_name, + .get_function_groups = as3722_pinctrl_get_func_groups, + .enable = as3722_pinctrl_enable, + .gpio_request_enable = as3722_pinctrl_gpio_request_enable, + .gpio_set_direction = as3722_pinctrl_gpio_set_direction, +}; + +static int as3722_pinconf_get(struct pinctrl_dev *pctldev, + unsigned pin, unsigned long *config) +{ + struct as3722_pctrl_info *as_pci = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(*config); + int arg = 0; + u16 prop; + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + prop = AS3722_GPIO_MODE_PULL_UP | + AS3722_GPIO_MODE_PULL_DOWN; + if (!(as_pci->gpio_control[pin].mode_prop & prop)) + arg = 1; + prop = 0; + break; + + case PIN_CONFIG_BIAS_PULL_UP: + prop = AS3722_GPIO_MODE_PULL_UP; + break; + + case PIN_CONFIG_BIAS_PULL_DOWN: + prop = AS3722_GPIO_MODE_PULL_DOWN; + break; + + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + prop = AS3722_GPIO_MODE_OPEN_DRAIN; + break; + + case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: + prop = AS3722_GPIO_MODE_HIGH_IMPED; + break; + + default: + dev_err(as_pci->dev, "Properties not supported\n"); + return -ENOTSUPP; + } + + if (as_pci->gpio_control[pin].mode_prop & prop) + arg = 1; + + *config = pinconf_to_config_packed(param, (u16)arg); + return 0; +} + +static int as3722_pinconf_set(struct pinctrl_dev *pctldev, + unsigned pin, unsigned long *configs, + unsigned num_configs) +{ + struct as3722_pctrl_info *as_pci = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param; + int mode_prop; + int i; + + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + mode_prop = as_pci->gpio_control[pin].mode_prop; + + switch (param) { + case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: + break; + + case PIN_CONFIG_BIAS_DISABLE: + mode_prop &= ~(AS3722_GPIO_MODE_PULL_UP | + AS3722_GPIO_MODE_PULL_DOWN); + break; + case PIN_CONFIG_BIAS_PULL_UP: + mode_prop |= AS3722_GPIO_MODE_PULL_UP; + break; + + case PIN_CONFIG_BIAS_PULL_DOWN: + mode_prop |= AS3722_GPIO_MODE_PULL_DOWN; + break; + + case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: + mode_prop |= AS3722_GPIO_MODE_HIGH_IMPED; + break; + + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + mode_prop |= AS3722_GPIO_MODE_OPEN_DRAIN; + break; + + default: + dev_err(as_pci->dev, "Properties not supported\n"); + return -ENOTSUPP; + } + + as_pci->gpio_control[pin].mode_prop = mode_prop; + } + return 0; +} + +static const struct pinconf_ops as3722_pinconf_ops = { + .pin_config_get = as3722_pinconf_get, + .pin_config_set = as3722_pinconf_set, +}; + +static struct pinctrl_desc as3722_pinctrl_desc = { + .pctlops = &as3722_pinctrl_ops, + .pmxops = &as3722_pinmux_ops, + .confops = &as3722_pinconf_ops, + .owner = THIS_MODULE, +}; + +static inline struct as3722_pctrl_info *to_as_pci(struct gpio_chip *chip) +{ + return container_of(chip, struct as3722_pctrl_info, gpio_chip); +} + +static int as3722_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct as3722_pctrl_info *as_pci = to_as_pci(chip); + struct as3722 *as3722 = as_pci->as3722; + int ret; + u32 reg; + u32 control; + u32 val; + int mode; + int invert_enable; + + ret = as3722_read(as3722, AS3722_GPIOn_CONTROL_REG(offset), &control); + if (ret < 0) { + dev_err(as_pci->dev, + "GPIO_CONTROL%d_REG read failed: %d\n", offset, ret); + return ret; + } + + invert_enable = !!(control & AS3722_GPIO_INV); + mode = control & AS3722_GPIO_MODE_MASK; + switch (mode) { + case AS3722_GPIO_MODE_INPUT: + case AS3722_GPIO_MODE_INPUT_PULL_UP: + case AS3722_GPIO_MODE_INPUT_PULL_DOWN: + case AS3722_GPIO_MODE_IO_OPEN_DRAIN: + case AS3722_GPIO_MODE_IO_OPEN_DRAIN_PULL_UP: + reg = AS3722_GPIO_SIGNAL_IN_REG; + break; + case AS3722_GPIO_MODE_OUTPUT_VDDH: + case AS3722_GPIO_MODE_OUTPUT_VDDL: + reg = AS3722_GPIO_SIGNAL_OUT_REG; + break; + default: + return -EINVAL; + } + + ret = as3722_read(as3722, reg, &val); + if (ret < 0) { + dev_err(as_pci->dev, + "GPIO_SIGNAL_IN_REG read failed: %d\n", ret); + return ret; + } + + val = !!(val & AS3722_GPIOn_SIGNAL(offset)); + return (invert_enable) ? !val : val; +} + +static void as3722_gpio_set(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct as3722_pctrl_info *as_pci = to_as_pci(chip); + struct as3722 *as3722 = as_pci->as3722; + int en_invert = as_pci->gpio_control[offset].enable_gpio_invert; + u32 val; + int ret; + + if (value) + val = (en_invert) ? 0 : AS3722_GPIOn_SIGNAL(offset); + else + val = (en_invert) ? AS3722_GPIOn_SIGNAL(offset) : 0; + + ret = as3722_update_bits(as3722, AS3722_GPIO_SIGNAL_OUT_REG, + AS3722_GPIOn_SIGNAL(offset), val); + if (ret < 0) + dev_err(as_pci->dev, + "GPIO_SIGNAL_OUT_REG update failed: %d\n", ret); +} + +static int as3722_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + return pinctrl_gpio_direction_input(chip->base + offset); +} + +static int as3722_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + as3722_gpio_set(chip, offset, value); + return pinctrl_gpio_direction_output(chip->base + offset); +} + +static int as3722_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct as3722_pctrl_info *as_pci = to_as_pci(chip); + + return as3722_irq_get_virq(as_pci->as3722, offset); +} + +static int as3722_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + return pinctrl_request_gpio(chip->base + offset); +} + +static void as3722_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + pinctrl_free_gpio(chip->base + offset); +} + +static const struct gpio_chip as3722_gpio_chip = { + .label = "as3722-gpio", + .owner = THIS_MODULE, + .request = as3722_gpio_request, + .free = as3722_gpio_free, + .get = as3722_gpio_get, + .set = as3722_gpio_set, + .direction_input = as3722_gpio_direction_input, + .direction_output = as3722_gpio_direction_output, + .to_irq = as3722_gpio_to_irq, + .can_sleep = 1, + .ngpio = AS3722_PIN_NUM, + .base = -1, +}; + +static int as3722_pinctrl_probe(struct platform_device *pdev) +{ + struct as3722_pctrl_info *as_pci; + int ret; + int tret; + + as_pci = devm_kzalloc(&pdev->dev, sizeof(*as_pci), GFP_KERNEL); + if (!as_pci) + return -ENOMEM; + + as_pci->dev = &pdev->dev; + as_pci->dev->of_node = pdev->dev.parent->of_node; + as_pci->as3722 = dev_get_drvdata(pdev->dev.parent); + platform_set_drvdata(pdev, as_pci); + + as_pci->pins = as3722_pins_desc; + as_pci->num_pins = ARRAY_SIZE(as3722_pins_desc); + as_pci->functions = as3722_pin_function; + as_pci->num_functions = ARRAY_SIZE(as3722_pin_function); + as_pci->pin_groups = as3722_pingroups; + as_pci->num_pin_groups = ARRAY_SIZE(as3722_pingroups); + as3722_pinctrl_desc.name = dev_name(&pdev->dev); + as3722_pinctrl_desc.pins = as3722_pins_desc; + as3722_pinctrl_desc.npins = ARRAY_SIZE(as3722_pins_desc); + as_pci->pctl = pinctrl_register(&as3722_pinctrl_desc, + &pdev->dev, as_pci); + if (!as_pci->pctl) { + dev_err(&pdev->dev, "Couldn't register pinctrl driver\n"); + return -EINVAL; + } + + as_pci->gpio_chip = as3722_gpio_chip; + as_pci->gpio_chip.dev = &pdev->dev; + as_pci->gpio_chip.of_node = pdev->dev.parent->of_node; + ret = gpiochip_add(&as_pci->gpio_chip); + if (ret < 0) { + dev_err(&pdev->dev, "Couldn't register gpiochip, %d\n", ret); + goto fail_chip_add; + } + + ret = gpiochip_add_pin_range(&as_pci->gpio_chip, dev_name(&pdev->dev), + 0, 0, AS3722_PIN_NUM); + if (ret < 0) { + dev_err(&pdev->dev, "Couldn't add pin range, %d\n", ret); + goto fail_range_add; + } + + return 0; + +fail_range_add: + tret = gpiochip_remove(&as_pci->gpio_chip); + if (tret < 0) + dev_warn(&pdev->dev, "Couldn't remove gpio chip, %d\n", tret); + +fail_chip_add: + pinctrl_unregister(as_pci->pctl); + return ret; +} + +static int as3722_pinctrl_remove(struct platform_device *pdev) +{ + struct as3722_pctrl_info *as_pci = platform_get_drvdata(pdev); + int ret; + + ret = gpiochip_remove(&as_pci->gpio_chip); + if (ret < 0) + return ret; + pinctrl_unregister(as_pci->pctl); + return 0; +} + +static struct of_device_id as3722_pinctrl_of_match[] = { + { .compatible = "ams,as3722-pinctrl", }, + { }, +}; +MODULE_DEVICE_TABLE(of, as3722_pinctrl_of_match); + +static struct platform_driver as3722_pinctrl_driver = { + .driver = { + .name = "as3722-pinctrl", + .owner = THIS_MODULE, + .of_match_table = as3722_pinctrl_of_match, + }, + .probe = as3722_pinctrl_probe, + .remove = as3722_pinctrl_remove, +}; +module_platform_driver(as3722_pinctrl_driver); + +MODULE_ALIAS("platform:as3722-pinctrl"); +MODULE_DESCRIPTION("AS3722 pin control and GPIO driver"); +MODULE_AUTHOR("Laxman Dewangan"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From cf9eb39c6f7b42fdf9aa6f1af28780b2522a847a Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Thu, 10 Oct 2013 17:19:17 +0300 Subject: spi: Fix modalias for ACPI enumerated SPI devices There is a minor fault about ACPI enumerated SPI devices with their modalias attribute. Now modalias is set by device instance not by hardware ID. For example "spi:INTABCD:00", "spi:INTABCD:01" etc. This means each device instance gets different modalias which does match with generated modules.alias. Currently this is not problem as matching can happen also with "acpi:INTABCD" modalias. Fix this by using ACPI hardware ID. Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 9e039c6..e57e9ac 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1025,7 +1025,7 @@ static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level, return AE_OK; } - strlcpy(spi->modalias, dev_name(&adev->dev), sizeof(spi->modalias)); + strlcpy(spi->modalias, acpi_device_hid(adev), sizeof(spi->modalias)); if (spi_add_device(spi)) { dev_err(&master->dev, "failed to add SPI device %s from ACPI\n", dev_name(&adev->dev)); -- cgit v0.10.2 From 9e7827b5ea4ca93b4d864bc07c0fafb838d496b1 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Mon, 30 Sep 2013 17:28:52 +0200 Subject: x86, hyperv: Get the local APIC timer frequency from the hypervisor Hyper-V supports a mechanism for retrieving the local APIC frequency. Use this and bypass the calibration code in the kernel . This would allow us to boot the Linux kernel as a "modern VM" on Hyper-V where many of the legacy devices (such as PIT) are not emulated. I would like to thank Olaf Hering , Jan Beulich and H. Peter Anvin for their help in this effort. In this version of the patch, I have addressed Jan's comments. Signed-off-by: K. Y. Srinivasan Link: http://lkml.kernel.org/r/1380554932-9888-1-git-send-email-olaf@aepfle.de Tested-by: Olaf Hering Signed-off-by: H. Peter Anvin diff --git a/arch/x86/include/uapi/asm/hyperv.h b/arch/x86/include/uapi/asm/hyperv.h index b80420b..b8f1c01 100644 --- a/arch/x86/include/uapi/asm/hyperv.h +++ b/arch/x86/include/uapi/asm/hyperv.h @@ -27,6 +27,19 @@ #define HV_X64_MSR_VP_RUNTIME_AVAILABLE (1 << 0) /* Partition Reference Counter (HV_X64_MSR_TIME_REF_COUNT) available*/ #define HV_X64_MSR_TIME_REF_COUNT_AVAILABLE (1 << 1) + +/* + * There is a single feature flag that signifies the presence of the MSR + * that can be used to retrieve both the local APIC Timer frequency as + * well as the TSC frequency. + */ + +/* Local APIC timer frequency MSR (HV_X64_MSR_APIC_FREQUENCY) is available */ +#define HV_X64_MSR_APIC_FREQUENCY_AVAILABLE (1 << 11) + +/* TSC frequency MSR (HV_X64_MSR_TSC_FREQUENCY) is available */ +#define HV_X64_MSR_TSC_FREQUENCY_AVAILABLE (1 << 11) + /* * Basic SynIC MSRs (HV_X64_MSR_SCONTROL through HV_X64_MSR_EOM * and HV_X64_MSR_SINT0 through HV_X64_MSR_SINT15) available @@ -136,6 +149,12 @@ /* MSR used to read the per-partition time reference counter */ #define HV_X64_MSR_TIME_REF_COUNT 0x40000020 +/* MSR used to retrieve the TSC frequency */ +#define HV_X64_MSR_TSC_FREQUENCY 0x40000022 + +/* MSR used to retrieve the local APIC timer frequency */ +#define HV_X64_MSR_APIC_FREQUENCY 0x40000023 + /* Define the virtual APIC registers */ #define HV_X64_MSR_EOI 0x40000070 #define HV_X64_MSR_ICR 0x40000071 diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c index 71a39f3..0a490ca 100644 --- a/arch/x86/kernel/cpu/mshyperv.c +++ b/arch/x86/kernel/cpu/mshyperv.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,7 @@ #include #include #include +#include struct ms_hyperv_info ms_hyperv; EXPORT_SYMBOL_GPL(ms_hyperv); @@ -67,6 +69,8 @@ static struct clocksource hyperv_cs = { static void __init ms_hyperv_init_platform(void) { + u64 hv_lapic_frequency; + /* * Extract the features and hints */ @@ -76,6 +80,26 @@ static void __init ms_hyperv_init_platform(void) printk(KERN_INFO "HyperV: features 0x%x, hints 0x%x\n", ms_hyperv.features, ms_hyperv.hints); + if (ms_hyperv.features & HV_X64_MSR_APIC_FREQUENCY_AVAILABLE) { + /* + * Get the APIC frequency. + */ + rdmsrl(HV_X64_MSR_APIC_FREQUENCY, hv_lapic_frequency); + hv_lapic_frequency = div_u64(hv_lapic_frequency, HZ); + lapic_timer_frequency = hv_lapic_frequency; + printk(KERN_INFO "HyperV: LAPIC Timer Frequency: %#x\n", + lapic_timer_frequency); + + /* + * On Hyper-V, when we are booting off an EFI firmware stack, + * we do not have many legacy devices including PIC, PIT etc. + */ + if (efi_enabled(EFI_BOOT)) { + printk(KERN_INFO "HyperV: Using null_legacy_pic\n"); + legacy_pic = &null_legacy_pic; + } + } + if (ms_hyperv.features & HV_X64_MSR_TIME_REF_COUNT_AVAILABLE) clocksource_register_hz(&hyperv_cs, NSEC_PER_SEC/100); } -- cgit v0.10.2 From 1a25f26138cde2b83fd74ead6da0bbd4b6c42b60 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 10 Oct 2013 20:55:03 +0100 Subject: regmap: Use async I/O for patch application Try to speed up patch application a little using async I/O. Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 0503d86..71282e1 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -2029,6 +2029,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, bypass = map->cache_bypass; map->cache_bypass = true; + map->async = true; /* Write out first; it's useful to apply even if we fail later. */ for (i = 0; i < num_regs; i++) { @@ -2052,10 +2053,13 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, } out: + map->async = false; map->cache_bypass = bypass; map->unlock(map->lock_arg); + regmap_async_complete(map); + return ret; } EXPORT_SYMBOL_GPL(regmap_register_patch); -- cgit v0.10.2 From affbe886e712437c25b575eac5fde5886bb42aec Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 10 Oct 2013 21:06:32 +0100 Subject: regmap: Use async I/O during cache sync Try to speed up I/O a little by not synchronising until we are finished scheduling writes. A brief survey of existing users suggests we have none that would currently benefit from an async cache sync. Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index a36112a..d4dd771 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -307,6 +307,8 @@ int regcache_sync(struct regmap *map) if (!map->cache_dirty) goto out; + map->async = true; + /* Apply any patch first */ map->cache_bypass = 1; for (i = 0; i < map->patch_regs; i++) { @@ -332,11 +334,15 @@ int regcache_sync(struct regmap *map) map->cache_dirty = false; out: - trace_regcache_sync(map->dev, name, "stop"); /* Restore the bypass state */ + map->async = false; map->cache_bypass = bypass; map->unlock(map->lock_arg); + regmap_async_complete(map); + + trace_regcache_sync(map->dev, name, "stop"); + return ret; } EXPORT_SYMBOL_GPL(regcache_sync); @@ -375,17 +381,23 @@ int regcache_sync_region(struct regmap *map, unsigned int min, if (!map->cache_dirty) goto out; + map->async = true; + if (map->cache_ops->sync) ret = map->cache_ops->sync(map, min, max); else ret = regcache_default_sync(map, min, max); out: - trace_regcache_sync(map->dev, name, "stop region"); /* Restore the bypass state */ map->cache_bypass = bypass; + map->async = false; map->unlock(map->lock_arg); + regmap_async_complete(map); + + trace_regcache_sync(map->dev, name, "stop region"); + return ret; } EXPORT_SYMBOL_GPL(regcache_sync_region); -- cgit v0.10.2 From 90ab9d5510932e146a9443bf5a591f95d5b5ada8 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 10 Oct 2013 15:30:24 -0700 Subject: x86, hyperv: Correctly guard the local APIC calibration code The code that gets the local APIC timer frequency from the hypervisor rather depends on there being a local APIC. Signed-off-by: K. Y. Srinivasan Link: http://lkml.kernel.org/r/1381444224-3303-1-git-send-email-kys@microsoft.com Signed-off-by: H. Peter Anvin diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c index 0a490ca..628ff50 100644 --- a/arch/x86/kernel/cpu/mshyperv.c +++ b/arch/x86/kernel/cpu/mshyperv.c @@ -80,6 +80,7 @@ static void __init ms_hyperv_init_platform(void) printk(KERN_INFO "HyperV: features 0x%x, hints 0x%x\n", ms_hyperv.features, ms_hyperv.hints); +#ifdef CONFIG_X86_LOCAL_APIC if (ms_hyperv.features & HV_X64_MSR_APIC_FREQUENCY_AVAILABLE) { /* * Get the APIC frequency. @@ -99,6 +100,7 @@ static void __init ms_hyperv_init_platform(void) legacy_pic = &null_legacy_pic; } } +#endif if (ms_hyperv.features & HV_X64_MSR_TIME_REF_COUNT_AVAILABLE) clocksource_register_hz(&hyperv_cs, NSEC_PER_SEC/100); -- cgit v0.10.2 From 88f182dd779b9d350b4774c12d16633a5b60f50c Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Thu, 10 Oct 2013 10:16:30 +0200 Subject: x86: Apply the asm_volatile_goto() compiler quirk Apply the asm_volatile_goto() compiler quirk to the new rmwcc.h file as well, introduced in: c2daa3bed53a sched, x86: Provide a per-cpu preempt_count implementation Reported-and-tested-by: Fengguang Wu Reported-by: Oleg Nesterov Reported-by: Peter Zijlstra Suggested-by: Jakub Jelinek Reviewed-by: Richard Henderson Cc: Linus Torvalds Cc: Andrew Morton Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/rmwcc.h b/arch/x86/include/asm/rmwcc.h index 735f184..1ff990f 100644 --- a/arch/x86/include/asm/rmwcc.h +++ b/arch/x86/include/asm/rmwcc.h @@ -5,7 +5,7 @@ #define __GEN_RMWcc(fullop, var, cc, ...) \ do { \ - asm volatile goto (fullop "; j" cc " %l[cc_label]" \ + asm_volatile_goto (fullop "; j" cc " %l[cc_label]" \ : : "m" (var), ## __VA_ARGS__ \ : "memory" : cc_label); \ return 0; \ -- cgit v0.10.2 From 32ee1e188eadd7c997837649a107fd1c50feef7a Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 23 Sep 2013 12:04:35 +1000 Subject: powerpc: Fix endian issues in VMX copy loops Fix the permute loops for little endian. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/lib/copyuser_power7.S b/arch/powerpc/lib/copyuser_power7.S index d1f1179..e8e9c36 100644 --- a/arch/powerpc/lib/copyuser_power7.S +++ b/arch/powerpc/lib/copyuser_power7.S @@ -19,6 +19,14 @@ */ #include +#ifdef __BIG_ENDIAN__ +#define LVS(VRT,RA,RB) lvsl VRT,RA,RB +#define VPERM(VRT,VRA,VRB,VRC) vperm VRT,VRA,VRB,VRC +#else +#define LVS(VRT,RA,RB) lvsr VRT,RA,RB +#define VPERM(VRT,VRA,VRB,VRC) vperm VRT,VRB,VRA,VRC +#endif + .macro err1 100: .section __ex_table,"a" @@ -552,13 +560,13 @@ err3; stw r7,4(r3) li r10,32 li r11,48 - lvsl vr16,0,r4 /* Setup permute control vector */ + LVS(vr16,0,r4) /* Setup permute control vector */ err3; lvx vr0,0,r4 addi r4,r4,16 bf cr7*4+3,5f err3; lvx vr1,r0,r4 - vperm vr8,vr0,vr1,vr16 + VPERM(vr8,vr0,vr1,vr16) addi r4,r4,16 err3; stvx vr8,r0,r3 addi r3,r3,16 @@ -566,9 +574,9 @@ err3; stvx vr8,r0,r3 5: bf cr7*4+2,6f err3; lvx vr1,r0,r4 - vperm vr8,vr0,vr1,vr16 + VPERM(vr8,vr0,vr1,vr16) err3; lvx vr0,r4,r9 - vperm vr9,vr1,vr0,vr16 + VPERM(vr9,vr1,vr0,vr16) addi r4,r4,32 err3; stvx vr8,r0,r3 err3; stvx vr9,r3,r9 @@ -576,13 +584,13 @@ err3; stvx vr9,r3,r9 6: bf cr7*4+1,7f err3; lvx vr3,r0,r4 - vperm vr8,vr0,vr3,vr16 + VPERM(vr8,vr0,vr3,vr16) err3; lvx vr2,r4,r9 - vperm vr9,vr3,vr2,vr16 + VPERM(vr9,vr3,vr2,vr16) err3; lvx vr1,r4,r10 - vperm vr10,vr2,vr1,vr16 + VPERM(vr10,vr2,vr1,vr16) err3; lvx vr0,r4,r11 - vperm vr11,vr1,vr0,vr16 + VPERM(vr11,vr1,vr0,vr16) addi r4,r4,64 err3; stvx vr8,r0,r3 err3; stvx vr9,r3,r9 @@ -611,21 +619,21 @@ err3; stvx vr11,r3,r11 .align 5 8: err4; lvx vr7,r0,r4 - vperm vr8,vr0,vr7,vr16 + VPERM(vr8,vr0,vr7,vr16) err4; lvx vr6,r4,r9 - vperm vr9,vr7,vr6,vr16 + VPERM(vr9,vr7,vr6,vr16) err4; lvx vr5,r4,r10 - vperm vr10,vr6,vr5,vr16 + VPERM(vr10,vr6,vr5,vr16) err4; lvx vr4,r4,r11 - vperm vr11,vr5,vr4,vr16 + VPERM(vr11,vr5,vr4,vr16) err4; lvx vr3,r4,r12 - vperm vr12,vr4,vr3,vr16 + VPERM(vr12,vr4,vr3,vr16) err4; lvx vr2,r4,r14 - vperm vr13,vr3,vr2,vr16 + VPERM(vr13,vr3,vr2,vr16) err4; lvx vr1,r4,r15 - vperm vr14,vr2,vr1,vr16 + VPERM(vr14,vr2,vr1,vr16) err4; lvx vr0,r4,r16 - vperm vr15,vr1,vr0,vr16 + VPERM(vr15,vr1,vr0,vr16) addi r4,r4,128 err4; stvx vr8,r0,r3 err4; stvx vr9,r3,r9 @@ -649,13 +657,13 @@ err4; stvx vr15,r3,r16 bf cr7*4+1,9f err3; lvx vr3,r0,r4 - vperm vr8,vr0,vr3,vr16 + VPERM(vr8,vr0,vr3,vr16) err3; lvx vr2,r4,r9 - vperm vr9,vr3,vr2,vr16 + VPERM(vr9,vr3,vr2,vr16) err3; lvx vr1,r4,r10 - vperm vr10,vr2,vr1,vr16 + VPERM(vr10,vr2,vr1,vr16) err3; lvx vr0,r4,r11 - vperm vr11,vr1,vr0,vr16 + VPERM(vr11,vr1,vr0,vr16) addi r4,r4,64 err3; stvx vr8,r0,r3 err3; stvx vr9,r3,r9 @@ -665,9 +673,9 @@ err3; stvx vr11,r3,r11 9: bf cr7*4+2,10f err3; lvx vr1,r0,r4 - vperm vr8,vr0,vr1,vr16 + VPERM(vr8,vr0,vr1,vr16) err3; lvx vr0,r4,r9 - vperm vr9,vr1,vr0,vr16 + VPERM(vr9,vr1,vr0,vr16) addi r4,r4,32 err3; stvx vr8,r0,r3 err3; stvx vr9,r3,r9 @@ -675,7 +683,7 @@ err3; stvx vr9,r3,r9 10: bf cr7*4+3,11f err3; lvx vr1,r0,r4 - vperm vr8,vr0,vr1,vr16 + VPERM(vr8,vr0,vr1,vr16) addi r4,r4,16 err3; stvx vr8,r0,r3 addi r3,r3,16 diff --git a/arch/powerpc/lib/memcpy_power7.S b/arch/powerpc/lib/memcpy_power7.S index 0663630..e4177db 100644 --- a/arch/powerpc/lib/memcpy_power7.S +++ b/arch/powerpc/lib/memcpy_power7.S @@ -20,6 +20,15 @@ #include _GLOBAL(memcpy_power7) + +#ifdef __BIG_ENDIAN__ +#define LVS(VRT,RA,RB) lvsl VRT,RA,RB +#define VPERM(VRT,VRA,VRB,VRC) vperm VRT,VRA,VRB,VRC +#else +#define LVS(VRT,RA,RB) lvsr VRT,RA,RB +#define VPERM(VRT,VRA,VRB,VRC) vperm VRT,VRB,VRA,VRC +#endif + #ifdef CONFIG_ALTIVEC cmpldi r5,16 cmpldi cr1,r5,4096 @@ -485,13 +494,13 @@ _GLOBAL(memcpy_power7) li r10,32 li r11,48 - lvsl vr16,0,r4 /* Setup permute control vector */ + LVS(vr16,0,r4) /* Setup permute control vector */ lvx vr0,0,r4 addi r4,r4,16 bf cr7*4+3,5f lvx vr1,r0,r4 - vperm vr8,vr0,vr1,vr16 + VPERM(vr8,vr0,vr1,vr16) addi r4,r4,16 stvx vr8,r0,r3 addi r3,r3,16 @@ -499,9 +508,9 @@ _GLOBAL(memcpy_power7) 5: bf cr7*4+2,6f lvx vr1,r0,r4 - vperm vr8,vr0,vr1,vr16 + VPERM(vr8,vr0,vr1,vr16) lvx vr0,r4,r9 - vperm vr9,vr1,vr0,vr16 + VPERM(vr9,vr1,vr0,vr16) addi r4,r4,32 stvx vr8,r0,r3 stvx vr9,r3,r9 @@ -509,13 +518,13 @@ _GLOBAL(memcpy_power7) 6: bf cr7*4+1,7f lvx vr3,r0,r4 - vperm vr8,vr0,vr3,vr16 + VPERM(vr8,vr0,vr3,vr16) lvx vr2,r4,r9 - vperm vr9,vr3,vr2,vr16 + VPERM(vr9,vr3,vr2,vr16) lvx vr1,r4,r10 - vperm vr10,vr2,vr1,vr16 + VPERM(vr10,vr2,vr1,vr16) lvx vr0,r4,r11 - vperm vr11,vr1,vr0,vr16 + VPERM(vr11,vr1,vr0,vr16) addi r4,r4,64 stvx vr8,r0,r3 stvx vr9,r3,r9 @@ -544,21 +553,21 @@ _GLOBAL(memcpy_power7) .align 5 8: lvx vr7,r0,r4 - vperm vr8,vr0,vr7,vr16 + VPERM(vr8,vr0,vr7,vr16) lvx vr6,r4,r9 - vperm vr9,vr7,vr6,vr16 + VPERM(vr9,vr7,vr6,vr16) lvx vr5,r4,r10 - vperm vr10,vr6,vr5,vr16 + VPERM(vr10,vr6,vr5,vr16) lvx vr4,r4,r11 - vperm vr11,vr5,vr4,vr16 + VPERM(vr11,vr5,vr4,vr16) lvx vr3,r4,r12 - vperm vr12,vr4,vr3,vr16 + VPERM(vr12,vr4,vr3,vr16) lvx vr2,r4,r14 - vperm vr13,vr3,vr2,vr16 + VPERM(vr13,vr3,vr2,vr16) lvx vr1,r4,r15 - vperm vr14,vr2,vr1,vr16 + VPERM(vr14,vr2,vr1,vr16) lvx vr0,r4,r16 - vperm vr15,vr1,vr0,vr16 + VPERM(vr15,vr1,vr0,vr16) addi r4,r4,128 stvx vr8,r0,r3 stvx vr9,r3,r9 @@ -582,13 +591,13 @@ _GLOBAL(memcpy_power7) bf cr7*4+1,9f lvx vr3,r0,r4 - vperm vr8,vr0,vr3,vr16 + VPERM(vr8,vr0,vr3,vr16) lvx vr2,r4,r9 - vperm vr9,vr3,vr2,vr16 + VPERM(vr9,vr3,vr2,vr16) lvx vr1,r4,r10 - vperm vr10,vr2,vr1,vr16 + VPERM(vr10,vr2,vr1,vr16) lvx vr0,r4,r11 - vperm vr11,vr1,vr0,vr16 + VPERM(vr11,vr1,vr0,vr16) addi r4,r4,64 stvx vr8,r0,r3 stvx vr9,r3,r9 @@ -598,9 +607,9 @@ _GLOBAL(memcpy_power7) 9: bf cr7*4+2,10f lvx vr1,r0,r4 - vperm vr8,vr0,vr1,vr16 + VPERM(vr8,vr0,vr1,vr16) lvx vr0,r4,r9 - vperm vr9,vr1,vr0,vr16 + VPERM(vr9,vr1,vr0,vr16) addi r4,r4,32 stvx vr8,r0,r3 stvx vr9,r3,r9 @@ -608,7 +617,7 @@ _GLOBAL(memcpy_power7) 10: bf cr7*4+3,11f lvx vr1,r0,r4 - vperm vr8,vr0,vr1,vr16 + VPERM(vr8,vr0,vr1,vr16) addi r4,r4,16 stvx vr8,r0,r3 addi r3,r3,16 -- cgit v0.10.2 From 12f04f2be80dd8d9da24534828f3ab3189ca5af2 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 23 Sep 2013 12:04:36 +1000 Subject: powerpc: Book 3S MMU little endian support Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/mmu-hash64.h b/arch/powerpc/include/asm/mmu-hash64.h index c4cf011..807014d 100644 --- a/arch/powerpc/include/asm/mmu-hash64.h +++ b/arch/powerpc/include/asm/mmu-hash64.h @@ -135,8 +135,8 @@ extern char initial_stab[]; #ifndef __ASSEMBLY__ struct hash_pte { - unsigned long v; - unsigned long r; + __be64 v; + __be64 r; }; extern struct hash_pte *htab_address; diff --git a/arch/powerpc/mm/hash_native_64.c b/arch/powerpc/mm/hash_native_64.c index c33d939..3ea26c2 100644 --- a/arch/powerpc/mm/hash_native_64.c +++ b/arch/powerpc/mm/hash_native_64.c @@ -35,7 +35,11 @@ #define DBG_LOW(fmt...) #endif +#ifdef __BIG_ENDIAN__ #define HPTE_LOCK_BIT 3 +#else +#define HPTE_LOCK_BIT (56+3) +#endif DEFINE_RAW_SPINLOCK(native_tlbie_lock); @@ -172,7 +176,7 @@ static inline void tlbie(unsigned long vpn, int psize, int apsize, static inline void native_lock_hpte(struct hash_pte *hptep) { - unsigned long *word = &hptep->v; + unsigned long *word = (unsigned long *)&hptep->v; while (1) { if (!test_and_set_bit_lock(HPTE_LOCK_BIT, word)) @@ -184,7 +188,7 @@ static inline void native_lock_hpte(struct hash_pte *hptep) static inline void native_unlock_hpte(struct hash_pte *hptep) { - unsigned long *word = &hptep->v; + unsigned long *word = (unsigned long *)&hptep->v; clear_bit_unlock(HPTE_LOCK_BIT, word); } @@ -204,10 +208,10 @@ static long native_hpte_insert(unsigned long hpte_group, unsigned long vpn, } for (i = 0; i < HPTES_PER_GROUP; i++) { - if (! (hptep->v & HPTE_V_VALID)) { + if (! (be64_to_cpu(hptep->v) & HPTE_V_VALID)) { /* retry with lock held */ native_lock_hpte(hptep); - if (! (hptep->v & HPTE_V_VALID)) + if (! (be64_to_cpu(hptep->v) & HPTE_V_VALID)) break; native_unlock_hpte(hptep); } @@ -226,14 +230,14 @@ static long native_hpte_insert(unsigned long hpte_group, unsigned long vpn, i, hpte_v, hpte_r); } - hptep->r = hpte_r; + hptep->r = cpu_to_be64(hpte_r); /* Guarantee the second dword is visible before the valid bit */ eieio(); /* * Now set the first dword including the valid bit * NOTE: this also unlocks the hpte */ - hptep->v = hpte_v; + hptep->v = cpu_to_be64(hpte_v); __asm__ __volatile__ ("ptesync" : : : "memory"); @@ -254,12 +258,12 @@ static long native_hpte_remove(unsigned long hpte_group) for (i = 0; i < HPTES_PER_GROUP; i++) { hptep = htab_address + hpte_group + slot_offset; - hpte_v = hptep->v; + hpte_v = be64_to_cpu(hptep->v); if ((hpte_v & HPTE_V_VALID) && !(hpte_v & HPTE_V_BOLTED)) { /* retry with lock held */ native_lock_hpte(hptep); - hpte_v = hptep->v; + hpte_v = be64_to_cpu(hptep->v); if ((hpte_v & HPTE_V_VALID) && !(hpte_v & HPTE_V_BOLTED)) break; @@ -294,7 +298,7 @@ static long native_hpte_updatepp(unsigned long slot, unsigned long newpp, native_lock_hpte(hptep); - hpte_v = hptep->v; + hpte_v = be64_to_cpu(hptep->v); /* * We need to invalidate the TLB always because hpte_remove doesn't do * a tlb invalidate. If a hash bucket gets full, we "evict" a more/less @@ -308,8 +312,8 @@ static long native_hpte_updatepp(unsigned long slot, unsigned long newpp, } else { DBG_LOW(" -> hit\n"); /* Update the HPTE */ - hptep->r = (hptep->r & ~(HPTE_R_PP | HPTE_R_N)) | - (newpp & (HPTE_R_PP | HPTE_R_N | HPTE_R_C)); + hptep->r = cpu_to_be64((be64_to_cpu(hptep->r) & ~(HPTE_R_PP | HPTE_R_N)) | + (newpp & (HPTE_R_PP | HPTE_R_N | HPTE_R_C))); } native_unlock_hpte(hptep); @@ -334,7 +338,7 @@ static long native_hpte_find(unsigned long vpn, int psize, int ssize) slot = (hash & htab_hash_mask) * HPTES_PER_GROUP; for (i = 0; i < HPTES_PER_GROUP; i++) { hptep = htab_address + slot; - hpte_v = hptep->v; + hpte_v = be64_to_cpu(hptep->v); if (HPTE_V_COMPARE(hpte_v, want_v) && (hpte_v & HPTE_V_VALID)) /* HPTE matches */ @@ -369,8 +373,9 @@ static void native_hpte_updateboltedpp(unsigned long newpp, unsigned long ea, hptep = htab_address + slot; /* Update the HPTE */ - hptep->r = (hptep->r & ~(HPTE_R_PP | HPTE_R_N)) | - (newpp & (HPTE_R_PP | HPTE_R_N)); + hptep->r = cpu_to_be64((be64_to_cpu(hptep->r) & + ~(HPTE_R_PP | HPTE_R_N)) | + (newpp & (HPTE_R_PP | HPTE_R_N))); /* * Ensure it is out of the tlb too. Bolted entries base and * actual page size will be same. @@ -392,7 +397,7 @@ static void native_hpte_invalidate(unsigned long slot, unsigned long vpn, want_v = hpte_encode_avpn(vpn, bpsize, ssize); native_lock_hpte(hptep); - hpte_v = hptep->v; + hpte_v = be64_to_cpu(hptep->v); /* * We need to invalidate the TLB always because hpte_remove doesn't do @@ -458,7 +463,7 @@ static void native_hugepage_invalidate(struct mm_struct *mm, hptep = htab_address + slot; want_v = hpte_encode_avpn(vpn, psize, ssize); native_lock_hpte(hptep); - hpte_v = hptep->v; + hpte_v = be64_to_cpu(hptep->v); /* Even if we miss, we need to invalidate the TLB */ if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID)) @@ -519,11 +524,12 @@ static void hpte_decode(struct hash_pte *hpte, unsigned long slot, int *psize, int *apsize, int *ssize, unsigned long *vpn) { unsigned long avpn, pteg, vpi; - unsigned long hpte_v = hpte->v; + unsigned long hpte_v = be64_to_cpu(hpte->v); + unsigned long hpte_r = be64_to_cpu(hpte->r); unsigned long vsid, seg_off; int size, a_size, shift; /* Look at the 8 bit LP value */ - unsigned int lp = (hpte->r >> LP_SHIFT) & ((1 << LP_BITS) - 1); + unsigned int lp = (hpte_r >> LP_SHIFT) & ((1 << LP_BITS) - 1); if (!(hpte_v & HPTE_V_LARGE)) { size = MMU_PAGE_4K; @@ -612,7 +618,7 @@ static void native_hpte_clear(void) * running, right? and for crash dump, we probably * don't want to wait for a maybe bad cpu. */ - hpte_v = hptep->v; + hpte_v = be64_to_cpu(hptep->v); /* * Call __tlbie() here rather than tlbie() since we @@ -664,7 +670,7 @@ static void native_flush_hash_range(unsigned long number, int local) hptep = htab_address + slot; want_v = hpte_encode_avpn(vpn, psize, ssize); native_lock_hpte(hptep); - hpte_v = hptep->v; + hpte_v = be64_to_cpu(hptep->v); if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID)) native_unlock_hpte(hptep); diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c index bde8b55..6176b3c 100644 --- a/arch/powerpc/mm/hash_utils_64.c +++ b/arch/powerpc/mm/hash_utils_64.c @@ -251,19 +251,18 @@ static int __init htab_dt_scan_seg_sizes(unsigned long node, void *data) { char *type = of_get_flat_dt_prop(node, "device_type", NULL); - u32 *prop; + __be32 *prop; unsigned long size = 0; /* We are scanning "cpu" nodes only */ if (type == NULL || strcmp(type, "cpu") != 0) return 0; - prop = (u32 *)of_get_flat_dt_prop(node, "ibm,processor-segment-sizes", - &size); + prop = of_get_flat_dt_prop(node, "ibm,processor-segment-sizes", &size); if (prop == NULL) return 0; for (; size >= 4; size -= 4, ++prop) { - if (prop[0] == 40) { + if (be32_to_cpu(prop[0]) == 40) { DBG("1T segment support detected\n"); cur_cpu_spec->mmu_features |= MMU_FTR_1T_SEGMENT; return 1; @@ -307,23 +306,22 @@ static int __init htab_dt_scan_page_sizes(unsigned long node, void *data) { char *type = of_get_flat_dt_prop(node, "device_type", NULL); - u32 *prop; + __be32 *prop; unsigned long size = 0; /* We are scanning "cpu" nodes only */ if (type == NULL || strcmp(type, "cpu") != 0) return 0; - prop = (u32 *)of_get_flat_dt_prop(node, - "ibm,segment-page-sizes", &size); + prop = of_get_flat_dt_prop(node, "ibm,segment-page-sizes", &size); if (prop != NULL) { pr_info("Page sizes from device-tree:\n"); size /= 4; cur_cpu_spec->mmu_features &= ~(MMU_FTR_16M_PAGE); while(size > 0) { - unsigned int base_shift = prop[0]; - unsigned int slbenc = prop[1]; - unsigned int lpnum = prop[2]; + unsigned int base_shift = be32_to_cpu(prop[0]); + unsigned int slbenc = be32_to_cpu(prop[1]); + unsigned int lpnum = be32_to_cpu(prop[2]); struct mmu_psize_def *def; int idx, base_idx; @@ -356,8 +354,8 @@ static int __init htab_dt_scan_page_sizes(unsigned long node, def->tlbiel = 0; while (size > 0 && lpnum) { - unsigned int shift = prop[0]; - int penc = prop[1]; + unsigned int shift = be32_to_cpu(prop[0]); + int penc = be32_to_cpu(prop[1]); prop += 2; size -= 2; lpnum--; @@ -390,8 +388,8 @@ static int __init htab_dt_scan_hugepage_blocks(unsigned long node, const char *uname, int depth, void *data) { char *type = of_get_flat_dt_prop(node, "device_type", NULL); - unsigned long *addr_prop; - u32 *page_count_prop; + __be64 *addr_prop; + __be32 *page_count_prop; unsigned int expected_pages; long unsigned int phys_addr; long unsigned int block_size; @@ -405,12 +403,12 @@ static int __init htab_dt_scan_hugepage_blocks(unsigned long node, page_count_prop = of_get_flat_dt_prop(node, "ibm,expected#pages", NULL); if (page_count_prop == NULL) return 0; - expected_pages = (1 << page_count_prop[0]); + expected_pages = (1 << be32_to_cpu(page_count_prop[0])); addr_prop = of_get_flat_dt_prop(node, "reg", NULL); if (addr_prop == NULL) return 0; - phys_addr = addr_prop[0]; - block_size = addr_prop[1]; + phys_addr = be64_to_cpu(addr_prop[0]); + block_size = be64_to_cpu(addr_prop[1]); if (block_size != (16 * GB)) return 0; printk(KERN_INFO "Huge page(16GB) memory: " @@ -534,16 +532,16 @@ static int __init htab_dt_scan_pftsize(unsigned long node, void *data) { char *type = of_get_flat_dt_prop(node, "device_type", NULL); - u32 *prop; + __be32 *prop; /* We are scanning "cpu" nodes only */ if (type == NULL || strcmp(type, "cpu") != 0) return 0; - prop = (u32 *)of_get_flat_dt_prop(node, "ibm,pft-size", NULL); + prop = of_get_flat_dt_prop(node, "ibm,pft-size", NULL); if (prop != NULL) { /* pft_size[0] is the NUMA CEC cookie */ - ppc64_pft_size = prop[1]; + ppc64_pft_size = be32_to_cpu(prop[1]); return 1; } return 0; -- cgit v0.10.2 From e156bd8ad76939a9bcd66d85cf06f8cde1fb8030 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 23 Sep 2013 12:04:37 +1000 Subject: powerpc: Fix offset of FPRs in VSX registers in little endian builds The FPRs overlap the high doublewords of the first 32 VSX registers. Fix TS_FPROFFSET and TS_VSRLOWOFFSET so we access the correct fields in little endian mode. If VSX is disabled the FPRs are only one doubleword in length so TS_FPROFFSET needs adjusting in little endian. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h index ce4de5a..82c6ee9 100644 --- a/arch/powerpc/include/asm/processor.h +++ b/arch/powerpc/include/asm/processor.h @@ -14,8 +14,18 @@ #ifdef CONFIG_VSX #define TS_FPRWIDTH 2 + +#ifdef __BIG_ENDIAN__ +#define TS_FPROFFSET 0 +#define TS_VSRLOWOFFSET 1 +#else +#define TS_FPROFFSET 1 +#define TS_VSRLOWOFFSET 0 +#endif + #else #define TS_FPRWIDTH 1 +#define TS_FPROFFSET 0 #endif #ifdef CONFIG_PPC64 @@ -142,8 +152,6 @@ typedef struct { unsigned long seg; } mm_segment_t; -#define TS_FPROFFSET 0 -#define TS_VSRLOWOFFSET 1 #define TS_FPR(i) fpr[i][TS_FPROFFSET] #define TS_TRANS_FPR(i) transact_fpr[i][TS_FPROFFSET] -- cgit v0.10.2 From 87fec0514f613f8ac43c01b0bc0bc7072c5d10ae Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 23 Sep 2013 12:04:38 +1000 Subject: powerpc: PTRACE_PEEKUSR/PTRACE_POKEUSER of FPR registers in little endian builds FPRs overlap the high 64bits of the first 32 VSX registers. The ptrace FP read/write code assumes big endian ordering and grabs the lowest 64 bits. Fix this by using the TS_FPR macro which does the right thing. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index 9a0d24c..8d5d4e9 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c @@ -1554,8 +1554,8 @@ long arch_ptrace(struct task_struct *child, long request, flush_fp_to_thread(child); if (fpidx < (PT_FPSCR - PT_FPR0)) - tmp = ((unsigned long *)child->thread.fpr) - [fpidx * TS_FPRWIDTH]; + memcpy(&tmp, &child->thread.TS_FPR(fpidx), + sizeof(long)); else tmp = child->thread.fpscr.val; } @@ -1587,8 +1587,8 @@ long arch_ptrace(struct task_struct *child, long request, flush_fp_to_thread(child); if (fpidx < (PT_FPSCR - PT_FPR0)) - ((unsigned long *)child->thread.fpr) - [fpidx * TS_FPRWIDTH] = data; + memcpy(&child->thread.TS_FPR(fpidx), &data, + sizeof(long)); else child->thread.fpscr.val = data; ret = 0; -- cgit v0.10.2 From 926f160f460170b0361f600f365369685ad74009 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 23 Sep 2013 12:04:39 +1000 Subject: powerpc: Little endian builds double word swap VSX state during context save/restore The elements within VSX loads and stores are big endian ordered regardless of endianness. Our VSX context save/restore code uses lxvd2x and stxvd2x which is a 2x doubleword operation. This means the two doublewords will be swapped and we have to perform another swap to undo it. We need to do this on save and restore. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h index d7fe9f5..ad5fcf5 100644 --- a/arch/powerpc/include/asm/ppc-opcode.h +++ b/arch/powerpc/include/asm/ppc-opcode.h @@ -181,6 +181,7 @@ #define PPC_INST_TLBIVAX 0x7c000624 #define PPC_INST_TLBSRX_DOT 0x7c0006a5 #define PPC_INST_XXLOR 0xf0000510 +#define PPC_INST_XXSWAPD 0xf0000250 #define PPC_INST_XVCPSGNDP 0xf0000780 #define PPC_INST_TRECHKPT 0x7c0007dd #define PPC_INST_TRECLAIM 0x7c00075d @@ -344,6 +345,8 @@ VSX_XX1((s), a, b)) #define XXLOR(t, a, b) stringify_in_c(.long PPC_INST_XXLOR | \ VSX_XX3((t), a, b)) +#define XXSWAPD(t, a) stringify_in_c(.long PPC_INST_XXSWAPD | \ + VSX_XX3((t), a, a)) #define XVCPSGNDP(t, a, b) stringify_in_c(.long (PPC_INST_XVCPSGNDP | \ VSX_XX3((t), (a), (b)))) diff --git a/arch/powerpc/include/asm/ppc_asm.h b/arch/powerpc/include/asm/ppc_asm.h index 5995457..0c51fb4 100644 --- a/arch/powerpc/include/asm/ppc_asm.h +++ b/arch/powerpc/include/asm/ppc_asm.h @@ -180,9 +180,20 @@ END_FW_FTR_SECTION_IFSET(FW_FEATURE_SPLPAR) #define REST_32VRS_TRANSACT(n,b,base) REST_16VRS_TRANSACT(n,b,base); \ REST_16VRS_TRANSACT(n+16,b,base) +#ifdef __BIG_ENDIAN__ +#define STXVD2X_ROT(n,b,base) STXVD2X(n,b,base) +#define LXVD2X_ROT(n,b,base) LXVD2X(n,b,base) +#else +#define STXVD2X_ROT(n,b,base) XXSWAPD(n,n); \ + STXVD2X(n,b,base); \ + XXSWAPD(n,n) + +#define LXVD2X_ROT(n,b,base) LXVD2X(n,b,base); \ + XXSWAPD(n,n) +#endif #define SAVE_VSR_TRANSACT(n,b,base) li b,THREAD_TRANSACT_VSR0+(16*(n)); \ - STXVD2X(n,R##base,R##b) + STXVD2X_ROT(n,R##base,R##b) #define SAVE_2VSRS_TRANSACT(n,b,base) SAVE_VSR_TRANSACT(n,b,base); \ SAVE_VSR_TRANSACT(n+1,b,base) #define SAVE_4VSRS_TRANSACT(n,b,base) SAVE_2VSRS_TRANSACT(n,b,base); \ @@ -195,7 +206,7 @@ END_FW_FTR_SECTION_IFSET(FW_FEATURE_SPLPAR) SAVE_16VSRS_TRANSACT(n+16,b,base) #define REST_VSR_TRANSACT(n,b,base) li b,THREAD_TRANSACT_VSR0+(16*(n)); \ - LXVD2X(n,R##base,R##b) + LXVD2X_ROT(n,R##base,R##b) #define REST_2VSRS_TRANSACT(n,b,base) REST_VSR_TRANSACT(n,b,base); \ REST_VSR_TRANSACT(n+1,b,base) #define REST_4VSRS_TRANSACT(n,b,base) REST_2VSRS_TRANSACT(n,b,base); \ @@ -208,13 +219,15 @@ END_FW_FTR_SECTION_IFSET(FW_FEATURE_SPLPAR) REST_16VSRS_TRANSACT(n+16,b,base) /* Save the lower 32 VSRs in the thread VSR region */ -#define SAVE_VSR(n,b,base) li b,THREAD_VSR0+(16*(n)); STXVD2X(n,R##base,R##b) +#define SAVE_VSR(n,b,base) li b,THREAD_VSR0+(16*(n)); \ + STXVD2X_ROT(n,R##base,R##b) #define SAVE_2VSRS(n,b,base) SAVE_VSR(n,b,base); SAVE_VSR(n+1,b,base) #define SAVE_4VSRS(n,b,base) SAVE_2VSRS(n,b,base); SAVE_2VSRS(n+2,b,base) #define SAVE_8VSRS(n,b,base) SAVE_4VSRS(n,b,base); SAVE_4VSRS(n+4,b,base) #define SAVE_16VSRS(n,b,base) SAVE_8VSRS(n,b,base); SAVE_8VSRS(n+8,b,base) #define SAVE_32VSRS(n,b,base) SAVE_16VSRS(n,b,base); SAVE_16VSRS(n+16,b,base) -#define REST_VSR(n,b,base) li b,THREAD_VSR0+(16*(n)); LXVD2X(n,R##base,R##b) +#define REST_VSR(n,b,base) li b,THREAD_VSR0+(16*(n)); \ + LXVD2X_ROT(n,R##base,R##b) #define REST_2VSRS(n,b,base) REST_VSR(n,b,base); REST_VSR(n+1,b,base) #define REST_4VSRS(n,b,base) REST_2VSRS(n,b,base); REST_2VSRS(n+2,b,base) #define REST_8VSRS(n,b,base) REST_4VSRS(n,b,base); REST_4VSRS(n+4,b,base) -- cgit v0.10.2 From 15cba23e69435c86197d6e5035445469ca2b8484 Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Mon, 23 Sep 2013 12:04:40 +1000 Subject: powerpc: Support endian agnostic MMIO This patch maps the MMIO functions for 32bit PowerPC to their appropriate instructions depending on CPU endianness. The macros used to create the corresponding inline functions are also renamed by this patch. Previously they had BE or LE in their names which was misleading - they had nothing to do with endianness, but actually created different instruction forms so their new names reflect the instruction form they are creating (D-Form and X-Form). Little endian 64bit PowerPC is not supported, so the lack of mappings (and corresponding breakage) for that case is intentional to bring the attention of anyone doing a 64bit little endian port. 64bit big endian is unaffected. [ Added 64 bit versions - Anton ] Signed-off-by: Ian Munsie Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/io.h b/arch/powerpc/include/asm/io.h index 5a64757..db1f296 100644 --- a/arch/powerpc/include/asm/io.h +++ b/arch/powerpc/include/asm/io.h @@ -113,7 +113,7 @@ extern bool isa_io_special; /* gcc 4.0 and older doesn't have 'Z' constraint */ #if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ == 0) -#define DEF_MMIO_IN_LE(name, size, insn) \ +#define DEF_MMIO_IN_X(name, size, insn) \ static inline u##size name(const volatile u##size __iomem *addr) \ { \ u##size ret; \ @@ -122,7 +122,7 @@ static inline u##size name(const volatile u##size __iomem *addr) \ return ret; \ } -#define DEF_MMIO_OUT_LE(name, size, insn) \ +#define DEF_MMIO_OUT_X(name, size, insn) \ static inline void name(volatile u##size __iomem *addr, u##size val) \ { \ __asm__ __volatile__("sync;"#insn" %1,0,%2" \ @@ -130,7 +130,7 @@ static inline void name(volatile u##size __iomem *addr, u##size val) \ IO_SET_SYNC_FLAG(); \ } #else /* newer gcc */ -#define DEF_MMIO_IN_LE(name, size, insn) \ +#define DEF_MMIO_IN_X(name, size, insn) \ static inline u##size name(const volatile u##size __iomem *addr) \ { \ u##size ret; \ @@ -139,7 +139,7 @@ static inline u##size name(const volatile u##size __iomem *addr) \ return ret; \ } -#define DEF_MMIO_OUT_LE(name, size, insn) \ +#define DEF_MMIO_OUT_X(name, size, insn) \ static inline void name(volatile u##size __iomem *addr, u##size val) \ { \ __asm__ __volatile__("sync;"#insn" %1,%y0" \ @@ -148,7 +148,7 @@ static inline void name(volatile u##size __iomem *addr, u##size val) \ } #endif -#define DEF_MMIO_IN_BE(name, size, insn) \ +#define DEF_MMIO_IN_D(name, size, insn) \ static inline u##size name(const volatile u##size __iomem *addr) \ { \ u##size ret; \ @@ -157,7 +157,7 @@ static inline u##size name(const volatile u##size __iomem *addr) \ return ret; \ } -#define DEF_MMIO_OUT_BE(name, size, insn) \ +#define DEF_MMIO_OUT_D(name, size, insn) \ static inline void name(volatile u##size __iomem *addr, u##size val) \ { \ __asm__ __volatile__("sync;"#insn"%U0%X0 %1,%0" \ @@ -165,22 +165,37 @@ static inline void name(volatile u##size __iomem *addr, u##size val) \ IO_SET_SYNC_FLAG(); \ } +DEF_MMIO_IN_D(in_8, 8, lbz); +DEF_MMIO_OUT_D(out_8, 8, stb); -DEF_MMIO_IN_BE(in_8, 8, lbz); -DEF_MMIO_IN_BE(in_be16, 16, lhz); -DEF_MMIO_IN_BE(in_be32, 32, lwz); -DEF_MMIO_IN_LE(in_le16, 16, lhbrx); -DEF_MMIO_IN_LE(in_le32, 32, lwbrx); +#ifdef __BIG_ENDIAN__ +DEF_MMIO_IN_D(in_be16, 16, lhz); +DEF_MMIO_IN_D(in_be32, 32, lwz); +DEF_MMIO_IN_X(in_le16, 16, lhbrx); +DEF_MMIO_IN_X(in_le32, 32, lwbrx); -DEF_MMIO_OUT_BE(out_8, 8, stb); -DEF_MMIO_OUT_BE(out_be16, 16, sth); -DEF_MMIO_OUT_BE(out_be32, 32, stw); -DEF_MMIO_OUT_LE(out_le16, 16, sthbrx); -DEF_MMIO_OUT_LE(out_le32, 32, stwbrx); +DEF_MMIO_OUT_D(out_be16, 16, sth); +DEF_MMIO_OUT_D(out_be32, 32, stw); +DEF_MMIO_OUT_X(out_le16, 16, sthbrx); +DEF_MMIO_OUT_X(out_le32, 32, stwbrx); +#else +DEF_MMIO_IN_X(in_be16, 16, lhbrx); +DEF_MMIO_IN_X(in_be32, 32, lwbrx); +DEF_MMIO_IN_D(in_le16, 16, lhz); +DEF_MMIO_IN_D(in_le32, 32, lwz); + +DEF_MMIO_OUT_X(out_be16, 16, sthbrx); +DEF_MMIO_OUT_X(out_be32, 32, stwbrx); +DEF_MMIO_OUT_D(out_le16, 16, sth); +DEF_MMIO_OUT_D(out_le32, 32, stw); + +#endif /* __BIG_ENDIAN */ #ifdef __powerpc64__ -DEF_MMIO_OUT_BE(out_be64, 64, std); -DEF_MMIO_IN_BE(in_be64, 64, ld); + +#ifdef __BIG_ENDIAN__ +DEF_MMIO_OUT_D(out_be64, 64, std); +DEF_MMIO_IN_D(in_be64, 64, ld); /* There is no asm instructions for 64 bits reverse loads and stores */ static inline u64 in_le64(const volatile u64 __iomem *addr) @@ -192,6 +207,22 @@ static inline void out_le64(volatile u64 __iomem *addr, u64 val) { out_be64(addr, swab64(val)); } +#else +DEF_MMIO_OUT_D(out_le64, 64, std); +DEF_MMIO_IN_D(in_le64, 64, ld); + +/* There is no asm instructions for 64 bits reverse loads and stores */ +static inline u64 in_be64(const volatile u64 __iomem *addr) +{ + return swab64(in_le64(addr)); +} + +static inline void out_be64(volatile u64 __iomem *addr, u64 val) +{ + out_le64(addr, swab64(val)); +} + +#endif #endif /* __powerpc64__ */ /* -- cgit v0.10.2 From 4c74c330c2d84aec9f2b4e87827b08814c266b6b Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 23 Sep 2013 12:04:41 +1000 Subject: powerpc: Add little endian support for word-at-a-time functions The powerpc word-at-a-time functions are big endian specific. Bring in the x86 version in order to support little endian builds. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/word-at-a-time.h b/arch/powerpc/include/asm/word-at-a-time.h index d0b6d4a..213a5f2 100644 --- a/arch/powerpc/include/asm/word-at-a-time.h +++ b/arch/powerpc/include/asm/word-at-a-time.h @@ -8,6 +8,8 @@ #include #include +#ifdef __BIG_ENDIAN__ + struct word_at_a_time { const unsigned long high_bits, low_bits; }; @@ -38,4 +40,73 @@ static inline bool has_zero(unsigned long val, unsigned long *data, const struct return (val + c->high_bits) & ~rhs; } +#else + +/* + * This is largely generic for little-endian machines, but the + * optimal byte mask counting is probably going to be something + * that is architecture-specific. If you have a reliably fast + * bit count instruction, that might be better than the multiply + * and shift, for example. + */ +struct word_at_a_time { + const unsigned long one_bits, high_bits; +}; + +#define WORD_AT_A_TIME_CONSTANTS { REPEAT_BYTE(0x01), REPEAT_BYTE(0x80) } + +#ifdef CONFIG_64BIT + +/* + * Jan Achrenius on G+: microoptimized version of + * the simpler "(mask & ONEBYTES) * ONEBYTES >> 56" + * that works for the bytemasks without having to + * mask them first. + */ +static inline long count_masked_bytes(unsigned long mask) +{ + return mask*0x0001020304050608ul >> 56; +} + +#else /* 32-bit case */ + +/* Carl Chatfield / Jan Achrenius G+ version for 32-bit */ +static inline long count_masked_bytes(long mask) +{ + /* (000000 0000ff 00ffff ffffff) -> ( 1 1 2 3 ) */ + long a = (0x0ff0001+mask) >> 23; + /* Fix the 1 for 00 case */ + return a & mask; +} + +#endif + +/* Return nonzero if it has a zero */ +static inline unsigned long has_zero(unsigned long a, unsigned long *bits, const struct word_at_a_time *c) +{ + unsigned long mask = ((a - c->one_bits) & ~a) & c->high_bits; + *bits = mask; + return mask; +} + +static inline unsigned long prep_zero_mask(unsigned long a, unsigned long bits, const struct word_at_a_time *c) +{ + return bits; +} + +static inline unsigned long create_zero_mask(unsigned long bits) +{ + bits = (bits - 1) & ~bits; + return bits >> 7; +} + +/* The mask we created is directly usable as a bytemask */ +#define zero_bytemask(mask) (mask) + +static inline unsigned long find_zero(unsigned long mask) +{ + return count_masked_bytes(mask); +} +#endif + #endif /* _ASM_WORD_AT_A_TIME_H */ -- cgit v0.10.2 From ef1967ff875d58c290108f9c6872a53a79855c4b Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 23 Sep 2013 12:04:42 +1000 Subject: powerpc: Set MSR_LE bit on little endian builds We need to set MSR_LE in kernel and userspace for little endian builds Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h index 10d1ef0..126f6e9 100644 --- a/arch/powerpc/include/asm/reg.h +++ b/arch/powerpc/include/asm/reg.h @@ -115,7 +115,12 @@ #define MSR_64BIT MSR_SF /* Server variant */ -#define MSR_ (MSR_ME | MSR_RI | MSR_IR | MSR_DR | MSR_ISF |MSR_HV) +#define __MSR (MSR_ME | MSR_RI | MSR_IR | MSR_DR | MSR_ISF |MSR_HV) +#ifdef __BIG_ENDIAN__ +#define MSR_ __MSR +#else +#define MSR_ (__MSR | MSR_LE) +#endif #define MSR_KERNEL (MSR_ | MSR_64BIT) #define MSR_USER32 (MSR_ | MSR_PR | MSR_EE) #define MSR_USER64 (MSR_USER32 | MSR_64BIT) -- cgit v0.10.2 From e871c6bbf66c1b44af0fb925427e957301e2e1ff Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 23 Sep 2013 12:04:43 +1000 Subject: powerpc: Reset MSR_LE on signal entry We always take signals in big endian which is wrong. Signals should be taken in native endian. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index bebdf1a..b386b0b 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c @@ -1045,8 +1045,9 @@ int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka, regs->gpr[5] = (unsigned long) &rt_sf->uc; regs->gpr[6] = (unsigned long) rt_sf; regs->nip = (unsigned long) ka->sa.sa_handler; - /* enter the signal handler in big-endian mode */ + /* enter the signal handler in native-endian mode */ regs->msr &= ~MSR_LE; + regs->msr |= (MSR_KERNEL & MSR_LE); #ifdef CONFIG_PPC_TRANSACTIONAL_MEM /* Remove TM bits from thread's MSR. The MSR in the sigcontext * just indicates to userland that we were doing a transaction, but we diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index f93ec28..8b51b02 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c @@ -773,8 +773,9 @@ int handle_rt_signal64(int signr, struct k_sigaction *ka, siginfo_t *info, /* Set up "regs" so we "return" to the signal handler. */ err |= get_user(regs->nip, &funct_desc_ptr->entry); - /* enter the signal handler in big-endian mode */ + /* enter the signal handler in native-endian mode */ regs->msr &= ~MSR_LE; + regs->msr |= (MSR_KERNEL & MSR_LE); regs->gpr[1] = newsp; err |= get_user(regs->gpr[2], &funct_desc_ptr->toc); regs->gpr[3] = signr; -- cgit v0.10.2 From c57a5ce0df04be61bafedc0f3043d568103c7ab5 Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Mon, 23 Sep 2013 12:04:44 +1000 Subject: powerpc: Include the appropriate endianness header This patch will have powerpc include the appropriate generic endianness header depending on what the compiler reports. Signed-off-by: Ian Munsie Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/uapi/asm/byteorder.h b/arch/powerpc/include/uapi/asm/byteorder.h index aa6cc4f..ca931d0 100644 --- a/arch/powerpc/include/uapi/asm/byteorder.h +++ b/arch/powerpc/include/uapi/asm/byteorder.h @@ -7,6 +7,10 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ +#ifdef __LITTLE_ENDIAN__ +#include +#else #include +#endif #endif /* _ASM_POWERPC_BYTEORDER_H */ -- cgit v0.10.2 From 5c0484e25ec03243d4c2f2d4416d4a13efc77f6a Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 23 Sep 2013 12:04:45 +1000 Subject: powerpc: Endian safe trampoline Create a trampoline that works in either endian and flips to the expected endian. Use it for primary and secondary thread entry as well as RTAS and OF call return. Credit for finding the magic instruction goes to Paul Mackerras Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/ppc_asm.h b/arch/powerpc/include/asm/ppc_asm.h index 0c51fb4..ce05bba 100644 --- a/arch/powerpc/include/asm/ppc_asm.h +++ b/arch/powerpc/include/asm/ppc_asm.h @@ -845,6 +845,35 @@ END_FTR_SECTION_NESTED(CPU_FTR_HAS_PPR,CPU_FTR_HAS_PPR,946) #define N_SLINE 68 #define N_SO 100 -#endif /* __ASSEMBLY__ */ +/* + * Create an endian fixup trampoline + * + * This starts with a "tdi 0,0,0x48" instruction which is + * essentially a "trap never", and thus akin to a nop. + * + * The opcode for this instruction read with the wrong endian + * however results in a b . + 8 + * + * So essentially we use that trick to execute the following + * trampoline in "reverse endian" if we are running with the + * MSR_LE bit set the "wrong" way for whatever endianness the + * kernel is built for. + */ +#ifdef CONFIG_PPC_BOOK3E +#define FIXUP_ENDIAN +#else +#define FIXUP_ENDIAN \ + tdi 0,0,0x48; /* Reverse endian of b . + 8 */ \ + b $+36; /* Skip trampoline if endian is good */ \ + .long 0x05009f42; /* bcl 20,31,$+4 */ \ + .long 0xa602487d; /* mflr r10 */ \ + .long 0x1c004a39; /* addi r10,r10,28 */ \ + .long 0xa600607d; /* mfmsr r11 */ \ + .long 0x01006b69; /* xori r11,r11,1 */ \ + .long 0xa6035a7d; /* mtsrr0 r10 */ \ + .long 0xa6037b7d; /* mtsrr1 r11 */ \ + .long 0x2400004c /* rfid */ +#endif /* !CONFIG_PPC_BOOK3E */ +#endif /* __ASSEMBLY__ */ #endif /* _ASM_POWERPC_PPC_ASM_H */ diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index c04cdf7..889ea2b 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -1017,7 +1017,7 @@ _GLOBAL(enter_rtas) li r9,1 rldicr r9,r9,MSR_SF_LG,(63-MSR_SF_LG) - ori r9,r9,MSR_IR|MSR_DR|MSR_FE0|MSR_FE1|MSR_FP|MSR_RI + ori r9,r9,MSR_IR|MSR_DR|MSR_FE0|MSR_FE1|MSR_FP|MSR_RI|MSR_LE andc r6,r0,r9 sync /* disable interrupts so SRR0/1 */ mtmsrd r0 /* don't get trashed */ @@ -1032,6 +1032,8 @@ _GLOBAL(enter_rtas) b . /* prevent speculative execution */ _STATIC(rtas_return_loc) + FIXUP_ENDIAN + /* relocation is off at this point */ GET_PACA(r4) clrldi r4,r4,2 /* convert to realmode address */ @@ -1103,28 +1105,30 @@ _GLOBAL(enter_prom) std r10,_CCR(r1) std r11,_MSR(r1) - /* Get the PROM entrypoint */ - mtlr r4 + /* Put PROM address in SRR0 */ + mtsrr0 r4 + + /* Setup our trampoline return addr in LR */ + bcl 20,31,$+4 +0: mflr r4 + addi r4,r4,(1f - 0b) + mtlr r4 - /* Switch MSR to 32 bits mode + /* Prepare a 32-bit mode big endian MSR */ #ifdef CONFIG_PPC_BOOK3E rlwinm r11,r11,0,1,31 - mtmsr r11 + mtsrr1 r11 + rfi #else /* CONFIG_PPC_BOOK3E */ - mfmsr r11 - li r12,1 - rldicr r12,r12,MSR_SF_LG,(63-MSR_SF_LG) - andc r11,r11,r12 - li r12,1 - rldicr r12,r12,MSR_ISF_LG,(63-MSR_ISF_LG) - andc r11,r11,r12 - mtmsrd r11 + LOAD_REG_IMMEDIATE(r12, MSR_SF | MSR_ISF | MSR_LE) + andc r11,r11,r12 + mtsrr1 r11 + rfid #endif /* CONFIG_PPC_BOOK3E */ - isync - /* Enter PROM here... */ - blrl +1: /* Return from OF */ + FIXUP_ENDIAN /* Just make sure that r1 top 32 bits didn't get * corrupt by OF diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S index 3d11d80..2ae41ab 100644 --- a/arch/powerpc/kernel/head_64.S +++ b/arch/powerpc/kernel/head_64.S @@ -68,6 +68,7 @@ _stext: _GLOBAL(__start) /* NOP this out unconditionally */ BEGIN_FTR_SECTION + FIXUP_ENDIAN b .__start_initialization_multiplatform END_FTR_SECTION(0, 1) @@ -115,6 +116,7 @@ __run_at_load: */ .globl __secondary_hold __secondary_hold: + FIXUP_ENDIAN #ifndef CONFIG_PPC_BOOK3E mfmsr r24 ori r24,r24,MSR_RI @@ -205,6 +207,7 @@ _GLOBAL(generic_secondary_thread_init) * as SCOM before entry). */ _GLOBAL(generic_secondary_smp_init) + FIXUP_ENDIAN mr r24,r3 mr r25,r4 -- cgit v0.10.2 From f626190d27a8525fbb6e3a72831e177c092f8a44 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 23 Sep 2013 12:04:46 +1000 Subject: powerpc: Remove open coded byte swap macro in alignment handler Use swab64/32/16 instead of open coding it. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c index a27ccd5..af830df 100644 --- a/arch/powerpc/kernel/align.c +++ b/arch/powerpc/kernel/align.c @@ -54,8 +54,6 @@ struct aligninfo { /* DSISR bits reported for a DCBZ instruction: */ #define DCBZ 0x5f /* 8xx/82xx dcbz faults when cache not enabled */ -#define SWAP(a, b) (t = (a), (a) = (b), (b) = t) - /* * The PowerPC stores certain bits of the instruction that caused the * alignment exception in the DSISR register. This array maps those @@ -458,7 +456,7 @@ static struct aligninfo spe_aligninfo[32] = { static int emulate_spe(struct pt_regs *regs, unsigned int reg, unsigned int instr) { - int t, ret; + int ret; union { u64 ll; u32 w[2]; @@ -581,24 +579,18 @@ static int emulate_spe(struct pt_regs *regs, unsigned int reg, if (flags & SW) { switch (flags & 0xf0) { case E8: - SWAP(data.v[0], data.v[7]); - SWAP(data.v[1], data.v[6]); - SWAP(data.v[2], data.v[5]); - SWAP(data.v[3], data.v[4]); + data.ll = swab64(data.ll); break; case E4: - - SWAP(data.v[0], data.v[3]); - SWAP(data.v[1], data.v[2]); - SWAP(data.v[4], data.v[7]); - SWAP(data.v[5], data.v[6]); + data.w[0] = swab32(data.w[0]); + data.w[1] = swab32(data.w[1]); break; /* Its half word endian */ default: - SWAP(data.v[0], data.v[1]); - SWAP(data.v[2], data.v[3]); - SWAP(data.v[4], data.v[5]); - SWAP(data.v[6], data.v[7]); + data.h[0] = swab16(data.h[0]); + data.h[1] = swab16(data.h[1]); + data.h[2] = swab16(data.h[2]); + data.h[3] = swab16(data.h[3]); break; } } @@ -710,7 +702,7 @@ int fix_alignment(struct pt_regs *regs) unsigned int dsisr; unsigned char __user *addr; unsigned long p, swiz; - int ret, t; + int ret; union { u64 ll; double dd; @@ -915,17 +907,13 @@ int fix_alignment(struct pt_regs *regs) if (flags & SW) { switch (nb) { case 8: - SWAP(data.v[0], data.v[7]); - SWAP(data.v[1], data.v[6]); - SWAP(data.v[2], data.v[5]); - SWAP(data.v[3], data.v[4]); + data.ll = swab64(data.ll); break; case 4: - SWAP(data.v[4], data.v[7]); - SWAP(data.v[5], data.v[6]); + data.x32.low32 = swab32(data.x32.low32); break; case 2: - SWAP(data.v[6], data.v[7]); + data.x16.low16 = swab16(data.x16.low16); break; } } -- cgit v0.10.2 From c324496456208e4a6a377d8a36b8fad76d004c7f Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 23 Sep 2013 12:04:47 +1000 Subject: powerpc: Remove hard coded FP offsets in alignment handler The alignment handler assumes big endian ordering when selecting the low word of a 64bit floating point value. Use the existing union which works in both little and big endian. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c index af830df..25d8d8b 100644 --- a/arch/powerpc/kernel/align.c +++ b/arch/powerpc/kernel/align.c @@ -895,7 +895,7 @@ int fix_alignment(struct pt_regs *regs) #ifdef CONFIG_PPC_FPU preempt_disable(); enable_kernel_fp(); - cvt_df(&data.dd, (float *)&data.v[4]); + cvt_df(&data.dd, (float *)&data.x32.low32); preempt_enable(); #else return 0; @@ -935,7 +935,7 @@ int fix_alignment(struct pt_regs *regs) #ifdef CONFIG_PPC_FPU preempt_disable(); enable_kernel_fp(); - cvt_fd((float *)&data.v[4], &data.dd); + cvt_fd((float *)&data.x32.low32, &data.dd); preempt_enable(); #else return 0; -- cgit v0.10.2 From a5841a46022e1ebe97d4c926c32ef4e9acec96a7 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 23 Sep 2013 12:04:48 +1000 Subject: powerpc: Alignment handler shouldn't access VSX registers with TS_FPR The TS_FPR macro selects the FPR component of a VSX register (the high doubleword). emulate_vsx is using this macro to get the address of the associated VSX register. This happens to work on big endian, but fails on little endian. Replace it with an explicit array access. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c index 25d8d8b..3049bd0 100644 --- a/arch/powerpc/kernel/align.c +++ b/arch/powerpc/kernel/align.c @@ -650,7 +650,7 @@ static int emulate_vsx(unsigned char __user *addr, unsigned int reg, flush_vsx_to_thread(current); if (reg < 32) - ptr = (char *) ¤t->thread.TS_FPR(reg); + ptr = (char *) ¤t->thread.fpr[reg][0]; else ptr = (char *) ¤t->thread.vr[reg - 32]; -- cgit v0.10.2 From 835e206a67703f8cfb9f13ffc90d5fe5a374f40c Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 23 Sep 2013 12:04:49 +1000 Subject: powerpc: Add little endian support to alignment handler Handle most unaligned load and store faults in little endian mode. Strings, multiples and VSX are not supported. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c index 3049bd0..cce82b1 100644 --- a/arch/powerpc/kernel/align.c +++ b/arch/powerpc/kernel/align.c @@ -262,6 +262,7 @@ static int emulate_dcbz(struct pt_regs *regs, unsigned char __user *addr) #define SWIZ_PTR(p) ((unsigned char __user *)((p) ^ swiz)) +#ifdef __BIG_ENDIAN__ static int emulate_multiple(struct pt_regs *regs, unsigned char __user *addr, unsigned int reg, unsigned int nb, unsigned int flags, unsigned int instr, @@ -390,6 +391,7 @@ static int emulate_fp_pair(unsigned char __user *addr, unsigned int reg, return -EFAULT; return 1; /* exception handled and fixed up */ } +#endif #ifdef CONFIG_SPE @@ -628,7 +630,7 @@ static int emulate_spe(struct pt_regs *regs, unsigned int reg, } #endif /* CONFIG_SPE */ -#ifdef CONFIG_VSX +#if defined(CONFIG_VSX) && defined(__BIG_ENDIAN__) /* * Emulate VSX instructions... */ @@ -702,18 +704,28 @@ int fix_alignment(struct pt_regs *regs) unsigned int dsisr; unsigned char __user *addr; unsigned long p, swiz; - int ret; - union { + int ret, i; + union data { u64 ll; double dd; unsigned char v[8]; struct { +#ifdef __LITTLE_ENDIAN__ + int low32; + unsigned hi32; +#else unsigned hi32; int low32; +#endif } x32; struct { +#ifdef __LITTLE_ENDIAN__ + short low16; + unsigned char hi48[6]; +#else unsigned char hi48[6]; short low16; +#endif } x16; } data; @@ -772,8 +784,9 @@ int fix_alignment(struct pt_regs *regs) /* Byteswap little endian loads and stores */ swiz = 0; - if (regs->msr & MSR_LE) { + if ((regs->msr & MSR_LE) != (MSR_KERNEL & MSR_LE)) { flags ^= SW; +#ifdef __BIG_ENDIAN__ /* * So-called "PowerPC little endian" mode works by * swizzling addresses rather than by actually doing @@ -786,11 +799,13 @@ int fix_alignment(struct pt_regs *regs) */ if (cpu_has_feature(CPU_FTR_PPC_LE)) swiz = 7; +#endif } /* DAR has the operand effective address */ addr = (unsigned char __user *)regs->dar; +#ifdef __BIG_ENDIAN__ #ifdef CONFIG_VSX if ((instruction & 0xfc00003e) == 0x7c000018) { unsigned int elsize; @@ -810,7 +825,7 @@ int fix_alignment(struct pt_regs *regs) elsize = 8; flags = 0; - if (regs->msr & MSR_LE) + if ((regs->msr & MSR_LE) != (MSR_KERNEL & MSR_LE)) flags |= SW; if (instruction & 0x100) flags |= ST; @@ -825,6 +840,9 @@ int fix_alignment(struct pt_regs *regs) return emulate_vsx(addr, reg, areg, regs, flags, nb, elsize); } #endif +#else + return -EFAULT; +#endif /* A size of 0 indicates an instruction we don't support, with * the exception of DCBZ which is handled as a special case here */ @@ -839,9 +857,13 @@ int fix_alignment(struct pt_regs *regs) * function */ if (flags & M) { +#ifdef __BIG_ENDIAN__ PPC_WARN_ALIGNMENT(multiple, regs); return emulate_multiple(regs, addr, reg, nb, flags, instr, swiz); +#else + return -EFAULT; +#endif } /* Verify the address of the operand */ @@ -860,8 +882,12 @@ int fix_alignment(struct pt_regs *regs) /* Special case for 16-byte FP loads and stores */ if (nb == 16) { +#ifdef __BIG_ENDIAN__ PPC_WARN_ALIGNMENT(fp_pair, regs); return emulate_fp_pair(addr, reg, flags); +#else + return -EFAULT; +#endif } PPC_WARN_ALIGNMENT(unaligned, regs); @@ -870,24 +896,28 @@ int fix_alignment(struct pt_regs *regs) * get it from register values */ if (!(flags & ST)) { - data.ll = 0; - ret = 0; - p = (unsigned long) addr; + unsigned int start = 0; + switch (nb) { - case 8: - ret |= __get_user_inatomic(data.v[0], SWIZ_PTR(p++)); - ret |= __get_user_inatomic(data.v[1], SWIZ_PTR(p++)); - ret |= __get_user_inatomic(data.v[2], SWIZ_PTR(p++)); - ret |= __get_user_inatomic(data.v[3], SWIZ_PTR(p++)); case 4: - ret |= __get_user_inatomic(data.v[4], SWIZ_PTR(p++)); - ret |= __get_user_inatomic(data.v[5], SWIZ_PTR(p++)); + start = offsetof(union data, x32.low32); + break; case 2: - ret |= __get_user_inatomic(data.v[6], SWIZ_PTR(p++)); - ret |= __get_user_inatomic(data.v[7], SWIZ_PTR(p++)); - if (unlikely(ret)) - return -EFAULT; + start = offsetof(union data, x16.low16); + break; } + + data.ll = 0; + ret = 0; + p = (unsigned long)addr; + + for (i = 0; i < nb; i++) + ret |= __get_user_inatomic(data.v[start + i], + SWIZ_PTR(p++)); + + if (unlikely(ret)) + return -EFAULT; + } else if (flags & F) { data.dd = current->thread.TS_FPR(reg); if (flags & S) { @@ -945,21 +975,24 @@ int fix_alignment(struct pt_regs *regs) /* Store result to memory or update registers */ if (flags & ST) { - ret = 0; - p = (unsigned long) addr; + unsigned int start = 0; + switch (nb) { - case 8: - ret |= __put_user_inatomic(data.v[0], SWIZ_PTR(p++)); - ret |= __put_user_inatomic(data.v[1], SWIZ_PTR(p++)); - ret |= __put_user_inatomic(data.v[2], SWIZ_PTR(p++)); - ret |= __put_user_inatomic(data.v[3], SWIZ_PTR(p++)); case 4: - ret |= __put_user_inatomic(data.v[4], SWIZ_PTR(p++)); - ret |= __put_user_inatomic(data.v[5], SWIZ_PTR(p++)); + start = offsetof(union data, x32.low32); + break; case 2: - ret |= __put_user_inatomic(data.v[6], SWIZ_PTR(p++)); - ret |= __put_user_inatomic(data.v[7], SWIZ_PTR(p++)); + start = offsetof(union data, x16.low16); + break; } + + ret = 0; + p = (unsigned long)addr; + + for (i = 0; i < nb; i++) + ret |= __put_user_inatomic(data.v[start + i], + SWIZ_PTR(p++)); + if (unlikely(ret)) return -EFAULT; } else if (flags & F) -- cgit v0.10.2 From 52055d07ce0a465949552c9c5d65d2b8b37672c5 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 23 Sep 2013 12:04:50 +1000 Subject: powerpc: Handle VSX alignment faults in little endian mode Things are complicated by the fact that VSX elements are big endian ordered even in little endian mode. 8 byte loads and stores also write to the top 8 bytes of the register. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c index cce82b1..59f70ad 100644 --- a/arch/powerpc/kernel/align.c +++ b/arch/powerpc/kernel/align.c @@ -630,7 +630,7 @@ static int emulate_spe(struct pt_regs *regs, unsigned int reg, } #endif /* CONFIG_SPE */ -#if defined(CONFIG_VSX) && defined(__BIG_ENDIAN__) +#ifdef CONFIG_VSX /* * Emulate VSX instructions... */ @@ -658,8 +658,25 @@ static int emulate_vsx(unsigned char __user *addr, unsigned int reg, lptr = (unsigned long *) ptr; +#ifdef __LITTLE_ENDIAN__ + if (flags & SW) { + elsize = length; + sw = length-1; + } else { + /* + * The elements are BE ordered, even in LE mode, so process + * them in reverse order. + */ + addr += length - elsize; + + /* 8 byte memory accesses go in the top 8 bytes of the VR */ + if (length == 8) + ptr += 8; + } +#else if (flags & SW) sw = elsize-1; +#endif for (j = 0; j < length; j += elsize) { for (i = 0; i < elsize; ++i) { @@ -669,19 +686,31 @@ static int emulate_vsx(unsigned char __user *addr, unsigned int reg, ret |= __get_user(ptr[i^sw], addr + i); } ptr += elsize; +#ifdef __LITTLE_ENDIAN__ + addr -= elsize; +#else addr += elsize; +#endif } +#ifdef __BIG_ENDIAN__ +#define VSX_HI 0 +#define VSX_LO 1 +#else +#define VSX_HI 1 +#define VSX_LO 0 +#endif + if (!ret) { if (flags & U) regs->gpr[areg] = regs->dar; /* Splat load copies the same data to top and bottom 8 bytes */ if (flags & SPLT) - lptr[1] = lptr[0]; - /* For 8 byte loads, zero the top 8 bytes */ + lptr[VSX_LO] = lptr[VSX_HI]; + /* For 8 byte loads, zero the low 8 bytes */ else if (!(flags & ST) && (8 == length)) - lptr[1] = 0; + lptr[VSX_LO] = 0; } else return -EFAULT; @@ -805,7 +834,6 @@ int fix_alignment(struct pt_regs *regs) /* DAR has the operand effective address */ addr = (unsigned char __user *)regs->dar; -#ifdef __BIG_ENDIAN__ #ifdef CONFIG_VSX if ((instruction & 0xfc00003e) == 0x7c000018) { unsigned int elsize; @@ -840,9 +868,6 @@ int fix_alignment(struct pt_regs *regs) return emulate_vsx(addr, reg, areg, regs, flags, nb, elsize); } #endif -#else - return -EFAULT; -#endif /* A size of 0 indicates an instruction we don't support, with * the exception of DCBZ which is handled as a special case here */ -- cgit v0.10.2 From 7a332b0c9a59e0b0777dec55eefdda0f9a24ac52 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 23 Sep 2013 12:04:51 +1000 Subject: powerpc: Use generic checksum code in little endian We need to fix some endian issues in our checksum code. For now just enable the generic checksum routines for little endian builds. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 38f3b7e..2c69c43 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -139,6 +139,9 @@ config PPC select OLD_SIGACTION if PPC32 select HAVE_DEBUG_STACKOVERFLOW +config GENERIC_CSUM + def_bool CPU_LITTLE_ENDIAN + config EARLY_PRINTK bool default y diff --git a/arch/powerpc/include/asm/checksum.h b/arch/powerpc/include/asm/checksum.h index ce0c284..8251a3b 100644 --- a/arch/powerpc/include/asm/checksum.h +++ b/arch/powerpc/include/asm/checksum.h @@ -14,6 +14,9 @@ * which always checksum on 4 octet boundaries. ihl is the number * of 32-bit words and is always >= 5. */ +#ifdef CONFIG_GENERIC_CSUM +#include +#else extern __sum16 ip_fast_csum(const void *iph, unsigned int ihl); /* @@ -123,5 +126,7 @@ static inline __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr, return sum; #endif } + +#endif #endif /* __KERNEL__ */ #endif diff --git a/arch/powerpc/kernel/ppc_ksyms.c b/arch/powerpc/kernel/ppc_ksyms.c index 21646db..3b485c5 100644 --- a/arch/powerpc/kernel/ppc_ksyms.c +++ b/arch/powerpc/kernel/ppc_ksyms.c @@ -79,10 +79,12 @@ EXPORT_SYMBOL(strlen); EXPORT_SYMBOL(strcmp); EXPORT_SYMBOL(strncmp); +#ifndef CONFIG_GENERIC_CSUM EXPORT_SYMBOL(csum_partial); EXPORT_SYMBOL(csum_partial_copy_generic); EXPORT_SYMBOL(ip_fast_csum); EXPORT_SYMBOL(csum_tcpudp_magic); +#endif EXPORT_SYMBOL(__copy_tofrom_user); EXPORT_SYMBOL(__clear_user); diff --git a/arch/powerpc/lib/Makefile b/arch/powerpc/lib/Makefile index 4504332..33ab261 100644 --- a/arch/powerpc/lib/Makefile +++ b/arch/powerpc/lib/Makefile @@ -10,15 +10,20 @@ CFLAGS_REMOVE_code-patching.o = -pg CFLAGS_REMOVE_feature-fixups.o = -pg obj-y := string.o alloc.o \ - checksum_$(CONFIG_WORD_SIZE).o crtsavres.o + crtsavres.o obj-$(CONFIG_PPC32) += div64.o copy_32.o obj-$(CONFIG_HAS_IOMEM) += devres.o obj-$(CONFIG_PPC64) += copypage_64.o copyuser_64.o \ memcpy_64.o usercopy_64.o mem_64.o string.o \ - checksum_wrappers_64.o hweight_64.o \ + hweight_64.o \ copyuser_power7.o string_64.o copypage_power7.o \ memcpy_power7.o +ifeq ($(CONFIG_GENERIC_CSUM),) +obj-y += checksum_$(CONFIG_WORD_SIZE).o +obj-$(CONFIG_PPC64) += checksum_wrappers_64.o +endif + obj-$(CONFIG_PPC_EMULATE_SSTEP) += sstep.o ldstfp.o ifeq ($(CONFIG_PPC64),y) -- cgit v0.10.2 From de577a356848a629b2c7f252ca3d1bc87375b52b Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 23 Sep 2013 12:04:52 +1000 Subject: powerpc: Use generic memcpy code in little endian We need to fix some endian issues in our memcpy code. For now just enable the generic memcpy routine for little endian builds. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/string.h b/arch/powerpc/include/asm/string.h index e40010a..0dffad6 100644 --- a/arch/powerpc/include/asm/string.h +++ b/arch/powerpc/include/asm/string.h @@ -10,7 +10,9 @@ #define __HAVE_ARCH_STRNCMP #define __HAVE_ARCH_STRCAT #define __HAVE_ARCH_MEMSET +#ifdef __BIG_ENDIAN__ #define __HAVE_ARCH_MEMCPY +#endif #define __HAVE_ARCH_MEMMOVE #define __HAVE_ARCH_MEMCMP #define __HAVE_ARCH_MEMCHR @@ -22,7 +24,9 @@ extern int strcmp(const char *,const char *); extern int strncmp(const char *, const char *, __kernel_size_t); extern char * strcat(char *, const char *); extern void * memset(void *,int,__kernel_size_t); +#ifdef __BIG_ENDIAN__ extern void * memcpy(void *,const void *,__kernel_size_t); +#endif extern void * memmove(void *,const void *,__kernel_size_t); extern int memcmp(const void *,const void *,__kernel_size_t); extern void * memchr(const void *,int,__kernel_size_t); diff --git a/arch/powerpc/kernel/ppc_ksyms.c b/arch/powerpc/kernel/ppc_ksyms.c index 3b485c5..60bbeb2 100644 --- a/arch/powerpc/kernel/ppc_ksyms.c +++ b/arch/powerpc/kernel/ppc_ksyms.c @@ -148,7 +148,9 @@ EXPORT_SYMBOL(__ucmpdi2); #endif long long __bswapdi2(long long); EXPORT_SYMBOL(__bswapdi2); +#ifdef __BIG_ENDIAN__ EXPORT_SYMBOL(memcpy); +#endif EXPORT_SYMBOL(memset); EXPORT_SYMBOL(memmove); EXPORT_SYMBOL(memcmp); diff --git a/arch/powerpc/lib/Makefile b/arch/powerpc/lib/Makefile index 33ab261..5310132 100644 --- a/arch/powerpc/lib/Makefile +++ b/arch/powerpc/lib/Makefile @@ -15,15 +15,18 @@ obj-$(CONFIG_PPC32) += div64.o copy_32.o obj-$(CONFIG_HAS_IOMEM) += devres.o obj-$(CONFIG_PPC64) += copypage_64.o copyuser_64.o \ - memcpy_64.o usercopy_64.o mem_64.o string.o \ + usercopy_64.o mem_64.o string.o \ hweight_64.o \ - copyuser_power7.o string_64.o copypage_power7.o \ - memcpy_power7.o + copyuser_power7.o string_64.o copypage_power7.o ifeq ($(CONFIG_GENERIC_CSUM),) obj-y += checksum_$(CONFIG_WORD_SIZE).o obj-$(CONFIG_PPC64) += checksum_wrappers_64.o endif +ifeq ($(CONFIG_CPU_LITTLE_ENDIAN),) +obj-$(CONFIG_PPC64) += memcpy_power7.o memcpy_64.o +endif + obj-$(CONFIG_PPC_EMULATE_SSTEP) += sstep.o ldstfp.o ifeq ($(CONFIG_PPC64),y) -- cgit v0.10.2 From a0588015deab1844261b27a67ae6f5b910fe2830 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 23 Sep 2013 12:04:53 +1000 Subject: powerpc: uname should return ppc64le/ppcle on little endian builds We need to distinguish between big endian and little endian environments, so fix uname to return the right thing. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile index 51cfb78..debfa2b 100644 --- a/arch/powerpc/Makefile +++ b/arch/powerpc/Makefile @@ -36,17 +36,26 @@ KBUILD_DEFCONFIG := ppc64_defconfig endif ifeq ($(CONFIG_PPC64),y) -OLDARCH := ppc64 - new_nm := $(shell if $(NM) --help 2>&1 | grep -- '--synthetic' > /dev/null; then echo y; else echo n; fi) ifeq ($(new_nm),y) NM := $(NM) --synthetic endif +endif +ifeq ($(CONFIG_PPC64),y) +ifeq ($(CONFIG_CPU_LITTLE_ENDIAN),y) +OLDARCH := ppc64le +else +OLDARCH := ppc64 +endif +else +ifeq ($(CONFIG_CPU_LITTLE_ENDIAN),y) +OLDARCH := ppcle else OLDARCH := ppc endif +endif # It seems there are times we use this Makefile without # including the config file, but this replicates the old behaviour -- cgit v0.10.2 From 1cc79bc80606229b0e7e56c76ee3cb3594409aa9 Mon Sep 17 00:00:00 2001 From: Alistair Popple Date: Mon, 23 Sep 2013 12:04:54 +1000 Subject: powerpc: Little endian fixes for platforms/powernv/opal.c Signed-off-by: Alistair Popple Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c index 2911abe..4ffa75e 100644 --- a/arch/powerpc/platforms/powernv/opal.c +++ b/arch/powerpc/platforms/powernv/opal.c @@ -372,7 +372,7 @@ static irqreturn_t opal_interrupt(int irq, void *data) static int __init opal_init(void) { struct device_node *np, *consoles; - const u32 *irqs; + const __be32 *irqs; int rc, i, irqlen; opal_node = of_find_node_by_path("/ibm,opal"); -- cgit v0.10.2 From 044cb69c53f286673b6809641f9c0ce929f9f221 Mon Sep 17 00:00:00 2001 From: Alistair Popple Date: Mon, 23 Sep 2013 12:04:55 +1000 Subject: powerpc: Little endian fix for arch/powerpc/platforms/powernv/pci.c Signed-off-by: Alistair Popple Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c index a28d3b5..9122215 100644 --- a/arch/powerpc/platforms/powernv/pci.c +++ b/arch/powerpc/platforms/powernv/pci.c @@ -462,8 +462,8 @@ void pnv_pci_setup_iommu_table(struct iommu_table *tbl, static struct iommu_table *pnv_pci_setup_bml_iommu(struct pci_controller *hose) { struct iommu_table *tbl; - const __be64 *basep, *swinvp; - const __be32 *sizep; + const __be64 *basep; + const __be32 *sizep, *swinvp; basep = of_get_property(hose->dn, "linux,tce-base", NULL); sizep = of_get_property(hose->dn, "linux,tce-size", NULL); @@ -484,8 +484,9 @@ static struct iommu_table *pnv_pci_setup_bml_iommu(struct pci_controller *hose) swinvp = of_get_property(hose->dn, "linux,tce-sw-invalidate-info", NULL); if (swinvp) { - tbl->it_busno = swinvp[1]; - tbl->it_index = (unsigned long)ioremap(swinvp[0], 8); + tbl->it_busno = of_read_ulong(&swinvp[1], 2); + tbl->it_index = + (unsigned long)ioremap(of_read_number(swinvp, 2), 8); tbl->it_type = TCE_PCI_SWINV_CREATE | TCE_PCI_SWINV_FREE; } return tbl; -- cgit v0.10.2 From 8c5fcc83afd0738a089641bc0862b9058e9cfd06 Mon Sep 17 00:00:00 2001 From: Alistair Popple Date: Mon, 23 Sep 2013 12:04:56 +1000 Subject: powerpc: Little endian fix for arch/powerpc/platforms/powernv/pci-p5ioc2.c Signed-off-by: Alistair Popple Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/powernv/pci-p5ioc2.c b/arch/powerpc/platforms/powernv/pci-p5ioc2.c index b68db63..f8b4bd8 100644 --- a/arch/powerpc/platforms/powernv/pci-p5ioc2.c +++ b/arch/powerpc/platforms/powernv/pci-p5ioc2.c @@ -99,7 +99,7 @@ static void __init pnv_pci_init_p5ioc2_phb(struct device_node *np, u64 hub_id, void *tce_mem, u64 tce_size) { struct pnv_phb *phb; - const u64 *prop64; + const __be64 *prop64; u64 phb_id; int64_t rc; static int primary = 1; @@ -178,7 +178,7 @@ static void __init pnv_pci_init_p5ioc2_phb(struct device_node *np, u64 hub_id, void __init pnv_pci_init_p5ioc2_hub(struct device_node *np) { struct device_node *phbn; - const u64 *prop64; + const __be64 *prop64; u64 hub_id; void *tce_mem; uint64_t tce_per_phb; -- cgit v0.10.2 From c681b93c1fd016e94c8f3dd948dcc28a78790df7 Mon Sep 17 00:00:00 2001 From: Alistair Popple Date: Mon, 23 Sep 2013 12:04:57 +1000 Subject: powerpc: Little endian sparse clean up for arch/powerpc/platforms/powernv/pci-ioda.c Signed-off-by: Alistair Popple Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index 74a5a57..9a903ed 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -1106,7 +1106,7 @@ void __init pnv_pci_init_ioda_phb(struct device_node *np, struct pci_controller *hose; struct pnv_phb *phb; unsigned long size, m32map_off, iomap_off, pemap_off; - const u64 *prop64; + const __be64 *prop64; const u32 *prop32; int len; u64 phb_id; @@ -1285,7 +1285,7 @@ void __init pnv_pci_init_ioda2_phb(struct device_node *np) void __init pnv_pci_init_ioda_hub(struct device_node *np) { struct device_node *phbn; - const u64 *prop64; + const __be64 *prop64; u64 hub_id; pr_info("Probing IODA IO-Hub %s\n", np->full_name); -- cgit v0.10.2 From 46d319e5d8c8add09378a67e27237504d0132283 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 23 Sep 2013 12:04:58 +1000 Subject: powerpc/powernv: Fix endian issues in OPAL RTC driver Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/powernv/opal-rtc.c b/arch/powerpc/platforms/powernv/opal-rtc.c index 2aa7641..dbfdba3 100644 --- a/arch/powerpc/platforms/powernv/opal-rtc.c +++ b/arch/powerpc/platforms/powernv/opal-rtc.c @@ -48,6 +48,8 @@ unsigned long __init opal_get_boot_time(void) } if (rc != OPAL_SUCCESS) return 0; + y_m_d = be32_to_cpu(y_m_d); + h_m_s_ms = be64_to_cpu(h_m_s_ms); opal_to_tm(y_m_d, h_m_s_ms, &tm); return mktime(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); @@ -68,6 +70,8 @@ void opal_get_rtc_time(struct rtc_time *tm) } if (rc != OPAL_SUCCESS) return; + y_m_d = be32_to_cpu(y_m_d); + h_m_s_ms = be64_to_cpu(h_m_s_ms); opal_to_tm(y_m_d, h_m_s_ms, tm); } @@ -86,6 +90,9 @@ int opal_set_rtc_time(struct rtc_time *tm) h_m_s_ms |= ((u64)bin2bcd(tm->tm_min)) << 48; h_m_s_ms |= ((u64)bin2bcd(tm->tm_sec)) << 40; + y_m_d = cpu_to_be32(y_m_d); + h_m_s_ms = cpu_to_be64(h_m_s_ms); + while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { rc = opal_rtc_write(y_m_d, h_m_s_ms); if (rc == OPAL_BUSY_EVENT) -- cgit v0.10.2 From bf8e0f891a32ba24fa8811e496fabb449589d942 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 23 Sep 2013 12:04:59 +1000 Subject: powerpc/powernv: Fix endian issues in OPAL ICS backend Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/sysdev/xics/ics-opal.c b/arch/powerpc/sysdev/xics/ics-opal.c index 39d7221..3c6ee1b 100644 --- a/arch/powerpc/sysdev/xics/ics-opal.c +++ b/arch/powerpc/sysdev/xics/ics-opal.c @@ -112,6 +112,7 @@ static int ics_opal_set_affinity(struct irq_data *d, bool force) { unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d); + __be16 oserver; int16_t server; int8_t priority; int64_t rc; @@ -120,13 +121,13 @@ static int ics_opal_set_affinity(struct irq_data *d, if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS) return -1; - rc = opal_get_xive(hw_irq, &server, &priority); + rc = opal_get_xive(hw_irq, &oserver, &priority); if (rc != OPAL_SUCCESS) { - pr_err("%s: opal_set_xive(irq=%d [hw 0x%x] server=%x)" - " error %lld\n", - __func__, d->irq, hw_irq, server, rc); + pr_err("%s: opal_get_xive(irq=%d [hw 0x%x]) error %lld\n", + __func__, d->irq, hw_irq, rc); return -1; } + server = be16_to_cpu(oserver); wanted_server = xics_get_irq_server(d->irq, cpumask, 1); if (wanted_server < 0) { @@ -181,7 +182,7 @@ static int ics_opal_map(struct ics *ics, unsigned int virq) { unsigned int hw_irq = (unsigned int)virq_to_hw(virq); int64_t rc; - int16_t server; + __be16 server; int8_t priority; if (WARN_ON(hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)) @@ -201,7 +202,7 @@ static int ics_opal_map(struct ics *ics, unsigned int virq) static void ics_opal_mask_unknown(struct ics *ics, unsigned long vec) { int64_t rc; - int16_t server; + __be16 server; int8_t priority; /* Check if HAL knows about this interrupt */ @@ -215,14 +216,14 @@ static void ics_opal_mask_unknown(struct ics *ics, unsigned long vec) static long ics_opal_get_server(struct ics *ics, unsigned long vec) { int64_t rc; - int16_t server; + __be16 server; int8_t priority; /* Check if HAL knows about this interrupt */ rc = opal_get_xive(vec, &server, &priority); if (rc != OPAL_SUCCESS) return -1; - return ics_opal_unmangle_server(server); + return ics_opal_unmangle_server(be16_to_cpu(server)); } int __init ics_opal_init(void) -- cgit v0.10.2 From 81063cdd61795f0db85725bae4945d0762269e04 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 23 Sep 2013 12:05:00 +1000 Subject: powerpc/powernv: Make OPAL NVRAM device tree accesses endian safe Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/powernv/opal-nvram.c b/arch/powerpc/platforms/powernv/opal-nvram.c index 3f83e1a..acd9f7e 100644 --- a/arch/powerpc/platforms/powernv/opal-nvram.c +++ b/arch/powerpc/platforms/powernv/opal-nvram.c @@ -65,7 +65,7 @@ static ssize_t opal_nvram_write(char *buf, size_t count, loff_t *index) void __init opal_nvram_init(void) { struct device_node *np; - const u32 *nbytes_p; + const __be32 *nbytes_p; np = of_find_compatible_node(NULL, NULL, "ibm,opal-nvram"); if (np == NULL) @@ -76,7 +76,7 @@ void __init opal_nvram_init(void) of_node_put(np); return; } - nvram_size = *nbytes_p; + nvram_size = be32_to_cpup(nbytes_p); printk(KERN_INFO "OPAL nvram setup, %u bytes\n", nvram_size); of_node_put(np); -- cgit v0.10.2 From 3a1a46612d4882462e8d06866df717e1707abbba Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 23 Sep 2013 12:05:01 +1000 Subject: powerpc/powernv: Fix endian issues in powernv PCI code Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index 9a903ed..f9cb6c5 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -457,7 +457,7 @@ static void pnv_ioda_setup_bus_dma(struct pnv_ioda_pe *pe, struct pci_bus *bus) static void pnv_pci_ioda1_tce_invalidate(struct iommu_table *tbl, u64 *startp, u64 *endp) { - u64 __iomem *invalidate = (u64 __iomem *)tbl->it_index; + __be64 __iomem *invalidate = (__be64 __iomem *)tbl->it_index; unsigned long start, end, inc; start = __pa(startp); @@ -484,7 +484,7 @@ static void pnv_pci_ioda1_tce_invalidate(struct iommu_table *tbl, mb(); /* Ensure above stores are visible */ while (start <= end) { - __raw_writeq(start, invalidate); + __raw_writeq(cpu_to_be64(start), invalidate); start += inc; } @@ -499,7 +499,7 @@ static void pnv_pci_ioda2_tce_invalidate(struct pnv_ioda_pe *pe, u64 *startp, u64 *endp) { unsigned long start, end, inc; - u64 __iomem *invalidate = (u64 __iomem *)tbl->it_index; + __be64 __iomem *invalidate = (__be64 __iomem *)tbl->it_index; /* We'll invalidate DMA address in PE scope */ start = 0x2ul << 60; @@ -515,7 +515,7 @@ static void pnv_pci_ioda2_tce_invalidate(struct pnv_ioda_pe *pe, mb(); while (start <= end) { - __raw_writeq(start, invalidate); + __raw_writeq(cpu_to_be64(start), invalidate); start += inc; } } @@ -786,8 +786,7 @@ static int pnv_pci_ioda_msi_setup(struct pnv_phb *phb, struct pci_dev *dev, struct irq_data *idata; struct irq_chip *ichip; unsigned int xive_num = hwirq - phb->msi_base; - uint64_t addr64; - uint32_t addr32, data; + __be32 data; int rc; /* No PE assigned ? bail out ... no MSI for you ! */ @@ -811,6 +810,8 @@ static int pnv_pci_ioda_msi_setup(struct pnv_phb *phb, struct pci_dev *dev, } if (is_64) { + __be64 addr64; + rc = opal_get_msi_64(phb->opal_id, pe->mve_number, xive_num, 1, &addr64, &data); if (rc) { @@ -818,9 +819,11 @@ static int pnv_pci_ioda_msi_setup(struct pnv_phb *phb, struct pci_dev *dev, pci_name(dev), rc); return -EIO; } - msg->address_hi = addr64 >> 32; - msg->address_lo = addr64 & 0xfffffffful; + msg->address_hi = be64_to_cpu(addr64) >> 32; + msg->address_lo = be64_to_cpu(addr64) & 0xfffffffful; } else { + __be32 addr32; + rc = opal_get_msi_32(phb->opal_id, pe->mve_number, xive_num, 1, &addr32, &data); if (rc) { @@ -829,9 +832,9 @@ static int pnv_pci_ioda_msi_setup(struct pnv_phb *phb, struct pci_dev *dev, return -EIO; } msg->address_hi = 0; - msg->address_lo = addr32; + msg->address_lo = be32_to_cpu(addr32); } - msg->data = data; + msg->data = be32_to_cpu(data); /* * Change the IRQ chip for the MSI interrupts on PHB3. @@ -1107,7 +1110,7 @@ void __init pnv_pci_init_ioda_phb(struct device_node *np, struct pnv_phb *phb; unsigned long size, m32map_off, iomap_off, pemap_off; const __be64 *prop64; - const u32 *prop32; + const __be32 *prop32; int len; u64 phb_id; void *aux; @@ -1142,8 +1145,8 @@ void __init pnv_pci_init_ioda_phb(struct device_node *np, spin_lock_init(&phb->lock); prop32 = of_get_property(np, "bus-range", &len); if (prop32 && len == 8) { - hose->first_busno = prop32[0]; - hose->last_busno = prop32[1]; + hose->first_busno = be32_to_cpu(prop32[0]); + hose->last_busno = be32_to_cpu(prop32[1]); } else { pr_warn(" Broken on %s\n", np->full_name); hose->first_busno = 0; @@ -1175,7 +1178,7 @@ void __init pnv_pci_init_ioda_phb(struct device_node *np, if (!prop32) phb->ioda.total_pe = 1; else - phb->ioda.total_pe = *prop32; + phb->ioda.total_pe = be32_to_cpup(prop32); phb->ioda.m32_size = resource_size(&hose->mem_resources[0]); /* FW Has already off top 64k of M32 space (MSI space) */ diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c index 9122215..2f73e0d 100644 --- a/arch/powerpc/platforms/powernv/pci.c +++ b/arch/powerpc/platforms/powernv/pci.c @@ -236,7 +236,7 @@ static void pnv_pci_config_check_eeh(struct pnv_phb *phb, { s64 rc; u8 fstate; - u16 pcierr; + __be16 pcierr; u32 pe_no; /* @@ -283,16 +283,16 @@ int pnv_pci_cfg_read(struct device_node *dn, break; } case 2: { - u16 v16; + __be16 v16; rc = opal_pci_config_read_half_word(phb->opal_id, bdfn, where, &v16); - *val = (rc == OPAL_SUCCESS) ? v16 : 0xffff; + *val = (rc == OPAL_SUCCESS) ? be16_to_cpu(v16) : 0xffff; break; } case 4: { - u32 v32; + __be32 v32; rc = opal_pci_config_read_word(phb->opal_id, bdfn, where, &v32); - *val = (rc == OPAL_SUCCESS) ? v32 : 0xffffffff; + *val = (rc == OPAL_SUCCESS) ? be32_to_cpu(v32) : 0xffffffff; break; } default: @@ -404,7 +404,7 @@ static int pnv_tce_build(struct iommu_table *tbl, long index, long npages, struct dma_attrs *attrs) { u64 proto_tce; - u64 *tcep, *tces; + __be64 *tcep, *tces; u64 rpn; proto_tce = TCE_PCI_READ; // Read allowed @@ -416,7 +416,7 @@ static int pnv_tce_build(struct iommu_table *tbl, long index, long npages, rpn = __pa(uaddr) >> TCE_SHIFT; while (npages--) - *(tcep++) = proto_tce | (rpn++ << TCE_RPN_SHIFT); + *(tcep++) = cpu_to_be64(proto_tce | (rpn++ << TCE_RPN_SHIFT)); /* Some implementations won't cache invalid TCEs and thus may not * need that flush. We'll probably turn it_type into a bit mask @@ -430,12 +430,12 @@ static int pnv_tce_build(struct iommu_table *tbl, long index, long npages, static void pnv_tce_free(struct iommu_table *tbl, long index, long npages) { - u64 *tcep, *tces; + __be64 *tcep, *tces; tces = tcep = ((u64 *)tbl->it_base) + index - tbl->it_offset; while (npages--) - *(tcep++) = 0; + *(tcep++) = cpu_to_be64(0); if (tbl->it_type & TCE_PCI_SWINV_FREE) pnv_pci_ioda_tce_invalidate(tbl, tces, tcep - 1); @@ -462,8 +462,8 @@ void pnv_pci_setup_iommu_table(struct iommu_table *tbl, static struct iommu_table *pnv_pci_setup_bml_iommu(struct pci_controller *hose) { struct iommu_table *tbl; - const __be64 *basep; - const __be32 *sizep, *swinvp; + const __be64 *basep, *swinvp; + const __be32 *sizep; basep = of_get_property(hose->dn, "linux,tce-base", NULL); sizep = of_get_property(hose->dn, "linux,tce-size", NULL); @@ -484,9 +484,8 @@ static struct iommu_table *pnv_pci_setup_bml_iommu(struct pci_controller *hose) swinvp = of_get_property(hose->dn, "linux,tce-sw-invalidate-info", NULL); if (swinvp) { - tbl->it_busno = of_read_ulong(&swinvp[1], 2); - tbl->it_index = - (unsigned long)ioremap(of_read_number(swinvp, 2), 8); + tbl->it_busno = swinvp[1]; + tbl->it_index = (unsigned long)ioremap(be64_to_cpup(swinvp), 8); tbl->it_type = TCE_PCI_SWINV_CREATE | TCE_PCI_SWINV_FREE; } return tbl; -- cgit v0.10.2 From 4f89363b1187ca0cc36ee6aebe0bee550f74288d Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 23 Sep 2013 12:05:02 +1000 Subject: powerpc/powernv: Fix endian issues in OPAL console and udbg backend Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h index c5cd728..6622ea4 100644 --- a/arch/powerpc/include/asm/opal.h +++ b/arch/powerpc/include/asm/opal.h @@ -537,12 +537,12 @@ typedef struct oppanel_line { } oppanel_line_t; /* API functions */ -int64_t opal_console_write(int64_t term_number, int64_t *length, +int64_t opal_console_write(int64_t term_number, __be64 *length, const uint8_t *buffer); -int64_t opal_console_read(int64_t term_number, int64_t *length, +int64_t opal_console_read(int64_t term_number, __be64 *length, uint8_t *buffer); int64_t opal_console_write_buffer_space(int64_t term_number, - int64_t *length); + __be64 *length); int64_t opal_rtc_read(uint32_t *year_month_day, uint64_t *hour_minute_second_millisecond); int64_t opal_rtc_write(uint32_t year_month_day, @@ -552,7 +552,7 @@ int64_t opal_cec_reboot(void); int64_t opal_read_nvram(uint64_t buffer, uint64_t size, uint64_t offset); int64_t opal_write_nvram(uint64_t buffer, uint64_t size, uint64_t offset); int64_t opal_handle_interrupt(uint64_t isn, uint64_t *outstanding_event_mask); -int64_t opal_poll_events(uint64_t *outstanding_event_mask); +int64_t opal_poll_events(__be64 *outstanding_event_mask); int64_t opal_pci_set_hub_tce_memory(uint64_t hub_id, uint64_t tce_mem_addr, uint64_t tce_mem_size); int64_t opal_pci_set_phb_tce_memory(uint64_t phb_id, uint64_t tce_mem_addr, diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c index 4ffa75e..eb7bf3b 100644 --- a/arch/powerpc/platforms/powernv/opal.c +++ b/arch/powerpc/platforms/powernv/opal.c @@ -164,27 +164,28 @@ void opal_notifier_disable(void) int opal_get_chars(uint32_t vtermno, char *buf, int count) { - s64 len, rc; - u64 evt; + s64 rc; + __be64 evt, len; if (!opal.entry) return -ENODEV; opal_poll_events(&evt); - if ((evt & OPAL_EVENT_CONSOLE_INPUT) == 0) + if ((be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_INPUT) == 0) return 0; - len = count; - rc = opal_console_read(vtermno, &len, buf); + len = cpu_to_be64(count); + rc = opal_console_read(vtermno, &len, buf); if (rc == OPAL_SUCCESS) - return len; + return be64_to_cpu(len); return 0; } int opal_put_chars(uint32_t vtermno, const char *data, int total_len) { int written = 0; + __be64 olen; s64 len, rc; unsigned long flags; - u64 evt; + __be64 evt; if (!opal.entry) return -ENODEV; @@ -199,13 +200,14 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len) */ spin_lock_irqsave(&opal_write_lock, flags); if (firmware_has_feature(FW_FEATURE_OPALv2)) { - rc = opal_console_write_buffer_space(vtermno, &len); + rc = opal_console_write_buffer_space(vtermno, &olen); + len = be64_to_cpu(olen); if (rc || len < total_len) { spin_unlock_irqrestore(&opal_write_lock, flags); /* Closed -> drop characters */ if (rc) return total_len; - opal_poll_events(&evt); + opal_poll_events(NULL); return -EAGAIN; } } @@ -216,8 +218,9 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len) rc = OPAL_BUSY; while(total_len > 0 && (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT || rc == OPAL_SUCCESS)) { - len = total_len; - rc = opal_console_write(vtermno, &len, data); + olen = cpu_to_be64(total_len); + rc = opal_console_write(vtermno, &olen, data); + len = be64_to_cpu(olen); /* Closed or other error drop */ if (rc != OPAL_SUCCESS && rc != OPAL_BUSY && @@ -237,7 +240,8 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len) */ do opal_poll_events(&evt); - while(rc == OPAL_SUCCESS && (evt & OPAL_EVENT_CONSOLE_OUTPUT)); + while(rc == OPAL_SUCCESS && + (be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_OUTPUT)); } spin_unlock_irqrestore(&opal_write_lock, flags); return written; -- cgit v0.10.2 From be401b372fae219cf04c0c38890834c813d8dc90 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 23 Sep 2013 12:05:03 +1000 Subject: powerpc/powernv: Fix OPAL entry and exit in little endian mode Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/powernv/opal-wrappers.S b/arch/powerpc/platforms/powernv/opal-wrappers.S index 8f38445..2a03e1e 100644 --- a/arch/powerpc/platforms/powernv/opal-wrappers.S +++ b/arch/powerpc/platforms/powernv/opal-wrappers.S @@ -34,7 +34,7 @@ mtmsrd r12,1; \ LOAD_REG_ADDR(r0,.opal_return); \ mtlr r0; \ - li r0,MSR_DR|MSR_IR; \ + li r0,MSR_DR|MSR_IR|MSR_LE;\ andc r12,r12,r0; \ li r0,token; \ mtspr SPRN_HSRR1,r12; \ @@ -45,6 +45,13 @@ hrfid _STATIC(opal_return) + /* + * Fixup endian on OPAL return... we should be able to simplify + * this by instead converting the below trampoline to a set of + * bytes (always BE) since MSR:LE will end up fixed up as a side + * effect of the rfid. + */ + FIXUP_ENDIAN ld r2,PACATOC(r13); ld r4,8(r1); ld r5,16(r1); -- cgit v0.10.2 From 29186097f9267938af399897482c5a53686b3cce Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 23 Sep 2013 12:05:04 +1000 Subject: powerpc/powernv: Don't register exception handlers in little endian mode The powernv exception handlers are not ready to take exceptions in little endian mode, so disable them. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c index eb7bf3b..c2391bb 100644 --- a/arch/powerpc/platforms/powernv/opal.c +++ b/arch/powerpc/platforms/powernv/opal.c @@ -77,6 +77,7 @@ int __init early_init_dt_scan_opal(unsigned long node, static int __init opal_register_exception_handlers(void) { +#ifdef __BIG_ENDIAN__ u64 glue; if (!(powerpc_firmware_features & FW_FEATURE_OPAL)) @@ -94,6 +95,7 @@ static int __init opal_register_exception_handlers(void) 0, glue); glue += 128; opal_register_exception_handler(OPAL_SOFTPATCH_HANDLER, 0, glue); +#endif return 0; } -- cgit v0.10.2 From 6feff6d4a5e1ac8c48d88860bf705be7709b42af Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 23 Sep 2013 12:05:05 +1000 Subject: powerpc/powernv: More little endian issues in OPAL RTC driver Sparse caught an issue where opal_set_rtc_time was incorrectly byteswapping. Also fix a number of sparse warnings. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h index 6622ea4..3db5e82 100644 --- a/arch/powerpc/include/asm/opal.h +++ b/arch/powerpc/include/asm/opal.h @@ -543,8 +543,8 @@ int64_t opal_console_read(int64_t term_number, __be64 *length, uint8_t *buffer); int64_t opal_console_write_buffer_space(int64_t term_number, __be64 *length); -int64_t opal_rtc_read(uint32_t *year_month_day, - uint64_t *hour_minute_second_millisecond); +int64_t opal_rtc_read(__be32 *year_month_day, + __be64 *hour_minute_second_millisecond); int64_t opal_rtc_write(uint32_t year_month_day, uint64_t hour_minute_second_millisecond); int64_t opal_cec_power_down(uint64_t request); diff --git a/arch/powerpc/platforms/powernv/opal-rtc.c b/arch/powerpc/platforms/powernv/opal-rtc.c index dbfdba3..7d07c7e 100644 --- a/arch/powerpc/platforms/powernv/opal-rtc.c +++ b/arch/powerpc/platforms/powernv/opal-rtc.c @@ -37,10 +37,12 @@ unsigned long __init opal_get_boot_time(void) struct rtc_time tm; u32 y_m_d; u64 h_m_s_ms; + __be32 __y_m_d; + __be64 __h_m_s_ms; long rc = OPAL_BUSY; while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { - rc = opal_rtc_read(&y_m_d, &h_m_s_ms); + rc = opal_rtc_read(&__y_m_d, &__h_m_s_ms); if (rc == OPAL_BUSY_EVENT) opal_poll_events(NULL); else @@ -48,8 +50,8 @@ unsigned long __init opal_get_boot_time(void) } if (rc != OPAL_SUCCESS) return 0; - y_m_d = be32_to_cpu(y_m_d); - h_m_s_ms = be64_to_cpu(h_m_s_ms); + y_m_d = be32_to_cpu(__y_m_d); + h_m_s_ms = be64_to_cpu(__h_m_s_ms); opal_to_tm(y_m_d, h_m_s_ms, &tm); return mktime(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); @@ -60,9 +62,11 @@ void opal_get_rtc_time(struct rtc_time *tm) long rc = OPAL_BUSY; u32 y_m_d; u64 h_m_s_ms; + __be32 __y_m_d; + __be64 __h_m_s_ms; while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { - rc = opal_rtc_read(&y_m_d, &h_m_s_ms); + rc = opal_rtc_read(&__y_m_d, &__h_m_s_ms); if (rc == OPAL_BUSY_EVENT) opal_poll_events(NULL); else @@ -70,8 +74,8 @@ void opal_get_rtc_time(struct rtc_time *tm) } if (rc != OPAL_SUCCESS) return; - y_m_d = be32_to_cpu(y_m_d); - h_m_s_ms = be64_to_cpu(h_m_s_ms); + y_m_d = be32_to_cpu(__y_m_d); + h_m_s_ms = be64_to_cpu(__h_m_s_ms); opal_to_tm(y_m_d, h_m_s_ms, tm); } @@ -90,9 +94,6 @@ int opal_set_rtc_time(struct rtc_time *tm) h_m_s_ms |= ((u64)bin2bcd(tm->tm_min)) << 48; h_m_s_ms |= ((u64)bin2bcd(tm->tm_sec)) << 40; - y_m_d = cpu_to_be32(y_m_d); - h_m_s_ms = cpu_to_be64(h_m_s_ms); - while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { rc = opal_rtc_write(y_m_d, h_m_s_ms); if (rc == OPAL_BUSY_EVENT) -- cgit v0.10.2 From 5e4da530a5348e53bbb9f6f7f73c9afc67ed6c35 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 23 Sep 2013 12:05:06 +1000 Subject: powerpc/powernv: Fix some PCI sparse errors and one LE bug pnv_pci_setup_bml_iommu was missing a byteswap of a device tree property. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h index 3db5e82..51e3b26 100644 --- a/arch/powerpc/include/asm/opal.h +++ b/arch/powerpc/include/asm/opal.h @@ -551,7 +551,7 @@ int64_t opal_cec_power_down(uint64_t request); int64_t opal_cec_reboot(void); int64_t opal_read_nvram(uint64_t buffer, uint64_t size, uint64_t offset); int64_t opal_write_nvram(uint64_t buffer, uint64_t size, uint64_t offset); -int64_t opal_handle_interrupt(uint64_t isn, uint64_t *outstanding_event_mask); +int64_t opal_handle_interrupt(uint64_t isn, __be64 *outstanding_event_mask); int64_t opal_poll_events(__be64 *outstanding_event_mask); int64_t opal_pci_set_hub_tce_memory(uint64_t hub_id, uint64_t tce_mem_addr, uint64_t tce_mem_size); @@ -560,9 +560,9 @@ int64_t opal_pci_set_phb_tce_memory(uint64_t phb_id, uint64_t tce_mem_addr, int64_t opal_pci_config_read_byte(uint64_t phb_id, uint64_t bus_dev_func, uint64_t offset, uint8_t *data); int64_t opal_pci_config_read_half_word(uint64_t phb_id, uint64_t bus_dev_func, - uint64_t offset, uint16_t *data); + uint64_t offset, __be16 *data); int64_t opal_pci_config_read_word(uint64_t phb_id, uint64_t bus_dev_func, - uint64_t offset, uint32_t *data); + uint64_t offset, __be32 *data); int64_t opal_pci_config_write_byte(uint64_t phb_id, uint64_t bus_dev_func, uint64_t offset, uint8_t data); int64_t opal_pci_config_write_half_word(uint64_t phb_id, uint64_t bus_dev_func, @@ -570,14 +570,14 @@ int64_t opal_pci_config_write_half_word(uint64_t phb_id, uint64_t bus_dev_func, int64_t opal_pci_config_write_word(uint64_t phb_id, uint64_t bus_dev_func, uint64_t offset, uint32_t data); int64_t opal_set_xive(uint32_t isn, uint16_t server, uint8_t priority); -int64_t opal_get_xive(uint32_t isn, uint16_t *server, uint8_t *priority); +int64_t opal_get_xive(uint32_t isn, __be16 *server, uint8_t *priority); int64_t opal_register_exception_handler(uint64_t opal_exception, uint64_t handler_address, uint64_t glue_cache_line); int64_t opal_pci_eeh_freeze_status(uint64_t phb_id, uint64_t pe_number, uint8_t *freeze_state, - uint16_t *pci_error_type, - uint64_t *phb_status); + __be16 *pci_error_type, + __be64 *phb_status); int64_t opal_pci_eeh_freeze_clear(uint64_t phb_id, uint64_t pe_number, uint64_t eeh_action_token); int64_t opal_pci_shpc(uint64_t phb_id, uint64_t shpc_action, uint8_t *state); @@ -614,13 +614,13 @@ int64_t opal_pci_msi_eoi(uint64_t phb_id, uint32_t hw_irq); int64_t opal_pci_set_xive_pe(uint64_t phb_id, uint32_t pe_number, uint32_t xive_num); int64_t opal_get_xive_source(uint64_t phb_id, uint32_t xive_num, - int32_t *interrupt_source_number); + __be32 *interrupt_source_number); int64_t opal_get_msi_32(uint64_t phb_id, uint32_t mve_number, uint32_t xive_num, - uint8_t msi_range, uint32_t *msi_address, - uint32_t *message_data); + uint8_t msi_range, __be32 *msi_address, + __be32 *message_data); int64_t opal_get_msi_64(uint64_t phb_id, uint32_t mve_number, uint32_t xive_num, uint8_t msi_range, - uint64_t *msi_address, uint32_t *message_data); + __be64 *msi_address, __be32 *message_data); int64_t opal_start_cpu(uint64_t thread_number, uint64_t start_address); int64_t opal_query_cpu_status(uint64_t thread_number, uint8_t *thread_status); int64_t opal_write_oppanel(oppanel_line_t *lines, uint64_t num_lines); @@ -642,7 +642,7 @@ int64_t opal_pci_fence_phb(uint64_t phb_id); int64_t opal_pci_reinit(uint64_t phb_id, uint8_t reinit_scope); int64_t opal_pci_mask_pe_error(uint64_t phb_id, uint16_t pe_number, uint8_t error_type, uint8_t mask_action); int64_t opal_set_slot_led_status(uint64_t phb_id, uint64_t slot_id, uint8_t led_type, uint8_t led_action); -int64_t opal_get_epow_status(uint64_t *status); +int64_t opal_get_epow_status(__be64 *status); int64_t opal_set_system_attention_led(uint8_t led_action); int64_t opal_pci_next_error(uint64_t phb_id, uint64_t *first_frozen_pe, uint16_t *pci_error_type, uint16_t *severity); diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c index c2391bb..09336f0 100644 --- a/arch/powerpc/platforms/powernv/opal.c +++ b/arch/powerpc/platforms/powernv/opal.c @@ -366,7 +366,7 @@ int opal_machine_check(struct pt_regs *regs) static irqreturn_t opal_interrupt(int irq, void *data) { - uint64_t events; + __be64 events; opal_handle_interrupt(virq_to_hw(irq), &events); diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index f9cb6c5..a6531d2 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -455,7 +455,7 @@ static void pnv_ioda_setup_bus_dma(struct pnv_ioda_pe *pe, struct pci_bus *bus) } static void pnv_pci_ioda1_tce_invalidate(struct iommu_table *tbl, - u64 *startp, u64 *endp) + __be64 *startp, __be64 *endp) { __be64 __iomem *invalidate = (__be64 __iomem *)tbl->it_index; unsigned long start, end, inc; @@ -496,7 +496,7 @@ static void pnv_pci_ioda1_tce_invalidate(struct iommu_table *tbl, static void pnv_pci_ioda2_tce_invalidate(struct pnv_ioda_pe *pe, struct iommu_table *tbl, - u64 *startp, u64 *endp) + __be64 *startp, __be64 *endp) { unsigned long start, end, inc; __be64 __iomem *invalidate = (__be64 __iomem *)tbl->it_index; @@ -521,7 +521,7 @@ static void pnv_pci_ioda2_tce_invalidate(struct pnv_ioda_pe *pe, } void pnv_pci_ioda_tce_invalidate(struct iommu_table *tbl, - u64 *startp, u64 *endp) + __be64 *startp, __be64 *endp) { struct pnv_ioda_pe *pe = container_of(tbl, struct pnv_ioda_pe, tce32_table); diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c index 2f73e0d..a26956c 100644 --- a/arch/powerpc/platforms/powernv/pci.c +++ b/arch/powerpc/platforms/powernv/pci.c @@ -412,7 +412,7 @@ static int pnv_tce_build(struct iommu_table *tbl, long index, long npages, if (direction != DMA_TO_DEVICE) proto_tce |= TCE_PCI_WRITE; - tces = tcep = ((u64 *)tbl->it_base) + index - tbl->it_offset; + tces = tcep = ((__be64 *)tbl->it_base) + index - tbl->it_offset; rpn = __pa(uaddr) >> TCE_SHIFT; while (npages--) @@ -432,7 +432,7 @@ static void pnv_tce_free(struct iommu_table *tbl, long index, long npages) { __be64 *tcep, *tces; - tces = tcep = ((u64 *)tbl->it_base) + index - tbl->it_offset; + tces = tcep = ((__be64 *)tbl->it_base) + index - tbl->it_offset; while (npages--) *(tcep++) = cpu_to_be64(0); @@ -484,7 +484,7 @@ static struct iommu_table *pnv_pci_setup_bml_iommu(struct pci_controller *hose) swinvp = of_get_property(hose->dn, "linux,tce-sw-invalidate-info", NULL); if (swinvp) { - tbl->it_busno = swinvp[1]; + tbl->it_busno = be64_to_cpu(swinvp[1]); tbl->it_index = (unsigned long)ioremap(be64_to_cpup(swinvp), 8); tbl->it_type = TCE_PCI_SWINV_CREATE | TCE_PCI_SWINV_FREE; } diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h index d633c64..dfe2010 100644 --- a/arch/powerpc/platforms/powernv/pci.h +++ b/arch/powerpc/platforms/powernv/pci.h @@ -193,6 +193,6 @@ extern void pnv_pci_init_p5ioc2_hub(struct device_node *np); extern void pnv_pci_init_ioda_hub(struct device_node *np); extern void pnv_pci_init_ioda2_phb(struct device_node *np); extern void pnv_pci_ioda_tce_invalidate(struct iommu_table *tbl, - u64 *startp, u64 *endp); + __be64 *startp, __be64 *endp); #endif /* __POWERNV_PCI_H */ -- cgit v0.10.2 From 99fc1d91b8fc30c969b0a2d152c803413ecb8cef Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 23 Sep 2013 12:05:07 +1000 Subject: powerpc/hvsi: Fix endian issues in HVSI driver Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/hvsi.h b/arch/powerpc/include/asm/hvsi.h index d3f64f3..d4a5315 100644 --- a/arch/powerpc/include/asm/hvsi.h +++ b/arch/powerpc/include/asm/hvsi.h @@ -25,7 +25,7 @@ struct hvsi_header { uint8_t type; uint8_t len; - uint16_t seqno; + __be16 seqno; } __attribute__((packed)); struct hvsi_data { @@ -35,24 +35,24 @@ struct hvsi_data { struct hvsi_control { struct hvsi_header hdr; - uint16_t verb; + __be16 verb; /* optional depending on verb: */ - uint32_t word; - uint32_t mask; + __be32 word; + __be32 mask; } __attribute__((packed)); struct hvsi_query { struct hvsi_header hdr; - uint16_t verb; + __be16 verb; } __attribute__((packed)); struct hvsi_query_response { struct hvsi_header hdr; - uint16_t verb; - uint16_t query_seqno; + __be16 verb; + __be16 query_seqno; union { uint8_t version; - uint32_t mctrl_word; + __be32 mctrl_word; } u; } __attribute__((packed)); diff --git a/drivers/tty/hvc/hvsi_lib.c b/drivers/tty/hvc/hvsi_lib.c index ac27671..347050e 100644 --- a/drivers/tty/hvc/hvsi_lib.c +++ b/drivers/tty/hvc/hvsi_lib.c @@ -9,7 +9,7 @@ static int hvsi_send_packet(struct hvsi_priv *pv, struct hvsi_header *packet) { - packet->seqno = atomic_inc_return(&pv->seqno); + packet->seqno = cpu_to_be16(atomic_inc_return(&pv->seqno)); /* Assumes that always succeeds, works in practice */ return pv->put_chars(pv->termno, (char *)packet, packet->len); @@ -28,7 +28,7 @@ static void hvsi_start_handshake(struct hvsi_priv *pv) /* Send version query */ q.hdr.type = VS_QUERY_PACKET_HEADER; q.hdr.len = sizeof(struct hvsi_query); - q.verb = VSV_SEND_VERSION_NUMBER; + q.verb = cpu_to_be16(VSV_SEND_VERSION_NUMBER); hvsi_send_packet(pv, &q.hdr); } @@ -40,7 +40,7 @@ static int hvsi_send_close(struct hvsi_priv *pv) ctrl.hdr.type = VS_CONTROL_PACKET_HEADER; ctrl.hdr.len = sizeof(struct hvsi_control); - ctrl.verb = VSV_CLOSE_PROTOCOL; + ctrl.verb = cpu_to_be16(VSV_CLOSE_PROTOCOL); return hvsi_send_packet(pv, &ctrl.hdr); } @@ -69,14 +69,14 @@ static void hvsi_got_control(struct hvsi_priv *pv) { struct hvsi_control *pkt = (struct hvsi_control *)pv->inbuf; - switch (pkt->verb) { + switch (be16_to_cpu(pkt->verb)) { case VSV_CLOSE_PROTOCOL: /* We restart the handshaking */ hvsi_start_handshake(pv); break; case VSV_MODEM_CTL_UPDATE: /* Transition of carrier detect */ - hvsi_cd_change(pv, pkt->word & HVSI_TSCD); + hvsi_cd_change(pv, be32_to_cpu(pkt->word) & HVSI_TSCD); break; } } @@ -87,7 +87,7 @@ static void hvsi_got_query(struct hvsi_priv *pv) struct hvsi_query_response r; /* We only handle version queries */ - if (pkt->verb != VSV_SEND_VERSION_NUMBER) + if (be16_to_cpu(pkt->verb) != VSV_SEND_VERSION_NUMBER) return; pr_devel("HVSI@%x: Got version query, sending response...\n", @@ -96,7 +96,7 @@ static void hvsi_got_query(struct hvsi_priv *pv) /* Send version response */ r.hdr.type = VS_QUERY_RESPONSE_PACKET_HEADER; r.hdr.len = sizeof(struct hvsi_query_response); - r.verb = VSV_SEND_VERSION_NUMBER; + r.verb = cpu_to_be16(VSV_SEND_VERSION_NUMBER); r.u.version = HVSI_VERSION; r.query_seqno = pkt->hdr.seqno; hvsi_send_packet(pv, &r.hdr); @@ -112,7 +112,7 @@ static void hvsi_got_response(struct hvsi_priv *pv) switch(r->verb) { case VSV_SEND_MODEM_CTL_STATUS: - hvsi_cd_change(pv, r->u.mctrl_word & HVSI_TSCD); + hvsi_cd_change(pv, be32_to_cpu(r->u.mctrl_word) & HVSI_TSCD); pv->mctrl_update = 1; break; } @@ -265,8 +265,7 @@ int hvsilib_read_mctrl(struct hvsi_priv *pv) pv->mctrl_update = 0; q.hdr.type = VS_QUERY_PACKET_HEADER; q.hdr.len = sizeof(struct hvsi_query); - q.hdr.seqno = atomic_inc_return(&pv->seqno); - q.verb = VSV_SEND_MODEM_CTL_STATUS; + q.verb = cpu_to_be16(VSV_SEND_MODEM_CTL_STATUS); rc = hvsi_send_packet(pv, &q.hdr); if (rc <= 0) { pr_devel("HVSI@%x: Error %d...\n", pv->termno, rc); @@ -304,9 +303,9 @@ int hvsilib_write_mctrl(struct hvsi_priv *pv, int dtr) ctrl.hdr.type = VS_CONTROL_PACKET_HEADER, ctrl.hdr.len = sizeof(struct hvsi_control); - ctrl.verb = VSV_SET_MODEM_CTL; - ctrl.mask = HVSI_TSDTR; - ctrl.word = dtr ? HVSI_TSDTR : 0; + ctrl.verb = cpu_to_be16(VSV_SET_MODEM_CTL); + ctrl.mask = cpu_to_be32(HVSI_TSDTR); + ctrl.word = cpu_to_be32(dtr ? HVSI_TSDTR : 0); return hvsi_send_packet(pv, &ctrl.hdr); } -- cgit v0.10.2 From 5c94913c5f9834b0d35e20359db457783962bed5 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 23 Sep 2013 12:05:08 +1000 Subject: tty/hvc_opal: powerpc: Make OPAL HVC device tree accesses endian safe Signed-off-by: Benjamin Herrenschmidt diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c index cd69b48..6496872 100644 --- a/drivers/tty/hvc/hvc_opal.c +++ b/drivers/tty/hvc/hvc_opal.c @@ -329,7 +329,7 @@ static void udbg_init_opal_common(void) void __init hvc_opal_init_early(void) { struct device_node *stdout_node = NULL; - const u32 *termno; + const __be32 *termno; const char *name = NULL; const struct hv_ops *ops; u32 index; @@ -371,7 +371,7 @@ void __init hvc_opal_init_early(void) if (!stdout_node) return; termno = of_get_property(stdout_node, "reg", NULL); - index = termno ? *termno : 0; + index = termno ? be32_to_cpup(termno) : 0; if (index >= MAX_NR_HVC_CONSOLES) return; hvc_opal_privs[index] = &hvc_opal_boot_priv; -- cgit v0.10.2 From 7df697c81587114ad4847598dd2d6061b73f1a12 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 23 Sep 2013 12:05:09 +1000 Subject: KVM: PPC: Disable KVM on little endian builds There are a number of KVM issues with little endian builds. We are working on fixing them, but in the meantime disable it. Signed-off-by: Anton Blanchard Cc: Alexander Graf Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kvm/Kconfig b/arch/powerpc/kvm/Kconfig index ffaef2c..e593ff2 100644 --- a/arch/powerpc/kvm/Kconfig +++ b/arch/powerpc/kvm/Kconfig @@ -6,6 +6,7 @@ source "virt/kvm/Kconfig" menuconfig VIRTUALIZATION bool "Virtualization" + depends on !CPU_LITTLE_ENDIAN ---help--- Say Y here to get to see options for using your Linux host to run other operating systems inside virtual machines (guests). -- cgit v0.10.2 From d72b08017161ab385d4ae080ea415c9eb7ceef83 Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Mon, 23 Sep 2013 12:05:11 +1000 Subject: powerpc: Add ability to build little endian kernels This patch allows the kbuild system to successfully compile a kernel for the little endian PowerPC64 architecture. A subsequent patch will add the CONFIG_CPU_LITTLE_ENDIAN kernel config option which must be set to build such a kernel. If cross compiling, CROSS_COMPILE must point to a suitable toolchain (compiled for the powerpc64le-linux and powerpcle-linux targets). Signed-off-by: Ian Munsie Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile index debfa2b..d3c91bf 100644 --- a/arch/powerpc/Makefile +++ b/arch/powerpc/Makefile @@ -65,11 +65,29 @@ endif UTS_MACHINE := $(OLDARCH) +ifeq ($(CONFIG_CPU_LITTLE_ENDIAN),y) +override CC += -mlittle-endian +override AS += -mlittle-endian +override LD += -EL +override CROSS32CC += -mlittle-endian +override CROSS32AS += -mlittle-endian +LDEMULATION := lppc +GNUTARGET := powerpcle +MULTIPLEWORD := -mno-multiple +else +override CC += -mbig-endian +override AS += -mbig-endian +override LD += -EB +LDEMULATION := ppc +GNUTARGET := powerpc +MULTIPLEWORD := -mmultiple +endif + ifeq ($(HAS_BIARCH),y) override AS += -a$(CONFIG_WORD_SIZE) -override LD += -m elf$(CONFIG_WORD_SIZE)ppc +override LD += -m elf$(CONFIG_WORD_SIZE)$(LDEMULATION) override CC += -m$(CONFIG_WORD_SIZE) -override AR := GNUTARGET=elf$(CONFIG_WORD_SIZE)-powerpc $(AR) +override AR := GNUTARGET=elf$(CONFIG_WORD_SIZE)-$(GNUTARGET) $(AR) endif LDFLAGS_vmlinux-y := -Bstatic @@ -95,7 +113,7 @@ endif CFLAGS-$(CONFIG_PPC64) := -mtraceback=no -mcall-aixdesc CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mcmodel=medium,-mminimal-toc) CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mno-pointers-to-nested-functions) -CFLAGS-$(CONFIG_PPC32) := -ffixed-r2 -mmultiple +CFLAGS-$(CONFIG_PPC32) := -ffixed-r2 $(MULTIPLEWORD) ifeq ($(CONFIG_PPC_BOOK3S_64),y) CFLAGS-$(CONFIG_GENERIC_CPU) += $(call cc-option,-mtune=power7,-mtune=power4) diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index 15ca225..ca7f08c 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -22,7 +22,8 @@ all: $(obj)/zImage BOOTCFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \ -fno-strict-aliasing -Os -msoft-float -pipe \ -fomit-frame-pointer -fno-builtin -fPIC -nostdinc \ - -isystem $(shell $(CROSS32CC) -print-file-name=include) + -isystem $(shell $(CROSS32CC) -print-file-name=include) \ + -mbig-endian BOOTAFLAGS := -D__ASSEMBLY__ $(BOOTCFLAGS) -traditional -nostdinc ifdef CONFIG_DEBUG_INFO diff --git a/arch/powerpc/kernel/vdso32/vdso32.lds.S b/arch/powerpc/kernel/vdso32/vdso32.lds.S index f223409..e58ee10 100644 --- a/arch/powerpc/kernel/vdso32/vdso32.lds.S +++ b/arch/powerpc/kernel/vdso32/vdso32.lds.S @@ -4,7 +4,11 @@ */ #include +#ifdef __LITTLE_ENDIAN__ +OUTPUT_FORMAT("elf32-powerpcle", "elf32-powerpcle", "elf32-powerpcle") +#else OUTPUT_FORMAT("elf32-powerpc", "elf32-powerpc", "elf32-powerpc") +#endif OUTPUT_ARCH(powerpc:common) ENTRY(_start) diff --git a/arch/powerpc/kernel/vdso64/vdso64.lds.S b/arch/powerpc/kernel/vdso64/vdso64.lds.S index e486381..64fb183 100644 --- a/arch/powerpc/kernel/vdso64/vdso64.lds.S +++ b/arch/powerpc/kernel/vdso64/vdso64.lds.S @@ -4,7 +4,11 @@ */ #include +#ifdef __LITTLE_ENDIAN__ +OUTPUT_FORMAT("elf64-powerpcle", "elf64-powerpcle", "elf64-powerpcle") +#else OUTPUT_FORMAT("elf64-powerpc", "elf64-powerpc", "elf64-powerpc") +#endif OUTPUT_ARCH(powerpc:common64) ENTRY(_start) -- cgit v0.10.2 From 0b5e6661ac69983a54d9832ac33fd27d733306f8 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 23 Sep 2013 12:05:12 +1000 Subject: powerpc: Don't set HAVE_EFFICIENT_UNALIGNED_ACCESS on little endian builds POWER7 takes alignment exceptions on some unaligned addresses, so disable HAVE_EFFICIENT_UNALIGNED_ACCESS. This fixes an early boot issue in the printk code. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 2c69c43..c5a868b 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -97,7 +97,7 @@ config PPC select VIRT_TO_BUS if !PPC64 select HAVE_IDE select HAVE_IOREMAP_PROT - select HAVE_EFFICIENT_UNALIGNED_ACCESS + select HAVE_EFFICIENT_UNALIGNED_ACCESS if !CPU_LITTLE_ENDIAN select HAVE_KPROBES select HAVE_ARCH_KGDB select HAVE_KRETPROBES -- cgit v0.10.2 From f036b368196263f648bede42bbbe1c1e6e834d10 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 23 Sep 2013 12:05:13 +1000 Subject: powerpc: Work around little endian gcc bug Temporarily work around an ICE we are seeing while building in little endian mode: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57134 Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile index d3c91bf..607acf5 100644 --- a/arch/powerpc/Makefile +++ b/arch/powerpc/Makefile @@ -66,7 +66,7 @@ endif UTS_MACHINE := $(OLDARCH) ifeq ($(CONFIG_CPU_LITTLE_ENDIAN),y) -override CC += -mlittle-endian +override CC += -mlittle-endian -mno-strict-align override AS += -mlittle-endian override LD += -EL override CROSS32CC += -mlittle-endian -- cgit v0.10.2 From 32dda05f4ec2b854b594bd91590c46c5197d77e1 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Thu, 26 Sep 2013 19:18:18 -0500 Subject: powerpc/mpic: Disable preemption when calling mpic_processor_id() Otherwise, we get a debug traceback due to the use of smp_processor_id() (or get_paca()) inside hard_smp_processor_id(). mpic_host_map() is just looking for a default CPU, so it doesn't matter if we migrate after getting the CPU ID. Signed-off-by: Scott Wood Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 1be54fa..bdcb858 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -1088,8 +1088,14 @@ static int mpic_host_map(struct irq_domain *h, unsigned int virq, * is done here. */ if (!mpic_is_ipi(mpic, hw) && (mpic->flags & MPIC_NO_RESET)) { + int cpu; + + preempt_disable(); + cpu = mpic_processor_id(mpic); + preempt_enable(); + mpic_set_vector(virq, hw); - mpic_set_destination(virq, mpic_processor_id(mpic)); + mpic_set_destination(virq, cpu); mpic_irq_set_priority(virq, 8); } -- cgit v0.10.2 From dc1473dcfaf4fb38ba2dcb2fb7ac7d0242185fa3 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 30 Sep 2013 15:11:42 +0200 Subject: powerpc/legacy_serial: Fix incorrect placement of __initdata tag __initdata tag should be placed between the variable name and equal sign for the variable to be placed in the intended .init.data section. Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/legacy_serial.c b/arch/powerpc/kernel/legacy_serial.c index 22e88dd..40bd7bd 100644 --- a/arch/powerpc/kernel/legacy_serial.c +++ b/arch/powerpc/kernel/legacy_serial.c @@ -35,7 +35,7 @@ static struct legacy_serial_info { phys_addr_t taddr; } legacy_serial_infos[MAX_LEGACY_SERIAL_PORTS]; -static struct __initdata of_device_id legacy_serial_parents[] = { +static struct of_device_id legacy_serial_parents[] __initdata = { {.type = "soc",}, {.type = "tsi-bridge",}, {.type = "opb", }, -- cgit v0.10.2 From 1443d6a2a740b0b0dd644dba0fb96863eea238eb Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 30 Sep 2013 15:13:55 +0200 Subject: powerpc/8xx/tqm8xx: Fix incorrect placement of __initdata tag __initdata tag should be placed between the variable name and equal sign for the variable to be placed in the intended .init.data section. Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/8xx/tqm8xx_setup.c b/arch/powerpc/platforms/8xx/tqm8xx_setup.c index 8d21ab7..ef0778a 100644 --- a/arch/powerpc/platforms/8xx/tqm8xx_setup.c +++ b/arch/powerpc/platforms/8xx/tqm8xx_setup.c @@ -48,7 +48,7 @@ struct cpm_pin { int port, pin, flags; }; -static struct __initdata cpm_pin tqm8xx_pins[] = { +static struct cpm_pin tqm8xx_pins[] __initdata = { /* SMC1 */ {CPM_PORTB, 24, CPM_PIN_INPUT}, /* RX */ {CPM_PORTB, 25, CPM_PIN_INPUT | CPM_PIN_SECONDARY}, /* TX */ @@ -63,7 +63,7 @@ static struct __initdata cpm_pin tqm8xx_pins[] = { {CPM_PORTC, 11, CPM_PIN_INPUT | CPM_PIN_SECONDARY | CPM_PIN_GPIO}, }; -static struct __initdata cpm_pin tqm8xx_fec_pins[] = { +static struct cpm_pin tqm8xx_fec_pins[] __initdata = { /* MII */ {CPM_PORTD, 3, CPM_PIN_OUTPUT}, {CPM_PORTD, 4, CPM_PIN_OUTPUT}, -- cgit v0.10.2 From 0edfdd10f57bd989f7c2bc31ce6f601bbee1b664 Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Thu, 26 Sep 2013 16:41:34 +0800 Subject: powerpc/ppc64: Remove the unneeded load of ti_flags in resume_kernel We already got the value of current_thread_info and ti_flags and store them into r9 and r4 respectively before jumping to resume_kernel. So there is no reason to reload them again. Signed-off-by: Kevin Hao Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index 889ea2b..12679cd 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -673,9 +673,7 @@ _GLOBAL(ret_from_except_lite) resume_kernel: /* check current_thread_info, _TIF_EMULATE_STACK_STORE */ - CURRENT_THREAD_INFO(r9, r1) - ld r8,TI_FLAGS(r9) - andis. r8,r8,_TIF_EMULATE_STACK_STORE@h + andis. r8,r4,_TIF_EMULATE_STACK_STORE@h beq+ 1f addi r8,r1,INT_FRAME_SIZE /* Get the kprobed function entry */ -- cgit v0.10.2 From 8616dff5b0e969444cc8484875ebd936956b5c0d Mon Sep 17 00:00:00 2001 From: Vladimir Murzin Date: Sun, 29 Sep 2013 14:41:18 +0200 Subject: powerpc: Fix section mismatch warning in free_lppacas While cross-building for PPC64 I've got bunch of WARNING: arch/powerpc/kernel/built-in.o(.text.unlikely+0x2d2): Section mismatch in reference from the function .free_lppacas() to the variable .init.data:lppaca_size The function .free_lppacas() references the variable __initdata lppaca_size. This is often because .free_lppacas lacks a __initdata annotation or the annotation of lppaca_size is wrong. Fix it by using proper annotation for free_lppacas. Additionally, annotate {allocate,new}_llpcas properly. Signed-off-by: Vladimir Murzin Acked-by: Michael Ellerman Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/paca.c b/arch/powerpc/kernel/paca.c index 3fc16e3..0620eaa 100644 --- a/arch/powerpc/kernel/paca.c +++ b/arch/powerpc/kernel/paca.c @@ -46,7 +46,7 @@ struct lppaca lppaca[] = { static struct lppaca *extra_lppacas; static long __initdata lppaca_size; -static void allocate_lppacas(int nr_cpus, unsigned long limit) +static void __init allocate_lppacas(int nr_cpus, unsigned long limit) { if (nr_cpus <= NR_LPPACAS) return; @@ -57,7 +57,7 @@ static void allocate_lppacas(int nr_cpus, unsigned long limit) PAGE_SIZE, limit)); } -static struct lppaca *new_lppaca(int cpu) +static struct lppaca * __init new_lppaca(int cpu) { struct lppaca *lp; @@ -70,7 +70,7 @@ static struct lppaca *new_lppaca(int cpu) return lp; } -static void free_lppacas(void) +static void __init free_lppacas(void) { long new_size = 0, nr; -- cgit v0.10.2 From 41b93b238ab394c8cf0935ec3ba31c700a2b3b25 Mon Sep 17 00:00:00 2001 From: Bharat Bhushan Date: Wed, 9 Oct 2013 10:41:17 +0530 Subject: powerpc: Added __cmpdi2 for signed 64bit comparision This was missing on powerpc and I am getting compilation error drivers/vfio/pci/vfio_pci_rdwr.c:193: undefined reference to `__cmpdi2' drivers/vfio/pci/vfio_pci_rdwr.c:193: undefined reference to `__cmpdi2' Signed-off-by: Bharat Bhushan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/misc_32.S b/arch/powerpc/kernel/misc_32.S index 2b0ad98..e47d268 100644 --- a/arch/powerpc/kernel/misc_32.S +++ b/arch/powerpc/kernel/misc_32.S @@ -659,6 +659,20 @@ _GLOBAL(__lshrdi3) blr /* + * 64-bit comparison: __cmpdi2(s64 a, s64 b) + * Returns 0 if a < b, 1 if a == b, 2 if a > b. + */ +_GLOBAL(__cmpdi2) + cmpw r3,r5 + li r3,1 + bne 1f + cmplw r4,r6 + beqlr +1: li r3,0 + bltlr + li r3,2 + blr +/* * 64-bit comparison: __ucmpdi2(u64 a, u64 b) * Returns 0 if a < b, 1 if a == b, 2 if a > b. */ diff --git a/arch/powerpc/kernel/ppc_ksyms.c b/arch/powerpc/kernel/ppc_ksyms.c index 60bbeb2..d8c3407 100644 --- a/arch/powerpc/kernel/ppc_ksyms.c +++ b/arch/powerpc/kernel/ppc_ksyms.c @@ -145,6 +145,8 @@ EXPORT_SYMBOL(__ashldi3); EXPORT_SYMBOL(__lshrdi3); int __ucmpdi2(unsigned long long, unsigned long long); EXPORT_SYMBOL(__ucmpdi2); +int __cmpdi2(long long, long long); +EXPORT_SYMBOL(__cmpdi2); #endif long long __bswapdi2(long long); EXPORT_SYMBOL(__bswapdi2); -- cgit v0.10.2 From f95dabef4c70e27e5114f4802fe6234ff82ce406 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Wed, 25 Sep 2013 19:24:17 +1000 Subject: hwrng: Return errors to upper levels in pseries-rng.c We don't expect to get errors from the hypervisor when reading the rng, but if we do we should pass the error up to the hwrng driver. Otherwise the hwrng driver will continue calling us forever. Signed-off-by: Michael Ellerman Signed-off-by: Benjamin Herrenschmidt diff --git a/drivers/char/hw_random/pseries-rng.c b/drivers/char/hw_random/pseries-rng.c index 5f11979..b761459a 100644 --- a/drivers/char/hw_random/pseries-rng.c +++ b/drivers/char/hw_random/pseries-rng.c @@ -17,6 +17,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include #include #include #include @@ -25,10 +28,15 @@ static int pseries_rng_data_read(struct hwrng *rng, u32 *data) { - if (plpar_hcall(H_RANDOM, (unsigned long *)data) != H_SUCCESS) { - printk(KERN_ERR "pseries rng hcall error\n"); - return 0; + int rc; + + rc = plpar_hcall(H_RANDOM, (unsigned long *)data); + if (rc != H_SUCCESS) { + pr_err_ratelimited("H_RANDOM call failed %d\n", rc); + return -EIO; } + + /* The hypervisor interface returns 64 bits */ return 8; } -- cgit v0.10.2 From a4da0d50b2a00b79390092e6248ca88b7d93c81d Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 11 Oct 2013 14:07:57 +1100 Subject: powerpc: Implement arch_get_random_long/int() for powernv Add the plumbing to implement arch_get_random_long/int(). It didn't seem worth adding an extra ppc_md hook for int, so we reuse the one for long. Add an implementation for powernv based on the hwrng found in power7+ systems. We whiten the output of the hwrng, and the result passes all the dieharder tests. Signed-off-by: Michael Ellerman Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index c5a868b..875d815 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -1012,6 +1012,9 @@ config PHYSICAL_START default "0x00000000" endif +config ARCH_RANDOM + def_bool n + source "net/Kconfig" source "drivers/Kconfig" diff --git a/arch/powerpc/include/asm/archrandom.h b/arch/powerpc/include/asm/archrandom.h new file mode 100644 index 0000000..d853d16 --- /dev/null +++ b/arch/powerpc/include/asm/archrandom.h @@ -0,0 +1,32 @@ +#ifndef _ASM_POWERPC_ARCHRANDOM_H +#define _ASM_POWERPC_ARCHRANDOM_H + +#ifdef CONFIG_ARCH_RANDOM + +#include + +static inline int arch_get_random_long(unsigned long *v) +{ + if (ppc_md.get_random_long) + return ppc_md.get_random_long(v); + + return 0; +} + +static inline int arch_get_random_int(unsigned int *v) +{ + unsigned long val; + int rc; + + rc = arch_get_random_long(&val); + if (rc) + *v = val; + + return rc; +} + +int powernv_get_random_long(unsigned long *v); + +#endif /* CONFIG_ARCH_RANDOM */ + +#endif /* _ASM_POWERPC_ARCHRANDOM_H */ diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h index 8b48090..ce6cc2a 100644 --- a/arch/powerpc/include/asm/machdep.h +++ b/arch/powerpc/include/asm/machdep.h @@ -263,6 +263,10 @@ struct machdep_calls { ssize_t (*cpu_probe)(const char *, size_t); ssize_t (*cpu_release)(const char *, size_t); #endif + +#ifdef CONFIG_ARCH_RANDOM + int (*get_random_long)(unsigned long *v); +#endif }; extern void e500_idle(void); diff --git a/arch/powerpc/platforms/powernv/Kconfig b/arch/powerpc/platforms/powernv/Kconfig index 6fae5eb..2108464 100644 --- a/arch/powerpc/platforms/powernv/Kconfig +++ b/arch/powerpc/platforms/powernv/Kconfig @@ -9,6 +9,7 @@ config PPC_POWERNV select EPAPR_BOOT select PPC_INDIRECT_PIO select PPC_UDBG_16550 + select ARCH_RANDOM default y config POWERNV_MSI diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile index 300c437..6760a86 100644 --- a/arch/powerpc/platforms/powernv/Makefile +++ b/arch/powerpc/platforms/powernv/Makefile @@ -1,5 +1,5 @@ obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o -obj-y += opal-rtc.o opal-nvram.o opal-lpc.o +obj-y += opal-rtc.o opal-nvram.o opal-lpc.o rng.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o pci-ioda.o diff --git a/arch/powerpc/platforms/powernv/rng.c b/arch/powerpc/platforms/powernv/rng.c new file mode 100644 index 0000000..02db7d7 --- /dev/null +++ b/arch/powerpc/platforms/powernv/rng.c @@ -0,0 +1,122 @@ +/* + * Copyright 2013, Michael Ellerman, IBM Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "powernv-rng: " fmt + +#include +#include +#include +#include +#include +#include +#include + + +struct powernv_rng { + void __iomem *regs; + unsigned long mask; +}; + +static DEFINE_PER_CPU(struct powernv_rng *, powernv_rng); + + +static unsigned long rng_whiten(struct powernv_rng *rng, unsigned long val) +{ + unsigned long parity; + + /* Calculate the parity of the value */ + asm ("popcntd %0,%1" : "=r" (parity) : "r" (val)); + + /* xor our value with the previous mask */ + val ^= rng->mask; + + /* update the mask based on the parity of this value */ + rng->mask = (rng->mask << 1) | (parity & 1); + + return val; +} + +int powernv_get_random_long(unsigned long *v) +{ + struct powernv_rng *rng; + + rng = get_cpu_var(powernv_rng); + + *v = rng_whiten(rng, in_be64(rng->regs)); + + put_cpu_var(rng); + + return 1; +} +EXPORT_SYMBOL_GPL(powernv_get_random_long); + +static __init void rng_init_per_cpu(struct powernv_rng *rng, + struct device_node *dn) +{ + int chip_id, cpu; + + chip_id = of_get_ibm_chip_id(dn); + if (chip_id == -1) + pr_warn("No ibm,chip-id found for %s.\n", dn->full_name); + + for_each_possible_cpu(cpu) { + if (per_cpu(powernv_rng, cpu) == NULL || + cpu_to_chip_id(cpu) == chip_id) { + per_cpu(powernv_rng, cpu) = rng; + } + } +} + +static __init int rng_create(struct device_node *dn) +{ + struct powernv_rng *rng; + unsigned long val; + + rng = kzalloc(sizeof(*rng), GFP_KERNEL); + if (!rng) + return -ENOMEM; + + rng->regs = of_iomap(dn, 0); + if (!rng->regs) { + kfree(rng); + return -ENXIO; + } + + val = in_be64(rng->regs); + rng->mask = val; + + rng_init_per_cpu(rng, dn); + + pr_info_once("Registering arch random hook.\n"); + + ppc_md.get_random_long = powernv_get_random_long; + + return 0; +} + +static __init int rng_init(void) +{ + struct device_node *dn; + int rc; + + for_each_compatible_node(dn, NULL, "ibm,power-rng") { + rc = rng_create(dn); + if (rc) { + pr_err("Failed creating rng for %s (%d).\n", + dn->full_name, rc); + continue; + } + + /* Create devices for hwrng driver */ + of_platform_device_create(dn, NULL, NULL); + } + + return 0; +} +subsys_initcall(rng_init); -- cgit v0.10.2 From 66548e40583b1470300341c6784fdc5176f7609f Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 11 Oct 2013 14:07:58 +1100 Subject: hwrng: Add a driver for the hwrng found in power7+ systems Add a driver for the hwrng found in power7+ systems, based on the existing code for the arch_get_random_long() hook. We only register a single instance of the driver, not one per device, because we use the existing per_cpu array of devices in the arch code. This means we always read from the "closest" device, avoiding inter-chip memory traffic. Signed-off-by: Guo Chao Signed-off-by: Michael Ellerman Signed-off-by: Benjamin Herrenschmidt diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index 0aa9d91..c206de2 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -290,6 +290,19 @@ config HW_RANDOM_PSERIES If unsure, say Y. +config HW_RANDOM_POWERNV + tristate "PowerNV Random Number Generator support" + depends on HW_RANDOM && PPC_POWERNV + default HW_RANDOM + ---help--- + This is the driver for Random Number Generator hardware found + in POWER7+ and above machines for PowerNV platform. + + To compile this driver as a module, choose M here: the + module will be called powernv-rng. + + If unsure, say Y. + config HW_RANDOM_EXYNOS tristate "EXYNOS HW random number generator support" depends on HW_RANDOM && HAS_IOMEM && HAVE_CLK diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index bed467c..d7d2435 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_HW_RANDOM_NOMADIK) += nomadik-rng.o obj-$(CONFIG_HW_RANDOM_PICOXCELL) += picoxcell-rng.o obj-$(CONFIG_HW_RANDOM_PPC4XX) += ppc4xx-rng.o obj-$(CONFIG_HW_RANDOM_PSERIES) += pseries-rng.o +obj-$(CONFIG_HW_RANDOM_POWERNV) += powernv-rng.o obj-$(CONFIG_HW_RANDOM_EXYNOS) += exynos-rng.o obj-$(CONFIG_HW_RANDOM_TPM) += tpm-rng.o obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o diff --git a/drivers/char/hw_random/powernv-rng.c b/drivers/char/hw_random/powernv-rng.c new file mode 100644 index 0000000..3f4f632 --- /dev/null +++ b/drivers/char/hw_random/powernv-rng.c @@ -0,0 +1,81 @@ +/* + * Copyright 2013 Michael Ellerman, Guo Chao, IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +static int powernv_rng_read(struct hwrng *rng, void *data, size_t max, bool wait) +{ + unsigned long *buf; + int i, len; + + /* We rely on rng_buffer_size() being >= sizeof(unsigned long) */ + len = max / sizeof(unsigned long); + + buf = (unsigned long *)data; + + for (i = 0; i < len; i++) + powernv_get_random_long(buf++); + + return len * sizeof(unsigned long); +} + +static struct hwrng powernv_hwrng = { + .name = "powernv-rng", + .read = powernv_rng_read, +}; + +static int powernv_rng_remove(struct platform_device *pdev) +{ + hwrng_unregister(&powernv_hwrng); + + return 0; +} + +static int powernv_rng_probe(struct platform_device *pdev) +{ + int rc; + + rc = hwrng_register(&powernv_hwrng); + if (rc) { + /* We only register one device, ignore any others */ + if (rc == -EEXIST) + rc = -ENODEV; + + return rc; + } + + pr_info("Registered powernv hwrng.\n"); + + return 0; +} + +static struct of_device_id powernv_rng_match[] = { + { .compatible = "ibm,power-rng",}, + {}, +}; +MODULE_DEVICE_TABLE(of, powernv_rng_match); + +static struct platform_driver powernv_rng_driver = { + .driver = { + .name = "powernv_rng", + .of_match_table = powernv_rng_match, + }, + .probe = powernv_rng_probe, + .remove = powernv_rng_remove, +}; +module_platform_driver(powernv_rng_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Bare metal HWRNG driver for POWER7+ and above"); -- cgit v0.10.2 From a489043f462639988f86c1cf49475580e9dba965 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 11 Oct 2013 14:07:59 +1100 Subject: powerpc/pseries: Implement arch_get_random_long() based on H_RANDOM Add support for the arch_get_random_long() hook based on the H_RANDOM hypervisor call. We trust the hypervisor to provide us with random data, ie. we don't whiten it in anyway. Signed-off-by: Michael Ellerman Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile index 6c61ec5..fbccac9 100644 --- a/arch/powerpc/platforms/pseries/Makefile +++ b/arch/powerpc/platforms/pseries/Makefile @@ -3,7 +3,7 @@ ccflags-$(CONFIG_PPC_PSERIES_DEBUG) += -DDEBUG obj-y := lpar.o hvCall.o nvram.o reconfig.o \ setup.o iommu.o event_sources.o ras.o \ - firmware.o power.o dlpar.o mobility.o + firmware.o power.o dlpar.o mobility.o rng.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_SCANLOG) += scanlog.o obj-$(CONFIG_EEH) += eeh_pseries.o diff --git a/arch/powerpc/platforms/pseries/rng.c b/arch/powerpc/platforms/pseries/rng.c new file mode 100644 index 0000000..a702f1c --- /dev/null +++ b/arch/powerpc/platforms/pseries/rng.c @@ -0,0 +1,44 @@ +/* + * Copyright 2013, Michael Ellerman, IBM Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "pseries-rng: " fmt + +#include +#include +#include +#include + + +static int pseries_get_random_long(unsigned long *v) +{ + unsigned long retbuf[PLPAR_HCALL_BUFSIZE]; + + if (plpar_hcall(H_RANDOM, retbuf) == H_SUCCESS) { + *v = retbuf[0]; + return 1; + } + + return 0; +} + +static __init int rng_init(void) +{ + struct device_node *dn; + + dn = of_find_compatible_node(NULL, NULL, "ibm,random"); + if (!dn) + return -ENODEV; + + pr_info("Registering arch random hook.\n"); + + ppc_md.get_random_long = pseries_get_random_long; + + return 0; +} +subsys_initcall(rng_init); -- cgit v0.10.2 From cf059965713b71b7301fd88f1e98b31c6484dd46 Mon Sep 17 00:00:00 2001 From: Cedric Le Goater Date: Mon, 23 Sep 2013 14:17:54 +0200 Subject: powerpc/kernel: Fix endian issue in rtas_pci MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédric Le Goater Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/rtas_pci.c b/arch/powerpc/kernel/rtas_pci.c index 6e7b7cd..7d4c717 100644 --- a/arch/powerpc/kernel/rtas_pci.c +++ b/arch/powerpc/kernel/rtas_pci.c @@ -223,7 +223,7 @@ unsigned long get_phb_buid(struct device_node *phb) static int phb_set_bus_ranges(struct device_node *dev, struct pci_controller *phb) { - const int *bus_range; + const __be32 *bus_range; unsigned int len; bus_range = of_get_property(dev, "bus-range", &len); @@ -231,8 +231,8 @@ static int phb_set_bus_ranges(struct device_node *dev, return 1; } - phb->first_busno = bus_range[0]; - phb->last_busno = bus_range[1]; + phb->first_busno = be32_to_cpu(bus_range[0]); + phb->last_busno = be32_to_cpu(bus_range[1]); return 0; } -- cgit v0.10.2 From e48673360b8b113ca83dc3a45e02ad37fdf9f2d0 Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Thu, 26 Sep 2013 16:23:56 +0800 Subject: powerpc/booke64: Check napping in performance monitor interrupt The performance monitor interrupt is asynchronous, so we should check if the current processor is in napping status in the handler of this interrupt. Signed-off-by: Kevin Hao Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/exceptions-64e.S b/arch/powerpc/kernel/exceptions-64e.S index 2d06704..68d74b4 100644 --- a/arch/powerpc/kernel/exceptions-64e.S +++ b/arch/powerpc/kernel/exceptions-64e.S @@ -607,6 +607,7 @@ kernel_dbg_exc: NORMAL_EXCEPTION_PROLOG(0x260, BOOKE_INTERRUPT_PERFORMANCE_MONITOR, PROLOG_ADDITION_NONE) EXCEPTION_COMMON(0x260, PACA_EXGEN, INTS_DISABLE) + CHECK_NAPPING() addi r3,r1,STACK_FRAME_OVERHEAD bl .performance_monitor_exception b .ret_from_except_lite -- cgit v0.10.2 From dbd0c5d5296f291a5c3affee4fbdde254632ffca Mon Sep 17 00:00:00 2001 From: Laurent Dufour Date: Tue, 17 Sep 2013 11:52:48 +0200 Subject: powerpc: prom_init exception when updating core value Since the CPU is generating an exception when accessing unaligned word, and as this exception is not yet handled when running prom_init, data should be copied from the architecture vector byte per byte. Signed-off-by: Laurent Dufour Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index 5fe2842..cb64a6e 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -858,7 +858,8 @@ static void __init prom_send_capabilities(void) { ihandle root; prom_arg_t ret; - __be32 *cores; + u32 cores; + unsigned char *ptcores; root = call_prom("open", 1, 1, ADDR("/")); if (root != 0) { @@ -868,15 +869,30 @@ static void __init prom_send_capabilities(void) * (we assume this is the same for all cores) and use it to * divide NR_CPUS. */ - cores = (__be32 *)&ibm_architecture_vec[IBM_ARCH_VEC_NRCORES_OFFSET]; - if (be32_to_cpup(cores) != NR_CPUS) { + + /* The core value may start at an odd address. If such a word + * access is made at a cache line boundary, this leads to an + * exception which may not be handled at this time. + * Forcing a per byte access to avoid exception. + */ + ptcores = &ibm_architecture_vec[IBM_ARCH_VEC_NRCORES_OFFSET]; + cores = 0; + cores |= ptcores[0] << 24; + cores |= ptcores[1] << 16; + cores |= ptcores[2] << 8; + cores |= ptcores[3]; + if (cores != NR_CPUS) { prom_printf("WARNING ! " "ibm_architecture_vec structure inconsistent: %lu!\n", - be32_to_cpup(cores)); + cores); } else { - *cores = cpu_to_be32(DIV_ROUND_UP(NR_CPUS, prom_count_smt_threads())); + cores = DIV_ROUND_UP(NR_CPUS, prom_count_smt_threads()); prom_printf("Max number of cores passed to firmware: %lu (NR_CPUS = %lu)\n", - be32_to_cpup(cores), NR_CPUS); + cores, NR_CPUS); + ptcores[0] = (cores >> 24) & 0xff; + ptcores[1] = (cores >> 16) & 0xff; + ptcores[2] = (cores >> 8) & 0xff; + ptcores[3] = cores & 0xff; } /* try calling the ibm,client-architecture-support method */ -- cgit v0.10.2 From fed8393ef9f779e51c3e0c844e549e0bbc8d6dd4 Mon Sep 17 00:00:00 2001 From: Eugene Surovegin Date: Fri, 20 Sep 2013 11:42:20 -0700 Subject: powerpc: Make kernel module helper endian-safe. Signed-off-by: Eugene Surovegin Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c index 6ee59a0..a102f44 100644 --- a/arch/powerpc/kernel/module_64.c +++ b/arch/powerpc/kernel/module_64.c @@ -62,6 +62,16 @@ struct ppc64_stub_entry r2) into the stub. */ static struct ppc64_stub_entry ppc64_stub = { .jump = { +#ifdef __LITTLE_ENDIAN__ + 0x00, 0x00, 0x82, 0x3d, /* addis r12,r2, */ + 0x00, 0x00, 0x8c, 0x39, /* addi r12,r12, */ + /* Save current r2 value in magic place on the stack. */ + 0x28, 0x00, 0x41, 0xf8, /* std r2,40(r1) */ + 0x20, 0x00, 0x6c, 0xe9, /* ld r11,32(r12) */ + 0x28, 0x00, 0x4c, 0xe8, /* ld r2,40(r12) */ + 0xa6, 0x03, 0x69, 0x7d, /* mtctr r11 */ + 0x20, 0x04, 0x80, 0x4e /* bctr */ +#else 0x3d, 0x82, 0x00, 0x00, /* addis r12,r2, */ 0x39, 0x8c, 0x00, 0x00, /* addi r12,r12, */ /* Save current r2 value in magic place on the stack. */ @@ -70,6 +80,7 @@ static struct ppc64_stub_entry ppc64_stub = 0xe8, 0x4c, 0x00, 0x28, /* ld r2,40(r12) */ 0x7d, 0x69, 0x03, 0xa6, /* mtctr r11 */ 0x4e, 0x80, 0x04, 0x20 /* bctr */ +#endif } }; /* Count how many different 24-bit relocations (different symbol, @@ -269,8 +280,13 @@ static inline int create_stub(Elf64_Shdr *sechdrs, *entry = ppc64_stub; +#ifdef __LITTLE_ENDIAN__ + loc1 = (Elf64_Half *)&entry->jump[0]; + loc2 = (Elf64_Half *)&entry->jump[4]; +#else loc1 = (Elf64_Half *)&entry->jump[2]; loc2 = (Elf64_Half *)&entry->jump[6]; +#endif /* Stub uses address relative to r2. */ reladdr = (unsigned long)entry - my_r2(sechdrs, me); -- cgit v0.10.2 From 306474304fdfe3723562a02226c0a44e8078d3a0 Mon Sep 17 00:00:00 2001 From: Eugene Surovegin Date: Fri, 20 Sep 2013 11:42:21 -0700 Subject: powerpc: Make ftrace endian-safe. Signed-off-by: Eugene Surovegin Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/ftrace.c b/arch/powerpc/kernel/ftrace.c index 1fb7856..9b27b29 100644 --- a/arch/powerpc/kernel/ftrace.c +++ b/arch/powerpc/kernel/ftrace.c @@ -174,7 +174,11 @@ __ftrace_make_nop(struct module *mod, pr_devel(" %08x %08x\n", jmp[0], jmp[1]); +#ifdef __LITTLE_ENDIAN__ + ptr = ((unsigned long)jmp[1] << 32) + jmp[0]; +#else ptr = ((unsigned long)jmp[0] << 32) + jmp[1]; +#endif /* This should match what was called */ if (ptr != ppc_function_entry((void *)addr)) { -- cgit v0.10.2 From ac237b65f56c9b80d7774c35ccce15a74d445621 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 29 Aug 2013 16:55:07 +1000 Subject: powerpc: Enable /dev/port when isa_io_special is set isa_io_special is set when the platform provides a "special" implementation of inX/outX via some FW interface for example. Such a platform doesn't need an ISA bridge on PCI, and so /dev/port should be made available even if one isn't present. This makes the LPC bus IOs accessible via /dev/port on PowerNV Power8 Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/io.h b/arch/powerpc/include/asm/io.h index db1f296..575fbf8 100644 --- a/arch/powerpc/include/asm/io.h +++ b/arch/powerpc/include/asm/io.h @@ -21,7 +21,7 @@ extern struct pci_dev *isa_bridge_pcidev; /* * has legacy ISA devices ? */ -#define arch_has_dev_port() (isa_bridge_pcidev != NULL) +#define arch_has_dev_port() (isa_bridge_pcidev != NULL || isa_io_special) #endif #include -- cgit v0.10.2 From aaa63093dd4c393391a3368e1c7305b0cc620571 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 29 Aug 2013 16:55:45 +1000 Subject: powerpc/scom: Change scom_read() and scom_write() to return errors scom_read() now returns the read value via a pointer argument and both functions return an int error code Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/scom.h b/arch/powerpc/include/asm/scom.h index 0cabfd7..07dcdcf 100644 --- a/arch/powerpc/include/asm/scom.h +++ b/arch/powerpc/include/asm/scom.h @@ -54,8 +54,8 @@ struct scom_controller { scom_map_t (*map)(struct device_node *ctrl_dev, u64 reg, u64 count); void (*unmap)(scom_map_t map); - u64 (*read)(scom_map_t map, u32 reg); - void (*write)(scom_map_t map, u32 reg, u64 value); + int (*read)(scom_map_t map, u32 reg, u64 *value); + int (*write)(scom_map_t map, u32 reg, u64 value); }; extern const struct scom_controller *scom_controller; @@ -133,10 +133,18 @@ static inline void scom_unmap(scom_map_t map) * scom_read - Read a SCOM register * @map: Result of scom_map * @reg: Register index within that map + * @value: Updated with the value read + * + * Returns 0 (success) or a negative error code */ -static inline u64 scom_read(scom_map_t map, u32 reg) +static inline int scom_read(scom_map_t map, u32 reg, u64 *value) { - return scom_controller->read(map, reg); + int rc; + + rc = scom_controller->read(map, reg, value); + if (rc) + *value = 0xfffffffffffffffful; + return rc; } /** @@ -144,12 +152,15 @@ static inline u64 scom_read(scom_map_t map, u32 reg) * @map: Result of scom_map * @reg: Register index within that map * @value: Value to write + * + * Returns 0 (success) or a negative error code */ -static inline void scom_write(scom_map_t map, u32 reg, u64 value) +static inline int scom_write(scom_map_t map, u32 reg, u64 value) { - scom_controller->write(map, reg, value); + return scom_controller->write(map, reg, value); } + #endif /* CONFIG_PPC_SCOM */ #endif /* __ASSEMBLY__ */ #endif /* __KERNEL__ */ diff --git a/arch/powerpc/platforms/wsp/scom_smp.c b/arch/powerpc/platforms/wsp/scom_smp.c index b56b70a..268bc89 100644 --- a/arch/powerpc/platforms/wsp/scom_smp.c +++ b/arch/powerpc/platforms/wsp/scom_smp.c @@ -116,7 +116,14 @@ static int a2_scom_ram(scom_map_t scom, int thread, u32 insn, int extmask) scom_write(scom, SCOM_RAMIC, cmd); - while (!((val = scom_read(scom, SCOM_RAMC)) & mask)) { + for (;;) { + if (scom_read(scom, SCOM_RAMC, &val) != 0) { + pr_err("SCOM error on instruction 0x%08x, thread %d\n", + insn, thread); + return -1; + } + if (val & mask) + break; pr_devel("Waiting on RAMC = 0x%llx\n", val); if (++n == 3) { pr_err("RAMC timeout on instruction 0x%08x, thread %d\n", @@ -151,9 +158,7 @@ static int a2_scom_getgpr(scom_map_t scom, int thread, int gpr, int alt, if (rc) return rc; - *out_gpr = scom_read(scom, SCOM_RAMD); - - return 0; + return scom_read(scom, SCOM_RAMD, out_gpr); } static int a2_scom_getspr(scom_map_t scom, int thread, int spr, u64 *out_spr) @@ -353,7 +358,10 @@ int a2_scom_startup_cpu(unsigned int lcpu, int thr_idx, struct device_node *np) pr_devel("Bringing up CPU%d using SCOM...\n", lcpu); - pccr0 = scom_read(scom, SCOM_PCCR0); + if (scom_read(scom, SCOM_PCCR0, &pccr0) != 0) { + printk(KERN_ERR "XSCOM failure readng PCCR0 on CPU%d\n", lcpu); + return -1; + } scom_write(scom, SCOM_PCCR0, pccr0 | SCOM_PCCR0_ENABLE_DEBUG | SCOM_PCCR0_ENABLE_RAM); diff --git a/arch/powerpc/platforms/wsp/scom_wsp.c b/arch/powerpc/platforms/wsp/scom_wsp.c index 4052e22..54172c4 100644 --- a/arch/powerpc/platforms/wsp/scom_wsp.c +++ b/arch/powerpc/platforms/wsp/scom_wsp.c @@ -50,18 +50,22 @@ static void wsp_scom_unmap(scom_map_t map) iounmap((void *)map); } -static u64 wsp_scom_read(scom_map_t map, u32 reg) +static int wsp_scom_read(scom_map_t map, u32 reg, u64 *value) { u64 __iomem *addr = (u64 __iomem *)map; - return in_be64(addr + reg); + *value = in_be64(addr + reg); + + return 0; } -static void wsp_scom_write(scom_map_t map, u32 reg, u64 value) +static int wsp_scom_write(scom_map_t map, u32 reg, u64 value) { u64 __iomem *addr = (u64 __iomem *)map; - return out_be64(addr + reg, value); + out_be64(addr + reg, value); + + return 0; } static const struct scom_controller wsp_scom_controller = { diff --git a/arch/powerpc/platforms/wsp/wsp.c b/arch/powerpc/platforms/wsp/wsp.c index d25cc96..ddb6efe 100644 --- a/arch/powerpc/platforms/wsp/wsp.c +++ b/arch/powerpc/platforms/wsp/wsp.c @@ -89,6 +89,7 @@ void wsp_halt(void) struct device_node *dn; struct device_node *mine; struct device_node *me; + int rc; me = of_get_cpu_node(smp_processor_id(), NULL); mine = scom_find_parent(me); @@ -101,15 +102,15 @@ void wsp_halt(void) /* read-modify-write it so the HW probe does not get * confused */ - val = scom_read(m, 0); - val |= 1; - scom_write(m, 0, val); + rc = scom_read(m, 0, &val); + if (rc == 0) + scom_write(m, 0, val | 1); scom_unmap(m); } m = scom_map(mine, 0, 1); - val = scom_read(m, 0); - val |= 1; - scom_write(m, 0, val); + rc = scom_read(m, 0, &val); + if (rc == 0) + scom_write(m, 0, val | 1); /* should never return */ scom_unmap(m); } diff --git a/arch/powerpc/sysdev/scom.c b/arch/powerpc/sysdev/scom.c index 9193e12..10f1d9e 100644 --- a/arch/powerpc/sysdev/scom.c +++ b/arch/powerpc/sysdev/scom.c @@ -137,8 +137,7 @@ static int scom_val_get(void *data, u64 *val) if (!scom_map_ok(ent->map)) return -EFAULT; - *val = scom_read(ent->map, 0); - return 0; + return scom_read(ent->map, 0, val); } DEFINE_SIMPLE_ATTRIBUTE(scom_val_fops, scom_val_get, scom_val_set, "0x%llx\n"); -- cgit v0.10.2 From 5f33af4c0059255bcbf82a98a3789a01171b72e5 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 29 Aug 2013 16:56:16 +1000 Subject: powerpc/scom: Add support for "reg" property When devices are direct children of a scom controller node, they should be able to use the normal "reg" property instead of "scom-reg". In that case, they also use #address-cells rather than #scom-cells to indicate the size of an entry. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/sysdev/scom.c b/arch/powerpc/sysdev/scom.c index 10f1d9e..413622d 100644 --- a/arch/powerpc/sysdev/scom.c +++ b/arch/powerpc/sysdev/scom.c @@ -53,7 +53,7 @@ scom_map_t scom_map_device(struct device_node *dev, int index) { struct device_node *parent; unsigned int cells, size; - const u32 *prop; + const __be32 *prop, *sprop; u64 reg, cnt; scom_map_t ret; @@ -62,12 +62,24 @@ scom_map_t scom_map_device(struct device_node *dev, int index) if (parent == NULL) return 0; - prop = of_get_property(parent, "#scom-cells", NULL); - cells = prop ? *prop : 1; - + /* + * We support "scom-reg" properties for adding scom registers + * to a random device-tree node with an explicit scom-parent + * + * We also support the simple "reg" property if the device is + * a direct child of a scom controller. + * + * In case both exist, "scom-reg" takes precedence. + */ prop = of_get_property(dev, "scom-reg", &size); + sprop = of_get_property(parent, "#scom-cells", NULL); + if (!prop && parent == dev->parent) { + prop = of_get_property(dev, "reg", &size); + sprop = of_get_property(parent, "#address-cells", NULL); + } if (!prop) - return 0; + return NULL; + cells = sprop ? be32_to_cpup(sprop) : 1; size >>= 2; if (index >= (size / (2*cells))) -- cgit v0.10.2 From 762fd3ab6d2c4b438ce49f54860dc509e591209c Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 29 Aug 2013 16:56:59 +1000 Subject: powerpc/scom: Create debugfs files using ibm,chip-id if available When creating the debugfs scom files, use "ibm,chip-id" as the scom%d index rather than a simple made up number when possible. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/sysdev/scom.c b/arch/powerpc/sysdev/scom.c index 413622d..cb20d54 100644 --- a/arch/powerpc/sysdev/scom.c +++ b/arch/powerpc/sysdev/scom.c @@ -196,8 +196,13 @@ static int scom_debug_init(void) return -1; i = rc = 0; - for_each_node_with_property(dn, "scom-controller") - rc |= scom_debug_init_one(root, dn, i++); + for_each_node_with_property(dn, "scom-controller") { + int id = of_get_ibm_chip_id(dn); + if (id == -1) + id = i; + rc |= scom_debug_init_one(root, dn, id); + i++; + } return rc; } -- cgit v0.10.2 From 8adae0c8d822e54242694cdfed33cc9c346d607a Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 29 Aug 2013 16:57:33 +1000 Subject: powerpc/powernv: Add scom support under OPALv3 OPAL v3 provides interfaces to access the chips XSCOM, expose this via the existing scom infrastructure. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/powernv/Kconfig b/arch/powerpc/platforms/powernv/Kconfig index 2108464..9fced3f 100644 --- a/arch/powerpc/platforms/powernv/Kconfig +++ b/arch/powerpc/platforms/powernv/Kconfig @@ -9,6 +9,7 @@ config PPC_POWERNV select EPAPR_BOOT select PPC_INDIRECT_PIO select PPC_UDBG_16550 + select PPC_SCOM select ARCH_RANDOM default y diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile index 6760a86..050d57e 100644 --- a/arch/powerpc/platforms/powernv/Makefile +++ b/arch/powerpc/platforms/powernv/Makefile @@ -4,3 +4,4 @@ obj-y += opal-rtc.o opal-nvram.o opal-lpc.o rng.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o pci-ioda.o obj-$(CONFIG_EEH) += eeh-ioda.o eeh-powernv.o +obj-$(CONFIG_PPC_SCOM) += opal-xscom.o diff --git a/arch/powerpc/platforms/powernv/opal-xscom.c b/arch/powerpc/platforms/powernv/opal-xscom.c new file mode 100644 index 0000000..3ed5c64 --- /dev/null +++ b/arch/powerpc/platforms/powernv/opal-xscom.c @@ -0,0 +1,105 @@ +/* + * PowerNV LPC bus handling. + * + * Copyright 2013 IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * We could probably fit that inside the scom_map_t + * which is a void* after all but it's really too ugly + * so let's kmalloc it for now + */ +struct opal_scom_map { + uint32_t chip; + uint32_t addr; +}; + +static scom_map_t opal_scom_map(struct device_node *dev, u64 reg, u64 count) +{ + struct opal_scom_map *m; + const __be32 *gcid; + + if (!of_get_property(dev, "scom-controller", NULL)) { + pr_err("%s: device %s is not a SCOM controller\n", + __func__, dev->full_name); + return SCOM_MAP_INVALID; + } + gcid = of_get_property(dev, "ibm,chip-id", NULL); + if (!gcid) { + pr_err("%s: device %s has no ibm,chip-id\n", + __func__, dev->full_name); + return SCOM_MAP_INVALID; + } + m = kmalloc(sizeof(struct opal_scom_map), GFP_KERNEL); + if (!m) + return NULL; + m->chip = be32_to_cpup(gcid); + m->addr = reg; + + return (scom_map_t)m; +} + +static void opal_scom_unmap(scom_map_t map) +{ + kfree(map); +} + +static int opal_xscom_err_xlate(int64_t rc) +{ + switch(rc) { + case 0: + return 0; + /* Add more translations if necessary */ + default: + return -EIO; + } +} + +static int opal_scom_read(scom_map_t map, u32 reg, u64 *value) +{ + struct opal_scom_map *m = map; + int64_t rc; + + rc = opal_xscom_read(m->chip, m->addr + reg, (uint64_t *)__pa(value)); + return opal_xscom_err_xlate(rc); +} + +static int opal_scom_write(scom_map_t map, u32 reg, u64 value) +{ + struct opal_scom_map *m = map; + int64_t rc; + + rc = opal_xscom_write(m->chip, m->addr + reg, value); + return opal_xscom_err_xlate(rc); +} + +static const struct scom_controller opal_scom_controller = { + .map = opal_scom_map, + .unmap = opal_scom_unmap, + .read = opal_scom_read, + .write = opal_scom_write +}; + +static int opal_xscom_init(void) +{ + if (firmware_has_feature(FW_FEATURE_OPALv3)) + scom_init(&opal_scom_controller); + return 0; +} +arch_initcall(opal_xscom_init); -- cgit v0.10.2 From 4777f79689bc722d12b3cfa1e2e92809cbe7bb38 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 29 Aug 2013 16:58:12 +1000 Subject: powerpc/scom: CONFIG_SCOM_DEBUGFS should depend on CONFIG_DEBUG_FS Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/sysdev/Kconfig b/arch/powerpc/sysdev/Kconfig index ab4cb54..13ec968 100644 --- a/arch/powerpc/sysdev/Kconfig +++ b/arch/powerpc/sysdev/Kconfig @@ -28,7 +28,7 @@ config PPC_SCOM config SCOM_DEBUGFS bool "Expose SCOM controllers via debugfs" - depends on PPC_SCOM + depends on PPC_SCOM && DEBUG_FS default n config GE_FPGA -- cgit v0.10.2 From 81fafea67a2ea418673df2df2b22a81f0c5a455a Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 29 Aug 2013 17:25:35 +1000 Subject: powerpc/scom: Use "devspec" rather than "path" in debugfs entries This is the traditional name for device-tree path, used in sysfs, do the same for the XSCOM debugfs files. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/sysdev/scom.c b/arch/powerpc/sysdev/scom.c index cb20d54..3963d99 100644 --- a/arch/powerpc/sysdev/scom.c +++ b/arch/powerpc/sysdev/scom.c @@ -180,7 +180,7 @@ static int scom_debug_init_one(struct dentry *root, struct device_node *dn, debugfs_create_file("addr", 0600, dir, ent, &scom_addr_fops); debugfs_create_file("value", 0600, dir, ent, &scom_val_fops); - debugfs_create_blob("path", 0400, dir, &ent->blob); + debugfs_create_blob("devspec", 0400, dir, &ent->blob); return 0; } -- cgit v0.10.2 From 20bb842b9b0e26a5768b62ee639e64a2ecaada8c Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Fri, 6 Sep 2013 09:00:00 +0800 Subject: powerpc/powernv: Enable EEH for PHB3 The EEH isn't enabled for PHB3 and the patch intends to enable it. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/powernv/eeh-ioda.c b/arch/powerpc/platforms/powernv/eeh-ioda.c index cf42e74..799ccaa 100644 --- a/arch/powerpc/platforms/powernv/eeh-ioda.c +++ b/arch/powerpc/platforms/powernv/eeh-ioda.c @@ -106,27 +106,23 @@ static int ioda_eeh_post_init(struct pci_controller *hose) ioda_eeh_nb_init = 1; } - /* FIXME: Enable it for PHB3 later */ - if (phb->type == PNV_PHB_IODA1) { + /* We needn't HUB diag-data on PHB3 */ + if (phb->type == PNV_PHB_IODA1 && !hub_diag) { + hub_diag = (char *)__get_free_page(GFP_KERNEL | __GFP_ZERO); if (!hub_diag) { - hub_diag = (char *)__get_free_page(GFP_KERNEL | - __GFP_ZERO); - if (!hub_diag) { - pr_err("%s: Out of memory !\n", - __func__); - return -ENOMEM; - } + pr_err("%s: Out of memory !\n", __func__); + return -ENOMEM; } + } #ifdef CONFIG_DEBUG_FS - if (phb->dbgfs) - debugfs_create_file("err_injct", 0600, - phb->dbgfs, hose, - &ioda_eeh_dbgfs_ops); + if (phb->dbgfs) + debugfs_create_file("err_injct", 0600, + phb->dbgfs, hose, + &ioda_eeh_dbgfs_ops); #endif - phb->eeh_state |= PNV_EEH_STATE_ENABLED; - } + phb->eeh_state |= PNV_EEH_STATE_ENABLED; return 0; } diff --git a/arch/powerpc/platforms/powernv/eeh-powernv.c b/arch/powerpc/platforms/powernv/eeh-powernv.c index 79663d2..73b9814 100644 --- a/arch/powerpc/platforms/powernv/eeh-powernv.c +++ b/arch/powerpc/platforms/powernv/eeh-powernv.c @@ -144,11 +144,8 @@ static int powernv_eeh_dev_probe(struct pci_dev *dev, void *flag) /* * Enable EEH explicitly so that we will do EEH check * while accessing I/O stuff - * - * FIXME: Enable that for PHB3 later */ - if (phb->type == PNV_PHB_IODA1) - eeh_subsystem_enabled = 1; + eeh_subsystem_enabled = 1; /* Save memory bars */ eeh_save_bars(edev); -- cgit v0.10.2 From ff6bdcd967f6f5ca870a42a744acc8788faaa1d2 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Fri, 6 Sep 2013 09:00:01 +0800 Subject: powerpc/powernv: Support inbound error injection For now, we only support outbound error injection. Actually, the hardware supports injecting inbound errors as well. The patch enables to inject inbound errors. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/powernv/eeh-ioda.c b/arch/powerpc/platforms/powernv/eeh-ioda.c index 799ccaa..34e058310 100644 --- a/arch/powerpc/platforms/powernv/eeh-ioda.c +++ b/arch/powerpc/platforms/powernv/eeh-ioda.c @@ -59,26 +59,60 @@ static struct notifier_block ioda_eeh_nb = { }; #ifdef CONFIG_DEBUG_FS -static int ioda_eeh_dbgfs_set(void *data, u64 val) +static int ioda_eeh_dbgfs_set(void *data, int offset, u64 val) { struct pci_controller *hose = data; struct pnv_phb *phb = hose->private_data; - out_be64(phb->regs + 0xD10, val); + out_be64(phb->regs + offset, val); return 0; } -static int ioda_eeh_dbgfs_get(void *data, u64 *val) +static int ioda_eeh_dbgfs_get(void *data, int offset, u64 *val) { struct pci_controller *hose = data; struct pnv_phb *phb = hose->private_data; - *val = in_be64(phb->regs + 0xD10); + *val = in_be64(phb->regs + offset); return 0; } -DEFINE_SIMPLE_ATTRIBUTE(ioda_eeh_dbgfs_ops, ioda_eeh_dbgfs_get, - ioda_eeh_dbgfs_set, "0x%llx\n"); +static int ioda_eeh_outb_dbgfs_set(void *data, u64 val) +{ + return ioda_eeh_dbgfs_set(data, 0xD10, val); +} + +static int ioda_eeh_outb_dbgfs_get(void *data, u64 *val) +{ + return ioda_eeh_dbgfs_get(data, 0xD10, val); +} + +static int ioda_eeh_inbA_dbgfs_set(void *data, u64 val) +{ + return ioda_eeh_dbgfs_set(data, 0xD90, val); +} + +static int ioda_eeh_inbA_dbgfs_get(void *data, u64 *val) +{ + return ioda_eeh_dbgfs_get(data, 0xD90, val); +} + +static int ioda_eeh_inbB_dbgfs_set(void *data, u64 val) +{ + return ioda_eeh_dbgfs_set(data, 0xE10, val); +} + +static int ioda_eeh_inbB_dbgfs_get(void *data, u64 *val) +{ + return ioda_eeh_dbgfs_get(data, 0xE10, val); +} + +DEFINE_SIMPLE_ATTRIBUTE(ioda_eeh_outb_dbgfs_ops, ioda_eeh_outb_dbgfs_get, + ioda_eeh_outb_dbgfs_set, "0x%llx\n"); +DEFINE_SIMPLE_ATTRIBUTE(ioda_eeh_inbA_dbgfs_ops, ioda_eeh_inbA_dbgfs_get, + ioda_eeh_inbA_dbgfs_set, "0x%llx\n"); +DEFINE_SIMPLE_ATTRIBUTE(ioda_eeh_inbB_dbgfs_ops, ioda_eeh_inbB_dbgfs_get, + ioda_eeh_inbB_dbgfs_set, "0x%llx\n"); #endif /* CONFIG_DEBUG_FS */ /** @@ -116,10 +150,17 @@ static int ioda_eeh_post_init(struct pci_controller *hose) } #ifdef CONFIG_DEBUG_FS - if (phb->dbgfs) - debugfs_create_file("err_injct", 0600, + if (phb->dbgfs) { + debugfs_create_file("err_injct_outbound", 0600, + phb->dbgfs, hose, + &ioda_eeh_outb_dbgfs_ops); + debugfs_create_file("err_injct_inboundA", 0600, phb->dbgfs, hose, - &ioda_eeh_dbgfs_ops); + &ioda_eeh_inbA_dbgfs_ops); + debugfs_create_file("err_injct_inboundB", 0600, + phb->dbgfs, hose, + &ioda_eeh_inbB_dbgfs_ops); + } #endif phb->eeh_state |= PNV_EEH_STATE_ENABLED; -- cgit v0.10.2 From 98cea5fec48a4af8498a0c5c5fccf64f82fc1ce2 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Fri, 6 Sep 2013 09:00:02 +0800 Subject: powerpc/eeh: Output error number The patch prints the error number while failing to retrieve error log from firmware. It's helpful for debugging. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/powernv/eeh-ioda.c b/arch/powerpc/platforms/powernv/eeh-ioda.c index 34e058310..f426f4c 100644 --- a/arch/powerpc/platforms/powernv/eeh-ioda.c +++ b/arch/powerpc/platforms/powernv/eeh-ioda.c @@ -583,8 +583,8 @@ static int ioda_eeh_get_log(struct eeh_pe *pe, int severity, phb->diag.blob, PNV_PCI_DIAG_BUF_SIZE); if (ret) { spin_unlock_irqrestore(&phb->lock, flags); - pr_warning("%s: Failed to get log for PHB#%x-PE#%x\n", - __func__, hose->global_number, pe->addr); + pr_warning("%s: Can't get log for PHB#%x-PE#%x (%lld)\n", + __func__, hose->global_number, pe->addr, ret); return -EIO; } -- cgit v0.10.2 From 5c9d6d759b3be9f9bdd6f7e5f440d870a4ec675a Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Fri, 6 Sep 2013 09:00:03 +0800 Subject: powerpc/powernv: Double size of log blob Each PHB instance (struct pnv_phb) has its corresponding log blob, which is used to hold the retrieved error log from firmware. The current size of that (4096) isn't enough for PHB3 case and the patch makes that double to 8192. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h index dfe2010..d0bb520 100644 --- a/arch/powerpc/platforms/powernv/pci.h +++ b/arch/powerpc/platforms/powernv/pci.h @@ -17,7 +17,7 @@ enum pnv_phb_model { PNV_PHB_MODEL_PHB3, }; -#define PNV_PCI_DIAG_BUF_SIZE 4096 +#define PNV_PCI_DIAG_BUF_SIZE 8192 #define PNV_IODA_PE_DEV (1 << 0) /* PE has single PCI device */ #define PNV_IODA_PE_BUS (1 << 1) /* PE has primary PCI bus */ #define PNV_IODA_PE_BUS_ALL (1 << 2) /* PE has subordinate buses */ -- cgit v0.10.2 From 8c6852e036daa512376de381a3b61547d90465d4 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Fri, 6 Sep 2013 09:00:04 +0800 Subject: powerpc/eeh: Output PHB3 diag-data The patch adds function ioda_eeh_phb3_phb_diag() to dump PHB3 PHB diag-data. That's called while detecting informative errors or frozen PE on the specific PHB. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h index 51e3b26..4cc33ba 100644 --- a/arch/powerpc/include/asm/opal.h +++ b/arch/powerpc/include/asm/opal.h @@ -460,10 +460,12 @@ enum { enum { OPAL_PHB_ERROR_DATA_TYPE_P7IOC = 1, + OPAL_PHB_ERROR_DATA_TYPE_PHB3 = 2 }; enum { OPAL_P7IOC_NUM_PEST_REGS = 128, + OPAL_PHB3_NUM_PEST_REGS = 256 }; struct OpalIoPhbErrorCommon { @@ -531,6 +533,69 @@ struct OpalIoP7IOCPhbErrorData { uint64_t pestB[OPAL_P7IOC_NUM_PEST_REGS]; }; +struct OpalIoPhb3ErrorData { + struct OpalIoPhbErrorCommon common; + + uint32_t brdgCtl; + + /* PHB3 UTL regs */ + uint32_t portStatusReg; + uint32_t rootCmplxStatus; + uint32_t busAgentStatus; + + /* PHB3 cfg regs */ + uint32_t deviceStatus; + uint32_t slotStatus; + uint32_t linkStatus; + uint32_t devCmdStatus; + uint32_t devSecStatus; + + /* cfg AER regs */ + uint32_t rootErrorStatus; + uint32_t uncorrErrorStatus; + uint32_t corrErrorStatus; + uint32_t tlpHdr1; + uint32_t tlpHdr2; + uint32_t tlpHdr3; + uint32_t tlpHdr4; + uint32_t sourceId; + + uint32_t rsv3; + + /* Record data about the call to allocate a buffer */ + uint64_t errorClass; + uint64_t correlator; + + uint64_t nFir; /* 000 */ + uint64_t nFirMask; /* 003 */ + uint64_t nFirWOF; /* 008 */ + + /* PHB3 MMIO Error Regs */ + uint64_t phbPlssr; /* 120 */ + uint64_t phbCsr; /* 110 */ + uint64_t lemFir; /* C00 */ + uint64_t lemErrorMask; /* C18 */ + uint64_t lemWOF; /* C40 */ + uint64_t phbErrorStatus; /* C80 */ + uint64_t phbFirstErrorStatus; /* C88 */ + uint64_t phbErrorLog0; /* CC0 */ + uint64_t phbErrorLog1; /* CC8 */ + uint64_t mmioErrorStatus; /* D00 */ + uint64_t mmioFirstErrorStatus; /* D08 */ + uint64_t mmioErrorLog0; /* D40 */ + uint64_t mmioErrorLog1; /* D48 */ + uint64_t dma0ErrorStatus; /* D80 */ + uint64_t dma0FirstErrorStatus; /* D88 */ + uint64_t dma0ErrorLog0; /* DC0 */ + uint64_t dma0ErrorLog1; /* DC8 */ + uint64_t dma1ErrorStatus; /* E00 */ + uint64_t dma1FirstErrorStatus; /* E08 */ + uint64_t dma1ErrorLog0; /* E40 */ + uint64_t dma1ErrorLog1; /* E48 */ + uint64_t pestA[OPAL_PHB3_NUM_PEST_REGS]; + uint64_t pestB[OPAL_PHB3_NUM_PEST_REGS]; +}; + typedef struct oppanel_line { const char * line; uint64_t line_len; diff --git a/arch/powerpc/platforms/powernv/eeh-ioda.c b/arch/powerpc/platforms/powernv/eeh-ioda.c index f426f4c..02245ce 100644 --- a/arch/powerpc/platforms/powernv/eeh-ioda.c +++ b/arch/powerpc/platforms/powernv/eeh-ioda.c @@ -747,6 +747,73 @@ static void ioda_eeh_p7ioc_phb_diag(struct pci_controller *hose, } } +static void ioda_eeh_phb3_phb_diag(struct pci_controller *hose, + struct OpalIoPhbErrorCommon *common) +{ + struct OpalIoPhb3ErrorData *data; + int i; + + data = (struct OpalIoPhb3ErrorData*)common; + pr_info("PHB3 PHB#%x Diag-data (Version: %d)\n\n", + hose->global_number, common->version); + + pr_info(" brdgCtl: %08x\n", data->brdgCtl); + + pr_info(" portStatusReg: %08x\n", data->portStatusReg); + pr_info(" rootCmplxStatus: %08x\n", data->rootCmplxStatus); + pr_info(" busAgentStatus: %08x\n", data->busAgentStatus); + + pr_info(" deviceStatus: %08x\n", data->deviceStatus); + pr_info(" slotStatus: %08x\n", data->slotStatus); + pr_info(" linkStatus: %08x\n", data->linkStatus); + pr_info(" devCmdStatus: %08x\n", data->devCmdStatus); + pr_info(" devSecStatus: %08x\n", data->devSecStatus); + + pr_info(" rootErrorStatus: %08x\n", data->rootErrorStatus); + pr_info(" uncorrErrorStatus: %08x\n", data->uncorrErrorStatus); + pr_info(" corrErrorStatus: %08x\n", data->corrErrorStatus); + pr_info(" tlpHdr1: %08x\n", data->tlpHdr1); + pr_info(" tlpHdr2: %08x\n", data->tlpHdr2); + pr_info(" tlpHdr3: %08x\n", data->tlpHdr3); + pr_info(" tlpHdr4: %08x\n", data->tlpHdr4); + pr_info(" sourceId: %08x\n", data->sourceId); + pr_info(" errorClass: %016llx\n", data->errorClass); + pr_info(" correlator: %016llx\n", data->correlator); + pr_info(" nFir: %016llx\n", data->nFir); + pr_info(" nFirMask: %016llx\n", data->nFirMask); + pr_info(" nFirWOF: %016llx\n", data->nFirWOF); + pr_info(" PhbPlssr: %016llx\n", data->phbPlssr); + pr_info(" PhbCsr: %016llx\n", data->phbCsr); + pr_info(" lemFir: %016llx\n", data->lemFir); + pr_info(" lemErrorMask: %016llx\n", data->lemErrorMask); + pr_info(" lemWOF: %016llx\n", data->lemWOF); + pr_info(" phbErrorStatus: %016llx\n", data->phbErrorStatus); + pr_info(" phbFirstErrorStatus: %016llx\n", data->phbFirstErrorStatus); + pr_info(" phbErrorLog0: %016llx\n", data->phbErrorLog0); + pr_info(" phbErrorLog1: %016llx\n", data->phbErrorLog1); + pr_info(" mmioErrorStatus: %016llx\n", data->mmioErrorStatus); + pr_info(" mmioFirstErrorStatus: %016llx\n", data->mmioFirstErrorStatus); + pr_info(" mmioErrorLog0: %016llx\n", data->mmioErrorLog0); + pr_info(" mmioErrorLog1: %016llx\n", data->mmioErrorLog1); + pr_info(" dma0ErrorStatus: %016llx\n", data->dma0ErrorStatus); + pr_info(" dma0FirstErrorStatus: %016llx\n", data->dma0FirstErrorStatus); + pr_info(" dma0ErrorLog0: %016llx\n", data->dma0ErrorLog0); + pr_info(" dma0ErrorLog1: %016llx\n", data->dma0ErrorLog1); + pr_info(" dma1ErrorStatus: %016llx\n", data->dma1ErrorStatus); + pr_info(" dma1FirstErrorStatus: %016llx\n", data->dma1FirstErrorStatus); + pr_info(" dma1ErrorLog0: %016llx\n", data->dma1ErrorLog0); + pr_info(" dma1ErrorLog1: %016llx\n", data->dma1ErrorLog1); + + for (i = 0; i < OPAL_PHB3_NUM_PEST_REGS; i++) { + if ((data->pestA[i] >> 63) == 0 && + (data->pestB[i] >> 63) == 0) + continue; + + pr_info(" PE[%3d] PESTA: %016llx\n", i, data->pestA[i]); + pr_info(" PESTB: %016llx\n", data->pestB[i]); + } +} + static void ioda_eeh_phb_diag(struct pci_controller *hose) { struct pnv_phb *phb = hose->private_data; @@ -765,6 +832,9 @@ static void ioda_eeh_phb_diag(struct pci_controller *hose) case OPAL_PHB_ERROR_DATA_TYPE_P7IOC: ioda_eeh_p7ioc_phb_diag(hose, common); break; + case OPAL_PHB_ERROR_DATA_TYPE_PHB3: + ioda_eeh_phb3_phb_diag(hose, common); + break; default: pr_warning("%s: Unrecognized I/O chip %d\n", __func__, common->ioType); -- cgit v0.10.2 From 5293bf97a27e1be8ac6096aa198ff6a9e3e6837c Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Fri, 6 Sep 2013 09:00:05 +0800 Subject: powerpc/eeh: Reorder output messages We already had some output messages from EEH core. Occasionally, we can see the output messages from EEH core before the stack dump. That's not what we expected. The patch fixes that and shows the stack dump prior to output messages from EEH core. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c index 55593ee..1fb331d 100644 --- a/arch/powerpc/kernel/eeh.c +++ b/arch/powerpc/kernel/eeh.c @@ -327,11 +327,11 @@ static int eeh_phb_check_failure(struct eeh_pe *pe) /* Isolate the PHB and send event */ eeh_pe_state_mark(phb_pe, EEH_PE_ISOLATED); eeh_serialize_unlock(flags); - eeh_send_failure_event(phb_pe); pr_err("EEH: PHB#%x failure detected\n", phb_pe->phb->global_number); dump_stack(); + eeh_send_failure_event(phb_pe); return 1; out: @@ -454,8 +454,6 @@ int eeh_dev_check_failure(struct eeh_dev *edev) eeh_pe_state_mark(pe, EEH_PE_ISOLATED); eeh_serialize_unlock(flags); - eeh_send_failure_event(pe); - /* Most EEH events are due to device driver bugs. Having * a stack trace will help the device-driver authors figure * out what happened. So print that out. @@ -464,6 +462,8 @@ int eeh_dev_check_failure(struct eeh_dev *edev) pe->addr, pe->phb->global_number); dump_stack(); + eeh_send_failure_event(pe); + return 1; dn_unlock: -- cgit v0.10.2 From 81fcfb813fe99c30f77dd3ed9a4e541d14a9ed01 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Wed, 28 Aug 2013 18:37:39 +1000 Subject: hashtable: add hash_for_each_possible_rcu_notrace() This adds hash_for_each_possible_rcu_notrace() which is basically a notrace clone of hash_for_each_possible_rcu() which cannot be used in real mode due to its tracing/debugging capability. Signed-off-by: Alexey Kardashevskiy Signed-off-by: Benjamin Herrenschmidt diff --git a/include/linux/hashtable.h b/include/linux/hashtable.h index a9df51f..519b6e2 100644 --- a/include/linux/hashtable.h +++ b/include/linux/hashtable.h @@ -174,6 +174,21 @@ static inline void hash_del_rcu(struct hlist_node *node) member) /** + * hash_for_each_possible_rcu_notrace - iterate over all possible objects hashing + * to the same bucket in an rcu enabled hashtable in a rcu enabled hashtable + * @name: hashtable to iterate + * @obj: the type * to use as a loop cursor for each entry + * @member: the name of the hlist_node within the struct + * @key: the key of the objects to iterate over + * + * This is the same as hash_for_each_possible_rcu() except that it does + * not do any RCU debugging or tracing. + */ +#define hash_for_each_possible_rcu_notrace(name, obj, member, key) \ + hlist_for_each_entry_rcu_notrace(obj, \ + &name[hash_min(key, HASH_BITS(name))], member) + +/** * hash_for_each_possible_safe - iterate over all possible objects hashing to the * same bucket safe against removals * @name: hashtable to iterate -- cgit v0.10.2 From 8e0861fa3c4edfc2f30dd4cf4d58d3929f7c1b23 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Wed, 28 Aug 2013 18:37:42 +1000 Subject: powerpc: Prepare to support kernel handling of IOMMU map/unmap The current VFIO-on-POWER implementation supports only user mode driven mapping, i.e. QEMU is sending requests to map/unmap pages. However this approach is really slow, so we want to move that to KVM. Since H_PUT_TCE can be extremely performance sensitive (especially with network adapters where each packet needs to be mapped/unmapped) we chose to implement that as a "fast" hypercall directly in "real mode" (processor still in the guest context but MMU off). To be able to do that, we need to provide some facilities to access the struct page count within that real mode environment as things like the sparsemem vmemmap mappings aren't accessible. This adds an API function realmode_pfn_to_page() to get page struct when MMU is off. This adds to MM a new function put_page_unless_one() which drops a page if counter is bigger than 1. It is going to be used when MMU is off (for example, real mode on PPC64) and we want to make sure that page release will not happen in real mode as it may crash the kernel in a horrible way. CONFIG_SPARSEMEM_VMEMMAP and CONFIG_FLATMEM are supported. Cc: linux-mm@kvack.org Cc: Benjamin Herrenschmidt Cc: Andrew Morton Reviewed-by: Paul Mackerras Signed-off-by: Paul Mackerras Signed-off-by: Alexey Kardashevskiy Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/pgtable-ppc64.h b/arch/powerpc/include/asm/pgtable-ppc64.h index 46db094..4a191c4 100644 --- a/arch/powerpc/include/asm/pgtable-ppc64.h +++ b/arch/powerpc/include/asm/pgtable-ppc64.h @@ -394,6 +394,8 @@ static inline void mark_hpte_slot_valid(unsigned char *hpte_slot_array, hpte_slot_array[index] = hidx << 4 | 0x1 << 3; } +struct page *realmode_pfn_to_page(unsigned long pfn); + static inline char *get_hpte_slot_array(pmd_t *pmdp) { /* diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c index 8ed035d..e3734ed 100644 --- a/arch/powerpc/mm/init_64.c +++ b/arch/powerpc/mm/init_64.c @@ -304,5 +304,54 @@ void register_page_bootmem_memmap(unsigned long section_nr, struct page *start_page, unsigned long size) { } -#endif /* CONFIG_SPARSEMEM_VMEMMAP */ +/* + * We do not have access to the sparsemem vmemmap, so we fallback to + * walking the list of sparsemem blocks which we already maintain for + * the sake of crashdump. In the long run, we might want to maintain + * a tree if performance of that linear walk becomes a problem. + * + * realmode_pfn_to_page functions can fail due to: + * 1) As real sparsemem blocks do not lay in RAM continously (they + * are in virtual address space which is not available in the real mode), + * the requested page struct can be split between blocks so get_page/put_page + * may fail. + * 2) When huge pages are used, the get_page/put_page API will fail + * in real mode as the linked addresses in the page struct are virtual + * too. + */ +struct page *realmode_pfn_to_page(unsigned long pfn) +{ + struct vmemmap_backing *vmem_back; + struct page *page; + unsigned long page_size = 1 << mmu_psize_defs[mmu_vmemmap_psize].shift; + unsigned long pg_va = (unsigned long) pfn_to_page(pfn); + + for (vmem_back = vmemmap_list; vmem_back; vmem_back = vmem_back->list) { + if (pg_va < vmem_back->virt_addr) + continue; + + /* Check that page struct is not split between real pages */ + if ((pg_va + sizeof(struct page)) > + (vmem_back->virt_addr + page_size)) + return NULL; + + page = (struct page *) (vmem_back->phys + pg_va - + vmem_back->virt_addr); + return page; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(realmode_pfn_to_page); + +#elif defined(CONFIG_FLATMEM) + +struct page *realmode_pfn_to_page(unsigned long pfn) +{ + struct page *page = pfn_to_page(pfn); + return page; +} +EXPORT_SYMBOL_GPL(realmode_pfn_to_page); + +#endif /* CONFIG_SPARSEMEM_VMEMMAP/CONFIG_FLATMEM */ diff --git a/include/linux/mm.h b/include/linux/mm.h index 8b6e55e..1a0668e 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -297,12 +297,26 @@ static inline int put_page_testzero(struct page *page) /* * Try to grab a ref unless the page has a refcount of zero, return false if * that is the case. + * This can be called when MMU is off so it must not access + * any of the virtual mappings. */ static inline int get_page_unless_zero(struct page *page) { return atomic_inc_not_zero(&page->_count); } +/* + * Try to drop a ref unless the page has a refcount of one, return false if + * that is the case. + * This is to make sure that the refcount won't become zero after this drop. + * This can be called when MMU is off so it must not access + * any of the virtual mappings. + */ +static inline int put_page_unless_one(struct page *page) +{ + return atomic_add_unless(&page->_count, -1, 1); +} + extern int page_is_ram(unsigned long pfn); /* Support for virtually mapped pages */ diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index 6d53675..98ada58 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -329,7 +329,9 @@ static inline void set_page_writeback(struct page *page) * System with lots of page flags available. This allows separate * flags for PageHead() and PageTail() checks of compound pages so that bit * tests can be used in performance sensitive paths. PageCompound is - * generally not used in hot code paths. + * generally not used in hot code paths except arch/powerpc/mm/init_64.c + * and arch/powerpc/kvm/book3s_64_vio_hv.c which use it to detect huge pages + * and avoid handling those in real mode. */ __PAGEFLAG(Head, head) CLEARPAGEFLAG(Head, head) __PAGEFLAG(Tail, tail) -- cgit v0.10.2 From 8e0a1611cb891e72a9affc4a8ee4795c634896a6 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Wed, 28 Aug 2013 18:37:43 +1000 Subject: powerpc: add real mode support for dma operations on powernv The existing TCE machine calls (tce_build and tce_free) only support virtual mode as they call __raw_writeq for TCE invalidation what fails in real mode. This introduces tce_build_rm and tce_free_rm real mode versions which do mostly the same but use "Store Doubleword Caching Inhibited Indexed" instruction for TCE invalidation. This new feature is going to be utilized by real mode support of VFIO. Signed-off-by: Alexey Kardashevskiy Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h index 8b48090..07dd3b1 100644 --- a/arch/powerpc/include/asm/machdep.h +++ b/arch/powerpc/include/asm/machdep.h @@ -78,6 +78,18 @@ struct machdep_calls { long index); void (*tce_flush)(struct iommu_table *tbl); + /* _rm versions are for real mode use only */ + int (*tce_build_rm)(struct iommu_table *tbl, + long index, + long npages, + unsigned long uaddr, + enum dma_data_direction direction, + struct dma_attrs *attrs); + void (*tce_free_rm)(struct iommu_table *tbl, + long index, + long npages); + void (*tce_flush_rm)(struct iommu_table *tbl); + void __iomem * (*ioremap)(phys_addr_t addr, unsigned long size, unsigned long flags, void *caller); void (*iounmap)(volatile void __iomem *token); diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index 74a5a57..307015d 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -70,6 +70,16 @@ define_pe_printk_level(pe_err, KERN_ERR); define_pe_printk_level(pe_warn, KERN_WARNING); define_pe_printk_level(pe_info, KERN_INFO); +/* + * stdcix is only supposed to be used in hypervisor real mode as per + * the architecture spec + */ +static inline void __raw_rm_writeq(u64 val, volatile void __iomem *paddr) +{ + __asm__ __volatile__("stdcix %0,0,%1" + : : "r" (val), "r" (paddr) : "memory"); +} + static int pnv_ioda_alloc_pe(struct pnv_phb *phb) { unsigned long pe; @@ -454,10 +464,13 @@ static void pnv_ioda_setup_bus_dma(struct pnv_ioda_pe *pe, struct pci_bus *bus) } } -static void pnv_pci_ioda1_tce_invalidate(struct iommu_table *tbl, - u64 *startp, u64 *endp) +static void pnv_pci_ioda1_tce_invalidate(struct pnv_ioda_pe *pe, + struct iommu_table *tbl, + u64 *startp, u64 *endp, bool rm) { - u64 __iomem *invalidate = (u64 __iomem *)tbl->it_index; + u64 __iomem *invalidate = rm ? + (u64 __iomem *)pe->tce_inval_reg_phys : + (u64 __iomem *)tbl->it_index; unsigned long start, end, inc; start = __pa(startp); @@ -484,7 +497,10 @@ static void pnv_pci_ioda1_tce_invalidate(struct iommu_table *tbl, mb(); /* Ensure above stores are visible */ while (start <= end) { - __raw_writeq(start, invalidate); + if (rm) + __raw_rm_writeq(start, invalidate); + else + __raw_writeq(start, invalidate); start += inc; } @@ -496,10 +512,12 @@ static void pnv_pci_ioda1_tce_invalidate(struct iommu_table *tbl, static void pnv_pci_ioda2_tce_invalidate(struct pnv_ioda_pe *pe, struct iommu_table *tbl, - u64 *startp, u64 *endp) + u64 *startp, u64 *endp, bool rm) { unsigned long start, end, inc; - u64 __iomem *invalidate = (u64 __iomem *)tbl->it_index; + u64 __iomem *invalidate = rm ? + (u64 __iomem *)pe->tce_inval_reg_phys : + (u64 __iomem *)tbl->it_index; /* We'll invalidate DMA address in PE scope */ start = 0x2ul << 60; @@ -515,22 +533,25 @@ static void pnv_pci_ioda2_tce_invalidate(struct pnv_ioda_pe *pe, mb(); while (start <= end) { - __raw_writeq(start, invalidate); + if (rm) + __raw_rm_writeq(start, invalidate); + else + __raw_writeq(start, invalidate); start += inc; } } void pnv_pci_ioda_tce_invalidate(struct iommu_table *tbl, - u64 *startp, u64 *endp) + u64 *startp, u64 *endp, bool rm) { struct pnv_ioda_pe *pe = container_of(tbl, struct pnv_ioda_pe, tce32_table); struct pnv_phb *phb = pe->phb; if (phb->type == PNV_PHB_IODA1) - pnv_pci_ioda1_tce_invalidate(tbl, startp, endp); + pnv_pci_ioda1_tce_invalidate(pe, tbl, startp, endp, rm); else - pnv_pci_ioda2_tce_invalidate(pe, tbl, startp, endp); + pnv_pci_ioda2_tce_invalidate(pe, tbl, startp, endp, rm); } static void pnv_pci_ioda_setup_dma_pe(struct pnv_phb *phb, @@ -603,7 +624,9 @@ static void pnv_pci_ioda_setup_dma_pe(struct pnv_phb *phb, * bus number, print that out instead. */ tbl->it_busno = 0; - tbl->it_index = (unsigned long)ioremap(be64_to_cpup(swinvp), 8); + pe->tce_inval_reg_phys = be64_to_cpup(swinvp); + tbl->it_index = (unsigned long)ioremap(pe->tce_inval_reg_phys, + 8); tbl->it_type = TCE_PCI_SWINV_CREATE | TCE_PCI_SWINV_FREE | TCE_PCI_SWINV_PAIR; } @@ -681,7 +704,9 @@ static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb, * bus number, print that out instead. */ tbl->it_busno = 0; - tbl->it_index = (unsigned long)ioremap(be64_to_cpup(swinvp), 8); + pe->tce_inval_reg_phys = be64_to_cpup(swinvp); + tbl->it_index = (unsigned long)ioremap(pe->tce_inval_reg_phys, + 8); tbl->it_type = TCE_PCI_SWINV_CREATE | TCE_PCI_SWINV_FREE; } iommu_init_table(tbl, phb->hose->node); diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c index a28d3b5..420abe3 100644 --- a/arch/powerpc/platforms/powernv/pci.c +++ b/arch/powerpc/platforms/powernv/pci.c @@ -401,7 +401,7 @@ struct pci_ops pnv_pci_ops = { static int pnv_tce_build(struct iommu_table *tbl, long index, long npages, unsigned long uaddr, enum dma_data_direction direction, - struct dma_attrs *attrs) + struct dma_attrs *attrs, bool rm) { u64 proto_tce; u64 *tcep, *tces; @@ -423,12 +423,22 @@ static int pnv_tce_build(struct iommu_table *tbl, long index, long npages, * of flags if that becomes the case */ if (tbl->it_type & TCE_PCI_SWINV_CREATE) - pnv_pci_ioda_tce_invalidate(tbl, tces, tcep - 1); + pnv_pci_ioda_tce_invalidate(tbl, tces, tcep - 1, rm); return 0; } -static void pnv_tce_free(struct iommu_table *tbl, long index, long npages) +static int pnv_tce_build_vm(struct iommu_table *tbl, long index, long npages, + unsigned long uaddr, + enum dma_data_direction direction, + struct dma_attrs *attrs) +{ + return pnv_tce_build(tbl, index, npages, uaddr, direction, attrs, + false); +} + +static void pnv_tce_free(struct iommu_table *tbl, long index, long npages, + bool rm) { u64 *tcep, *tces; @@ -438,7 +448,12 @@ static void pnv_tce_free(struct iommu_table *tbl, long index, long npages) *(tcep++) = 0; if (tbl->it_type & TCE_PCI_SWINV_FREE) - pnv_pci_ioda_tce_invalidate(tbl, tces, tcep - 1); + pnv_pci_ioda_tce_invalidate(tbl, tces, tcep - 1, rm); +} + +static void pnv_tce_free_vm(struct iommu_table *tbl, long index, long npages) +{ + pnv_tce_free(tbl, index, npages, false); } static unsigned long pnv_tce_get(struct iommu_table *tbl, long index) @@ -446,6 +461,19 @@ static unsigned long pnv_tce_get(struct iommu_table *tbl, long index) return ((u64 *)tbl->it_base)[index - tbl->it_offset]; } +static int pnv_tce_build_rm(struct iommu_table *tbl, long index, long npages, + unsigned long uaddr, + enum dma_data_direction direction, + struct dma_attrs *attrs) +{ + return pnv_tce_build(tbl, index, npages, uaddr, direction, attrs, true); +} + +static void pnv_tce_free_rm(struct iommu_table *tbl, long index, long npages) +{ + pnv_tce_free(tbl, index, npages, true); +} + void pnv_pci_setup_iommu_table(struct iommu_table *tbl, void *tce_mem, u64 tce_size, u64 dma_offset) @@ -610,8 +638,10 @@ void __init pnv_pci_init(void) /* Configure IOMMU DMA hooks */ ppc_md.pci_dma_dev_setup = pnv_pci_dma_dev_setup; - ppc_md.tce_build = pnv_tce_build; - ppc_md.tce_free = pnv_tce_free; + ppc_md.tce_build = pnv_tce_build_vm; + ppc_md.tce_free = pnv_tce_free_vm; + ppc_md.tce_build_rm = pnv_tce_build_rm; + ppc_md.tce_free_rm = pnv_tce_free_rm; ppc_md.tce_get = pnv_tce_get; ppc_md.pci_probe_mode = pnv_pci_probe_mode; set_pci_dma_ops(&dma_iommu_ops); diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h index d633c64..170dd98 100644 --- a/arch/powerpc/platforms/powernv/pci.h +++ b/arch/powerpc/platforms/powernv/pci.h @@ -52,6 +52,7 @@ struct pnv_ioda_pe { int tce32_seg; int tce32_segcount; struct iommu_table tce32_table; + phys_addr_t tce_inval_reg_phys; /* XXX TODO: Add support for additional 64-bit iommus */ @@ -193,6 +194,6 @@ extern void pnv_pci_init_p5ioc2_hub(struct device_node *np); extern void pnv_pci_init_ioda_hub(struct device_node *np); extern void pnv_pci_init_ioda2_phb(struct device_node *np); extern void pnv_pci_ioda_tce_invalidate(struct iommu_table *tbl, - u64 *startp, u64 *endp); + u64 *startp, u64 *endp, bool rm); #endif /* __POWERNV_PCI_H */ -- cgit v0.10.2 From de79f7b9f6f92ec1bd6f61fa1f20de60728a5b5e Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 10 Sep 2013 20:20:42 +1000 Subject: powerpc: Put FP/VSX and VR state into structures This creates new 'thread_fp_state' and 'thread_vr_state' structures to store FP/VSX state (including FPSCR) and Altivec/VSX state (including VSCR), and uses them in the thread_struct. In the thread_fp_state, the FPRs and VSRs are represented as u64 rather than double, since we rarely perform floating-point computations on the values, and this will enable the structures to be used in KVM code as well. Similarly FPSCR is now a u64 rather than a structure of two 32-bit values. This takes the offsets out of the macros such as SAVE_32FPRS, REST_32FPRS, etc. This enables the same macros to be used for normal and transactional state, enabling us to delete the transactional versions of the macros. This also removes the unused do_load_up_fpu and do_load_up_altivec, which were in fact buggy since they didn't create large enough stack frames to account for the fact that load_up_fpu and load_up_altivec are not designed to be called from C and assume that their caller's stack frame is an interrupt frame. Signed-off-by: Paul Mackerras Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/ppc_asm.h b/arch/powerpc/include/asm/ppc_asm.h index 5995457..140f670 100644 --- a/arch/powerpc/include/asm/ppc_asm.h +++ b/arch/powerpc/include/asm/ppc_asm.h @@ -98,123 +98,40 @@ END_FW_FTR_SECTION_IFSET(FW_FEATURE_SPLPAR) #define REST_8GPRS(n, base) REST_4GPRS(n, base); REST_4GPRS(n+4, base) #define REST_10GPRS(n, base) REST_8GPRS(n, base); REST_2GPRS(n+8, base) -#define SAVE_FPR(n, base) stfd n,THREAD_FPR0+8*TS_FPRWIDTH*(n)(base) +#define SAVE_FPR(n, base) stfd n,8*TS_FPRWIDTH*(n)(base) #define SAVE_2FPRS(n, base) SAVE_FPR(n, base); SAVE_FPR(n+1, base) #define SAVE_4FPRS(n, base) SAVE_2FPRS(n, base); SAVE_2FPRS(n+2, base) #define SAVE_8FPRS(n, base) SAVE_4FPRS(n, base); SAVE_4FPRS(n+4, base) #define SAVE_16FPRS(n, base) SAVE_8FPRS(n, base); SAVE_8FPRS(n+8, base) #define SAVE_32FPRS(n, base) SAVE_16FPRS(n, base); SAVE_16FPRS(n+16, base) -#define REST_FPR(n, base) lfd n,THREAD_FPR0+8*TS_FPRWIDTH*(n)(base) +#define REST_FPR(n, base) lfd n,8*TS_FPRWIDTH*(n)(base) #define REST_2FPRS(n, base) REST_FPR(n, base); REST_FPR(n+1, base) #define REST_4FPRS(n, base) REST_2FPRS(n, base); REST_2FPRS(n+2, base) #define REST_8FPRS(n, base) REST_4FPRS(n, base); REST_4FPRS(n+4, base) #define REST_16FPRS(n, base) REST_8FPRS(n, base); REST_8FPRS(n+8, base) #define REST_32FPRS(n, base) REST_16FPRS(n, base); REST_16FPRS(n+16, base) -#define SAVE_VR(n,b,base) li b,THREAD_VR0+(16*(n)); stvx n,base,b +#define SAVE_VR(n,b,base) li b,16*(n); stvx n,base,b #define SAVE_2VRS(n,b,base) SAVE_VR(n,b,base); SAVE_VR(n+1,b,base) #define SAVE_4VRS(n,b,base) SAVE_2VRS(n,b,base); SAVE_2VRS(n+2,b,base) #define SAVE_8VRS(n,b,base) SAVE_4VRS(n,b,base); SAVE_4VRS(n+4,b,base) #define SAVE_16VRS(n,b,base) SAVE_8VRS(n,b,base); SAVE_8VRS(n+8,b,base) #define SAVE_32VRS(n,b,base) SAVE_16VRS(n,b,base); SAVE_16VRS(n+16,b,base) -#define REST_VR(n,b,base) li b,THREAD_VR0+(16*(n)); lvx n,base,b +#define REST_VR(n,b,base) li b,16*(n); lvx n,base,b #define REST_2VRS(n,b,base) REST_VR(n,b,base); REST_VR(n+1,b,base) #define REST_4VRS(n,b,base) REST_2VRS(n,b,base); REST_2VRS(n+2,b,base) #define REST_8VRS(n,b,base) REST_4VRS(n,b,base); REST_4VRS(n+4,b,base) #define REST_16VRS(n,b,base) REST_8VRS(n,b,base); REST_8VRS(n+8,b,base) #define REST_32VRS(n,b,base) REST_16VRS(n,b,base); REST_16VRS(n+16,b,base) -/* Save/restore FPRs, VRs and VSRs from their checkpointed backups in - * thread_struct: - */ -#define SAVE_FPR_TRANSACT(n, base) stfd n,THREAD_TRANSACT_FPR0+ \ - 8*TS_FPRWIDTH*(n)(base) -#define SAVE_2FPRS_TRANSACT(n, base) SAVE_FPR_TRANSACT(n, base); \ - SAVE_FPR_TRANSACT(n+1, base) -#define SAVE_4FPRS_TRANSACT(n, base) SAVE_2FPRS_TRANSACT(n, base); \ - SAVE_2FPRS_TRANSACT(n+2, base) -#define SAVE_8FPRS_TRANSACT(n, base) SAVE_4FPRS_TRANSACT(n, base); \ - SAVE_4FPRS_TRANSACT(n+4, base) -#define SAVE_16FPRS_TRANSACT(n, base) SAVE_8FPRS_TRANSACT(n, base); \ - SAVE_8FPRS_TRANSACT(n+8, base) -#define SAVE_32FPRS_TRANSACT(n, base) SAVE_16FPRS_TRANSACT(n, base); \ - SAVE_16FPRS_TRANSACT(n+16, base) - -#define REST_FPR_TRANSACT(n, base) lfd n,THREAD_TRANSACT_FPR0+ \ - 8*TS_FPRWIDTH*(n)(base) -#define REST_2FPRS_TRANSACT(n, base) REST_FPR_TRANSACT(n, base); \ - REST_FPR_TRANSACT(n+1, base) -#define REST_4FPRS_TRANSACT(n, base) REST_2FPRS_TRANSACT(n, base); \ - REST_2FPRS_TRANSACT(n+2, base) -#define REST_8FPRS_TRANSACT(n, base) REST_4FPRS_TRANSACT(n, base); \ - REST_4FPRS_TRANSACT(n+4, base) -#define REST_16FPRS_TRANSACT(n, base) REST_8FPRS_TRANSACT(n, base); \ - REST_8FPRS_TRANSACT(n+8, base) -#define REST_32FPRS_TRANSACT(n, base) REST_16FPRS_TRANSACT(n, base); \ - REST_16FPRS_TRANSACT(n+16, base) - - -#define SAVE_VR_TRANSACT(n,b,base) li b,THREAD_TRANSACT_VR0+(16*(n)); \ - stvx n,b,base -#define SAVE_2VRS_TRANSACT(n,b,base) SAVE_VR_TRANSACT(n,b,base); \ - SAVE_VR_TRANSACT(n+1,b,base) -#define SAVE_4VRS_TRANSACT(n,b,base) SAVE_2VRS_TRANSACT(n,b,base); \ - SAVE_2VRS_TRANSACT(n+2,b,base) -#define SAVE_8VRS_TRANSACT(n,b,base) SAVE_4VRS_TRANSACT(n,b,base); \ - SAVE_4VRS_TRANSACT(n+4,b,base) -#define SAVE_16VRS_TRANSACT(n,b,base) SAVE_8VRS_TRANSACT(n,b,base); \ - SAVE_8VRS_TRANSACT(n+8,b,base) -#define SAVE_32VRS_TRANSACT(n,b,base) SAVE_16VRS_TRANSACT(n,b,base); \ - SAVE_16VRS_TRANSACT(n+16,b,base) - -#define REST_VR_TRANSACT(n,b,base) li b,THREAD_TRANSACT_VR0+(16*(n)); \ - lvx n,b,base -#define REST_2VRS_TRANSACT(n,b,base) REST_VR_TRANSACT(n,b,base); \ - REST_VR_TRANSACT(n+1,b,base) -#define REST_4VRS_TRANSACT(n,b,base) REST_2VRS_TRANSACT(n,b,base); \ - REST_2VRS_TRANSACT(n+2,b,base) -#define REST_8VRS_TRANSACT(n,b,base) REST_4VRS_TRANSACT(n,b,base); \ - REST_4VRS_TRANSACT(n+4,b,base) -#define REST_16VRS_TRANSACT(n,b,base) REST_8VRS_TRANSACT(n,b,base); \ - REST_8VRS_TRANSACT(n+8,b,base) -#define REST_32VRS_TRANSACT(n,b,base) REST_16VRS_TRANSACT(n,b,base); \ - REST_16VRS_TRANSACT(n+16,b,base) - - -#define SAVE_VSR_TRANSACT(n,b,base) li b,THREAD_TRANSACT_VSR0+(16*(n)); \ - STXVD2X(n,R##base,R##b) -#define SAVE_2VSRS_TRANSACT(n,b,base) SAVE_VSR_TRANSACT(n,b,base); \ - SAVE_VSR_TRANSACT(n+1,b,base) -#define SAVE_4VSRS_TRANSACT(n,b,base) SAVE_2VSRS_TRANSACT(n,b,base); \ - SAVE_2VSRS_TRANSACT(n+2,b,base) -#define SAVE_8VSRS_TRANSACT(n,b,base) SAVE_4VSRS_TRANSACT(n,b,base); \ - SAVE_4VSRS_TRANSACT(n+4,b,base) -#define SAVE_16VSRS_TRANSACT(n,b,base) SAVE_8VSRS_TRANSACT(n,b,base); \ - SAVE_8VSRS_TRANSACT(n+8,b,base) -#define SAVE_32VSRS_TRANSACT(n,b,base) SAVE_16VSRS_TRANSACT(n,b,base); \ - SAVE_16VSRS_TRANSACT(n+16,b,base) - -#define REST_VSR_TRANSACT(n,b,base) li b,THREAD_TRANSACT_VSR0+(16*(n)); \ - LXVD2X(n,R##base,R##b) -#define REST_2VSRS_TRANSACT(n,b,base) REST_VSR_TRANSACT(n,b,base); \ - REST_VSR_TRANSACT(n+1,b,base) -#define REST_4VSRS_TRANSACT(n,b,base) REST_2VSRS_TRANSACT(n,b,base); \ - REST_2VSRS_TRANSACT(n+2,b,base) -#define REST_8VSRS_TRANSACT(n,b,base) REST_4VSRS_TRANSACT(n,b,base); \ - REST_4VSRS_TRANSACT(n+4,b,base) -#define REST_16VSRS_TRANSACT(n,b,base) REST_8VSRS_TRANSACT(n,b,base); \ - REST_8VSRS_TRANSACT(n+8,b,base) -#define REST_32VSRS_TRANSACT(n,b,base) REST_16VSRS_TRANSACT(n,b,base); \ - REST_16VSRS_TRANSACT(n+16,b,base) - /* Save the lower 32 VSRs in the thread VSR region */ -#define SAVE_VSR(n,b,base) li b,THREAD_VSR0+(16*(n)); STXVD2X(n,R##base,R##b) +#define SAVE_VSR(n,b,base) li b,16*(n); STXVD2X(n,R##base,R##b) #define SAVE_2VSRS(n,b,base) SAVE_VSR(n,b,base); SAVE_VSR(n+1,b,base) #define SAVE_4VSRS(n,b,base) SAVE_2VSRS(n,b,base); SAVE_2VSRS(n+2,b,base) #define SAVE_8VSRS(n,b,base) SAVE_4VSRS(n,b,base); SAVE_4VSRS(n+4,b,base) #define SAVE_16VSRS(n,b,base) SAVE_8VSRS(n,b,base); SAVE_8VSRS(n+8,b,base) #define SAVE_32VSRS(n,b,base) SAVE_16VSRS(n,b,base); SAVE_16VSRS(n+16,b,base) -#define REST_VSR(n,b,base) li b,THREAD_VSR0+(16*(n)); LXVD2X(n,R##base,R##b) +#define REST_VSR(n,b,base) li b,16*(n); LXVD2X(n,R##base,R##b) #define REST_2VSRS(n,b,base) REST_VSR(n,b,base); REST_VSR(n+1,b,base) #define REST_4VSRS(n,b,base) REST_2VSRS(n,b,base); REST_2VSRS(n+2,b,base) #define REST_8VSRS(n,b,base) REST_4VSRS(n,b,base); REST_4VSRS(n+4,b,base) diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h index ce4de5a..afe695e 100644 --- a/arch/powerpc/include/asm/processor.h +++ b/arch/powerpc/include/asm/processor.h @@ -144,8 +144,20 @@ typedef struct { #define TS_FPROFFSET 0 #define TS_VSRLOWOFFSET 1 -#define TS_FPR(i) fpr[i][TS_FPROFFSET] -#define TS_TRANS_FPR(i) transact_fpr[i][TS_FPROFFSET] +#define TS_FPR(i) fp_state.fpr[i][TS_FPROFFSET] +#define TS_TRANS_FPR(i) transact_fp.fpr[i][TS_FPROFFSET] + +/* FP and VSX 0-31 register set */ +struct thread_fp_state { + u64 fpr[32][TS_FPRWIDTH] __attribute__((aligned(16))); + u64 fpscr; /* Floating point status */ +}; + +/* Complete AltiVec register set including VSCR */ +struct thread_vr_state { + vector128 vr[32] __attribute__((aligned(16))); + vector128 vscr __attribute__((aligned(16))); +}; struct thread_struct { unsigned long ksp; /* Kernel stack pointer */ @@ -198,13 +210,7 @@ struct thread_struct { unsigned long dvc2; #endif #endif - /* FP and VSX 0-31 register set */ - double fpr[32][TS_FPRWIDTH] __attribute__((aligned(16))); - struct { - - unsigned int pad; - unsigned int val; /* Floating point status */ - } fpscr; + struct thread_fp_state fp_state; int fpexc_mode; /* floating-point exception mode */ unsigned int align_ctl; /* alignment handling control */ #ifdef CONFIG_PPC64 @@ -222,10 +228,7 @@ struct thread_struct { struct arch_hw_breakpoint hw_brk; /* info on the hardware breakpoint */ unsigned long trap_nr; /* last trap # on this thread */ #ifdef CONFIG_ALTIVEC - /* Complete AltiVec register set */ - vector128 vr[32] __attribute__((aligned(16))); - /* AltiVec status */ - vector128 vscr __attribute__((aligned(16))); + struct thread_vr_state vr_state; unsigned long vrsave; int used_vr; /* set if process has used altivec */ #endif /* CONFIG_ALTIVEC */ @@ -262,13 +265,8 @@ struct thread_struct { * transact_fpr[] is the new set of transactional values. * VRs work the same way. */ - double transact_fpr[32][TS_FPRWIDTH]; - struct { - unsigned int pad; - unsigned int val; /* Floating point status */ - } transact_fpscr; - vector128 transact_vr[32] __attribute__((aligned(16))); - vector128 transact_vscr __attribute__((aligned(16))); + struct thread_fp_state transact_fp; + struct thread_vr_state transact_vr; unsigned long transact_vrsave; #endif /* CONFIG_PPC_TRANSACTIONAL_MEM */ #ifdef CONFIG_KVM_BOOK3S_32_HANDLER @@ -322,8 +320,6 @@ struct thread_struct { .ksp = INIT_SP, \ .regs = (struct pt_regs *)INIT_SP - 1, /* XXX bogus, I think */ \ .fs = KERNEL_DS, \ - .fpr = {{0}}, \ - .fpscr = { .val = 0, }, \ .fpexc_mode = 0, \ .ppr = INIT_PPR, \ } diff --git a/arch/powerpc/include/asm/sfp-machine.h b/arch/powerpc/include/asm/sfp-machine.h index 3a7a67a..d89beab 100644 --- a/arch/powerpc/include/asm/sfp-machine.h +++ b/arch/powerpc/include/asm/sfp-machine.h @@ -125,7 +125,7 @@ #define FP_EX_DIVZERO (1 << (31 - 5)) #define FP_EX_INEXACT (1 << (31 - 6)) -#define __FPU_FPSCR (current->thread.fpscr.val) +#define __FPU_FPSCR (current->thread.fp_state.fpscr) /* We only actually write to the destination register * if exceptions signalled (if any) will not trap. diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c index a27ccd5..eaa16bc 100644 --- a/arch/powerpc/kernel/align.c +++ b/arch/powerpc/kernel/align.c @@ -660,7 +660,7 @@ static int emulate_vsx(unsigned char __user *addr, unsigned int reg, if (reg < 32) ptr = (char *) ¤t->thread.TS_FPR(reg); else - ptr = (char *) ¤t->thread.vr[reg - 32]; + ptr = (char *) ¤t->thread.vr_state.vr[reg - 32]; lptr = (unsigned long *) ptr; @@ -897,7 +897,7 @@ int fix_alignment(struct pt_regs *regs) return -EFAULT; } } else if (flags & F) { - data.dd = current->thread.TS_FPR(reg); + data.ll = current->thread.TS_FPR(reg); if (flags & S) { /* Single-precision FP store requires conversion... */ #ifdef CONFIG_PPC_FPU @@ -975,7 +975,7 @@ int fix_alignment(struct pt_regs *regs) if (unlikely(ret)) return -EFAULT; } else if (flags & F) - current->thread.TS_FPR(reg) = data.dd; + current->thread.TS_FPR(reg) = data.ll; else regs->gpr[reg] = data.ll; diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 502c7a4..8d27b61 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -90,16 +90,15 @@ int main(void) DEFINE(THREAD_NORMSAVES, offsetof(struct thread_struct, normsave[0])); #endif DEFINE(THREAD_FPEXC_MODE, offsetof(struct thread_struct, fpexc_mode)); - DEFINE(THREAD_FPR0, offsetof(struct thread_struct, fpr[0])); - DEFINE(THREAD_FPSCR, offsetof(struct thread_struct, fpscr)); + DEFINE(THREAD_FPSTATE, offsetof(struct thread_struct, fp_state)); + DEFINE(FPSTATE_FPSCR, offsetof(struct thread_fp_state, fpscr)); #ifdef CONFIG_ALTIVEC - DEFINE(THREAD_VR0, offsetof(struct thread_struct, vr[0])); + DEFINE(THREAD_VRSTATE, offsetof(struct thread_struct, vr_state)); DEFINE(THREAD_VRSAVE, offsetof(struct thread_struct, vrsave)); - DEFINE(THREAD_VSCR, offsetof(struct thread_struct, vscr)); DEFINE(THREAD_USED_VR, offsetof(struct thread_struct, used_vr)); + DEFINE(VRSTATE_VSCR, offsetof(struct thread_vr_state, vscr)); #endif /* CONFIG_ALTIVEC */ #ifdef CONFIG_VSX - DEFINE(THREAD_VSR0, offsetof(struct thread_struct, fpr)); DEFINE(THREAD_USED_VSR, offsetof(struct thread_struct, used_vsr)); #endif /* CONFIG_VSX */ #ifdef CONFIG_PPC64 @@ -143,20 +142,12 @@ int main(void) DEFINE(THREAD_TM_PPR, offsetof(struct thread_struct, tm_ppr)); DEFINE(THREAD_TM_DSCR, offsetof(struct thread_struct, tm_dscr)); DEFINE(PT_CKPT_REGS, offsetof(struct thread_struct, ckpt_regs)); - DEFINE(THREAD_TRANSACT_VR0, offsetof(struct thread_struct, - transact_vr[0])); - DEFINE(THREAD_TRANSACT_VSCR, offsetof(struct thread_struct, - transact_vscr)); + DEFINE(THREAD_TRANSACT_VRSTATE, offsetof(struct thread_struct, + transact_vr)); DEFINE(THREAD_TRANSACT_VRSAVE, offsetof(struct thread_struct, transact_vrsave)); - DEFINE(THREAD_TRANSACT_FPR0, offsetof(struct thread_struct, - transact_fpr[0])); - DEFINE(THREAD_TRANSACT_FPSCR, offsetof(struct thread_struct, - transact_fpscr)); -#ifdef CONFIG_VSX - DEFINE(THREAD_TRANSACT_VSR0, offsetof(struct thread_struct, - transact_fpr[0])); -#endif + DEFINE(THREAD_TRANSACT_FPSTATE, offsetof(struct thread_struct, + transact_fp)); /* Local pt_regs on stack for Transactional Memory funcs. */ DEFINE(TM_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct pt_regs) + 16); diff --git a/arch/powerpc/kernel/fpu.S b/arch/powerpc/kernel/fpu.S index caeaabf..34b96e6 100644 --- a/arch/powerpc/kernel/fpu.S +++ b/arch/powerpc/kernel/fpu.S @@ -35,15 +35,6 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX); \ 2: REST_32VSRS(n,c,base); \ 3: -#define __REST_32FPVSRS_TRANSACT(n,c,base) \ -BEGIN_FTR_SECTION \ - b 2f; \ -END_FTR_SECTION_IFSET(CPU_FTR_VSX); \ - REST_32FPRS_TRANSACT(n,base); \ - b 3f; \ -2: REST_32VSRS_TRANSACT(n,c,base); \ -3: - #define __SAVE_32FPVSRS(n,c,base) \ BEGIN_FTR_SECTION \ b 2f; \ @@ -54,40 +45,12 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX); \ 3: #else #define __REST_32FPVSRS(n,b,base) REST_32FPRS(n, base) -#define __REST_32FPVSRS_TRANSACT(n,b,base) REST_32FPRS(n, base) #define __SAVE_32FPVSRS(n,b,base) SAVE_32FPRS(n, base) #endif #define REST_32FPVSRS(n,c,base) __REST_32FPVSRS(n,__REG_##c,__REG_##base) -#define REST_32FPVSRS_TRANSACT(n,c,base) \ - __REST_32FPVSRS_TRANSACT(n,__REG_##c,__REG_##base) #define SAVE_32FPVSRS(n,c,base) __SAVE_32FPVSRS(n,__REG_##c,__REG_##base) #ifdef CONFIG_PPC_TRANSACTIONAL_MEM -/* - * Wrapper to call load_up_fpu from C. - * void do_load_up_fpu(struct pt_regs *regs); - */ -_GLOBAL(do_load_up_fpu) - mflr r0 - std r0, 16(r1) - stdu r1, -112(r1) - - subi r6, r3, STACK_FRAME_OVERHEAD - /* load_up_fpu expects r12=MSR, r13=PACA, and returns - * with r12 = new MSR. - */ - ld r12,_MSR(r6) - GET_PACA(r13) - - bl load_up_fpu - std r12,_MSR(r6) - - ld r0, 112+16(r1) - addi r1, r1, 112 - mtlr r0 - blr - - /* void do_load_up_transact_fpu(struct thread_struct *thread) * * This is similar to load_up_fpu but for the transactional version of the FP @@ -105,9 +68,10 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX) SYNC MTMSRD(r5) - lfd fr0,THREAD_TRANSACT_FPSCR(r3) + addi r7,r3,THREAD_TRANSACT_FPSTATE + lfd fr0,FPSTATE_FPSCR(r7) MTFSF_L(fr0) - REST_32FPVSRS_TRANSACT(0, R4, R3) + REST_32FPVSRS(0, R4, R7) /* FP/VSX off again */ MTMSRD(r6) @@ -147,9 +111,10 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX) beq 1f toreal(r4) addi r4,r4,THREAD /* want last_task_used_math->thread */ - SAVE_32FPVSRS(0, R5, R4) + addi r8,r4,THREAD_FPSTATE + SAVE_32FPVSRS(0, R5, R8) mffs fr0 - stfd fr0,THREAD_FPSCR(r4) + stfd fr0,FPSTATE_FPSCR(r8) PPC_LL r5,PT_REGS(r4) toreal(r5) PPC_LL r4,_MSR-STACK_FRAME_OVERHEAD(r5) @@ -160,7 +125,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX) #endif /* CONFIG_SMP */ /* enable use of FP after return */ #ifdef CONFIG_PPC32 - mfspr r5,SPRN_SPRG_THREAD /* current task's THREAD (phys) */ + mfspr r5,SPRN_SPRG_THREAD /* current task's THREAD (phys) */ lwz r4,THREAD_FPEXC_MODE(r5) ori r9,r9,MSR_FP /* enable FP for current */ or r9,r9,r4 @@ -172,9 +137,10 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX) or r12,r12,r4 std r12,_MSR(r1) #endif - lfd fr0,THREAD_FPSCR(r5) + addi r7,r5,THREAD_FPSTATE + lfd fr0,FPSTATE_FPSCR(r7) MTFSF_L(fr0) - REST_32FPVSRS(0, R4, R5) + REST_32FPVSRS(0, R4, R7) #ifndef CONFIG_SMP subi r4,r5,THREAD fromreal(r4) @@ -208,9 +174,10 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX) addi r3,r3,THREAD /* want THREAD of task */ PPC_LL r5,PT_REGS(r3) PPC_LCMPI 0,r5,0 - SAVE_32FPVSRS(0, R4 ,R3) + addi r6,r3,THREAD_FPSTATE + SAVE_32FPVSRS(0, R4, R6) mffs fr0 - stfd fr0,THREAD_FPSCR(r3) + stfd fr0,FPSTATE_FPSCR(r6) beq 1f PPC_LL r4,_MSR-STACK_FRAME_OVERHEAD(r5) li r3,MSR_FP|MSR_FE0|MSR_FE1 diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 96d2fdf..7a28141 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -1113,12 +1113,10 @@ void start_thread(struct pt_regs *regs, unsigned long start, unsigned long sp) #ifdef CONFIG_VSX current->thread.used_vsr = 0; #endif - memset(current->thread.fpr, 0, sizeof(current->thread.fpr)); - current->thread.fpscr.val = 0; + memset(¤t->thread.fp_state, 0, sizeof(current->thread.fp_state)); #ifdef CONFIG_ALTIVEC - memset(current->thread.vr, 0, sizeof(current->thread.vr)); - memset(¤t->thread.vscr, 0, sizeof(current->thread.vscr)); - current->thread.vscr.u[3] = 0x00010000; /* Java mode disabled */ + memset(¤t->thread.vr_state, 0, sizeof(current->thread.vr_state)); + current->thread.vr_state.vscr.u[3] = 0x00010000; /* Java mode disabled */ current->thread.vrsave = 0; current->thread.used_vr = 0; #endif /* CONFIG_ALTIVEC */ diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index 9a0d24c..2385800 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c @@ -362,7 +362,7 @@ static int fpr_get(struct task_struct *target, const struct user_regset *regset, void *kbuf, void __user *ubuf) { #ifdef CONFIG_VSX - double buf[33]; + u64 buf[33]; int i; #endif flush_fp_to_thread(target); @@ -371,15 +371,15 @@ static int fpr_get(struct task_struct *target, const struct user_regset *regset, /* copy to local buffer then write that out */ for (i = 0; i < 32 ; i++) buf[i] = target->thread.TS_FPR(i); - memcpy(&buf[32], &target->thread.fpscr, sizeof(double)); + buf[32] = target->thread.fp_state.fpscr; return user_regset_copyout(&pos, &count, &kbuf, &ubuf, buf, 0, -1); #else - BUILD_BUG_ON(offsetof(struct thread_struct, fpscr) != - offsetof(struct thread_struct, TS_FPR(32))); + BUILD_BUG_ON(offsetof(struct thread_fp_state, fpscr) != + offsetof(struct thread_fp_state, fpr[32][0])); return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.fpr, 0, -1); + &target->thread.fp_state, 0, -1); #endif } @@ -388,7 +388,7 @@ static int fpr_set(struct task_struct *target, const struct user_regset *regset, const void *kbuf, const void __user *ubuf) { #ifdef CONFIG_VSX - double buf[33]; + u64 buf[33]; int i; #endif flush_fp_to_thread(target); @@ -400,14 +400,14 @@ static int fpr_set(struct task_struct *target, const struct user_regset *regset, return i; for (i = 0; i < 32 ; i++) target->thread.TS_FPR(i) = buf[i]; - memcpy(&target->thread.fpscr, &buf[32], sizeof(double)); + target->thread.fp_state.fpscr = buf[32]; return 0; #else - BUILD_BUG_ON(offsetof(struct thread_struct, fpscr) != - offsetof(struct thread_struct, TS_FPR(32))); + BUILD_BUG_ON(offsetof(struct thread_fp_state, fpscr) != + offsetof(struct thread_fp_state, fpr[32][0])); return user_regset_copyin(&pos, &count, &kbuf, &ubuf, - &target->thread.fpr, 0, -1); + &target->thread.fp_state, 0, -1); #endif } @@ -440,11 +440,11 @@ static int vr_get(struct task_struct *target, const struct user_regset *regset, flush_altivec_to_thread(target); - BUILD_BUG_ON(offsetof(struct thread_struct, vscr) != - offsetof(struct thread_struct, vr[32])); + BUILD_BUG_ON(offsetof(struct thread_vr_state, vscr) != + offsetof(struct thread_vr_state, vr[32])); ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.vr, 0, + &target->thread.vr_state, 0, 33 * sizeof(vector128)); if (!ret) { /* @@ -471,11 +471,12 @@ static int vr_set(struct task_struct *target, const struct user_regset *regset, flush_altivec_to_thread(target); - BUILD_BUG_ON(offsetof(struct thread_struct, vscr) != - offsetof(struct thread_struct, vr[32])); + BUILD_BUG_ON(offsetof(struct thread_vr_state, vscr) != + offsetof(struct thread_vr_state, vr[32])); ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, - &target->thread.vr, 0, 33 * sizeof(vector128)); + &target->thread.vr_state, 0, + 33 * sizeof(vector128)); if (!ret && count > 0) { /* * We use only the first word of vrsave. @@ -514,13 +515,13 @@ static int vsr_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { - double buf[32]; + u64 buf[32]; int ret, i; flush_vsx_to_thread(target); for (i = 0; i < 32 ; i++) - buf[i] = target->thread.fpr[i][TS_VSRLOWOFFSET]; + buf[i] = target->thread.fp_state.fpr[i][TS_VSRLOWOFFSET]; ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, buf, 0, 32 * sizeof(double)); @@ -531,7 +532,7 @@ static int vsr_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf) { - double buf[32]; + u64 buf[32]; int ret,i; flush_vsx_to_thread(target); @@ -539,7 +540,7 @@ static int vsr_set(struct task_struct *target, const struct user_regset *regset, ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, buf, 0, 32 * sizeof(double)); for (i = 0; i < 32 ; i++) - target->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i]; + target->thread.fp_state.fpr[i][TS_VSRLOWOFFSET] = buf[i]; return ret; @@ -1554,10 +1555,10 @@ long arch_ptrace(struct task_struct *child, long request, flush_fp_to_thread(child); if (fpidx < (PT_FPSCR - PT_FPR0)) - tmp = ((unsigned long *)child->thread.fpr) + tmp = ((unsigned long *)child->thread.fp_state.fpr) [fpidx * TS_FPRWIDTH]; else - tmp = child->thread.fpscr.val; + tmp = child->thread.fp_state.fpscr; } ret = put_user(tmp, datalp); break; @@ -1587,10 +1588,10 @@ long arch_ptrace(struct task_struct *child, long request, flush_fp_to_thread(child); if (fpidx < (PT_FPSCR - PT_FPR0)) - ((unsigned long *)child->thread.fpr) + ((unsigned long *)child->thread.fp_state.fpr) [fpidx * TS_FPRWIDTH] = data; else - child->thread.fpscr.val = data; + child->thread.fp_state.fpscr = data; ret = 0; } break; diff --git a/arch/powerpc/kernel/ptrace32.c b/arch/powerpc/kernel/ptrace32.c index f51599e..097f8dc 100644 --- a/arch/powerpc/kernel/ptrace32.c +++ b/arch/powerpc/kernel/ptrace32.c @@ -43,7 +43,6 @@ #define FPRNUMBER(i) (((i) - PT_FPR0) >> 1) #define FPRHALF(i) (((i) - PT_FPR0) & 1) #define FPRINDEX(i) TS_FPRWIDTH * FPRNUMBER(i) * 2 + FPRHALF(i) -#define FPRINDEX_3264(i) (TS_FPRWIDTH * ((i) - PT_FPR0)) long compat_arch_ptrace(struct task_struct *child, compat_long_t request, compat_ulong_t caddr, compat_ulong_t cdata) @@ -105,7 +104,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, * to be an array of unsigned int (32 bits) - the * index passed in is based on this assumption. */ - tmp = ((unsigned int *)child->thread.fpr) + tmp = ((unsigned int *)child->thread.fp_state.fpr) [FPRINDEX(index)]; } ret = put_user((unsigned int)tmp, (u32 __user *)data); @@ -147,8 +146,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, if (numReg >= PT_FPR0) { flush_fp_to_thread(child); /* get 64 bit FPR */ - tmp = ((u64 *)child->thread.fpr) - [FPRINDEX_3264(numReg)]; + tmp = child->thread.fp_state.fpr[numReg - PT_FPR0][0]; } else { /* register within PT_REGS struct */ unsigned long tmp2; ret = ptrace_get_reg(child, numReg, &tmp2); @@ -207,7 +205,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, * to be an array of unsigned int (32 bits) - the * index passed in is based on this assumption. */ - ((unsigned int *)child->thread.fpr) + ((unsigned int *)child->thread.fp_state.fpr) [FPRINDEX(index)] = data; ret = 0; } @@ -251,8 +249,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, u64 *tmp; flush_fp_to_thread(child); /* get 64 bit FPR ... */ - tmp = &(((u64 *)child->thread.fpr) - [FPRINDEX_3264(numReg)]); + tmp = &child->thread.fp_state.fpr[numReg - PT_FPR0][0]; /* ... write the 32 bit part we want */ ((u32 *)tmp)[index % 2] = data; ret = 0; diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index bebdf1a..ea25e45 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c @@ -265,27 +265,27 @@ struct rt_sigframe { unsigned long copy_fpr_to_user(void __user *to, struct task_struct *task) { - double buf[ELF_NFPREG]; + u64 buf[ELF_NFPREG]; int i; /* save FPR copy to local buffer then write to the thread_struct */ for (i = 0; i < (ELF_NFPREG - 1) ; i++) buf[i] = task->thread.TS_FPR(i); - memcpy(&buf[i], &task->thread.fpscr, sizeof(double)); + buf[i] = task->thread.fp_state.fpscr; return __copy_to_user(to, buf, ELF_NFPREG * sizeof(double)); } unsigned long copy_fpr_from_user(struct task_struct *task, void __user *from) { - double buf[ELF_NFPREG]; + u64 buf[ELF_NFPREG]; int i; if (__copy_from_user(buf, from, ELF_NFPREG * sizeof(double))) return 1; for (i = 0; i < (ELF_NFPREG - 1) ; i++) task->thread.TS_FPR(i) = buf[i]; - memcpy(&task->thread.fpscr, &buf[i], sizeof(double)); + task->thread.fp_state.fpscr = buf[i]; return 0; } @@ -293,25 +293,25 @@ unsigned long copy_fpr_from_user(struct task_struct *task, unsigned long copy_vsx_to_user(void __user *to, struct task_struct *task) { - double buf[ELF_NVSRHALFREG]; + u64 buf[ELF_NVSRHALFREG]; int i; /* save FPR copy to local buffer then write to the thread_struct */ for (i = 0; i < ELF_NVSRHALFREG; i++) - buf[i] = task->thread.fpr[i][TS_VSRLOWOFFSET]; + buf[i] = task->thread.fp_state.fpr[i][TS_VSRLOWOFFSET]; return __copy_to_user(to, buf, ELF_NVSRHALFREG * sizeof(double)); } unsigned long copy_vsx_from_user(struct task_struct *task, void __user *from) { - double buf[ELF_NVSRHALFREG]; + u64 buf[ELF_NVSRHALFREG]; int i; if (__copy_from_user(buf, from, ELF_NVSRHALFREG * sizeof(double))) return 1; for (i = 0; i < ELF_NVSRHALFREG ; i++) - task->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i]; + task->thread.fp_state.fpr[i][TS_VSRLOWOFFSET] = buf[i]; return 0; } @@ -319,27 +319,27 @@ unsigned long copy_vsx_from_user(struct task_struct *task, unsigned long copy_transact_fpr_to_user(void __user *to, struct task_struct *task) { - double buf[ELF_NFPREG]; + u64 buf[ELF_NFPREG]; int i; /* save FPR copy to local buffer then write to the thread_struct */ for (i = 0; i < (ELF_NFPREG - 1) ; i++) buf[i] = task->thread.TS_TRANS_FPR(i); - memcpy(&buf[i], &task->thread.transact_fpscr, sizeof(double)); + buf[i] = task->thread.transact_fp.fpscr; return __copy_to_user(to, buf, ELF_NFPREG * sizeof(double)); } unsigned long copy_transact_fpr_from_user(struct task_struct *task, void __user *from) { - double buf[ELF_NFPREG]; + u64 buf[ELF_NFPREG]; int i; if (__copy_from_user(buf, from, ELF_NFPREG * sizeof(double))) return 1; for (i = 0; i < (ELF_NFPREG - 1) ; i++) task->thread.TS_TRANS_FPR(i) = buf[i]; - memcpy(&task->thread.transact_fpscr, &buf[i], sizeof(double)); + task->thread.transact_fp.fpscr = buf[i]; return 0; } @@ -347,25 +347,25 @@ unsigned long copy_transact_fpr_from_user(struct task_struct *task, unsigned long copy_transact_vsx_to_user(void __user *to, struct task_struct *task) { - double buf[ELF_NVSRHALFREG]; + u64 buf[ELF_NVSRHALFREG]; int i; /* save FPR copy to local buffer then write to the thread_struct */ for (i = 0; i < ELF_NVSRHALFREG; i++) - buf[i] = task->thread.transact_fpr[i][TS_VSRLOWOFFSET]; + buf[i] = task->thread.transact_fp.fpr[i][TS_VSRLOWOFFSET]; return __copy_to_user(to, buf, ELF_NVSRHALFREG * sizeof(double)); } unsigned long copy_transact_vsx_from_user(struct task_struct *task, void __user *from) { - double buf[ELF_NVSRHALFREG]; + u64 buf[ELF_NVSRHALFREG]; int i; if (__copy_from_user(buf, from, ELF_NVSRHALFREG * sizeof(double))) return 1; for (i = 0; i < ELF_NVSRHALFREG ; i++) - task->thread.transact_fpr[i][TS_VSRLOWOFFSET] = buf[i]; + task->thread.transact_fp.fpr[i][TS_VSRLOWOFFSET] = buf[i]; return 0; } #endif /* CONFIG_PPC_TRANSACTIONAL_MEM */ @@ -373,14 +373,14 @@ unsigned long copy_transact_vsx_from_user(struct task_struct *task, inline unsigned long copy_fpr_to_user(void __user *to, struct task_struct *task) { - return __copy_to_user(to, task->thread.fpr, + return __copy_to_user(to, task->thread.fp_state.fpr, ELF_NFPREG * sizeof(double)); } inline unsigned long copy_fpr_from_user(struct task_struct *task, void __user *from) { - return __copy_from_user(task->thread.fpr, from, + return __copy_from_user(task->thread.fp_state.fpr, from, ELF_NFPREG * sizeof(double)); } @@ -388,14 +388,14 @@ inline unsigned long copy_fpr_from_user(struct task_struct *task, inline unsigned long copy_transact_fpr_to_user(void __user *to, struct task_struct *task) { - return __copy_to_user(to, task->thread.transact_fpr, + return __copy_to_user(to, task->thread.transact_fp.fpr, ELF_NFPREG * sizeof(double)); } inline unsigned long copy_transact_fpr_from_user(struct task_struct *task, void __user *from) { - return __copy_from_user(task->thread.transact_fpr, from, + return __copy_from_user(task->thread.transact_fp.fpr, from, ELF_NFPREG * sizeof(double)); } #endif /* CONFIG_PPC_TRANSACTIONAL_MEM */ @@ -423,7 +423,7 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame, /* save altivec registers */ if (current->thread.used_vr) { flush_altivec_to_thread(current); - if (__copy_to_user(&frame->mc_vregs, current->thread.vr, + if (__copy_to_user(&frame->mc_vregs, ¤t->thread.vr_state, ELF_NVRREG * sizeof(vector128))) return 1; /* set MSR_VEC in the saved MSR value to indicate that @@ -534,17 +534,17 @@ static int save_tm_user_regs(struct pt_regs *regs, /* save altivec registers */ if (current->thread.used_vr) { flush_altivec_to_thread(current); - if (__copy_to_user(&frame->mc_vregs, current->thread.vr, + if (__copy_to_user(&frame->mc_vregs, ¤t->thread.vr_state, ELF_NVRREG * sizeof(vector128))) return 1; if (msr & MSR_VEC) { if (__copy_to_user(&tm_frame->mc_vregs, - current->thread.transact_vr, + ¤t->thread.transact_vr, ELF_NVRREG * sizeof(vector128))) return 1; } else { if (__copy_to_user(&tm_frame->mc_vregs, - current->thread.vr, + ¤t->thread.vr_state, ELF_NVRREG * sizeof(vector128))) return 1; } @@ -692,11 +692,12 @@ static long restore_user_regs(struct pt_regs *regs, regs->msr &= ~MSR_VEC; if (msr & MSR_VEC) { /* restore altivec registers from the stack */ - if (__copy_from_user(current->thread.vr, &sr->mc_vregs, + if (__copy_from_user(¤t->thread.vr_state, &sr->mc_vregs, sizeof(sr->mc_vregs))) return 1; } else if (current->thread.used_vr) - memset(current->thread.vr, 0, ELF_NVRREG * sizeof(vector128)); + memset(¤t->thread.vr_state, 0, + ELF_NVRREG * sizeof(vector128)); /* Always get VRSAVE back */ if (__get_user(current->thread.vrsave, (u32 __user *)&sr->mc_vregs[32])) @@ -722,7 +723,7 @@ static long restore_user_regs(struct pt_regs *regs, return 1; } else if (current->thread.used_vsr) for (i = 0; i < 32 ; i++) - current->thread.fpr[i][TS_VSRLOWOFFSET] = 0; + current->thread.fp_state.fpr[i][TS_VSRLOWOFFSET] = 0; #endif /* CONFIG_VSX */ /* * force the process to reload the FP registers from @@ -798,15 +799,16 @@ static long restore_tm_user_regs(struct pt_regs *regs, regs->msr &= ~MSR_VEC; if (msr & MSR_VEC) { /* restore altivec registers from the stack */ - if (__copy_from_user(current->thread.vr, &sr->mc_vregs, + if (__copy_from_user(¤t->thread.vr_state, &sr->mc_vregs, sizeof(sr->mc_vregs)) || - __copy_from_user(current->thread.transact_vr, + __copy_from_user(¤t->thread.transact_vr, &tm_sr->mc_vregs, sizeof(sr->mc_vregs))) return 1; } else if (current->thread.used_vr) { - memset(current->thread.vr, 0, ELF_NVRREG * sizeof(vector128)); - memset(current->thread.transact_vr, 0, + memset(¤t->thread.vr_state, 0, + ELF_NVRREG * sizeof(vector128)); + memset(¤t->thread.transact_vr, 0, ELF_NVRREG * sizeof(vector128)); } @@ -838,8 +840,8 @@ static long restore_tm_user_regs(struct pt_regs *regs, return 1; } else if (current->thread.used_vsr) for (i = 0; i < 32 ; i++) { - current->thread.fpr[i][TS_VSRLOWOFFSET] = 0; - current->thread.transact_fpr[i][TS_VSRLOWOFFSET] = 0; + current->thread.fp_state.fpr[i][TS_VSRLOWOFFSET] = 0; + current->thread.transact_fp.fpr[i][TS_VSRLOWOFFSET] = 0; } #endif /* CONFIG_VSX */ @@ -1030,7 +1032,7 @@ int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka, if (__put_user(0, &rt_sf->uc.uc_link)) goto badframe; - current->thread.fpscr.val = 0; /* turn off all fp exceptions */ + current->thread.fp_state.fpscr = 0; /* turn off all fp exceptions */ /* create a stack frame for the caller of the handler */ newsp = ((unsigned long)rt_sf) - (__SIGNAL_FRAMESIZE + 16); @@ -1462,7 +1464,7 @@ int handle_signal32(unsigned long sig, struct k_sigaction *ka, regs->link = tramp; - current->thread.fpscr.val = 0; /* turn off all fp exceptions */ + current->thread.fp_state.fpscr = 0; /* turn off all fp exceptions */ /* create a stack frame for the caller of the handler */ newsp = ((unsigned long)frame) - __SIGNAL_FRAMESIZE; diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index f93ec28..a3c1ed4 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c @@ -103,7 +103,8 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, if (current->thread.used_vr) { flush_altivec_to_thread(current); /* Copy 33 vec registers (vr0..31 and vscr) to the stack */ - err |= __copy_to_user(v_regs, current->thread.vr, 33 * sizeof(vector128)); + err |= __copy_to_user(v_regs, ¤t->thread.vr_state, + 33 * sizeof(vector128)); /* set MSR_VEC in the MSR value in the frame to indicate that sc->v_reg) * contains valid data. */ @@ -195,18 +196,18 @@ static long setup_tm_sigcontexts(struct sigcontext __user *sc, if (current->thread.used_vr) { flush_altivec_to_thread(current); /* Copy 33 vec registers (vr0..31 and vscr) to the stack */ - err |= __copy_to_user(v_regs, current->thread.vr, + err |= __copy_to_user(v_regs, ¤t->thread.vr_state, 33 * sizeof(vector128)); /* If VEC was enabled there are transactional VRs valid too, * else they're a copy of the checkpointed VRs. */ if (msr & MSR_VEC) err |= __copy_to_user(tm_v_regs, - current->thread.transact_vr, + ¤t->thread.transact_vr, 33 * sizeof(vector128)); else err |= __copy_to_user(tm_v_regs, - current->thread.vr, + ¤t->thread.vr_state, 33 * sizeof(vector128)); /* set MSR_VEC in the MSR value in the frame to indicate @@ -349,10 +350,10 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig, return -EFAULT; /* Copy 33 vec registers (vr0..31 and vscr) from the stack */ if (v_regs != NULL && (msr & MSR_VEC) != 0) - err |= __copy_from_user(current->thread.vr, v_regs, + err |= __copy_from_user(¤t->thread.vr_state, v_regs, 33 * sizeof(vector128)); else if (current->thread.used_vr) - memset(current->thread.vr, 0, 33 * sizeof(vector128)); + memset(¤t->thread.vr_state, 0, 33 * sizeof(vector128)); /* Always get VRSAVE back */ if (v_regs != NULL) err |= __get_user(current->thread.vrsave, (u32 __user *)&v_regs[33]); @@ -374,7 +375,7 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig, err |= copy_vsx_from_user(current, v_regs); else for (i = 0; i < 32 ; i++) - current->thread.fpr[i][TS_VSRLOWOFFSET] = 0; + current->thread.fp_state.fpr[i][TS_VSRLOWOFFSET] = 0; #endif return err; } @@ -468,14 +469,14 @@ static long restore_tm_sigcontexts(struct pt_regs *regs, return -EFAULT; /* Copy 33 vec registers (vr0..31 and vscr) from the stack */ if (v_regs != NULL && tm_v_regs != NULL && (msr & MSR_VEC) != 0) { - err |= __copy_from_user(current->thread.vr, v_regs, + err |= __copy_from_user(¤t->thread.vr_state, v_regs, 33 * sizeof(vector128)); - err |= __copy_from_user(current->thread.transact_vr, tm_v_regs, + err |= __copy_from_user(¤t->thread.transact_vr, tm_v_regs, 33 * sizeof(vector128)); } else if (current->thread.used_vr) { - memset(current->thread.vr, 0, 33 * sizeof(vector128)); - memset(current->thread.transact_vr, 0, 33 * sizeof(vector128)); + memset(¤t->thread.vr_state, 0, 33 * sizeof(vector128)); + memset(¤t->thread.transact_vr, 0, 33 * sizeof(vector128)); } /* Always get VRSAVE back */ if (v_regs != NULL && tm_v_regs != NULL) { @@ -507,8 +508,8 @@ static long restore_tm_sigcontexts(struct pt_regs *regs, err |= copy_transact_vsx_from_user(current, tm_v_regs); } else { for (i = 0; i < 32 ; i++) { - current->thread.fpr[i][TS_VSRLOWOFFSET] = 0; - current->thread.transact_fpr[i][TS_VSRLOWOFFSET] = 0; + current->thread.fp_state.fpr[i][TS_VSRLOWOFFSET] = 0; + current->thread.transact_fp.fpr[i][TS_VSRLOWOFFSET] = 0; } } #endif @@ -747,7 +748,7 @@ int handle_rt_signal64(int signr, struct k_sigaction *ka, siginfo_t *info, goto badframe; /* Make sure signal handler doesn't get spurious FP exceptions */ - current->thread.fpscr.val = 0; + current->thread.fp_state.fpscr = 0; #ifdef CONFIG_PPC_TRANSACTIONAL_MEM /* Remove TM bits from thread's MSR. The MSR in the sigcontext * just indicates to userland that we were doing a transaction, but we diff --git a/arch/powerpc/kernel/tm.S b/arch/powerpc/kernel/tm.S index cd809ea..761af4f 100644 --- a/arch/powerpc/kernel/tm.S +++ b/arch/powerpc/kernel/tm.S @@ -12,16 +12,15 @@ #include #ifdef CONFIG_VSX -/* See fpu.S, this is very similar but to save/restore checkpointed FPRs/VSRs */ -#define __SAVE_32FPRS_VSRS_TRANSACT(n,c,base) \ +/* See fpu.S, this is borrowed from there */ +#define __SAVE_32FPRS_VSRS(n,c,base) \ BEGIN_FTR_SECTION \ b 2f; \ END_FTR_SECTION_IFSET(CPU_FTR_VSX); \ - SAVE_32FPRS_TRANSACT(n,base); \ + SAVE_32FPRS(n,base); \ b 3f; \ -2: SAVE_32VSRS_TRANSACT(n,c,base); \ +2: SAVE_32VSRS(n,c,base); \ 3: -/* ...and this is just plain borrowed from there. */ #define __REST_32FPRS_VSRS(n,c,base) \ BEGIN_FTR_SECTION \ b 2f; \ @@ -31,11 +30,11 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX); \ 2: REST_32VSRS(n,c,base); \ 3: #else -#define __SAVE_32FPRS_VSRS_TRANSACT(n,c,base) SAVE_32FPRS_TRANSACT(n, base) -#define __REST_32FPRS_VSRS(n,c,base) REST_32FPRS(n, base) +#define __SAVE_32FPRS_VSRS(n,c,base) SAVE_32FPRS(n, base) +#define __REST_32FPRS_VSRS(n,c,base) REST_32FPRS(n, base) #endif -#define SAVE_32FPRS_VSRS_TRANSACT(n,c,base) \ - __SAVE_32FPRS_VSRS_TRANSACT(n,__REG_##c,__REG_##base) +#define SAVE_32FPRS_VSRS(n,c,base) \ + __SAVE_32FPRS_VSRS(n,__REG_##c,__REG_##base) #define REST_32FPRS_VSRS(n,c,base) \ __REST_32FPRS_VSRS(n,__REG_##c,__REG_##base) @@ -157,10 +156,11 @@ _GLOBAL(tm_reclaim) andis. r0, r4, MSR_VEC@h beq dont_backup_vec - SAVE_32VRS_TRANSACT(0, r6, r3) /* r6 scratch, r3 thread */ + addi r7, r3, THREAD_TRANSACT_VRSTATE + SAVE_32VRS(0, r6, r7) /* r6 scratch, r7 transact vr state */ mfvscr vr0 - li r6, THREAD_TRANSACT_VSCR - stvx vr0, r3, r6 + li r6, VRSTATE_VSCR + stvx vr0, r7, r6 dont_backup_vec: mfspr r0, SPRN_VRSAVE std r0, THREAD_TRANSACT_VRSAVE(r3) @@ -168,10 +168,11 @@ dont_backup_vec: andi. r0, r4, MSR_FP beq dont_backup_fp - SAVE_32FPRS_VSRS_TRANSACT(0, R6, R3) /* r6 scratch, r3 thread */ + addi r7, r3, THREAD_TRANSACT_FPSTATE + SAVE_32FPRS_VSRS(0, R6, R7) /* r6 scratch, r7 transact fp state */ mffs fr0 - stfd fr0,THREAD_TRANSACT_FPSCR(r3) + stfd fr0,FPSTATE_FPSCR(r7) dont_backup_fp: /* The moment we treclaim, ALL of our GPRs will switch @@ -358,10 +359,11 @@ _GLOBAL(tm_recheckpoint) andis. r0, r4, MSR_VEC@h beq dont_restore_vec - li r5, THREAD_VSCR - lvx vr0, r3, r5 + addi r8, r3, THREAD_VRSTATE + li r5, VRSTATE_VSCR + lvx vr0, r8, r5 mtvscr vr0 - REST_32VRS(0, r5, r3) /* r5 scratch, r3 THREAD ptr */ + REST_32VRS(0, r5, r8) /* r5 scratch, r8 ptr */ dont_restore_vec: ld r5, THREAD_VRSAVE(r3) mtspr SPRN_VRSAVE, r5 @@ -370,9 +372,10 @@ dont_restore_vec: andi. r0, r4, MSR_FP beq dont_restore_fp - lfd fr0, THREAD_FPSCR(r3) + addi r8, r3, THREAD_FPSTATE + lfd fr0, FPSTATE_FPSCR(r8) MTFSF_L(fr0) - REST_32FPRS_VSRS(0, R4, R3) + REST_32FPRS_VSRS(0, R4, R8) dont_restore_fp: mtmsr r6 /* FP/Vec off again! */ diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index f783c93..f0a6814 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -816,7 +816,7 @@ static void parse_fpe(struct pt_regs *regs) flush_fp_to_thread(current); - code = __parse_fpscr(current->thread.fpscr.val); + code = __parse_fpscr(current->thread.fp_state.fpscr); _exception(SIGFPE, regs, code, regs->nip); } @@ -1069,7 +1069,7 @@ static int emulate_math(struct pt_regs *regs) return 0; case 1: { int code = 0; - code = __parse_fpscr(current->thread.fpscr.val); + code = __parse_fpscr(current->thread.fp_state.fpscr); _exception(SIGFPE, regs, code, regs->nip); return 0; } @@ -1371,8 +1371,6 @@ void facility_unavailable_exception(struct pt_regs *regs) #ifdef CONFIG_PPC_TRANSACTIONAL_MEM -extern void do_load_up_fpu(struct pt_regs *regs); - void fp_unavailable_tm(struct pt_regs *regs) { /* Note: This does not handle any kind of FP laziness. */ @@ -1403,8 +1401,6 @@ void fp_unavailable_tm(struct pt_regs *regs) } #ifdef CONFIG_ALTIVEC -extern void do_load_up_altivec(struct pt_regs *regs); - void altivec_unavailable_tm(struct pt_regs *regs) { /* See the comments in fp_unavailable_tm(). This function operates @@ -1634,7 +1630,7 @@ void altivec_assist_exception(struct pt_regs *regs) /* XXX quick hack for now: set the non-Java bit in the VSCR */ printk_ratelimited(KERN_ERR "Unrecognized altivec instruction " "in %s at %lx\n", current->comm, regs->nip); - current->thread.vscr.u[3] |= 0x10000; + current->thread.vr_state.vscr.u[3] |= 0x10000; } } #endif /* CONFIG_ALTIVEC */ diff --git a/arch/powerpc/kernel/vecemu.c b/arch/powerpc/kernel/vecemu.c index 604d094..c4bfadb 100644 --- a/arch/powerpc/kernel/vecemu.c +++ b/arch/powerpc/kernel/vecemu.c @@ -271,7 +271,7 @@ int emulate_altivec(struct pt_regs *regs) vb = (instr >> 11) & 0x1f; vc = (instr >> 6) & 0x1f; - vrs = current->thread.vr; + vrs = current->thread.vr_state.vr; switch (instr & 0x3f) { case 10: switch (vc) { @@ -320,12 +320,12 @@ int emulate_altivec(struct pt_regs *regs) case 14: /* vctuxs */ for (i = 0; i < 4; ++i) vrs[vd].u[i] = ctuxs(vrs[vb].u[i], va, - ¤t->thread.vscr.u[3]); + ¤t->thread.vr_state.vscr.u[3]); break; case 15: /* vctsxs */ for (i = 0; i < 4; ++i) vrs[vd].u[i] = ctsxs(vrs[vb].u[i], va, - ¤t->thread.vscr.u[3]); + ¤t->thread.vr_state.vscr.u[3]); break; default: return -EINVAL; diff --git a/arch/powerpc/kernel/vector.S b/arch/powerpc/kernel/vector.S index 9e20999..a48df87 100644 --- a/arch/powerpc/kernel/vector.S +++ b/arch/powerpc/kernel/vector.S @@ -8,29 +8,6 @@ #include #ifdef CONFIG_PPC_TRANSACTIONAL_MEM -/* - * Wrapper to call load_up_altivec from C. - * void do_load_up_altivec(struct pt_regs *regs); - */ -_GLOBAL(do_load_up_altivec) - mflr r0 - std r0, 16(r1) - stdu r1, -112(r1) - - subi r6, r3, STACK_FRAME_OVERHEAD - /* load_up_altivec expects r12=MSR, r13=PACA, and returns - * with r12 = new MSR. - */ - ld r12,_MSR(r6) - GET_PACA(r13) - bl load_up_altivec - std r12,_MSR(r6) - - ld r0, 112+16(r1) - addi r1, r1, 112 - mtlr r0 - blr - /* void do_load_up_transact_altivec(struct thread_struct *thread) * * This is similar to load_up_altivec but for the transactional version of the @@ -46,10 +23,11 @@ _GLOBAL(do_load_up_transact_altivec) li r4,1 stw r4,THREAD_USED_VR(r3) - li r10,THREAD_TRANSACT_VSCR + li r10,THREAD_TRANSACT_VRSTATE+VRSTATE_VSCR lvx vr0,r10,r3 mtvscr vr0 - REST_32VRS_TRANSACT(0,r4,r3) + addi r10,r3,THREAD_TRANSACT_VRSTATE + REST_32VRS(0,r4,r10) /* Disable VEC again. */ MTMSRD(r6) @@ -59,7 +37,6 @@ _GLOBAL(do_load_up_transact_altivec) #endif /* - * load_up_altivec(unused, unused, tsk) * Disable VMX for the task which had it previously, * and save its vector registers in its thread_struct. * Enables the VMX for use in the kernel on return. @@ -90,10 +67,11 @@ _GLOBAL(load_up_altivec) /* Save VMX state to last_task_used_altivec's THREAD struct */ toreal(r4) addi r4,r4,THREAD - SAVE_32VRS(0,r5,r4) + addi r7,r4,THREAD_VRSTATE + SAVE_32VRS(0,r5,r7) mfvscr vr0 - li r10,THREAD_VSCR - stvx vr0,r10,r4 + li r10,VRSTATE_VSCR + stvx vr0,r10,r7 /* Disable VMX for last_task_used_altivec */ PPC_LL r5,PT_REGS(r4) toreal(r5) @@ -125,12 +103,13 @@ _GLOBAL(load_up_altivec) oris r12,r12,MSR_VEC@h std r12,_MSR(r1) #endif + addi r7,r5,THREAD_VRSTATE li r4,1 - li r10,THREAD_VSCR + li r10,VRSTATE_VSCR stw r4,THREAD_USED_VR(r5) - lvx vr0,r10,r5 + lvx vr0,r10,r7 mtvscr vr0 - REST_32VRS(0,r4,r5) + REST_32VRS(0,r4,r7) #ifndef CONFIG_SMP /* Update last_task_used_altivec to 'current' */ subi r4,r5,THREAD /* Back to 'current' */ @@ -165,12 +144,13 @@ _GLOBAL(giveup_altivec) PPC_LCMPI 0,r3,0 beqlr /* if no previous owner, done */ addi r3,r3,THREAD /* want THREAD of task */ + addi r7,r3,THREAD_VRSTATE PPC_LL r5,PT_REGS(r3) PPC_LCMPI 0,r5,0 - SAVE_32VRS(0,r4,r3) + SAVE_32VRS(0,r4,r7) mfvscr vr0 - li r4,THREAD_VSCR - stvx vr0,r4,r3 + li r4,VRSTATE_VSCR + stvx vr0,r4,r7 beq 1f PPC_LL r4,_MSR-STACK_FRAME_OVERHEAD(r5) #ifdef CONFIG_VSX diff --git a/arch/powerpc/kvm/book3s_pr.c b/arch/powerpc/kvm/book3s_pr.c index 27db1e6..c0b48f96 100644 --- a/arch/powerpc/kvm/book3s_pr.c +++ b/arch/powerpc/kvm/book3s_pr.c @@ -444,7 +444,7 @@ void kvmppc_giveup_ext(struct kvm_vcpu *vcpu, ulong msr) #ifdef CONFIG_VSX u64 *vcpu_vsx = vcpu->arch.vsr; #endif - u64 *thread_fpr = (u64*)t->fpr; + u64 *thread_fpr = &t->fp_state.fpr[0][0]; int i; /* @@ -466,14 +466,14 @@ void kvmppc_giveup_ext(struct kvm_vcpu *vcpu, ulong msr) /* * Note that on CPUs with VSX, giveup_fpu stores * both the traditional FP registers and the added VSX - * registers into thread.fpr[]. + * registers into thread.fp_state.fpr[]. */ if (current->thread.regs->msr & MSR_FP) giveup_fpu(current); for (i = 0; i < ARRAY_SIZE(vcpu->arch.fpr); i++) vcpu_fpr[i] = thread_fpr[get_fpr_index(i)]; - vcpu->arch.fpscr = t->fpscr.val; + vcpu->arch.fpscr = t->fp_state.fpscr; #ifdef CONFIG_VSX if (cpu_has_feature(CPU_FTR_VSX)) @@ -486,8 +486,8 @@ void kvmppc_giveup_ext(struct kvm_vcpu *vcpu, ulong msr) if (msr & MSR_VEC) { if (current->thread.regs->msr & MSR_VEC) giveup_altivec(current); - memcpy(vcpu->arch.vr, t->vr, sizeof(vcpu->arch.vr)); - vcpu->arch.vscr = t->vscr; + memcpy(vcpu->arch.vr, t->vr_state.vr, sizeof(vcpu->arch.vr)); + vcpu->arch.vscr = t->vr_state.vscr; } #endif @@ -539,7 +539,7 @@ static int kvmppc_handle_ext(struct kvm_vcpu *vcpu, unsigned int exit_nr, #ifdef CONFIG_VSX u64 *vcpu_vsx = vcpu->arch.vsr; #endif - u64 *thread_fpr = (u64*)t->fpr; + u64 *thread_fpr = &t->fp_state.fpr[0][0]; int i; /* When we have paired singles, we emulate in software */ @@ -584,15 +584,15 @@ static int kvmppc_handle_ext(struct kvm_vcpu *vcpu, unsigned int exit_nr, for (i = 0; i < ARRAY_SIZE(vcpu->arch.vsr) / 2; i++) thread_fpr[get_fpr_index(i) + 1] = vcpu_vsx[i]; #endif - t->fpscr.val = vcpu->arch.fpscr; + t->fp_state.fpscr = vcpu->arch.fpscr; t->fpexc_mode = 0; kvmppc_load_up_fpu(); } if (msr & MSR_VEC) { #ifdef CONFIG_ALTIVEC - memcpy(t->vr, vcpu->arch.vr, sizeof(vcpu->arch.vr)); - t->vscr = vcpu->arch.vscr; + memcpy(t->vr_state.vr, vcpu->arch.vr, sizeof(vcpu->arch.vr)); + t->vr_state.vscr = vcpu->arch.vscr; t->vrsave = -1; kvmppc_load_up_altivec(); #endif @@ -1116,12 +1116,10 @@ void kvmppc_core_vcpu_free(struct kvm_vcpu *vcpu) int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu) { int ret; - double fpr[32][TS_FPRWIDTH]; - unsigned int fpscr; + struct thread_fp_state fp; int fpexc_mode; #ifdef CONFIG_ALTIVEC - vector128 vr[32]; - vector128 vscr; + struct thread_vr_state vr; unsigned long uninitialized_var(vrsave); int used_vr; #endif @@ -1153,8 +1151,7 @@ int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu) /* Save FPU state in stack */ if (current->thread.regs->msr & MSR_FP) giveup_fpu(current); - memcpy(fpr, current->thread.fpr, sizeof(current->thread.fpr)); - fpscr = current->thread.fpscr.val; + fp = current->thread.fp_state; fpexc_mode = current->thread.fpexc_mode; #ifdef CONFIG_ALTIVEC @@ -1163,8 +1160,7 @@ int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu) if (used_vr) { if (current->thread.regs->msr & MSR_VEC) giveup_altivec(current); - memcpy(vr, current->thread.vr, sizeof(current->thread.vr)); - vscr = current->thread.vscr; + vr = current->thread.vr_state; vrsave = current->thread.vrsave; } #endif @@ -1196,15 +1192,13 @@ int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu) current->thread.regs->msr = ext_msr; /* Restore FPU/VSX state from stack */ - memcpy(current->thread.fpr, fpr, sizeof(current->thread.fpr)); - current->thread.fpscr.val = fpscr; + current->thread.fp_state = fp; current->thread.fpexc_mode = fpexc_mode; #ifdef CONFIG_ALTIVEC /* Restore Altivec state from stack */ if (used_vr && current->thread.used_vr) { - memcpy(current->thread.vr, vr, sizeof(current->thread.vr)); - current->thread.vscr = vscr; + current->thread.vr_state = vr; current->thread.vrsave = vrsave; } current->thread.used_vr = used_vr; diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index 17722d8..5133199 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -656,9 +656,8 @@ int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu) { int ret, s; #ifdef CONFIG_PPC_FPU - unsigned int fpscr; + struct thread_fp_state fp; int fpexc_mode; - u64 fpr[32]; #endif if (!vcpu->arch.sane) { @@ -677,13 +676,13 @@ int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu) #ifdef CONFIG_PPC_FPU /* Save userspace FPU state in stack */ enable_kernel_fp(); - memcpy(fpr, current->thread.fpr, sizeof(current->thread.fpr)); - fpscr = current->thread.fpscr.val; + fp = current->thread.fp_state; fpexc_mode = current->thread.fpexc_mode; /* Restore guest FPU state to thread */ - memcpy(current->thread.fpr, vcpu->arch.fpr, sizeof(vcpu->arch.fpr)); - current->thread.fpscr.val = vcpu->arch.fpscr; + memcpy(current->thread.fp_state.fpr, vcpu->arch.fpr, + sizeof(vcpu->arch.fpr)); + current->thread.fp_state.fpscr = vcpu->arch.fpscr; /* * Since we can't trap on MSR_FP in GS-mode, we consider the guest @@ -709,12 +708,12 @@ int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu) vcpu->fpu_active = 0; /* Save guest FPU state from thread */ - memcpy(vcpu->arch.fpr, current->thread.fpr, sizeof(vcpu->arch.fpr)); - vcpu->arch.fpscr = current->thread.fpscr.val; + memcpy(vcpu->arch.fpr, current->thread.fp_state.fpr, + sizeof(vcpu->arch.fpr)); + vcpu->arch.fpscr = current->thread.fp_state.fpscr; /* Restore userspace FPU state from stack */ - memcpy(current->thread.fpr, fpr, sizeof(current->thread.fpr)); - current->thread.fpscr.val = fpscr; + current->thread.fp_state = fp; current->thread.fpexc_mode = fpexc_mode; #endif -- cgit v0.10.2 From 18461960cbf50bf345ef0667d45d5f64de8fb893 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 10 Sep 2013 20:21:10 +1000 Subject: powerpc: Provide for giveup_fpu/altivec to save state in alternate location This provides a facility which is intended for use by KVM, where the contents of the FP/VSX and VMX (Altivec) registers can be saved away to somewhere other than the thread_struct when kernel code wants to use floating point or VMX instructions. This is done by providing a pointer in the thread_struct to indicate where the state should be saved to. The giveup_fpu() and giveup_altivec() functions test these pointers and save state to the indicated location if they are non-NULL. Note that the MSR_FP/VEC bits in task->thread.regs->msr are still used to indicate whether the CPU register state is live, even when an alternate save location is being used. This also provides load_fp_state() and load_vr_state() functions, which load up FP/VSX and VMX state from memory into the CPU registers, and corresponding store_fp_state() and store_vr_state() functions, which store FP/VSX and VMX state into memory from the CPU registers. Signed-off-by: Paul Mackerras Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h index afe695e..ea88e7b 100644 --- a/arch/powerpc/include/asm/processor.h +++ b/arch/powerpc/include/asm/processor.h @@ -211,6 +211,7 @@ struct thread_struct { #endif #endif struct thread_fp_state fp_state; + struct thread_fp_state *fp_save_area; int fpexc_mode; /* floating-point exception mode */ unsigned int align_ctl; /* alignment handling control */ #ifdef CONFIG_PPC64 @@ -229,6 +230,7 @@ struct thread_struct { unsigned long trap_nr; /* last trap # on this thread */ #ifdef CONFIG_ALTIVEC struct thread_vr_state vr_state; + struct thread_vr_state *vr_save_area; unsigned long vrsave; int used_vr; /* set if process has used altivec */ #endif /* CONFIG_ALTIVEC */ @@ -357,6 +359,11 @@ extern int set_endian(struct task_struct *tsk, unsigned int val); extern int get_unalign_ctl(struct task_struct *tsk, unsigned long adr); extern int set_unalign_ctl(struct task_struct *tsk, unsigned int val); +extern void load_fp_state(struct thread_fp_state *fp); +extern void store_fp_state(struct thread_fp_state *fp); +extern void load_vr_state(struct thread_vr_state *vr); +extern void store_vr_state(struct thread_vr_state *vr); + static inline unsigned int __unpack_fe01(unsigned long msr_bits) { return ((msr_bits & MSR_FE0) >> 10) | ((msr_bits & MSR_FE1) >> 8); diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 8d27b61..6278edd 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -91,9 +91,11 @@ int main(void) #endif DEFINE(THREAD_FPEXC_MODE, offsetof(struct thread_struct, fpexc_mode)); DEFINE(THREAD_FPSTATE, offsetof(struct thread_struct, fp_state)); + DEFINE(THREAD_FPSAVEAREA, offsetof(struct thread_struct, fp_save_area)); DEFINE(FPSTATE_FPSCR, offsetof(struct thread_fp_state, fpscr)); #ifdef CONFIG_ALTIVEC DEFINE(THREAD_VRSTATE, offsetof(struct thread_struct, vr_state)); + DEFINE(THREAD_VRSAVEAREA, offsetof(struct thread_struct, vr_save_area)); DEFINE(THREAD_VRSAVE, offsetof(struct thread_struct, vrsave)); DEFINE(THREAD_USED_VR, offsetof(struct thread_struct, used_vr)); DEFINE(VRSTATE_VSCR, offsetof(struct thread_vr_state, vscr)); diff --git a/arch/powerpc/kernel/fpu.S b/arch/powerpc/kernel/fpu.S index 34b96e6..4dca05e 100644 --- a/arch/powerpc/kernel/fpu.S +++ b/arch/powerpc/kernel/fpu.S @@ -81,6 +81,26 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX) #endif /* CONFIG_PPC_TRANSACTIONAL_MEM */ /* + * Load state from memory into FP registers including FPSCR. + * Assumes the caller has enabled FP in the MSR. + */ +_GLOBAL(load_fp_state) + lfd fr0,FPSTATE_FPSCR(r3) + MTFSF_L(fr0) + REST_32FPVSRS(0, R4, R3) + blr + +/* + * Store FP state into memory, including FPSCR + * Assumes the caller has enabled FP in the MSR. + */ +_GLOBAL(store_fp_state) + SAVE_32FPVSRS(0, R4, R3) + mffs fr0 + stfd fr0,FPSTATE_FPSCR(r3) + blr + +/* * This task wants to use the FPU now. * On UP, disable FP for the task which had the FPU previously, * and save its floating-point registers in its thread_struct. @@ -172,9 +192,12 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX) PPC_LCMPI 0,r3,0 beqlr- /* if no previous owner, done */ addi r3,r3,THREAD /* want THREAD of task */ + PPC_LL r6,THREAD_FPSAVEAREA(r3) PPC_LL r5,PT_REGS(r3) - PPC_LCMPI 0,r5,0 + PPC_LCMPI 0,r6,0 + bne 2f addi r6,r3,THREAD_FPSTATE +2: PPC_LCMPI 0,r5,0 SAVE_32FPVSRS(0, R4, R6) mffs fr0 stfd fr0,FPSTATE_FPSCR(r6) diff --git a/arch/powerpc/kernel/ppc_ksyms.c b/arch/powerpc/kernel/ppc_ksyms.c index 21646db..56a4bec 100644 --- a/arch/powerpc/kernel/ppc_ksyms.c +++ b/arch/powerpc/kernel/ppc_ksyms.c @@ -98,9 +98,13 @@ EXPORT_SYMBOL(start_thread); #ifdef CONFIG_PPC_FPU EXPORT_SYMBOL(giveup_fpu); +EXPORT_SYMBOL(load_fp_state); +EXPORT_SYMBOL(store_fp_state); #endif #ifdef CONFIG_ALTIVEC EXPORT_SYMBOL(giveup_altivec); +EXPORT_SYMBOL(load_vr_state); +EXPORT_SYMBOL(store_vr_state); #endif /* CONFIG_ALTIVEC */ #ifdef CONFIG_VSX EXPORT_SYMBOL(giveup_vsx); diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 7a28141..8649a3d 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -1008,6 +1008,11 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, p->thread.ptrace_bps[0] = NULL; #endif + p->thread.fp_save_area = NULL; +#ifdef CONFIG_ALTIVEC + p->thread.vr_save_area = NULL; +#endif + #ifdef CONFIG_PPC_STD_MMU_64 if (mmu_has_feature(MMU_FTR_SLB)) { unsigned long sp_vsid; @@ -1114,9 +1119,11 @@ void start_thread(struct pt_regs *regs, unsigned long start, unsigned long sp) current->thread.used_vsr = 0; #endif memset(¤t->thread.fp_state, 0, sizeof(current->thread.fp_state)); + current->thread.fp_save_area = NULL; #ifdef CONFIG_ALTIVEC memset(¤t->thread.vr_state, 0, sizeof(current->thread.vr_state)); current->thread.vr_state.vscr.u[3] = 0x00010000; /* Java mode disabled */ + current->thread.vr_save_area = NULL; current->thread.vrsave = 0; current->thread.used_vr = 0; #endif /* CONFIG_ALTIVEC */ diff --git a/arch/powerpc/kernel/vector.S b/arch/powerpc/kernel/vector.S index a48df87..eacda4e 100644 --- a/arch/powerpc/kernel/vector.S +++ b/arch/powerpc/kernel/vector.S @@ -37,6 +37,28 @@ _GLOBAL(do_load_up_transact_altivec) #endif /* + * Load state from memory into VMX registers including VSCR. + * Assumes the caller has enabled VMX in the MSR. + */ +_GLOBAL(load_vr_state) + li r4,VRSTATE_VSCR + lvx vr0,r4,r3 + mtvscr vr0 + REST_32VRS(0,r4,r3) + blr + +/* + * Store VMX state into memory, including VSCR. + * Assumes the caller has enabled VMX in the MSR. + */ +_GLOBAL(store_vr_state) + SAVE_32VRS(0, r4, r3) + mfvscr vr0 + li r4, VRSTATE_VSCR + stvx vr0, r4, r3 + blr + +/* * Disable VMX for the task which had it previously, * and save its vector registers in its thread_struct. * Enables the VMX for use in the kernel on return. @@ -144,9 +166,12 @@ _GLOBAL(giveup_altivec) PPC_LCMPI 0,r3,0 beqlr /* if no previous owner, done */ addi r3,r3,THREAD /* want THREAD of task */ - addi r7,r3,THREAD_VRSTATE + PPC_LL r7,THREAD_VRSAVEAREA(r3) PPC_LL r5,PT_REGS(r3) - PPC_LCMPI 0,r5,0 + PPC_LCMPI 0,r7,0 + bne 2f + addi r7,r3,THREAD_VRSTATE +2: PPC_LCMPI 0,r5,0 SAVE_32VRS(0,r4,r7) mfvscr vr0 li r4,VRSTATE_VSCR -- cgit v0.10.2 From 70b5341138dcb931df318afb51e8fb5310f9f095 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 10 Oct 2013 11:01:07 +0300 Subject: gpiolib / ACPI: move acpi_gpiochip_free_interrupts next to the request function It makes more sense to have these functions close to each other. No functional changes. Signed-off-by: Mika Westerberg Acked-by: Rafael J. Wysocki Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index f2beb72..1745ce5 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -194,6 +194,44 @@ void acpi_gpiochip_request_interrupts(struct gpio_chip *chip) } EXPORT_SYMBOL(acpi_gpiochip_request_interrupts); +/** + * acpi_gpiochip_free_interrupts() - Free GPIO _EVT ACPI event interrupts. + * @chip: gpio chip + * + * Free interrupts associated with the _EVT method for the given GPIO chip. + * + * The remaining ACPI event interrupts associated with the chip are freed + * automatically. + */ +void acpi_gpiochip_free_interrupts(struct gpio_chip *chip) +{ + acpi_handle handle; + acpi_status status; + struct list_head *evt_pins; + struct acpi_gpio_evt_pin *evt_pin, *ep; + + if (!chip->dev || !chip->to_irq) + return; + + handle = ACPI_HANDLE(chip->dev); + if (!handle) + return; + + status = acpi_get_data(handle, acpi_gpio_evt_dh, (void **)&evt_pins); + if (ACPI_FAILURE(status)) + return; + + list_for_each_entry_safe_reverse(evt_pin, ep, evt_pins, node) { + devm_free_irq(chip->dev, evt_pin->irq, evt_pin); + list_del(&evt_pin->node); + kfree(evt_pin); + } + + acpi_detach_data(handle, acpi_gpio_evt_dh); + kfree(evt_pins); +} +EXPORT_SYMBOL(acpi_gpiochip_free_interrupts); + struct acpi_gpio_lookup { struct acpi_gpio_info info; int index; @@ -271,41 +309,3 @@ int acpi_get_gpio_by_index(struct device *dev, int index, return lookup.gpio; } EXPORT_SYMBOL_GPL(acpi_get_gpio_by_index); - -/** - * acpi_gpiochip_free_interrupts() - Free GPIO _EVT ACPI event interrupts. - * @chip: gpio chip - * - * Free interrupts associated with the _EVT method for the given GPIO chip. - * - * The remaining ACPI event interrupts associated with the chip are freed - * automatically. - */ -void acpi_gpiochip_free_interrupts(struct gpio_chip *chip) -{ - acpi_handle handle; - acpi_status status; - struct list_head *evt_pins; - struct acpi_gpio_evt_pin *evt_pin, *ep; - - if (!chip->dev || !chip->to_irq) - return; - - handle = ACPI_HANDLE(chip->dev); - if (!handle) - return; - - status = acpi_get_data(handle, acpi_gpio_evt_dh, (void **)&evt_pins); - if (ACPI_FAILURE(status)) - return; - - list_for_each_entry_safe_reverse(evt_pin, ep, evt_pins, node) { - devm_free_irq(chip->dev, evt_pin->irq, evt_pin); - list_del(&evt_pin->node); - kfree(evt_pin); - } - - acpi_detach_data(handle, acpi_gpio_evt_dh); - kfree(evt_pins); -} -EXPORT_SYMBOL(acpi_gpiochip_free_interrupts); -- cgit v0.10.2 From 9a633a2bced158c57b73cf4d8e87be60473de1d2 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Fri, 11 Oct 2013 05:04:12 -0500 Subject: regulator: ti-abb: Fix operator precedence typo commit 40b1936e (regulator: Introduce TI Adaptive Body Bias(ABB) on-chip LDO driver) missed a pair of brackets which cause the wrong vset data to be picked up from efuse, resulting in bad VBB voltage values. Signed-off-by: Nishanth Menon Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/drivers/regulator/ti-abb-regulator.c b/drivers/regulator/ti-abb-regulator.c index 20c271d..b993ec5 100644 --- a/drivers/regulator/ti-abb-regulator.c +++ b/drivers/regulator/ti-abb-regulator.c @@ -615,7 +615,7 @@ static int ti_abb_init_table(struct device *dev, struct ti_abb *abb, pname, *volt_table, vset_mask); continue; } - info->vset = efuse_val & vset_mask >> __ffs(vset_mask); + info->vset = (efuse_val & vset_mask) >> __ffs(vset_mask); dev_dbg(dev, "[%d]v=%d vset=%x\n", i, *volt_table, info->vset); check_abb: switch (info->opp_sel) { -- cgit v0.10.2 From c6452e39e8286b88872aee20a4d083cfa65516bc Mon Sep 17 00:00:00 2001 From: Steffen Trumtrar Date: Fri, 11 Oct 2013 12:28:13 +0200 Subject: ASoC: mc13783: add mixer controls Add more controls to the alsa mixer infrastructure. Signed-off-by: Steffen Trumtrar Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c index eedbf05..2b62737 100644 --- a/sound/soc/codecs/mc13783.c +++ b/sound/soc/codecs/mc13783.c @@ -541,8 +541,26 @@ static const struct soc_enum mc13783_enum_3d_mixer = static struct snd_kcontrol_new mc13783_control_list[] = { SOC_SINGLE("Loudspeaker enable", MC13783_AUDIO_RX0, 5, 1, 0), SOC_SINGLE("PCM Playback Volume", MC13783_AUDIO_RX1, 6, 15, 0), + SOC_SINGLE("PCM Playback Switch", MC13783_AUDIO_RX1, 5, 1, 0), SOC_DOUBLE("PCM Capture Volume", MC13783_AUDIO_TX, 19, 14, 31, 0), SOC_ENUM("3D Control", mc13783_enum_3d_mixer), + + SOC_SINGLE("CDCOUT Switch", MC13783_AUDIO_RX0, 18, 1, 0), + SOC_SINGLE("Earpiece Amp Switch", MC13783_AUDIO_RX0, 3, 1, 0), + SOC_DOUBLE("Headset Amp Switch", MC13783_AUDIO_RX0, 10, 9, 1, 0), + SOC_DOUBLE("Line out Amp Switch", MC13783_AUDIO_RX0, 16, 15, 1, 0), + + SOC_SINGLE("PCM Capture Mixin Switch", MC13783_AUDIO_RX0, 22, 1, 0), + SOC_SINGLE("Line in Capture Mixin Switch", MC13783_AUDIO_RX0, 23, 1, 0), + + SOC_SINGLE("CODEC Capture Volume", MC13783_AUDIO_RX1, 1, 15, 0), + SOC_SINGLE("CODEC Capture Mixin Switch", MC13783_AUDIO_RX0, 21, 1, 0), + + SOC_SINGLE("Line in Capture Volume", MC13783_AUDIO_RX1, 12, 15, 0), + SOC_SINGLE("Line in Capture Switch", MC13783_AUDIO_RX1, 10, 1, 0), + + SOC_SINGLE("MC1 Capture Bias Switch", MC13783_AUDIO_TX, 0, 1, 0), + SOC_SINGLE("MC2 Capture Bias Switch", MC13783_AUDIO_TX, 1, 1, 0), }; static int mc13783_probe(struct snd_soc_codec *codec) -- cgit v0.10.2 From bb7838d4f13c50df8ef7324f5fd4aeb729269e22 Mon Sep 17 00:00:00 2001 From: Steffen Trumtrar Date: Fri, 11 Oct 2013 12:28:14 +0200 Subject: ASoC: mc13783: add more DAPM routes Add more infrastructure (i.e. routes, muxes, switches) to the mc13783 DAPM. Signed-off-by: Steffen Trumtrar Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c index 2b62737..f5472ad 100644 --- a/sound/soc/codecs/mc13783.c +++ b/sound/soc/codecs/mc13783.c @@ -427,6 +427,29 @@ static const struct snd_kcontrol_new right_input_mux = static const struct snd_kcontrol_new samp_ctl = SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 3, 1, 0); +static const char * const speaker_amp_source_text[] = { + "CODEC", "Right" +}; +static const SOC_ENUM_SINGLE_DECL(speaker_amp_source, MC13783_AUDIO_RX0, 4, + speaker_amp_source_text); +static const struct snd_kcontrol_new speaker_amp_source_mux = + SOC_DAPM_ENUM("Speaker Amp Source MUX", speaker_amp_source); + +static const char * const headset_amp_source_text[] = { + "CODEC", "Mixer" +}; + +static const SOC_ENUM_SINGLE_DECL(headset_amp_source, MC13783_AUDIO_RX0, 11, + headset_amp_source_text); +static const struct snd_kcontrol_new headset_amp_source_mux = + SOC_DAPM_ENUM("Headset Amp Source MUX", headset_amp_source); + +static const struct snd_kcontrol_new cdcout_ctl = + SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 18, 1, 0); + +static const struct snd_kcontrol_new adc_bypass_ctl = + SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_CODEC, 16, 1, 0); + static const struct snd_kcontrol_new lamp_ctl = SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 5, 1, 0); @@ -464,12 +487,22 @@ static const struct snd_soc_dapm_widget mc13783_dapm_widgets[] = { SND_SOC_DAPM_VIRT_MUX("PGA Right Input Mux", SND_SOC_NOPM, 0, 0, &right_input_mux), + SND_SOC_DAPM_MUX("Speaker Amp Source MUX", SND_SOC_NOPM, 0, 0, + &speaker_amp_source_mux), + + SND_SOC_DAPM_MUX("Headset Amp Source MUX", SND_SOC_NOPM, 0, 0, + &headset_amp_source_mux), + SND_SOC_DAPM_PGA("PGA Left Input", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("PGA Right Input", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_ADC("ADC", "Capture", MC13783_AUDIO_CODEC, 11, 0), SND_SOC_DAPM_SUPPLY("ADC_Reset", MC13783_AUDIO_CODEC, 15, 0, NULL, 0), + SND_SOC_DAPM_PGA("Voice CODEC PGA", MC13783_AUDIO_RX1, 0, 0, NULL, 0), + SND_SOC_DAPM_SWITCH("Voice CODEC Bypass", MC13783_AUDIO_CODEC, 16, 0, + &adc_bypass_ctl), + /* Output */ SND_SOC_DAPM_SUPPLY("DAC_E", MC13783_AUDIO_DAC, 11, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("DAC_Reset", MC13783_AUDIO_DAC, 15, 0, NULL, 0), @@ -477,10 +510,15 @@ static const struct snd_soc_dapm_widget mc13783_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("RXOUTR"), SND_SOC_DAPM_OUTPUT("HSL"), SND_SOC_DAPM_OUTPUT("HSR"), + SND_SOC_DAPM_OUTPUT("LSPL"), SND_SOC_DAPM_OUTPUT("LSP"), SND_SOC_DAPM_OUTPUT("SP"), + SND_SOC_DAPM_OUTPUT("CDCOUT"), - SND_SOC_DAPM_SWITCH("Speaker Amp", MC13783_AUDIO_RX0, 3, 0, &samp_ctl), + SND_SOC_DAPM_SWITCH("CDCOUT Switch", MC13783_AUDIO_RX0, 18, 0, + &cdcout_ctl), + SND_SOC_DAPM_SWITCH("Speaker Amp Switch", MC13783_AUDIO_RX0, 3, 0, + &samp_ctl), SND_SOC_DAPM_SWITCH("Loudspeaker Amp", SND_SOC_NOPM, 0, 0, &lamp_ctl), SND_SOC_DAPM_SWITCH("Headset Amp Left", MC13783_AUDIO_RX0, 10, 0, &hlamp_ctl), @@ -515,20 +553,28 @@ static struct snd_soc_dapm_route mc13783_routes[] = { { "ADC", NULL, "PGA Right Input"}, { "ADC", NULL, "ADC_Reset"}, + { "Voice CODEC PGA", "Voice CODEC Bypass", "ADC" }, + + { "Speaker Amp Source MUX", "CODEC", "Voice CODEC PGA"}, + { "Speaker Amp Source MUX", "Right", "DAC PGA"}, + + { "Headset Amp Source MUX", "CODEC", "Voice CODEC PGA"}, + { "Headset Amp Source MUX", "Mixer", "DAC PGA"}, + /* Output */ { "HSL", NULL, "Headset Amp Left" }, { "HSR", NULL, "Headset Amp Right"}, { "RXOUTL", NULL, "Line out Amp Left"}, { "RXOUTR", NULL, "Line out Amp Right"}, - { "SP", NULL, "Speaker Amp"}, - { "Speaker Amp", NULL, "DAC PGA"}, - { "LSP", NULL, "DAC PGA"}, - { "Headset Amp Left", NULL, "DAC PGA"}, - { "Headset Amp Right", NULL, "DAC PGA"}, + { "SP", "Speaker Amp Switch", "Speaker Amp Source MUX"}, + { "LSP", "Loudspeaker Amp", "Speaker Amp Source MUX"}, + { "HSL", "Headset Amp Left", "Headset Amp Source MUX"}, + { "HSR", "Headset Amp Right", "Headset Amp Source MUX"}, { "Line out Amp Left", NULL, "DAC PGA"}, { "Line out Amp Right", NULL, "DAC PGA"}, { "DAC PGA", NULL, "DAC"}, { "DAC", NULL, "DAC_E"}, + { "CDCOUT", "CDCOUT Switch", "Voice CODEC PGA"}, }; static const char * const mc13783_3d_mixer[] = {"Stereo", "Phase Mix", -- cgit v0.10.2 From e277e656804c85a0729d4fd8cdd3c8ab3e6b3b86 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 11 Oct 2013 09:30:24 +0800 Subject: regulator: Remove max_uV from struct regulator_linear_range linear ranges means each range has linear voltage settings. So we can calculate max_uV for each linear range in regulator core rather than set the max_uV field in drivers. Signed-off-by: Axel Lin Signed-off-by: Mark Brown diff --git a/drivers/regulator/88pm800.c b/drivers/regulator/88pm800.c index 3459f60..22ba4c4 100644 --- a/drivers/regulator/88pm800.c +++ b/drivers/regulator/88pm800.c @@ -141,18 +141,16 @@ struct pm800_regulators { /* Ranges are sorted in ascending order. */ static const struct regulator_linear_range buck1_volt_range[] = { - { .min_uV = 600000, .max_uV = 1587500, .min_sel = 0, .max_sel = 0x4f, - .uV_step = 12500 }, - { .min_uV = 1600000, .max_uV = 1800000, .min_sel = 0x50, - .max_sel = 0x54, .uV_step = 50000 }, + { .min_uV = 600000, .min_sel = 0, .max_sel = 0x4f, .uV_step = 12500 }, + { .min_uV = 1600000, .min_sel = 0x50, .max_sel = 0x54, + .uV_step = 50000 }, }; /* BUCK 2~5 have same ranges. */ static const struct regulator_linear_range buck2_5_volt_range[] = { - { .min_uV = 600000, .max_uV = 1587500, .min_sel = 0, .max_sel = 0x4f, - .uV_step = 12500 }, - { .min_uV = 1600000, .max_uV = 3300000, .min_sel = 0x50, - .max_sel = 0x72, .uV_step = 50000 }, + { .min_uV = 600000, .min_sel = 0, .max_sel = 0x4f, .uV_step = 12500 }, + { .min_uV = 1600000, .min_sel = 0x50, .max_sel = 0x72, + .uV_step = 50000 }, }; static const unsigned int ldo1_volt_table[] = { diff --git a/drivers/regulator/as3711-regulator.c b/drivers/regulator/as3711-regulator.c index 8406cd7..d0a97e5 100644 --- a/drivers/regulator/as3711-regulator.c +++ b/drivers/regulator/as3711-regulator.c @@ -117,26 +117,23 @@ static struct regulator_ops as3711_dldo_ops = { }; static const struct regulator_linear_range as3711_sd_ranges[] = { - { .min_uV = 612500, .max_uV = 1400000, - .min_sel = 0x1, .max_sel = 0x40, .uV_step = 12500 }, - { .min_uV = 1425000, .max_uV = 2600000, - .min_sel = 0x41, .max_sel = 0x70, .uV_step = 25000 }, - { .min_uV = 2650000, .max_uV = 3350000, - .min_sel = 0x71, .max_sel = 0x7f, .uV_step = 50000 }, + { .min_uV = 612500, .min_sel = 0x1, .max_sel = 0x40, .uV_step = 12500 }, + { .min_uV = 1425000, .min_sel = 0x41, .max_sel = 0x70, + .uV_step = 25000 }, + { .min_uV = 2650000, .min_sel = 0x71, .max_sel = 0x7f, + .uV_step = 50000 }, }; static const struct regulator_linear_range as3711_aldo_ranges[] = { - { .min_uV = 1200000, .max_uV = 1950000, - .min_sel = 0, .max_sel = 0xf, .uV_step = 50000 }, - { .min_uV = 1800000, .max_uV = 3300000, - .min_sel = 0x10, .max_sel = 0x1f, .uV_step = 100000 }, + { .min_uV = 1200000, .min_sel = 0, .max_sel = 0xf, .uV_step = 50000 }, + { .min_uV = 1800000, .min_sel = 0x10, .max_sel = 0x1f, + .uV_step = 100000 }, }; static const struct regulator_linear_range as3711_dldo_ranges[] = { - { .min_uV = 900000, .max_uV = 1700000, - .min_sel = 0, .max_sel = 0x10, .uV_step = 50000 }, - { .min_uV = 1750000, .max_uV = 3300000, - .min_sel = 0x20, .max_sel = 0x3f, .uV_step = 50000 }, + { .min_uV = 900000, .min_sel = 0, .max_sel = 0x10, .uV_step = 50000 }, + { .min_uV = 1750000, .min_sel = 0x20, .max_sel = 0x3f, + .uV_step = 50000 }, }; #define AS3711_REG(_id, _en_reg, _en_bit, _vmask, _vshift, _min_uV, _max_uV, _sfx) \ diff --git a/drivers/regulator/as3722-regulator.c b/drivers/regulator/as3722-regulator.c index d7b71a9..240ae6d 100644 --- a/drivers/regulator/as3722-regulator.c +++ b/drivers/regulator/as3722-regulator.c @@ -441,7 +441,6 @@ static struct regulator_ops as3722_ldo3_extcntrl_ops = { .max_sel = _max_sel, \ .uV_step = _step_uV, \ .min_uV = _min_uV, \ - .max_uV = _min_uV + (_max_sel - _min_sel) * _step_uV, \ } static const struct regulator_linear_range as3722_ldo_ranges[] = { diff --git a/drivers/regulator/da903x.c b/drivers/regulator/da903x.c index f06854c..90d55e0 100644 --- a/drivers/regulator/da903x.c +++ b/drivers/regulator/da903x.c @@ -253,10 +253,8 @@ static int da9034_set_dvc_voltage_sel(struct regulator_dev *rdev, } static const struct regulator_linear_range da9034_ldo12_ranges[] = { - { .min_uV = 1700000, .max_uV = 2050000, .min_sel = 0, .max_sel = 7, - .uV_step = 50000 }, - { .min_uV = 2700000, .max_uV = 3050000, .min_sel = 8, .max_sel = 15, - .uV_step = 50000 }, + { .min_uV = 1700000, .min_sel = 0, .max_sel = 7, .uV_step = 50000 }, + { .min_uV = 2700000, .min_sel = 8, .max_sel = 15, .uV_step = 50000 }, }; static struct regulator_ops da903x_regulator_ldo_ops = { diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c index 6e30df1..e221a27 100644 --- a/drivers/regulator/helpers.c +++ b/drivers/regulator/helpers.c @@ -284,9 +284,13 @@ int regulator_map_voltage_linear_range(struct regulator_dev *rdev, } for (i = 0; i < rdev->desc->n_linear_ranges; i++) { + int linear_max_uV; + range = &rdev->desc->linear_ranges[i]; + linear_max_uV = range->min_uV + + (range->max_sel - range->min_sel) * range->uV_step; - if (!(min_uV <= range->max_uV && max_uV >= range->min_uV)) + if (!(min_uV <= linear_max_uV && max_uV >= range->min_uV)) continue; if (min_uV <= range->min_uV) diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index 90861d6..484866f 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -52,25 +52,17 @@ static const unsigned int LDO1_VSEL_table[] = { }; static const struct regulator_linear_range tps65217_uv1_ranges[] = { - { .min_uV = 900000, .max_uV = 1500000, .min_sel = 0, .max_sel = 24, - .uV_step = 25000 }, - { .min_uV = 1550000, .max_uV = 1800000, .min_sel = 25, .max_sel = 30, - .uV_step = 50000 }, - { .min_uV = 1850000, .max_uV = 2900000, .min_sel = 31, .max_sel = 52, - .uV_step = 50000 }, - { .min_uV = 3000000, .max_uV = 3200000, .min_sel = 53, .max_sel = 55, - .uV_step = 100000 }, - { .min_uV = 3300000, .max_uV = 3300000, .min_sel = 56, .max_sel = 62, - .uV_step = 0 }, + { .min_uV = 900000, .min_sel = 0, .max_sel = 24, .uV_step = 25000 }, + { .min_uV = 1550000, .min_sel = 25, .max_sel = 30, .uV_step = 50000 }, + { .min_uV = 1850000, .min_sel = 31, .max_sel = 52, .uV_step = 50000 }, + { .min_uV = 3000000, .min_sel = 53, .max_sel = 55, .uV_step = 100000 }, + { .min_uV = 3300000, .min_sel = 56, .max_sel = 62, .uV_step = 0 }, }; static const struct regulator_linear_range tps65217_uv2_ranges[] = { - { .min_uV = 1500000, .max_uV = 1900000, .min_sel = 0, .max_sel = 8, - .uV_step = 50000 }, - { .min_uV = 2000000, .max_uV = 2400000, .min_sel = 9, .max_sel = 13, - .uV_step = 100000 }, - { .min_uV = 2450000, .max_uV = 3300000, .min_sel = 14, .max_sel = 31, - .uV_step = 50000 }, + { .min_uV = 1500000, .min_sel = 0, .max_sel = 8, .uV_step = 50000 }, + { .min_uV = 2000000, .min_sel = 9, .max_sel = 13, .uV_step = 100000 }, + { .min_uV = 2450000, .min_sel = 14, .max_sel = 31, .uV_step = 50000 }, }; static int tps65217_pmic_enable(struct regulator_dev *dev) diff --git a/drivers/regulator/tps65912-regulator.c b/drivers/regulator/tps65912-regulator.c index 281e52a..9fc87d8 100644 --- a/drivers/regulator/tps65912-regulator.c +++ b/drivers/regulator/tps65912-regulator.c @@ -119,12 +119,9 @@ struct tps65912_reg { }; static const struct regulator_linear_range tps65912_ldo_ranges[] = { - { .min_uV = 800000, .max_uV = 1600000, .min_sel = 0, .max_sel = 32, - .uV_step = 25000 }, - { .min_uV = 1650000, .max_uV = 3000000, .min_sel = 33, .max_sel = 60, - .uV_step = 50000 }, - { .min_uV = 3100000, .max_uV = 3300000, .min_sel = 61, .max_sel = 63, - .uV_step = 100000 }, + { .min_uV = 800000, .min_sel = 0, .max_sel = 32, .uV_step = 25000 }, + { .min_uV = 1650000, .min_sel = 33, .max_sel = 60, .uV_step = 50000 }, + { .min_uV = 3100000, .min_sel = 61, .max_sel = 63, .uV_step = 100000 }, }; static int tps65912_get_range(struct tps65912_reg *pmic, int id) diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c index 2205fbc..a958140 100644 --- a/drivers/regulator/wm831x-ldo.c +++ b/drivers/regulator/wm831x-ldo.c @@ -63,10 +63,8 @@ static irqreturn_t wm831x_ldo_uv_irq(int irq, void *data) */ static const struct regulator_linear_range wm831x_gp_ldo_ranges[] = { - { .min_uV = 900000, .max_uV = 1600000, .min_sel = 0, .max_sel = 14, - .uV_step = 50000 }, - { .min_uV = 1700000, .max_uV = 3300000, .min_sel = 15, .max_sel = 31, - .uV_step = 100000 }, + { .min_uV = 900000, .min_sel = 0, .max_sel = 14, .uV_step = 50000 }, + { .min_uV = 1700000, .min_sel = 15, .max_sel = 31, .uV_step = 100000 }, }; static int wm831x_gp_ldo_set_suspend_voltage(struct regulator_dev *rdev, @@ -332,10 +330,8 @@ static struct platform_driver wm831x_gp_ldo_driver = { */ static const struct regulator_linear_range wm831x_aldo_ranges[] = { - { .min_uV = 1000000, .max_uV = 1600000, .min_sel = 0, .max_sel = 12, - .uV_step = 50000 }, - { .min_uV = 1700000, .max_uV = 3500000, .min_sel = 13, .max_sel = 31, - .uV_step = 100000 }, + { .min_uV = 1000000, .min_sel = 0, .max_sel = 12, .uV_step = 50000 }, + { .min_uV = 1700000, .min_sel = 13, .max_sel = 31, .uV_step = 100000 }, }; static int wm831x_aldo_set_suspend_voltage(struct regulator_dev *rdev, diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index 61ca929..de9de26 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -543,10 +543,8 @@ static int wm8350_dcdc_set_suspend_mode(struct regulator_dev *rdev, } static const struct regulator_linear_range wm8350_ldo_ranges[] = { - { .min_uV = 900000, .max_uV = 1650000, .min_sel = 0, .max_sel = 15, - .uV_step = 50000 }, - { .min_uV = 1800000, .max_uV = 3300000, .min_sel = 16, .max_sel = 31, - .uV_step = 100000 }, + { .min_uV = 900000, .min_sel = 0, .max_sel = 15, .uV_step = 50000 }, + { .min_uV = 1800000, .min_sel = 16, .max_sel = 31, .uV_step = 100000 }, }; static int wm8350_ldo_set_suspend_voltage(struct regulator_dev *rdev, int uV) diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c index 58f51be..3352b20 100644 --- a/drivers/regulator/wm8400-regulator.c +++ b/drivers/regulator/wm8400-regulator.c @@ -20,10 +20,8 @@ #include static const struct regulator_linear_range wm8400_ldo_ranges[] = { - { .min_uV = 900000, .max_uV = 1600000, .min_sel = 0, .max_sel = 14, - .uV_step = 50000 }, - { .min_uV = 1700000, .max_uV = 3300000, .min_sel = 15, .max_sel = 31, - .uV_step = 100000 }, + { .min_uV = 900000, .min_sel = 0, .max_sel = 14, .uV_step = 50000 }, + { .min_uV = 1700000, .min_sel = 15, .max_sel = 31, .uV_step = 100000 }, }; static struct regulator_ops wm8400_ldo_ops = { diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 9bdad43..997ff5c 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -46,14 +46,12 @@ enum regulator_status { * regulator_list_linear_range(). * * @min_uV: Lowest voltage in range - * @max_uV: Highest voltage in range * @min_sel: Lowest selector for range * @max_sel: Highest selector for range * @uV_step: Step size */ struct regulator_linear_range { unsigned int min_uV; - unsigned int max_uV; unsigned int min_sel; unsigned int max_sel; unsigned int uV_step; -- cgit v0.10.2 From 8828bae464b129abed95b748263f1ab53bdc5755 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 11 Oct 2013 09:32:18 +0800 Subject: regulator: Add REGULATOR_LINEAR_RANGE macro Add REGULATOR_LINEAR_RANGE macro and convert regulator drivers to use it. Signed-off-by: Axel Lin Signed-off-by: Mark Brown diff --git a/drivers/regulator/88pm800.c b/drivers/regulator/88pm800.c index 22ba4c4..d333f7e 100644 --- a/drivers/regulator/88pm800.c +++ b/drivers/regulator/88pm800.c @@ -141,16 +141,14 @@ struct pm800_regulators { /* Ranges are sorted in ascending order. */ static const struct regulator_linear_range buck1_volt_range[] = { - { .min_uV = 600000, .min_sel = 0, .max_sel = 0x4f, .uV_step = 12500 }, - { .min_uV = 1600000, .min_sel = 0x50, .max_sel = 0x54, - .uV_step = 50000 }, + REGULATOR_LINEAR_RANGE(600000, 0, 0x4f, 12500), + REGULATOR_LINEAR_RANGE(1600000, 0x50, 0x54, 50000), }; /* BUCK 2~5 have same ranges. */ static const struct regulator_linear_range buck2_5_volt_range[] = { - { .min_uV = 600000, .min_sel = 0, .max_sel = 0x4f, .uV_step = 12500 }, - { .min_uV = 1600000, .min_sel = 0x50, .max_sel = 0x72, - .uV_step = 50000 }, + REGULATOR_LINEAR_RANGE(600000, 0, 0x4f, 12500), + REGULATOR_LINEAR_RANGE(1600000, 0x50, 0x72, 50000), }; static const unsigned int ldo1_volt_table[] = { diff --git a/drivers/regulator/as3711-regulator.c b/drivers/regulator/as3711-regulator.c index d0a97e5..f8524f9 100644 --- a/drivers/regulator/as3711-regulator.c +++ b/drivers/regulator/as3711-regulator.c @@ -117,23 +117,19 @@ static struct regulator_ops as3711_dldo_ops = { }; static const struct regulator_linear_range as3711_sd_ranges[] = { - { .min_uV = 612500, .min_sel = 0x1, .max_sel = 0x40, .uV_step = 12500 }, - { .min_uV = 1425000, .min_sel = 0x41, .max_sel = 0x70, - .uV_step = 25000 }, - { .min_uV = 2650000, .min_sel = 0x71, .max_sel = 0x7f, - .uV_step = 50000 }, + REGULATOR_LINEAR_RANGE(612500, 0x1, 0x40, 12500), + REGULATOR_LINEAR_RANGE(1425000, 0x41, 0x70, 25000), + REGULATOR_LINEAR_RANGE(2650000, 0x71, 0x7f, 50000), }; static const struct regulator_linear_range as3711_aldo_ranges[] = { - { .min_uV = 1200000, .min_sel = 0, .max_sel = 0xf, .uV_step = 50000 }, - { .min_uV = 1800000, .min_sel = 0x10, .max_sel = 0x1f, - .uV_step = 100000 }, + REGULATOR_LINEAR_RANGE(1200000, 0, 0xf, 50000), + REGULATOR_LINEAR_RANGE(1800000, 0x10, 0x1f, 100000), }; static const struct regulator_linear_range as3711_dldo_ranges[] = { - { .min_uV = 900000, .min_sel = 0, .max_sel = 0x10, .uV_step = 50000 }, - { .min_uV = 1750000, .min_sel = 0x20, .max_sel = 0x3f, - .uV_step = 50000 }, + REGULATOR_LINEAR_RANGE(900000, 0, 0x10, 50000), + REGULATOR_LINEAR_RANGE(1750000, 0x20, 0x3f, 50000), }; #define AS3711_REG(_id, _en_reg, _en_bit, _vmask, _vshift, _min_uV, _max_uV, _sfx) \ diff --git a/drivers/regulator/as3722-regulator.c b/drivers/regulator/as3722-regulator.c index 240ae6d..5917fe3 100644 --- a/drivers/regulator/as3722-regulator.c +++ b/drivers/regulator/as3722-regulator.c @@ -435,17 +435,9 @@ static struct regulator_ops as3722_ldo3_extcntrl_ops = { .get_current_limit = as3722_ldo3_get_current_limit, }; -#define regulator_lin_range(_min_sel, _max_sel, _min_uV, _step_uV) \ - { \ - .min_sel = _min_sel, \ - .max_sel = _max_sel, \ - .uV_step = _step_uV, \ - .min_uV = _min_uV, \ - } - static const struct regulator_linear_range as3722_ldo_ranges[] = { - regulator_lin_range(0x01, 0x24, 825000, 25000), - regulator_lin_range(0x40, 0x7F, 1725000, 25000), + REGULATOR_LINEAR_RANGE(825000, 0x01, 0x24, 25000), + REGULATOR_LINEAR_RANGE(1725000, 0x40, 0x7F, 25000), }; static struct regulator_ops as3722_ldo_ops = { @@ -604,9 +596,9 @@ static int as3722_sd016_set_current_limit(struct regulator_dev *rdev, } static const struct regulator_linear_range as3722_sd2345_ranges[] = { - regulator_lin_range(0x01, 0x40, 612500, 12500), - regulator_lin_range(0x41, 0x70, 1425000, 25000), - regulator_lin_range(0x71, 0x7F, 2650000, 50000), + REGULATOR_LINEAR_RANGE(612500, 0x01, 0x40, 12500), + REGULATOR_LINEAR_RANGE(1425000, 0x41, 0x70, 25000), + REGULATOR_LINEAR_RANGE(2650000, 0x71, 0x7F, 50000), }; static struct regulator_ops as3722_sd016_ops = { diff --git a/drivers/regulator/da903x.c b/drivers/regulator/da903x.c index 90d55e0..6d1b799 100644 --- a/drivers/regulator/da903x.c +++ b/drivers/regulator/da903x.c @@ -253,8 +253,8 @@ static int da9034_set_dvc_voltage_sel(struct regulator_dev *rdev, } static const struct regulator_linear_range da9034_ldo12_ranges[] = { - { .min_uV = 1700000, .min_sel = 0, .max_sel = 7, .uV_step = 50000 }, - { .min_uV = 2700000, .min_sel = 8, .max_sel = 15, .uV_step = 50000 }, + REGULATOR_LINEAR_RANGE(1700000, 0, 7, 50000), + REGULATOR_LINEAR_RANGE(2700000, 8, 15, 50000), }; static struct regulator_ops da903x_regulator_ldo_ops = { diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index 484866f..bbc7277 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -52,17 +52,17 @@ static const unsigned int LDO1_VSEL_table[] = { }; static const struct regulator_linear_range tps65217_uv1_ranges[] = { - { .min_uV = 900000, .min_sel = 0, .max_sel = 24, .uV_step = 25000 }, - { .min_uV = 1550000, .min_sel = 25, .max_sel = 30, .uV_step = 50000 }, - { .min_uV = 1850000, .min_sel = 31, .max_sel = 52, .uV_step = 50000 }, - { .min_uV = 3000000, .min_sel = 53, .max_sel = 55, .uV_step = 100000 }, - { .min_uV = 3300000, .min_sel = 56, .max_sel = 62, .uV_step = 0 }, + REGULATOR_LINEAR_RANGE(900000, 0, 24, 25000), + REGULATOR_LINEAR_RANGE(1550000, 25, 30, 50000), + REGULATOR_LINEAR_RANGE(1850000, 31, 52, 50000), + REGULATOR_LINEAR_RANGE(3000000, 53, 55, 100000), + REGULATOR_LINEAR_RANGE(3300000, 56, 62, 0), }; static const struct regulator_linear_range tps65217_uv2_ranges[] = { - { .min_uV = 1500000, .min_sel = 0, .max_sel = 8, .uV_step = 50000 }, - { .min_uV = 2000000, .min_sel = 9, .max_sel = 13, .uV_step = 100000 }, - { .min_uV = 2450000, .min_sel = 14, .max_sel = 31, .uV_step = 50000 }, + REGULATOR_LINEAR_RANGE(1500000, 0, 8, 50000), + REGULATOR_LINEAR_RANGE(2000000, 9, 13, 100000), + REGULATOR_LINEAR_RANGE(2450000, 14, 31, 50000), }; static int tps65217_pmic_enable(struct regulator_dev *dev) diff --git a/drivers/regulator/tps65912-regulator.c b/drivers/regulator/tps65912-regulator.c index 9fc87d8..697eab8 100644 --- a/drivers/regulator/tps65912-regulator.c +++ b/drivers/regulator/tps65912-regulator.c @@ -119,9 +119,9 @@ struct tps65912_reg { }; static const struct regulator_linear_range tps65912_ldo_ranges[] = { - { .min_uV = 800000, .min_sel = 0, .max_sel = 32, .uV_step = 25000 }, - { .min_uV = 1650000, .min_sel = 33, .max_sel = 60, .uV_step = 50000 }, - { .min_uV = 3100000, .min_sel = 61, .max_sel = 63, .uV_step = 100000 }, + REGULATOR_LINEAR_RANGE(800000, 0, 32, 25000), + REGULATOR_LINEAR_RANGE(1650000, 33, 60, 50000), + REGULATOR_LINEAR_RANGE(3100000, 61, 63, 100000), }; static int tps65912_get_range(struct tps65912_reg *pmic, int id) diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c index a958140..9111b65 100644 --- a/drivers/regulator/wm831x-ldo.c +++ b/drivers/regulator/wm831x-ldo.c @@ -63,8 +63,8 @@ static irqreturn_t wm831x_ldo_uv_irq(int irq, void *data) */ static const struct regulator_linear_range wm831x_gp_ldo_ranges[] = { - { .min_uV = 900000, .min_sel = 0, .max_sel = 14, .uV_step = 50000 }, - { .min_uV = 1700000, .min_sel = 15, .max_sel = 31, .uV_step = 100000 }, + REGULATOR_LINEAR_RANGE(900000, 0, 14, 50000), + REGULATOR_LINEAR_RANGE(1700000, 15, 31, 100000), }; static int wm831x_gp_ldo_set_suspend_voltage(struct regulator_dev *rdev, @@ -330,8 +330,8 @@ static struct platform_driver wm831x_gp_ldo_driver = { */ static const struct regulator_linear_range wm831x_aldo_ranges[] = { - { .min_uV = 1000000, .min_sel = 0, .max_sel = 12, .uV_step = 50000 }, - { .min_uV = 1700000, .min_sel = 13, .max_sel = 31, .uV_step = 100000 }, + REGULATOR_LINEAR_RANGE(1000000, 0, 12, 50000), + REGULATOR_LINEAR_RANGE(1700000, 13, 31, 100000), }; static int wm831x_aldo_set_suspend_voltage(struct regulator_dev *rdev, diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index de9de26..3827539 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -543,8 +543,8 @@ static int wm8350_dcdc_set_suspend_mode(struct regulator_dev *rdev, } static const struct regulator_linear_range wm8350_ldo_ranges[] = { - { .min_uV = 900000, .min_sel = 0, .max_sel = 15, .uV_step = 50000 }, - { .min_uV = 1800000, .min_sel = 16, .max_sel = 31, .uV_step = 100000 }, + REGULATOR_LINEAR_RANGE(900000, 0, 15, 50000), + REGULATOR_LINEAR_RANGE(1800000, 16, 31, 100000), }; static int wm8350_ldo_set_suspend_voltage(struct regulator_dev *rdev, int uV) diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c index 3352b20..8eedba2 100644 --- a/drivers/regulator/wm8400-regulator.c +++ b/drivers/regulator/wm8400-regulator.c @@ -20,8 +20,8 @@ #include static const struct regulator_linear_range wm8400_ldo_ranges[] = { - { .min_uV = 900000, .min_sel = 0, .max_sel = 14, .uV_step = 50000 }, - { .min_uV = 1700000, .min_sel = 15, .max_sel = 31, .uV_step = 100000 }, + REGULATOR_LINEAR_RANGE(900000, 0, 14, 50000), + REGULATOR_LINEAR_RANGE(1700000, 15, 31, 100000), }; static struct regulator_ops wm8400_ldo_ops = { diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 997ff5c..edb11b7 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -57,6 +57,15 @@ struct regulator_linear_range { unsigned int uV_step; }; +/* Initialize struct regulator_linear_range */ +#define REGULATOR_LINEAR_RANGE(_min_uV, _min_sel, _max_sel, _step_uV) \ +{ \ + .min_uV = _min_uV, \ + .min_sel = _min_sel, \ + .max_sel = _max_sel, \ + .uV_step = _step_uV, \ +} + /** * struct regulator_ops - regulator operations. * -- cgit v0.10.2 From d56d6b3d7d693581d346b05d089f842b48b7ec0a Mon Sep 17 00:00:00 2001 From: David Cohen Date: Fri, 4 Oct 2013 13:01:40 -0700 Subject: gpio: langwell: add Intel Merrifield support This patch implements a better way to handle multiple SoC's and adds Intel Merrifield support to gpio-langwell. It was based on previous work from Ning Li Signed-off-by: David Cohen Signed-off-by: Fei Yang Signed-off-by: Ning Li Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-langwell.c b/drivers/gpio/gpio-langwell.c index bfa1af1..bf3b959 100644 --- a/drivers/gpio/gpio-langwell.c +++ b/drivers/gpio/gpio-langwell.c @@ -37,6 +37,9 @@ #include #include +#define LNW_IRQ_TYPE_EDGE (1 << 0) +#define LNW_IRQ_TYPE_LEVEL (1 << 1) + /* * Langwell chip has 64 pins and thus there are 2 32bit registers to control * each feature, while Penwell chip has 96 pins for each block, and need 3 32bit @@ -62,6 +65,16 @@ enum GPIO_REG { GAFR, /* alt function */ }; +/* langwell gpio driver data */ +struct lnw_gpio_ddata { + u16 ngpio; /* number of gpio pins */ + u32 gplr_offset; /* offset of first GPLR register from base */ + u32 flis_base; /* base address of FLIS registers */ + u32 flis_len; /* length of FLIS registers */ + u32 (*get_flis_offset)(int gpio); + u32 chip_irq_type; /* chip interrupt type */ +}; + struct lnw_gpio { struct gpio_chip chip; void __iomem *reg_base; @@ -227,13 +240,71 @@ static struct irq_chip lnw_irqchip = { .irq_set_type = lnw_irq_type, }; -static DEFINE_PCI_DEVICE_TABLE(lnw_gpio_ids) = { /* pin number */ - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080f), .driver_data = 64 }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081f), .driver_data = 96 }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081a), .driver_data = 96 }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x08eb), .driver_data = 96 }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x08f7), .driver_data = 96 }, - { 0, } +static const struct lnw_gpio_ddata gpio_lincroft = { + .ngpio = 64, +}; + +static const struct lnw_gpio_ddata gpio_penwell_aon = { + .ngpio = 96, + .chip_irq_type = LNW_IRQ_TYPE_EDGE, +}; + +static const struct lnw_gpio_ddata gpio_penwell_core = { + .ngpio = 96, + .chip_irq_type = LNW_IRQ_TYPE_EDGE, +}; + +static const struct lnw_gpio_ddata gpio_cloverview_aon = { + .ngpio = 96, + .chip_irq_type = LNW_IRQ_TYPE_EDGE | LNW_IRQ_TYPE_LEVEL, +}; + +static const struct lnw_gpio_ddata gpio_cloverview_core = { + .ngpio = 96, + .chip_irq_type = LNW_IRQ_TYPE_EDGE, +}; + +static const struct lnw_gpio_ddata gpio_tangier = { + .ngpio = 192, + .gplr_offset = 4, + .flis_base = 0xff0c0000, + .flis_len = 0x8000, + .get_flis_offset = NULL, + .chip_irq_type = LNW_IRQ_TYPE_EDGE, +}; + +static DEFINE_PCI_DEVICE_TABLE(lnw_gpio_ids) = { + { + /* Lincroft */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080f), + .driver_data = (kernel_ulong_t)&gpio_lincroft, + }, + { + /* Penwell AON */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081f), + .driver_data = (kernel_ulong_t)&gpio_penwell_aon, + }, + { + /* Penwell Core */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081a), + .driver_data = (kernel_ulong_t)&gpio_penwell_core, + }, + { + /* Cloverview Aon */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x08eb), + .driver_data = (kernel_ulong_t)&gpio_cloverview_aon, + }, + { + /* Cloverview Core */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x08f7), + .driver_data = (kernel_ulong_t)&gpio_cloverview_core, + }, + { + /* Tangier */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x1199), + .driver_data = (kernel_ulong_t)&gpio_tangier, + }, + { 0 } }; MODULE_DEVICE_TABLE(pci, lnw_gpio_ids); @@ -316,7 +387,7 @@ static int lnw_gpio_probe(struct pci_dev *pdev, u32 gpio_base; u32 irq_base; int retval; - int ngpio = id->driver_data; + struct lnw_gpio_ddata *ddata = (struct lnw_gpio_ddata *)id->driver_data; retval = pcim_enable_device(pdev); if (retval) @@ -351,14 +422,14 @@ static int lnw_gpio_probe(struct pci_dev *pdev, lnw->chip.set = lnw_gpio_set; lnw->chip.to_irq = lnw_gpio_to_irq; lnw->chip.base = gpio_base; - lnw->chip.ngpio = ngpio; + lnw->chip.ngpio = ddata->ngpio; lnw->chip.can_sleep = 0; lnw->pdev = pdev; spin_lock_init(&lnw->lock); - lnw->domain = irq_domain_add_simple(pdev->dev.of_node, ngpio, irq_base, - &lnw_gpio_irq_ops, lnw); + lnw->domain = irq_domain_add_simple(pdev->dev.of_node, ddata->ngpio, + irq_base, &lnw_gpio_irq_ops, lnw); if (!lnw->domain) return -ENOMEM; -- cgit v0.10.2 From 84743ea3690703efdd033f5435cf4211057e0324 Mon Sep 17 00:00:00 2001 From: David Cohen Date: Fri, 4 Oct 2013 13:01:41 -0700 Subject: gpio: rename gpio-langwell to gpio-intel-mid gpio-langwell is a deprecated name. Despite the driver was made initially for Langwell, it supports now other Intel Mid SoC's. This patch does no change beside the file renaming with Kconfig/Makefile update. Signed-off-by: David Cohen Signed-off-by: Linus Walleij diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index c8b02a5..92e258c 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -621,12 +621,12 @@ config GPIO_AMD8111 If unsure, say N -config GPIO_LANGWELL - bool "Intel Langwell/Penwell GPIO support" +config GPIO_INTEL_MID + bool "Intel Mid GPIO support" depends on PCI && X86 select IRQ_DOMAIN help - Say Y here to support Intel Langwell/Penwell GPIO. + Say Y here to support Intel Mid GPIO. config GPIO_PCH tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7223/ML7831) GPIO" diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 5c353df..7655a36 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -34,7 +34,7 @@ obj-$(CONFIG_GPIO_IT8761E) += gpio-it8761e.o obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o obj-$(CONFIG_GPIO_KEMPLD) += gpio-kempld.o obj-$(CONFIG_ARCH_KS8695) += gpio-ks8695.o -obj-$(CONFIG_GPIO_LANGWELL) += gpio-langwell.o +obj-$(CONFIG_GPIO_INTEL_MID) += gpio-intel-mid.o obj-$(CONFIG_ARCH_LPC32XX) += gpio-lpc32xx.o obj-$(CONFIG_GPIO_LYNXPOINT) += gpio-lynxpoint.o obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o diff --git a/drivers/gpio/gpio-intel-mid.c b/drivers/gpio/gpio-intel-mid.c new file mode 100644 index 0000000..bf3b959 --- /dev/null +++ b/drivers/gpio/gpio-intel-mid.c @@ -0,0 +1,468 @@ +/* + * Moorestown platform Langwell chip GPIO driver + * + * Copyright (c) 2008, 2009, 2013, Intel Corporation. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * Moorestown platform Langwell chip. + * Medfield platform Penwell chip. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LNW_IRQ_TYPE_EDGE (1 << 0) +#define LNW_IRQ_TYPE_LEVEL (1 << 1) + +/* + * Langwell chip has 64 pins and thus there are 2 32bit registers to control + * each feature, while Penwell chip has 96 pins for each block, and need 3 32bit + * registers to control them, so we only define the order here instead of a + * structure, to get a bit offset for a pin (use GPDR as an example): + * + * nreg = ngpio / 32; + * reg = offset / 32; + * bit = offset % 32; + * reg_addr = reg_base + GPDR * nreg * 4 + reg * 4; + * + * so the bit of reg_addr is to control pin offset's GPDR feature +*/ + +enum GPIO_REG { + GPLR = 0, /* pin level read-only */ + GPDR, /* pin direction */ + GPSR, /* pin set */ + GPCR, /* pin clear */ + GRER, /* rising edge detect */ + GFER, /* falling edge detect */ + GEDR, /* edge detect result */ + GAFR, /* alt function */ +}; + +/* langwell gpio driver data */ +struct lnw_gpio_ddata { + u16 ngpio; /* number of gpio pins */ + u32 gplr_offset; /* offset of first GPLR register from base */ + u32 flis_base; /* base address of FLIS registers */ + u32 flis_len; /* length of FLIS registers */ + u32 (*get_flis_offset)(int gpio); + u32 chip_irq_type; /* chip interrupt type */ +}; + +struct lnw_gpio { + struct gpio_chip chip; + void __iomem *reg_base; + spinlock_t lock; + struct pci_dev *pdev; + struct irq_domain *domain; +}; + +#define to_lnw_priv(chip) container_of(chip, struct lnw_gpio, chip) + +static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned offset, + enum GPIO_REG reg_type) +{ + struct lnw_gpio *lnw = to_lnw_priv(chip); + unsigned nreg = chip->ngpio / 32; + u8 reg = offset / 32; + + return lnw->reg_base + reg_type * nreg * 4 + reg * 4; +} + +static void __iomem *gpio_reg_2bit(struct gpio_chip *chip, unsigned offset, + enum GPIO_REG reg_type) +{ + struct lnw_gpio *lnw = to_lnw_priv(chip); + unsigned nreg = chip->ngpio / 32; + u8 reg = offset / 16; + + return lnw->reg_base + reg_type * nreg * 4 + reg * 4; +} + +static int lnw_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + void __iomem *gafr = gpio_reg_2bit(chip, offset, GAFR); + u32 value = readl(gafr); + int shift = (offset % 16) << 1, af = (value >> shift) & 3; + + if (af) { + value &= ~(3 << shift); + writel(value, gafr); + } + return 0; +} + +static int lnw_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + void __iomem *gplr = gpio_reg(chip, offset, GPLR); + + return readl(gplr) & BIT(offset % 32); +} + +static void lnw_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + void __iomem *gpsr, *gpcr; + + if (value) { + gpsr = gpio_reg(chip, offset, GPSR); + writel(BIT(offset % 32), gpsr); + } else { + gpcr = gpio_reg(chip, offset, GPCR); + writel(BIT(offset % 32), gpcr); + } +} + +static int lnw_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct lnw_gpio *lnw = to_lnw_priv(chip); + void __iomem *gpdr = gpio_reg(chip, offset, GPDR); + u32 value; + unsigned long flags; + + if (lnw->pdev) + pm_runtime_get(&lnw->pdev->dev); + + spin_lock_irqsave(&lnw->lock, flags); + value = readl(gpdr); + value &= ~BIT(offset % 32); + writel(value, gpdr); + spin_unlock_irqrestore(&lnw->lock, flags); + + if (lnw->pdev) + pm_runtime_put(&lnw->pdev->dev); + + return 0; +} + +static int lnw_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct lnw_gpio *lnw = to_lnw_priv(chip); + void __iomem *gpdr = gpio_reg(chip, offset, GPDR); + unsigned long flags; + + lnw_gpio_set(chip, offset, value); + + if (lnw->pdev) + pm_runtime_get(&lnw->pdev->dev); + + spin_lock_irqsave(&lnw->lock, flags); + value = readl(gpdr); + value |= BIT(offset % 32); + writel(value, gpdr); + spin_unlock_irqrestore(&lnw->lock, flags); + + if (lnw->pdev) + pm_runtime_put(&lnw->pdev->dev); + + return 0; +} + +static int lnw_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct lnw_gpio *lnw = to_lnw_priv(chip); + return irq_create_mapping(lnw->domain, offset); +} + +static int lnw_irq_type(struct irq_data *d, unsigned type) +{ + struct lnw_gpio *lnw = irq_data_get_irq_chip_data(d); + u32 gpio = irqd_to_hwirq(d); + unsigned long flags; + u32 value; + void __iomem *grer = gpio_reg(&lnw->chip, gpio, GRER); + void __iomem *gfer = gpio_reg(&lnw->chip, gpio, GFER); + + if (gpio >= lnw->chip.ngpio) + return -EINVAL; + + if (lnw->pdev) + pm_runtime_get(&lnw->pdev->dev); + + spin_lock_irqsave(&lnw->lock, flags); + if (type & IRQ_TYPE_EDGE_RISING) + value = readl(grer) | BIT(gpio % 32); + else + value = readl(grer) & (~BIT(gpio % 32)); + writel(value, grer); + + if (type & IRQ_TYPE_EDGE_FALLING) + value = readl(gfer) | BIT(gpio % 32); + else + value = readl(gfer) & (~BIT(gpio % 32)); + writel(value, gfer); + spin_unlock_irqrestore(&lnw->lock, flags); + + if (lnw->pdev) + pm_runtime_put(&lnw->pdev->dev); + + return 0; +} + +static void lnw_irq_unmask(struct irq_data *d) +{ +} + +static void lnw_irq_mask(struct irq_data *d) +{ +} + +static struct irq_chip lnw_irqchip = { + .name = "LNW-GPIO", + .irq_mask = lnw_irq_mask, + .irq_unmask = lnw_irq_unmask, + .irq_set_type = lnw_irq_type, +}; + +static const struct lnw_gpio_ddata gpio_lincroft = { + .ngpio = 64, +}; + +static const struct lnw_gpio_ddata gpio_penwell_aon = { + .ngpio = 96, + .chip_irq_type = LNW_IRQ_TYPE_EDGE, +}; + +static const struct lnw_gpio_ddata gpio_penwell_core = { + .ngpio = 96, + .chip_irq_type = LNW_IRQ_TYPE_EDGE, +}; + +static const struct lnw_gpio_ddata gpio_cloverview_aon = { + .ngpio = 96, + .chip_irq_type = LNW_IRQ_TYPE_EDGE | LNW_IRQ_TYPE_LEVEL, +}; + +static const struct lnw_gpio_ddata gpio_cloverview_core = { + .ngpio = 96, + .chip_irq_type = LNW_IRQ_TYPE_EDGE, +}; + +static const struct lnw_gpio_ddata gpio_tangier = { + .ngpio = 192, + .gplr_offset = 4, + .flis_base = 0xff0c0000, + .flis_len = 0x8000, + .get_flis_offset = NULL, + .chip_irq_type = LNW_IRQ_TYPE_EDGE, +}; + +static DEFINE_PCI_DEVICE_TABLE(lnw_gpio_ids) = { + { + /* Lincroft */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080f), + .driver_data = (kernel_ulong_t)&gpio_lincroft, + }, + { + /* Penwell AON */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081f), + .driver_data = (kernel_ulong_t)&gpio_penwell_aon, + }, + { + /* Penwell Core */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081a), + .driver_data = (kernel_ulong_t)&gpio_penwell_core, + }, + { + /* Cloverview Aon */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x08eb), + .driver_data = (kernel_ulong_t)&gpio_cloverview_aon, + }, + { + /* Cloverview Core */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x08f7), + .driver_data = (kernel_ulong_t)&gpio_cloverview_core, + }, + { + /* Tangier */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x1199), + .driver_data = (kernel_ulong_t)&gpio_tangier, + }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, lnw_gpio_ids); + +static void lnw_irq_handler(unsigned irq, struct irq_desc *desc) +{ + struct irq_data *data = irq_desc_get_irq_data(desc); + struct lnw_gpio *lnw = irq_data_get_irq_handler_data(data); + struct irq_chip *chip = irq_data_get_irq_chip(data); + u32 base, gpio, mask; + unsigned long pending; + void __iomem *gedr; + + /* check GPIO controller to check which pin triggered the interrupt */ + for (base = 0; base < lnw->chip.ngpio; base += 32) { + gedr = gpio_reg(&lnw->chip, base, GEDR); + while ((pending = readl(gedr))) { + gpio = __ffs(pending); + mask = BIT(gpio); + /* Clear before handling so we can't lose an edge */ + writel(mask, gedr); + generic_handle_irq(irq_find_mapping(lnw->domain, + base + gpio)); + } + } + + chip->irq_eoi(data); +} + +static void lnw_irq_init_hw(struct lnw_gpio *lnw) +{ + void __iomem *reg; + unsigned base; + + for (base = 0; base < lnw->chip.ngpio; base += 32) { + /* Clear the rising-edge detect register */ + reg = gpio_reg(&lnw->chip, base, GRER); + writel(0, reg); + /* Clear the falling-edge detect register */ + reg = gpio_reg(&lnw->chip, base, GFER); + writel(0, reg); + /* Clear the edge detect status register */ + reg = gpio_reg(&lnw->chip, base, GEDR); + writel(~0, reg); + } +} + +static int lnw_gpio_irq_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hw) +{ + struct lnw_gpio *lnw = d->host_data; + + irq_set_chip_and_handler_name(virq, &lnw_irqchip, handle_simple_irq, + "demux"); + irq_set_chip_data(virq, lnw); + irq_set_irq_type(virq, IRQ_TYPE_NONE); + + return 0; +} + +static const struct irq_domain_ops lnw_gpio_irq_ops = { + .map = lnw_gpio_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + +static int lnw_gpio_runtime_idle(struct device *dev) +{ + pm_schedule_suspend(dev, 500); + return -EBUSY; +} + +static const struct dev_pm_ops lnw_gpio_pm_ops = { + SET_RUNTIME_PM_OPS(NULL, NULL, lnw_gpio_runtime_idle) +}; + +static int lnw_gpio_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + void __iomem *base; + struct lnw_gpio *lnw; + u32 gpio_base; + u32 irq_base; + int retval; + struct lnw_gpio_ddata *ddata = (struct lnw_gpio_ddata *)id->driver_data; + + retval = pcim_enable_device(pdev); + if (retval) + return retval; + + retval = pcim_iomap_regions(pdev, 1 << 0 | 1 << 1, pci_name(pdev)); + if (retval) { + dev_err(&pdev->dev, "I/O memory mapping error\n"); + return retval; + } + + base = pcim_iomap_table(pdev)[1]; + + irq_base = readl(base); + gpio_base = readl(sizeof(u32) + base); + + /* release the IO mapping, since we already get the info from bar1 */ + pcim_iounmap_regions(pdev, 1 << 1); + + lnw = devm_kzalloc(&pdev->dev, sizeof(*lnw), GFP_KERNEL); + if (!lnw) { + dev_err(&pdev->dev, "can't allocate chip data\n"); + return -ENOMEM; + } + + lnw->reg_base = pcim_iomap_table(pdev)[0]; + lnw->chip.label = dev_name(&pdev->dev); + lnw->chip.request = lnw_gpio_request; + lnw->chip.direction_input = lnw_gpio_direction_input; + lnw->chip.direction_output = lnw_gpio_direction_output; + lnw->chip.get = lnw_gpio_get; + lnw->chip.set = lnw_gpio_set; + lnw->chip.to_irq = lnw_gpio_to_irq; + lnw->chip.base = gpio_base; + lnw->chip.ngpio = ddata->ngpio; + lnw->chip.can_sleep = 0; + lnw->pdev = pdev; + + spin_lock_init(&lnw->lock); + + lnw->domain = irq_domain_add_simple(pdev->dev.of_node, ddata->ngpio, + irq_base, &lnw_gpio_irq_ops, lnw); + if (!lnw->domain) + return -ENOMEM; + + pci_set_drvdata(pdev, lnw); + retval = gpiochip_add(&lnw->chip); + if (retval) { + dev_err(&pdev->dev, "gpiochip_add error %d\n", retval); + return retval; + } + + lnw_irq_init_hw(lnw); + + irq_set_handler_data(pdev->irq, lnw); + irq_set_chained_handler(pdev->irq, lnw_irq_handler); + + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_allow(&pdev->dev); + + return 0; +} + +static struct pci_driver lnw_gpio_driver = { + .name = "langwell_gpio", + .id_table = lnw_gpio_ids, + .probe = lnw_gpio_probe, + .driver = { + .pm = &lnw_gpio_pm_ops, + }, +}; + +static int __init lnw_gpio_init(void) +{ + return pci_register_driver(&lnw_gpio_driver); +} + +device_initcall(lnw_gpio_init); diff --git a/drivers/gpio/gpio-langwell.c b/drivers/gpio/gpio-langwell.c deleted file mode 100644 index bf3b959..0000000 --- a/drivers/gpio/gpio-langwell.c +++ /dev/null @@ -1,468 +0,0 @@ -/* - * Moorestown platform Langwell chip GPIO driver - * - * Copyright (c) 2008, 2009, 2013, Intel Corporation. - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* Supports: - * Moorestown platform Langwell chip. - * Medfield platform Penwell chip. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define LNW_IRQ_TYPE_EDGE (1 << 0) -#define LNW_IRQ_TYPE_LEVEL (1 << 1) - -/* - * Langwell chip has 64 pins and thus there are 2 32bit registers to control - * each feature, while Penwell chip has 96 pins for each block, and need 3 32bit - * registers to control them, so we only define the order here instead of a - * structure, to get a bit offset for a pin (use GPDR as an example): - * - * nreg = ngpio / 32; - * reg = offset / 32; - * bit = offset % 32; - * reg_addr = reg_base + GPDR * nreg * 4 + reg * 4; - * - * so the bit of reg_addr is to control pin offset's GPDR feature -*/ - -enum GPIO_REG { - GPLR = 0, /* pin level read-only */ - GPDR, /* pin direction */ - GPSR, /* pin set */ - GPCR, /* pin clear */ - GRER, /* rising edge detect */ - GFER, /* falling edge detect */ - GEDR, /* edge detect result */ - GAFR, /* alt function */ -}; - -/* langwell gpio driver data */ -struct lnw_gpio_ddata { - u16 ngpio; /* number of gpio pins */ - u32 gplr_offset; /* offset of first GPLR register from base */ - u32 flis_base; /* base address of FLIS registers */ - u32 flis_len; /* length of FLIS registers */ - u32 (*get_flis_offset)(int gpio); - u32 chip_irq_type; /* chip interrupt type */ -}; - -struct lnw_gpio { - struct gpio_chip chip; - void __iomem *reg_base; - spinlock_t lock; - struct pci_dev *pdev; - struct irq_domain *domain; -}; - -#define to_lnw_priv(chip) container_of(chip, struct lnw_gpio, chip) - -static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned offset, - enum GPIO_REG reg_type) -{ - struct lnw_gpio *lnw = to_lnw_priv(chip); - unsigned nreg = chip->ngpio / 32; - u8 reg = offset / 32; - - return lnw->reg_base + reg_type * nreg * 4 + reg * 4; -} - -static void __iomem *gpio_reg_2bit(struct gpio_chip *chip, unsigned offset, - enum GPIO_REG reg_type) -{ - struct lnw_gpio *lnw = to_lnw_priv(chip); - unsigned nreg = chip->ngpio / 32; - u8 reg = offset / 16; - - return lnw->reg_base + reg_type * nreg * 4 + reg * 4; -} - -static int lnw_gpio_request(struct gpio_chip *chip, unsigned offset) -{ - void __iomem *gafr = gpio_reg_2bit(chip, offset, GAFR); - u32 value = readl(gafr); - int shift = (offset % 16) << 1, af = (value >> shift) & 3; - - if (af) { - value &= ~(3 << shift); - writel(value, gafr); - } - return 0; -} - -static int lnw_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - void __iomem *gplr = gpio_reg(chip, offset, GPLR); - - return readl(gplr) & BIT(offset % 32); -} - -static void lnw_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - void __iomem *gpsr, *gpcr; - - if (value) { - gpsr = gpio_reg(chip, offset, GPSR); - writel(BIT(offset % 32), gpsr); - } else { - gpcr = gpio_reg(chip, offset, GPCR); - writel(BIT(offset % 32), gpcr); - } -} - -static int lnw_gpio_direction_input(struct gpio_chip *chip, unsigned offset) -{ - struct lnw_gpio *lnw = to_lnw_priv(chip); - void __iomem *gpdr = gpio_reg(chip, offset, GPDR); - u32 value; - unsigned long flags; - - if (lnw->pdev) - pm_runtime_get(&lnw->pdev->dev); - - spin_lock_irqsave(&lnw->lock, flags); - value = readl(gpdr); - value &= ~BIT(offset % 32); - writel(value, gpdr); - spin_unlock_irqrestore(&lnw->lock, flags); - - if (lnw->pdev) - pm_runtime_put(&lnw->pdev->dev); - - return 0; -} - -static int lnw_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int value) -{ - struct lnw_gpio *lnw = to_lnw_priv(chip); - void __iomem *gpdr = gpio_reg(chip, offset, GPDR); - unsigned long flags; - - lnw_gpio_set(chip, offset, value); - - if (lnw->pdev) - pm_runtime_get(&lnw->pdev->dev); - - spin_lock_irqsave(&lnw->lock, flags); - value = readl(gpdr); - value |= BIT(offset % 32); - writel(value, gpdr); - spin_unlock_irqrestore(&lnw->lock, flags); - - if (lnw->pdev) - pm_runtime_put(&lnw->pdev->dev); - - return 0; -} - -static int lnw_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -{ - struct lnw_gpio *lnw = to_lnw_priv(chip); - return irq_create_mapping(lnw->domain, offset); -} - -static int lnw_irq_type(struct irq_data *d, unsigned type) -{ - struct lnw_gpio *lnw = irq_data_get_irq_chip_data(d); - u32 gpio = irqd_to_hwirq(d); - unsigned long flags; - u32 value; - void __iomem *grer = gpio_reg(&lnw->chip, gpio, GRER); - void __iomem *gfer = gpio_reg(&lnw->chip, gpio, GFER); - - if (gpio >= lnw->chip.ngpio) - return -EINVAL; - - if (lnw->pdev) - pm_runtime_get(&lnw->pdev->dev); - - spin_lock_irqsave(&lnw->lock, flags); - if (type & IRQ_TYPE_EDGE_RISING) - value = readl(grer) | BIT(gpio % 32); - else - value = readl(grer) & (~BIT(gpio % 32)); - writel(value, grer); - - if (type & IRQ_TYPE_EDGE_FALLING) - value = readl(gfer) | BIT(gpio % 32); - else - value = readl(gfer) & (~BIT(gpio % 32)); - writel(value, gfer); - spin_unlock_irqrestore(&lnw->lock, flags); - - if (lnw->pdev) - pm_runtime_put(&lnw->pdev->dev); - - return 0; -} - -static void lnw_irq_unmask(struct irq_data *d) -{ -} - -static void lnw_irq_mask(struct irq_data *d) -{ -} - -static struct irq_chip lnw_irqchip = { - .name = "LNW-GPIO", - .irq_mask = lnw_irq_mask, - .irq_unmask = lnw_irq_unmask, - .irq_set_type = lnw_irq_type, -}; - -static const struct lnw_gpio_ddata gpio_lincroft = { - .ngpio = 64, -}; - -static const struct lnw_gpio_ddata gpio_penwell_aon = { - .ngpio = 96, - .chip_irq_type = LNW_IRQ_TYPE_EDGE, -}; - -static const struct lnw_gpio_ddata gpio_penwell_core = { - .ngpio = 96, - .chip_irq_type = LNW_IRQ_TYPE_EDGE, -}; - -static const struct lnw_gpio_ddata gpio_cloverview_aon = { - .ngpio = 96, - .chip_irq_type = LNW_IRQ_TYPE_EDGE | LNW_IRQ_TYPE_LEVEL, -}; - -static const struct lnw_gpio_ddata gpio_cloverview_core = { - .ngpio = 96, - .chip_irq_type = LNW_IRQ_TYPE_EDGE, -}; - -static const struct lnw_gpio_ddata gpio_tangier = { - .ngpio = 192, - .gplr_offset = 4, - .flis_base = 0xff0c0000, - .flis_len = 0x8000, - .get_flis_offset = NULL, - .chip_irq_type = LNW_IRQ_TYPE_EDGE, -}; - -static DEFINE_PCI_DEVICE_TABLE(lnw_gpio_ids) = { - { - /* Lincroft */ - PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080f), - .driver_data = (kernel_ulong_t)&gpio_lincroft, - }, - { - /* Penwell AON */ - PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081f), - .driver_data = (kernel_ulong_t)&gpio_penwell_aon, - }, - { - /* Penwell Core */ - PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081a), - .driver_data = (kernel_ulong_t)&gpio_penwell_core, - }, - { - /* Cloverview Aon */ - PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x08eb), - .driver_data = (kernel_ulong_t)&gpio_cloverview_aon, - }, - { - /* Cloverview Core */ - PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x08f7), - .driver_data = (kernel_ulong_t)&gpio_cloverview_core, - }, - { - /* Tangier */ - PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x1199), - .driver_data = (kernel_ulong_t)&gpio_tangier, - }, - { 0 } -}; -MODULE_DEVICE_TABLE(pci, lnw_gpio_ids); - -static void lnw_irq_handler(unsigned irq, struct irq_desc *desc) -{ - struct irq_data *data = irq_desc_get_irq_data(desc); - struct lnw_gpio *lnw = irq_data_get_irq_handler_data(data); - struct irq_chip *chip = irq_data_get_irq_chip(data); - u32 base, gpio, mask; - unsigned long pending; - void __iomem *gedr; - - /* check GPIO controller to check which pin triggered the interrupt */ - for (base = 0; base < lnw->chip.ngpio; base += 32) { - gedr = gpio_reg(&lnw->chip, base, GEDR); - while ((pending = readl(gedr))) { - gpio = __ffs(pending); - mask = BIT(gpio); - /* Clear before handling so we can't lose an edge */ - writel(mask, gedr); - generic_handle_irq(irq_find_mapping(lnw->domain, - base + gpio)); - } - } - - chip->irq_eoi(data); -} - -static void lnw_irq_init_hw(struct lnw_gpio *lnw) -{ - void __iomem *reg; - unsigned base; - - for (base = 0; base < lnw->chip.ngpio; base += 32) { - /* Clear the rising-edge detect register */ - reg = gpio_reg(&lnw->chip, base, GRER); - writel(0, reg); - /* Clear the falling-edge detect register */ - reg = gpio_reg(&lnw->chip, base, GFER); - writel(0, reg); - /* Clear the edge detect status register */ - reg = gpio_reg(&lnw->chip, base, GEDR); - writel(~0, reg); - } -} - -static int lnw_gpio_irq_map(struct irq_domain *d, unsigned int virq, - irq_hw_number_t hw) -{ - struct lnw_gpio *lnw = d->host_data; - - irq_set_chip_and_handler_name(virq, &lnw_irqchip, handle_simple_irq, - "demux"); - irq_set_chip_data(virq, lnw); - irq_set_irq_type(virq, IRQ_TYPE_NONE); - - return 0; -} - -static const struct irq_domain_ops lnw_gpio_irq_ops = { - .map = lnw_gpio_irq_map, - .xlate = irq_domain_xlate_twocell, -}; - -static int lnw_gpio_runtime_idle(struct device *dev) -{ - pm_schedule_suspend(dev, 500); - return -EBUSY; -} - -static const struct dev_pm_ops lnw_gpio_pm_ops = { - SET_RUNTIME_PM_OPS(NULL, NULL, lnw_gpio_runtime_idle) -}; - -static int lnw_gpio_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - void __iomem *base; - struct lnw_gpio *lnw; - u32 gpio_base; - u32 irq_base; - int retval; - struct lnw_gpio_ddata *ddata = (struct lnw_gpio_ddata *)id->driver_data; - - retval = pcim_enable_device(pdev); - if (retval) - return retval; - - retval = pcim_iomap_regions(pdev, 1 << 0 | 1 << 1, pci_name(pdev)); - if (retval) { - dev_err(&pdev->dev, "I/O memory mapping error\n"); - return retval; - } - - base = pcim_iomap_table(pdev)[1]; - - irq_base = readl(base); - gpio_base = readl(sizeof(u32) + base); - - /* release the IO mapping, since we already get the info from bar1 */ - pcim_iounmap_regions(pdev, 1 << 1); - - lnw = devm_kzalloc(&pdev->dev, sizeof(*lnw), GFP_KERNEL); - if (!lnw) { - dev_err(&pdev->dev, "can't allocate chip data\n"); - return -ENOMEM; - } - - lnw->reg_base = pcim_iomap_table(pdev)[0]; - lnw->chip.label = dev_name(&pdev->dev); - lnw->chip.request = lnw_gpio_request; - lnw->chip.direction_input = lnw_gpio_direction_input; - lnw->chip.direction_output = lnw_gpio_direction_output; - lnw->chip.get = lnw_gpio_get; - lnw->chip.set = lnw_gpio_set; - lnw->chip.to_irq = lnw_gpio_to_irq; - lnw->chip.base = gpio_base; - lnw->chip.ngpio = ddata->ngpio; - lnw->chip.can_sleep = 0; - lnw->pdev = pdev; - - spin_lock_init(&lnw->lock); - - lnw->domain = irq_domain_add_simple(pdev->dev.of_node, ddata->ngpio, - irq_base, &lnw_gpio_irq_ops, lnw); - if (!lnw->domain) - return -ENOMEM; - - pci_set_drvdata(pdev, lnw); - retval = gpiochip_add(&lnw->chip); - if (retval) { - dev_err(&pdev->dev, "gpiochip_add error %d\n", retval); - return retval; - } - - lnw_irq_init_hw(lnw); - - irq_set_handler_data(pdev->irq, lnw); - irq_set_chained_handler(pdev->irq, lnw_irq_handler); - - pm_runtime_put_noidle(&pdev->dev); - pm_runtime_allow(&pdev->dev); - - return 0; -} - -static struct pci_driver lnw_gpio_driver = { - .name = "langwell_gpio", - .id_table = lnw_gpio_ids, - .probe = lnw_gpio_probe, - .driver = { - .pm = &lnw_gpio_pm_ops, - }, -}; - -static int __init lnw_gpio_init(void) -{ - return pci_register_driver(&lnw_gpio_driver); -} - -device_initcall(lnw_gpio_init); -- cgit v0.10.2 From f89a768f1c1dd1e4e8ea69c7b01344ff9fdb8fb5 Mon Sep 17 00:00:00 2001 From: David Cohen Date: Fri, 4 Oct 2013 13:01:42 -0700 Subject: gpio-intel-mid: update prefixes and names from langwell to intel-mid After file was renamed from gpio-langwell to gpio-intel-mid, this patch updates the variables, functions and structs to be based on intel-mid instead of langwell. There is no function change. Signed-off-by: David Cohen Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-intel-mid.c b/drivers/gpio/gpio-intel-mid.c index bf3b959..612b54d 100644 --- a/drivers/gpio/gpio-intel-mid.c +++ b/drivers/gpio/gpio-intel-mid.c @@ -20,6 +20,8 @@ /* Supports: * Moorestown platform Langwell chip. * Medfield platform Penwell chip. + * Clovertrail platform Cloverview chip. + * Merrifield platform Tangier chip. */ #include @@ -37,8 +39,8 @@ #include #include -#define LNW_IRQ_TYPE_EDGE (1 << 0) -#define LNW_IRQ_TYPE_LEVEL (1 << 1) +#define INTEL_MID_IRQ_TYPE_EDGE (1 << 0) +#define INTEL_MID_IRQ_TYPE_LEVEL (1 << 1) /* * Langwell chip has 64 pins and thus there are 2 32bit registers to control @@ -65,8 +67,8 @@ enum GPIO_REG { GAFR, /* alt function */ }; -/* langwell gpio driver data */ -struct lnw_gpio_ddata { +/* intel_mid gpio driver data */ +struct intel_mid_gpio_ddata { u16 ngpio; /* number of gpio pins */ u32 gplr_offset; /* offset of first GPLR register from base */ u32 flis_base; /* base address of FLIS registers */ @@ -75,7 +77,7 @@ struct lnw_gpio_ddata { u32 chip_irq_type; /* chip interrupt type */ }; -struct lnw_gpio { +struct intel_mid_gpio { struct gpio_chip chip; void __iomem *reg_base; spinlock_t lock; @@ -83,29 +85,29 @@ struct lnw_gpio { struct irq_domain *domain; }; -#define to_lnw_priv(chip) container_of(chip, struct lnw_gpio, chip) +#define to_intel_gpio_priv(chip) container_of(chip, struct intel_mid_gpio, chip) static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned offset, enum GPIO_REG reg_type) { - struct lnw_gpio *lnw = to_lnw_priv(chip); + struct intel_mid_gpio *priv = to_intel_gpio_priv(chip); unsigned nreg = chip->ngpio / 32; u8 reg = offset / 32; - return lnw->reg_base + reg_type * nreg * 4 + reg * 4; + return priv->reg_base + reg_type * nreg * 4 + reg * 4; } static void __iomem *gpio_reg_2bit(struct gpio_chip *chip, unsigned offset, enum GPIO_REG reg_type) { - struct lnw_gpio *lnw = to_lnw_priv(chip); + struct intel_mid_gpio *priv = to_intel_gpio_priv(chip); unsigned nreg = chip->ngpio / 32; u8 reg = offset / 16; - return lnw->reg_base + reg_type * nreg * 4 + reg * 4; + return priv->reg_base + reg_type * nreg * 4 + reg * 4; } -static int lnw_gpio_request(struct gpio_chip *chip, unsigned offset) +static int intel_gpio_request(struct gpio_chip *chip, unsigned offset) { void __iomem *gafr = gpio_reg_2bit(chip, offset, GAFR); u32 value = readl(gafr); @@ -118,14 +120,14 @@ static int lnw_gpio_request(struct gpio_chip *chip, unsigned offset) return 0; } -static int lnw_gpio_get(struct gpio_chip *chip, unsigned offset) +static int intel_gpio_get(struct gpio_chip *chip, unsigned offset) { void __iomem *gplr = gpio_reg(chip, offset, GPLR); return readl(gplr) & BIT(offset % 32); } -static void lnw_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static void intel_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { void __iomem *gpsr, *gpcr; @@ -138,74 +140,74 @@ static void lnw_gpio_set(struct gpio_chip *chip, unsigned offset, int value) } } -static int lnw_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +static int intel_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { - struct lnw_gpio *lnw = to_lnw_priv(chip); + struct intel_mid_gpio *priv = to_intel_gpio_priv(chip); void __iomem *gpdr = gpio_reg(chip, offset, GPDR); u32 value; unsigned long flags; - if (lnw->pdev) - pm_runtime_get(&lnw->pdev->dev); + if (priv->pdev) + pm_runtime_get(&priv->pdev->dev); - spin_lock_irqsave(&lnw->lock, flags); + spin_lock_irqsave(&priv->lock, flags); value = readl(gpdr); value &= ~BIT(offset % 32); writel(value, gpdr); - spin_unlock_irqrestore(&lnw->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); - if (lnw->pdev) - pm_runtime_put(&lnw->pdev->dev); + if (priv->pdev) + pm_runtime_put(&priv->pdev->dev); return 0; } -static int lnw_gpio_direction_output(struct gpio_chip *chip, +static int intel_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) { - struct lnw_gpio *lnw = to_lnw_priv(chip); + struct intel_mid_gpio *priv = to_intel_gpio_priv(chip); void __iomem *gpdr = gpio_reg(chip, offset, GPDR); unsigned long flags; - lnw_gpio_set(chip, offset, value); + intel_gpio_set(chip, offset, value); - if (lnw->pdev) - pm_runtime_get(&lnw->pdev->dev); + if (priv->pdev) + pm_runtime_get(&priv->pdev->dev); - spin_lock_irqsave(&lnw->lock, flags); + spin_lock_irqsave(&priv->lock, flags); value = readl(gpdr); value |= BIT(offset % 32); writel(value, gpdr); - spin_unlock_irqrestore(&lnw->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); - if (lnw->pdev) - pm_runtime_put(&lnw->pdev->dev); + if (priv->pdev) + pm_runtime_put(&priv->pdev->dev); return 0; } -static int lnw_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +static int intel_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { - struct lnw_gpio *lnw = to_lnw_priv(chip); - return irq_create_mapping(lnw->domain, offset); + struct intel_mid_gpio *priv = to_intel_gpio_priv(chip); + return irq_create_mapping(priv->domain, offset); } -static int lnw_irq_type(struct irq_data *d, unsigned type) +static int intel_mid_irq_type(struct irq_data *d, unsigned type) { - struct lnw_gpio *lnw = irq_data_get_irq_chip_data(d); + struct intel_mid_gpio *priv = irq_data_get_irq_chip_data(d); u32 gpio = irqd_to_hwirq(d); unsigned long flags; u32 value; - void __iomem *grer = gpio_reg(&lnw->chip, gpio, GRER); - void __iomem *gfer = gpio_reg(&lnw->chip, gpio, GFER); + void __iomem *grer = gpio_reg(&priv->chip, gpio, GRER); + void __iomem *gfer = gpio_reg(&priv->chip, gpio, GFER); - if (gpio >= lnw->chip.ngpio) + if (gpio >= priv->chip.ngpio) return -EINVAL; - if (lnw->pdev) - pm_runtime_get(&lnw->pdev->dev); + if (priv->pdev) + pm_runtime_get(&priv->pdev->dev); - spin_lock_irqsave(&lnw->lock, flags); + spin_lock_irqsave(&priv->lock, flags); if (type & IRQ_TYPE_EDGE_RISING) value = readl(grer) | BIT(gpio % 32); else @@ -217,63 +219,63 @@ static int lnw_irq_type(struct irq_data *d, unsigned type) else value = readl(gfer) & (~BIT(gpio % 32)); writel(value, gfer); - spin_unlock_irqrestore(&lnw->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); - if (lnw->pdev) - pm_runtime_put(&lnw->pdev->dev); + if (priv->pdev) + pm_runtime_put(&priv->pdev->dev); return 0; } -static void lnw_irq_unmask(struct irq_data *d) +static void intel_mid_irq_unmask(struct irq_data *d) { } -static void lnw_irq_mask(struct irq_data *d) +static void intel_mid_irq_mask(struct irq_data *d) { } -static struct irq_chip lnw_irqchip = { - .name = "LNW-GPIO", - .irq_mask = lnw_irq_mask, - .irq_unmask = lnw_irq_unmask, - .irq_set_type = lnw_irq_type, +static struct irq_chip intel_mid_irqchip = { + .name = "INTEL_MID-GPIO", + .irq_mask = intel_mid_irq_mask, + .irq_unmask = intel_mid_irq_unmask, + .irq_set_type = intel_mid_irq_type, }; -static const struct lnw_gpio_ddata gpio_lincroft = { +static const struct intel_mid_gpio_ddata gpio_lincroft = { .ngpio = 64, }; -static const struct lnw_gpio_ddata gpio_penwell_aon = { +static const struct intel_mid_gpio_ddata gpio_penwell_aon = { .ngpio = 96, - .chip_irq_type = LNW_IRQ_TYPE_EDGE, + .chip_irq_type = INTEL_MID_IRQ_TYPE_EDGE, }; -static const struct lnw_gpio_ddata gpio_penwell_core = { +static const struct intel_mid_gpio_ddata gpio_penwell_core = { .ngpio = 96, - .chip_irq_type = LNW_IRQ_TYPE_EDGE, + .chip_irq_type = INTEL_MID_IRQ_TYPE_EDGE, }; -static const struct lnw_gpio_ddata gpio_cloverview_aon = { +static const struct intel_mid_gpio_ddata gpio_cloverview_aon = { .ngpio = 96, - .chip_irq_type = LNW_IRQ_TYPE_EDGE | LNW_IRQ_TYPE_LEVEL, + .chip_irq_type = INTEL_MID_IRQ_TYPE_EDGE | INTEL_MID_IRQ_TYPE_LEVEL, }; -static const struct lnw_gpio_ddata gpio_cloverview_core = { +static const struct intel_mid_gpio_ddata gpio_cloverview_core = { .ngpio = 96, - .chip_irq_type = LNW_IRQ_TYPE_EDGE, + .chip_irq_type = INTEL_MID_IRQ_TYPE_EDGE, }; -static const struct lnw_gpio_ddata gpio_tangier = { +static const struct intel_mid_gpio_ddata gpio_tangier = { .ngpio = 192, .gplr_offset = 4, .flis_base = 0xff0c0000, .flis_len = 0x8000, .get_flis_offset = NULL, - .chip_irq_type = LNW_IRQ_TYPE_EDGE, + .chip_irq_type = INTEL_MID_IRQ_TYPE_EDGE, }; -static DEFINE_PCI_DEVICE_TABLE(lnw_gpio_ids) = { +static DEFINE_PCI_DEVICE_TABLE(intel_gpio_ids) = { { /* Lincroft */ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080f), @@ -306,26 +308,26 @@ static DEFINE_PCI_DEVICE_TABLE(lnw_gpio_ids) = { }, { 0 } }; -MODULE_DEVICE_TABLE(pci, lnw_gpio_ids); +MODULE_DEVICE_TABLE(pci, intel_gpio_ids); -static void lnw_irq_handler(unsigned irq, struct irq_desc *desc) +static void intel_mid_irq_handler(unsigned irq, struct irq_desc *desc) { struct irq_data *data = irq_desc_get_irq_data(desc); - struct lnw_gpio *lnw = irq_data_get_irq_handler_data(data); + struct intel_mid_gpio *priv = irq_data_get_irq_handler_data(data); struct irq_chip *chip = irq_data_get_irq_chip(data); u32 base, gpio, mask; unsigned long pending; void __iomem *gedr; /* check GPIO controller to check which pin triggered the interrupt */ - for (base = 0; base < lnw->chip.ngpio; base += 32) { - gedr = gpio_reg(&lnw->chip, base, GEDR); + for (base = 0; base < priv->chip.ngpio; base += 32) { + gedr = gpio_reg(&priv->chip, base, GEDR); while ((pending = readl(gedr))) { gpio = __ffs(pending); mask = BIT(gpio); /* Clear before handling so we can't lose an edge */ writel(mask, gedr); - generic_handle_irq(irq_find_mapping(lnw->domain, + generic_handle_irq(irq_find_mapping(priv->domain, base + gpio)); } } @@ -333,61 +335,62 @@ static void lnw_irq_handler(unsigned irq, struct irq_desc *desc) chip->irq_eoi(data); } -static void lnw_irq_init_hw(struct lnw_gpio *lnw) +static void intel_mid_irq_init_hw(struct intel_mid_gpio *priv) { void __iomem *reg; unsigned base; - for (base = 0; base < lnw->chip.ngpio; base += 32) { + for (base = 0; base < priv->chip.ngpio; base += 32) { /* Clear the rising-edge detect register */ - reg = gpio_reg(&lnw->chip, base, GRER); + reg = gpio_reg(&priv->chip, base, GRER); writel(0, reg); /* Clear the falling-edge detect register */ - reg = gpio_reg(&lnw->chip, base, GFER); + reg = gpio_reg(&priv->chip, base, GFER); writel(0, reg); /* Clear the edge detect status register */ - reg = gpio_reg(&lnw->chip, base, GEDR); + reg = gpio_reg(&priv->chip, base, GEDR); writel(~0, reg); } } -static int lnw_gpio_irq_map(struct irq_domain *d, unsigned int virq, +static int intel_gpio_irq_map(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw) { - struct lnw_gpio *lnw = d->host_data; + struct intel_mid_gpio *priv = d->host_data; - irq_set_chip_and_handler_name(virq, &lnw_irqchip, handle_simple_irq, - "demux"); - irq_set_chip_data(virq, lnw); + irq_set_chip_and_handler_name(virq, &intel_mid_irqchip, + handle_simple_irq, "demux"); + irq_set_chip_data(virq, priv); irq_set_irq_type(virq, IRQ_TYPE_NONE); return 0; } -static const struct irq_domain_ops lnw_gpio_irq_ops = { - .map = lnw_gpio_irq_map, +static const struct irq_domain_ops intel_gpio_irq_ops = { + .map = intel_gpio_irq_map, .xlate = irq_domain_xlate_twocell, }; -static int lnw_gpio_runtime_idle(struct device *dev) +static int intel_gpio_runtime_idle(struct device *dev) { pm_schedule_suspend(dev, 500); return -EBUSY; } -static const struct dev_pm_ops lnw_gpio_pm_ops = { - SET_RUNTIME_PM_OPS(NULL, NULL, lnw_gpio_runtime_idle) +static const struct dev_pm_ops intel_gpio_pm_ops = { + SET_RUNTIME_PM_OPS(NULL, NULL, intel_gpio_runtime_idle) }; -static int lnw_gpio_probe(struct pci_dev *pdev, +static int intel_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id) { void __iomem *base; - struct lnw_gpio *lnw; + struct intel_mid_gpio *priv; u32 gpio_base; u32 irq_base; int retval; - struct lnw_gpio_ddata *ddata = (struct lnw_gpio_ddata *)id->driver_data; + struct intel_mid_gpio_ddata *ddata = + (struct intel_mid_gpio_ddata *)id->driver_data; retval = pcim_enable_device(pdev); if (retval) @@ -407,43 +410,43 @@ static int lnw_gpio_probe(struct pci_dev *pdev, /* release the IO mapping, since we already get the info from bar1 */ pcim_iounmap_regions(pdev, 1 << 1); - lnw = devm_kzalloc(&pdev->dev, sizeof(*lnw), GFP_KERNEL); - if (!lnw) { + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { dev_err(&pdev->dev, "can't allocate chip data\n"); return -ENOMEM; } - lnw->reg_base = pcim_iomap_table(pdev)[0]; - lnw->chip.label = dev_name(&pdev->dev); - lnw->chip.request = lnw_gpio_request; - lnw->chip.direction_input = lnw_gpio_direction_input; - lnw->chip.direction_output = lnw_gpio_direction_output; - lnw->chip.get = lnw_gpio_get; - lnw->chip.set = lnw_gpio_set; - lnw->chip.to_irq = lnw_gpio_to_irq; - lnw->chip.base = gpio_base; - lnw->chip.ngpio = ddata->ngpio; - lnw->chip.can_sleep = 0; - lnw->pdev = pdev; - - spin_lock_init(&lnw->lock); - - lnw->domain = irq_domain_add_simple(pdev->dev.of_node, ddata->ngpio, - irq_base, &lnw_gpio_irq_ops, lnw); - if (!lnw->domain) + priv->reg_base = pcim_iomap_table(pdev)[0]; + priv->chip.label = dev_name(&pdev->dev); + priv->chip.request = intel_gpio_request; + priv->chip.direction_input = intel_gpio_direction_input; + priv->chip.direction_output = intel_gpio_direction_output; + priv->chip.get = intel_gpio_get; + priv->chip.set = intel_gpio_set; + priv->chip.to_irq = intel_gpio_to_irq; + priv->chip.base = gpio_base; + priv->chip.ngpio = ddata->ngpio; + priv->chip.can_sleep = 0; + priv->pdev = pdev; + + spin_lock_init(&priv->lock); + + priv->domain = irq_domain_add_simple(pdev->dev.of_node, ddata->ngpio, + irq_base, &intel_gpio_irq_ops, priv); + if (!priv->domain) return -ENOMEM; - pci_set_drvdata(pdev, lnw); - retval = gpiochip_add(&lnw->chip); + pci_set_drvdata(pdev, priv); + retval = gpiochip_add(&priv->chip); if (retval) { dev_err(&pdev->dev, "gpiochip_add error %d\n", retval); return retval; } - lnw_irq_init_hw(lnw); + intel_mid_irq_init_hw(priv); - irq_set_handler_data(pdev->irq, lnw); - irq_set_chained_handler(pdev->irq, lnw_irq_handler); + irq_set_handler_data(pdev->irq, priv); + irq_set_chained_handler(pdev->irq, intel_mid_irq_handler); pm_runtime_put_noidle(&pdev->dev); pm_runtime_allow(&pdev->dev); @@ -451,18 +454,18 @@ static int lnw_gpio_probe(struct pci_dev *pdev, return 0; } -static struct pci_driver lnw_gpio_driver = { - .name = "langwell_gpio", - .id_table = lnw_gpio_ids, - .probe = lnw_gpio_probe, +static struct pci_driver intel_gpio_driver = { + .name = "intel_mid_gpio", + .id_table = intel_gpio_ids, + .probe = intel_gpio_probe, .driver = { - .pm = &lnw_gpio_pm_ops, + .pm = &intel_gpio_pm_ops, }, }; -static int __init lnw_gpio_init(void) +static int __init intel_gpio_init(void) { - return pci_register_driver(&lnw_gpio_driver); + return pci_register_driver(&intel_gpio_driver); } -device_initcall(lnw_gpio_init); +device_initcall(intel_gpio_init); -- cgit v0.10.2 From ee0f5b390a0d7375a8b6af4cb0e167692e5e8a10 Mon Sep 17 00:00:00 2001 From: David Cohen Date: Mon, 7 Oct 2013 12:17:27 -0700 Subject: MAINTAINERS: GPIO-INTEL-MID: add maintainer Assign david.a.cohen@linux.intel.com as maintainer of Intel MID gpio driver at drivers/gpio/gpio-intel-mid.c. Signed-off-by: David Cohen Acked-by: Mika Westerberg Signed-off-by: Linus Walleij diff --git a/MAINTAINERS b/MAINTAINERS index e61c2e8..fe17482 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1903,6 +1903,12 @@ W: http://bu3sch.de/btgpio.php S: Maintained F: drivers/gpio/gpio-bt8xx.c +INTEL-MID GPIO DRIVER +M: David Cohen +L: linux-gpio@vger.kernel.org +S: Maintained +F: drivers/gpio/gpio-intel-mid.c + BTRFS FILE SYSTEM M: Chris Mason L: linux-btrfs@vger.kernel.org -- cgit v0.10.2 From fac7ce92d0864e2ab64b9a0e6238e00c019b11ec Mon Sep 17 00:00:00 2001 From: Markus Mayer Date: Mon, 7 Oct 2013 14:51:00 -0700 Subject: gpio: bcm281xx: Fix nested interrupt handler issue The GPIO interrupt handler does not need to be marked as nested. Signed-off-by: Markus Mayer Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index c0751a8..ff5c98e 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -478,7 +478,6 @@ static int bcm_kona_gpio_irq_map(struct irq_domain *d, unsigned int irq, return ret; irq_set_lockdep_class(irq, &gpio_lock_class); irq_set_chip_and_handler(irq, &bcm_gpio_irq_chip, handle_simple_irq); - irq_set_nested_thread(irq, 1); #ifdef CONFIG_ARM set_irq_flags(irq, IRQF_VALID); #else -- cgit v0.10.2 From b52bc23414124b0d37fd9933b5a894d9b4a9720d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 24 Sep 2013 11:56:36 -0300 Subject: perf trace: Add 'trace' alias to 'perf trace' Make 'perf trace' more accessible by aliasing it to just 'trace': [root@zoo linux]# trace --duration 15 -a -e futex sleep 1 110.092 (16.188 ms): libvirtd/1166 futex(uaddr: 0x185b344, op: WAIT|PRIV, val: 174293 ) = 0 110.101 (15.903 ms): libvirtd/1171 futex(uaddr: 0x185b3dc, op: WAIT|PRIV, val: 139265 ) = 0 111.594 (15.776 ms): libvirtd/1165 futex(uaddr: 0x185b344, op: WAIT|PRIV, val: 174295 ) = 0 111.610 (15.969 ms): libvirtd/1169 futex(uaddr: 0x185b3dc, op: WAIT|PRIV, val: 139267 ) = 0 113.556 (16.216 ms): libvirtd/1168 futex(uaddr: 0x185b3dc, op: WAIT|PRIV, val: 139269 ) = 0 291.265 (199.508 ms): chromium-brows/15830 futex(uaddr: 0x7fff2986bcb4, op: WAIT_BITSET|PRIV|CLKRT, val: 1, utime: 0x7fff2986bab0, val3: 4294967295) = -1 ETIMEDOUT Connection timed out 360.354 (69.053 ms): chromium-brows/15830 futex(uaddr: 0x7fff2986bcb4, op: WAIT_BITSET|PRIV|CLKRT, val: 1, utime: 0x7fff2986bab0, val3: 4294967295) = -1 ETIMEDOUT Connection timed out [root@zoo linux]# I.e. looking for futex calls that take at least 15ms, system wide, during a one second window. Now to get callchains into 'trace' to figure out what are those locks :-) Requested-by: Ingo Molnar Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-ch4smqz8b5fmgrte7c5e4fuw@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 1f13615..2badb08 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -78,6 +78,7 @@ CC = $(CROSS_COMPILE)gcc AR = $(CROSS_COMPILE)ar RM = rm -f +LN = ln -f MKDIR = mkdir FIND = find INSTALL = install @@ -809,6 +810,7 @@ install-gtk: install-bin: all install-gtk $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)' + $(LN) '$(DESTDIR_SQ)$(bindir_SQ)/perf' '$(DESTDIR_SQ)$(bindir_SQ)/trace' $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' ifndef NO_LIBPERL diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 6265778..8b38b4e 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -481,7 +481,14 @@ int main(int argc, const char **argv) fprintf(stderr, "cannot handle %s internally", cmd); goto out; } - +#ifdef HAVE_LIBAUDIT_SUPPORT + if (!prefixcmp(cmd, "trace")) { + set_buildid_dir(); + setup_path(); + argv[0] = "trace"; + return cmd_trace(argc, argv, NULL); + } +#endif /* Look for flags.. */ argv++; argc--; -- cgit v0.10.2 From a9faa0cab619fad380c2669825aa84212943d528 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 23 Sep 2013 10:17:35 +0200 Subject: perf bench sched: Add --threaded option Allow the measurement of thread versus process context switch performance. The default stays at 'process' based measurement, like lmbench's lat_ctx benchmark. Sample output: comet:~/tip/tools/perf> taskset 1 ./perf bench sched pipe # Running sched/pipe benchmark... # Executed 1000000 pipe operations between two processes Total time: 4.138 [sec] 4.138729 usecs/op 241620 ops/sec comet:~/tip/tools/perf> taskset 1 ./perf bench sched pipe --threaded # Running sched/pipe benchmark... # Executed 1000000 pipe operations between two threads Total time: 3.667 [sec] 3.667667 usecs/op 272652 ops/sec Signed-off-by: Ingo Molnar Cc: Peter Zijlstra Cc: Namhyung Kim Cc: Jiri Olsa Cc: Arnaldo Carvalho de Melo Link: http://lkml.kernel.org/r/20130917114256.GA31159@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/bench/sched-pipe.c b/tools/perf/bench/sched-pipe.c index 69cfba8..07a8d76 100644 --- a/tools/perf/bench/sched-pipe.c +++ b/tools/perf/bench/sched-pipe.c @@ -7,9 +7,7 @@ * Based on pipe-test-1m.c by Ingo Molnar * http://people.redhat.com/mingo/cfs-scheduler/tools/pipe-test-1m.c * Ported to perf by Hitoshi Mitake - * */ - #include "../perf.h" #include "../util/util.h" #include "../util/parse-options.h" @@ -28,12 +26,24 @@ #include #include +#include + +struct thread_data { + int nr; + int pipe_read; + int pipe_write; + pthread_t pthread; +}; + #define LOOPS_DEFAULT 1000000 -static int loops = LOOPS_DEFAULT; +static int loops = LOOPS_DEFAULT; + +/* Use processes by default: */ +static bool threaded; static const struct option options[] = { - OPT_INTEGER('l', "loop", &loops, - "Specify number of loops"), + OPT_INTEGER('l', "loop", &loops, "Specify number of loops"), + OPT_BOOLEAN('T', "threaded", &threaded, "Specify threads/process based task setup"), OPT_END() }; @@ -42,13 +52,37 @@ static const char * const bench_sched_pipe_usage[] = { NULL }; -int bench_sched_pipe(int argc, const char **argv, - const char *prefix __maybe_unused) +static void *worker_thread(void *__tdata) { - int pipe_1[2], pipe_2[2]; + struct thread_data *td = __tdata; int m = 0, i; + int ret; + + for (i = 0; i < loops; i++) { + if (!td->nr) { + ret = read(td->pipe_read, &m, sizeof(int)); + BUG_ON(ret != sizeof(int)); + ret = write(td->pipe_write, &m, sizeof(int)); + BUG_ON(ret != sizeof(int)); + } else { + ret = write(td->pipe_write, &m, sizeof(int)); + BUG_ON(ret != sizeof(int)); + ret = read(td->pipe_read, &m, sizeof(int)); + BUG_ON(ret != sizeof(int)); + } + } + + return NULL; +} + +int bench_sched_pipe(int argc, const char **argv, const char *prefix __maybe_unused) +{ + struct thread_data threads[2], *td; + int pipe_1[2], pipe_2[2]; struct timeval start, stop, diff; unsigned long long result_usec = 0; + int nr_threads = 2; + int t; /* * why does "ret" exist? @@ -58,43 +92,66 @@ int bench_sched_pipe(int argc, const char **argv, int __maybe_unused ret, wait_stat; pid_t pid, retpid __maybe_unused; - argc = parse_options(argc, argv, options, - bench_sched_pipe_usage, 0); + argc = parse_options(argc, argv, options, bench_sched_pipe_usage, 0); BUG_ON(pipe(pipe_1)); BUG_ON(pipe(pipe_2)); - pid = fork(); - assert(pid >= 0); - gettimeofday(&start, NULL); - if (!pid) { - for (i = 0; i < loops; i++) { - ret = read(pipe_1[0], &m, sizeof(int)); - ret = write(pipe_2[1], &m, sizeof(int)); - } - } else { - for (i = 0; i < loops; i++) { - ret = write(pipe_1[1], &m, sizeof(int)); - ret = read(pipe_2[0], &m, sizeof(int)); + for (t = 0; t < nr_threads; t++) { + td = threads + t; + + td->nr = t; + + if (t == 0) { + td->pipe_read = pipe_1[0]; + td->pipe_write = pipe_2[1]; + } else { + td->pipe_write = pipe_1[1]; + td->pipe_read = pipe_2[0]; } } - gettimeofday(&stop, NULL); - timersub(&stop, &start, &diff); - if (pid) { + if (threaded) { + + for (t = 0; t < nr_threads; t++) { + td = threads + t; + + ret = pthread_create(&td->pthread, NULL, worker_thread, td); + BUG_ON(ret); + } + + for (t = 0; t < nr_threads; t++) { + td = threads + t; + + ret = pthread_join(td->pthread, NULL); + BUG_ON(ret); + } + + } else { + pid = fork(); + assert(pid >= 0); + + if (!pid) { + worker_thread(threads + 0); + exit(0); + } else { + worker_thread(threads + 1); + } + retpid = waitpid(pid, &wait_stat, 0); assert((retpid == pid) && WIFEXITED(wait_stat)); - } else { - exit(0); } + gettimeofday(&stop, NULL); + timersub(&stop, &start, &diff); + switch (bench_format) { case BENCH_FORMAT_DEFAULT: - printf("# Executed %d pipe operations between two tasks\n\n", - loops); + printf("# Executed %d pipe operations between two %s\n\n", + loops, threaded ? "threads" : "processes"); result_usec = diff.tv_sec * 1000000; result_usec += diff.tv_usec; -- cgit v0.10.2 From f4be904d2fa7c65e230ed4b1008ebdf5f4053ac3 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Sun, 22 Sep 2013 13:22:09 +0300 Subject: perf machine: Use snprintf instead of sprintf To avoid buffer overruns. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1379845338-29637-2-git-send-email-adrian.hunter@intel.com [ Split from aa7fe3b ] Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 6188d28..ddf917b 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -785,10 +785,10 @@ static int machine__create_modules(struct machine *machine) const char *modules; char path[PATH_MAX]; - if (machine__is_default_guest(machine)) + if (machine__is_default_guest(machine)) { modules = symbol_conf.default_guest_modules; - else { - sprintf(path, "%s/proc/modules", machine->root_dir); + } else { + snprintf(path, PATH_MAX, "%s/proc/modules", machine->root_dir); modules = path; } -- cgit v0.10.2 From e2137086be7bc52893a790292635cfafc475b693 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 26 Sep 2013 20:55:54 +0200 Subject: perf tools: Add missing -ldl for gtk build If we build perf with NO_LIBPYTHON=1 NO_LIBPERL=1 the '-ldl' is not added to libs build fails if we have gtk2 code in, because it depends on it. Signed-off-by: Jiri Olsa Acked-by: Namhyung Kim Cc: Andi Kleen Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1380221754-29865-1-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index cf6ad5d..29ad7d6 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -382,6 +382,7 @@ ifndef NO_GTK2 CFLAGS += -DHAVE_GTK2_SUPPORT GTK_CFLAGS += $(shell pkg-config --cflags gtk+-2.0 2>/dev/null) GTK_LIBS := $(shell pkg-config --libs gtk+-2.0 2>/dev/null) + EXTLIBS += -ldl endif endif -- cgit v0.10.2 From 820042233bd3c1b24b5ca4c75a88a4e0de39814a Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Fri, 27 Sep 2013 18:29:58 +0200 Subject: perf tools: Move start conditions to start of the flex file Moving start conditions to start of the flex file so it's clear what the INITIAL condition rules are. Plus adding default rule for INITIAL condition. This prevents default space to be printed for events like: $ ./perf stat -e "cycles " kill 2>/dev/null $ ^^^^^^^^ Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1380299398-10839-1-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 91346b7..3432995 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l @@ -126,6 +126,37 @@ modifier_bp [rwx]{1,3} } +{ +config { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); } +config1 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); } +config2 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2); } +name { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NAME); } +period { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); } +branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); } +, { return ','; } +"/" { BEGIN(INITIAL); return '/'; } +{name_minus} { return str(yyscanner, PE_NAME); } +} + +{ +{modifier_bp} { return str(yyscanner, PE_MODIFIER_BP); } +: { return ':'; } +{num_dec} { return value(yyscanner, 10); } +{num_hex} { return value(yyscanner, 16); } + /* + * We need to separate 'mem:' scanner part, in order to get specific + * modifier bits parsed out. Otherwise we would need to handle PE_NAME + * and we'd need to parse it manually. During the escape from + * state we need to put the escaping char back, so we dont miss it. + */ +. { unput(*yytext); BEGIN(INITIAL); } + /* + * We destroy the scanner after reaching EOF, + * but anyway just to be sure get back to INIT state. + */ +<> { BEGIN(INITIAL); } +} + cpu-cycles|cycles { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); } stalled-cycles-frontend|idle-cycles-frontend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); } stalled-cycles-backend|idle-cycles-backend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); } @@ -162,18 +193,6 @@ speculative-read|speculative-load | refs|Reference|ops|access | misses|miss { return str(yyscanner, PE_NAME_CACHE_OP_RESULT); } -{ -config { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); } -config1 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); } -config2 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2); } -name { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NAME); } -period { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); } -branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); } -, { return ','; } -"/" { BEGIN(INITIAL); return '/'; } -{name_minus} { return str(yyscanner, PE_NAME); } -} - mem: { BEGIN(mem); return PE_PREFIX_MEM; } r{num_raw_hex} { return raw(yyscanner); } {num_dec} { return value(yyscanner, 10); } @@ -189,25 +208,7 @@ r{num_raw_hex} { return raw(yyscanner); } "}" { return '}'; } = { return '='; } \n { } - -{ -{modifier_bp} { return str(yyscanner, PE_MODIFIER_BP); } -: { return ':'; } -{num_dec} { return value(yyscanner, 10); } -{num_hex} { return value(yyscanner, 16); } - /* - * We need to separate 'mem:' scanner part, in order to get specific - * modifier bits parsed out. Otherwise we would need to handle PE_NAME - * and we'd need to parse it manually. During the escape from - * state we need to put the escaping char back, so we dont miss it. - */ -. { unput(*yytext); BEGIN(INITIAL); } - /* - * We destroy the scanner after reaching EOF, - * but anyway just to be sure get back to INIT state. - */ -<> { BEGIN(INITIAL); } -} +. { } %% -- cgit v0.10.2 From c458fe62ca31496664c1211a7906d261220b18f9 Mon Sep 17 00:00:00 2001 From: Ramkumar Ramachandra Date: Mon, 30 Sep 2013 16:43:05 +0530 Subject: perf stat: Don't print bogus data on -e cycles When only the cycles event is requested: $ perf stat -e cycles dd if=/dev/zero of=/dev/null count=1000000 1000000+0 records in 1000000+0 records out 512000000 bytes (512 MB) copied, 0.26123 s, 2.0 GB/s Performance counter stats for 'dd if=/dev/zero of=/dev/null count=1000000': 911,626,453 cycles # 0.000 GHz 0.262113350 seconds time elapsed The 0.000 GHz comment in the output is totally bogus and misleading. It happens because update_shadow_stats() doesn't touch runtime_nsecs_stats; it is only written when a requested counter matches a SW_TASK_CLOCK. In our case, since we have only requested HW_CPU_CYCLES, runtime_nsecs_stats is unavailable. So, omit printing the comment altogether. Signed-off-by: Ramkumar Ramachandra Acked-by: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1380539585-23859-3-git-send-email-artagnon@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 700b478..ce2266c 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -997,10 +997,10 @@ static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) } else if (perf_evsel__match(evsel, HARDWARE, HW_CPU_CYCLES)) { total = avg_stats(&runtime_nsecs_stats[cpu]); - if (total) - ratio = 1.0 * avg / total; - - fprintf(output, " # %8.3f GHz ", ratio); + if (total) { + ratio = avg / total; + fprintf(output, " # %8.3f GHz ", ratio); + } } else if (transaction_run && perf_evsel__cmp(evsel, nth_evsel(T_CYCLES_IN_TX))) { total = avg_stats(&runtime_cycles_stats[cpu]); -- cgit v0.10.2 From 3e7a081796146f97f166d77a655c0eb585065077 Mon Sep 17 00:00:00 2001 From: Ramkumar Ramachandra Date: Tue, 1 Oct 2013 14:06:44 +0530 Subject: perf stat: Don't print bogus data on -e instructions When only the instructions event is requested: $ perf stat -e instructions git s M builtin-stat.c Performance counter stats for 'git s': 917,453,420 instructions # 0.00 insns per cycle 0.213002926 seconds time elapsed The 0.00 insns per cycle comment in the output is totally bogus and misleading. It happens because update_shadow_stats() doesn't touch runtime_cycles_stats when only the instructions event is requested. So, omit printing the bogus data altogether. Signed-off-by: Ramkumar Ramachandra Acked-by: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1380616604-4077-1-git-send-email-artagnon@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index ce2266c..fb02b53 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -930,11 +930,10 @@ static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) if (perf_evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS)) { total = avg_stats(&runtime_cycles_stats[cpu]); - if (total) + if (total) { ratio = avg / total; - - fprintf(output, " # %5.2f insns per cycle ", ratio); - + fprintf(output, " # %5.2f insns per cycle ", ratio); + } total = avg_stats(&runtime_stalled_cycles_front_stats[cpu]); total = max(total, avg_stats(&runtime_stalled_cycles_back_stats[cpu])); -- cgit v0.10.2 From f3c236b0c7a84e5d59cc639a1673a20b0a59ecc0 Mon Sep 17 00:00:00 2001 From: Ramkumar Ramachandra Date: Thu, 3 Oct 2013 14:10:36 +0530 Subject: perf tools: Ignore 'perf timechart' output file The default output file produced by the 'perf timechart' tool is called output.svg, add it to .gitignore. Signed-off-by: Ramkumar Ramachandra Cc: Ingo Molnar Link: http://lkml.kernel.org/r/1380789636-4512-1-git-send-email-artagnon@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore index 8f8fbc2..782d86e 100644 --- a/tools/perf/.gitignore +++ b/tools/perf/.gitignore @@ -13,6 +13,7 @@ perf*.html common-cmds.h perf.data perf.data.old +output.svg perf-archive tags TAGS -- cgit v0.10.2 From b81a48ea877e1a104dace1392d92f708ff208f97 Mon Sep 17 00:00:00 2001 From: Petr Holasek Date: Thu, 3 Oct 2013 19:28:45 +0200 Subject: perf bench: Fix failing assertions in numa bench Patch adds more subtle handling of -C and -N parameters in parse_{cpu,node}_setup_list() functions when there isn't enough NUMA nodes or CPUs present. Instead of assertion and terminating benchmark, partial test is skipped with error message and perf will continue to the next one. Fixed problem can be easily reproduced on machine with only one NUMA node: # Running numa/mem benchmark... # Running main, "perf bench numa mem -a" ... # Running RAM-bw-remote, "perf bench numa mem -p 1 -t 1 -P 1024 -C 0 -M 1 -s perf: bench/numa.c:622: parse_setup_node_list: Assertion `!(bind_node_0 < 0 || bind_node_0 >= g->p.nr_nodes)' failed. Aborted Signed-off-by: Petr Holasek Acked-by: Ingo Molnar Cc: Ingo Molnar Cc: Jiri Olsa Cc: Petr Benas Link: http://lkml.kernel.org/r/1380821325-4017-1-git-send-email-pholasek@redhat.com Signed-off-by: Petr Benas Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/bench/numa.c b/tools/perf/bench/numa.c index 30d1c32..64fa01c 100644 --- a/tools/perf/bench/numa.c +++ b/tools/perf/bench/numa.c @@ -429,14 +429,14 @@ static int parse_cpu_list(const char *arg) return 0; } -static void parse_setup_cpu_list(void) +static int parse_setup_cpu_list(void) { struct thread_data *td; char *str0, *str; int t; if (!g->p.cpu_list_str) - return; + return 0; dprintf("g->p.nr_tasks: %d\n", g->p.nr_tasks); @@ -500,8 +500,12 @@ static void parse_setup_cpu_list(void) dprintf("CPUs: %d_%d-%d#%dx%d\n", bind_cpu_0, bind_len, bind_cpu_1, step, mul); - BUG_ON(bind_cpu_0 < 0 || bind_cpu_0 >= g->p.nr_cpus); - BUG_ON(bind_cpu_1 < 0 || bind_cpu_1 >= g->p.nr_cpus); + if (bind_cpu_0 >= g->p.nr_cpus || bind_cpu_1 >= g->p.nr_cpus) { + printf("\nTest not applicable, system has only %d CPUs.\n", g->p.nr_cpus); + return -1; + } + + BUG_ON(bind_cpu_0 < 0 || bind_cpu_1 < 0); BUG_ON(bind_cpu_0 > bind_cpu_1); for (bind_cpu = bind_cpu_0; bind_cpu <= bind_cpu_1; bind_cpu += step) { @@ -541,6 +545,7 @@ out: printf("# NOTE: %d tasks bound, %d tasks unbound\n", t, g->p.nr_tasks - t); free(str0); + return 0; } static int parse_cpus_opt(const struct option *opt __maybe_unused, @@ -561,14 +566,14 @@ static int parse_node_list(const char *arg) return 0; } -static void parse_setup_node_list(void) +static int parse_setup_node_list(void) { struct thread_data *td; char *str0, *str; int t; if (!g->p.node_list_str) - return; + return 0; dprintf("g->p.nr_tasks: %d\n", g->p.nr_tasks); @@ -619,8 +624,12 @@ static void parse_setup_node_list(void) dprintf("NODEs: %d-%d #%d\n", bind_node_0, bind_node_1, step); - BUG_ON(bind_node_0 < 0 || bind_node_0 >= g->p.nr_nodes); - BUG_ON(bind_node_1 < 0 || bind_node_1 >= g->p.nr_nodes); + if (bind_node_0 >= g->p.nr_nodes || bind_node_1 >= g->p.nr_nodes) { + printf("\nTest not applicable, system has only %d nodes.\n", g->p.nr_nodes); + return -1; + } + + BUG_ON(bind_node_0 < 0 || bind_node_1 < 0); BUG_ON(bind_node_0 > bind_node_1); for (bind_node = bind_node_0; bind_node <= bind_node_1; bind_node += step) { @@ -651,6 +660,7 @@ out: printf("# NOTE: %d tasks mem-bound, %d tasks unbound\n", t, g->p.nr_tasks - t); free(str0); + return 0; } static int parse_nodes_opt(const struct option *opt __maybe_unused, @@ -1356,8 +1366,8 @@ static int init(void) init_thread_data(); tprintf("#\n"); - parse_setup_cpu_list(); - parse_setup_node_list(); + if (parse_setup_cpu_list() || parse_setup_node_list()) + return -1; tprintf("#\n"); print_summary(); @@ -1600,7 +1610,6 @@ static int run_bench_numa(const char *name, const char **argv) return 0; err: - usage_with_options(numa_usage, options); return -1; } @@ -1701,8 +1710,7 @@ static int bench_all(void) BUG_ON(ret < 0); for (i = 0; i < nr; i++) { - if (run_bench_numa(tests[i][0], tests[i] + 1)) - return -1; + run_bench_numa(tests[i][0], tests[i] + 1); } printf("\n"); -- cgit v0.10.2 From a65cb4b9f8a777a715371c63c0525408048cea3e Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 2 Oct 2013 15:46:39 +0200 Subject: perf evlist: Fix perf_evlist__mmap_read event overflow The perf_evlist__mmap_read used 'union perf_event' as a placeholder for event crossing the mmap boundary. This is ok for sample shorter than ~PATH_MAX. However we could grow up to the maximum sample size which is 16 bits max. I hit this overflow issue when using 'perf top -G dwarf' which produces sample with the size around 8192 bytes. We could configure any valid sample size here using: '-G dwarf,size'. Using array with sample max size instead for the event placeholder. Also adding another safe check for the dynamic size of the user stack. TODO: The 'struct perf_mmap' is quite big now, maybe we could use some lazy allocation for event_copy size. Signed-off-by: Jiri Olsa Acked-by: David Ahern Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1380721599-24285-1-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 9b7d4d3..752709c 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -75,6 +75,9 @@ struct throttle_event { PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD | \ PERF_SAMPLE_IDENTIFIER) +/* perf sample has 16 bits size limit */ +#define PERF_SAMPLE_MAX_SIZE (1 << 16) + struct sample_event { struct perf_event_header header; u64 array[]; diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index f0d71a9..cb9523f 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -540,7 +540,7 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) if ((old & md->mask) + size != ((old + size) & md->mask)) { unsigned int offset = old; unsigned int len = min(sizeof(*event), size), cpy; - void *dst = &md->event_copy; + void *dst = md->event_copy; do { cpy = min(md->mask + 1 - (offset & md->mask), len); @@ -550,7 +550,7 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) len -= cpy; } while (len); - event = &md->event_copy; + event = (union perf_event *) md->event_copy; } old += size; diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 871b55a..722618f 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -21,7 +21,7 @@ struct perf_mmap { void *base; int mask; unsigned int prev; - union perf_event event_copy; + char event_copy[PERF_SAMPLE_MAX_SIZE]; }; struct perf_evlist { diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index abe69af..bfebc1e 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1456,6 +1456,9 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, array = (void *)array + sz; OVERFLOW_CHECK_u64(array); data->user_stack.size = *array++; + if (WARN_ONCE(data->user_stack.size > sz, + "user stack dump failure\n")) + return -EFAULT; } } -- cgit v0.10.2 From 62d3b617c02785a4f1fbde8d93ca77a0b33d8454 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Sat, 28 Sep 2013 14:27:58 -0600 Subject: perf stat: Fix misleading message when specifying cpu list or system wide The "perf stat" tool displays the command run in its summary output which is misleading when using a cpu list or system wide collection. Before: perf stat -a -- sleep 1 Performance counter stats for 'sleep 1': 16152.670249 task-clock # 16.132 CPUs utilized 417 context-switches # 0.002 M/sec 7 cpu-migrations # 0.030 K/sec ... After: perf stat -a -- sleep 1 Performance counter stats for 'system wide': 16206.931120 task-clock # 16.144 CPUs utilized 395 context-switches # 0.002 M/sec 5 cpu-migrations # 0.030 K/sec ... or perf stat -C1 -- sleep 1 Performance counter stats for 'CPU(s) 1': 1001.669257 task-clock # 1.000 CPUs utilized 4,264 context-switches # 0.004 M/sec 3 cpu-migrations # 0.003 K/sec ... Signed-off-by: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1380400080-9211-2-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index fb02b53..c8a2662 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1229,7 +1229,11 @@ static void print_stat(int argc, const char **argv) if (!csv_output) { fprintf(output, "\n"); fprintf(output, " Performance counter stats for "); - if (!perf_target__has_task(&target)) { + if (target.system_wide) + fprintf(output, "\'system wide"); + else if (target.cpu_list) + fprintf(output, "\'CPU(s) %s", target.cpu_list); + else if (!perf_target__has_task(&target)) { fprintf(output, "\'%s", argv[0]); for (i = 1; i < argc; i++) fprintf(output, " %s", argv[i]); -- cgit v0.10.2 From ac3063bd4725689f39d7a23fdfca2e034c73dcac Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 30 Sep 2013 07:37:37 -0600 Subject: perf stat: Don't require a workload when using system wide or CPU options The "perf stat" command can do system wide counters or one or more cpus. For these options do not require a workload to be specified. v2: use perf_target__none per Namhyung's comment. Signed-off-by: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/52497F3C.9070908@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index c8a2662..2178e66 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1659,8 +1659,9 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) } else if (big_num_opt == 0) /* User passed --no-big-num */ big_num = false; - if (!argc && !perf_target__has_task(&target)) + if (!argc && perf_target__none(&target)) usage_with_options(stat_usage, options); + if (run_count < 0) { usage_with_options(stat_usage, options); } else if (run_count == 0) { -- cgit v0.10.2 From 4bbe5a61f29b13437a6a16467328d3bae8fce9e7 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Sat, 28 Sep 2013 14:28:00 -0600 Subject: perf stat: Add units to nanosec-based counters Ingo pointed out that the task-clock counter should have the units explicitly stated since it is not a counter. Before: perf stat -a -- sleep 1 Performance counter stats for 'sleep 1': 16186.874834 task-clock # 16.154 CPUs utilized ... After: perf stat -a -- sleep 1 Performance counter stats for 'system wide': 16146.402138 task-clock (msec) # 16.125 CPUs utilized ... Reported-by: Ingo Molnar Signed-off-by: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1380400080-9211-4-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 2178e66..1a9c95d 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -706,10 +706,13 @@ static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) { double msecs = avg / 1e6; const char *fmt = csv_output ? "%.6f%s%s" : "%18.6f%s%-25s"; + char name[25]; aggr_printout(evsel, cpu, nr); - fprintf(output, fmt, msecs, csv_sep, perf_evsel__name(evsel)); + scnprintf(name, sizeof(name), "%s%s", + perf_evsel__name(evsel), csv_output ? "" : " (msec)"); + fprintf(output, fmt, msecs, csv_sep, name); if (evsel->cgrp) fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); -- cgit v0.10.2 From 8fb598e5a3b0ac213012e8461a309843ba0f2e74 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Sat, 28 Sep 2013 13:13:00 -0600 Subject: perf trace: Fix comm resolution when reading events from file Task comm's are getting lost when processing events from a file. The problem is that the trace struct used by the live processing has its host machine and the perf-session used for file based processing has its host machine. Fix by having both references point to the same machine. Before: 0.030 ( 0.001 ms): :27743/27743 brk( ... 0.057 ( 0.004 ms): :27743/27743 mmap(len: 4096, prot: READ|WRITE, flags: ... 0.075 ( 0.006 ms): :27743/27743 access(filename: 0x7f3809fbce00, mode: R ... 0.091 ( 0.005 ms): :27743/27743 open(filename: 0x7f3809fba14c, flags: CLOEXEC ... ... After: 0.030 ( 0.001 ms): make/27743 brk( ... 0.057 ( 0.004 ms): make/27743 mmap(len: 4096, prot: READ|WRITE, flags: ... 0.075 ( 0.006 ms): make/27743 access(filename: 0x7f3809fbce00, mode: R ... 0.091 ( 0.005 ms): make/27743 open(filename: 0x7f3809fba14c, flags: CLOEXEC ... ... Signed-off-by: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1380395584-9025-4-git-send-email-dsahern@gmail.com [ Moved creation of new host machine to a separate constructor: machine__new_host() ] Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index fcc157f..5776b5f 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -906,7 +906,7 @@ struct trace { struct syscall *table; } syscalls; struct perf_record_opts opts; - struct machine host; + struct machine *host; u64 base_time; bool full_time; FILE *output; @@ -1083,16 +1083,17 @@ static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) if (err) return err; - machine__init(&trace->host, "", HOST_KERNEL_ID); - machine__create_kernel_maps(&trace->host); + trace->host = machine__new_host(); + if (trace->host == NULL) + return -ENOMEM; if (perf_target__has_task(&trace->opts.target)) { err = perf_event__synthesize_thread_map(&trace->tool, evlist->threads, trace__tool_process, - &trace->host); + trace->host); } else { err = perf_event__synthesize_threads(&trace->tool, trace__tool_process, - &trace->host); + trace->host); } if (err) @@ -1304,8 +1305,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, if (sc->filtered) return 0; - thread = machine__findnew_thread(&trace->host, sample->pid, - sample->tid); + thread = machine__findnew_thread(trace->host, sample->pid, sample->tid); ttrace = thread__trace(thread, trace->output); if (ttrace == NULL) return -1; @@ -1357,8 +1357,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, if (sc->filtered) return 0; - thread = machine__findnew_thread(&trace->host, sample->pid, - sample->tid); + thread = machine__findnew_thread(trace->host, sample->pid, sample->tid); ttrace = thread__trace(thread, trace->output); if (ttrace == NULL) return -1; @@ -1414,7 +1413,7 @@ static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evs { u64 runtime = perf_evsel__intval(evsel, sample, "runtime"); double runtime_ms = (double)runtime / NSEC_PER_MSEC; - struct thread *thread = machine__findnew_thread(&trace->host, + struct thread *thread = machine__findnew_thread(trace->host, sample->pid, sample->tid); struct thread_trace *ttrace = thread__trace(thread, trace->output); @@ -1597,7 +1596,7 @@ again: trace->base_time = sample.time; if (type != PERF_RECORD_SAMPLE) { - trace__process_event(trace, &trace->host, event); + trace__process_event(trace, trace->host, event); continue; } @@ -1681,6 +1680,8 @@ static int trace__replay(struct trace *trace) if (session == NULL) return -ENOMEM; + trace->host = &session->machines.host; + err = perf_session__set_tracepoints_handlers(session, handlers); if (err) goto out; @@ -1728,7 +1729,7 @@ static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp) size_t printed = trace__fprintf_threads_header(fp); struct rb_node *nd; - for (nd = rb_first(&trace->host.threads); nd; nd = rb_next(nd)) { + for (nd = rb_first(&trace->host->threads); nd; nd = rb_next(nd)) { struct thread *thread = rb_entry(nd, struct thread, rb_node); struct thread_trace *ttrace = thread->priv; const char *color; diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index ddf917b..fc14f9b 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -46,6 +46,23 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid) return 0; } +struct machine *machine__new_host(void) +{ + struct machine *machine = malloc(sizeof(*machine)); + + if (machine != NULL) { + machine__init(machine, "", HOST_KERNEL_ID); + + if (machine__create_kernel_maps(machine) < 0) + goto out_delete; + } + + return machine; +out_delete: + free(machine); + return NULL; +} + static void dsos__delete(struct list_head *dsos) { struct dso *pos, *n; diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 58a6be1..5150d5e 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -74,6 +74,7 @@ char *machine__mmap_name(struct machine *machine, char *bf, size_t size); void machines__set_symbol_filter(struct machines *machines, symbol_filter_t symbol_filter); +struct machine *machine__new_host(void); int machine__init(struct machine *machine, const char *root_dir, pid_t pid); void machine__exit(struct machine *machine); void machine__delete_dead_threads(struct machine *machine); -- cgit v0.10.2 From 5e2485b1a2813faa6b80007c653f8bbbed9457ee Mon Sep 17 00:00:00 2001 From: David Ahern Date: Sat, 28 Sep 2013 13:13:01 -0600 Subject: perf trace: Add record option The record option is a convience alias to include the -e raw_syscalls:* argument to perf-record. All other options are passed to perf-record's handler. Resulting data file can be analyzed by perf-trace -i. Signed-off-by: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1380395584-9025-5-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index 7f70d36..1a22486 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt @@ -9,6 +9,7 @@ SYNOPSIS -------- [verse] 'perf trace' +'perf trace record' DESCRIPTION ----------- @@ -16,9 +17,14 @@ This command will show the events associated with the target, initially syscalls, but other system events like pagefaults, task lifetime events, scheduling events, etc. -Initially this is a live mode only tool, but eventually will work with -perf.data files like the other tools, allowing a detached 'record' from -analysis phases. +This is a live mode tool in addition to working with perf.data files like +the other perf tools. Files can be generated using the 'perf record' command +but the session needs to include the raw_syscalls events (-e 'raw_syscalls:*'). +Alernatively, the 'perf trace record' can be used as a shortcut to +automatically include the raw_syscalls events when writing events to a file. + +The following options apply to perf trace; options to perf trace record are +found in the perf record man page. OPTIONS ------- diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 5776b5f..1e2368f 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1501,6 +1501,33 @@ static int parse_target_str(struct trace *trace) return 0; } +static int trace__record(int argc, const char **argv) +{ + unsigned int rec_argc, i, j; + const char **rec_argv; + const char * const record_args[] = { + "record", + "-R", + "-m", "1024", + "-c", "1", + "-e", "raw_syscalls:sys_enter,raw_syscalls:sys_exit", + }; + + rec_argc = ARRAY_SIZE(record_args) + argc; + rec_argv = calloc(rec_argc + 1, sizeof(char *)); + + if (rec_argv == NULL) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(record_args); i++) + rec_argv[i] = record_args[i]; + + for (j = 0; j < (unsigned int)argc; j++, i++) + rec_argv[i] = argv[j]; + + return cmd_record(i, rec_argv, NULL); +} + static int trace__run(struct trace *trace, int argc, const char **argv) { struct perf_evlist *evlist = perf_evlist__new(); @@ -1788,6 +1815,8 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) const char * const trace_usage[] = { "perf trace [] []", "perf trace [] -- []", + "perf trace record [] []", + "perf trace record [] -- []", NULL }; struct trace trace = { @@ -1844,6 +1873,9 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) int err; char bf[BUFSIZ]; + if ((argc > 1) && (strcmp(argv[1], "record") == 0)) + return trace__record(argc-2, &argv[2]); + argc = parse_options(argc, argv, trace_options, trace_usage, 0); if (output_name != NULL) { -- cgit v0.10.2 From 35feee19f9fda7447f51073b5be3f6d082b508f5 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Sat, 28 Sep 2013 13:12:58 -0600 Subject: perf machine: Add method to loop over threads and invoke handler Loop over all threads within a machine - including threads moved to the dead threads list -- and invoked a function. This allows commands to run some specific function on each thread (eg., dump statistics) yet hides how the threads are maintained within the machine. Signed-off-by: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1380395584-9025-2-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index fc14f9b..901397a 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1393,3 +1393,26 @@ int machine__resolve_callchain(struct machine *machine, sample); } + +int machine__for_each_thread(struct machine *machine, + int (*fn)(struct thread *thread, void *p), + void *priv) +{ + struct rb_node *nd; + struct thread *thread; + int rc = 0; + + for (nd = rb_first(&machine->threads); nd; nd = rb_next(nd)) { + thread = rb_entry(nd, struct thread, rb_node); + rc = fn(thread, priv); + if (rc != 0) + return rc; + } + + list_for_each_entry(thread, &machine->dead_threads, node) { + rc = fn(thread, priv); + if (rc != 0) + return rc; + } + return rc; +} diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 5150d5e..d44c09b 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -166,4 +166,8 @@ void machines__destroy_kernel_maps(struct machines *machines); size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp); +int machine__for_each_thread(struct machine *machine, + int (*fn)(struct thread *thread, void *p), + void *priv); + #endif /* __PERF_MACHINE_H */ -- cgit v0.10.2 From 896cbb56bfee6ad99e0ee1b8209dc678f1a49f5a Mon Sep 17 00:00:00 2001 From: David Ahern Date: Sat, 28 Sep 2013 13:12:59 -0600 Subject: perf trace: Use new machine method to loop over threads Use the new machine method that loops over threads to dump summary data. Signed-off-by: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1380395584-9025-3-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 1e2368f..addc3e1 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1751,37 +1751,57 @@ static size_t trace__fprintf_threads_header(FILE *fp) return printed; } +/* struct used to pass data to per-thread function */ +struct summary_data { + FILE *fp; + struct trace *trace; + size_t printed; +}; + +static int trace__fprintf_one_thread(struct thread *thread, void *priv) +{ + struct summary_data *data = priv; + FILE *fp = data->fp; + size_t printed = data->printed; + struct trace *trace = data->trace; + struct thread_trace *ttrace = thread->priv; + const char *color; + double ratio; + + if (ttrace == NULL) + return 0; + + ratio = (double)ttrace->nr_events / trace->nr_events * 100.0; + + color = PERF_COLOR_NORMAL; + if (ratio > 50.0) + color = PERF_COLOR_RED; + else if (ratio > 25.0) + color = PERF_COLOR_GREEN; + else if (ratio > 5.0) + color = PERF_COLOR_YELLOW; + + printed += color_fprintf(fp, color, "%20s", thread->comm); + printed += fprintf(fp, " - %-5d :%11lu [", thread->tid, ttrace->nr_events); + printed += color_fprintf(fp, color, "%5.1f%%", ratio); + printed += fprintf(fp, " ] %10.3f ms\n", ttrace->runtime_ms); + + data->printed += printed; + + return 0; +} + static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp) { - size_t printed = trace__fprintf_threads_header(fp); - struct rb_node *nd; - - for (nd = rb_first(&trace->host->threads); nd; nd = rb_next(nd)) { - struct thread *thread = rb_entry(nd, struct thread, rb_node); - struct thread_trace *ttrace = thread->priv; - const char *color; - double ratio; - - if (ttrace == NULL) - continue; - - ratio = (double)ttrace->nr_events / trace->nr_events * 100.0; - - color = PERF_COLOR_NORMAL; - if (ratio > 50.0) - color = PERF_COLOR_RED; - else if (ratio > 25.0) - color = PERF_COLOR_GREEN; - else if (ratio > 5.0) - color = PERF_COLOR_YELLOW; - - printed += color_fprintf(fp, color, "%20s", thread->comm); - printed += fprintf(fp, " - %-5d :%11lu [", thread->tid, ttrace->nr_events); - printed += color_fprintf(fp, color, "%5.1f%%", ratio); - printed += fprintf(fp, " ] %10.3f ms\n", ttrace->runtime_ms); - } + struct summary_data data = { + .fp = fp, + .trace = trace + }; + data.printed = trace__fprintf_threads_header(fp); - return printed; + machine__for_each_thread(trace->host, trace__fprintf_one_thread, &data); + + return data.printed; } static int trace__set_duration(const struct option *opt, const char *str, -- cgit v0.10.2 From 2969b12993ca7a8b9692048431e075a67815002d Mon Sep 17 00:00:00 2001 From: David Ahern Date: Sat, 28 Sep 2013 13:13:02 -0600 Subject: perf intlist: Add priv member Allows commands to leverage intlist infrastructure for opaque structures. For example an upcoming perf-trace change will use this as a means of tracking syscalls statistics by task. Signed-off-by: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1380395584-9025-6-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/intlist.c b/tools/perf/util/intlist.c index 11a8d86..826d7b3 100644 --- a/tools/perf/util/intlist.c +++ b/tools/perf/util/intlist.c @@ -20,6 +20,7 @@ static struct rb_node *intlist__node_new(struct rblist *rblist __maybe_unused, if (node != NULL) { node->i = i; + node->priv = NULL; rc = &node->rb_node; } diff --git a/tools/perf/util/intlist.h b/tools/perf/util/intlist.h index 62351da..0eb00ac 100644 --- a/tools/perf/util/intlist.h +++ b/tools/perf/util/intlist.h @@ -9,6 +9,7 @@ struct int_node { struct rb_node rb_node; int i; + void *priv; }; struct intlist { -- cgit v0.10.2 From 316d70d6dbde540b275289563cbddd9f0c903fc6 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 8 Oct 2013 11:45:48 +0300 Subject: perf symbols: Make a separate function to parse /proc/modules Make a separate function to parse /proc/modules so that it can be reused. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1381221956-16699-2-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 901397a..6b861ae 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -793,12 +793,22 @@ static int machine__set_modules_path(struct machine *machine) return map_groups__set_modules_path_dir(&machine->kmaps, modules_path); } -static int machine__create_modules(struct machine *machine) +static int machine__create_module(void *arg, const char *name, u64 start) { - char *line = NULL; - size_t n; - FILE *file; + struct machine *machine = arg; struct map *map; + + map = machine__new_module(machine, start, name); + if (map == NULL) + return -1; + + dso__kernel_module_get_build_id(map->dso, machine->root_dir); + + return 0; +} + +static int machine__create_modules(struct machine *machine) +{ const char *modules; char path[PATH_MAX]; @@ -812,56 +822,15 @@ static int machine__create_modules(struct machine *machine) if (symbol__restricted_filename(modules, "/proc/modules")) return -1; - file = fopen(modules, "r"); - if (file == NULL) + if (modules__parse(modules, machine, machine__create_module)) return -1; - while (!feof(file)) { - char name[PATH_MAX]; - u64 start; - char *sep; - int line_len; - - line_len = getline(&line, &n, file); - if (line_len < 0) - break; - - if (!line) - goto out_failure; - - line[--line_len] = '\0'; /* \n */ - - sep = strrchr(line, 'x'); - if (sep == NULL) - continue; - - hex2u64(sep + 1, &start); - - sep = strchr(line, ' '); - if (sep == NULL) - continue; - - *sep = '\0'; - - snprintf(name, sizeof(name), "[%s]", line); - map = machine__new_module(machine, start, name); - if (map == NULL) - goto out_delete_line; - dso__kernel_module_get_build_id(map->dso, machine->root_dir); - } + if (!machine__set_modules_path(machine)) + return 0; - free(line); - fclose(file); + pr_debug("Problems setting modules path maps, continuing anyway...\n"); - if (machine__set_modules_path(machine) < 0) { - pr_debug("Problems setting modules path maps, continuing anyway...\n"); - } return 0; - -out_delete_line: - free(line); -out_failure: - return -1; } int machine__create_kernel_maps(struct machine *machine) diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 48c3879..5fd9513 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -500,6 +500,64 @@ out_failure: return -1; } +int modules__parse(const char *filename, void *arg, + int (*process_module)(void *arg, const char *name, + u64 start)) +{ + char *line = NULL; + size_t n; + FILE *file; + int err = 0; + + file = fopen(filename, "r"); + if (file == NULL) + return -1; + + while (1) { + char name[PATH_MAX]; + u64 start; + char *sep; + ssize_t line_len; + + line_len = getline(&line, &n, file); + if (line_len < 0) { + if (feof(file)) + break; + err = -1; + goto out; + } + + if (!line) { + err = -1; + goto out; + } + + line[--line_len] = '\0'; /* \n */ + + sep = strrchr(line, 'x'); + if (sep == NULL) + continue; + + hex2u64(sep + 1, &start); + + sep = strchr(line, ' '); + if (sep == NULL) + continue; + + *sep = '\0'; + + scnprintf(name, sizeof(name), "[%s]", line); + + err = process_module(arg, name, start); + if (err) + break; + } +out: + free(line); + fclose(file); + return err; +} + struct process_kallsyms_args { struct map *map; struct dso *dso; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 9b8b213..2d3eb43 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -223,6 +223,9 @@ int sysfs__read_build_id(const char *filename, void *bf, size_t size); int kallsyms__parse(const char *filename, void *arg, int (*process_symbol)(void *arg, const char *name, char type, u64 start)); +int modules__parse(const char *filename, void *arg, + int (*process_module)(void *arg, const char *name, + u64 start)); int filename__read_debuglink(const char *filename, char *debuglink, size_t size); -- cgit v0.10.2 From 03e3adc9f4d8b57dc83475c8c4c6e462a78ff709 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 8 Oct 2013 16:00:21 -0300 Subject: perf trace: Allow specifying index offset in strarrays So that the index passed doesn't have to start at zero, being decremented from an offset specified when declaring the strarray before being used as the real array index. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-k1ce6uqyt4qar9edrj3mevod@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index addc3e1..7424298 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -44,6 +44,7 @@ struct syscall_arg { }; struct strarray { + int offset; int nr_entries; const char **entries; }; @@ -53,14 +54,20 @@ struct strarray { .entries = array, \ } +#define DEFINE_STRARRAY_OFFSET(array, off) struct strarray strarray__##array = { \ + .offset = off, \ + .nr_entries = ARRAY_SIZE(array), \ + .entries = array, \ +} + static size_t syscall_arg__scnprintf_strarray(char *bf, size_t size, struct syscall_arg *arg) { - int idx = arg->val; struct strarray *sa = arg->parm; + int idx = arg->val - sa->offset; if (idx < 0 || idx >= sa->nr_entries) - return scnprintf(bf, size, "%d", idx); + return scnprintf(bf, size, "%d", arg->val); return scnprintf(bf, size, "%s", sa->entries[idx]); } @@ -288,8 +295,8 @@ static size_t syscall_arg__scnprintf_futex_op(char *bf, size_t size, struct sysc #define SCA_FUTEX_OP syscall_arg__scnprintf_futex_op -static const char *epoll_ctl_ops[] = { [1] = "ADD", "DEL", "MOD", }; -static DEFINE_STRARRAY(epoll_ctl_ops); +static const char *epoll_ctl_ops[] = { "ADD", "DEL", "MOD", }; +static DEFINE_STRARRAY_OFFSET(epoll_ctl_ops, 1); static const char *itimers[] = { "REAL", "VIRTUAL", "PROF", }; static DEFINE_STRARRAY(itimers); -- cgit v0.10.2 From 975b7c2f40d431da4fdb46dd8b9d90c129eecc12 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 8 Oct 2013 17:17:43 -0300 Subject: perf trace: Prepare the strarray scnprintf method for reuse Right now when an index passed to that method has no string associated it'll print the index as a decimal number, prepare it so that we can use it to print it in hex as well, for ioctls, for instance. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-nsvy06sqj64qvnkmzvwxsx2v@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 7424298..0d4af1d 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -60,18 +60,25 @@ struct strarray { .entries = array, \ } -static size_t syscall_arg__scnprintf_strarray(char *bf, size_t size, - struct syscall_arg *arg) +static size_t __syscall_arg__scnprintf_strarray(char *bf, size_t size, + const char *intfmt, + struct syscall_arg *arg) { struct strarray *sa = arg->parm; int idx = arg->val - sa->offset; if (idx < 0 || idx >= sa->nr_entries) - return scnprintf(bf, size, "%d", arg->val); + return scnprintf(bf, size, intfmt, arg->val); return scnprintf(bf, size, "%s", sa->entries[idx]); } +static size_t syscall_arg__scnprintf_strarray(char *bf, size_t size, + struct syscall_arg *arg) +{ + return __syscall_arg__scnprintf_strarray(bf, size, "%d", arg); +} + #define SCA_STRARRAY syscall_arg__scnprintf_strarray static size_t syscall_arg__scnprintf_fd(char *bf, size_t size, -- cgit v0.10.2 From 78645cf3ed32860a3e83b8e35aa469f5b844a4ba Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 8 Oct 2013 17:43:20 -0300 Subject: perf trace: Initial beautifier for ioctl's 'cmd' arg [root@zoo linux]# trace -e ioctl | grep -v "cmd: 0x" | head -10 0.386 ( 0.001 ms): trace/1602 ioctl(fd: 1, cmd: TCGETS, arg: 0x7fff59fcb4d0 ) = -1 ENOTTY Inappropriate ioctl for device 1459.368 ( 0.002 ms): inotify_reader/10352 ioctl(fd: 18, cmd: FIONREAD, arg: 0x7fb835228bcc ) = 0 1463.586 ( 0.002 ms): inotify_reader/10352 ioctl(fd: 18, cmd: FIONREAD, arg: 0x7fb835228bcc ) = 0 1463.611 ( 0.002 ms): inotify_reader/10352 ioctl(fd: 18, cmd: FIONREAD, arg: 0x7fb835228bcc ) = 0 3740.526 ( 0.002 ms): awk/1612 ioctl(fd: 1, cmd: TCGETS, arg: 0x7fff4d166b90 ) = -1 ENOTTY Inappropriate ioctl for device 3740.704 ( 0.001 ms): awk/1612 ioctl(fd: 3, cmd: TCGETS, arg: 0x7fff4d1669a0 ) = -1 ENOTTY Inappropriate ioctl for device 3742.550 ( 0.002 ms): ps/1614 ioctl(fd: 1, cmd: TIOCGWINSZ, arg: 0x7fff591762b0 ) = -1 ENOTTY Inappropriate ioctl for device 3742.555 ( 0.003 ms): ps/1614 ioctl(fd: 2, cmd: TIOCGWINSZ, arg: 0x7fff591762b0 ) = -1 ENOTTY Inappropriate ioctl for device 3742.558 ( 0.002 ms): ps/1614 ioctl(cmd: TIOCGWINSZ, arg: 0x7fff591762b0 ) = -1 ENOTTY Inappropriate ioctl for device 3742.572 ( 0.002 ms): ps/1614 ioctl(fd: 1, cmd: TCGETS, arg: 0x7fff59176220 ) = -1 ENOTTY Inappropriate ioctl for device [root@zoo linux]# Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-afajwap3mr60dfl4qpdl1pxn@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 0d4af1d..19ddcab 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -81,6 +81,14 @@ static size_t syscall_arg__scnprintf_strarray(char *bf, size_t size, #define SCA_STRARRAY syscall_arg__scnprintf_strarray +static size_t syscall_arg__scnprintf_strhexarray(char *bf, size_t size, + struct syscall_arg *arg) +{ + return __syscall_arg__scnprintf_strarray(bf, size, "%#x", arg); +} + +#define SCA_STRHEXARRAY syscall_arg__scnprintf_strhexarray + static size_t syscall_arg__scnprintf_fd(char *bf, size_t size, struct syscall_arg *arg); @@ -633,6 +641,28 @@ static size_t syscall_arg__scnprintf_signum(char *bf, size_t size, struct syscal #define SCA_SIGNUM syscall_arg__scnprintf_signum +#define TCGETS 0x5401 + +static const char *tioctls[] = { + "TCGETS", "TCSETS", "TCSETSW", "TCSETSF", "TCGETA", "TCSETA", "TCSETAW", + "TCSETAF", "TCSBRK", "TCXONC", "TCFLSH", "TIOCEXCL", "TIOCNXCL", + "TIOCSCTTY", "TIOCGPGRP", "TIOCSPGRP", "TIOCOUTQ", "TIOCSTI", + "TIOCGWINSZ", "TIOCSWINSZ", "TIOCMGET", "TIOCMBIS", "TIOCMBIC", + "TIOCMSET", "TIOCGSOFTCAR", "TIOCSSOFTCAR", "FIONREAD", "TIOCLINUX", + "TIOCCONS", "TIOCGSERIAL", "TIOCSSERIAL", "TIOCPKT", "FIONBIO", + "TIOCNOTTY", "TIOCSETD", "TIOCGETD", "TCSBRKP", [0x27] = "TIOCSBRK", + "TIOCCBRK", "TIOCGSID", "TCGETS2", "TCSETS2", "TCSETSW2", "TCSETSF2", + "TIOCGRS485", "TIOCSRS485", "TIOCGPTN", "TIOCSPTLCK", + "TIOCGDEV||TCGETX", "TCSETX", "TCSETXF", "TCSETXW", "TIOCSIG", + "TIOCVHANGUP", "TIOCGPKT", "TIOCGPTLCK", "TIOCGEXCL", + [0x50] = "FIONCLEX", "FIOCLEX", "FIOASYNC", "TIOCSERCONFIG", + "TIOCSERGWILD", "TIOCSERSWILD", "TIOCGLCKTRMIOS", "TIOCSLCKTRMIOS", + "TIOCSERGSTRUCT", "TIOCSERGETLSR", "TIOCSERGETMULTI", "TIOCSERSETMULTI", + "TIOCMIWAIT", "TIOCGICOUNT", [0x60] = "FIOQSIZE", +}; + +static DEFINE_STRARRAY_OFFSET(tioctls, 0x5401); + #define STRARRAY(arg, name, array) \ .arg_scnprintf = { [arg] = SCA_STRARRAY, }, \ .arg_parm = { [arg] = &strarray__##array, } @@ -713,7 +743,9 @@ static struct syscall_fmt { { .name = "getrlimit", .errmsg = true, STRARRAY(0, resource, rlimit_resources), }, { .name = "ioctl", .errmsg = true, .arg_scnprintf = { [0] = SCA_FD, /* fd */ - [2] = SCA_HEX, /* arg */ }, }, + [1] = SCA_STRHEXARRAY, /* cmd */ + [2] = SCA_HEX, /* arg */ }, + .arg_parm = { [1] = &strarray__tioctls, /* cmd */ }, }, { .name = "kill", .errmsg = true, .arg_scnprintf = { [1] = SCA_SIGNUM, /* sig */ }, }, { .name = "linkat", .errmsg = true, -- cgit v0.10.2 From cee972c0e6940ec75bab2d02f37e96d06ce143eb Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Thu, 10 Oct 2013 07:42:56 +0200 Subject: perf tools: Fix redirection printouts Fix the duplicate util/util printout Arnaldo reported: $ make V=1 O=/tmp/build/perf -C tools/perf/ util/srcline.o ... # Redirected target util/srcline.o => /tmp/build/perf/util/util/srcline.o Reported-by: Arnaldo Carvalho de Melo Signed-off-by: Ingo Molnar Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20131010054256.GA23716@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 2badb08..8bc6d0c 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -600,11 +600,11 @@ ifneq ($(OUTPUT),) %.o: $(OUTPUT)%.o @echo " # Redirected target $@ => $(OUTPUT)$@" util/%.o: $(OUTPUT)util/%.o - @echo " # Redirected target $@ => $(OUTPUT)util/$@" + @echo " # Redirected target $@ => $(OUTPUT)$@" bench/%.o: $(OUTPUT)bench/%.o - @echo " # Redirected target $@ => $(OUTPUT)bench/$@" + @echo " # Redirected target $@ => $(OUTPUT)$@" tests/%.o: $(OUTPUT)tests/%.o - @echo " # Redirected target $@ => $(OUTPUT)tests/$@" + @echo " # Redirected target $@ => $(OUTPUT)$@" endif # These two need to be here so that when O= is not used they take precedence -- cgit v0.10.2 From 8ec19c0eba73d7221e93e993c1c8bd62484354e9 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 9 Oct 2013 11:49:26 +0200 Subject: perf tools: Implement summary output for 'make clean' 'make clean' used to show all the rm lines, which isn't really informative in any way and spams the console. Implement summary output: comet:~/tip/tools/perf> make clean CLEAN libtraceevent CLEAN liblk CLEAN config CLEAN core-objs CLEAN core-progs CLEAN core-gen CLEAN Documentation CLEAN python 'make clean V=1' will still show the old, detailed output. Signed-off-by: Ingo Molnar Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1381312169-17354-2-git-send-email-mingo@kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Documentation/Makefile b/tools/perf/Documentation/Makefile index eaa477f..be5adb1 100644 --- a/tools/perf/Documentation/Makefile +++ b/tools/perf/Documentation/Makefile @@ -246,15 +246,17 @@ $(OUTPUT)cmd-list.made: cmd-list.perl ../command-list.txt $(MAN1_TXT) $(PERL_PATH) ./cmd-list.perl ../command-list.txt $(QUIET_STDERR) && \ date >$@ +CLEAN_FILES = \ + $(MAN_XML) $(addsuffix +,$(MAN_XML)) \ + $(MAN_HTML) $(addsuffix +,$(MAN_HTML)) \ + $(DOC_HTML) $(DOC_MAN1) $(DOC_MAN5) $(DOC_MAN7) \ + $(OUTPUT)*.texi $(OUTPUT)*.texi+ $(OUTPUT)*.texi++ \ + $(OUTPUT)perf.info $(OUTPUT)perfman.info \ + $(OUTPUT)howto-index.txt $(OUTPUT)howto/*.html $(OUTPUT)doc.dep \ + $(OUTPUT)technical/api-*.html $(OUTPUT)technical/api-index.txt \ + $(cmds_txt) $(OUTPUT)*.made clean: - $(RM) $(MAN_XML) $(addsuffix +,$(MAN_XML)) - $(RM) $(MAN_HTML) $(addsuffix +,$(MAN_HTML)) - $(RM) $(DOC_HTML) $(DOC_MAN1) $(DOC_MAN5) $(DOC_MAN7) - $(RM) $(OUTPUT)*.texi $(OUTPUT)*.texi+ $(OUTPUT)*.texi++ - $(RM) $(OUTPUT)perf.info $(OUTPUT)perfman.info - $(RM) $(OUTPUT)howto-index.txt $(OUTPUT)howto/*.html $(OUTPUT)doc.dep - $(RM) $(OUTPUT)technical/api-*.html $(OUTPUT)technical/api-index.txt - $(RM) $(cmds_txt) $(OUTPUT)*.made + $(call QUIET_CLEAN, Documentation) $(RM) $(CLEAN_FILES) $(MAN_HTML): $(OUTPUT)%.html : %.txt $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \ diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 8bc6d0c..250b276 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -148,7 +148,7 @@ PYTHON_EXTBUILD_LIB := $(PYTHON_EXTBUILD)lib/ PYTHON_EXTBUILD_TMP := $(PYTHON_EXTBUILD)tmp/ export PYTHON_EXTBUILD_LIB PYTHON_EXTBUILD_TMP -python-clean := rm -rf $(PYTHON_EXTBUILD) $(OUTPUT)python/perf.so +python-clean := $(call QUIET_CLEAN, python) $(RM) -r $(PYTHON_EXTBUILD) $(OUTPUT)python/perf.so PYTHON_EXT_SRCS := $(shell grep -v ^\# util/python-ext-sources) PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py $(LIBTRACEEVENT) $(LIBLK) @@ -708,7 +708,8 @@ $(LIBTRACEEVENT): $(TE_SOURCES) $(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) libtraceevent.a $(LIBTRACEEVENT)-clean: - $(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) clean + $(call QUIET_CLEAN, libtraceevent) + @$(MAKE) -C $(TRACE_EVENT_DIR) O=$(OUTPUT) clean >/dev/null LIBLK_SOURCES = $(wildcard $(LK_PATH)*.[ch]) @@ -721,7 +722,8 @@ endif $(LIBLK)-clean: ifeq ($(subdir),) - $(QUIET_SUBDIR0)$(LK_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) clean + $(call QUIET_CLEAN, liblk) + @$(MAKE) -C $(LK_DIR) O=$(OUTPUT) clean >/dev/null endif help: @@ -850,17 +852,15 @@ $(INSTALL_DOC_TARGETS): # not get included for the clean target: # config-clean: - @$(MAKE) -C config/feature-checks clean + $(call QUIET_CLEAN, config) + @$(MAKE) -C config/feature-checks clean >/dev/null clean: $(LIBTRACEEVENT)-clean $(LIBLK)-clean config-clean - $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(GTK_OBJS) - $(RM) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS) - $(RM) $(ALL_PROGRAMS) perf - $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* - $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean - $(RM) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS - $(RM) $(OUTPUT)util/*-bison* - $(RM) $(OUTPUT)util/*-flex* + $(call QUIET_CLEAN, core-objs) $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS) $(GTK_OBJS) + $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf + $(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* + $(call QUIET_CLEAN, Documentation) + @$(MAKE) -C Documentation O=$(OUTPUT) clean >/dev/null $(python-clean) # diff --git a/tools/perf/config/utilities.mak b/tools/perf/config/utilities.mak index 4d985e0..94908a5 100644 --- a/tools/perf/config/utilities.mak +++ b/tools/perf/config/utilities.mak @@ -178,3 +178,9 @@ endef _ge_attempt = $(if $(get-executable),$(get-executable),$(_gea_warn)$(call _gea_err,$(2))) _gea_warn = $(warning The path '$(1)' is not executable.) _gea_err = $(if $(1),$(error Please set '$(1)' appropriately)) + +ifneq ($(findstring $(MAKEFLAGS),s),s) + ifneq ($(V),1) + QUIET_CLEAN = @printf ' CLEAN %s\n' $(1); + endif +endif diff --git a/tools/scripts/Makefile.include b/tools/scripts/Makefile.include index 0d0506d..1853736 100644 --- a/tools/scripts/Makefile.include +++ b/tools/scripts/Makefile.include @@ -66,7 +66,7 @@ ifneq ($(V),1) QUIET_MKDIR = @echo ' ' MKDIR $@; QUIET_GEN = @echo ' ' GEN $@; QUIET_SUBDIR0 = +@subdir= - QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \ + QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \ $(MAKE) $(PRINT_DIR) -C $$subdir QUIET_FLEX = @echo ' ' FLEX $@; QUIET_BISON = @echo ' ' BISON $@; -- cgit v0.10.2 From 65fb09922d4ca5da54fe18d7b44e5961caf169ad Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 9 Oct 2013 11:49:27 +0200 Subject: tools: Harmonize the various build messages in perf, lib-traceevent, lib-lk The various build lines from libtraceevent and perf mix up during a parallel build and produce unaligned output like: CC builtin-buildid-list.o CC builtin-buildid-cache.o CC builtin-list.o CC FPIC trace-seq.o CC builtin-record.o CC FPIC parse-filter.o CC builtin-report.o CC builtin-stat.o CC FPIC parse-utils.o CC FPIC kbuffer-parse.o CC builtin-timechart.o CC builtin-top.o CC builtin-script.o BUILD STATIC LIB libtraceevent.a CC builtin-probe.o CC builtin-kmem.o CC builtin-lock.o To solve this, harmonize all the build message alignments to be similar to the kernel's kbuild output: prefixed by two spaces and 11-char wide. After the patch the output looks pretty tidy, even if output lines get mixed up: CC builtin-annotate.o FLAGS: * new build flags or cross compiler CC builtin-bench.o AR liblk.a CC bench/sched-messaging.o CC FPIC event-parse.o CC bench/sched-pipe.o CC FPIC trace-seq.o CC bench/mem-memcpy.o CC bench/mem-memset.o CC FPIC parse-filter.o CC builtin-diff.o CC builtin-evlist.o CC builtin-help.o Signed-off-by: Ingo Molnar Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1381312169-17354-3-git-send-email-mingo@kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/lib/traceevent/Makefile b/tools/lib/traceevent/Makefile index ca6cb77..fc15020 100644 --- a/tools/lib/traceevent/Makefile +++ b/tools/lib/traceevent/Makefile @@ -134,14 +134,14 @@ ifeq ($(VERBOSE),1) print_install = else Q = @ - print_compile = echo ' CC '$(OBJ); - print_app_build = echo ' BUILD '$(OBJ); - print_fpic_compile = echo ' CC FPIC '$(OBJ); - print_shared_lib_compile = echo ' BUILD SHARED LIB '$(OBJ); - print_plugin_obj_compile = echo ' CC PLUGIN OBJ '$(OBJ); - print_plugin_build = echo ' CC PLUGI '$(OBJ); - print_static_lib_build = echo ' BUILD STATIC LIB '$(OBJ); - print_install = echo ' INSTALL '$1' to $(DESTDIR_SQ)$2'; + print_compile = echo ' CC '$(OBJ); + print_app_build = echo ' BUILD '$(OBJ); + print_fpic_compile = echo ' CC FPIC '$(OBJ); + print_shared_lib_compile = echo ' BUILD SHARED LIB '$(OBJ); + print_plugin_obj_compile = echo ' BUILD PLUGIN OBJ '$(OBJ); + print_plugin_build = echo ' BUILD PLUGIN '$(OBJ); + print_static_lib_build = echo ' BUILD STATIC LIB '$(OBJ); + print_install = echo ' INSTALL '$1' to $(DESTDIR_SQ)$2'; endif do_fpic_compile = \ @@ -268,7 +268,7 @@ TRACK_CFLAGS = $(subst ','\'',$(CFLAGS)):$(ARCH):$(CROSS_COMPILE) TRACEEVENT-CFLAGS: force @FLAGS='$(TRACK_CFLAGS)'; \ if test x"$$FLAGS" != x"`cat TRACEEVENT-CFLAGS 2>/dev/null`" ; then \ - echo 1>&2 " * new build flags or cross compiler"; \ + echo 1>&2 " FLAGS: * new build flags or cross compiler"; \ echo "$$FLAGS" >TRACEEVENT-CFLAGS; \ fi diff --git a/tools/perf/Documentation/Makefile b/tools/perf/Documentation/Makefile index be5adb1..c4c300c 100644 --- a/tools/perf/Documentation/Makefile +++ b/tools/perf/Documentation/Makefile @@ -145,16 +145,17 @@ endif ifneq ($(findstring $(MAKEFLAGS),s),s) ifneq ($(V),1) - QUIET_ASCIIDOC = @echo ' ' ASCIIDOC $@; - QUIET_XMLTO = @echo ' ' XMLTO $@; - QUIET_DB2TEXI = @echo ' ' DB2TEXI $@; - QUIET_MAKEINFO = @echo ' ' MAKEINFO $@; - QUIET_DBLATEX = @echo ' ' DBLATEX $@; - QUIET_XSLTPROC = @echo ' ' XSLTPROC $@; - QUIET_GEN = @echo ' ' GEN $@; + QUIET_ASCIIDOC = @echo ' ASCIIDOC '$@; + QUIET_XMLTO = @echo ' XMLTO '$@; + QUIET_DB2TEXI = @echo ' DB2TEXI '$@; + QUIET_MAKEINFO = @echo ' MAKEINFO '$@; + QUIET_DBLATEX = @echo ' DBLATEX '$@; + QUIET_XSLTPROC = @echo ' XSLTPROC '$@; + QUIET_GEN = @echo ' GEN '$@; QUIET_STDERR = 2> /dev/null QUIET_SUBDIR0 = +@subdir= - QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \ + QUIET_SUBDIR1 = ;$(NO_SUBDIR) \ + echo ' SUBDIR ' $$subdir; \ $(MAKE) $(PRINT_DIR) -C $$subdir export V endif diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 9580ebe..5aa3d04 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -38,7 +38,7 @@ ifneq ($(O),) endif define print_msg - @printf ' BUILD: Doing '\''make \033[33m-j'$(JOBS)'\033[m'\'' parallel build\n' + @printf ' BUILD: Doing '\''make \033[33m-j'$(JOBS)'\033[m'\'' parallel build\n' endef define make diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 250b276..f91bd5a 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -784,7 +784,7 @@ TRACK_CFLAGS = $(subst ','\'',$(CFLAGS)):\ $(OUTPUT)PERF-CFLAGS: .FORCE-PERF-CFLAGS @FLAGS='$(TRACK_CFLAGS)'; \ if test x"$$FLAGS" != x"`cat $(OUTPUT)PERF-CFLAGS 2>/dev/null`" ; then \ - echo 1>&2 " * new build flags or prefix"; \ + echo 1>&2 " FLAGS: * new build flags or prefix"; \ echo "$$FLAGS" >$(OUTPUT)PERF-CFLAGS; \ fi diff --git a/tools/scripts/Makefile.include b/tools/scripts/Makefile.include index 1853736..ee76544 100644 --- a/tools/scripts/Makefile.include +++ b/tools/scripts/Makefile.include @@ -59,21 +59,22 @@ QUIET_SUBDIR0 = +$(MAKE) $(COMMAND_O) -C # space to separate -C and subdir QUIET_SUBDIR1 = ifneq ($(findstring $(MAKEFLAGS),s),s) -ifneq ($(V),1) - QUIET_CC = @echo ' ' CC $@; - QUIET_AR = @echo ' ' AR $@; - QUIET_LINK = @echo ' ' LINK $@; - QUIET_MKDIR = @echo ' ' MKDIR $@; - QUIET_GEN = @echo ' ' GEN $@; + ifneq ($(V),1) + QUIET_CC = @echo ' CC '$@; + QUIET_AR = @echo ' AR '$@; + QUIET_LINK = @echo ' LINK '$@; + QUIET_MKDIR = @echo ' MKDIR '$@; + QUIET_GEN = @echo ' GEN '$@; QUIET_SUBDIR0 = +@subdir= - QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \ + QUIET_SUBDIR1 = ;$(NO_SUBDIR) \ + echo ' SUBDIR '$$subdir; \ $(MAKE) $(PRINT_DIR) -C $$subdir - QUIET_FLEX = @echo ' ' FLEX $@; - QUIET_BISON = @echo ' ' BISON $@; + QUIET_FLEX = @echo ' FLEX '$@; + QUIET_BISON = @echo ' BISON '$@; descend = \ - +@echo ' ' DESCEND $(1); \ + +@echo ' DESCEND '$(1); \ mkdir -p $(OUTPUT)$(1) && \ $(MAKE) $(COMMAND_O) subdir=$(if $(subdir),$(subdir)/$(1),$(1)) $(PRINT_DIR) -C $(1) $(2) -endif + endif endif -- cgit v0.10.2 From 3fae82db558468c01f36eb6398e9459ac240e697 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 9 Oct 2013 11:49:28 +0200 Subject: perf tools: Align perf version output to other build messages Before: CC util/pmu.o CC util/parse-events.o PERF_VERSION = 3.12.rc4.g1b30c CC util/parse-events-flex.o GEN perf-archive After: CC util/pmu.o CC util/parse-events.o PERF_VERSION = 3.12.rc4.g1b30c CC util/parse-events-flex.o GEN perf-archive Signed-off-by: Ingo Molnar Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1381312169-17354-4-git-send-email-mingo@kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/PERF-VERSION-GEN b/tools/perf/util/PERF-VERSION-GEN index 15a77b7..ce7a804 100755 --- a/tools/perf/util/PERF-VERSION-GEN +++ b/tools/perf/util/PERF-VERSION-GEN @@ -40,7 +40,7 @@ else VC=unset fi test "$VN" = "$VC" || { - echo >&2 "PERF_VERSION = $VN" + echo >&2 " PERF_VERSION = $VN" echo "#define PERF_VERSION \"$VN\"" >$GVF } -- cgit v0.10.2 From 8a5411e9a3e65ab70b094022e5aa6deaec82ae7c Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 9 Oct 2013 11:49:29 +0200 Subject: perf tools: Implement summary output for 'make install' 'make install' used to show all the install lines, which is way too verbose to be really informative to the user. Implement summary output instead: comet:~/tip/tools/perf> make install BUILD: Doing 'make -j12' parallel build SUBDIR Documentation INSTALL Documentation-man INSTALL binaries INSTALL libexec INSTALL perf-archive INSTALL perl-scripts INSTALL python-scripts INSTALL bash_completion-script INSTALL tests 'make install V=1' will still show the old, detailed output. Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Ingo Molnar Link: http://lkml.kernel.org/r/1381312169-17354-5-git-send-email-mingo@kernel.org [ Fixed conflict with libperf-gtk patches in acme/perf/core, cope with 'trace' alias ] Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Documentation/Makefile b/tools/perf/Documentation/Makefile index c4c300c..3ba1c0b 100644 --- a/tools/perf/Documentation/Makefile +++ b/tools/perf/Documentation/Makefile @@ -184,12 +184,13 @@ ifdef missing_tools endif do-install-man: man - $(INSTALL) -d -m 755 $(DESTDIR)$(man1dir) -# $(INSTALL) -d -m 755 $(DESTDIR)$(man5dir) -# $(INSTALL) -d -m 755 $(DESTDIR)$(man7dir) - $(INSTALL) -m 644 $(DOC_MAN1) $(DESTDIR)$(man1dir) -# $(INSTALL) -m 644 $(DOC_MAN5) $(DESTDIR)$(man5dir) -# $(INSTALL) -m 644 $(DOC_MAN7) $(DESTDIR)$(man7dir) + $(call QUIET_INSTALL, Documentation-man) \ + $(INSTALL) -d -m 755 $(DESTDIR)$(man1dir); \ +# $(INSTALL) -d -m 755 $(DESTDIR)$(man5dir); \ +# $(INSTALL) -d -m 755 $(DESTDIR)$(man7dir); \ + $(INSTALL) -m 644 $(DOC_MAN1) $(DESTDIR)$(man1dir); \ +# $(INSTALL) -m 644 $(DOC_MAN5) $(DESTDIR)$(man5dir); \ +# $(INSTALL) -m 644 $(DOC_MAN7) $(DESTDIR)$(man7dir) install-man: check-man-tools man @@ -202,18 +203,20 @@ endif try-install-man: $(DO_INSTALL_MAN) install-info: info - $(INSTALL) -d -m 755 $(DESTDIR)$(infodir) - $(INSTALL) -m 644 $(OUTPUT)perf.info $(OUTPUT)perfman.info $(DESTDIR)$(infodir) + $(call QUIET_INSTALL, Documentation-info) \ + $(INSTALL) -d -m 755 $(DESTDIR)$(infodir); \ + $(INSTALL) -m 644 $(OUTPUT)perf.info $(OUTPUT)perfman.info $(DESTDIR)$(infodir); \ if test -r $(DESTDIR)$(infodir)/dir; then \ - $(INSTALL_INFO) --info-dir=$(DESTDIR)$(infodir) perf.info ;\ - $(INSTALL_INFO) --info-dir=$(DESTDIR)$(infodir) perfman.info ;\ + $(INSTALL_INFO) --info-dir=$(DESTDIR)$(infodir) perf.info ;\ + $(INSTALL_INFO) --info-dir=$(DESTDIR)$(infodir) perfman.info ;\ else \ echo "No directory found in $(DESTDIR)$(infodir)" >&2 ; \ fi install-pdf: pdf - $(INSTALL) -d -m 755 $(DESTDIR)$(pdfdir) - $(INSTALL) -m 644 $(OUTPUT)user-manual.pdf $(DESTDIR)$(pdfdir) + $(call QUIET_INSTALL, Documentation-pdf) \ + $(INSTALL) -d -m 755 $(DESTDIR)$(pdfdir); \ + $(INSTALL) -m 644 $(OUTPUT)user-manual.pdf $(DESTDIR)$(pdfdir) #install-html: html # '$(SHELL_PATH_SQ)' ./install-webdoc.sh $(DESTDIR)$(htmldir) diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index f91bd5a..c873e03 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -504,8 +504,9 @@ ifndef NO_GTK2 GTK_OBJS += $(OUTPUT)ui/gtk/annotate.o install-gtk: $(OUTPUT)libperf-gtk.so - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(libdir_SQ)' - $(INSTALL) $(OUTPUT)libperf-gtk.so '$(DESTDIR_SQ)$(libdir_SQ)' + $(call QUIET_INSTALL, 'GTK UI') \ + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(libdir_SQ)'; \ + $(INSTALL) $(OUTPUT)libperf-gtk.so '$(DESTDIR_SQ)$(libdir_SQ)' endif ifndef NO_LIBPERL @@ -810,31 +811,38 @@ check: $(OUTPUT)common-cmds.h install-gtk: install-bin: all install-gtk - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' - $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)' - $(LN) '$(DESTDIR_SQ)$(bindir_SQ)/perf' '$(DESTDIR_SQ)$(bindir_SQ)/trace' - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' - $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' + $(call QUIET_INSTALL, binaries) \ + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'; \ + $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)'; \ + $(LN) '$(DESTDIR_SQ)$(bindir_SQ)/perf' '$(DESTDIR_SQ)$(bindir_SQ)/trace' + $(call QUIET_INSTALL, libexec) \ + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' + $(call QUIET_INSTALL, perf-archive) \ + $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' ifndef NO_LIBPERL - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' - $(INSTALL) scripts/perl/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' - $(INSTALL) scripts/perl/*.pl -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl' - $(INSTALL) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' + $(call QUIET_INSTALL, perl-scripts) \ + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'; \ + $(INSTALL) scripts/perl/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'; \ + $(INSTALL) scripts/perl/*.pl -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl'; \ + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin'; \ + $(INSTALL) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' endif ifndef NO_LIBPYTHON - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace' - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' - $(INSTALL) scripts/python/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace' - $(INSTALL) scripts/python/*.py -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python' - $(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' + $(call QUIET_INSTALL, python-scripts) \ + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace'; \ + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin'; \ + $(INSTALL) scripts/python/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace'; \ + $(INSTALL) scripts/python/*.py -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python'; \ + $(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' endif - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d' - $(INSTALL) bash_completion '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf' - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests' - $(INSTALL) tests/attr.py '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests' - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr' - $(INSTALL) tests/attr/* '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr' + $(call QUIET_INSTALL, bash_completion-script) \ + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d'; \ + $(INSTALL) bash_completion '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf' + $(call QUIET_INSTALL, tests) \ + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'; \ + $(INSTALL) tests/attr.py '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'; \ + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr'; \ + $(INSTALL) tests/attr/* '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr' install: install-bin try-install-man diff --git a/tools/perf/config/utilities.mak b/tools/perf/config/utilities.mak index 94908a5..f168deb 100644 --- a/tools/perf/config/utilities.mak +++ b/tools/perf/config/utilities.mak @@ -181,6 +181,7 @@ _gea_err = $(if $(1),$(error Please set '$(1)' appropriately)) ifneq ($(findstring $(MAKEFLAGS),s),s) ifneq ($(V),1) - QUIET_CLEAN = @printf ' CLEAN %s\n' $(1); + QUIET_CLEAN = @printf ' CLEAN %s\n' $1; + QUIET_INSTALL = @printf ' INSTALL %s\n' $1; endif endif -- cgit v0.10.2 From d366c53e1d4fc9d7a5826fd82010b3cffaabe5f1 Mon Sep 17 00:00:00 2001 From: Ramkumar Ramachandra Date: Thu, 3 Oct 2013 14:45:16 +0530 Subject: perf timechart: Add example in the documentation While at it, update the synopsis to show both forms. Signed-off-by: Ramkumar Ramachandra Cc: David Ahern Cc: Ingo Molnar Cc: Namhyung Kim Link: http://lkml.kernel.org/r/1380791716-10325-1-git-send-email-artagnon@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Documentation/perf-timechart.txt b/tools/perf/Documentation/perf-timechart.txt index 1632b0e..3ff8bd4 100644 --- a/tools/perf/Documentation/perf-timechart.txt +++ b/tools/perf/Documentation/perf-timechart.txt @@ -8,7 +8,8 @@ perf-timechart - Tool to visualize total system behavior during a workload SYNOPSIS -------- [verse] -'perf timechart' {record} +'perf timechart' record +'perf timechart' [] DESCRIPTION ----------- @@ -41,6 +42,18 @@ OPTIONS --symfs=:: Look for files with symbols relative to this directory. +EXAMPLES +-------- + +$ perf timechart record git pull + + [ perf record: Woken up 13 times to write data ] + [ perf record: Captured and wrote 4.253 MB perf.data (~185801 samples) ] + +$ perf timechart + + Written 10.2 seconds of trace to output.svg. + SEE ALSO -------- linkperf:perf-record[1] -- cgit v0.10.2 From e300376d3c3a1ed473bd7312ec74aace8f7cd2f0 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 11 Oct 2013 19:06:12 +0200 Subject: gpio: tc3589x: drop references to "virtual" IRQ Rename the argument "virq" to just "irq", this IRQ isn't any more "virtual" than any other Linux IRQ number, we use "hwirq" for the actual hw-numbers, "virq" is just bogus. Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c index 4a5de27..ddb5fef 100644 --- a/drivers/gpio/gpio-tc3589x.c +++ b/drivers/gpio/gpio-tc3589x.c @@ -96,27 +96,27 @@ static int tc3589x_gpio_direction_input(struct gpio_chip *chip, } /** - * tc3589x_gpio_irq_get_virq(): Map an interrupt on a chip to a virtual IRQ + * tc3589x_gpio_irq_get_irq(): Map a hardware IRQ on a chip to a Linux IRQ * * @tc3589x_gpio: tc3589x_gpio_irq controller to operate on. - * @irq: index of the interrupt requested in the chip IRQs + * @irq: index of the hardware interrupt requested in the chip IRQs * * Useful for drivers to request their own IRQs. */ -static int tc3589x_gpio_irq_get_virq(struct tc3589x_gpio *tc3589x_gpio, - int irq) +static int tc3589x_gpio_irq_get_irq(struct tc3589x_gpio *tc3589x_gpio, + int hwirq) { if (!tc3589x_gpio) return -EINVAL; - return irq_create_mapping(tc3589x_gpio->domain, irq); + return irq_create_mapping(tc3589x_gpio->domain, hwirq); } static int tc3589x_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip); - return tc3589x_gpio_irq_get_virq(tc3589x_gpio, offset); + return tc3589x_gpio_irq_get_irq(tc3589x_gpio, offset); } static struct gpio_chip template_chip = { @@ -242,9 +242,9 @@ static irqreturn_t tc3589x_gpio_irq(int irq, void *dev) while (stat) { int bit = __ffs(stat); int line = i * 8 + bit; - int virq = tc3589x_gpio_irq_get_virq(tc3589x_gpio, line); + int irq = tc3589x_gpio_irq_get_irq(tc3589x_gpio, line); - handle_nested_irq(virq); + handle_nested_irq(irq); stat &= ~(1 << bit); } @@ -254,31 +254,31 @@ static irqreturn_t tc3589x_gpio_irq(int irq, void *dev) return IRQ_HANDLED; } -static int tc3589x_gpio_irq_map(struct irq_domain *d, unsigned int virq, +static int tc3589x_gpio_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hwirq) { struct tc3589x *tc3589x_gpio = d->host_data; - irq_set_chip_data(virq, tc3589x_gpio); - irq_set_chip_and_handler(virq, &tc3589x_gpio_irq_chip, + irq_set_chip_data(irq, tc3589x_gpio); + irq_set_chip_and_handler(irq, &tc3589x_gpio_irq_chip, handle_simple_irq); - irq_set_nested_thread(virq, 1); + irq_set_nested_thread(irq, 1); #ifdef CONFIG_ARM - set_irq_flags(virq, IRQF_VALID); + set_irq_flags(irq, IRQF_VALID); #else - irq_set_noprobe(virq); + irq_set_noprobe(irq); #endif return 0; } -static void tc3589x_gpio_irq_unmap(struct irq_domain *d, unsigned int virq) +static void tc3589x_gpio_irq_unmap(struct irq_domain *d, unsigned int irq) { #ifdef CONFIG_ARM - set_irq_flags(virq, 0); + set_irq_flags(irq, 0); #endif - irq_set_chip_and_handler(virq, NULL, NULL); - irq_set_chip_data(virq, NULL); + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_chip_data(irq, NULL); } static struct irq_domain_ops tc3589x_irq_ops = { -- cgit v0.10.2 From 740ad6c328823f066efb8b907576a54ef92aca69 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 11 Oct 2013 00:06:34 -0700 Subject: ASoC: rcar: fixup rsnd_platform_call() return value Un-implemented platform callback is not error. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index fc83f0f..28c24fc 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -103,7 +103,7 @@ * rsnd_platform functions */ #define rsnd_platform_call(priv, dai, func, param...) \ - (!(priv->info->func) ? -ENODEV : \ + (!(priv->info->func) ? 0 : \ priv->info->func(param)) /* -- cgit v0.10.2 From 2192f81c53a7879c803f0f7d6c49645fdf6c2f6a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 11 Oct 2013 00:07:48 -0700 Subject: ASoC: rcar: add ID check on rsnd_dai_get() checking id in rsnd_dai_get() is good idea Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 28c24fc..b234ed6 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -318,6 +318,9 @@ int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai) struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id) { + if ((id < 0) || (id >= rsnd_dai_nr(priv))) + return NULL; + return priv->rdai + id; } -- cgit v0.10.2 From 2841a5fc375e9c573d10b82db30fa8a4cc25301c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 5 Oct 2013 00:23:12 +0100 Subject: spi: Provide per-message prepare and unprepare operations Many SPI drivers perform setup and tear down on every message, usually doing things like DMA mapping the message. Provide hooks for them to use to provide such operations. This is of limited value for drivers that implement transfer_one_message() but will be of much greater utility with future factoring out of standard implementations of that function. Signed-off-by: Mark Brown diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 8bef0c9..8a30c6b 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -606,6 +606,18 @@ static void spi_pump_messages(struct kthread_work *work) trace_spi_message_start(master->cur_msg); + if (master->prepare_message) { + ret = master->prepare_message(master, master->cur_msg); + if (ret) { + dev_err(&master->dev, + "failed to prepare message: %d\n", ret); + master->cur_msg->status = ret; + spi_finalize_current_message(master); + return; + } + master->cur_msg_prepared = true; + } + ret = master->transfer_one_message(master, master->cur_msg); if (ret) { dev_err(&master->dev, @@ -687,6 +699,7 @@ void spi_finalize_current_message(struct spi_master *master) { struct spi_message *mesg; unsigned long flags; + int ret; spin_lock_irqsave(&master->queue_lock, flags); mesg = master->cur_msg; @@ -695,6 +708,15 @@ void spi_finalize_current_message(struct spi_master *master) queue_kthread_work(&master->kworker, &master->pump_messages); spin_unlock_irqrestore(&master->queue_lock, flags); + if (master->cur_msg_prepared && master->unprepare_message) { + ret = master->unprepare_message(master, mesg); + if (ret) { + dev_err(&master->dev, + "failed to unprepare message: %d\n", ret); + } + } + master->cur_msg_prepared = false; + mesg->state = NULL; if (mesg->complete) mesg->complete(mesg->context); diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 887116d..000b50b 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -257,6 +257,8 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * @queue_lock: spinlock to syncronise access to message queue * @queue: message queue * @cur_msg: the currently in-flight message + * @cur_msg_prepared: spi_prepare_message was called for the currently + * in-flight message * @busy: message pump is busy * @running: message pump is running * @rt: whether this queue is set to run as a realtime task @@ -274,6 +276,10 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * @unprepare_transfer_hardware: there are currently no more messages on the * queue so the subsystem notifies the driver that it may relax the * hardware by issuing this call + * @prepare_message: set up the controller to transfer a single message, + * for example doing DMA mapping. Called from threaded + * context. + * @unprepare_message: undo any work done by prepare_message(). * @cs_gpios: Array of GPIOs to use as chip select lines; one per CS * number. Any individual value may be -ENOENT for CS lines that * are not GPIOs (driven by the SPI controller itself). @@ -388,11 +394,16 @@ struct spi_master { bool running; bool rt; bool auto_runtime_pm; + bool cur_msg_prepared; int (*prepare_transfer_hardware)(struct spi_master *master); int (*transfer_one_message)(struct spi_master *master, struct spi_message *mesg); int (*unprepare_transfer_hardware)(struct spi_master *master); + int (*prepare_message)(struct spi_master *master, + struct spi_message *message); + int (*unprepare_message)(struct spi_master *master, + struct spi_message *message); /* gpio chip select */ int *cs_gpios; -- cgit v0.10.2 From 6bb9c0e34185717f5b0df4ad468476f64f0d73fb Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 5 Oct 2013 00:42:58 +0100 Subject: spi/s3c64xx: Use prepare_message() and unprepare_message() This is of very little value in itself but will be useful once the loop iterating over the transfers is also factored out into the core. Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 512b889..27b744b 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -862,16 +862,12 @@ static void s3c64xx_spi_unmap_mssg(struct s3c64xx_spi_driver_data *sdd, } } -static int s3c64xx_spi_transfer_one_message(struct spi_master *master, - struct spi_message *msg) +static int s3c64xx_spi_prepare_message(struct spi_master *master, + struct spi_message *msg) { struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); struct spi_device *spi = msg->spi; struct s3c64xx_spi_csinfo *cs = spi->controller_data; - struct spi_transfer *xfer; - int status = 0, cs_toggle = 0; - u32 speed; - u8 bpw; /* If Master's(controller) state differs from that needed by Slave */ if (sdd->cur_speed != spi->max_speed_hz @@ -887,13 +883,25 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master, if (s3c64xx_spi_map_mssg(sdd, msg)) { dev_err(&spi->dev, "Xfer: Unable to map message buffers!\n"); - status = -ENOMEM; - goto out; + return -ENOMEM; } /* Configure feedback delay */ writel(cs->fb_delay & 0x3, sdd->regs + S3C64XX_SPI_FB_CLK); + return 0; +} + +static int s3c64xx_spi_transfer_one_message(struct spi_master *master, + struct spi_message *msg) +{ + struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); + struct spi_device *spi = msg->spi; + struct spi_transfer *xfer; + int status = 0, cs_toggle = 0; + u32 speed; + u8 bpw; + list_for_each_entry(xfer, &msg->transfers, transfer_list) { unsigned long flags; @@ -991,6 +999,16 @@ out: return 0; } +static int s3c64xx_spi_unprepare_message(struct spi_master *master, + struct spi_message *msg) +{ + struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); + + s3c64xx_spi_unmap_mssg(sdd, msg); + + return 0; +} + static struct s3c64xx_spi_csinfo *s3c64xx_get_slave_ctrldata( struct spi_device *spi) { @@ -1359,7 +1377,9 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) master->setup = s3c64xx_spi_setup; master->cleanup = s3c64xx_spi_cleanup; master->prepare_transfer_hardware = s3c64xx_spi_prepare_transfer; + master->prepare_message = s3c64xx_spi_prepare_message; master->transfer_one_message = s3c64xx_spi_transfer_one_message; + master->unprepare_message = s3c64xx_spi_unprepare_message; master->unprepare_transfer_hardware = s3c64xx_spi_unprepare_transfer; master->num_chipselect = sci->num_cs; master->dma_alignment = 8; -- cgit v0.10.2 From b158935f70b9c156903338053216dd0adf7ce31c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 5 Oct 2013 11:50:40 +0100 Subject: spi: Provide common spi_message processing loop The loops which SPI controller drivers use to process the list of transfers in a spi_message are typically very similar and have some error prone areas such as the handling of /CS. Help simplify drivers by factoring this code out into the core - if drivers provide a transfer_one() function instead of a transfer_one_message() function the core will handle processing at the message level. /CS can be controlled by either setting cs_gpio or providing a set_cs function. If this is not possible for hardware reasons then both can be omitted and the driver should continue to implement manual /CS handling. This is a first step in refactoring and it is expected that there will be further enhancements, for example factoring out of the mapping of transfers for DMA and the initiation and completion of interrupt driven transfers. Signed-off-by: Mark Brown diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 8a30c6b..85c18d8 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -526,6 +526,95 @@ int spi_register_board_info(struct spi_board_info const *info, unsigned n) /*-------------------------------------------------------------------------*/ +static void spi_set_cs(struct spi_device *spi, bool enable) +{ + if (spi->mode & SPI_CS_HIGH) + enable = !enable; + + if (spi->cs_gpio >= 0) + gpio_set_value(spi->cs_gpio, !enable); + else if (spi->master->set_cs) + spi->master->set_cs(spi, !enable); +} + +/* + * spi_transfer_one_message - Default implementation of transfer_one_message() + * + * This is a standard implementation of transfer_one_message() for + * drivers which impelment a transfer_one() operation. It provides + * standard handling of delays and chip select management. + */ +static int spi_transfer_one_message(struct spi_master *master, + struct spi_message *msg) +{ + struct spi_transfer *xfer; + bool cur_cs = true; + bool keep_cs = false; + int ret = 0; + + spi_set_cs(msg->spi, true); + + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + trace_spi_transfer_start(msg, xfer); + + INIT_COMPLETION(master->xfer_completion); + + ret = master->transfer_one(master, msg->spi, xfer); + if (ret < 0) { + dev_err(&msg->spi->dev, + "SPI transfer failed: %d\n", ret); + goto out; + } + + if (ret > 0) + wait_for_completion(&master->xfer_completion); + + trace_spi_transfer_stop(msg, xfer); + + if (msg->status != -EINPROGRESS) + goto out; + + if (xfer->delay_usecs) + udelay(xfer->delay_usecs); + + if (xfer->cs_change) { + if (list_is_last(&xfer->transfer_list, + &msg->transfers)) { + keep_cs = true; + } else { + cur_cs = !cur_cs; + spi_set_cs(msg->spi, cur_cs); + } + } + + msg->actual_length += xfer->len; + } + +out: + if (ret != 0 || !keep_cs) + spi_set_cs(msg->spi, false); + + if (msg->status == -EINPROGRESS) + msg->status = ret; + + spi_finalize_current_message(master); + + return ret; +} + +/** + * spi_finalize_current_transfer - report completion of a transfer + * + * Called by SPI drivers using the core transfer_one_message() + * implementation to notify it that the current interrupt driven + * transfer has finised and the next one may be scheduled. + */ +void spi_finalize_current_transfer(struct spi_master *master) +{ + complete(&master->xfer_completion); +} +EXPORT_SYMBOL_GPL(spi_finalize_current_transfer); + /** * spi_pump_messages - kthread work function which processes spi message queue * @work: pointer to kthread work struct contained in the master struct @@ -836,6 +925,8 @@ static int spi_master_initialize_queue(struct spi_master *master) master->queued = true; master->transfer = spi_queued_transfer; + if (!master->transfer_one_message) + master->transfer_one_message = spi_transfer_one_message; /* Initialize and start queue */ ret = spi_init_queue(master); @@ -1242,6 +1333,7 @@ int spi_register_master(struct spi_master *master) spin_lock_init(&master->bus_lock_spinlock); mutex_init(&master->bus_lock_mutex); master->bus_lock_flag = 0; + init_completion(&master->xfer_completion); /* register the device, then userspace will see it. * registration fails if the bus ID is in use. diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 000b50b..da371ab 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -23,6 +23,7 @@ #include #include #include +#include /* * INTERFACES between SPI master-side drivers and SPI infrastructure. @@ -150,8 +151,7 @@ static inline void *spi_get_drvdata(struct spi_device *spi) } struct spi_message; - - +struct spi_transfer; /** * struct spi_driver - Host side "protocol" driver @@ -259,6 +259,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * @cur_msg: the currently in-flight message * @cur_msg_prepared: spi_prepare_message was called for the currently * in-flight message + * @xfer_completion: used by core tranfer_one_message() * @busy: message pump is busy * @running: message pump is running * @rt: whether this queue is set to run as a realtime task @@ -276,9 +277,15 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * @unprepare_transfer_hardware: there are currently no more messages on the * queue so the subsystem notifies the driver that it may relax the * hardware by issuing this call + * @set_cs: assert or deassert chip select, true to assert. May be called + * from interrupt context. * @prepare_message: set up the controller to transfer a single message, * for example doing DMA mapping. Called from threaded * context. + * @transfer_one: transfer a single spi_transfer. When the + * driver is finished with this transfer it must call + * spi_finalize_current_transfer() so the subsystem can issue + * the next transfer * @unprepare_message: undo any work done by prepare_message(). * @cs_gpios: Array of GPIOs to use as chip select lines; one per CS * number. Any individual value may be -ENOENT for CS lines that @@ -395,6 +402,7 @@ struct spi_master { bool rt; bool auto_runtime_pm; bool cur_msg_prepared; + struct completion xfer_completion; int (*prepare_transfer_hardware)(struct spi_master *master); int (*transfer_one_message)(struct spi_master *master, @@ -405,6 +413,14 @@ struct spi_master { int (*unprepare_message)(struct spi_master *master, struct spi_message *message); + /* + * These hooks are for drivers that use a generic implementation + * of transfer_one_message() provied by the core. + */ + void (*set_cs)(struct spi_device *spi, bool enable); + int (*transfer_one)(struct spi_master *master, struct spi_device *spi, + struct spi_transfer *transfer); + /* gpio chip select */ int *cs_gpios; }; @@ -439,6 +455,7 @@ extern int spi_master_resume(struct spi_master *master); /* Calls the driver make to interact with the message queue */ extern struct spi_message *spi_get_next_queued_message(struct spi_master *master); extern void spi_finalize_current_message(struct spi_master *master); +extern void spi_finalize_current_transfer(struct spi_master *master); /* the spi driver core manages memory for the spi_master classdev */ extern struct spi_master * diff --git a/include/trace/events/spi.h b/include/trace/events/spi.h index 5e77e21..7e02c98 100644 --- a/include/trace/events/spi.h +++ b/include/trace/events/spi.h @@ -108,6 +108,48 @@ TRACE_EVENT(spi_message_done, (unsigned)__entry->actual, (unsigned)__entry->frame) ); +DECLARE_EVENT_CLASS(spi_transfer, + + TP_PROTO(struct spi_message *msg, struct spi_transfer *xfer), + + TP_ARGS(msg, xfer), + + TP_STRUCT__entry( + __field( int, bus_num ) + __field( int, chip_select ) + __field( struct spi_transfer *, xfer ) + __field( int, len ) + ), + + TP_fast_assign( + __entry->bus_num = msg->spi->master->bus_num; + __entry->chip_select = msg->spi->chip_select; + __entry->xfer = xfer; + __entry->len = xfer->len; + ), + + TP_printk("spi%d.%d %p len=%d", (int)__entry->bus_num, + (int)__entry->chip_select, + (struct spi_message *)__entry->xfer, + (int)__entry->len) +); + +DEFINE_EVENT(spi_transfer, spi_transfer_start, + + TP_PROTO(struct spi_message *msg, struct spi_transfer *xfer), + + TP_ARGS(msg, xfer) + +); + +DEFINE_EVENT(spi_transfer, spi_transfer_stop, + + TP_PROTO(struct spi_message *msg, struct spi_transfer *xfer), + + TP_ARGS(msg, xfer) + +); + #endif /* _TRACE_POWER_H */ /* This part must be outside protection */ -- cgit v0.10.2 From 0732a9d2a1551e3be5fd8a3ea680deb3d9e56193 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 5 Oct 2013 11:51:14 +0100 Subject: spi/s3c64xx: Use core message handling Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index c7b36c0..25eb352 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -879,121 +879,81 @@ static int s3c64xx_spi_prepare_message(struct spi_master *master, return 0; } -static int s3c64xx_spi_transfer_one_message(struct spi_master *master, - struct spi_message *msg) +static int s3c64xx_spi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) { struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); - struct spi_device *spi = msg->spi; - struct spi_transfer *xfer; - int status = 0, cs_toggle = 0; + int status; u32 speed; u8 bpw; + unsigned long flags; + int use_dma; - list_for_each_entry(xfer, &msg->transfers, transfer_list) { - - unsigned long flags; - int use_dma; - - INIT_COMPLETION(sdd->xfer_completion); - - /* Only BPW and Speed may change across transfers */ - bpw = xfer->bits_per_word; - speed = xfer->speed_hz ? : spi->max_speed_hz; - - if (xfer->len % (bpw / 8)) { - dev_err(&spi->dev, - "Xfer length(%u) not a multiple of word size(%u)\n", - xfer->len, bpw / 8); - status = -EIO; - goto out; - } - - if (bpw != sdd->cur_bpw || speed != sdd->cur_speed) { - sdd->cur_bpw = bpw; - sdd->cur_speed = speed; - s3c64xx_spi_config(sdd); - } - - /* Slave Select */ - enable_cs(sdd, spi); - - /* Polling method for xfers not bigger than FIFO capacity */ - use_dma = 0; - if (!is_polling(sdd) && - (sdd->rx_dma.ch && sdd->tx_dma.ch && - (xfer->len > ((FIFO_LVL_MASK(sdd) >> 1) + 1)))) - use_dma = 1; - - spin_lock_irqsave(&sdd->lock, flags); + INIT_COMPLETION(sdd->xfer_completion); - /* Pending only which is to be done */ - sdd->state &= ~RXBUSY; - sdd->state &= ~TXBUSY; + /* Only BPW and Speed may change across transfers */ + bpw = xfer->bits_per_word; + speed = xfer->speed_hz ? : spi->max_speed_hz; - enable_datapath(sdd, spi, xfer, use_dma); - - /* Start the signals */ - writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); + if (xfer->len % (bpw / 8)) { + dev_err(&spi->dev, + "Xfer length(%u) not a multiple of word size(%u)\n", + xfer->len, bpw / 8); + return -EIO; + } - /* Start the signals */ - writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); + if (bpw != sdd->cur_bpw || speed != sdd->cur_speed) { + sdd->cur_bpw = bpw; + sdd->cur_speed = speed; + s3c64xx_spi_config(sdd); + } - spin_unlock_irqrestore(&sdd->lock, flags); + /* Polling method for xfers not bigger than FIFO capacity */ + use_dma = 0; + if (!is_polling(sdd) && + (sdd->rx_dma.ch && sdd->tx_dma.ch && + (xfer->len > ((FIFO_LVL_MASK(sdd) >> 1) + 1)))) + use_dma = 1; - status = wait_for_xfer(sdd, xfer, use_dma); + spin_lock_irqsave(&sdd->lock, flags); - if (status) { - dev_err(&spi->dev, "I/O Error: rx-%d tx-%d res:rx-%c tx-%c len-%d\n", - xfer->rx_buf ? 1 : 0, xfer->tx_buf ? 1 : 0, - (sdd->state & RXBUSY) ? 'f' : 'p', - (sdd->state & TXBUSY) ? 'f' : 'p', - xfer->len); + /* Pending only which is to be done */ + sdd->state &= ~RXBUSY; + sdd->state &= ~TXBUSY; - if (use_dma) { - if (xfer->tx_buf != NULL - && (sdd->state & TXBUSY)) - s3c64xx_spi_dma_stop(sdd, &sdd->tx_dma); - if (xfer->rx_buf != NULL - && (sdd->state & RXBUSY)) - s3c64xx_spi_dma_stop(sdd, &sdd->rx_dma); - } + enable_datapath(sdd, spi, xfer, use_dma); - goto out; - } + /* Start the signals */ + writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); - flush_fifo(sdd); + /* Start the signals */ + writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); - if (xfer->delay_usecs) - udelay(xfer->delay_usecs); + spin_unlock_irqrestore(&sdd->lock, flags); - if (xfer->cs_change) { - /* Hint that the next mssg is gonna be - for the same device */ - if (list_is_last(&xfer->transfer_list, - &msg->transfers)) - cs_toggle = 1; + status = wait_for_xfer(sdd, xfer, use_dma); + + if (status) { + dev_err(&spi->dev, "I/O Error: rx-%d tx-%d res:rx-%c tx-%c len-%d\n", + xfer->rx_buf ? 1 : 0, xfer->tx_buf ? 1 : 0, + (sdd->state & RXBUSY) ? 'f' : 'p', + (sdd->state & TXBUSY) ? 'f' : 'p', + xfer->len); + + if (use_dma) { + if (xfer->tx_buf != NULL + && (sdd->state & TXBUSY)) + s3c64xx_spi_dma_stop(sdd, &sdd->tx_dma); + if (xfer->rx_buf != NULL + && (sdd->state & RXBUSY)) + s3c64xx_spi_dma_stop(sdd, &sdd->rx_dma); } - - msg->actual_length += xfer->len; - } - -out: - if (!cs_toggle || status) { - /* Quiese the signals */ - writel(S3C64XX_SPI_SLAVE_SIG_INACT, - sdd->regs + S3C64XX_SPI_SLAVE_SEL); - disable_cs(sdd, spi); } else { - sdd->tgl_spi = spi; + flush_fifo(sdd); } - s3c64xx_spi_unmap_mssg(sdd, msg); - - msg->status = status; - - spi_finalize_current_message(master); - - return 0; + return status; } static int s3c64xx_spi_unprepare_message(struct spi_master *master, @@ -1379,7 +1339,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) master->cleanup = s3c64xx_spi_cleanup; master->prepare_transfer_hardware = s3c64xx_spi_prepare_transfer; master->prepare_message = s3c64xx_spi_prepare_message; - master->transfer_one_message = s3c64xx_spi_transfer_one_message; + master->transfer_one = s3c64xx_spi_transfer_one; master->unprepare_message = s3c64xx_spi_unprepare_message; master->unprepare_transfer_hardware = s3c64xx_spi_unprepare_transfer; master->num_chipselect = sci->num_cs; -- cgit v0.10.2 From 62e947cb0cd27c392aabe732c64f5023e272cf0e Mon Sep 17 00:00:00 2001 From: Ramkumar Ramachandra Date: Thu, 10 Oct 2013 15:50:33 +0530 Subject: sched: Remove bogus parameter in structured comment The balance parameter was removed by 23f0d20 ("sched: Factor out code to should_we_balance()", 2013-08-06). Signed-off-by: Ramkumar Ramachandra Cc: Joonsoo Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1381400433-2030-1-git-send-email-artagnon@gmail.com Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 803e343..8274679 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -5586,7 +5586,6 @@ static inline enum fbq_type fbq_classify_rq(struct rq *rq) /** * update_sd_lb_stats - Update sched_domain's statistics for load balancing. * @env: The load balancing environment. - * @balance: Should we balance. * @sds: variable to hold the statistics for this sched_domain. */ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sds) -- cgit v0.10.2 From a0393713530c49697e49ce0456c039228ab7facb Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 29 Mar 2013 13:54:50 -0700 Subject: hwmon: Remove unnecessary semicolons Semicolons after closing } of conditional blocks are not needed and can be removed. Signed-off-by: Guenter Roeck Reviewed-by: Jean Delvare diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c index 3a6d9ef..b3498ac 100644 --- a/drivers/hwmon/adm1026.c +++ b/drivers/hwmon/adm1026.c @@ -616,7 +616,7 @@ static struct adm1026_data *adm1026_update_device(struct device *dev) data->gpio = gpio; data->last_reading = jiffies; - }; /* last_reading */ + } /* last_reading */ if (!data->valid || time_after(jiffies, data->last_config + ADM1026_CONFIG_INTERVAL)) { @@ -700,7 +700,7 @@ static struct adm1026_data *adm1026_update_device(struct device *dev) } data->last_config = jiffies; - }; /* last_config */ + } /* last_config */ data->valid = 1; mutex_unlock(&data->update_lock); @@ -1791,7 +1791,7 @@ static int adm1026_detect(struct i2c_client *client, if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { /* We need to be able to do byte I/O */ return -ENODEV; - }; + } /* Now, we do the remaining detection. */ diff --git a/drivers/hwmon/asc7621.c b/drivers/hwmon/asc7621.c index 3ad9d84..8d9f2a0 100644 --- a/drivers/hwmon/asc7621.c +++ b/drivers/hwmon/asc7621.c @@ -138,7 +138,7 @@ static inline u8 read_byte(struct i2c_client *client, u8 reg) dev_err(&client->dev, "Unable to read from register 0x%02x.\n", reg); return 0; - }; + } return res & 0xff; } @@ -149,7 +149,7 @@ static inline int write_byte(struct i2c_client *client, u8 reg, u8 data) dev_err(&client->dev, "Unable to write value 0x%02x to register 0x%02x.\n", data, reg); - }; + } return res; } @@ -1030,7 +1030,7 @@ static struct asc7621_data *asc7621_update_device(struct device *dev) } } data->last_high_reading = jiffies; - }; /* last_reading */ + } /* last_reading */ /* Read all the low priority registers. */ @@ -1044,7 +1044,7 @@ static struct asc7621_data *asc7621_update_device(struct device *dev) } } data->last_low_reading = jiffies; - }; /* last_reading */ + } /* last_reading */ data->valid = 1; @@ -1084,11 +1084,11 @@ static void asc7621_init_client(struct i2c_client *client) dev_err(&client->dev, "Client (%d,0x%02x) config is locked.\n", i2c_adapter_id(client->adapter), client->addr); - }; + } if (!(value & 0x04)) { dev_err(&client->dev, "Client (%d,0x%02x) is not ready.\n", i2c_adapter_id(client->adapter), client->addr); - }; + } /* * Start monitoring -- cgit v0.10.2 From e8ab508c27be9868411b6578507e93e02bdb8cdb Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 11 Sep 2013 10:32:18 -0700 Subject: hwmon: (nct6775) Use return value from find_temp_source smatch complains that we don't use the return value from find_temp_source(). Valid point, only find_temp_source() doesn't return a valid error code. Have it return a valid error code and use it. Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index 6eb03ce..de57e0d 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -1545,7 +1545,7 @@ static int find_temp_source(struct nct6775_data *data, int index, int count) if (src == source) return nr; } - return -1; + return -ENODEV; } static ssize_t @@ -1644,7 +1644,7 @@ store_temp_beep(struct device *dev, struct device_attribute *attr, nr = find_temp_source(data, sattr->index, data->num_temp_beeps); if (nr < 0) - return -ENODEV; + return nr; bit = data->BEEP_BITS[nr + TEMP_ALARM_BASE]; regindex = bit >> 3; -- cgit v0.10.2 From 45a5b3a183b43e07b7d1eaf1ef7c00fc87511b1b Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 11 Sep 2013 10:35:47 -0700 Subject: hwmon: (nct6775) Check array index when accessing temp_offset smatch complains about a potential out-of-bounds access to the temp_offset array. That doesn't happen in practice, but it doesn't hurt to add an explicit check either. This prevents potential problems in the future (for example if the number of 'fixed' temperature sensors is increased to add support for another chip). Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index de57e0d..b82fad4 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -1457,7 +1457,8 @@ static struct nct6775_data *nct6775_update_device(struct device *dev) = nct6775_read_temp(data, data->reg_temp[j][i]); } - if (!(data->have_temp_fixed & (1 << i))) + if (i >= NUM_TEMP_FIXED || + !(data->have_temp_fixed & (1 << i))) continue; data->temp_offset[i] = nct6775_read_value(data, data->REG_TEMP_OFFSET[i]); -- cgit v0.10.2 From ae02e7418ffa2ba5d927869ef9eab4c87549d8e9 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 13 Sep 2013 10:25:53 -0700 Subject: hwmon: (mc13783-adc) Increase size of name string smatch complains about mc13783_adc_probe() error: snprintf() chops off the last chars of 'id->name': 20 +vs 10 Use PLATFORM_NAME_SIZE instead of '10' as size when declaring the name variable. Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/mc13783-adc.c b/drivers/hwmon/mc13783-adc.c index 982d862..ae00e60 100644 --- a/drivers/hwmon/mc13783-adc.c +++ b/drivers/hwmon/mc13783-adc.c @@ -37,7 +37,7 @@ struct mc13783_adc_priv { struct mc13xxx *mc13xxx; struct device *hwmon_dev; - char name[10]; + char name[PLATFORM_NAME_SIZE]; }; static ssize_t mc13783_adc_show_name(struct device *dev, struct device_attribute -- cgit v0.10.2 From af78fdf4a61827bed7f0fcbba8cc9ae393a3ad82 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 13 Sep 2013 10:31:38 -0700 Subject: hwmon: (pmbus) Don't unnecessarily crash the kernel pmbus code currently crashes the kernel if it detects an internal implementation error. While the detected condition suggests that there is a bug in the code, it is hardly fatal. Therefore, it should not trigger a crash. Replace BUG() with WARN(). Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 9319fcf..871cf64 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -686,7 +686,7 @@ static int pmbus_get_boolean(struct pmbus_data *data, struct pmbus_boolean *b, if (!s1 && !s2) { ret = !!regval; } else if (!s1 || !s2) { - BUG(); + WARN(1, "Bad boolean descriptor %p: s1=%p, s2=%p\n", b, s1, s2); return 0; } else { long v1, v2; -- cgit v0.10.2 From bb34c0da646530425d9ae0e6e6229e641469da4c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 13 Sep 2013 10:38:09 -0700 Subject: hwmon: (adt7462) Use error value returned from find_trange_value() find_trange_value() returns -ENODEV in case of errors, only to have it ignored and replaced with -EINVAL. Make it return -EINVAL and use it. Signed-off-by: Guenter Roeck Reviewed-by: Jean Delvare diff --git a/drivers/hwmon/adt7462.c b/drivers/hwmon/adt7462.c index addb5a4..562cc38 100644 --- a/drivers/hwmon/adt7462.c +++ b/drivers/hwmon/adt7462.c @@ -700,7 +700,7 @@ static int find_trange_value(int trange) if (trange_values[i] == trange) return i; - return -ENODEV; + return -EINVAL; } static struct adt7462_data *adt7462_update_device(struct device *dev) @@ -1294,9 +1294,8 @@ static ssize_t set_pwm_tmax(struct device *dev, /* trange = tmax - tmin */ tmin = (data->pwm_tmin[attr->index] - 64) * 1000; trange_value = find_trange_value(trange - tmin); - if (trange_value < 0) - return -EINVAL; + return trange_value; temp = trange_value << ADT7462_PWM_RANGE_SHIFT; temp |= data->pwm_trange[attr->index] & ADT7462_PWM_HYST_MASK; -- cgit v0.10.2 From c52ae3d2794ee2a248245a9e5a26f717e1c401f5 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 13 Sep 2013 10:42:39 -0700 Subject: hwmon: (gpio_fan) Use error value returned from get_fan_speed_index() get_fan_speed_index() returns -EINVAL in case of errors, only to have it ignored and replaced with -ENODEV. Make it return -ENODEV and use it. Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index b7d6a57..155c78f 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -169,7 +169,7 @@ static int get_fan_speed_index(struct gpio_fan_data *fan_data) dev_warn(&fan_data->pdev->dev, "missing speed array entry for GPIO value 0x%x\n", ctrl_val); - return -EINVAL; + return -ENODEV; } static int rpm_to_speed_index(struct gpio_fan_data *fan_data, int rpm) @@ -384,7 +384,7 @@ static int fan_ctrl_init(struct gpio_fan_data *fan_data, fan_data->pwm_enable = true; /* Enable manual fan speed control. */ fan_data->speed_index = get_fan_speed_index(fan_data); if (fan_data->speed_index < 0) - return -ENODEV; + return fan_data->speed_index; return 0; } -- cgit v0.10.2 From 83c97fe13e026a7795a07bfca76fe5d4b5cc5157 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 13 Sep 2013 10:51:35 -0700 Subject: hwmon: (acpi_power_meter) Don't crash the kernel unnecessarily acpi_power_meter crashes the kernel if it detects an unexpected event or an internal implementation error. While the detected conditions suggest that there is a bug in the code, the condition is not fatal. Replace BUG() with WARN(). Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index a9e3d01..51b045b 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -381,8 +381,10 @@ static ssize_t show_str(struct device *dev, val = resource->oem_info; break; default: - BUG(); + WARN(1, "Implementation error: unexpected attribute index %d\n", + attr->index); val = ""; + break; } return sprintf(buf, "%s\n", val); @@ -436,7 +438,9 @@ static ssize_t show_val(struct device *dev, val = resource->trip[attr->index - 7] * 1000; break; default: - BUG(); + WARN(1, "Implementation error: unexpected attribute index %d\n", + attr->index); + break; } return sprintf(buf, "%llu\n", val); @@ -855,7 +859,8 @@ static void acpi_power_meter_notify(struct acpi_device *device, u32 event) dev_info(&device->dev, "Capping in progress.\n"); break; default: - BUG(); + WARN(1, "Unexpected event %d\n", event); + break; } mutex_unlock(&resource->lock); -- cgit v0.10.2 From 19f053c8406542eafb534b33d698677f076a3421 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 13 Sep 2013 10:56:11 -0700 Subject: hwmon: (acpi_power_meter) Use return value from acpi_bus_register_driver acpi_bus_register_driver() returns a valid error code. Use it. Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index 51b045b..8d40da3 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -996,7 +996,7 @@ static int __init acpi_power_meter_init(void) result = acpi_bus_register_driver(&acpi_power_meter_driver); if (result < 0) - return -ENODEV; + return result; return 0; } -- cgit v0.10.2 From 674d0ed8588c11ec9f70c8427ac83a73e0d156d5 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 13 Sep 2013 10:59:27 -0700 Subject: hwmon: (atxp1) Set and use error code from vid_to_reg() vid_to_reg() returns -1 if it encounters an error. Return -EINVAL instead. Its only caller, atxp1_storevcore(), doesn't use the return code but returns -1 instead, which is wrong anyway as it means -EPERM. Use the return value from vid_to_reg() instead to report the error. Signed-off-by: Guenter Roeck Reviewed-by: Jean Delvare diff --git a/drivers/hwmon/atxp1.c b/drivers/hwmon/atxp1.c index aecb9ea..ddff02e 100644 --- a/drivers/hwmon/atxp1.c +++ b/drivers/hwmon/atxp1.c @@ -147,10 +147,9 @@ static ssize_t atxp1_storevcore(struct device *dev, /* Calculate VID */ vid = vid_to_reg(vcore, data->vrm); - if (vid < 0) { dev_err(dev, "VID calculation failed.\n"); - return -1; + return vid; } /* diff --git a/include/linux/hwmon-vid.h b/include/linux/hwmon-vid.h index f346e4d..da0a680 100644 --- a/include/linux/hwmon-vid.h +++ b/include/linux/hwmon-vid.h @@ -38,7 +38,7 @@ static inline int vid_to_reg(int val, u8 vrm) return ((val >= 1100) && (val <= 1850) ? ((18499 - val * 10) / 25 + 5) / 10 : -1); default: - return -1; + return -EINVAL; } } -- cgit v0.10.2 From 39b103b4d2e6f006dbd425cf71c46914bd31ce80 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 14 Sep 2013 00:43:12 -0700 Subject: hwmon: (f75375s) Don't crash the kernel unnecessarily The f75375s driver crashes the kernel if it detects an an internal implementation error. While the detected conditions suggest that there is a bug in the code, the condition is not fatal. Replace BUG() with WARN(). Cc: Riku Voipio Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c index a837b94..80c42be 100644 --- a/drivers/hwmon/f75375s.c +++ b/drivers/hwmon/f75375s.c @@ -275,7 +275,7 @@ static bool duty_mode_enabled(u8 pwm_enable) case 3: /* Manual, speed mode */ return false; default: - BUG(); + WARN(1, "Unexpected pwm_enable value %d\n", pwm_enable); return true; } } @@ -291,7 +291,7 @@ static bool auto_mode_enabled(u8 pwm_enable) case 4: /* Auto, duty mode */ return true; default: - BUG(); + WARN(1, "Unexpected pwm_enable value %d\n", pwm_enable); return false; } } -- cgit v0.10.2 From 522928a87fb98f0d6c99a5d66d2049768be6935b Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 14 Sep 2013 00:47:24 -0700 Subject: hwmon: (f71882fg) Remove extra return statement Leftover from commit 33cd66e3 (hwmon: (f71882fg) Convert to use devm_ functions). Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index 31b221e..03d8592 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -2420,7 +2420,6 @@ static int f71882fg_probe(struct platform_device *pdev) exit_unregister_sysfs: f71882fg_remove(pdev); /* Will unregister the sysfs files for us */ return err; /* f71882fg_remove() also frees our data */ - return err; } static int f71882fg_remove(struct platform_device *pdev) -- cgit v0.10.2 From bab2243ce1897865e31ea6d59b0478391f51812b Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 6 Jul 2013 13:57:23 -0700 Subject: hwmon: Introduce hwmon_device_register_with_groups hwmon_device_register_with_groups() lets callers register a hwmon device together with all sysfs attributes in a single call. When using hwmon_device_register_with_groups(), hwmon attributes are attached to the hwmon device directly and no longer with its parent device. Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 646314f..a982528 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -25,35 +26,122 @@ #define HWMON_ID_PREFIX "hwmon" #define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d" -static struct class *hwmon_class; +struct hwmon_device { + const char *name; + struct device dev; +}; +#define to_hwmon_device(d) container_of(d, struct hwmon_device, dev) + +static ssize_t +show_name(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", to_hwmon_device(dev)->name); +} +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + +static struct attribute *hwmon_dev_attrs[] = { + &dev_attr_name.attr, + NULL +}; + +static umode_t hwmon_dev_name_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + + if (to_hwmon_device(dev)->name == NULL) + return 0; + + return attr->mode; +} + +static struct attribute_group hwmon_dev_attr_group = { + .attrs = hwmon_dev_attrs, + .is_visible = hwmon_dev_name_is_visible, +}; + +static const struct attribute_group *hwmon_dev_attr_groups[] = { + &hwmon_dev_attr_group, + NULL +}; + +static void hwmon_dev_release(struct device *dev) +{ + kfree(to_hwmon_device(dev)); +} + +static struct class hwmon_class = { + .name = "hwmon", + .owner = THIS_MODULE, + .dev_groups = hwmon_dev_attr_groups, + .dev_release = hwmon_dev_release, +}; static DEFINE_IDA(hwmon_ida); /** - * hwmon_device_register - register w/ hwmon - * @dev: the device to register + * hwmon_device_register_with_groups - register w/ hwmon + * @dev: the parent device + * @name: hwmon name attribute + * @drvdata: driver data to attach to created device + * @groups: List of attribute groups to create * * hwmon_device_unregister() must be called when the device is no * longer needed. * * Returns the pointer to the new device. */ -struct device *hwmon_device_register(struct device *dev) +struct device * +hwmon_device_register_with_groups(struct device *dev, const char *name, + void *drvdata, + const struct attribute_group **groups) { - struct device *hwdev; - int id; + struct hwmon_device *hwdev; + int err, id; id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL); if (id < 0) return ERR_PTR(id); - hwdev = device_create(hwmon_class, dev, MKDEV(0, 0), NULL, - HWMON_ID_FORMAT, id); + hwdev = kzalloc(sizeof(*hwdev), GFP_KERNEL); + if (hwdev == NULL) { + err = -ENOMEM; + goto ida_remove; + } - if (IS_ERR(hwdev)) - ida_simple_remove(&hwmon_ida, id); + hwdev->name = name; + hwdev->dev.class = &hwmon_class; + hwdev->dev.parent = dev; + hwdev->dev.groups = groups; + hwdev->dev.of_node = dev ? dev->of_node : NULL; + dev_set_drvdata(&hwdev->dev, drvdata); + dev_set_name(&hwdev->dev, HWMON_ID_FORMAT, id); + err = device_register(&hwdev->dev); + if (err) + goto free; + + return &hwdev->dev; + +free: + kfree(hwdev); +ida_remove: + ida_simple_remove(&hwmon_ida, id); + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(hwmon_device_register_with_groups); - return hwdev; +/** + * hwmon_device_register - register w/ hwmon + * @dev: the device to register + * + * hwmon_device_unregister() must be called when the device is no + * longer needed. + * + * Returns the pointer to the new device. + */ +struct device *hwmon_device_register(struct device *dev) +{ + return hwmon_device_register_with_groups(dev, NULL, NULL, NULL); } EXPORT_SYMBOL_GPL(hwmon_device_register); @@ -105,19 +193,21 @@ static void __init hwmon_pci_quirks(void) static int __init hwmon_init(void) { + int err; + hwmon_pci_quirks(); - hwmon_class = class_create(THIS_MODULE, "hwmon"); - if (IS_ERR(hwmon_class)) { - pr_err("couldn't create sysfs class\n"); - return PTR_ERR(hwmon_class); + err = class_register(&hwmon_class); + if (err) { + pr_err("couldn't register hwmon sysfs class\n"); + return err; } return 0; } static void __exit hwmon_exit(void) { - class_destroy(hwmon_class); + class_unregister(&hwmon_class); } subsys_initcall(hwmon_init); diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h index b2514f7..6d02ff7 100644 --- a/include/linux/hwmon.h +++ b/include/linux/hwmon.h @@ -15,8 +15,13 @@ #define _HWMON_H_ struct device; +struct attribute_group; struct device *hwmon_device_register(struct device *dev); +struct device * +hwmon_device_register_with_groups(struct device *dev, const char *name, + void *drvdata, + const struct attribute_group **groups); void hwmon_device_unregister(struct device *dev); -- cgit v0.10.2 From 8269b75ae72c4501df7caf93d053526cef7e8a8a Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 5 Jul 2013 22:30:39 -0700 Subject: hwmon: (ds1621) Convert to use hwmon_device_register_with_groups Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c index a26ba7a..595f4ef 100644 --- a/drivers/hwmon/ds1621.c +++ b/drivers/hwmon/ds1621.c @@ -120,7 +120,7 @@ static const u8 DS1621_REG_TEMP[3] = { /* Each client has this additional data */ struct ds1621_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex update_lock; char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ @@ -151,10 +151,10 @@ static inline u16 DS1621_TEMP_TO_REG(long temp, u8 zbits) return temp; } -static void ds1621_init_client(struct i2c_client *client) +static void ds1621_init_client(struct ds1621_data *data, + struct i2c_client *client) { u8 conf, new_conf, sreg, resol; - struct ds1621_data *data = i2c_get_clientdata(client); new_conf = conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF); /* switch to continuous conversion mode */ @@ -197,8 +197,8 @@ static void ds1621_init_client(struct i2c_client *client) static struct ds1621_data *ds1621_update_client(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct ds1621_data *data = i2c_get_clientdata(client); + struct ds1621_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; u8 new_conf; mutex_lock(&data->update_lock); @@ -247,8 +247,7 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct i2c_client *client = to_i2c_client(dev); - struct ds1621_data *data = i2c_get_clientdata(client); + struct ds1621_data *data = dev_get_drvdata(dev); long val; int err; @@ -258,7 +257,7 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da, mutex_lock(&data->update_lock); data->temp[attr->index] = DS1621_TEMP_TO_REG(val, data->zbits); - i2c_smbus_write_word_swapped(client, DS1621_REG_TEMP[attr->index], + i2c_smbus_write_word_swapped(data->client, DS1621_REG_TEMP[attr->index], data->temp[attr->index]); mutex_unlock(&data->update_lock); return count; @@ -282,16 +281,15 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute *da, static ssize_t show_convrate(struct device *dev, struct device_attribute *da, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct ds1621_data *data = i2c_get_clientdata(client); + struct ds1621_data *data = dev_get_drvdata(dev); return scnprintf(buf, PAGE_SIZE, "%hu\n", data->update_interval); } static ssize_t set_convrate(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct ds1621_data *data = i2c_get_clientdata(client); + struct ds1621_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned long convrate; s32 err; int resol = 0; @@ -343,8 +341,7 @@ static umode_t ds1621_attribute_visible(struct kobject *kobj, struct attribute *attr, int index) { struct device *dev = container_of(kobj, struct device, kobj); - struct i2c_client *client = to_i2c_client(dev); - struct ds1621_data *data = i2c_get_clientdata(client); + struct ds1621_data *data = dev_get_drvdata(dev); if (attr == &dev_attr_update_interval.attr) if (data->kind == ds1621 || data->kind == ds1625) @@ -358,49 +355,46 @@ static const struct attribute_group ds1621_group = { .is_visible = ds1621_attribute_visible }; +static const struct attribute_group *ds1621_groups[] = { + &ds1621_group, + NULL +}; + static int ds1621_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ds1621_data *data; - int err; + struct device *hwmon_dev; data = devm_kzalloc(&client->dev, sizeof(struct ds1621_data), GFP_KERNEL); if (!data) return -ENOMEM; - i2c_set_clientdata(client, data); mutex_init(&data->update_lock); data->kind = id->driver_data; + data->client = client; /* Initialize the DS1621 chip */ - ds1621_init_client(client); + ds1621_init_client(data, client); - /* Register sysfs hooks */ - err = sysfs_create_group(&client->dev.kobj, &ds1621_group); - if (err) - return err; + hwmon_dev = hwmon_device_register_with_groups(&client->dev, + client->name, data, + ds1621_groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove_files; - } + i2c_set_clientdata(client, hwmon_dev); return 0; - - exit_remove_files: - sysfs_remove_group(&client->dev.kobj, &ds1621_group); - return err; } static int ds1621_remove(struct i2c_client *client) { - struct ds1621_data *data = i2c_get_clientdata(client); + struct device *hwmon_dev = i2c_get_clientdata(client); - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &ds1621_group); + hwmon_device_unregister(hwmon_dev); return 0; } -- cgit v0.10.2 From 7258a12536da7f0db881b9ec9348f0d00a67e062 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 6 Jul 2013 09:46:14 -0700 Subject: hwmon: (gpio-fan) Convert to use hwmon_device_register_with_groups Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index 155c78f..d5148c8 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -309,12 +309,6 @@ exit_unlock: return ret; } -static ssize_t show_name(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "gpio-fan\n"); -} - static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm); static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, show_pwm_enable, set_pwm_enable); @@ -324,26 +318,23 @@ static DEVICE_ATTR(fan1_max, S_IRUGO, show_rpm_max, NULL); static DEVICE_ATTR(fan1_input, S_IRUGO, show_rpm, NULL); static DEVICE_ATTR(fan1_target, S_IRUGO | S_IWUSR, show_rpm, set_rpm); -static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); - static umode_t gpio_fan_is_visible(struct kobject *kobj, struct attribute *attr, int index) { struct device *dev = container_of(kobj, struct device, kobj); struct gpio_fan_data *data = dev_get_drvdata(dev); - if (index == 1 && !data->alarm) + if (index == 0 && !data->alarm) return 0; - if (index > 1 && !data->ctrl) + if (index > 0 && !data->ctrl) return 0; return attr->mode; } static struct attribute *gpio_fan_attributes[] = { - &dev_attr_name.attr, - &dev_attr_fan1_alarm.attr, /* 1 */ - &dev_attr_pwm1.attr, /* 2 */ + &dev_attr_fan1_alarm.attr, /* 0 */ + &dev_attr_pwm1.attr, /* 1 */ &dev_attr_pwm1_enable.attr, &dev_attr_pwm1_mode.attr, &dev_attr_fan1_input.attr, @@ -358,6 +349,11 @@ static const struct attribute_group gpio_fan_group = { .is_visible = gpio_fan_is_visible, }; +static const struct attribute_group *gpio_fan_groups[] = { + &gpio_fan_group, + NULL +}; + static int fan_ctrl_init(struct gpio_fan_data *fan_data, struct gpio_fan_platform_data *pdata) { @@ -539,24 +535,16 @@ static int gpio_fan_probe(struct platform_device *pdev) return err; } - err = sysfs_create_group(&pdev->dev.kobj, &gpio_fan_group); - if (err) - return err; - /* Make this driver part of hwmon class. */ - fan_data->hwmon_dev = hwmon_device_register(&pdev->dev); - if (IS_ERR(fan_data->hwmon_dev)) { - err = PTR_ERR(fan_data->hwmon_dev); - goto err_remove; - } + fan_data->hwmon_dev = hwmon_device_register_with_groups(&pdev->dev, + "gpio-fan", fan_data, + gpio_fan_groups); + if (IS_ERR(fan_data->hwmon_dev)) + return PTR_ERR(fan_data->hwmon_dev); dev_info(&pdev->dev, "GPIO fan initialized\n"); return 0; - -err_remove: - sysfs_remove_group(&pdev->dev.kobj, &gpio_fan_group); - return err; } static int gpio_fan_remove(struct platform_device *pdev) @@ -564,7 +552,6 @@ static int gpio_fan_remove(struct platform_device *pdev) struct gpio_fan_data *fan_data = platform_get_drvdata(pdev); hwmon_device_unregister(fan_data->hwmon_dev); - sysfs_remove_group(&pdev->dev.kobj, &gpio_fan_group); return 0; } -- cgit v0.10.2 From 9a85d53cfcf1d65c08033aac067f79d2cfee5ad7 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 6 Jul 2013 09:46:38 -0700 Subject: hwmon: (ltc4245) Convert to use hwmon_device_register_with_groups Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c index cdc1ecc..d4172933 100644 --- a/drivers/hwmon/ltc4245.c +++ b/drivers/hwmon/ltc4245.c @@ -51,7 +51,9 @@ enum ltc4245_cmd { }; struct ltc4245_data { - struct device *hwmon_dev; + struct i2c_client *client; + + const struct attribute_group *groups[3]; struct mutex update_lock; bool valid; @@ -77,8 +79,8 @@ struct ltc4245_data { */ static void ltc4245_update_gpios(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct ltc4245_data *data = i2c_get_clientdata(client); + struct ltc4245_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; u8 gpio_curr, gpio_next, gpio_reg; int i; @@ -130,8 +132,8 @@ static void ltc4245_update_gpios(struct device *dev) static struct ltc4245_data *ltc4245_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct ltc4245_data *data = i2c_get_clientdata(client); + struct ltc4245_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; s32 val; int i; @@ -455,41 +457,14 @@ static const struct attribute_group ltc4245_gpio_group = { .attrs = ltc4245_gpio_attributes, }; -static int ltc4245_sysfs_create_groups(struct i2c_client *client) +static void ltc4245_sysfs_add_groups(struct ltc4245_data *data) { - struct ltc4245_data *data = i2c_get_clientdata(client); - struct device *dev = &client->dev; - int ret; - - /* register the standard sysfs attributes */ - ret = sysfs_create_group(&dev->kobj, <c4245_std_group); - if (ret) { - dev_err(dev, "unable to register standard attributes\n"); - return ret; - } + /* standard sysfs attributes */ + data->groups[0] = <c4245_std_group; /* if we're using the extra gpio support, register it's attributes */ - if (data->use_extra_gpios) { - ret = sysfs_create_group(&dev->kobj, <c4245_gpio_group); - if (ret) { - dev_err(dev, "unable to register gpio attributes\n"); - sysfs_remove_group(&dev->kobj, <c4245_std_group); - return ret; - } - } - - return 0; -} - -static void ltc4245_sysfs_remove_groups(struct i2c_client *client) -{ - struct ltc4245_data *data = i2c_get_clientdata(client); - struct device *dev = &client->dev; - if (data->use_extra_gpios) - sysfs_remove_group(&dev->kobj, <c4245_gpio_group); - - sysfs_remove_group(&dev->kobj, <c4245_std_group); + data->groups[1] = <c4245_gpio_group; } static bool ltc4245_use_extra_gpios(struct i2c_client *client) @@ -517,7 +492,7 @@ static int ltc4245_probe(struct i2c_client *client, { struct i2c_adapter *adapter = client->adapter; struct ltc4245_data *data; - int ret; + struct device *hwmon_dev; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; @@ -526,7 +501,7 @@ static int ltc4245_probe(struct i2c_client *client, if (!data) return -ENOMEM; - i2c_set_clientdata(client, data); + data->client = client; mutex_init(&data->update_lock); data->use_extra_gpios = ltc4245_use_extra_gpios(client); @@ -534,30 +509,25 @@ static int ltc4245_probe(struct i2c_client *client, i2c_smbus_write_byte_data(client, LTC4245_FAULT1, 0x00); i2c_smbus_write_byte_data(client, LTC4245_FAULT2, 0x00); - /* Register sysfs hooks */ - ret = ltc4245_sysfs_create_groups(client); - if (ret) - return ret; + /* Add sysfs hooks */ + ltc4245_sysfs_add_groups(data); - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - ret = PTR_ERR(data->hwmon_dev); - goto out_hwmon_device_register; - } + hwmon_dev = hwmon_device_register_with_groups(&client->dev, + client->name, data, + data->groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); - return 0; + i2c_set_clientdata(client, hwmon_dev); -out_hwmon_device_register: - ltc4245_sysfs_remove_groups(client); - return ret; + return 0; } static int ltc4245_remove(struct i2c_client *client) { - struct ltc4245_data *data = i2c_get_clientdata(client); + struct device *hwmon_dev = i2c_get_clientdata(client); - hwmon_device_unregister(data->hwmon_dev); - ltc4245_sysfs_remove_groups(client); + hwmon_device_unregister(hwmon_dev); return 0; } -- cgit v0.10.2 From c3b7cddc70075d525db6b3068d8b2b9158eedc84 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 6 Jul 2013 09:47:08 -0700 Subject: hwmon: (pmbus) Convert to use hwmon_device_register_with_groups Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 871cf64..7f9ce32 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -97,6 +97,7 @@ struct pmbus_data { int max_attributes; int num_attributes; struct attribute_group group; + const struct attribute_group *groups[2]; struct pmbus_sensor *sensors; @@ -348,7 +349,7 @@ static struct _pmbus_status { static struct pmbus_data *pmbus_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); + struct i2c_client *client = to_i2c_client(dev->parent); struct pmbus_data *data = i2c_get_clientdata(client); const struct pmbus_driver_info *info = data->info; struct pmbus_sensor *sensor; @@ -733,7 +734,7 @@ static ssize_t pmbus_set_sensor(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); + struct i2c_client *client = to_i2c_client(dev->parent); struct pmbus_data *data = i2c_get_clientdata(client); struct pmbus_sensor *sensor = to_pmbus_sensor(devattr); ssize_t rv = count; @@ -1768,22 +1769,16 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, goto out_kfree; } - /* Register sysfs hooks */ - ret = sysfs_create_group(&dev->kobj, &data->group); - if (ret) { - dev_err(dev, "Failed to create sysfs entries\n"); - goto out_kfree; - } - data->hwmon_dev = hwmon_device_register(dev); + data->groups[0] = &data->group; + data->hwmon_dev = hwmon_device_register_with_groups(dev, client->name, + data, data->groups); if (IS_ERR(data->hwmon_dev)) { ret = PTR_ERR(data->hwmon_dev); dev_err(dev, "Failed to register hwmon device\n"); - goto out_hwmon_device_register; + goto out_kfree; } return 0; -out_hwmon_device_register: - sysfs_remove_group(&dev->kobj, &data->group); out_kfree: kfree(data->group.attrs); return ret; @@ -1794,7 +1789,6 @@ int pmbus_do_remove(struct i2c_client *client) { struct pmbus_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &data->group); kfree(data->group.attrs); return 0; } -- cgit v0.10.2 From 615fc8cb0f900c91ee50f0da0a1821a7c6823671 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 6 Jul 2013 09:43:30 -0700 Subject: hwmon: (nct6775) Convert to use hwmon_device_register_with_groups Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index b82fad4..58e9e11 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -725,10 +725,9 @@ struct nct6775_data { const char *name; struct device *hwmon_dev; - struct attribute_group *group_in; - struct attribute_group *group_fan; - struct attribute_group *group_temp; - struct attribute_group *group_pwm; + + int num_attr_groups; + const struct attribute_group *groups[6]; u16 reg_temp[5][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst, * 3=temp_crit, 4=temp_lcrit @@ -942,7 +941,7 @@ nct6775_create_attr_group(struct device *dev, struct sensor_template_group *tg, struct sensor_device_attribute_2 *a2; struct attribute **attrs; struct sensor_device_template **t; - int err, i, j, count; + int i, j, count; if (repeat <= 0) return ERR_PTR(-EINVAL); @@ -1002,10 +1001,6 @@ nct6775_create_attr_group(struct device *dev, struct sensor_template_group *tg, } } - err = sysfs_create_group(&dev->kobj, group); - if (err) - return ERR_PTR(-ENOMEM); - return group; } @@ -2727,16 +2722,6 @@ store_fan_time(struct device *dev, struct device_attribute *attr, } static ssize_t -show_name(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct nct6775_data *data = dev_get_drvdata(dev); - - return sprintf(buf, "%s\n", data->name); -} - -static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); - -static ssize_t show_auto_pwm(struct device *dev, struct device_attribute *attr, char *buf) { struct nct6775_data *data = nct6775_update_device(dev); @@ -3062,16 +3047,16 @@ static umode_t nct6775_other_is_visible(struct kobject *kobj, struct device *dev = container_of(kobj, struct device, kobj); struct nct6775_data *data = dev_get_drvdata(dev); - if (index == 1 && !data->have_vid) + if (index == 0 && !data->have_vid) return 0; - if (index == 2 || index == 3) { - if (data->ALARM_BITS[INTRUSION_ALARM_BASE + index - 2] < 0) + if (index == 1 || index == 2) { + if (data->ALARM_BITS[INTRUSION_ALARM_BASE + index - 1] < 0) return 0; } - if (index == 4 || index == 5) { - if (data->BEEP_BITS[INTRUSION_ALARM_BASE + index - 4] < 0) + if (index == 3 || index == 4) { + if (data->BEEP_BITS[INTRUSION_ALARM_BASE + index - 3] < 0) return 0; } @@ -3084,13 +3069,12 @@ static umode_t nct6775_other_is_visible(struct kobject *kobj, * Any change in order or content must be matched. */ static struct attribute *nct6775_attributes_other[] = { - &dev_attr_name.attr, - &dev_attr_cpu0_vid.attr, /* 1 */ - &sensor_dev_attr_intrusion0_alarm.dev_attr.attr, /* 2 */ - &sensor_dev_attr_intrusion1_alarm.dev_attr.attr, /* 3 */ - &sensor_dev_attr_intrusion0_beep.dev_attr.attr, /* 4 */ - &sensor_dev_attr_intrusion1_beep.dev_attr.attr, /* 5 */ - &sensor_dev_attr_beep_enable.dev_attr.attr, /* 6 */ + &dev_attr_cpu0_vid.attr, /* 0 */ + &sensor_dev_attr_intrusion0_alarm.dev_attr.attr, /* 1 */ + &sensor_dev_attr_intrusion1_alarm.dev_attr.attr, /* 2 */ + &sensor_dev_attr_intrusion0_beep.dev_attr.attr, /* 3 */ + &sensor_dev_attr_intrusion1_beep.dev_attr.attr, /* 4 */ + &sensor_dev_attr_beep_enable.dev_attr.attr, /* 5 */ NULL }; @@ -3100,27 +3084,6 @@ static const struct attribute_group nct6775_group_other = { .is_visible = nct6775_other_is_visible, }; -/* - * Driver and device management - */ - -static void nct6775_device_remove_files(struct device *dev) -{ - struct nct6775_data *data = dev_get_drvdata(dev); - - if (data->group_pwm) - sysfs_remove_group(&dev->kobj, data->group_pwm); - if (data->group_in) - sysfs_remove_group(&dev->kobj, data->group_in); - if (data->group_fan) - sysfs_remove_group(&dev->kobj, data->group_fan); - if (data->group_temp) - sysfs_remove_group(&dev->kobj, data->group_temp); - - sysfs_remove_group(&dev->kobj, &nct6775_group_other); -} - -/* Get the monitoring functions started */ static inline void nct6775_init_device(struct nct6775_data *data) { int i; @@ -3871,51 +3834,39 @@ static int nct6775_probe(struct platform_device *pdev) /* Register sysfs hooks */ group = nct6775_create_attr_group(dev, &nct6775_pwm_template_group, data->pwm_num); - if (IS_ERR(group)) { - err = PTR_ERR(group); - goto exit_remove; - } - data->group_pwm = group; + if (IS_ERR(group)) + return PTR_ERR(group); + + data->groups[data->num_attr_groups++] = group; group = nct6775_create_attr_group(dev, &nct6775_in_template_group, fls(data->have_in)); - if (IS_ERR(group)) { - err = PTR_ERR(group); - goto exit_remove; - } - data->group_in = group; + if (IS_ERR(group)) + return PTR_ERR(group); + + data->groups[data->num_attr_groups++] = group; group = nct6775_create_attr_group(dev, &nct6775_fan_template_group, fls(data->has_fan)); - if (IS_ERR(group)) { - err = PTR_ERR(group); - goto exit_remove; - } - data->group_fan = group; + if (IS_ERR(group)) + return PTR_ERR(group); + + data->groups[data->num_attr_groups++] = group; group = nct6775_create_attr_group(dev, &nct6775_temp_template_group, fls(data->have_temp)); - if (IS_ERR(group)) { - err = PTR_ERR(group); - goto exit_remove; - } - data->group_temp = group; + if (IS_ERR(group)) + return PTR_ERR(group); - err = sysfs_create_group(&dev->kobj, &nct6775_group_other); - if (err) - goto exit_remove; + data->groups[data->num_attr_groups++] = group; + data->groups[data->num_attr_groups++] = &nct6775_group_other; - data->hwmon_dev = hwmon_device_register(dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove; - } + data->hwmon_dev = hwmon_device_register_with_groups(dev, data->name, + data, data->groups); + if (IS_ERR(data->hwmon_dev)) + return PTR_ERR(data->hwmon_dev); return 0; - -exit_remove: - nct6775_device_remove_files(dev); - return err; } static int nct6775_remove(struct platform_device *pdev) @@ -3923,7 +3874,6 @@ static int nct6775_remove(struct platform_device *pdev) struct nct6775_data *data = platform_get_drvdata(pdev); hwmon_device_unregister(data->hwmon_dev); - nct6775_device_remove_files(&pdev->dev); return 0; } @@ -4102,7 +4052,7 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) /* * when Super-I/O functions move to a separate file, the Super-I/O * bus will manage the lifetime of the device and this module will only keep - * track of the nct6775 driver. But since we platform_device_alloc(), we + * track of the nct6775 driver. But since we use platform_device_alloc(), we * must keep track of the device */ static struct platform_device *pdev[2]; -- cgit v0.10.2 From 74188cba088192e14cd7fd5433876e8c947bcdd8 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 11 Jul 2013 20:00:12 -0700 Subject: hwmon: Provide managed hwmon registration Drivers using the new hwmon_device_register_with_groups API often have a remove function which consists solely of a call hwmon_device_unregister(). Provide support for devm_hwmon_device_register_with_groups and devm_hwmon_device_unregister to allow this repeated code to be removed and help eliminate error handling code. Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index a982528..e176a43 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -163,6 +163,69 @@ void hwmon_device_unregister(struct device *dev) } EXPORT_SYMBOL_GPL(hwmon_device_unregister); +static void devm_hwmon_release(struct device *dev, void *res) +{ + struct device *hwdev = *(struct device **)res; + + hwmon_device_unregister(hwdev); +} + +/** + * devm_hwmon_device_register_with_groups - register w/ hwmon + * @dev: the parent device + * @name: hwmon name attribute + * @drvdata: driver data to attach to created device + * @groups: List of attribute groups to create + * + * Returns the pointer to the new device. The new device is automatically + * unregistered with the parent device. + */ +struct device * +devm_hwmon_device_register_with_groups(struct device *dev, const char *name, + void *drvdata, + const struct attribute_group **groups) +{ + struct device **ptr, *hwdev; + + if (!dev) + return ERR_PTR(-EINVAL); + + ptr = devres_alloc(devm_hwmon_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + hwdev = hwmon_device_register_with_groups(dev, name, drvdata, groups); + if (IS_ERR(hwdev)) + goto error; + + *ptr = hwdev; + devres_add(dev, ptr); + return hwdev; + +error: + devres_free(ptr); + return hwdev; +} +EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_groups); + +static int devm_hwmon_match(struct device *dev, void *res, void *data) +{ + struct device **hwdev = res; + + return *hwdev == data; +} + +/** + * devm_hwmon_device_unregister - removes a previously registered hwmon device + * + * @dev: the parent device of the device to unregister + */ +void devm_hwmon_device_unregister(struct device *dev) +{ + WARN_ON(devres_release(dev, devm_hwmon_release, devm_hwmon_match, dev)); +} +EXPORT_SYMBOL_GPL(devm_hwmon_device_unregister); + static void __init hwmon_pci_quirks(void) { #if defined CONFIG_X86 && defined CONFIG_PCI diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h index 6d02ff7..09354f6 100644 --- a/include/linux/hwmon.h +++ b/include/linux/hwmon.h @@ -22,7 +22,12 @@ struct device * hwmon_device_register_with_groups(struct device *dev, const char *name, void *drvdata, const struct attribute_group **groups); +struct device * +devm_hwmon_device_register_with_groups(struct device *dev, const char *name, + void *drvdata, + const struct attribute_group **groups); void hwmon_device_unregister(struct device *dev); +void devm_hwmon_device_unregister(struct device *dev); #endif -- cgit v0.10.2 From a150d95b7ce55fca87143f8ddce02f661e2fc6ec Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 11 Jul 2013 22:55:22 -0700 Subject: hwmon: (nct6775) Convert to use devm_hwmon_device_register_with_groups Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index 58e9e11..fdbd632 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -724,8 +724,6 @@ struct nct6775_data { enum kinds kind; const char *name; - struct device *hwmon_dev; - int num_attr_groups; const struct attribute_group *groups[6]; @@ -3260,6 +3258,7 @@ static int nct6775_probe(struct platform_device *pdev) int num_reg_temp; u8 cr2a; struct attribute_group *group; + struct device *hwmon_dev; res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!devm_request_region(&pdev->dev, res->start, IOREGION_LENGTH, @@ -3861,19 +3860,10 @@ static int nct6775_probe(struct platform_device *pdev) data->groups[data->num_attr_groups++] = group; data->groups[data->num_attr_groups++] = &nct6775_group_other; - data->hwmon_dev = hwmon_device_register_with_groups(dev, data->name, - data, data->groups); - if (IS_ERR(data->hwmon_dev)) - return PTR_ERR(data->hwmon_dev); - - return 0; -} - -static int nct6775_remove(struct platform_device *pdev) -{ - struct nct6775_data *data = platform_get_drvdata(pdev); - - hwmon_device_unregister(data->hwmon_dev); + hwmon_dev = devm_hwmon_device_register_with_groups(dev, data->name, + data, data->groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); return 0; } @@ -3964,7 +3954,6 @@ static struct platform_driver nct6775_driver = { .pm = NCT6775_DEV_PM_OPS, }, .probe = nct6775_probe, - .remove = nct6775_remove, }; static const char * const nct6775_sio_names[] __initconst = { -- cgit v0.10.2 From 0d36dce0f1d15a9ca55f893b3aa5ce7c297e11c8 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 11 Jul 2013 22:55:51 -0700 Subject: hwmon: (ds1621) Convert to use devm_hwmon_device_register_with_groups Also use new macro __ATTRIBUTE_GROUPS to declare attribute groups. Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c index 595f4ef..5e398c9 100644 --- a/drivers/hwmon/ds1621.c +++ b/drivers/hwmon/ds1621.c @@ -354,11 +354,7 @@ static const struct attribute_group ds1621_group = { .attrs = ds1621_attributes, .is_visible = ds1621_attribute_visible }; - -static const struct attribute_group *ds1621_groups[] = { - &ds1621_group, - NULL -}; +__ATTRIBUTE_GROUPS(ds1621); static int ds1621_probe(struct i2c_client *client, const struct i2c_device_id *id) @@ -379,23 +375,12 @@ static int ds1621_probe(struct i2c_client *client, /* Initialize the DS1621 chip */ ds1621_init_client(data, client); - hwmon_dev = hwmon_device_register_with_groups(&client->dev, - client->name, data, - ds1621_groups); + hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev, + client->name, data, + ds1621_groups); if (IS_ERR(hwmon_dev)) return PTR_ERR(hwmon_dev); - i2c_set_clientdata(client, hwmon_dev); - - return 0; -} - -static int ds1621_remove(struct i2c_client *client) -{ - struct device *hwmon_dev = i2c_get_clientdata(client); - - hwmon_device_unregister(hwmon_dev); - return 0; } @@ -416,7 +401,6 @@ static struct i2c_driver ds1621_driver = { .name = "ds1621", }, .probe = ds1621_probe, - .remove = ds1621_remove, .id_table = ds1621_id, }; -- cgit v0.10.2 From 9bd024e9fd2bb82fdeb2a2bc8a4b687548f1467e Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 1 Sep 2013 16:48:48 -0700 Subject: hwmon: (max6642) Convert to use devm_hwmon_device_register_with_groups Also rename new_client variable to client and introduce new variable dev pointing to client->dev in the probe function, and use new macro ATTRIBUTE_GROUPS to declare attribute groups. Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/max6642.c b/drivers/hwmon/max6642.c index 57d58cd..3d61f8d 100644 --- a/drivers/hwmon/max6642.c +++ b/drivers/hwmon/max6642.c @@ -87,7 +87,7 @@ static int temp_to_reg(int val) */ struct max6642_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex update_lock; bool valid; /* zero until following fields are valid */ unsigned long last_updated; /* in jiffies */ @@ -102,10 +102,10 @@ struct max6642_data { * Real code */ -static void max6642_init_client(struct i2c_client *client) +static void max6642_init_client(struct max6642_data *data, + struct i2c_client *client) { u8 config; - struct max6642_data *data = i2c_get_clientdata(client); /* * Start the conversions. @@ -168,14 +168,14 @@ static int max6642_detect(struct i2c_client *client, static struct max6642_data *max6642_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct max6642_data *data = i2c_get_clientdata(client); + struct max6642_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; u16 val, tmp; mutex_lock(&data->update_lock); if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { - dev_dbg(&client->dev, "Updating max6642 data.\n"); + dev_dbg(dev, "Updating max6642 data.\n"); val = i2c_smbus_read_byte_data(client, MAX6642_REG_R_LOCAL_TEMPL); tmp = (val >> 6) & 3; @@ -209,8 +209,8 @@ static struct max6642_data *max6642_update_device(struct device *dev) static ssize_t show_temp_max10(struct device *dev, struct device_attribute *dev_attr, char *buf) { - struct max6642_data *data = max6642_update_device(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct max6642_data *data = max6642_update_device(dev); return sprintf(buf, "%d\n", temp_from_reg10(data->temp_input[attr->index])); @@ -219,8 +219,8 @@ static ssize_t show_temp_max10(struct device *dev, static ssize_t show_temp_max(struct device *dev, struct device_attribute *attr, char *buf) { - struct max6642_data *data = max6642_update_device(dev); struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(attr); + struct max6642_data *data = max6642_update_device(dev); return sprintf(buf, "%d\n", temp_from_reg(data->temp_high[attr2->nr])); } @@ -228,11 +228,10 @@ static ssize_t show_temp_max(struct device *dev, struct device_attribute *attr, static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(attr); + struct max6642_data *data = dev_get_drvdata(dev); unsigned long val; int err; - struct i2c_client *client = to_i2c_client(dev); - struct max6642_data *data = i2c_get_clientdata(client); - struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(attr); err = kstrtoul(buf, 10, &val); if (err < 0) @@ -240,7 +239,7 @@ static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr, mutex_lock(&data->update_lock); data->temp_high[attr2->nr] = clamp_val(temp_to_reg(val), 0, 255); - i2c_smbus_write_byte_data(client, attr2->index, + i2c_smbus_write_byte_data(data->client, attr2->index, data->temp_high[attr2->nr]); mutex_unlock(&data->update_lock); return count; @@ -264,7 +263,7 @@ static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, 2); static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6); static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 4); -static struct attribute *max6642_attributes[] = { +static struct attribute *max6642_attrs[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp2_input.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, @@ -275,52 +274,30 @@ static struct attribute *max6642_attributes[] = { &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, NULL }; +ATTRIBUTE_GROUPS(max6642); -static const struct attribute_group max6642_group = { - .attrs = max6642_attributes, -}; - -static int max6642_probe(struct i2c_client *new_client, +static int max6642_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct device *dev = &client->dev; struct max6642_data *data; - int err; + struct device *hwmon_dev; - data = devm_kzalloc(&new_client->dev, sizeof(struct max6642_data), - GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(struct max6642_data), GFP_KERNEL); if (!data) return -ENOMEM; - i2c_set_clientdata(new_client, data); + data->client = client; mutex_init(&data->update_lock); /* Initialize the MAX6642 chip */ - max6642_init_client(new_client); - - /* Register sysfs hooks */ - err = sysfs_create_group(&new_client->dev.kobj, &max6642_group); - if (err) - return err; - - data->hwmon_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove_files; - } - - return 0; - -exit_remove_files: - sysfs_remove_group(&new_client->dev.kobj, &max6642_group); - return err; -} - -static int max6642_remove(struct i2c_client *client) -{ - struct max6642_data *data = i2c_get_clientdata(client); + max6642_init_client(data, client); - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &max6642_group); + hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev, + client->name, data, + max6642_groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); return 0; } @@ -341,7 +318,6 @@ static struct i2c_driver max6642_driver = { .name = "max6642", }, .probe = max6642_probe, - .remove = max6642_remove, .id_table = max6642_id, .detect = max6642_detect, .address_list = normal_i2c, -- cgit v0.10.2 From 3710263f136e629af1e4bae8df1c02b7fdec6260 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 1 Sep 2013 18:32:52 -0700 Subject: hwmon: (lm73) Convert to use devm_hwmon_device_register_with_groups Also introduce new variable dev pointing to client->dev in the probe function, and use new macro ATTRIBUTE_GROUPS to declare attribute groups. Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/lm73.c b/drivers/hwmon/lm73.c index 9bde964..9653bb8 100644 --- a/drivers/hwmon/lm73.c +++ b/drivers/hwmon/lm73.c @@ -55,7 +55,7 @@ static const unsigned short lm73_convrates[] = { }; struct lm73_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex lock; u8 ctrl; /* control register value */ }; @@ -66,7 +66,7 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct i2c_client *client = to_i2c_client(dev); + struct lm73_data *data = dev_get_drvdata(dev); long temp; short value; s32 err; @@ -77,7 +77,7 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da, /* Write value */ value = clamp_val(temp / 250, LM73_TEMP_MIN, LM73_TEMP_MAX) << 5; - err = i2c_smbus_write_word_swapped(client, attr->index, value); + err = i2c_smbus_write_word_swapped(data->client, attr->index, value); return (err < 0) ? err : count; } @@ -85,10 +85,10 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *da, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct i2c_client *client = to_i2c_client(dev); + struct lm73_data *data = dev_get_drvdata(dev); int temp; - s32 err = i2c_smbus_read_word_swapped(client, attr->index); + s32 err = i2c_smbus_read_word_swapped(data->client, attr->index); if (err < 0) return err; @@ -101,8 +101,7 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *da, static ssize_t set_convrate(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm73_data *data = i2c_get_clientdata(client); + struct lm73_data *data = dev_get_drvdata(dev); unsigned long convrate; s32 err; int res = 0; @@ -124,7 +123,8 @@ static ssize_t set_convrate(struct device *dev, struct device_attribute *da, mutex_lock(&data->lock); data->ctrl &= LM73_CTRL_TO_MASK; data->ctrl |= res << LM73_CTRL_RES_SHIFT; - err = i2c_smbus_write_byte_data(client, LM73_REG_CTRL, data->ctrl); + err = i2c_smbus_write_byte_data(data->client, LM73_REG_CTRL, + data->ctrl); mutex_unlock(&data->lock); if (err < 0) @@ -136,8 +136,7 @@ static ssize_t set_convrate(struct device *dev, struct device_attribute *da, static ssize_t show_convrate(struct device *dev, struct device_attribute *da, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct lm73_data *data = i2c_get_clientdata(client); + struct lm73_data *data = dev_get_drvdata(dev); int res; res = (data->ctrl & LM73_CTRL_RES_MASK) >> LM73_CTRL_RES_SHIFT; @@ -147,13 +146,12 @@ static ssize_t show_convrate(struct device *dev, struct device_attribute *da, static ssize_t show_maxmin_alarm(struct device *dev, struct device_attribute *da, char *buf) { - struct i2c_client *client = to_i2c_client(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct lm73_data *data = i2c_get_clientdata(client); + struct lm73_data *data = dev_get_drvdata(dev); s32 ctrl; mutex_lock(&data->lock); - ctrl = i2c_smbus_read_byte_data(client, LM73_REG_CTRL); + ctrl = i2c_smbus_read_byte_data(data->client, LM73_REG_CTRL); if (ctrl < 0) goto abort; data->ctrl = ctrl; @@ -183,7 +181,7 @@ static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_maxmin_alarm, NULL, LM73_CTRL_LO_SHIFT); -static struct attribute *lm73_attributes[] = { +static struct attribute *lm73_attrs[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, &sensor_dev_attr_temp1_min.dev_attr.attr, @@ -192,10 +190,7 @@ static struct attribute *lm73_attributes[] = { &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, NULL }; - -static const struct attribute_group lm73_group = { - .attrs = lm73_attributes, -}; +ATTRIBUTE_GROUPS(lm73); /*-----------------------------------------------------------------------*/ @@ -204,16 +199,16 @@ static const struct attribute_group lm73_group = { static int lm73_probe(struct i2c_client *client, const struct i2c_device_id *id) { - int status; + struct device *dev = &client->dev; + struct device *hwmon_dev; struct lm73_data *data; int ctrl; - data = devm_kzalloc(&client->dev, sizeof(struct lm73_data), - GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(struct lm73_data), GFP_KERNEL); if (!data) return -ENOMEM; - i2c_set_clientdata(client, data); + data->client = client; mutex_init(&data->lock); ctrl = i2c_smbus_read_byte_data(client, LM73_REG_CTRL); @@ -221,33 +216,13 @@ lm73_probe(struct i2c_client *client, const struct i2c_device_id *id) return ctrl; data->ctrl = ctrl; - /* Register sysfs hooks */ - status = sysfs_create_group(&client->dev.kobj, &lm73_group); - if (status) - return status; - - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - status = PTR_ERR(data->hwmon_dev); - goto exit_remove; - } + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, lm73_groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); - dev_info(&client->dev, "%s: sensor '%s'\n", - dev_name(data->hwmon_dev), client->name); - - return 0; - -exit_remove: - sysfs_remove_group(&client->dev.kobj, &lm73_group); - return status; -} - -static int lm73_remove(struct i2c_client *client) -{ - struct lm73_data *data = i2c_get_clientdata(client); + dev_info(dev, "sensor '%s'\n", client->name); - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &lm73_group); return 0; } @@ -300,7 +275,6 @@ static struct i2c_driver lm73_driver = { .name = "lm73", }, .probe = lm73_probe, - .remove = lm73_remove, .id_table = lm73_ids, .detect = lm73_detect, .address_list = normal_i2c, -- cgit v0.10.2 From 8d3735522fb192738d7161865d41688f10983613 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 1 Sep 2013 22:49:25 -0700 Subject: hwmon: (max6697) Convert to use devm_hwmon_device_register_with_groups Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/max6697.c b/drivers/hwmon/max6697.c index a41b5f3..0af910a 100644 --- a/drivers/hwmon/max6697.c +++ b/drivers/hwmon/max6697.c @@ -77,7 +77,7 @@ struct max6697_chip_data { }; struct max6697_data { - struct device *hwmon_dev; + struct i2c_client *client; enum chips type; const struct max6697_chip_data *chip; @@ -181,8 +181,8 @@ static const struct max6697_chip_data max6697_chip_data[] = { static struct max6697_data *max6697_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct max6697_data *data = i2c_get_clientdata(client); + struct max6697_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; struct max6697_data *ret = data; int val; int i; @@ -303,8 +303,7 @@ static ssize_t set_temp(struct device *dev, { int nr = to_sensor_dev_attr_2(devattr)->nr; int index = to_sensor_dev_attr_2(devattr)->index; - struct i2c_client *client = to_i2c_client(dev); - struct max6697_data *data = i2c_get_clientdata(client); + struct max6697_data *data = dev_get_drvdata(dev); long temp; int ret; @@ -316,7 +315,7 @@ static ssize_t set_temp(struct device *dev, temp = DIV_ROUND_CLOSEST(temp, 1000) + data->temp_offset; temp = clamp_val(temp, 0, data->type == max6581 ? 255 : 127); data->temp[nr][index] = temp; - ret = i2c_smbus_write_byte_data(client, + ret = i2c_smbus_write_byte_data(data->client, index == 2 ? MAX6697_REG_MAX[nr] : MAX6697_REG_CRIT[nr], temp); @@ -405,8 +404,7 @@ static umode_t max6697_is_visible(struct kobject *kobj, struct attribute *attr, int index) { struct device *dev = container_of(kobj, struct device, kobj); - struct i2c_client *client = to_i2c_client(dev); - struct max6697_data *data = i2c_get_clientdata(client); + struct max6697_data *data = dev_get_drvdata(dev); const struct max6697_chip_data *chip = data->chip; int channel = index / 6; /* channel number */ int nr = index % 6; /* attribute index within channel */ @@ -489,6 +487,7 @@ static struct attribute *max6697_attributes[] = { static const struct attribute_group max6697_group = { .attrs = max6697_attributes, .is_visible = max6697_is_visible, }; +__ATTRIBUTE_GROUPS(max6697); static void max6697_get_config_of(struct device_node *node, struct max6697_platform_data *pdata) @@ -525,9 +524,9 @@ static void max6697_get_config_of(struct device_node *node, } } -static int max6697_init_chip(struct i2c_client *client) +static int max6697_init_chip(struct max6697_data *data, + struct i2c_client *client) { - struct max6697_data *data = i2c_get_clientdata(client); struct max6697_platform_data *pdata = dev_get_platdata(&client->dev); struct max6697_platform_data p; const struct max6697_chip_data *chip = data->chip; @@ -625,6 +624,7 @@ static int max6697_probe(struct i2c_client *client, struct i2c_adapter *adapter = client->adapter; struct device *dev = &client->dev; struct max6697_data *data; + struct device *hwmon_dev; int err; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -636,37 +636,18 @@ static int max6697_probe(struct i2c_client *client, data->type = id->driver_data; data->chip = &max6697_chip_data[data->type]; - - i2c_set_clientdata(client, data); + data->client = client; mutex_init(&data->update_lock); - err = max6697_init_chip(client); + err = max6697_init_chip(data, client); if (err) return err; - err = sysfs_create_group(&client->dev.kobj, &max6697_group); - if (err) - return err; - - data->hwmon_dev = hwmon_device_register(dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto error; - } - - return 0; - -error: - sysfs_remove_group(&client->dev.kobj, &max6697_group); - return err; -} - -static int max6697_remove(struct i2c_client *client) -{ - struct max6697_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &max6697_group); + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, + max6697_groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); return 0; } @@ -692,7 +673,6 @@ static struct i2c_driver max6697_driver = { .name = "max6697", }, .probe = max6697_probe, - .remove = max6697_remove, .id_table = max6697_id, }; -- cgit v0.10.2 From 38c75dc36dd368d10c4d0030665ec58565e4259b Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 2 Sep 2013 11:31:28 -0700 Subject: hwmon: (max16065) Convert to use devm_hwmon_device_register_with_groups Modify code to use is_visible to determine if an attribute should be created or not, then use devm_hwmon_device_register_with_groups to create the hwmon device and all attributes in one operation. Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/max16065.c b/drivers/hwmon/max16065.c index 2fa2c02..d4efc79 100644 --- a/drivers/hwmon/max16065.c +++ b/drivers/hwmon/max16065.c @@ -83,7 +83,8 @@ static const bool max16065_have_current[] = { struct max16065_data { enum chips type; - struct device *hwmon_dev; + struct i2c_client *client; + const struct attribute_group *groups[4]; struct mutex update_lock; bool valid; unsigned long last_updated; /* in jiffies */ @@ -144,8 +145,8 @@ static int max16065_read_adc(struct i2c_client *client, int reg) static struct max16065_data *max16065_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct max16065_data *data = i2c_get_clientdata(client); + struct max16065_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; mutex_lock(&data->update_lock); if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { @@ -186,7 +187,7 @@ static ssize_t max16065_show_alarm(struct device *dev, val &= (1 << attr2->index); if (val) - i2c_smbus_write_byte_data(to_i2c_client(dev), + i2c_smbus_write_byte_data(data->client, MAX16065_FAULT(attr2->nr), val); return snprintf(buf, PAGE_SIZE, "%d\n", !!val); @@ -223,8 +224,7 @@ static ssize_t max16065_set_limit(struct device *dev, const char *buf, size_t count) { struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(da); - struct i2c_client *client = to_i2c_client(dev); - struct max16065_data *data = i2c_get_clientdata(client); + struct max16065_data *data = dev_get_drvdata(dev); unsigned long val; int err; int limit; @@ -238,7 +238,7 @@ static ssize_t max16065_set_limit(struct device *dev, mutex_lock(&data->update_lock); data->limit[attr2->nr][attr2->index] = LIMIT_TO_MV(limit, data->range[attr2->index]); - i2c_smbus_write_byte_data(client, + i2c_smbus_write_byte_data(data->client, MAX16065_LIMIT(attr2->nr, attr2->index), limit); mutex_unlock(&data->update_lock); @@ -250,8 +250,7 @@ static ssize_t max16065_show_limit(struct device *dev, struct device_attribute *da, char *buf) { struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(da); - struct i2c_client *client = to_i2c_client(dev); - struct max16065_data *data = i2c_get_clientdata(client); + struct max16065_data *data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%d\n", data->limit[attr2->nr][attr2->index]); @@ -516,8 +515,32 @@ static struct attribute *max16065_max_attributes[] = { NULL }; +static umode_t max16065_basic_is_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct max16065_data *data = dev_get_drvdata(dev); + int index = n / 4; + + if (index >= data->num_adc || !data->range[index]) + return 0; + return a->mode; +} + +static umode_t max16065_secondary_is_visible(struct kobject *kobj, + struct attribute *a, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct max16065_data *data = dev_get_drvdata(dev); + + if (index >= data->num_adc) + return 0; + return a->mode; +} + static const struct attribute_group max16065_basic_group = { .attrs = max16065_basic_attributes, + .is_visible = max16065_basic_is_visible, }; static const struct attribute_group max16065_current_group = { @@ -526,38 +549,35 @@ static const struct attribute_group max16065_current_group = { static const struct attribute_group max16065_min_group = { .attrs = max16065_min_attributes, + .is_visible = max16065_secondary_is_visible, }; static const struct attribute_group max16065_max_group = { .attrs = max16065_max_attributes, + .is_visible = max16065_secondary_is_visible, }; -static void max16065_cleanup(struct i2c_client *client) -{ - sysfs_remove_group(&client->dev.kobj, &max16065_max_group); - sysfs_remove_group(&client->dev.kobj, &max16065_min_group); - sysfs_remove_group(&client->dev.kobj, &max16065_current_group); - sysfs_remove_group(&client->dev.kobj, &max16065_basic_group); -} - static int max16065_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_adapter *adapter = client->adapter; struct max16065_data *data; - int i, j, val, ret; + struct device *dev = &client->dev; + struct device *hwmon_dev; + int i, j, val; bool have_secondary; /* true if chip has secondary limits */ bool secondary_is_max = false; /* secondary limits reflect max */ + int groups = 0; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_READ_WORD_DATA)) return -ENODEV; - data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (unlikely(!data)) return -ENOMEM; - i2c_set_clientdata(client, data); + data->client = client; mutex_init(&data->update_lock); data->num_adc = max16065_num_adc[id->driver_data]; @@ -596,38 +616,16 @@ static int max16065_probe(struct i2c_client *client, } } - /* Register sysfs hooks */ - for (i = 0; i < data->num_adc * 4; i++) { - /* Do not create sysfs entry if channel is disabled */ - if (!data->range[i / 4]) - continue; - - ret = sysfs_create_file(&client->dev.kobj, - max16065_basic_attributes[i]); - if (unlikely(ret)) - goto out; - } - - if (have_secondary) { - struct attribute **attr = secondary_is_max ? - max16065_max_attributes : max16065_min_attributes; - - for (i = 0; i < data->num_adc; i++) { - if (!data->range[i]) - continue; - - ret = sysfs_create_file(&client->dev.kobj, attr[i]); - if (unlikely(ret)) - goto out; - } - } + /* sysfs hooks */ + data->groups[groups++] = &max16065_basic_group; + if (have_secondary) + data->groups[groups++] = secondary_is_max ? + &max16065_max_group : &max16065_min_group; if (data->have_current) { val = i2c_smbus_read_byte_data(client, MAX16065_CURR_CONTROL); - if (unlikely(val < 0)) { - ret = val; - goto out; - } + if (unlikely(val < 0)) + return val; if (val & MAX16065_CURR_ENABLE) { /* * Current gain is 6, 12, 24, 48 based on values in @@ -636,33 +634,16 @@ static int max16065_probe(struct i2c_client *client, data->curr_gain = 6 << ((val >> 2) & 0x03); data->range[MAX16065_NUM_ADC] = max16065_csp_adc_range[(val >> 1) & 0x01]; - ret = sysfs_create_group(&client->dev.kobj, - &max16065_current_group); - if (unlikely(ret)) - goto out; + data->groups[groups++] = &max16065_current_group; } else { data->have_current = false; } } - data->hwmon_dev = hwmon_device_register(&client->dev); - if (unlikely(IS_ERR(data->hwmon_dev))) { - ret = PTR_ERR(data->hwmon_dev); - goto out; - } - return 0; - -out: - max16065_cleanup(client); - return ret; -} - -static int max16065_remove(struct i2c_client *client) -{ - struct max16065_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - max16065_cleanup(client); + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, data->groups); + if (unlikely(IS_ERR(hwmon_dev))) + return PTR_ERR(hwmon_dev); return 0; } @@ -685,7 +666,6 @@ static struct i2c_driver max16065_driver = { .name = "max16065", }, .probe = max16065_probe, - .remove = max16065_remove, .id_table = max16065_id, }; -- cgit v0.10.2 From 468bf0e395363c12a41f508cc202b8d33f201807 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 2 Sep 2013 13:16:19 -0700 Subject: hwmon: (ina2xx) Convert to use devm_hwmon_device_register_with_groups Also introduce dev variable in probe function to simplify access to client->dev, and use new macro ATTRIBUTE_GROUPS to declare attribute groups. Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index 70a39a8..93d26e8 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -78,7 +78,7 @@ struct ina2xx_config { }; struct ina2xx_data { - struct device *hwmon_dev; + struct i2c_client *client; const struct ina2xx_config *config; struct mutex update_lock; @@ -112,8 +112,8 @@ static const struct ina2xx_config ina2xx_config[] = { static struct ina2xx_data *ina2xx_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct ina2xx_data *data = i2c_get_clientdata(client); + struct ina2xx_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; struct ina2xx_data *ret = data; mutex_lock(&data->update_lock); @@ -203,41 +203,39 @@ static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ina2xx_show_value, NULL, INA2XX_POWER); /* pointers to created device attributes */ -static struct attribute *ina2xx_attributes[] = { +static struct attribute *ina2xx_attrs[] = { &sensor_dev_attr_in0_input.dev_attr.attr, &sensor_dev_attr_in1_input.dev_attr.attr, &sensor_dev_attr_curr1_input.dev_attr.attr, &sensor_dev_attr_power1_input.dev_attr.attr, NULL, }; - -static const struct attribute_group ina2xx_group = { - .attrs = ina2xx_attributes, -}; +ATTRIBUTE_GROUPS(ina2xx); static int ina2xx_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_adapter *adapter = client->adapter; - struct ina2xx_data *data; struct ina2xx_platform_data *pdata; - int ret; - u32 val; + struct device *dev = &client->dev; + struct ina2xx_data *data; + struct device *hwmon_dev; long shunt = 10000; /* default shunt value 10mOhms */ + u32 val; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; - data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - if (dev_get_platdata(&client->dev)) { - pdata = dev_get_platdata(&client->dev); + if (dev_get_platdata(dev)) { + pdata = dev_get_platdata(dev); shunt = pdata->shunt_uohms; - } else if (!of_property_read_u32(client->dev.of_node, - "shunt-resistor", &val)) { - shunt = val; + } else if (!of_property_read_u32(dev->of_node, + "shunt-resistor", &val)) { + shunt = val; } if (shunt <= 0) @@ -255,37 +253,18 @@ static int ina2xx_probe(struct i2c_client *client, i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION, data->config->calibration_factor / shunt); - i2c_set_clientdata(client, data); + data->client = client; mutex_init(&data->update_lock); - ret = sysfs_create_group(&client->dev.kobj, &ina2xx_group); - if (ret) - return ret; - - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - ret = PTR_ERR(data->hwmon_dev); - goto out_err_hwmon; - } + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, ina2xx_groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); - dev_info(&client->dev, "power monitor %s (Rshunt = %li uOhm)\n", + dev_info(dev, "power monitor %s (Rshunt = %li uOhm)\n", id->name, shunt); return 0; - -out_err_hwmon: - sysfs_remove_group(&client->dev.kobj, &ina2xx_group); - return ret; -} - -static int ina2xx_remove(struct i2c_client *client) -{ - struct ina2xx_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &ina2xx_group); - - return 0; } static const struct i2c_device_id ina2xx_id[] = { @@ -302,7 +281,6 @@ static struct i2c_driver ina2xx_driver = { .name = "ina2xx", }, .probe = ina2xx_probe, - .remove = ina2xx_remove, .id_table = ina2xx_id, }; -- cgit v0.10.2 From ed1b7732868035990f07aeb532b1d86272ea909e Mon Sep 17 00:00:00 2001 From: Kamalesh Babulal Date: Sun, 13 Oct 2013 23:06:15 +0530 Subject: sched/fair: Fix trivial typos in comments - 'load_icx' => 'load_idx' - 'calculcate_imbalance' => 'calculate_imbalance' Signed-off-by: Kamalesh Babulal Cc: peterz@infradead.org Link: http://lkml.kernel.org/r/1381685775-3544-1-git-send-email-kamalesh@linux.vnet.ibm.com [ Also, don't capitalize 'idle' unnecessarily. ] Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 8274679..4aa0b10 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -5206,7 +5206,7 @@ static inline void init_sd_lb_stats(struct sd_lb_stats *sds) /** * get_sd_load_idx - Obtain the load index for a given sched domain. * @sd: The sched_domain whose load_idx is to be obtained. - * @idle: The Idle status of the CPU for whose sd load_icx is obtained. + * @idle: The idle status of the CPU for whose sd load_idx is obtained. * * Return: The load index. */ @@ -5412,7 +5412,7 @@ fix_small_capacity(struct sched_domain *sd, struct sched_group *group) * moving tasks due to affinity constraints. * * When this is so detected; this group becomes a candidate for busiest; see - * update_sd_pick_busiest(). And calculcate_imbalance() and + * update_sd_pick_busiest(). And calculate_imbalance() and * find_busiest_group() avoid some of the usual balance conditions to allow it * to create an effective group imbalance. * -- cgit v0.10.2 From 1d198f26c98e6501659d741d530f7b65e4b7aec3 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 8 Oct 2013 15:55:45 -0700 Subject: sound: Remove unnecessary semicolons These aren't necessary after switch and if blocks. Signed-off-by: Joe Perches Signed-off-by: Takashi Iwai diff --git a/sound/oss/sb_ess.c b/sound/oss/sb_ess.c index c0be085..0e7254b 100644 --- a/sound/oss/sb_ess.c +++ b/sound/oss/sb_ess.c @@ -1544,7 +1544,7 @@ static int ess_has_rec_mixer (int submodel) return 1; default: return 0; - }; + } }; #ifdef FKS_LOGGING diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c index b46dc9b..9fb03b4 100644 --- a/sound/pci/au88x0/au88x0_pcm.c +++ b/sound/pci/au88x0/au88x0_pcm.c @@ -671,7 +671,7 @@ static int snd_vortex_new_pcm(vortex_t *chip, int idx, int nr) return err; break; #endif - }; + } if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_SPDIF) { for (i = 0; i < ARRAY_SIZE(snd_vortex_mixer_spdif); i++) { diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index bb53dea..8697ced 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -777,7 +777,7 @@ static int asoc_ssc_init(struct device *dev) if (ret) { dev_err(dev, "Could not register PCM: %d\n", ret); goto err_unregister_dai; - }; + } return 0; diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c index 5f9af1f..49cc5f6 100644 --- a/sound/soc/codecs/ak4641.c +++ b/sound/soc/codecs/ak4641.c @@ -328,7 +328,7 @@ static int ak4641_i2s_hw_params(struct snd_pcm_substream *substream, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ak4641->playback_fs = rate; ak4641_set_deemph(codec); - }; + } return 0; } diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c index ea141e1..1544330 100644 --- a/sound/soc/codecs/mc13783.c +++ b/sound/soc/codecs/mc13783.c @@ -382,7 +382,7 @@ static int mc13783_set_tdm_slot_dac(struct snd_soc_dai *dai, break; default: return -EINVAL; - }; + } snd_soc_update_bits(codec, MC13783_SSI_NETWORK, mask, val); diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c index 6d31d88..e29cdb7 100644 --- a/sound/soc/codecs/tas5086.c +++ b/sound/soc/codecs/tas5086.c @@ -429,7 +429,7 @@ static int tas5086_hw_params(struct snd_pcm_substream *substream, default: dev_err(codec->dev, "Invalid bit width\n"); return -EINVAL; - }; + } ret = regmap_write(priv->regmap, TAS5086_SERIAL_DATA_IF, val); if (ret < 0) diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 3c79dbb..35059a2 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -246,7 +246,7 @@ static bool twl6040_is_path_unmuted(struct snd_soc_codec *codec, return priv->dl2_unmuted; default: return 1; - }; + } } /* @@ -1100,7 +1100,7 @@ static void twl6040_mute_path(struct snd_soc_codec *codec, enum twl6040_dai_id i break; default: break; - }; + } } static int twl6040_digital_mute(struct snd_soc_dai *dai, int mute) diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 3920c3e..c0fea02 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -963,7 +963,7 @@ static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg) return true; default: return false; - }; + } } static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg) @@ -982,7 +982,7 @@ static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg) return true; default: return false; - }; + } } static const struct regmap_config fsl_spdif_regmap_config = { diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c index 52af7f6..364bf6a 100644 --- a/sound/soc/tegra/tegra20_i2s.c +++ b/sound/soc/tegra/tegra20_i2s.c @@ -297,7 +297,7 @@ static bool tegra20_i2s_wr_rd_reg(struct device *dev, unsigned int reg) return true; default: return false; - }; + } } static bool tegra20_i2s_volatile_reg(struct device *dev, unsigned int reg) @@ -310,7 +310,7 @@ static bool tegra20_i2s_volatile_reg(struct device *dev, unsigned int reg) return true; default: return false; - }; + } } static bool tegra20_i2s_precious_reg(struct device *dev, unsigned int reg) @@ -321,7 +321,7 @@ static bool tegra20_i2s_precious_reg(struct device *dev, unsigned int reg) return true; default: return false; - }; + } } static const struct regmap_config tegra20_i2s_regmap_config = { diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c index 551b3c9..08bc693 100644 --- a/sound/soc/tegra/tegra20_spdif.c +++ b/sound/soc/tegra/tegra20_spdif.c @@ -213,7 +213,7 @@ static bool tegra20_spdif_wr_rd_reg(struct device *dev, unsigned int reg) return true; default: return false; - }; + } } static bool tegra20_spdif_volatile_reg(struct device *dev, unsigned int reg) @@ -234,7 +234,7 @@ static bool tegra20_spdif_volatile_reg(struct device *dev, unsigned int reg) return true; default: return false; - }; + } } static bool tegra20_spdif_precious_reg(struct device *dev, unsigned int reg) @@ -247,7 +247,7 @@ static bool tegra20_spdif_precious_reg(struct device *dev, unsigned int reg) return true; default: return false; - }; + } } static const struct regmap_config tegra20_spdif_regmap_config = { diff --git a/sound/soc/tegra/tegra30_ahub.c b/sound/soc/tegra/tegra30_ahub.c index d554d46..805b1f3 100644 --- a/sound/soc/tegra/tegra30_ahub.c +++ b/sound/soc/tegra/tegra30_ahub.c @@ -346,7 +346,7 @@ static bool tegra30_ahub_apbif_wr_rd_reg(struct device *dev, unsigned int reg) return true; default: break; - }; + } if (REG_IN_ARRAY(reg, CHANNEL_CTRL) || REG_IN_ARRAY(reg, CHANNEL_CLEAR) || @@ -381,7 +381,7 @@ static bool tegra30_ahub_apbif_volatile_reg(struct device *dev, return true; default: break; - }; + } if (REG_IN_ARRAY(reg, CHANNEL_CLEAR) || REG_IN_ARRAY(reg, CHANNEL_STATUS) || diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c index 47565fd04..bd71145 100644 --- a/sound/soc/tegra/tegra30_i2s.c +++ b/sound/soc/tegra/tegra30_i2s.c @@ -369,7 +369,7 @@ static bool tegra30_i2s_wr_rd_reg(struct device *dev, unsigned int reg) return true; default: return false; - }; + } } static bool tegra30_i2s_volatile_reg(struct device *dev, unsigned int reg) @@ -382,7 +382,7 @@ static bool tegra30_i2s_volatile_reg(struct device *dev, unsigned int reg) return true; default: return false; - }; + } } static const struct regmap_config tegra30_i2s_regmap_config = { -- cgit v0.10.2 From 1b3ed70a1b22e54b6adaf6ffebe1aa6f26465bae Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Sat, 12 Oct 2013 19:35:06 -0300 Subject: ASoC: fsl: Fix memory leak in imx-audmux.c When audmux_clk is used and clk_prepare_enable function succeed, the memory alloc'd to buf variable is leaked Signed-off-by: Felipe Pena Reviewed-by: Sascha Hauer Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/imx-audmux.c b/sound/soc/fsl/imx-audmux.c index d3bf71a..ac86993 100644 --- a/sound/soc/fsl/imx-audmux.c +++ b/sound/soc/fsl/imx-audmux.c @@ -66,13 +66,10 @@ static ssize_t audmux_read_file(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { ssize_t ret; - char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + char *buf; int port = (int)file->private_data; u32 pdcr, ptcr; - if (!buf) - return -ENOMEM; - if (audmux_clk) { ret = clk_prepare_enable(audmux_clk); if (ret) @@ -85,6 +82,10 @@ static ssize_t audmux_read_file(struct file *file, char __user *user_buf, if (audmux_clk) clk_disable_unprepare(audmux_clk); + buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + ret = snprintf(buf, PAGE_SIZE, "PDCR: %08x\nPTCR: %08x\n", pdcr, ptcr); -- cgit v0.10.2 From 48afa793525800eff66a2e792037108b7f0d8613 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 13 Oct 2013 18:17:50 +0200 Subject: ASoC: atmel: don't use devm_pinctrl_get_select_default() in probe Since commit ab78029 (drivers/pinctrl: grab default handles from device core), we can rely on device core for setting the default pins. Signed-off-by: Wolfram Sang Acked-by: Linus Walleij (personally at LCE13) Acked-by: Bo Shen Signed-off-by: Mark Brown diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c index 7222380..b4e3690 100644 --- a/sound/soc/atmel/atmel_wm8904.c +++ b/sound/soc/atmel/atmel_wm8904.c @@ -12,7 +12,6 @@ #include #include #include -#include #include @@ -155,15 +154,8 @@ static int atmel_asoc_wm8904_probe(struct platform_device *pdev) struct snd_soc_card *card = &atmel_asoc_wm8904_card; struct snd_soc_dai_link *dailink = &atmel_asoc_wm8904_dailink; struct clk *clk_src; - struct pinctrl *pinctrl; int id, ret; - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) { - dev_err(&pdev->dev, "failed to request pinctrl\n"); - return PTR_ERR(pinctrl); - } - card->dev = &pdev->dev; ret = atmel_asoc_wm8904_dt_init(pdev); if (ret) { -- cgit v0.10.2 From 67093b253bb09276420c7b0a311425705d2714b4 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 13 Oct 2013 21:51:26 +0200 Subject: ASoC: fsl: remove leftover release_mem_region When converting this driver to devm_ioremap_resource, the removal of this now unneeded function has been forgotten. Signed-off-by: Wolfram Sang Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c index f58bcd8..0272289 100644 --- a/sound/soc/fsl/imx-ssi.c +++ b/sound/soc/fsl/imx-ssi.c @@ -615,7 +615,6 @@ failed_pcm_dma: failed_pcm_fiq: snd_soc_unregister_component(&pdev->dev); failed_register: - release_mem_region(res->start, resource_size(res)); clk_disable_unprepare(ssi->clk); failed_clk: snd_soc_set_ac97_ops(NULL); @@ -625,7 +624,6 @@ failed_clk: static int imx_ssi_remove(struct platform_device *pdev) { - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct imx_ssi *ssi = platform_get_drvdata(pdev); imx_pcm_dma_exit(pdev); @@ -636,7 +634,6 @@ static int imx_ssi_remove(struct platform_device *pdev) if (ssi->flags & IMX_SSI_USE_AC97) ac97_ssi = NULL; - release_mem_region(res->start, resource_size(res)); clk_disable_unprepare(ssi->clk); snd_soc_set_ac97_ops(NULL); -- cgit v0.10.2 From 87f918685a452be514d060a09eeb4e0c91422e86 Mon Sep 17 00:00:00 2001 From: Ramkumar Ramachandra Date: Fri, 4 Oct 2013 10:47:31 +0530 Subject: perf trace: Improve the error messages Currently, execution of 'perf trace' reports the following cryptic message to the user: $ perf trace Couldn't read the raw_syscalls tracepoints information! Typically this happens because the user does not have permissions to read the debugfs filesystem. Also handle the case when the kernel was not compiled with debugfs support or when it isn't mounted. Now, the tool prints detailed error messages: $ perf trace Error: Unable to find debugfs Hint: Was your kernel was compiled with debugfs support? Hint: Is the debugfs filesystem mounted? Hint: Try 'sudo mount -t debugfs nodev /sys/kernel/debug' $ perf trace Error: No permissions to read /sys/kernel/debug//tracing/events/raw_syscalls Hint: Try 'sudo mount -o remount,mode=755 /sys/kernel/debug/' Signed-off-by: Ramkumar Ramachandra Cc: David Ahern Cc: Ingo Molnar Link: http://lkml.kernel.org/r/1380863851-14460-1-git-send-email-artagnon@gmail.com [ Added ready to use commands to fix the issues as extra hints, use the current debugfs mount point when reporting permission error, use strerror_r instead of the deprecated sys_errlist, as reported by David Ahern ] Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 19ddcab..5496546 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1590,17 +1590,13 @@ static int trace__run(struct trace *trace, int argc, const char **argv) } if (perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_enter", trace__sys_enter) || - perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_exit", trace__sys_exit)) { - fprintf(trace->output, "Couldn't read the raw_syscalls tracepoints information!\n"); - goto out_delete_evlist; - } + perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_exit", trace__sys_exit)) + goto out_error_tp; if (trace->sched && - perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime", - trace__sched_stat_runtime)) { - fprintf(trace->output, "Couldn't read the sched_stat_runtime tracepoint information!\n"); - goto out_delete_evlist; - } + perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime", + trace__sched_stat_runtime)) + goto out_error_tp; err = perf_evlist__create_maps(evlist, &trace->opts.target); if (err < 0) { @@ -1717,6 +1713,29 @@ out_delete_evlist: out: trace->live = false; return err; +out_error_tp: + switch(errno) { + case ENOENT: + fputs("Error:\tUnable to find debugfs\n" + "Hint:\tWas your kernel was compiled with debugfs support?\n" + "Hint:\tIs the debugfs filesystem mounted?\n" + "Hint:\tTry 'sudo mount -t debugfs nodev /sys/kernel/debug'\n", + trace->output); + break; + case EACCES: + fprintf(trace->output, + "Error:\tNo permissions to read %s/tracing/events/raw_syscalls\n" + "Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n", + debugfs_mountpoint, debugfs_mountpoint); + break; + default: { + char bf[256]; + fprintf(trace->output, "Can't trace: %s\n", + strerror_r(errno, bf, sizeof(bf))); + } + break; + } + goto out_delete_evlist; } static int trace__replay(struct trace *trace) -- cgit v0.10.2 From 813335b8b27d9ceeb67a073f501ada8b8dde37a7 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 8 Oct 2013 21:26:52 -0600 Subject: perf util: Add findnew method to intlist Similar to other findnew based methods if the requested object is not found, add it to the list. v2: followed format of other findnew methods per acme's request Signed-off-by: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1381289214-24885-2-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/intlist.c b/tools/perf/util/intlist.c index 826d7b3..89715b6 100644 --- a/tools/perf/util/intlist.c +++ b/tools/perf/util/intlist.c @@ -58,22 +58,36 @@ void intlist__remove(struct intlist *ilist, struct int_node *node) rblist__remove_node(&ilist->rblist, &node->rb_node); } -struct int_node *intlist__find(struct intlist *ilist, int i) +static struct int_node *__intlist__findnew(struct intlist *ilist, + int i, bool create) { - struct int_node *node; + struct int_node *node = NULL; struct rb_node *rb_node; if (ilist == NULL) return NULL; - node = NULL; - rb_node = rblist__find(&ilist->rblist, (void *)((long)i)); + if (create) + rb_node = rblist__findnew(&ilist->rblist, (void *)((long)i)); + else + rb_node = rblist__find(&ilist->rblist, (void *)((long)i)); + if (rb_node) node = container_of(rb_node, struct int_node, rb_node); return node; } +struct int_node *intlist__find(struct intlist *ilist, int i) +{ + return __intlist__findnew(ilist, i, false); +} + +struct int_node *intlist__findnew(struct intlist *ilist, int i) +{ + return __intlist__findnew(ilist, i, true); +} + static int intlist__parse_list(struct intlist *ilist, const char *s) { char *sep; diff --git a/tools/perf/util/intlist.h b/tools/perf/util/intlist.h index 0eb00ac..aa6877d 100644 --- a/tools/perf/util/intlist.h +++ b/tools/perf/util/intlist.h @@ -24,6 +24,7 @@ int intlist__add(struct intlist *ilist, int i); struct int_node *intlist__entry(const struct intlist *ilist, unsigned int idx); struct int_node *intlist__find(struct intlist *ilist, int i); +struct int_node *intlist__findnew(struct intlist *ilist, int i); static inline bool intlist__has_entry(struct intlist *ilist, int i) { diff --git a/tools/perf/util/rblist.c b/tools/perf/util/rblist.c index a16cdd2..0dfe27d 100644 --- a/tools/perf/util/rblist.c +++ b/tools/perf/util/rblist.c @@ -48,10 +48,12 @@ void rblist__remove_node(struct rblist *rblist, struct rb_node *rb_node) rblist->node_delete(rblist, rb_node); } -struct rb_node *rblist__find(struct rblist *rblist, const void *entry) +static struct rb_node *__rblist__findnew(struct rblist *rblist, + const void *entry, + bool create) { struct rb_node **p = &rblist->entries.rb_node; - struct rb_node *parent = NULL; + struct rb_node *parent = NULL, *new_node = NULL; while (*p != NULL) { int rc; @@ -67,7 +69,26 @@ struct rb_node *rblist__find(struct rblist *rblist, const void *entry) return parent; } - return NULL; + if (create) { + new_node = rblist->node_new(rblist, entry); + if (new_node) { + rb_link_node(new_node, parent, p); + rb_insert_color(new_node, &rblist->entries); + ++rblist->nr_entries; + } + } + + return new_node; +} + +struct rb_node *rblist__find(struct rblist *rblist, const void *entry) +{ + return __rblist__findnew(rblist, entry, false); +} + +struct rb_node *rblist__findnew(struct rblist *rblist, const void *entry) +{ + return __rblist__findnew(rblist, entry, true); } void rblist__init(struct rblist *rblist) diff --git a/tools/perf/util/rblist.h b/tools/perf/util/rblist.h index 6d0cae5..ff9913b 100644 --- a/tools/perf/util/rblist.h +++ b/tools/perf/util/rblist.h @@ -32,6 +32,7 @@ void rblist__delete(struct rblist *rblist); int rblist__add_node(struct rblist *rblist, const void *new_entry); void rblist__remove_node(struct rblist *rblist, struct rb_node *rb_node); struct rb_node *rblist__find(struct rblist *rblist, const void *entry); +struct rb_node *rblist__findnew(struct rblist *rblist, const void *entry); struct rb_node *rblist__entry(const struct rblist *rblist, unsigned int idx); static inline bool rblist__empty(const struct rblist *rblist) -- cgit v0.10.2 From bf2575c121ca11247ef07fd02b43f7430834f7b1 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 8 Oct 2013 21:26:53 -0600 Subject: perf trace: Add summary option to dump syscall statistics When enabled dumps a summary of all syscalls by task with the usual statistics -- min, max, average and relative stddev. For example, make - 26341 : 3344 [ 17.4% ] 0.000 ms read : 52 0.000 4.802 0.644 30.08 write : 20 0.004 0.036 0.010 21.72 open : 24 0.003 0.046 0.014 23.68 close : 64 0.002 0.055 0.008 22.53 stat : 2714 0.002 0.222 0.004 4.47 fstat : 18 0.001 0.041 0.006 46.26 mmap : 30 0.003 0.009 0.006 5.71 mprotect : 8 0.006 0.039 0.016 32.16 munmap : 12 0.007 0.077 0.020 38.25 brk : 48 0.002 0.014 0.004 10.18 rt_sigaction : 18 0.002 0.002 0.002 2.11 rt_sigprocmask : 60 0.002 0.128 0.010 32.88 access : 2 0.006 0.006 0.006 0.00 pipe : 12 0.004 0.048 0.013 35.98 vfork : 34 0.448 0.980 0.692 3.04 execve : 20 0.000 0.387 0.046 56.66 wait4 : 34 0.017 9923.287 593.221 68.45 fcntl : 8 0.001 0.041 0.013 48.79 getdents : 48 0.002 0.079 0.013 19.62 getcwd : 2 0.005 0.005 0.005 0.00 chdir : 2 0.070 0.070 0.070 0.00 getrlimit : 2 0.045 0.045 0.045 0.00 arch_prctl : 2 0.002 0.002 0.002 0.00 setrlimit : 2 0.002 0.002 0.002 0.00 openat : 94 0.003 0.005 0.003 2.11 Signed-off-by: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1381289214-24885-3-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index 1a22486..54139c6 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt @@ -93,6 +93,10 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs. --comm:: Show process COMM right beside its ID, on by default, disable with --no-comm. +--summary:: + Show a summary of syscalls by thread with min, max, and average times (in + msec) and relative stddev. + SEE ALSO -------- linkperf:perf-record[1], linkperf:perf-script[1] diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 5496546..d0f91fe 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -10,6 +10,7 @@ #include "util/strlist.h" #include "util/intlist.h" #include "util/thread_map.h" +#include "util/stat.h" #include #include @@ -909,6 +910,8 @@ struct thread_trace { int max; char **table; } paths; + + struct intlist *syscall_stats; }; static struct thread_trace *thread_trace__new(void) @@ -918,6 +921,8 @@ static struct thread_trace *thread_trace__new(void) if (ttrace) ttrace->paths.max = -1; + ttrace->syscall_stats = intlist__new(NULL); + return ttrace; } @@ -964,6 +969,7 @@ struct trace { struct intlist *pid_list; bool sched; bool multiple_threads; + bool summary; bool show_comm; double duration_filter; double runtime_ms; @@ -1291,10 +1297,8 @@ typedef int (*tracepoint_handler)(struct trace *trace, struct perf_evsel *evsel, struct perf_sample *sample); static struct syscall *trace__syscall_info(struct trace *trace, - struct perf_evsel *evsel, - struct perf_sample *sample) + struct perf_evsel *evsel, int id) { - int id = perf_evsel__intval(evsel, sample, "id"); if (id < 0) { @@ -1335,6 +1339,32 @@ out_cant_read: return NULL; } +static void thread__update_stats(struct thread_trace *ttrace, + int id, struct perf_sample *sample) +{ + struct int_node *inode; + struct stats *stats; + u64 duration = 0; + + inode = intlist__findnew(ttrace->syscall_stats, id); + if (inode == NULL) + return; + + stats = inode->priv; + if (stats == NULL) { + stats = malloc(sizeof(struct stats)); + if (stats == NULL) + return; + init_stats(stats); + inode->priv = stats; + } + + if (ttrace->entry_time && sample->time > ttrace->entry_time) + duration = sample->time - ttrace->entry_time; + + update_stats(stats, duration); +} + static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, struct perf_sample *sample) { @@ -1342,7 +1372,8 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, void *args; size_t printed = 0; struct thread *thread; - struct syscall *sc = trace__syscall_info(trace, evsel, sample); + int id = perf_evsel__intval(evsel, sample, "id"); + struct syscall *sc = trace__syscall_info(trace, evsel, id); struct thread_trace *ttrace; if (sc == NULL) @@ -1394,7 +1425,8 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, int ret; u64 duration = 0; struct thread *thread; - struct syscall *sc = trace__syscall_info(trace, evsel, sample); + int id = perf_evsel__intval(evsel, sample, "id"); + struct syscall *sc = trace__syscall_info(trace, evsel, id); struct thread_trace *ttrace; if (sc == NULL) @@ -1408,6 +1440,9 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, if (ttrace == NULL) return -1; + if (trace->summary) + thread__update_stats(ttrace, id, sample); + ret = perf_evsel__intval(evsel, sample, "ret"); ttrace = thread->priv; @@ -1574,6 +1609,8 @@ static int trace__record(int argc, const char **argv) return cmd_record(i, rec_argv, NULL); } +static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp); + static int trace__run(struct trace *trace, int argc, const char **argv) { struct perf_evlist *evlist = perf_evlist__new(); @@ -1703,6 +1740,9 @@ again: goto again; out_unmap_evlist: + if (!err && trace->summary) + trace__fprintf_thread_summary(trace, trace->output); + perf_evlist__munmap(evlist); out_close_evlist: perf_evlist__close(evlist); @@ -1798,6 +1838,9 @@ static int trace__replay(struct trace *trace) if (err) pr_err("Failed to process events, error %d", err); + else if (trace->summary) + trace__fprintf_thread_summary(trace, trace->output); + out: perf_session__delete(session); @@ -1808,10 +1851,53 @@ static size_t trace__fprintf_threads_header(FILE *fp) { size_t printed; - printed = fprintf(fp, "\n _____________________________________________________________________\n"); - printed += fprintf(fp," __) Summary of events (__\n\n"); - printed += fprintf(fp," [ task - pid ] [ events ] [ ratio ] [ runtime ]\n"); - printed += fprintf(fp," _____________________________________________________________________\n\n"); + printed = fprintf(fp, "\n _____________________________________________________________________________\n"); + printed += fprintf(fp, " __) Summary of events (__\n\n"); + printed += fprintf(fp, " [ task - pid ] [ events ] [ ratio ] [ runtime ]\n"); + printed += fprintf(fp, " syscall count min max avg stddev\n"); + printed += fprintf(fp, " msec msec msec %%\n"); + printed += fprintf(fp, " _____________________________________________________________________________\n\n"); + + return printed; +} + +static size_t thread__dump_stats(struct thread_trace *ttrace, + struct trace *trace, FILE *fp) +{ + struct stats *stats; + size_t printed = 0; + struct syscall *sc; + struct int_node *inode = intlist__first(ttrace->syscall_stats); + + if (inode == NULL) + return 0; + + printed += fprintf(fp, "\n"); + + /* each int_node is a syscall */ + while (inode) { + stats = inode->priv; + if (stats) { + double min = (double)(stats->min) / NSEC_PER_MSEC; + double max = (double)(stats->max) / NSEC_PER_MSEC; + double avg = avg_stats(stats); + double pct; + u64 n = (u64) stats->n; + + pct = avg ? 100.0 * stddev_stats(stats)/avg : 0.0; + avg /= NSEC_PER_MSEC; + + sc = &trace->syscalls.table[inode->i]; + printed += fprintf(fp, "%24s %14s : ", "", sc->name); + printed += fprintf(fp, "%5" PRIu64 " %8.3f %8.3f", + n, min, max); + printed += fprintf(fp, " %8.3f %6.2f\n", avg, pct); + } + + inode = intlist__next(inode); + } + + printed += fprintf(fp, "\n\n"); return printed; } @@ -1850,6 +1936,7 @@ static int trace__fprintf_one_thread(struct thread *thread, void *priv) printed += fprintf(fp, " - %-5d :%11lu [", thread->tid, ttrace->nr_events); printed += color_fprintf(fp, color, "%5.1f%%", ratio); printed += fprintf(fp, " ] %10.3f ms\n", ttrace->runtime_ms); + printed += thread__dump_stats(ttrace, trace, fp); data->printed += printed; @@ -1953,6 +2040,8 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) OPT_INCR('v', "verbose", &verbose, "be more verbose"), OPT_BOOLEAN('T', "time", &trace.full_time, "Show full timestamp, not time relative to first start"), + OPT_BOOLEAN(0, "summary", &trace.summary, + "Show syscall summary with statistics"), OPT_END() }; int err; @@ -2008,9 +2097,6 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) else err = trace__run(&trace, argc, argv); - if (trace.sched && !err) - trace__fprintf_thread_summary(&trace, trace.output); - out_close: if (output_name != NULL) fclose(trace.output); -- cgit v0.10.2 From a949fffb84df6f9be136198a00f796a9dc696bd0 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 9 Oct 2013 21:51:31 -0600 Subject: perf tools: Fix old GCC build error in 'get_srcline' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit trace-event-parse.c:parse_proc_kallsyms() Old GCC (4.4.2) does not see through the code flow of get_srcline() and gets confused about the status of 'file' and 'line': CC /tmp/build/perf/util/srcline.o cc1: warnings being treated as errors util/srcline.c: In function ¿get_srcline¿: util/srcline.c:226: error: ¿file¿ may be used uninitialized in this function util/srcline.c:227: error: ¿line¿ may be used uninitialized in this function make[1]: *** [/tmp/build/perf/util/srcline.o] Error 1 make: *** [install] Error 2 make: Leaving directory `/home/acme/git/linux/tools/perf' [acme@fedora12 linux]$ Help out GCC by initializing 'file' and 'line'. Signed-off-by: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Link: http://lkml.kernel.org/n/tip-h8k7h49z3cndqgjdftkmm9f8@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index 3735319..d11aefb 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c @@ -223,8 +223,8 @@ out: char *get_srcline(struct dso *dso, unsigned long addr) { - char *file; - unsigned line; + char *file = NULL; + unsigned line = 0; char *srcline; char *dso_name = dso->long_name; size_t size; -- cgit v0.10.2 From 1df9297c8535a5bb2b776381e63d8334f87d4abe Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Wed, 9 Oct 2013 23:00:38 -0300 Subject: perf tests: Fix memory leak in dso-data.c Fix for a memory leak on test_file() function in dso-data.c. Signed-off-by: Felipe Pena Acked-by: Jiri Olsa Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1381370438-4209-1-git-send-email-felipensp@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/tests/dso-data.c b/tools/perf/tests/dso-data.c index dffe055..9cc81a3 100644 --- a/tools/perf/tests/dso-data.c +++ b/tools/perf/tests/dso-data.c @@ -35,6 +35,7 @@ static char *test_file(int size) if (size != write(fd, buf, size)) templ = NULL; + free(buf); close(fd); return templ; } -- cgit v0.10.2 From 3e6a147deef93ddbb899cb394d8d44118289e76a Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 10 Oct 2013 22:24:00 +0200 Subject: perf tools: Separate lbfd check out of NO_DEMANGLE condition We fail build with NO_DEMANGLE with missing -lbfd externals error. The reason is that we now use bfd code in srcline object: perf tools: Implement addr2line directly using libbfd So we need to check/add -lbfd always now. Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 29ad7d6..9680424 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -470,6 +470,10 @@ else endif endif +ifeq ($(feature-libbfd), 1) + EXTLIBS += -lbfd +endif + ifdef NO_DEMANGLE CFLAGS += -DNO_DEMANGLE else @@ -477,9 +481,7 @@ else EXTLIBS += -liberty CFLAGS += -DHAVE_CPLUS_DEMANGLE_SUPPORT else - ifeq ($(feature-libbfd), 1) - EXTLIBS += -lbfd - else + ifneq ($(feature-libbfd), 1) $(feature_check,liberty) ifeq ($(feature-liberty), 1) EXTLIBS += -lbfd -liberty @@ -502,6 +504,10 @@ else endif endif +ifneq ($(filter -lbfd,$(EXTLIBS)),) + CFLAGS += -DHAVE_LIBBFD_SUPPORT +endif + ifndef NO_ON_EXIT ifeq ($(feature-on-exit), 1) CFLAGS += -DHAVE_ON_EXIT_SUPPORT @@ -524,10 +530,6 @@ ifndef NO_LIBNUMA endif endif -ifndef ($(filter -lbfd,$(EXTLIBS)),) - CFLAGS += -DHAVE_LIBBFD_SUPPORT -endif - # Among the variables below, these: # perfexecdir # template_dir -- cgit v0.10.2 From 52afdaf9f0c6a35e154ba42ac9510044e16d75ec Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 9 Oct 2013 15:01:11 +0300 Subject: perf symbols: Validate kcore module addresses Before using kcore we need to check that modules are in memory at the same addresses that they were when data was recorded. This is done because, while we could remap symbols to different addresses, the object code linkages would still be different which would provide an erroneous view of the object code. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1381320078-16497-2-git-send-email-adrian.hunter@intel.com [ Rename basename to base_name to avoid shadowing libgen's basename in fedora 12 ] Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 5fd9513..b2f60dd 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -798,51 +798,201 @@ bool symbol__restricted_filename(const char *filename, return restricted; } -struct kcore_mapfn_data { - struct dso *dso; - enum map_type type; - struct list_head maps; +struct module_info { + struct rb_node rb_node; + char *name; + u64 start; }; -static int kcore_mapfn(u64 start, u64 len, u64 pgoff, void *data) +static void add_module(struct module_info *mi, struct rb_root *modules) { - struct kcore_mapfn_data *md = data; - struct map *map; + struct rb_node **p = &modules->rb_node; + struct rb_node *parent = NULL; + struct module_info *m; - map = map__new2(start, md->dso, md->type); - if (map == NULL) + while (*p != NULL) { + parent = *p; + m = rb_entry(parent, struct module_info, rb_node); + if (strcmp(mi->name, m->name) < 0) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + rb_link_node(&mi->rb_node, parent, p); + rb_insert_color(&mi->rb_node, modules); +} + +static void delete_modules(struct rb_root *modules) +{ + struct module_info *mi; + struct rb_node *next = rb_first(modules); + + while (next) { + mi = rb_entry(next, struct module_info, rb_node); + next = rb_next(&mi->rb_node); + rb_erase(&mi->rb_node, modules); + free(mi->name); + free(mi); + } +} + +static struct module_info *find_module(const char *name, + struct rb_root *modules) +{ + struct rb_node *n = modules->rb_node; + + while (n) { + struct module_info *m; + int cmp; + + m = rb_entry(n, struct module_info, rb_node); + cmp = strcmp(name, m->name); + if (cmp < 0) + n = n->rb_left; + else if (cmp > 0) + n = n->rb_right; + else + return m; + } + + return NULL; +} + +static int __read_proc_modules(void *arg, const char *name, u64 start) +{ + struct rb_root *modules = arg; + struct module_info *mi; + + mi = zalloc(sizeof(struct module_info)); + if (!mi) return -ENOMEM; - map->end = map->start + len; - map->pgoff = pgoff; + mi->name = strdup(name); + mi->start = start; - list_add(&map->node, &md->maps); + if (!mi->name) { + free(mi); + return -ENOMEM; + } + + add_module(mi, modules); + + return 0; +} + +static int read_proc_modules(const char *filename, struct rb_root *modules) +{ + if (symbol__restricted_filename(filename, "/proc/modules")) + return -1; + + if (modules__parse(filename, modules, __read_proc_modules)) { + delete_modules(modules); + return -1; + } return 0; } +static int do_validate_kcore_modules(const char *filename, struct map *map, + struct map_groups *kmaps) +{ + struct rb_root modules = RB_ROOT; + struct map *old_map; + int err; + + err = read_proc_modules(filename, &modules); + if (err) + return err; + + old_map = map_groups__first(kmaps, map->type); + while (old_map) { + struct map *next = map_groups__next(old_map); + struct module_info *mi; + + if (old_map == map || old_map->start == map->start) { + /* The kernel map */ + old_map = next; + continue; + } + + /* Module must be in memory at the same address */ + mi = find_module(old_map->dso->short_name, &modules); + if (!mi || mi->start != old_map->start) { + err = -EINVAL; + goto out; + } + + old_map = next; + } +out: + delete_modules(&modules); + return err; +} + /* - * If kallsyms is referenced by name then we look for kcore in the same + * If kallsyms is referenced by name then we look for filename in the same * directory. */ -static bool kcore_filename_from_kallsyms_filename(char *kcore_filename, - const char *kallsyms_filename) +static bool filename_from_kallsyms_filename(char *filename, + const char *base_name, + const char *kallsyms_filename) { char *name; - strcpy(kcore_filename, kallsyms_filename); - name = strrchr(kcore_filename, '/'); + strcpy(filename, kallsyms_filename); + name = strrchr(filename, '/'); if (!name) return false; - if (!strcmp(name, "/kallsyms")) { - strcpy(name, "/kcore"); + name += 1; + + if (!strcmp(name, "kallsyms")) { + strcpy(name, base_name); return true; } return false; } +static int validate_kcore_modules(const char *kallsyms_filename, + struct map *map) +{ + struct map_groups *kmaps = map__kmap(map)->kmaps; + char modules_filename[PATH_MAX]; + + if (!filename_from_kallsyms_filename(modules_filename, "modules", + kallsyms_filename)) + return -EINVAL; + + if (do_validate_kcore_modules(modules_filename, map, kmaps)) + return -EINVAL; + + return 0; +} + +struct kcore_mapfn_data { + struct dso *dso; + enum map_type type; + struct list_head maps; +}; + +static int kcore_mapfn(u64 start, u64 len, u64 pgoff, void *data) +{ + struct kcore_mapfn_data *md = data; + struct map *map; + + map = map__new2(start, md->dso, md->type); + if (map == NULL) + return -ENOMEM; + + map->end = map->start + len; + map->pgoff = pgoff; + + list_add(&map->node, &md->maps); + + return 0; +} + static int dso__load_kcore(struct dso *dso, struct map *map, const char *kallsyms_filename) { @@ -859,8 +1009,12 @@ static int dso__load_kcore(struct dso *dso, struct map *map, if (map != machine->vmlinux_maps[map->type]) return -EINVAL; - if (!kcore_filename_from_kallsyms_filename(kcore_filename, - kallsyms_filename)) + if (!filename_from_kallsyms_filename(kcore_filename, "kcore", + kallsyms_filename)) + return -EINVAL; + + /* All modules must be present at their original addresses */ + if (validate_kcore_modules(kallsyms_filename, map)) return -EINVAL; md.dso = dso; -- cgit v0.10.2 From afba19d9dc8eba66ea26901708cf99354c637786 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 9 Oct 2013 15:01:12 +0300 Subject: perf symbols: Workaround objdump difficulties with kcore The objdump tool fails to annotate module symbols when looking at kcore. Workaround this by extracting object code from kcore and putting it in a temporary file for objdump to use instead. The temporary file is created to look like kcore but contains only the function being disassembled. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1381320078-16497-3-git-send-email-adrian.hunter@intel.com [ Renamed 'index' to 'idx' to avoid shadowing string.h's 'index' in Fedora 12, Replace local with variable length with malloc/free to fix build in Fedora 12 ] Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index d73e800..882bb86 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -879,6 +879,8 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) FILE *file; int err = 0; char symfs_filename[PATH_MAX]; + struct kcore_extract kce; + bool delete_extract = false; if (filename) { snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", @@ -940,6 +942,23 @@ fallback: pr_debug("annotating [%p] %30s : [%p] %30s\n", dso, dso->long_name, sym, sym->name); + if (dso__is_kcore(dso)) { + kce.kcore_filename = symfs_filename; + kce.addr = map__rip_2objdump(map, sym->start); + kce.offs = sym->start; + kce.len = sym->end + 1 - sym->start; + if (!kcore_extract__create(&kce)) { + delete_extract = true; + strlcpy(symfs_filename, kce.extract_filename, + sizeof(symfs_filename)); + if (free_filename) { + free(filename); + free_filename = false; + } + filename = symfs_filename; + } + } + snprintf(command, sizeof(command), "%s %s%s --start-address=0x%016" PRIx64 " --stop-address=0x%016" PRIx64 @@ -972,6 +991,8 @@ fallback: pclose(file); out_free_filename: + if (delete_extract) + kcore_extract__delete(&kce); if (free_filename) free(filename); return err; diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index c376930..499c71d 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -1018,6 +1018,235 @@ int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data, return err; } +static int copy_bytes(int from, off_t from_offs, int to, off_t to_offs, u64 len) +{ + ssize_t r; + size_t n; + int err = -1; + char *buf = malloc(page_size); + + if (buf == NULL) + return -1; + + if (lseek(to, to_offs, SEEK_SET) != to_offs) + goto out; + + if (lseek(from, from_offs, SEEK_SET) != from_offs) + goto out; + + while (len) { + n = page_size; + if (len < n) + n = len; + /* Use read because mmap won't work on proc files */ + r = read(from, buf, n); + if (r < 0) + goto out; + if (!r) + break; + n = r; + r = write(to, buf, n); + if (r < 0) + goto out; + if ((size_t)r != n) + goto out; + len -= n; + } + + err = 0; +out: + free(buf); + return err; +} + +struct kcore { + int fd; + int elfclass; + Elf *elf; + GElf_Ehdr ehdr; +}; + +static int kcore__open(struct kcore *kcore, const char *filename) +{ + GElf_Ehdr *ehdr; + + kcore->fd = open(filename, O_RDONLY); + if (kcore->fd == -1) + return -1; + + kcore->elf = elf_begin(kcore->fd, ELF_C_READ, NULL); + if (!kcore->elf) + goto out_close; + + kcore->elfclass = gelf_getclass(kcore->elf); + if (kcore->elfclass == ELFCLASSNONE) + goto out_end; + + ehdr = gelf_getehdr(kcore->elf, &kcore->ehdr); + if (!ehdr) + goto out_end; + + return 0; + +out_end: + elf_end(kcore->elf); +out_close: + close(kcore->fd); + return -1; +} + +static int kcore__init(struct kcore *kcore, char *filename, int elfclass, + bool temp) +{ + GElf_Ehdr *ehdr; + + kcore->elfclass = elfclass; + + if (temp) + kcore->fd = mkstemp(filename); + else + kcore->fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0400); + if (kcore->fd == -1) + return -1; + + kcore->elf = elf_begin(kcore->fd, ELF_C_WRITE, NULL); + if (!kcore->elf) + goto out_close; + + if (!gelf_newehdr(kcore->elf, elfclass)) + goto out_end; + + ehdr = gelf_getehdr(kcore->elf, &kcore->ehdr); + if (!ehdr) + goto out_end; + + return 0; + +out_end: + elf_end(kcore->elf); +out_close: + close(kcore->fd); + unlink(filename); + return -1; +} + +static void kcore__close(struct kcore *kcore) +{ + elf_end(kcore->elf); + close(kcore->fd); +} + +static int kcore__copy_hdr(struct kcore *from, struct kcore *to, size_t count) +{ + GElf_Ehdr *ehdr = &to->ehdr; + GElf_Ehdr *kehdr = &from->ehdr; + + memcpy(ehdr->e_ident, kehdr->e_ident, EI_NIDENT); + ehdr->e_type = kehdr->e_type; + ehdr->e_machine = kehdr->e_machine; + ehdr->e_version = kehdr->e_version; + ehdr->e_entry = 0; + ehdr->e_shoff = 0; + ehdr->e_flags = kehdr->e_flags; + ehdr->e_phnum = count; + ehdr->e_shentsize = 0; + ehdr->e_shnum = 0; + ehdr->e_shstrndx = 0; + + if (from->elfclass == ELFCLASS32) { + ehdr->e_phoff = sizeof(Elf32_Ehdr); + ehdr->e_ehsize = sizeof(Elf32_Ehdr); + ehdr->e_phentsize = sizeof(Elf32_Phdr); + } else { + ehdr->e_phoff = sizeof(Elf64_Ehdr); + ehdr->e_ehsize = sizeof(Elf64_Ehdr); + ehdr->e_phentsize = sizeof(Elf64_Phdr); + } + + if (!gelf_update_ehdr(to->elf, ehdr)) + return -1; + + if (!gelf_newphdr(to->elf, count)) + return -1; + + return 0; +} + +static int kcore__add_phdr(struct kcore *kcore, int idx, off_t offset, + u64 addr, u64 len) +{ + GElf_Phdr gphdr; + GElf_Phdr *phdr; + + phdr = gelf_getphdr(kcore->elf, idx, &gphdr); + if (!phdr) + return -1; + + phdr->p_type = PT_LOAD; + phdr->p_flags = PF_R | PF_W | PF_X; + phdr->p_offset = offset; + phdr->p_vaddr = addr; + phdr->p_paddr = 0; + phdr->p_filesz = len; + phdr->p_memsz = len; + phdr->p_align = page_size; + + if (!gelf_update_phdr(kcore->elf, idx, phdr)) + return -1; + + return 0; +} + +static off_t kcore__write(struct kcore *kcore) +{ + return elf_update(kcore->elf, ELF_C_WRITE); +} + +int kcore_extract__create(struct kcore_extract *kce) +{ + struct kcore kcore; + struct kcore extract; + size_t count = 1; + int idx = 0, err = -1; + off_t offset = page_size, sz; + + if (kcore__open(&kcore, kce->kcore_filename)) + return -1; + + strcpy(kce->extract_filename, PERF_KCORE_EXTRACT); + if (kcore__init(&extract, kce->extract_filename, kcore.elfclass, true)) + goto out_kcore_close; + + if (kcore__copy_hdr(&kcore, &extract, count)) + goto out_extract_close; + + if (kcore__add_phdr(&extract, idx, offset, kce->addr, kce->len)) + goto out_extract_close; + + sz = kcore__write(&extract); + if (sz < 0 || sz > offset) + goto out_extract_close; + + if (copy_bytes(kcore.fd, kce->offs, extract.fd, offset, kce->len)) + goto out_extract_close; + + err = 0; + +out_extract_close: + kcore__close(&extract); + if (err) + unlink(kce->extract_filename); +out_kcore_close: + kcore__close(&kcore); + + return err; +} + +void kcore_extract__delete(struct kcore_extract *kce) +{ + unlink(kce->extract_filename); +} + void symbol__elf_init(void) { elf_version(EV_CURRENT); diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index 3a802c3..928556d 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c @@ -308,6 +308,15 @@ int file__read_maps(int fd __maybe_unused, bool exe __maybe_unused, return -1; } +int kcore_extract__create(struct kcore_extract *kce __maybe_unused) +{ + return -1; +} + +void kcore_extract__delete(struct kcore_extract *kce __maybe_unused) +{ +} + void symbol__elf_init(void) { } diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 2d3eb43..fb107e1 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -256,4 +256,18 @@ typedef int (*mapfn_t)(u64 start, u64 len, u64 pgoff, void *data); int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data, bool *is_64_bit); +#define PERF_KCORE_EXTRACT "/tmp/perf-kcore-XXXXXX" + +struct kcore_extract { + char *kcore_filename; + u64 addr; + u64 offs; + u64 len; + char extract_filename[sizeof(PERF_KCORE_EXTRACT)]; + int fd; +}; + +int kcore_extract__create(struct kcore_extract *kce); +void kcore_extract__delete(struct kcore_extract *kce); + #endif /* __PERF_SYMBOL */ -- cgit v0.10.2 From 4e987712740a3634c19a6fedaf12577b26775dc5 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 14 Oct 2013 13:43:38 +0300 Subject: perf symbols: Add map_groups__find_ams() Add a function to find a symbol using an ip that might be on a different map. Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1381747424-3557-2-git-send-email-adrian.hunter@intel.com Signed-off-by: Adrian Hunter Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 17ee458..9dea404 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -371,6 +371,23 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, return NULL; } +int map_groups__find_ams(struct addr_map_symbol *ams, symbol_filter_t filter) +{ + if (ams->addr < ams->map->start || ams->addr > ams->map->end) { + if (ams->map->groups == NULL) + return -1; + ams->map = map_groups__find(ams->map->groups, ams->map->type, + ams->addr); + if (ams->map == NULL) + return -1; + } + + ams->al_addr = ams->map->map_ip(ams->map, ams->addr); + ams->sym = map__find_symbol(ams->map, ams->al_addr, filter); + + return ams->sym ? 0 : -1; +} + size_t __map_groups__fprintf_maps(struct map_groups *mg, enum map_type type, int verbose, FILE *fp) { diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 4886ca2..0359b4a 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -167,6 +167,10 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, struct map **mapp, symbol_filter_t filter); +struct addr_map_symbol; + +int map_groups__find_ams(struct addr_map_symbol *ams, symbol_filter_t filter); + static inline struct symbol *map_groups__find_function_by_name(struct map_groups *mg, const char *name, struct map **mapp, -- cgit v0.10.2 From 3fb66335e13ef7426affe9efa48c08857202c1cb Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 9 Oct 2013 17:00:23 +0200 Subject: tools/perf/build: Fix non-existent build directory handling Arnaldo reported that non-existent build directories were not recognized properly. The reason is readlink failure causing 'O' to become empty. Solve it by passing through the 'O' variable unmodified if readlink fails. Reported-by: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Link: http://lkml.kernel.org/r/20131009150023.GA10167@gmail.com Signed-off-by: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 5aa3d04..9147044 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -34,7 +34,7 @@ endif # Only pass canonical directory names as the output directory: # ifneq ($(O),) - FULL_O := $(shell readlink -f $(O)) + FULL_O := $(shell readlink -f $(O) || echo $(O)) endif define print_msg -- cgit v0.10.2 From fcf92585014f0a0e390d2819de8278ae90da5842 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Thu, 10 Oct 2013 08:05:25 +0200 Subject: tools/perf/build: Pass through DEBUG parameter Arnaldo reported that 'make DEBUG=1' does not work anymore. The reason is that 'Makefile' only passes it through to 'Makefile.perf' via the environment, but 'Makefile.perf' checks that it's a command line option: ifeq ("$(origin DEBUG)", "command line") PERF_DEBUG = $(DEBUG) endif So pass it through properly, and also clean up DEBUG parameter handling while at it and fix a couple of annoyances: - DEBUG=0 used to be interpreted as 'debugging on'. Turn it into 'debugging off' instead. - Same was the case for 'DEBUG=' - turn that into debug-off as well. - Pass in just a clean, sanitized 'DEBUG' value and get rid of the intermediate, unnecessary PERF_DEBUG variable. Reported-by: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Signed-off-by: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 9147044..4835618 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -37,12 +37,25 @@ ifneq ($(O),) FULL_O := $(shell readlink -f $(O) || echo $(O)) endif +# +# Only accept the 'DEBUG' variable from the command line: +# +ifeq ("$(origin DEBUG)", "command line") + ifeq ($(DEBUG),) + override DEBUG = 0 + else + SET_DEBUG = "DEBUG=$(DEBUG)" + endif +else + override DEBUG = 0 +endif + define print_msg @printf ' BUILD: Doing '\''make \033[33m-j'$(JOBS)'\033[m'\'' parallel build\n' endef define make - @$(MAKE) -f Makefile.perf --no-print-directory -j$(JOBS) O=$(FULL_O) $@ + @$(MAKE) -f Makefile.perf --no-print-directory -j$(JOBS) O=$(FULL_O) $(SET_DEBUG) $@ endef # diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 9680424..9524c0c 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -66,10 +66,7 @@ ifneq ($(WERROR),0) CFLAGS += -Werror endif -ifeq ("$(origin DEBUG)", "command line") - PERF_DEBUG = $(DEBUG) -endif -ifndef PERF_DEBUG +ifeq ($(DEBUG),0) CFLAGS += -O6 endif @@ -210,7 +207,7 @@ ifeq ($(feature-volatile-register-var), 1) CFLAGS += -Wvolatile-register-var endif -ifndef PERF_DEBUG +ifeq ($(DEBUG),0) ifeq ($(feature-fortify-source), 1) CFLAGS += -D_FORTIFY_SOURCE=2 endif -- cgit v0.10.2 From 0dc097421974e076b47d19806966adf5ebc7fd6b Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Thu, 10 Oct 2013 08:47:01 +0200 Subject: tools/perf/build: Fix DPACKAGE definitions for the libbfd et al testcases Namhyung Kim reported these duplicate DPACKAGE definitions: test-libbfd: $(BUILD) -DPACKAGE='perf' -DPACKAGE=perf -lbfd -ldl Fix all affected places and use Namhyung's suggestion that the definition should look like a normal C string: -DPACKAGE='"perf"'. Reported-by: Namhyung Kim Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Signed-off-by: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 8ecac19..cf33596 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -37,7 +37,7 @@ BUILD = $(CC) $(LDFLAGS) -o $(OUTPUT)$@ $@.c ############################### test-all: - $(BUILD) -Werror -fstack-protector -fstack-protector-all -Wvolatile-register-var -O2 -Werror -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lunwind -lunwind-x86_64 -lelf -laudit -I/usr/include/slang -lslang $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='perf' -DPACKAGE=perf -lbfd -ldl + $(BUILD) -Werror -fstack-protector -fstack-protector-all -Wvolatile-register-var -O2 -Werror -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lunwind -lunwind-x86_64 -lelf -laudit -I/usr/include/slang -lslang $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl test-hello: $(BUILD) @@ -123,13 +123,13 @@ test-libpython-version: $(BUILD) $(FLAGS_PYTHON_EMBED) test-libbfd: - $(BUILD) -DPACKAGE='perf' -DPACKAGE=perf -lbfd -ldl + $(BUILD) -DPACKAGE='"perf"' -lbfd -ldl test-liberty: - $(CC) -o $(OUTPUT)$@ test-libbfd.c -DPACKAGE='perf' -DPACKAGE=perf -lbfd -ldl -liberty + $(CC) -o $(OUTPUT)$@ test-libbfd.c -DPACKAGE='"perf"' -lbfd -ldl -liberty test-liberty-z: - $(CC) -o $(OUTPUT)$@ test-libbfd.c -DPACKAGE='perf' -DPACKAGE=perf -lbfd -ldl -liberty -lz + $(CC) -o $(OUTPUT)$@ test-libbfd.c -DPACKAGE='"perf"' -lbfd -ldl -liberty -lz test-cplus-demangle: $(BUILD) -liberty -- cgit v0.10.2 From 5f36978ca5b12b1e35535dedb8c999694fc0dfcf Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Thu, 10 Oct 2013 08:53:17 +0200 Subject: tools/perf/build: Simplify the libelf logic Ulrich Drepper and Namhyung Kim reported that the libelf logic in config/Makefile is duplicated in part. Remove the duplication, and also remove the now unused FLAGS_LIBELF variable. Reported-by: Ulrich Drepper Reported-by: Namhyung Kim Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: David Ahern Cc: Jiri Olsa Signed-off-by: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 9524c0c..d207922 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -280,7 +280,6 @@ endif # NO_LIBELF ifndef NO_LIBELF CFLAGS += -DHAVE_LIBELF_SUPPORT - FLAGS_LIBELF=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) ifeq ($(feature-libelf-mmap), 1) CFLAGS += -DHAVE_LIBELF_MMAP_SUPPORT @@ -305,13 +304,6 @@ ifndef NO_LIBELF endif # NO_DWARF endif # NO_LIBELF -ifndef NO_LIBELF - CFLAGS += -DHAVE_LIBELF_SUPPORT - ifeq ($(feature-libelf-mmap), 1) - CFLAGS += -DHAVE_LIBELF_MMAP_SUPPORT - endif -endif # NO_LIBELF - # There's only x86 (both 32 and 64) support for CFI unwind so far ifneq ($(ARCH),x86) NO_LIBUNWIND := 1 -- cgit v0.10.2 From 01287e2cb7ad17b3d77751888d458a6b6a2bba15 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Thu, 10 Oct 2013 08:58:57 +0200 Subject: tools/perf/build: Remove the volatile-register-var feature check Namhyung Kim noticed that the volatile-register-var feature check is superfluous: > The gcc manpage says this warning is enabled by -Wall, and we add -Wall > to CFLAGS before doing feature checks. So all gcc versions that support > -Wvolatile-register-var enables it by default without this check and > older gcc versions will always fail the feature check. Remove it - this will further speed up feature checks. Reported-by: Namhyung Kim Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: David Ahern Cc: Jiri Olsa Signed-off-by: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index d207922..c516d6b 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -132,8 +132,7 @@ CORE_FEATURE_TESTS = \ libunwind \ on-exit \ stackprotector \ - stackprotector-all \ - volatile-register-var + stackprotector-all # # So here we detect whether test-all was rebuilt, to be able @@ -203,10 +202,6 @@ ifeq ($(feature-stackprotector), 1) CFLAGS += -Wstack-protector endif -ifeq ($(feature-volatile-register-var), 1) - CFLAGS += -Wvolatile-register-var -endif - ifeq ($(DEBUG),0) ifeq ($(feature-fortify-source), 1) CFLAGS += -D_FORTIFY_SOURCE=2 diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index cf33596..2eb8346 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -25,8 +25,7 @@ FILES= \ test-libunwind \ test-on-exit \ test-stackprotector-all \ - test-stackprotector \ - test-volatile-register-var + test-stackprotector CC := $(CC) -MD @@ -37,7 +36,7 @@ BUILD = $(CC) $(LDFLAGS) -o $(OUTPUT)$@ $@.c ############################### test-all: - $(BUILD) -Werror -fstack-protector -fstack-protector-all -Wvolatile-register-var -O2 -Werror -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lunwind -lunwind-x86_64 -lelf -laudit -I/usr/include/slang -lslang $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl + $(BUILD) -Werror -fstack-protector -fstack-protector-all -O2 -Werror -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lunwind -lunwind-x86_64 -lelf -laudit -I/usr/include/slang -lslang $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl test-hello: $(BUILD) @@ -48,9 +47,6 @@ test-stackprotector-all: test-stackprotector: $(BUILD) -Werror -fstack-protector -test-volatile-register-var: - $(BUILD) -Werror -Wvolatile-register-var - test-fortify-source: $(BUILD) -O2 -Werror -D_FORTIFY_SOURCE=2 -- cgit v0.10.2 From 046fa7ae20d2390c65be8eb406efecff15e8b71d Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Thu, 10 Oct 2013 09:06:21 +0200 Subject: tools/perf/build: Improve the 'stackprotector' feature test Namhyung Kim noticed that the stackprotector testcase was incomplete: > The flag being checked should be -"W"stack-protector instead of > -"f"stack-protector. And the gcc manpage says that -Wstack-protector is > only active when -fstack-protector is active. So the end result should > look like > > $(BUILD) -Werror -fstack-protector -Wstack-protector Add -Wstack-protector. Reported-by: Namhyung Kim Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: David Ahern Cc: Jiri Olsa Signed-off-by: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 2eb8346..c70d23e 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -45,7 +45,7 @@ test-stackprotector-all: $(BUILD) -Werror -fstack-protector-all test-stackprotector: - $(BUILD) -Werror -fstack-protector + $(BUILD) -Werror -fstack-protector -Wstack-protector test-fortify-source: $(BUILD) -O2 -Werror -D_FORTIFY_SOURCE=2 -- cgit v0.10.2 From 231486a5223b1023bcabf53d16d63a83b9f27bf7 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Thu, 10 Oct 2013 09:10:59 +0200 Subject: tools/perf/build: Simplify the autodep inclusion rule Namhyung Kim noticed that the autodep .d file inclusion rule was unnecessarily complicated: > > +-include *.d */*.d > > Hmm.. this */*.d part is really needed? Only include *.d files. Reported-by: Namhyung Kim Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: David Ahern Cc: Jiri Olsa Signed-off-by: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index c70d23e..452b67c 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -136,7 +136,7 @@ test-on-exit: test-backtrace: $(BUILD) --include *.d */*.d +-include *.d ############################### -- cgit v0.10.2 From 6e427ab02c8886ca6c9ecdbb318e68fe8f605469 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Oct 2013 13:43:40 +0300 Subject: perf annotate: Find kcore symbols on other maps Use the new map_groups__find_ams() method to find kcore symbols on other maps. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1381747424-3557-4-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 882bb86..cf6242c 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -825,20 +825,16 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, dl->ops.target.offset = dl->ops.target.addr - map__rip_2objdump(map, sym->start); - /* - * kcore has no symbols, so add the call target name if it is on the - * same map. - */ + /* kcore has no symbols, so add the call target name */ if (dl->ins && ins__is_call(dl->ins) && !dl->ops.target.name) { - struct symbol *s; - u64 ip = dl->ops.target.addr; - - if (ip >= map->start && ip <= map->end) { - ip = map->map_ip(map, ip); - s = map__find_symbol(map, ip, NULL); - if (s && s->start == ip) - dl->ops.target.name = strdup(s->name); - } + struct addr_map_symbol target = { + .map = map, + .addr = dl->ops.target.addr, + }; + + if (!map_groups__find_ams(&target, NULL) && + target.sym->start == target.al_addr) + dl->ops.target.name = strdup(target.sym->name); } disasm__add(¬es->src->source, dl); -- cgit v0.10.2 From 9a17d7268d71674f0bbff6821f7d8e6dc0ece19a Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Oct 2013 13:43:41 +0300 Subject: perf tools: Add copyfile_mode() Add a function to copy a file specifying the permissions to use for the created file. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1381747424-3557-5-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index ab71d62..8dc8cf3 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -55,17 +55,20 @@ int mkdir_p(char *path, mode_t mode) return (stat(path, &st) && mkdir(path, mode)) ? -1 : 0; } -static int slow_copyfile(const char *from, const char *to) +static int slow_copyfile(const char *from, const char *to, mode_t mode) { - int err = 0; + int err = -1; char *line = NULL; size_t n; FILE *from_fp = fopen(from, "r"), *to_fp; + mode_t old_umask; if (from_fp == NULL) goto out; + old_umask = umask(mode ^ 0777); to_fp = fopen(to, "w"); + umask(old_umask); if (to_fp == NULL) goto out_fclose_from; @@ -82,7 +85,7 @@ out: return err; } -int copyfile(const char *from, const char *to) +int copyfile_mode(const char *from, const char *to, mode_t mode) { int fromfd, tofd; struct stat st; @@ -93,13 +96,13 @@ int copyfile(const char *from, const char *to) goto out; if (st.st_size == 0) /* /proc? do it slowly... */ - return slow_copyfile(from, to); + return slow_copyfile(from, to, mode); fromfd = open(from, O_RDONLY); if (fromfd < 0) goto out; - tofd = creat(to, 0755); + tofd = creat(to, mode); if (tofd < 0) goto out_close_from; @@ -121,6 +124,11 @@ out: return err; } +int copyfile(const char *from, const char *to) +{ + return copyfile_mode(from, to, 0755); +} + unsigned long convert_unit(unsigned long value, char *unit) { *unit = ' '; diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 1f06ba4..42dfba7 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -243,6 +243,7 @@ static inline int sane_case(int x, int high) int mkdir_p(char *path, mode_t mode); int copyfile(const char *from, const char *to); +int copyfile_mode(const char *from, const char *to, mode_t mode); s64 perf_atoll(const char *str); char **argv_split(const char *str, int *argcp); -- cgit v0.10.2 From 0544d4225c52ca31ab2a55b22ddce1392d8f45a4 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Oct 2013 13:43:43 +0300 Subject: perf symbols: Add ability to find kcore in build-id cache When no vmlinux is found, tools will use kallsyms and, if possible, kcore. Add the ability to find kcore in the build-id cache. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1381747424-3557-7-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index b2f60dd..76a9e93 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1401,6 +1401,105 @@ out: return err; } +static int find_matching_kcore(struct map *map, char *dir, size_t dir_sz) +{ + char kallsyms_filename[PATH_MAX]; + struct dirent *dent; + int ret = -1; + DIR *d; + + d = opendir(dir); + if (!d) + return -1; + + while (1) { + dent = readdir(d); + if (!dent) + break; + if (dent->d_type != DT_DIR) + continue; + scnprintf(kallsyms_filename, sizeof(kallsyms_filename), + "%s/%s/kallsyms", dir, dent->d_name); + if (!validate_kcore_modules(kallsyms_filename, map)) { + strlcpy(dir, kallsyms_filename, dir_sz); + ret = 0; + break; + } + } + + closedir(d); + + return ret; +} + +static char *dso__find_kallsyms(struct dso *dso, struct map *map) +{ + u8 host_build_id[BUILD_ID_SIZE]; + char sbuild_id[BUILD_ID_SIZE * 2 + 1]; + bool is_host = false; + char path[PATH_MAX]; + + if (!dso->has_build_id) { + /* + * Last resort, if we don't have a build-id and couldn't find + * any vmlinux file, try the running kernel kallsyms table. + */ + goto proc_kallsyms; + } + + if (sysfs__read_build_id("/sys/kernel/notes", host_build_id, + sizeof(host_build_id)) == 0) + is_host = dso__build_id_equal(dso, host_build_id); + + build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id); + + /* Use /proc/kallsyms if possible */ + if (is_host) { + DIR *d; + int fd; + + /* If no cached kcore go with /proc/kallsyms */ + scnprintf(path, sizeof(path), "%s/[kernel.kcore]/%s", + buildid_dir, sbuild_id); + d = opendir(path); + if (!d) + goto proc_kallsyms; + closedir(d); + + /* + * Do not check the build-id cache, until we know we cannot use + * /proc/kcore. + */ + fd = open("/proc/kcore", O_RDONLY); + if (fd != -1) { + close(fd); + /* If module maps match go with /proc/kallsyms */ + if (!validate_kcore_modules("/proc/kallsyms", map)) + goto proc_kallsyms; + } + + /* Find kallsyms in build-id cache with kcore */ + if (!find_matching_kcore(map, path, sizeof(path))) + return strdup(path); + + goto proc_kallsyms; + } + + scnprintf(path, sizeof(path), "%s/[kernel.kallsyms]/%s", + buildid_dir, sbuild_id); + + if (access(path, F_OK)) { + pr_err("No kallsyms or vmlinux with build-id %s was found\n", + sbuild_id); + return NULL; + } + + return strdup(path); + +proc_kallsyms: + return strdup("/proc/kallsyms"); +} + static int dso__load_kernel_sym(struct dso *dso, struct map *map, symbol_filter_t filter) { @@ -1449,51 +1548,11 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map, if (symbol_conf.symfs[0] != 0) return -1; - /* - * Say the kernel DSO was created when processing the build-id header table, - * we have a build-id, so check if it is the same as the running kernel, - * using it if it is. - */ - if (dso->has_build_id) { - u8 kallsyms_build_id[BUILD_ID_SIZE]; - char sbuild_id[BUILD_ID_SIZE * 2 + 1]; - - if (sysfs__read_build_id("/sys/kernel/notes", kallsyms_build_id, - sizeof(kallsyms_build_id)) == 0) { - if (dso__build_id_equal(dso, kallsyms_build_id)) { - kallsyms_filename = "/proc/kallsyms"; - goto do_kallsyms; - } - } - /* - * Now look if we have it on the build-id cache in - * $HOME/.debug/[kernel.kallsyms]. - */ - build_id__sprintf(dso->build_id, sizeof(dso->build_id), - sbuild_id); - - if (asprintf(&kallsyms_allocated_filename, - "%s/.debug/[kernel.kallsyms]/%s", - getenv("HOME"), sbuild_id) == -1) { - pr_err("Not enough memory for kallsyms file lookup\n"); - return -1; - } - - kallsyms_filename = kallsyms_allocated_filename; + kallsyms_allocated_filename = dso__find_kallsyms(dso, map); + if (!kallsyms_allocated_filename) + return -1; - if (access(kallsyms_filename, F_OK)) { - pr_err("No kallsyms or vmlinux with build-id %s " - "was found\n", sbuild_id); - free(kallsyms_allocated_filename); - return -1; - } - } else { - /* - * Last resort, if we don't have a build-id and couldn't find - * any vmlinux file, try the running kernel kallsyms table. - */ - kallsyms_filename = "/proc/kallsyms"; - } + kallsyms_filename = kallsyms_allocated_filename; do_kallsyms: err = dso__load_kallsyms(dso, kallsyms_filename, map, filter); -- cgit v0.10.2 From 1179e11bbb655162e83b33e17a97c45d3fe292da Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Oct 2013 13:43:39 +0300 Subject: perf annotate: Fix annotate_browser__callq() When following a call, annotate_browser__callq() uses the current symbol's map to look up the target ip. That will not work if the target ip is on a map with a different mapping (i.e. start - pgoff is different). Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1381747424-3557-3-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 08545ae..57d3a86 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -442,35 +442,34 @@ static bool annotate_browser__callq(struct annotate_browser *browser, { struct map_symbol *ms = browser->b.priv; struct disasm_line *dl = browser->selection; - struct symbol *sym = ms->sym; struct annotation *notes; - struct symbol *target; - u64 ip; + struct addr_map_symbol target = { + .map = ms->map, + .addr = dl->ops.target.addr, + }; char title[SYM_TITLE_MAX_SIZE]; if (!ins__is_call(dl->ins)) return false; - ip = ms->map->map_ip(ms->map, dl->ops.target.addr); - target = map__find_symbol(ms->map, ip, NULL); - if (target == NULL) { + if (map_groups__find_ams(&target, NULL)) { ui_helpline__puts("The called function was not found."); return true; } - notes = symbol__annotation(target); + notes = symbol__annotation(target.sym); pthread_mutex_lock(¬es->lock); - if (notes->src == NULL && symbol__alloc_hist(target) < 0) { + if (notes->src == NULL && symbol__alloc_hist(target.sym) < 0) { pthread_mutex_unlock(¬es->lock); ui__warning("Not enough memory for annotating '%s' symbol!\n", - target->name); + target.sym->name); return true; } pthread_mutex_unlock(¬es->lock); - symbol__tui_annotate(target, ms->map, evsel, hbt); - sym_title(sym, ms->map, title, sizeof(title)); + symbol__tui_annotate(target.sym, target.map, evsel, hbt); + sym_title(ms->sym, ms->map, title, sizeof(title)); ui_browser__show_title(&browser->b, title); return true; } -- cgit v0.10.2 From 5e049fce368dfe07702c3664add9ae7b45df1a9a Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 11 Oct 2013 15:43:17 -0600 Subject: ASoC: tegra: support new register layouts in Tegra124 Tegra124 introduces some small changes to the layout of some registers. Modify the affected drivers to program those registers appropriately based on which SoC they're running on. Tegra124 also introduced some new modules on the AHUB configlink register bus. These will require new entries in configlink_clocks[] in the AHUB driver. However, supporting that change likely relies on switching Tegra to the common reset framework, so I'll defer that change for now. Based-on-work-by: Arun Shamanna Lakshmi Based-on-work-by: Songhee Baek Signed-off-by: Stephen Warren Signed-off-by: Mark Brown diff --git a/sound/soc/tegra/tegra30_ahub.c b/sound/soc/tegra/tegra30_ahub.c index d554d46..bdd19db 100644 --- a/sound/soc/tegra/tegra30_ahub.c +++ b/sound/soc/tegra/tegra30_ahub.c @@ -100,6 +100,7 @@ int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif, { int channel; u32 reg, val; + struct tegra30_ahub_cif_conf cif_conf; channel = find_first_zero_bit(ahub->rx_usage, TEGRA30_AHUB_CHANNEL_CTRL_COUNT); @@ -123,15 +124,21 @@ int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif, TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_16; tegra30_apbif_write(reg, val); + cif_conf.threshold = 0; + cif_conf.audio_channels = 2; + cif_conf.client_channels = 2; + cif_conf.audio_bits = TEGRA30_AUDIOCIF_BITS_16; + cif_conf.client_bits = TEGRA30_AUDIOCIF_BITS_16; + cif_conf.expand = 0; + cif_conf.stereo_conv = 0; + cif_conf.replicate = 0; + cif_conf.direction = TEGRA30_AUDIOCIF_DIRECTION_RX; + cif_conf.truncate = 0; + cif_conf.mono_conv = 0; + reg = TEGRA30_AHUB_CIF_RX_CTRL + (channel * TEGRA30_AHUB_CIF_RX_CTRL_STRIDE); - val = (0 << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) | - (1 << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) | - (1 << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) | - TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_16 | - TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_16 | - TEGRA30_AUDIOCIF_CTRL_DIRECTION_RX; - tegra30_apbif_write(reg, val); + ahub->soc_data->set_audio_cif(ahub->regmap_apbif, reg, &cif_conf); return 0; } @@ -183,6 +190,7 @@ int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif, { int channel; u32 reg, val; + struct tegra30_ahub_cif_conf cif_conf; channel = find_first_zero_bit(ahub->tx_usage, TEGRA30_AHUB_CHANNEL_CTRL_COUNT); @@ -206,15 +214,21 @@ int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif, TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_16; tegra30_apbif_write(reg, val); + cif_conf.threshold = 0; + cif_conf.audio_channels = 2; + cif_conf.client_channels = 2; + cif_conf.audio_bits = TEGRA30_AUDIOCIF_BITS_16; + cif_conf.client_bits = TEGRA30_AUDIOCIF_BITS_16; + cif_conf.expand = 0; + cif_conf.stereo_conv = 0; + cif_conf.replicate = 0; + cif_conf.direction = TEGRA30_AUDIOCIF_DIRECTION_TX; + cif_conf.truncate = 0; + cif_conf.mono_conv = 0; + reg = TEGRA30_AHUB_CIF_TX_CTRL + (channel * TEGRA30_AHUB_CIF_TX_CTRL_STRIDE); - val = (0 << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) | - (1 << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) | - (1 << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) | - TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_16 | - TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_16 | - TEGRA30_AUDIOCIF_CTRL_DIRECTION_TX; - tegra30_apbif_write(reg, val); + ahub->soc_data->set_audio_cif(ahub->regmap_apbif, reg, &cif_conf); return 0; } @@ -437,13 +451,21 @@ static const struct regmap_config tegra30_ahub_ahub_regmap_config = { static struct tegra30_ahub_soc_data soc_data_tegra30 = { .clk_list_mask = CLK_LIST_MASK_TEGRA30, + .set_audio_cif = tegra30_ahub_set_cif, }; static struct tegra30_ahub_soc_data soc_data_tegra114 = { .clk_list_mask = CLK_LIST_MASK_TEGRA114, + .set_audio_cif = tegra30_ahub_set_cif, +}; + +static struct tegra30_ahub_soc_data soc_data_tegra124 = { + .clk_list_mask = CLK_LIST_MASK_TEGRA114, + .set_audio_cif = tegra124_ahub_set_cif, }; static const struct of_device_id tegra30_ahub_of_match[] = { + { .compatible = "nvidia,tegra124-ahub", .data = &soc_data_tegra124 }, { .compatible = "nvidia,tegra114-ahub", .data = &soc_data_tegra114 }, { .compatible = "nvidia,tegra30-ahub", .data = &soc_data_tegra30 }, {}, @@ -497,6 +519,7 @@ static int tegra30_ahub_probe(struct platform_device *pdev) } dev_set_drvdata(&pdev->dev, ahub); + ahub->soc_data = soc_data; ahub->dev = &pdev->dev; ahub->clk_d_audio = clk_get(&pdev->dev, "d_audio"); @@ -669,6 +692,70 @@ static struct platform_driver tegra30_ahub_driver = { }; module_platform_driver(tegra30_ahub_driver); +void tegra30_ahub_set_cif(struct regmap *regmap, unsigned int reg, + struct tegra30_ahub_cif_conf *conf) +{ + unsigned int value; + + value = (conf->threshold << + TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) | + ((conf->audio_channels - 1) << + TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) | + ((conf->client_channels - 1) << + TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) | + (conf->audio_bits << + TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) | + (conf->client_bits << + TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) | + (conf->expand << + TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT) | + (conf->stereo_conv << + TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) | + (conf->replicate << + TEGRA30_AUDIOCIF_CTRL_REPLICATE_SHIFT) | + (conf->direction << + TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT) | + (conf->truncate << + TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT) | + (conf->mono_conv << + TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT); + + regmap_write(regmap, reg, value); +} +EXPORT_SYMBOL_GPL(tegra30_ahub_set_cif); + +void tegra124_ahub_set_cif(struct regmap *regmap, unsigned int reg, + struct tegra30_ahub_cif_conf *conf) +{ + unsigned int value; + + value = (conf->threshold << + TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) | + ((conf->audio_channels - 1) << + TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) | + ((conf->client_channels - 1) << + TEGRA124_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) | + (conf->audio_bits << + TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) | + (conf->client_bits << + TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) | + (conf->expand << + TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT) | + (conf->stereo_conv << + TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) | + (conf->replicate << + TEGRA30_AUDIOCIF_CTRL_REPLICATE_SHIFT) | + (conf->direction << + TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT) | + (conf->truncate << + TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT) | + (conf->mono_conv << + TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT); + + regmap_write(regmap, reg, value); +} +EXPORT_SYMBOL_GPL(tegra124_ahub_set_cif); + MODULE_AUTHOR("Stephen Warren "); MODULE_DESCRIPTION("Tegra30 AHUB driver"); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/tegra/tegra30_ahub.h b/sound/soc/tegra/tegra30_ahub.h index 09766cd..d67321d 100644 --- a/sound/soc/tegra/tegra30_ahub.h +++ b/sound/soc/tegra/tegra30_ahub.h @@ -25,16 +25,30 @@ #define TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US 0xf #define TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK (TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) +#define TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT 24 +#define TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US 0x3f +#define TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK (TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US << TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) + /* Channel count minus 1 */ #define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT 24 #define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US 7 #define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK (TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) /* Channel count minus 1 */ +#define TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT 20 +#define TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US 0xf +#define TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK (TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US << TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) + +/* Channel count minus 1 */ #define TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT 16 #define TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK_US 7 #define TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK (TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK_US << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) +/* Channel count minus 1 */ +#define TEGRA124_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT 16 +#define TEGRA124_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK_US 0xf +#define TEGRA124_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK (TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK_US << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) + #define TEGRA30_AUDIOCIF_BITS_4 0 #define TEGRA30_AUDIOCIF_BITS_8 1 #define TEGRA30_AUDIOCIF_BITS_12 2 @@ -86,7 +100,7 @@ #define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_CH1 (TEGRA30_AUDIOCIF_STEREO_CONV_CH1 << TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) #define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_AVG (TEGRA30_AUDIOCIF_STEREO_CONV_AVG << TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) -#define TEGRA30_AUDIOCIF_CTRL_REPLICATE 3 +#define TEGRA30_AUDIOCIF_CTRL_REPLICATE_SHIFT 3 #define TEGRA30_AUDIOCIF_DIRECTION_TX 0 #define TEGRA30_AUDIOCIF_DIRECTION_RX 1 @@ -468,8 +482,30 @@ extern int tegra30_ahub_set_rx_cif_source(enum tegra30_ahub_rxcif rxcif, enum tegra30_ahub_txcif txcif); extern int tegra30_ahub_unset_rx_cif_source(enum tegra30_ahub_rxcif rxcif); +struct tegra30_ahub_cif_conf { + unsigned int threshold; + unsigned int audio_channels; + unsigned int client_channels; + unsigned int audio_bits; + unsigned int client_bits; + unsigned int expand; + unsigned int stereo_conv; + unsigned int replicate; + unsigned int direction; + unsigned int truncate; + unsigned int mono_conv; +}; + +void tegra30_ahub_set_cif(struct regmap *regmap, unsigned int reg, + struct tegra30_ahub_cif_conf *conf); +void tegra124_ahub_set_cif(struct regmap *regmap, unsigned int reg, + struct tegra30_ahub_cif_conf *conf); + struct tegra30_ahub_soc_data { u32 clk_list_mask; + void (*set_audio_cif)(struct regmap *regmap, + unsigned int reg, + struct tegra30_ahub_cif_conf *conf); /* * FIXME: There are many more differences in HW, such as: * - More APBIF channels. diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c index 47565fd04..5f20b69 100644 --- a/sound/soc/tegra/tegra30_i2s.c +++ b/sound/soc/tegra/tegra30_i2s.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -179,6 +180,7 @@ static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream, struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); unsigned int mask, val, reg; int ret, sample_size, srate, i2sclock, bitcnt; + struct tegra30_ahub_cif_conf cif_conf; if (params_channels(params) != 2) return -EINVAL; @@ -217,21 +219,26 @@ static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream, regmap_write(i2s->regmap, TEGRA30_I2S_TIMING, val); - val = (0 << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) | - (1 << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) | - (1 << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) | - TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_16 | - TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_16; + cif_conf.threshold = 0; + cif_conf.audio_channels = 2; + cif_conf.client_channels = 2; + cif_conf.audio_bits = TEGRA30_AUDIOCIF_BITS_16; + cif_conf.client_bits = TEGRA30_AUDIOCIF_BITS_16; + cif_conf.expand = 0; + cif_conf.stereo_conv = 0; + cif_conf.replicate = 0; + cif_conf.truncate = 0; + cif_conf.mono_conv = 0; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - val |= TEGRA30_AUDIOCIF_CTRL_DIRECTION_RX; + cif_conf.direction = TEGRA30_AUDIOCIF_DIRECTION_RX; reg = TEGRA30_I2S_CIF_RX_CTRL; } else { - val |= TEGRA30_AUDIOCIF_CTRL_DIRECTION_TX; + cif_conf.direction = TEGRA30_AUDIOCIF_DIRECTION_TX; reg = TEGRA30_I2S_CIF_TX_CTRL; } - regmap_write(i2s->regmap, reg, val); + i2s->soc_data->set_audio_cif(i2s->regmap, reg, &cif_conf); val = (1 << TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_SHIFT) | (1 << TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_SHIFT); @@ -396,9 +403,24 @@ static const struct regmap_config tegra30_i2s_regmap_config = { .cache_type = REGCACHE_RBTREE, }; +static const struct tegra30_i2s_soc_data tegra30_i2s_config = { + .set_audio_cif = tegra30_ahub_set_cif, +}; + +static const struct tegra30_i2s_soc_data tegra124_i2s_config = { + .set_audio_cif = tegra124_ahub_set_cif, +}; + +static const struct of_device_id tegra30_i2s_of_match[] = { + { .compatible = "nvidia,tegra124-i2s", .data = &tegra124_i2s_config }, + { .compatible = "nvidia,tegra30-i2s", .data = &tegra30_i2s_config }, + {}, +}; + static int tegra30_i2s_platform_probe(struct platform_device *pdev) { struct tegra30_i2s *i2s; + const struct of_device_id *match; u32 cif_ids[2]; struct resource *mem, *memregion; void __iomem *regs; @@ -412,6 +434,14 @@ static int tegra30_i2s_platform_probe(struct platform_device *pdev) } dev_set_drvdata(&pdev->dev, i2s); + match = of_match_device(tegra30_i2s_of_match, &pdev->dev); + if (!match) { + dev_err(&pdev->dev, "Error: No device match found\n"); + ret = -ENODEV; + goto err; + } + i2s->soc_data = (struct tegra30_i2s_soc_data *)match->data; + i2s->dai = tegra30_i2s_dai_template; i2s->dai.name = dev_name(&pdev->dev); @@ -539,11 +569,6 @@ static int tegra30_i2s_resume(struct device *dev) } #endif -static const struct of_device_id tegra30_i2s_of_match[] = { - { .compatible = "nvidia,tegra30-i2s", }, - {}, -}; - static const struct dev_pm_ops tegra30_i2s_pm_ops = { SET_RUNTIME_PM_OPS(tegra30_i2s_runtime_suspend, tegra30_i2s_runtime_resume, NULL) diff --git a/sound/soc/tegra/tegra30_i2s.h b/sound/soc/tegra/tegra30_i2s.h index bea23af..4d0b0a3 100644 --- a/sound/soc/tegra/tegra30_i2s.h +++ b/sound/soc/tegra/tegra30_i2s.h @@ -225,7 +225,14 @@ #define TEGRA30_I2S_LCOEF_COEF_MASK_US 0xffff #define TEGRA30_I2S_LCOEF_COEF_MASK (TEGRA30_I2S_LCOEF_COEF_MASK_US << TEGRA30_I2S_LCOEF_COEF_SHIFT) +struct tegra30_i2s_soc_data { + void (*set_audio_cif)(struct regmap *regmap, + unsigned int reg, + struct tegra30_ahub_cif_conf *conf); +}; + struct tegra30_i2s { + const struct tegra30_i2s_soc_data *soc_data; struct snd_soc_dai_driver dai; int cif_id; struct clk *clk_i2s; diff --git a/sound/soc/tegra/tegra_asoc_utils.c b/sound/soc/tegra/tegra_asoc_utils.c index d173880..1be311c 100644 --- a/sound/soc/tegra/tegra_asoc_utils.c +++ b/sound/soc/tegra/tegra_asoc_utils.c @@ -182,6 +182,8 @@ int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA30; else if (of_machine_is_compatible("nvidia,tegra114")) data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA114; + else if (of_machine_is_compatible("nvidia,tegra124")) + data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA124; else { dev_err(data->dev, "SoC unknown to Tegra ASoC utils\n"); return -EINVAL; diff --git a/sound/soc/tegra/tegra_asoc_utils.h b/sound/soc/tegra/tegra_asoc_utils.h index 19fdcaf..9577121 100644 --- a/sound/soc/tegra/tegra_asoc_utils.h +++ b/sound/soc/tegra/tegra_asoc_utils.h @@ -30,6 +30,7 @@ enum tegra_asoc_utils_soc { TEGRA_ASOC_UTILS_SOC_TEGRA20, TEGRA_ASOC_UTILS_SOC_TEGRA30, TEGRA_ASOC_UTILS_SOC_TEGRA114, + TEGRA_ASOC_UTILS_SOC_TEGRA124, }; struct tegra_asoc_utils_data { -- cgit v0.10.2 From e33fabd365596178e72f62bb4b89f0aaad0509ad Mon Sep 17 00:00:00 2001 From: Anthony Olech Date: Fri, 11 Oct 2013 15:31:11 +0100 Subject: regmap: new API regmap_multi_reg_write() definition New API regmap_multi_reg_write() is defined that allows a set of reg,val pairs to be written to a I2C client device as one block transfer from the point of view of a single I2C master system. A simple demonstration implementation is included that just splits the block write request into a sequence of single register writes. The implementation will be modified later to support those I2C clients that implement the alternative non-standard MULTIWRITE block write mode so to achieve a single I2C transfer that will be atomic even in multiple I2C master systems. Signed-off-by: Anthony Olech Signed-off-by: David Dajun Chen Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7d689a1..c42ad69 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1439,6 +1439,47 @@ out: } EXPORT_SYMBOL_GPL(regmap_bulk_write); +/* + * regmap_multi_reg_write(): Write multiple registers to the device + * + * where the set of register are supplied in any order + * + * @map: Register map to write to + * @regs: Array of structures containing register,value to be written + * @num_regs: Number of registers to write + * + * This function is intended to be used for writing a large block of data + * atomically to the device in single transfer for those I2C client devices + * that implement this alternative block write mode. + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_multi_reg_write(struct regmap *map, struct reg_default *regs, + int num_regs) +{ + int ret = 0, i; + + for (i = 0; i < num_regs; i++) { + int reg = regs[i].reg; + if (reg % map->reg_stride) + return -EINVAL; + } + + map->lock(map->lock_arg); + + for (i = 0; i < num_regs; i++) { + ret = _regmap_write(map, regs[i].reg, regs[i].def); + if (ret != 0) + goto out; + } +out: + map->unlock(map->lock_arg); + + return ret; +} +EXPORT_SYMBOL_GPL(regmap_multi_reg_write); + /** * regmap_raw_write_async(): Write raw values to one or more registers * asynchronously diff --git a/include/linux/regmap.h b/include/linux/regmap.h index a10380b..4b933a3 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -378,6 +378,8 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, const void *val, size_t val_len); int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, size_t val_count); +int regmap_multi_reg_write(struct regmap *map, struct reg_default *regs, + int num_regs); int regmap_raw_write_async(struct regmap *map, unsigned int reg, const void *val, size_t val_len); int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val); -- cgit v0.10.2 From fc1b691d7651d9496e912de7e0fc73a5be3294af Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Oct 2013 16:57:29 +0300 Subject: perf buildid-cache: Add ability to add kcore to the cache kcore can be used to view the running kernel object code. However, kcore changes as modules are loaded and unloaded, and when the kernel decides to modify its own code. Consequently it is useful to create a copy of kcore at a particular time. Unlike vmlinux, kcore is not unique for a given build-id. And in addition, the kallsyms and modules files are also needed. The tool therefore creates a directory: ~/.debug/[kernel.kcore]// which contains: kcore, kallsyms and modules. Note that the copied kcore contains only code sections. See the kcore_copy() function for how that is determined. The tool will not make additional copies of kcore if there is already one with the same modules at the same addresses. Currently, perf tools will not look for kcore in the cache. That is addressed in another patch. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/525BF849.5030405@intel.com [ renamed 'index' to 'idx' to avoid shadowing string.h symbol in f12, use at least one member initializer when initializing a struct to zeros, also to fix the build on f12 ] Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Documentation/perf-buildid-cache.txt b/tools/perf/Documentation/perf-buildid-cache.txt index e9a8349..fd77d81 100644 --- a/tools/perf/Documentation/perf-buildid-cache.txt +++ b/tools/perf/Documentation/perf-buildid-cache.txt @@ -21,6 +21,19 @@ OPTIONS -a:: --add=:: Add specified file to the cache. +-k:: +--kcore:: + Add specified kcore file to the cache. For the current host that is + /proc/kcore which requires root permissions to read. Be aware that + running 'perf buildid-cache' as root may update root's build-id cache + not the user's. Use the -v option to see where the file is created. + Note that the copied file contains only code sections not the whole core + image. Note also that files "kallsyms" and "modules" must also be in the + same directory and are also copied. All 3 files are created with read + permissions for root only. kcore will not be added if there is already a + kcore in the cache (with the same build-id) that has the same modules at + the same addresses. Use the -v option to see if a copy of kcore is + actually made. -r:: --remove=:: Remove specified file from the cache. diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c index c96c8fa..8140b7b 100644 --- a/tools/perf/builtin-buildid-cache.c +++ b/tools/perf/builtin-buildid-cache.c @@ -6,6 +6,11 @@ * Copyright (C) 2010, Red Hat Inc. * Copyright (C) 2010, Arnaldo Carvalho de Melo */ +#include +#include +#include +#include +#include #include "builtin.h" #include "perf.h" #include "util/cache.h" @@ -17,6 +22,140 @@ #include "util/session.h" #include "util/symbol.h" +static int build_id_cache__kcore_buildid(const char *proc_dir, char *sbuildid) +{ + char root_dir[PATH_MAX]; + char notes[PATH_MAX]; + u8 build_id[BUILD_ID_SIZE]; + char *p; + + strlcpy(root_dir, proc_dir, sizeof(root_dir)); + + p = strrchr(root_dir, '/'); + if (!p) + return -1; + *p = '\0'; + + scnprintf(notes, sizeof(notes), "%s/sys/kernel/notes", root_dir); + + if (sysfs__read_build_id(notes, build_id, sizeof(build_id))) + return -1; + + build_id__sprintf(build_id, sizeof(build_id), sbuildid); + + return 0; +} + +static int build_id_cache__kcore_dir(char *dir, size_t sz) +{ + struct timeval tv; + struct tm tm; + char dt[32]; + + if (gettimeofday(&tv, NULL) || !localtime_r(&tv.tv_sec, &tm)) + return -1; + + if (!strftime(dt, sizeof(dt), "%Y%m%d%H%M%S", &tm)) + return -1; + + scnprintf(dir, sz, "%s%02u", dt, (unsigned)tv.tv_usec / 10000); + + return 0; +} + +static int build_id_cache__kcore_existing(const char *from_dir, char *to_dir, + size_t to_dir_sz) +{ + char from[PATH_MAX]; + char to[PATH_MAX]; + struct dirent *dent; + int ret = -1; + DIR *d; + + d = opendir(to_dir); + if (!d) + return -1; + + scnprintf(from, sizeof(from), "%s/modules", from_dir); + + while (1) { + dent = readdir(d); + if (!dent) + break; + if (dent->d_type != DT_DIR) + continue; + scnprintf(to, sizeof(to), "%s/%s/modules", to_dir, + dent->d_name); + if (!compare_proc_modules(from, to)) { + scnprintf(to, sizeof(to), "%s/%s", to_dir, + dent->d_name); + strlcpy(to_dir, to, to_dir_sz); + ret = 0; + break; + } + } + + closedir(d); + + return ret; +} + +static int build_id_cache__add_kcore(const char *filename, const char *debugdir) +{ + char dir[32], sbuildid[BUILD_ID_SIZE * 2 + 1]; + char from_dir[PATH_MAX], to_dir[PATH_MAX]; + char *p; + + strlcpy(from_dir, filename, sizeof(from_dir)); + + p = strrchr(from_dir, '/'); + if (!p || strcmp(p + 1, "kcore")) + return -1; + *p = '\0'; + + if (build_id_cache__kcore_buildid(from_dir, sbuildid)) + return -1; + + scnprintf(to_dir, sizeof(to_dir), "%s/[kernel.kcore]/%s", + debugdir, sbuildid); + + if (!build_id_cache__kcore_existing(from_dir, to_dir, sizeof(to_dir))) { + pr_debug("same kcore found in %s\n", to_dir); + return 0; + } + + if (build_id_cache__kcore_dir(dir, sizeof(dir))) + return -1; + + scnprintf(to_dir, sizeof(to_dir), "%s/[kernel.kcore]/%s/%s", + debugdir, sbuildid, dir); + + if (mkdir_p(to_dir, 0755)) + return -1; + + if (kcore_copy(from_dir, to_dir)) { + /* Remove YYYYmmddHHMMSShh directory */ + if (!rmdir(to_dir)) { + p = strrchr(to_dir, '/'); + if (p) + *p = '\0'; + /* Try to remove buildid directory */ + if (!rmdir(to_dir)) { + p = strrchr(to_dir, '/'); + if (p) + *p = '\0'; + /* Try to remove [kernel.kcore] directory */ + rmdir(to_dir); + } + } + return -1; + } + + pr_debug("kcore added to build-id cache directory %s\n", to_dir); + + return 0; +} + static int build_id_cache__add_file(const char *filename, const char *debugdir) { char sbuild_id[BUILD_ID_SIZE * 2 + 1]; @@ -130,11 +269,14 @@ int cmd_buildid_cache(int argc, const char **argv, char const *add_name_list_str = NULL, *remove_name_list_str = NULL, *missing_filename = NULL, - *update_name_list_str = NULL; + *update_name_list_str = NULL, + *kcore_filename; const struct option buildid_cache_options[] = { OPT_STRING('a', "add", &add_name_list_str, "file list", "file(s) to add"), + OPT_STRING('k', "kcore", &kcore_filename, + "file", "kcore file to add"), OPT_STRING('r', "remove", &remove_name_list_str, "file list", "file(s) to remove"), OPT_STRING('M', "missing", &missing_filename, "file", @@ -217,5 +359,9 @@ int cmd_buildid_cache(int argc, const char **argv, } } + if (kcore_filename && + build_id_cache__add_kcore(kcore_filename, debugdir)) + pr_warning("Couldn't add %s\n", kcore_filename); + return ret; } diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 499c71d..d6b8af3 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -1202,6 +1202,372 @@ static off_t kcore__write(struct kcore *kcore) return elf_update(kcore->elf, ELF_C_WRITE); } +struct phdr_data { + off_t offset; + u64 addr; + u64 len; +}; + +struct kcore_copy_info { + u64 stext; + u64 etext; + u64 first_symbol; + u64 last_symbol; + u64 first_module; + u64 last_module_symbol; + struct phdr_data kernel_map; + struct phdr_data modules_map; +}; + +static int kcore_copy__process_kallsyms(void *arg, const char *name, char type, + u64 start) +{ + struct kcore_copy_info *kci = arg; + + if (!symbol_type__is_a(type, MAP__FUNCTION)) + return 0; + + if (strchr(name, '[')) { + if (start > kci->last_module_symbol) + kci->last_module_symbol = start; + return 0; + } + + if (!kci->first_symbol || start < kci->first_symbol) + kci->first_symbol = start; + + if (!kci->last_symbol || start > kci->last_symbol) + kci->last_symbol = start; + + if (!strcmp(name, "_stext")) { + kci->stext = start; + return 0; + } + + if (!strcmp(name, "_etext")) { + kci->etext = start; + return 0; + } + + return 0; +} + +static int kcore_copy__parse_kallsyms(struct kcore_copy_info *kci, + const char *dir) +{ + char kallsyms_filename[PATH_MAX]; + + scnprintf(kallsyms_filename, PATH_MAX, "%s/kallsyms", dir); + + if (symbol__restricted_filename(kallsyms_filename, "/proc/kallsyms")) + return -1; + + if (kallsyms__parse(kallsyms_filename, kci, + kcore_copy__process_kallsyms) < 0) + return -1; + + return 0; +} + +static int kcore_copy__process_modules(void *arg, + const char *name __maybe_unused, + u64 start) +{ + struct kcore_copy_info *kci = arg; + + if (!kci->first_module || start < kci->first_module) + kci->first_module = start; + + return 0; +} + +static int kcore_copy__parse_modules(struct kcore_copy_info *kci, + const char *dir) +{ + char modules_filename[PATH_MAX]; + + scnprintf(modules_filename, PATH_MAX, "%s/modules", dir); + + if (symbol__restricted_filename(modules_filename, "/proc/modules")) + return -1; + + if (modules__parse(modules_filename, kci, + kcore_copy__process_modules) < 0) + return -1; + + return 0; +} + +static void kcore_copy__map(struct phdr_data *p, u64 start, u64 end, u64 pgoff, + u64 s, u64 e) +{ + if (p->addr || s < start || s >= end) + return; + + p->addr = s; + p->offset = (s - start) + pgoff; + p->len = e < end ? e - s : end - s; +} + +static int kcore_copy__read_map(u64 start, u64 len, u64 pgoff, void *data) +{ + struct kcore_copy_info *kci = data; + u64 end = start + len; + + kcore_copy__map(&kci->kernel_map, start, end, pgoff, kci->stext, + kci->etext); + + kcore_copy__map(&kci->modules_map, start, end, pgoff, kci->first_module, + kci->last_module_symbol); + + return 0; +} + +static int kcore_copy__read_maps(struct kcore_copy_info *kci, Elf *elf) +{ + if (elf_read_maps(elf, true, kcore_copy__read_map, kci) < 0) + return -1; + + return 0; +} + +static int kcore_copy__calc_maps(struct kcore_copy_info *kci, const char *dir, + Elf *elf) +{ + if (kcore_copy__parse_kallsyms(kci, dir)) + return -1; + + if (kcore_copy__parse_modules(kci, dir)) + return -1; + + if (kci->stext) + kci->stext = round_down(kci->stext, page_size); + else + kci->stext = round_down(kci->first_symbol, page_size); + + if (kci->etext) { + kci->etext = round_up(kci->etext, page_size); + } else if (kci->last_symbol) { + kci->etext = round_up(kci->last_symbol, page_size); + kci->etext += page_size; + } + + kci->first_module = round_down(kci->first_module, page_size); + + if (kci->last_module_symbol) { + kci->last_module_symbol = round_up(kci->last_module_symbol, + page_size); + kci->last_module_symbol += page_size; + } + + if (!kci->stext || !kci->etext) + return -1; + + if (kci->first_module && !kci->last_module_symbol) + return -1; + + return kcore_copy__read_maps(kci, elf); +} + +static int kcore_copy__copy_file(const char *from_dir, const char *to_dir, + const char *name) +{ + char from_filename[PATH_MAX]; + char to_filename[PATH_MAX]; + + scnprintf(from_filename, PATH_MAX, "%s/%s", from_dir, name); + scnprintf(to_filename, PATH_MAX, "%s/%s", to_dir, name); + + return copyfile_mode(from_filename, to_filename, 0400); +} + +static int kcore_copy__unlink(const char *dir, const char *name) +{ + char filename[PATH_MAX]; + + scnprintf(filename, PATH_MAX, "%s/%s", dir, name); + + return unlink(filename); +} + +static int kcore_copy__compare_fds(int from, int to) +{ + char *buf_from; + char *buf_to; + ssize_t ret; + size_t len; + int err = -1; + + buf_from = malloc(page_size); + buf_to = malloc(page_size); + if (!buf_from || !buf_to) + goto out; + + while (1) { + /* Use read because mmap won't work on proc files */ + ret = read(from, buf_from, page_size); + if (ret < 0) + goto out; + + if (!ret) + break; + + len = ret; + + if (readn(to, buf_to, len) != (int)len) + goto out; + + if (memcmp(buf_from, buf_to, len)) + goto out; + } + + err = 0; +out: + free(buf_to); + free(buf_from); + return err; +} + +static int kcore_copy__compare_files(const char *from_filename, + const char *to_filename) +{ + int from, to, err = -1; + + from = open(from_filename, O_RDONLY); + if (from < 0) + return -1; + + to = open(to_filename, O_RDONLY); + if (to < 0) + goto out_close_from; + + err = kcore_copy__compare_fds(from, to); + + close(to); +out_close_from: + close(from); + return err; +} + +static int kcore_copy__compare_file(const char *from_dir, const char *to_dir, + const char *name) +{ + char from_filename[PATH_MAX]; + char to_filename[PATH_MAX]; + + scnprintf(from_filename, PATH_MAX, "%s/%s", from_dir, name); + scnprintf(to_filename, PATH_MAX, "%s/%s", to_dir, name); + + return kcore_copy__compare_files(from_filename, to_filename); +} + +/** + * kcore_copy - copy kallsyms, modules and kcore from one directory to another. + * @from_dir: from directory + * @to_dir: to directory + * + * This function copies kallsyms, modules and kcore files from one directory to + * another. kallsyms and modules are copied entirely. Only code segments are + * copied from kcore. It is assumed that two segments suffice: one for the + * kernel proper and one for all the modules. The code segments are determined + * from kallsyms and modules files. The kernel map starts at _stext or the + * lowest function symbol, and ends at _etext or the highest function symbol. + * The module map starts at the lowest module address and ends at the highest + * module symbol. Start addresses are rounded down to the nearest page. End + * addresses are rounded up to the nearest page. An extra page is added to the + * highest kernel symbol and highest module symbol to, hopefully, encompass that + * symbol too. Because it contains only code sections, the resulting kcore is + * unusual. One significant peculiarity is that the mapping (start -> pgoff) + * is not the same for the kernel map and the modules map. That happens because + * the data is copied adjacently whereas the original kcore has gaps. Finally, + * kallsyms and modules files are compared with their copies to check that + * modules have not been loaded or unloaded while the copies were taking place. + * + * Return: %0 on success, %-1 on failure. + */ +int kcore_copy(const char *from_dir, const char *to_dir) +{ + struct kcore kcore; + struct kcore extract; + size_t count = 2; + int idx = 0, err = -1; + off_t offset = page_size, sz, modules_offset = 0; + struct kcore_copy_info kci = { .stext = 0, }; + char kcore_filename[PATH_MAX]; + char extract_filename[PATH_MAX]; + + if (kcore_copy__copy_file(from_dir, to_dir, "kallsyms")) + return -1; + + if (kcore_copy__copy_file(from_dir, to_dir, "modules")) + goto out_unlink_kallsyms; + + scnprintf(kcore_filename, PATH_MAX, "%s/kcore", from_dir); + scnprintf(extract_filename, PATH_MAX, "%s/kcore", to_dir); + + if (kcore__open(&kcore, kcore_filename)) + goto out_unlink_modules; + + if (kcore_copy__calc_maps(&kci, from_dir, kcore.elf)) + goto out_kcore_close; + + if (kcore__init(&extract, extract_filename, kcore.elfclass, false)) + goto out_kcore_close; + + if (!kci.modules_map.addr) + count -= 1; + + if (kcore__copy_hdr(&kcore, &extract, count)) + goto out_extract_close; + + if (kcore__add_phdr(&extract, idx++, offset, kci.kernel_map.addr, + kci.kernel_map.len)) + goto out_extract_close; + + if (kci.modules_map.addr) { + modules_offset = offset + kci.kernel_map.len; + if (kcore__add_phdr(&extract, idx, modules_offset, + kci.modules_map.addr, kci.modules_map.len)) + goto out_extract_close; + } + + sz = kcore__write(&extract); + if (sz < 0 || sz > offset) + goto out_extract_close; + + if (copy_bytes(kcore.fd, kci.kernel_map.offset, extract.fd, offset, + kci.kernel_map.len)) + goto out_extract_close; + + if (modules_offset && copy_bytes(kcore.fd, kci.modules_map.offset, + extract.fd, modules_offset, + kci.modules_map.len)) + goto out_extract_close; + + if (kcore_copy__compare_file(from_dir, to_dir, "modules")) + goto out_extract_close; + + if (kcore_copy__compare_file(from_dir, to_dir, "kallsyms")) + goto out_extract_close; + + err = 0; + +out_extract_close: + kcore__close(&extract); + if (err) + unlink(extract_filename); +out_kcore_close: + kcore__close(&kcore); +out_unlink_modules: + if (err) + kcore_copy__unlink(to_dir, "modules"); +out_unlink_kallsyms: + if (err) + kcore_copy__unlink(to_dir, "kallsyms"); + + return err; +} + int kcore_extract__create(struct kcore_extract *kce) { struct kcore kcore; diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index 928556d..2d2dd05 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c @@ -317,6 +317,12 @@ void kcore_extract__delete(struct kcore_extract *kce __maybe_unused) { } +int kcore_copy(const char *from_dir __maybe_unused, + const char *to_dir __maybe_unused) +{ + return -1; +} + void symbol__elf_init(void) { } diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 76a9e93..b66c1ee 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -893,6 +893,47 @@ static int read_proc_modules(const char *filename, struct rb_root *modules) return 0; } +int compare_proc_modules(const char *from, const char *to) +{ + struct rb_root from_modules = RB_ROOT; + struct rb_root to_modules = RB_ROOT; + struct rb_node *from_node, *to_node; + struct module_info *from_m, *to_m; + int ret = -1; + + if (read_proc_modules(from, &from_modules)) + return -1; + + if (read_proc_modules(to, &to_modules)) + goto out_delete_from; + + from_node = rb_first(&from_modules); + to_node = rb_first(&to_modules); + while (from_node) { + if (!to_node) + break; + + from_m = rb_entry(from_node, struct module_info, rb_node); + to_m = rb_entry(to_node, struct module_info, rb_node); + + if (from_m->start != to_m->start || + strcmp(from_m->name, to_m->name)) + break; + + from_node = rb_next(from_node); + to_node = rb_next(to_node); + } + + if (!from_node && !to_node) + ret = 0; + + delete_modules(&to_modules); +out_delete_from: + delete_modules(&from_modules); + + return ret; +} + static int do_validate_kcore_modules(const char *filename, struct map *map, struct map_groups *kmaps) { diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index fb107e1..07de8fe 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -270,4 +270,7 @@ struct kcore_extract { int kcore_extract__create(struct kcore_extract *kce); void kcore_extract__delete(struct kcore_extract *kce); +int kcore_copy(const char *from_dir, const char *to_dir); +int compare_proc_modules(const char *from, const char *to); + #endif /* __PERF_SYMBOL */ -- cgit v0.10.2 From 1d5077bdd9a10c4297cded139989bb9ee2998a6c Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Oct 2013 13:43:44 +0300 Subject: perf annotate: Another fix for annotate_browser__callq() The target address is provided by objdump and is not necessary a memory address. Add a helper to get the correct address. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1381747424-3557-8-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 57d3a86..f0697a3 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -445,14 +445,17 @@ static bool annotate_browser__callq(struct annotate_browser *browser, struct annotation *notes; struct addr_map_symbol target = { .map = ms->map, - .addr = dl->ops.target.addr, + .addr = map__objdump_2mem(ms->map, dl->ops.target.addr), }; char title[SYM_TITLE_MAX_SIZE]; if (!ins__is_call(dl->ins)) return false; - if (map_groups__find_ams(&target, NULL)) { + if (map_groups__find_ams(&target, NULL) || + map__rip_2objdump(target.map, target.map->map_ip(target.map, + target.addr)) != + dl->ops.target.addr) { ui_helpline__puts("The called function was not found."); return true; } diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 9dea404..ef5bc91 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -252,10 +252,16 @@ size_t map__fprintf_dsoname(struct map *map, FILE *fp) return fprintf(fp, "%s", dsoname); } -/* +/** + * map__rip_2objdump - convert symbol start address to objdump address. + * @map: memory map + * @rip: symbol start address + * * objdump wants/reports absolute IPs for ET_EXEC, and RIPs for ET_DYN. * map->dso->adjust_symbols==1 for ET_EXEC-like cases except ET_REL which is * relative to section start. + * + * Return: Address suitable for passing to "objdump --start-address=" */ u64 map__rip_2objdump(struct map *map, u64 rip) { @@ -268,6 +274,29 @@ u64 map__rip_2objdump(struct map *map, u64 rip) return map->unmap_ip(map, rip); } +/** + * map__objdump_2mem - convert objdump address to a memory address. + * @map: memory map + * @ip: objdump address + * + * Closely related to map__rip_2objdump(), this function takes an address from + * objdump and converts it to a memory address. Note this assumes that @map + * contains the address. To be sure the result is valid, check it forwards + * e.g. map__rip_2objdump(map->map_ip(map, map__objdump_2mem(map, ip))) == ip + * + * Return: Memory address. + */ +u64 map__objdump_2mem(struct map *map, u64 ip) +{ + if (!map->dso->adjust_symbols) + return map->unmap_ip(map, ip); + + if (map->dso->rel) + return map->unmap_ip(map, ip + map->pgoff); + + return ip; +} + void map_groups__init(struct map_groups *mg) { int i; diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 0359b4a..e4e259c 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -84,6 +84,9 @@ static inline u64 identity__map_ip(struct map *map __maybe_unused, u64 ip) /* rip/ip <-> addr suitable for passing to `objdump --start-address=` */ u64 map__rip_2objdump(struct map *map, u64 rip); +/* objdump address -> memory address */ +u64 map__objdump_2mem(struct map *map, u64 ip); + struct symbol; typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); -- cgit v0.10.2 From d4f74eb89199dc7bde5579783e9188841e1271e3 Mon Sep 17 00:00:00 2001 From: Chenggang Qin Date: Fri, 11 Oct 2013 08:27:59 +0800 Subject: perf symbols: Fix a memory leak due to symbol__delete not being used In function symbols__fixup_duplicate(), while duplicated symbols are found, only the rb_node is removed from the tree. The symbol structures themself are ignored. Then, these memory areas are lost. Signed-off-by: Chenggang Qin Acked-by: Namhyung Kim Cc: Andrew Morton Cc: Arjan van de Ven Cc: David Ahern Cc: Ingo Molnar Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Wu Fengguang Cc: Yanmin Zhang Link: http://lkml.kernel.org/r/1381451279-4109-3-git-send-email-chenggang.qin@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index b66c1ee..c0c3696 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -160,10 +160,12 @@ again: if (choose_best_symbol(curr, next) == SYMBOL_A) { rb_erase(&next->rb_node, symbols); + symbol__delete(next); goto again; } else { nd = rb_next(&curr->rb_node); rb_erase(&curr->rb_node, symbols); + symbol__delete(curr); } } } -- cgit v0.10.2 From 784f3390f9bd900adfb3b0373615e105a0d9749a Mon Sep 17 00:00:00 2001 From: Chenggang Qin Date: Fri, 11 Oct 2013 08:27:57 +0800 Subject: perf symbols: Fix a mmap and munmap mismatched bug In function filename__read_debuglink(), while the ELF file is opend and mmapped in elf_begin(), but if this file is considered to not be usable during the following code, we will goto the close(fd) directly. The elf_end() is skipped. So, the mmaped ELF file cannot be munmapped. The mmapped areas exist during the life of perf. This is a memory leak. This patch fixed this bug. Reviewed-by: Namhyung Kim Signed-off-by: Chenggang Qin Cc: Andrew Morton Cc: Arjan van de Ven Cc: Chenggang Qin Cc: David Ahern Cc: Ingo Molnar Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Wu Fengguang Cc: Yanmin Zhang Link: http://lkml.kernel.org/r/1381451279-4109-1-git-send-email-chenggang.qin@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index d6b8af3..eed0b96 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -487,27 +487,27 @@ int filename__read_debuglink(const char *filename, char *debuglink, ek = elf_kind(elf); if (ek != ELF_K_ELF) - goto out_close; + goto out_elf_end; if (gelf_getehdr(elf, &ehdr) == NULL) { pr_err("%s: cannot get elf header.\n", __func__); - goto out_close; + goto out_elf_end; } sec = elf_section_by_name(elf, &ehdr, &shdr, ".gnu_debuglink", NULL); if (sec == NULL) - goto out_close; + goto out_elf_end; data = elf_getdata(sec, NULL); if (data == NULL) - goto out_close; + goto out_elf_end; /* the start of this section is a zero-terminated string */ strncpy(debuglink, data->d_buf, size); +out_elf_end: elf_end(elf); - out_close: close(fd); out: -- cgit v0.10.2 From 9f1614aae59033b79941220f65ad36f5fe54a579 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Fri, 11 Oct 2013 12:11:02 +0200 Subject: ASoC: snd_soc_dai_ops trigger function description Add a comment to the trigger function in snd_soc_dai_ops struct about possible command sequences. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index ae9a227..0f2e5da 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -166,6 +166,13 @@ struct snd_soc_dai_ops { struct snd_soc_dai *); int (*prepare)(struct snd_pcm_substream *, struct snd_soc_dai *); + /* + * NOTE: Commands passed to the trigger function are not necessarily + * compatible with the current state of the dai. For example this + * sequence of commands is possible: START STOP STOP. + * So do not unconditionally use refcounting functions in the trigger + * function, e.g. clk_enable/disable. + */ int (*trigger)(struct snd_pcm_substream *, int, struct snd_soc_dai *); int (*bespoke_trigger)(struct snd_pcm_substream *, int, -- cgit v0.10.2 From 88cf632a135188db35b4db412a06b155fa444eb1 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Fri, 11 Oct 2013 12:11:03 +0200 Subject: ASoC: mxs-saif: Store saif state Trigger commands may be passed multiple times. To avoid errors with clk_enable/disable, store the saif state and return if saif is already running/stopped. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index b56b8a0..c8ead01 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -503,6 +503,9 @@ static int mxs_saif_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (saif->state == MXS_SAIF_STATE_RUNNING) + return 0; + dev_dbg(cpu_dai->dev, "start\n"); clk_enable(master_saif->clk); @@ -543,6 +546,7 @@ static int mxs_saif_trigger(struct snd_pcm_substream *substream, int cmd, } master_saif->ongoing = 1; + saif->state = MXS_SAIF_STATE_RUNNING; dev_dbg(saif->dev, "CTRL 0x%x STAT 0x%x\n", __raw_readl(saif->base + SAIF_CTRL), @@ -555,6 +559,9 @@ static int mxs_saif_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (saif->state == MXS_SAIF_STATE_STOPPED) + return 0; + dev_dbg(cpu_dai->dev, "stop\n"); /* wait a while for the current sample to complete */ @@ -575,6 +582,7 @@ static int mxs_saif_trigger(struct snd_pcm_substream *substream, int cmd, } master_saif->ongoing = 0; + saif->state = MXS_SAIF_STATE_STOPPED; break; default: diff --git a/sound/soc/mxs/mxs-saif.h b/sound/soc/mxs/mxs-saif.h index 53eaa4b..fbaf7ba 100644 --- a/sound/soc/mxs/mxs-saif.h +++ b/sound/soc/mxs/mxs-saif.h @@ -124,6 +124,11 @@ struct mxs_saif { u32 fifo_underrun; u32 fifo_overrun; + + enum { + MXS_SAIF_STATE_STOPPED, + MXS_SAIF_STATE_RUNNING, + } state; }; extern int mxs_saif_put_mclk(unsigned int saif_id); -- cgit v0.10.2 From 863ebddec85c5ce2fb2e7742e8834a3bd69a2512 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Fri, 11 Oct 2013 12:11:04 +0200 Subject: ASoC: mxs-saif: Handle errors in trigger function Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index c8ead01..fc3d89b 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -494,6 +494,7 @@ static int mxs_saif_trigger(struct snd_pcm_substream *substream, int cmd, struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); struct mxs_saif *master_saif; u32 delay; + int ret; master_saif = mxs_saif_get_master(saif); if (!master_saif) @@ -508,21 +509,32 @@ static int mxs_saif_trigger(struct snd_pcm_substream *substream, int cmd, dev_dbg(cpu_dai->dev, "start\n"); - clk_enable(master_saif->clk); - if (!master_saif->mclk_in_use) - __raw_writel(BM_SAIF_CTRL_RUN, - master_saif->base + SAIF_CTRL + MXS_SET_ADDR); + ret = clk_enable(master_saif->clk); + if (ret) { + dev_err(saif->dev, "Failed to enable master clock\n"); + return ret; + } /* * If the saif's master is not himself, we also need to enable * itself clk for its internal basic logic to work. */ if (saif != master_saif) { - clk_enable(saif->clk); + ret = clk_enable(saif->clk); + if (ret) { + dev_err(saif->dev, "Failed to enable master clock\n"); + clk_disable(master_saif->clk); + return ret; + } + __raw_writel(BM_SAIF_CTRL_RUN, saif->base + SAIF_CTRL + MXS_SET_ADDR); } + if (!master_saif->mclk_in_use) + __raw_writel(BM_SAIF_CTRL_RUN, + master_saif->base + SAIF_CTRL + MXS_SET_ADDR); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { /* * write data to saif data register to trigger -- cgit v0.10.2 From 2c067509a1e3540613ef597c5dc6bd1f47d539cb Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Fri, 11 Oct 2013 18:29:58 +0900 Subject: spi: pl022: Use dev_info() instead of printk() Change raw printk() call to dev_info() to provide a better message to userspace so it can properly identify the device. Signed-off-by: Jingoo Han Acked-by: Linus Walleij Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index c13d523..6e77c938a 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -2173,8 +2173,8 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) status = -ENOMEM; goto err_no_ioremap; } - printk(KERN_INFO "pl022: mapped registers from %pa to %p\n", - &adev->res.start, pl022->virtbase); + dev_info(&adev->dev, "mapped registers from %pa to %p\n", + &adev->res.start, pl022->virtbase); pl022->clk = devm_clk_get(&adev->dev, NULL); if (IS_ERR(pl022->clk)) { -- cgit v0.10.2 From e0045ebf3be4a7461a42b7f87c1c094ac7e4db78 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Fri, 11 Oct 2013 18:31:41 +0900 Subject: spi: dw-pci: Use dev_info() instead of printk() Change raw printk() call to dev_info() to provide a better message to userspace so it can properly identify the device. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-dw-pci.c b/drivers/spi/spi-dw-pci.c index 6a70862..66fa995 100644 --- a/drivers/spi/spi-dw-pci.c +++ b/drivers/spi/spi-dw-pci.c @@ -40,7 +40,7 @@ static int spi_pci_probe(struct pci_dev *pdev, int pci_bar = 0; int ret; - printk(KERN_INFO "DW: found PCI SPI controller(ID: %04x:%04x)\n", + dev_info(&pdev->dev, "found PCI SPI controller(ID: %04x:%04x)\n", pdev->vendor, pdev->device); ret = pci_enable_device(pdev); -- cgit v0.10.2 From 5fe5f05e2202414a8b9015e9aedf19e08f6fe832 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 14 Oct 2013 10:31:51 +0900 Subject: spi: Fix checkpatch issue Fix the following checkpatch error and warnings. ERROR: space required after that ',' (ctx:VxV) WARNING: quoted string split across lines WARNING: max() should probably be max_t(int, nb, master->num_chipselect) WARNING: sizeof *spi should be sizeof(*spi) WARNING: sizeof *master should be sizeof(*master) WARNING: sizeof x should be sizeof(x) Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 7f8057f..956b26c 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -325,7 +325,7 @@ struct spi_device *spi_alloc_device(struct spi_master *master) if (!spi_master_get(master)) return NULL; - spi = kzalloc(sizeof *spi, GFP_KERNEL); + spi = kzalloc(sizeof(*spi), GFP_KERNEL); if (!spi) { dev_err(dev, "cannot alloc spi_device\n"); spi_master_put(master); @@ -1093,7 +1093,7 @@ struct spi_master *spi_alloc_master(struct device *dev, unsigned size) if (!dev) return NULL; - master = kzalloc(size + sizeof *master, GFP_KERNEL); + master = kzalloc(size + sizeof(*master), GFP_KERNEL); if (!master) return NULL; @@ -1118,7 +1118,7 @@ static int of_spi_register_master(struct spi_master *master) return 0; nb = of_gpio_named_count(np, "cs-gpios"); - master->num_chipselect = max(nb, (int)master->num_chipselect); + master->num_chipselect = max_t(int, nb, master->num_chipselect); /* Return error only for an incorrectly formed cs-gpios property */ if (nb == 0 || nb == -ENOENT) @@ -1398,8 +1398,7 @@ int spi_setup(struct spi_device *spi) if (spi->master->setup) status = spi->master->setup(spi); - dev_dbg(&spi->dev, "setup mode %d, %s%s%s%s" - "%u bits/w, %u Hz max --> %d\n", + dev_dbg(&spi->dev, "setup mode %d, %s%s%s%s%u bits/w, %u Hz max --> %d\n", (int) (spi->mode & (SPI_CPOL | SPI_CPHA)), (spi->mode & SPI_CS_HIGH) ? "cs_high, " : "", (spi->mode & SPI_LSB_FIRST) ? "lsb, " : "", @@ -1758,7 +1757,7 @@ int spi_bus_unlock(struct spi_master *master) EXPORT_SYMBOL_GPL(spi_bus_unlock); /* portable code must never pass more than 32 bytes */ -#define SPI_BUFSIZ max(32,SMP_CACHE_BYTES) +#define SPI_BUFSIZ max(32, SMP_CACHE_BYTES) static u8 *buf; @@ -1807,7 +1806,7 @@ int spi_write_then_read(struct spi_device *spi, } spi_message_init(&message); - memset(x, 0, sizeof x); + memset(x, 0, sizeof(x)); if (n_tx) { x[0].len = n_tx; spi_message_add_tail(&x[0], &message); -- cgit v0.10.2 From 01b9e0418689951c6ae4ba3b221bc42f49eb6407 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 14 Oct 2013 10:32:48 +0900 Subject: spi: atmel: Fix checkpatch issue Fix the following checkpatch warning. WARNING: quoted string split across lines Signed-off-by: Jingoo Han Acked-by: Nicolas Ferre Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 6478a97..a828222 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -1401,8 +1401,7 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg) asd = spi->controller_state; bits = (asd->csr >> 4) & 0xf; if (bits != xfer->bits_per_word - 8) { - dev_dbg(&spi->dev, "you can't yet change " - "bits_per_word in transfers\n"); + dev_dbg(&spi->dev, "you can't yet change bits_per_word in transfers\n"); return -ENOPROTOOPT; } } -- cgit v0.10.2 From cff93c58f7a99b3bf2fc3b343a1b6ca0546120da Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 14 Oct 2013 10:33:38 +0900 Subject: spi: bitbang: Fix checkpatch issue Fix the following checkpatch warnings WARNING: sizeof *cs should be sizeof(*cs) WARNING: please, no space before tabs WARNING: space prohibited between function name and open parenthesis '(' WARNING: line over 80 characters Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c index 0056623..bd222f6 100644 --- a/drivers/spi/spi-bitbang.c +++ b/drivers/spi/spi-bitbang.c @@ -191,7 +191,7 @@ int spi_bitbang_setup(struct spi_device *spi) bitbang = spi_master_get_devdata(spi->master); if (!cs) { - cs = kzalloc(sizeof *cs, GFP_KERNEL); + cs = kzalloc(sizeof(*cs), GFP_KERNEL); if (!cs) return -ENOMEM; spi->controller_state = cs; @@ -258,7 +258,7 @@ static int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t) static int spi_bitbang_prepare_hardware(struct spi_master *spi) { - struct spi_bitbang *bitbang; + struct spi_bitbang *bitbang; unsigned long flags; bitbang = spi_master_get_devdata(spi); @@ -273,7 +273,7 @@ static int spi_bitbang_prepare_hardware(struct spi_master *spi) static int spi_bitbang_transfer_one(struct spi_master *master, struct spi_message *m) { - struct spi_bitbang *bitbang; + struct spi_bitbang *bitbang; unsigned nsecs; struct spi_transfer *t = NULL; unsigned cs_change; @@ -292,7 +292,7 @@ static int spi_bitbang_transfer_one(struct spi_master *master, cs_change = 1; status = 0; - list_for_each_entry (t, &m->transfers, transfer_list) { + list_for_each_entry(t, &m->transfers, transfer_list) { /* override speed or wordsize? */ if (t->speed_hz || t->bits_per_word) @@ -349,7 +349,8 @@ static int spi_bitbang_transfer_one(struct spi_master *master, if (t->delay_usecs) udelay(t->delay_usecs); - if (cs_change && !list_is_last(&t->transfer_list, &m->transfers)) { + if (cs_change && + !list_is_last(&t->transfer_list, &m->transfers)) { /* sometimes a short mid-message deselect of the chip * may be needed to terminate a mode or command */ @@ -378,7 +379,7 @@ static int spi_bitbang_transfer_one(struct spi_master *master, static int spi_bitbang_unprepare_hardware(struct spi_master *spi) { - struct spi_bitbang *bitbang; + struct spi_bitbang *bitbang; unsigned long flags; bitbang = spi_master_get_devdata(spi); -- cgit v0.10.2 From cd1b9dd0220d3c126b3b61c1f96f0832fc21fc61 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 10 Oct 2013 22:25:23 +0100 Subject: regmap: spi: Handle async writes of only one buffer If the value is zero then assume it has been included in the register data and don't send anything, minimising the number of interactions with the hardware. Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c index 4c506bd..37f12ae 100644 --- a/drivers/base/regmap/regmap-spi.c +++ b/drivers/base/regmap/regmap-spi.c @@ -73,7 +73,8 @@ static int regmap_spi_async_write(void *context, spi_message_init(&async->m); spi_message_add_tail(&async->t[0], &async->m); - spi_message_add_tail(&async->t[1], &async->m); + if (val) + spi_message_add_tail(&async->t[1], &async->m); async->m.complete = regmap_spi_complete; async->m.context = async; -- cgit v0.10.2 From 04c50ccf0dab02923ef888a4839bfbd00de03181 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 10 Oct 2013 22:38:29 +0100 Subject: regmap: Only send a single buffer for async I/O if writing one register Extend the interface for async I/O by allowing the value buffer to be omitted and sending the value as part of the register buffer, minimising the number of separate hardware operations required. Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index a975a60..d0ce2fe 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1157,18 +1157,23 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, /* If the caller supplied the value we can use it safely. */ memcpy(async->work_buf, map->work_buf, map->format.pad_bytes + map->format.reg_bytes + map->format.val_bytes); - if (val == work_val) - val = async->work_buf + map->format.pad_bytes + - map->format.reg_bytes; spin_lock_irqsave(&map->async_lock, flags); list_add_tail(&async->list, &map->async_list); spin_unlock_irqrestore(&map->async_lock, flags); - ret = map->bus->async_write(map->bus_context, async->work_buf, - map->format.reg_bytes + - map->format.pad_bytes, - val, val_len, async); + if (val != work_val) + ret = map->bus->async_write(map->bus_context, + async->work_buf, + map->format.reg_bytes + + map->format.pad_bytes, + val, val_len, async); + else + ret = map->bus->async_write(map->bus_context, + async->work_buf, + map->format.reg_bytes + + map->format.pad_bytes + + val_len, NULL, 0, async); if (ret != 0) { dev_err(map->dev, "Failed to schedule write: %d\n", -- cgit v0.10.2 From 6650b181cc0b0c4218ebff9b0c368a2e56287907 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 14 Oct 2013 18:25:12 -0300 Subject: perf scripting perl: Fix build error on Fedora 12 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cast __u64 to u64 to silence this warning on older distros, such as Fedora 12: CC /tmp/build/perf/util/scripting-engines/trace-event-perl.o cc1: warnings being treated as errors util/scripting-engines/trace-event-perl.c: In function ‘perl_process_tracepoint’: util/scripting-engines/trace-event-perl.c:285: error: format ‘%lu’ expects type ‘long unsigned int’, but argument 2 has type ‘__u64’ make[1]: *** [/tmp/build/perf/util/scripting-engines/trace-event-perl.o] Error 1 make: *** [install] Error 2 make: Leaving directory `/home/acme/git/linux/tools/perf' [acme@fedora12 linux]$ Reported-by: Waiman Long Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi Cc: Waiman Long Link: http://lkml.kernel.org/n/tip-nlxofdqcdjfm0w9o6bgq4kqv@git.kernel.org Link: http://lkml.kernel.org/r/1381265120-58532-1-git-send-email-Waiman.Long@hp.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index a85e4ae..c0c9795 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c @@ -282,7 +282,7 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused, event = find_cache_event(evsel); if (!event) - die("ug! no event found for type %" PRIu64, evsel->attr.config); + die("ug! no event found for type %" PRIu64, (u64)evsel->attr.config); pid = raw_field_value(event, "common_pid", data); -- cgit v0.10.2 From ac8ed236a9765c8b0689ff97eaca071211d9e22b Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 14 Oct 2013 10:34:24 +0900 Subject: spi: butterfly: Fix checkpatch issue Fix the following checkpatch errors and warnings. ERROR: space required before the open brace '{' ERROR: space required after that close brace '}' ERROR: space required before the open parenthesis '(' ERROR: do not use C99 // comments WARNING: sizeof *pp should be sizeof(*pp) Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-butterfly.c b/drivers/spi/spi-butterfly.c index 5ed08e5..75b1860 100644 --- a/drivers/spi/spi-butterfly.c +++ b/drivers/spi/spi-butterfly.c @@ -147,8 +147,8 @@ static void butterfly_chipselect(struct spi_device *spi, int value) /* we only needed to implement one mode here, and choose SPI_MODE_0 */ -#define spidelay(X) do{}while(0) -//#define spidelay ndelay +#define spidelay(X) do { } while (0) +/* #define spidelay ndelay */ #include "spi-bitbang-txrx.h" @@ -171,15 +171,15 @@ static struct mtd_partition partitions[] = { { /* sector 0 = 8 pages * 264 bytes/page (1 block) * sector 1 = 248 pages * 264 bytes/page */ - .name = "bookkeeping", // 66 KB + .name = "bookkeeping", /* 66 KB */ .offset = 0, .size = (8 + 248) * 264, -// .mask_flags = MTD_WRITEABLE, + /* .mask_flags = MTD_WRITEABLE, */ }, { /* sector 2 = 256 pages * 264 bytes/page * sectors 3-5 = 512 pages * 264 bytes/page */ - .name = "filesystem", // 462 KB + .name = "filesystem", /* 462 KB */ .offset = MTDPART_OFS_APPEND, .size = MTDPART_SIZ_FULL, } }; @@ -209,7 +209,7 @@ static void butterfly_attach(struct parport *p) * and no way to be selective about what it binds to. */ - master = spi_alloc_master(dev, sizeof *pp); + master = spi_alloc_master(dev, sizeof(*pp)); if (!master) { status = -ENOMEM; goto done; @@ -289,7 +289,6 @@ static void butterfly_attach(struct parport *p) pr_debug("%s: dataflash at %s\n", p->name, dev_name(&pp->dataflash->dev)); - // dev_info(_what?_, ...) pr_info("%s: AVR Butterfly\n", p->name); butterfly = pp; return; -- cgit v0.10.2 From 3fed8068fbad01b044c40c4df24bb97e853813ff Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 14 Oct 2013 10:35:08 +0900 Subject: spi: orion: Fix checkpatch issue Fix the following checkpatch warnings. WARNING: quoted string split across lines WARNING: sizeof *spi should be sizeof(*spi) Signed-off-by: Jingoo Han Acked-by: Jason Cooper Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index 1d1d321..c6caed3 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -84,8 +84,8 @@ static int orion_spi_set_transfer_size(struct orion_spi *orion_spi, int size) orion_spi_clrbits(orion_spi, ORION_SPI_IF_CONFIG_REG, ORION_SPI_IF_8_16_BIT_MODE); } else { - pr_debug("Bad bits per word value %d (only 8 or 16 are " - "allowed).\n", size); + pr_debug("Bad bits per word value %d (only 8 or 16 are allowed).\n", + size); return -EINVAL; } @@ -407,7 +407,7 @@ static int orion_spi_probe(struct platform_device *pdev) const u32 *iprop; int size; - master = spi_alloc_master(&pdev->dev, sizeof *spi); + master = spi_alloc_master(&pdev->dev, sizeof(*spi)); if (master == NULL) { dev_dbg(&pdev->dev, "master allocation failed\n"); return -ENOMEM; -- cgit v0.10.2 From a29c8ae7187aee902d37d7677255de726614e43e Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 14 Oct 2013 10:35:42 +0900 Subject: spi: sh-hspi: Fix checkpatch issue Fix the following checkpatch warning. WARNING: space prohibited before semicolon Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-sh-hspi.c b/drivers/spi/spi-sh-hspi.c index e488a90..b9e7b5e 100644 --- a/drivers/spi/spi-sh-hspi.c +++ b/drivers/spi/spi-sh-hspi.c @@ -137,7 +137,7 @@ static void hspi_hw_setup(struct hspi_priv *hspi, rate /= 16; /* CLKCx calculation */ - rate /= (((idiv_clk & 0x1F) + 1) * 2) ; + rate /= (((idiv_clk & 0x1F) + 1) * 2); /* save best settings */ tmp = abs(target_rate - rate); -- cgit v0.10.2 From 3cb7b407ce84e5756076f64bf4341cc101955966 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 14 Oct 2013 10:36:10 +0900 Subject: spi: tegra20-slink: Fix checkpatch issue Fix the following checkpatch warning. WARNING: space prohibited before semicolon Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index 36d4e66..d1d2bff 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -278,7 +278,7 @@ static unsigned tegra_slink_calculate_curr_xfer_param( { unsigned remain_len = t->len - tspi->cur_pos; unsigned max_word; - unsigned bits_per_word ; + unsigned bits_per_word; unsigned max_len; unsigned total_fifo_words; -- cgit v0.10.2 From 256fbf36d159f286011667ad84c378452d5fc1bf Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 14 Oct 2013 10:36:31 +0900 Subject: spi: txx9: Fix checkpatch issue Fix the following checkpatch warnings. WARNING: space prohibited between function name and open parenthesis '(' Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-txx9.c b/drivers/spi/spi-txx9.c index 7c6d157..017139b 100644 --- a/drivers/spi/spi-txx9.c +++ b/drivers/spi/spi-txx9.c @@ -177,7 +177,7 @@ static void txx9spi_work_one(struct txx9spi *c, struct spi_message *m) | 0x08, TXx9_SPCR0); - list_for_each_entry (t, &m->transfers, transfer_list) { + list_for_each_entry(t, &m->transfers, transfer_list) { const void *txbuf = t->tx_buf; void *rxbuf = t->rx_buf; u32 data; @@ -308,7 +308,7 @@ static int txx9spi_transfer(struct spi_device *spi, struct spi_message *m) m->actual_length = 0; /* check each transfer's parameters */ - list_for_each_entry (t, &m->transfers, transfer_list) { + list_for_each_entry(t, &m->transfers, transfer_list) { u32 speed_hz = t->speed_hz ? : spi->max_speed_hz; u8 bits_per_word = t->bits_per_word; -- cgit v0.10.2 From 95c63cfba777bf2154565abbdc61ea970aaa632c Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 14 Oct 2013 10:36:54 +0900 Subject: spi: spidev: Fix checkpatch issue Fix the following checkpatch warnings. WARNING: Use #include instead of WARNING: braces {} are not necessary for any arm of this statement Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index c53556a..d7c6e36 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -37,7 +37,7 @@ #include #include -#include +#include /* @@ -206,9 +206,9 @@ spidev_write(struct file *filp, const char __user *buf, mutex_lock(&spidev->buf_lock); missing = copy_from_user(spidev->buffer, buf, count); - if (missing == 0) { + if (missing == 0) status = spidev_sync_write(spidev, count); - } else + else status = -EFAULT; mutex_unlock(&spidev->buf_lock); -- cgit v0.10.2 From 34f7568510564726d33fe8248736ece7d76d0054 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 14 Oct 2013 17:45:51 +0900 Subject: regulator: anatop: Fix checkpatch issue Fix the following checkpatch warning. WARNING: quoted string split across lines Signed-off-by: Jingoo Han Acked-by: Shawn Guo Signed-off-by: Mark Brown diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c index 0d4a8cc..75a9c78 100644 --- a/drivers/regulator/anatop-regulator.c +++ b/drivers/regulator/anatop-regulator.c @@ -256,7 +256,7 @@ static void __exit anatop_regulator_exit(void) } module_exit(anatop_regulator_exit); -MODULE_AUTHOR("Nancy Chen , " - "Ying-Chun Liu (PaulLiu) "); +MODULE_AUTHOR("Nancy Chen "); +MODULE_AUTHOR("Ying-Chun Liu (PaulLiu) "); MODULE_DESCRIPTION("ANATOP Regulator driver"); MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 609d5f6ddc18d05801646c869fcf4d0537b9110b Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 14 Oct 2013 17:46:36 +0900 Subject: regulator: fixed: Fix checkpatch issue Fix the following checkpatch warnings. WARNING: braces {} are not necessary for any arm of this statement Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index de811f3..5ea64b9 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -168,17 +168,15 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) cfg.ena_gpio = config->gpio; cfg.ena_gpio_invert = !config->enable_high; if (config->enabled_at_boot) { - if (config->enable_high) { + if (config->enable_high) cfg.ena_gpio_flags |= GPIOF_OUT_INIT_HIGH; - } else { + else cfg.ena_gpio_flags |= GPIOF_OUT_INIT_LOW; - } } else { - if (config->enable_high) { + if (config->enable_high) cfg.ena_gpio_flags |= GPIOF_OUT_INIT_LOW; - } else { + else cfg.ena_gpio_flags |= GPIOF_OUT_INIT_HIGH; - } } if (config->gpio_is_open_drain) cfg.ena_gpio_flags |= GPIOF_OPEN_DRAIN; -- cgit v0.10.2 From 0a3ee93a3226a951dc32197f6fcfb1b535fbabf4 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 14 Oct 2013 17:47:31 +0900 Subject: regulator: lp3971: Fix checkpatch issue Fix the following checkpatch warnings. WARNING: please, no spaces at the start of a line Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/regulator/lp3971.c b/drivers/regulator/lp3971.c index 5a4604e..947c05f 100644 --- a/drivers/regulator/lp3971.c +++ b/drivers/regulator/lp3971.c @@ -474,8 +474,8 @@ static int lp3971_i2c_remove(struct i2c_client *i2c) } static const struct i2c_device_id lp3971_i2c_id[] = { - { "lp3971", 0 }, - { } + { "lp3971", 0 }, + { } }; MODULE_DEVICE_TABLE(i2c, lp3971_i2c_id); -- cgit v0.10.2 From c4e3b1442a77e7fd7459168894f6416f00542f74 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 14 Oct 2013 17:49:11 +0900 Subject: regulator: max8997: Fix checkpatch issue Fix the following checkpatch warning. WARNING: quoted string split across lines Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/regulator/max8997.c b/drivers/regulator/max8997.c index df20069..e789b4b 100644 --- a/drivers/regulator/max8997.c +++ b/drivers/regulator/max8997.c @@ -690,8 +690,9 @@ static int max8997_set_voltage_buck(struct regulator_dev *rdev, if (max8997->ignore_gpiodvs_side_effect == false) return -EINVAL; - dev_warn(&rdev->dev, "MAX8997 GPIO-DVS Side Effect Warning: GPIO SET:" - " %d -> %d\n", max8997->buck125_gpioindex, tmp_idx); + dev_warn(&rdev->dev, + "MAX8997 GPIO-DVS Side Effect Warning: GPIO SET: %d -> %d\n", + max8997->buck125_gpioindex, tmp_idx); out: if (new_idx < 0 || new_val < 0) -- cgit v0.10.2 From 6e044c3fa87e310f24ec8062d7d6954180761fea Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 14 Oct 2013 17:49:55 +0900 Subject: regulator: mc13783: Fix checkpatch issue Fix the following checkpatch warnings. WARNING: Avoid unnecessary line continuations Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/regulator/mc13783-regulator.c b/drivers/regulator/mc13783-regulator.c index 5ff99d2..4921bcb 100644 --- a/drivers/regulator/mc13783-regulator.c +++ b/drivers/regulator/mc13783-regulator.c @@ -258,34 +258,34 @@ static struct mc13xxx_regulator mc13783_regulators[] = { MC13783_FIXED_DEFINE(REG, VAUDIO, REGULATORMODE0, mc13783_vaudio_val), MC13783_FIXED_DEFINE(REG, VIOHI, REGULATORMODE0, mc13783_viohi_val), - MC13783_DEFINE_REGU(VIOLO, REGULATORMODE0, REGULATORSETTING0, \ + MC13783_DEFINE_REGU(VIOLO, REGULATORMODE0, REGULATORSETTING0, mc13783_violo_val), - MC13783_DEFINE_REGU(VDIG, REGULATORMODE0, REGULATORSETTING0, \ + MC13783_DEFINE_REGU(VDIG, REGULATORMODE0, REGULATORSETTING0, mc13783_vdig_val), - MC13783_DEFINE_REGU(VGEN, REGULATORMODE0, REGULATORSETTING0, \ + MC13783_DEFINE_REGU(VGEN, REGULATORMODE0, REGULATORSETTING0, mc13783_vgen_val), - MC13783_DEFINE_REGU(VRFDIG, REGULATORMODE0, REGULATORSETTING0, \ + MC13783_DEFINE_REGU(VRFDIG, REGULATORMODE0, REGULATORSETTING0, mc13783_vrfdig_val), - MC13783_DEFINE_REGU(VRFREF, REGULATORMODE0, REGULATORSETTING0, \ + MC13783_DEFINE_REGU(VRFREF, REGULATORMODE0, REGULATORSETTING0, mc13783_vrfref_val), - MC13783_DEFINE_REGU(VRFCP, REGULATORMODE0, REGULATORSETTING0, \ + MC13783_DEFINE_REGU(VRFCP, REGULATORMODE0, REGULATORSETTING0, mc13783_vrfcp_val), - MC13783_DEFINE_REGU(VSIM, REGULATORMODE1, REGULATORSETTING0, \ + MC13783_DEFINE_REGU(VSIM, REGULATORMODE1, REGULATORSETTING0, mc13783_vsim_val), - MC13783_DEFINE_REGU(VESIM, REGULATORMODE1, REGULATORSETTING0, \ + MC13783_DEFINE_REGU(VESIM, REGULATORMODE1, REGULATORSETTING0, mc13783_vesim_val), - MC13783_DEFINE_REGU(VCAM, REGULATORMODE1, REGULATORSETTING0, \ + MC13783_DEFINE_REGU(VCAM, REGULATORMODE1, REGULATORSETTING0, mc13783_vcam_val), MC13783_FIXED_DEFINE(REG, VRFBG, REGULATORMODE1, mc13783_vrfbg_val), - MC13783_DEFINE_REGU(VVIB, REGULATORMODE1, REGULATORSETTING1, \ + MC13783_DEFINE_REGU(VVIB, REGULATORMODE1, REGULATORSETTING1, mc13783_vvib_val), - MC13783_DEFINE_REGU(VRF1, REGULATORMODE1, REGULATORSETTING1, \ + MC13783_DEFINE_REGU(VRF1, REGULATORMODE1, REGULATORSETTING1, mc13783_vrf_val), - MC13783_DEFINE_REGU(VRF2, REGULATORMODE1, REGULATORSETTING1, \ + MC13783_DEFINE_REGU(VRF2, REGULATORMODE1, REGULATORSETTING1, mc13783_vrf_val), - MC13783_DEFINE_REGU(VMMC1, REGULATORMODE1, REGULATORSETTING1, \ + MC13783_DEFINE_REGU(VMMC1, REGULATORMODE1, REGULATORSETTING1, mc13783_vmmc_val), - MC13783_DEFINE_REGU(VMMC2, REGULATORMODE1, REGULATORSETTING1, \ + MC13783_DEFINE_REGU(VMMC2, REGULATORMODE1, REGULATORSETTING1, mc13783_vmmc_val), MC13783_GPO_DEFINE(REG, GPO1, POWERMISC, mc13783_gpo_val), MC13783_GPO_DEFINE(REG, GPO2, POWERMISC, mc13783_gpo_val), -- cgit v0.10.2 From 4932e89d75f7b946d2f03a0157b8b9f6a0e12e10 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 14 Oct 2013 17:51:51 +0900 Subject: regulator: tps6105x: Fix checkpatch issue Fix the following checkpatch warning. WARNING: unnecessary whitespace before a quoted newline Signed-off-by: Jingoo Han Acked-by: Linus Walleij Signed-off-by: Mark Brown diff --git a/drivers/regulator/tps6105x-regulator.c b/drivers/regulator/tps6105x-regulator.c index ec9453f..3346e78 100644 --- a/drivers/regulator/tps6105x-regulator.c +++ b/drivers/regulator/tps6105x-regulator.c @@ -137,7 +137,7 @@ static int tps6105x_regulator_probe(struct platform_device *pdev) /* This instance is not set for regulator mode so bail out */ if (pdata->mode != TPS6105X_MODE_VOLTAGE) { dev_info(&pdev->dev, - "chip not in voltage mode mode, exit probe \n"); + "chip not in voltage mode mode, exit probe\n"); return 0; } -- cgit v0.10.2 From 5ee034e614a9e115763fa8d50ae18bf6a13b21d1 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 14 Oct 2013 17:52:45 +0900 Subject: regulator: tps65023: Fix checkpatch issue Fix the following checkpatch warning. WARNING: line over 80 characters Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index a15263d..f6e3982 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -293,7 +293,8 @@ static int tps_65023_probe(struct i2c_client *client, /* Enable setting output voltage by I2C */ regmap_update_bits(tps->regmap, TPS65023_REG_CON_CTRL2, - TPS65023_REG_CTRL2_CORE_ADJ, TPS65023_REG_CTRL2_CORE_ADJ); + TPS65023_REG_CTRL2_CORE_ADJ, + TPS65023_REG_CTRL2_CORE_ADJ); return 0; -- cgit v0.10.2 From 4b57927045c30d8f8579a43177459782f45f5468 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 14 Oct 2013 17:53:40 +0900 Subject: regulator: tps65910: Fix checkpatch issue Fix the following checkpatch errors and warning. ERROR: spaces required around that '=' (ctx:VxV) ERROR: space required before the open parenthesis '(' WARNING: line over 80 characters Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index 95ebc67..10615d2 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -481,7 +481,7 @@ static int tps65910_get_voltage_dcdc_sel(struct regulator_dev *dev) /* multiplier 0 == 1 but 2,3 normal */ if (!mult) - mult=1; + mult = 1; if (sr) { /* normalise to valid range */ @@ -685,7 +685,7 @@ static int tps65910_list_voltage_dcdc(struct regulator_dev *dev, case TPS65910_REG_VDD2: mult = (selector / VDD1_2_NUM_VOLT_FINE) + 1; volt = VDD1_2_MIN_VOLT + - (selector % VDD1_2_NUM_VOLT_FINE) * VDD1_2_OFFSET; + (selector % VDD1_2_NUM_VOLT_FINE) * VDD1_2_OFFSET; break; case TPS65911_REG_VDDCTRL: volt = VDDCTRL_MIN_VOLT + (selector * VDDCTRL_OFFSET); @@ -703,7 +703,7 @@ static int tps65911_list_voltage(struct regulator_dev *dev, unsigned selector) struct tps65910_reg *pmic = rdev_get_drvdata(dev); int step_mv = 0, id = rdev_get_id(dev); - switch(id) { + switch (id) { case TPS65911_REG_LDO1: case TPS65911_REG_LDO2: case TPS65911_REG_LDO4: @@ -1074,7 +1074,7 @@ static int tps65910_probe(struct platform_device *pdev) tps65910_reg_set_bits(pmic->mfd, TPS65910_DEVCTRL, DEVCTRL_SR_CTL_I2C_SEL_MASK); - switch(tps65910_chip_id(tps65910)) { + switch (tps65910_chip_id(tps65910)) { case TPS65910: pmic->get_ctrl_reg = &tps65910_get_ctrl_register; pmic->num_regulators = ARRAY_SIZE(tps65910_regs); -- cgit v0.10.2 From 05209f457069e595ce0262a9032cbade05398571 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 12 Oct 2013 15:15:31 +0800 Subject: spi: fsl-dspi: add missing clk_disable_unprepare() in dspi_remove() clock source is prepared and enabled by clk_prepare_enable() in probe function, but no disable or unprepare in remove. Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index dc3d4eb..3c26fb2 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -536,6 +536,7 @@ static int dspi_remove(struct platform_device *pdev) /* Disconnect from the SPI framework */ spi_bitbang_stop(&dspi->bitbang); + clk_disable_unprepare(dspi->clk); spi_master_put(dspi->bitbang.master); return 0; -- cgit v0.10.2 From d1cb9d1af0bc11b7450a6032f43935c746609418 Mon Sep 17 00:00:00 2001 From: David Miller Date: Thu, 3 Oct 2013 17:24:51 -0400 Subject: of: Make cpu node handling more portable. Use for_each_node_by_type() to iterate all cpu nodes in the system. Provide and overridable function arch_find_n_match_cpu_physical_id, which sees if the given device node matches 'cpu' and if so sets '*thread' when non-NULL to the cpu thread number within the core. The default implementation behaves the same as the existing code. Add a sparc64 implementation. Signed-off-by: David S. Miller Tested-by: Sudeep KarkadaNagesha Signed-off-by: Grant Likely diff --git a/arch/sparc/kernel/prom_64.c b/arch/sparc/kernel/prom_64.c index d397d7f..6b39125 100644 --- a/arch/sparc/kernel/prom_64.c +++ b/arch/sparc/kernel/prom_64.c @@ -373,6 +373,59 @@ static const char *get_mid_prop(void) return (tlb_type == spitfire ? "upa-portid" : "portid"); } +bool arch_find_n_match_cpu_physical_id(struct device_node *cpun, + int cpu, unsigned int *thread) +{ + const char *mid_prop = get_mid_prop(); + int this_cpu_id; + + /* On hypervisor based platforms we interrogate the 'reg' + * property. On everything else we look for a 'upa-portis', + * 'portid', or 'cpuid' property. + */ + + if (tlb_type == hypervisor) { + struct property *prop = of_find_property(cpun, "reg", NULL); + u32 *regs; + + if (!prop) { + pr_warn("CPU node missing reg property\n"); + return false; + } + regs = prop->value; + this_cpu_id = regs[0] & 0x0fffffff; + } else { + this_cpu_id = of_getintprop_default(cpun, mid_prop, -1); + + if (this_cpu_id < 0) { + mid_prop = "cpuid"; + this_cpu_id = of_getintprop_default(cpun, mid_prop, -1); + } + if (this_cpu_id < 0) { + pr_warn("CPU node missing cpu ID property\n"); + return false; + } + } + if (this_cpu_id == cpu) { + if (thread) { + int proc_id = cpu_data(cpu).proc_id; + + /* On sparc64, the cpu thread information is obtained + * either from OBP or the machine description. We've + * actually probed this information already long before + * this interface gets called so instead of interrogating + * both the OF node and the MDESC again, just use what + * we discovered already. + */ + if (proc_id < 0) + proc_id = 0; + *thread = proc_id; + } + return true; + } + return false; +} + static void *of_iterate_over_cpus(void *(*func)(struct device_node *, int, int), int arg) { struct device_node *dp; diff --git a/drivers/of/base.c b/drivers/of/base.c index 7d4c70f..e4c9945 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -280,6 +280,31 @@ static bool __of_find_n_match_cpu_property(struct device_node *cpun, return false; } +/* + * arch_find_n_match_cpu_physical_id - See if the given device node is + * for the cpu corresponding to logical cpu 'cpu'. Return true if so, + * else false. If 'thread' is non-NULL, the local thread number within the + * core is returned in it. + */ +bool __weak arch_find_n_match_cpu_physical_id(struct device_node *cpun, + int cpu, unsigned int *thread) +{ + /* Check for non-standard "ibm,ppc-interrupt-server#s" property + * for thread ids on PowerPC. If it doesn't exist fallback to + * standard "reg" property. + */ + if (IS_ENABLED(CONFIG_PPC) && + __of_find_n_match_cpu_property(cpun, + "ibm,ppc-interrupt-server#s", + cpu, thread)) + return true; + + if (__of_find_n_match_cpu_property(cpun, "reg", cpu, thread)) + return true; + + return false; +} + /** * of_get_cpu_node - Get device node associated with the given logical CPU * @@ -300,24 +325,10 @@ static bool __of_find_n_match_cpu_property(struct device_node *cpun, */ struct device_node *of_get_cpu_node(int cpu, unsigned int *thread) { - struct device_node *cpun, *cpus; - - cpus = of_find_node_by_path("/cpus"); - if (!cpus) - return NULL; + struct device_node *cpun; - for_each_child_of_node(cpus, cpun) { - if (of_node_cmp(cpun->type, "cpu")) - continue; - /* Check for non-standard "ibm,ppc-interrupt-server#s" property - * for thread ids on PowerPC. If it doesn't exist fallback to - * standard "reg" property. - */ - if (IS_ENABLED(CONFIG_PPC) && - __of_find_n_match_cpu_property(cpun, - "ibm,ppc-interrupt-server#s", cpu, thread)) - return cpun; - if (__of_find_n_match_cpu_property(cpun, "reg", cpu, thread)) + for_each_node_by_type(cpun, "cpu") { + if (arch_find_n_match_cpu_physical_id(cpun, cpu, thread)) return cpun; } return NULL; diff --git a/include/linux/cpu.h b/include/linux/cpu.h index 801ff9e..fbd25c3 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -18,6 +18,7 @@ #include struct device; +struct device_node; struct cpu { int node_id; /* The node which contains the CPU */ @@ -29,6 +30,8 @@ extern int register_cpu(struct cpu *cpu, int num); extern struct device *get_cpu_device(unsigned cpu); extern bool cpu_is_hotpluggable(unsigned cpu); extern bool arch_match_cpu_phys_id(int cpu, u64 phys_id); +extern bool arch_find_n_match_cpu_physical_id(struct device_node *cpun, + int cpu, unsigned int *thread); extern int cpu_add_dev_attr(struct device_attribute *attr); extern void cpu_remove_dev_attr(struct device_attribute *attr); -- cgit v0.10.2 From f3cea45a77c8ebdb7efad100e576eb6cb401bf25 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Fri, 4 Oct 2013 17:24:26 +0100 Subject: of: Fix iteration bug over CPU reg properties The size of each hwid in a cpu nodes 'reg' property is defined by the parents #address-cells property in the normal way. The cpu parsing code has a bug where it will overrun the end of the property if address-cells is greater than one. This commit fixes the problem by adjusting the array size by the number of address cells. It also makes sure address-cells isn't zero for that would cause an infinite loop. v2: bail if #address-cells is zero instead of forcing to OF_ROOT_NODE_ADDR_CELLS_DEFAULT. Forcing it will cause the reg property to be parsed incorrectly. Signed-off-by: Grant Likely Cc: Rob Herring Cc: Benjamin Herrenschmidt diff --git a/drivers/of/base.c b/drivers/of/base.c index e4c9945..3ae106d 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -265,9 +265,9 @@ static bool __of_find_n_match_cpu_property(struct device_node *cpun, ac = of_n_addr_cells(cpun); cell = of_get_property(cpun, prop_name, &prop_len); - if (!cell) + if (!cell || !ac) return false; - prop_len /= sizeof(*cell); + prop_len /= sizeof(*cell) * ac; for (tid = 0; tid < prop_len; tid++) { hwid = of_read_number(cell, ac); if (arch_match_cpu_phys_id(cpu, hwid)) { -- cgit v0.10.2 From 8804827b305dbc1c6e24f2b36f1df4a9856b80e8 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 19 Sep 2013 11:01:52 -0500 Subject: of: Fix dereferencing node name in debug output to be safe Several locations in the of_address and of_irq code dereference the full_name parameter from a device_node pointer without checking if the pointer is valid. This patch switches to use of_node_full_name() which always checks the pointer. Signed-off-by: Grant Likely diff --git a/drivers/of/address.c b/drivers/of/address.c index b55c218..71180b9 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -489,7 +489,7 @@ static u64 __of_translate_address(struct device_node *dev, int na, ns, pna, pns; u64 result = OF_BAD_ADDR; - pr_debug("OF: ** translation for device %s **\n", dev->full_name); + pr_debug("OF: ** translation for device %s **\n", of_node_full_name(dev)); /* Increase refcount at current level */ of_node_get(dev); @@ -504,13 +504,13 @@ static u64 __of_translate_address(struct device_node *dev, bus->count_cells(dev, &na, &ns); if (!OF_CHECK_COUNTS(na, ns)) { printk(KERN_ERR "prom_parse: Bad cell count for %s\n", - dev->full_name); + of_node_full_name(dev)); goto bail; } memcpy(addr, in_addr, na * 4); pr_debug("OF: bus is %s (na=%d, ns=%d) on %s\n", - bus->name, na, ns, parent->full_name); + bus->name, na, ns, of_node_full_name(parent)); of_dump_addr("OF: translating address:", addr, na); /* Translate */ diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 1752988..f5fa5d8 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -102,7 +102,7 @@ int of_irq_map_raw(struct device_node *parent, const __be32 *intspec, int imaplen, match, i; pr_debug("of_irq_map_raw: par=%s,intspec=[0x%08x 0x%08x...],ointsize=%d\n", - parent->full_name, be32_to_cpup(intspec), + of_node_full_name(parent), be32_to_cpup(intspec), be32_to_cpup(intspec + 1), ointsize); ipar = of_node_get(parent); @@ -126,7 +126,7 @@ int of_irq_map_raw(struct device_node *parent, const __be32 *intspec, goto fail; } - pr_debug("of_irq_map_raw: ipar=%s, size=%d\n", ipar->full_name, intsize); + pr_debug("of_irq_map_raw: ipar=%s, size=%d\n", of_node_full_name(ipar), intsize); if (ointsize != intsize) return -EINVAL; @@ -287,7 +287,7 @@ int of_irq_map_one(struct device_node *device, int index, struct of_irq *out_irq u32 intsize, intlen; int res = -EINVAL; - pr_debug("of_irq_map_one: dev=%s, index=%d\n", device->full_name, index); + pr_debug("of_irq_map_one: dev=%s, index=%d\n", of_node_full_name(device), index); /* OldWorld mac stuff is "special", handle out of line */ if (of_irq_workarounds & OF_IMAP_OLDWORLD_MAC) @@ -355,7 +355,7 @@ int of_irq_to_resource(struct device_node *dev, int index, struct resource *r) r->start = r->end = irq; r->flags = IORESOURCE_IRQ; - r->name = name ? name : dev->full_name; + r->name = name ? name : of_node_full_name(dev); } return irq; -- cgit v0.10.2 From 4a43d686fe336cc0e955c4400ba4d3fcff788786 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Sat, 28 Sep 2013 19:52:51 +0200 Subject: of/irq: Pass trigger type in IRQ resource flags Some drivers might rely on availability of trigger flags in IRQ resource, for example to configure the hardware for particular interrupt type. However current code creating IRQ resources from data in device tree does not configure trigger flags in resulting resources. This patch tries to solve the problem, based on the fact that irq_of_parse_and_map() configures the trigger based on DT interrupt specifier and IRQD_TRIGGER_* flags are consistent with IORESOURCE_IRQ_*, and we can get correct trigger flags by calling irqd_get_trigger_type() after mapping the interrupt. Signed-off-by: Tomasz Figa [grant.likely: Merged the two assignments to r->flags] Signed-off-by: Grant Likely diff --git a/drivers/of/irq.c b/drivers/of/irq.c index f5fa5d8..90068f9 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -354,7 +354,7 @@ int of_irq_to_resource(struct device_node *dev, int index, struct resource *r) &name); r->start = r->end = irq; - r->flags = IORESOURCE_IRQ; + r->flags = IORESOURCE_IRQ | irqd_get_trigger_type(irq_get_irq_data(irq)); r->name = name ? name : of_node_full_name(dev); } -- cgit v0.10.2 From 4102adab9189c8ea2f0cdd2f88345fd25d2790f1 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 8 Oct 2013 20:23:47 -0700 Subject: rcu: Move RCU-related source code to kernel/rcu directory Signed-off-by: Paul E. McKenney Reviewed-by: Ingo Molnar diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index fe397f9..6c9d9d3 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -87,7 +87,10 @@ X!Iinclude/linux/kobject.h !Ekernel/printk/printk.c !Ekernel/panic.c !Ekernel/sys.c -!Ekernel/rcupdate.c +!Ekernel/rcu/srcu.c +!Ekernel/rcu/tree.c +!Ekernel/rcu/tree_plugin.h +!Ekernel/rcu/update.c Device Resource Management diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 1a036cd9..c3dc13e 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2595,7 +2595,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted. ramdisk_size= [RAM] Sizes of RAM disks in kilobytes See Documentation/blockdev/ramdisk.txt. - rcu_nocbs= [KNL,BOOT] + rcu_nocbs= [KNL] In kernels built with CONFIG_RCU_NOCB_CPU=y, set the specified list of CPUs to be no-callback CPUs. Invocation of these CPUs' RCU callbacks will @@ -2608,7 +2608,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted. real-time workloads. It can also improve energy efficiency for asymmetric multiprocessors. - rcu_nocb_poll [KNL,BOOT] + rcu_nocb_poll [KNL] Rather than requiring that offloaded CPUs (specified by rcu_nocbs= above) explicitly awaken the corresponding "rcuoN" kthreads, @@ -2619,126 +2619,145 @@ bytes respectively. Such letter suffixes can also be entirely omitted. energy efficiency by requiring that the kthreads periodically wake up to do the polling. - rcutree.blimit= [KNL,BOOT] + rcutree.blimit= [KNL] Set maximum number of finished RCU callbacks to process in one batch. - rcutree.fanout_leaf= [KNL,BOOT] + rcutree.rcu_fanout_leaf= [KNL] Increase the number of CPUs assigned to each leaf rcu_node structure. Useful for very large systems. - rcutree.jiffies_till_first_fqs= [KNL,BOOT] + rcutree.jiffies_till_first_fqs= [KNL] Set delay from grace-period initialization to first attempt to force quiescent states. Units are jiffies, minimum value is zero, and maximum value is HZ. - rcutree.jiffies_till_next_fqs= [KNL,BOOT] + rcutree.jiffies_till_next_fqs= [KNL] Set delay between subsequent attempts to force quiescent states. Units are jiffies, minimum value is one, and maximum value is HZ. - rcutree.qhimark= [KNL,BOOT] + rcutree.qhimark= [KNL] Set threshold of queued RCU callbacks over which batch limiting is disabled. - rcutree.qlowmark= [KNL,BOOT] + rcutree.qlowmark= [KNL] Set threshold of queued RCU callbacks below which batch limiting is re-enabled. - rcutree.rcu_cpu_stall_suppress= [KNL,BOOT] - Suppress RCU CPU stall warning messages. - - rcutree.rcu_cpu_stall_timeout= [KNL,BOOT] - Set timeout for RCU CPU stall warning messages. - - rcutree.rcu_idle_gp_delay= [KNL,BOOT] + rcutree.rcu_idle_gp_delay= [KNL] Set wakeup interval for idle CPUs that have RCU callbacks (RCU_FAST_NO_HZ=y). - rcutree.rcu_idle_lazy_gp_delay= [KNL,BOOT] + rcutree.rcu_idle_lazy_gp_delay= [KNL] Set wakeup interval for idle CPUs that have only "lazy" RCU callbacks (RCU_FAST_NO_HZ=y). Lazy RCU callbacks are those which RCU can prove do nothing more than free memory. - rcutorture.fqs_duration= [KNL,BOOT] + rcutorture.fqs_duration= [KNL] Set duration of force_quiescent_state bursts. - rcutorture.fqs_holdoff= [KNL,BOOT] + rcutorture.fqs_holdoff= [KNL] Set holdoff time within force_quiescent_state bursts. - rcutorture.fqs_stutter= [KNL,BOOT] + rcutorture.fqs_stutter= [KNL] Set wait time between force_quiescent_state bursts. - rcutorture.irqreader= [KNL,BOOT] - Test RCU readers from irq handlers. + rcutorture.gp_exp= [KNL] + Use expedited update-side primitives. + + rcutorture.gp_normal= [KNL] + Use normal (non-expedited) update-side primitives. + If both gp_exp and gp_normal are set, do both. + If neither gp_exp nor gp_normal are set, still + do both. - rcutorture.n_barrier_cbs= [KNL,BOOT] + rcutorture.n_barrier_cbs= [KNL] Set callbacks/threads for rcu_barrier() testing. - rcutorture.nfakewriters= [KNL,BOOT] + rcutorture.nfakewriters= [KNL] Set number of concurrent RCU writers. These just stress RCU, they don't participate in the actual test, hence the "fake". - rcutorture.nreaders= [KNL,BOOT] + rcutorture.nreaders= [KNL] Set number of RCU readers. - rcutorture.onoff_holdoff= [KNL,BOOT] + rcutorture.object_debug= [KNL] + Enable debug-object double-call_rcu() testing. + + rcutorture.onoff_holdoff= [KNL] Set time (s) after boot for CPU-hotplug testing. - rcutorture.onoff_interval= [KNL,BOOT] + rcutorture.onoff_interval= [KNL] Set time (s) between CPU-hotplug operations, or zero to disable CPU-hotplug testing. - rcutorture.shuffle_interval= [KNL,BOOT] + rcutorture.rcutorture_runnable= [BOOT] + Start rcutorture running at boot time. + + rcutorture.shuffle_interval= [KNL] Set task-shuffle interval (s). Shuffling tasks allows some CPUs to go into dyntick-idle mode during the rcutorture test. - rcutorture.shutdown_secs= [KNL,BOOT] + rcutorture.shutdown_secs= [KNL] Set time (s) after boot system shutdown. This is useful for hands-off automated testing. - rcutorture.stall_cpu= [KNL,BOOT] + rcutorture.stall_cpu= [KNL] Duration of CPU stall (s) to test RCU CPU stall warnings, zero to disable. - rcutorture.stall_cpu_holdoff= [KNL,BOOT] + rcutorture.stall_cpu_holdoff= [KNL] Time to wait (s) after boot before inducing stall. - rcutorture.stat_interval= [KNL,BOOT] + rcutorture.stat_interval= [KNL] Time (s) between statistics printk()s. - rcutorture.stutter= [KNL,BOOT] + rcutorture.stutter= [KNL] Time (s) to stutter testing, for example, specifying five seconds causes the test to run for five seconds, wait for five seconds, and so on. This tests RCU's ability to transition abruptly to and from idle. - rcutorture.test_boost= [KNL,BOOT] + rcutorture.test_boost= [KNL] Test RCU priority boosting? 0=no, 1=maybe, 2=yes. "Maybe" means test if the RCU implementation under test support RCU priority boosting. - rcutorture.test_boost_duration= [KNL,BOOT] + rcutorture.test_boost_duration= [KNL] Duration (s) of each individual boost test. - rcutorture.test_boost_interval= [KNL,BOOT] + rcutorture.test_boost_interval= [KNL] Interval (s) between each boost test. - rcutorture.test_no_idle_hz= [KNL,BOOT] + rcutorture.test_no_idle_hz= [KNL] Test RCU's dyntick-idle handling. See also the rcutorture.shuffle_interval parameter. - rcutorture.torture_type= [KNL,BOOT] + rcutorture.torture_type= [KNL] Specify the RCU implementation to test. - rcutorture.verbose= [KNL,BOOT] + rcutorture.verbose= [KNL] Enable additional printk() statements. + rcupdate.rcu_expedited= [KNL] + Use expedited grace-period primitives, for + example, synchronize_rcu_expedited() instead + of synchronize_rcu(). This reduces latency, + but can increase CPU utilization, degrade + real-time latency, and degrade energy efficiency. + + rcupdate.rcu_cpu_stall_suppress= [KNL] + Suppress RCU CPU stall warning messages. + + rcupdate.rcu_cpu_stall_timeout= [KNL] + Set timeout for RCU CPU stall warning messages. + rdinit= [KNL] Format: Run specified binary instead of /init from the ramdisk, diff --git a/MAINTAINERS b/MAINTAINERS index e61c2e8..28f2478 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6903,7 +6903,7 @@ M: "Paul E. McKenney" S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git F: Documentation/RCU/torture.txt -F: kernel/rcutorture.c +F: kernel/rcu/torture.c RDC R-321X SoC M: Florian Fainelli @@ -6930,8 +6930,9 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git F: Documentation/RCU/ X: Documentation/RCU/torture.txt F: include/linux/rcu* -F: kernel/rcu* -X: kernel/rcutorture.c +X: include/linux/srcu.h +F: kernel/rcu/ +X: kernel/rcu/torture.c REAL TIME CLOCK (RTC) SUBSYSTEM M: Alessandro Zummo @@ -7618,8 +7619,8 @@ M: "Paul E. McKenney" W: http://www.rdrop.com/users/paulmck/RCU/ S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git -F: include/linux/srcu* -F: kernel/srcu* +F: include/linux/srcu.h +F: kernel/rcu/srcu.c SMACK SECURITY MODULE M: Casey Schaufler diff --git a/kernel/Makefile b/kernel/Makefile index 1ce4755..f99d908 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -6,9 +6,9 @@ obj-y = fork.o exec_domain.o panic.o \ cpu.o exit.o itimer.o time.o softirq.o resource.o \ sysctl.o sysctl_binary.o capability.o ptrace.o timer.o user.o \ signal.o sys.o kmod.o workqueue.o pid.o task_work.o \ - rcupdate.o extable.o params.o posix-timers.o \ + extable.o params.o posix-timers.o \ kthread.o wait.o sys_ni.o posix-cpu-timers.o mutex.o \ - hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \ + hrtimer.o rwsem.o nsproxy.o semaphore.o \ notifier.o ksysfs.o cred.o reboot.o \ async.o range.o groups.o lglock.o smpboot.o @@ -27,6 +27,7 @@ obj-y += power/ obj-y += printk/ obj-y += cpu/ obj-y += irq/ +obj-y += rcu/ obj-$(CONFIG_CHECKPOINT_RESTORE) += kcmp.o obj-$(CONFIG_FREEZER) += freezer.o @@ -81,12 +82,6 @@ obj-$(CONFIG_KGDB) += debug/ obj-$(CONFIG_DETECT_HUNG_TASK) += hung_task.o obj-$(CONFIG_LOCKUP_DETECTOR) += watchdog.o obj-$(CONFIG_SECCOMP) += seccomp.o -obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o -obj-$(CONFIG_TREE_RCU) += rcutree.o -obj-$(CONFIG_TREE_PREEMPT_RCU) += rcutree.o -obj-$(CONFIG_TREE_RCU_TRACE) += rcutree_trace.o -obj-$(CONFIG_TINY_RCU) += rcutiny.o -obj-$(CONFIG_TINY_PREEMPT_RCU) += rcutiny.o obj-$(CONFIG_RELAY) += relay.o obj-$(CONFIG_SYSCTL) += utsname_sysctl.o obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o diff --git a/kernel/rcu.h b/kernel/rcu.h deleted file mode 100644 index 7859a0a..0000000 --- a/kernel/rcu.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Read-Copy Update definitions shared among RCU implementations. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Copyright IBM Corporation, 2011 - * - * Author: Paul E. McKenney - */ - -#ifndef __LINUX_RCU_H -#define __LINUX_RCU_H - -#ifdef CONFIG_RCU_TRACE -#define RCU_TRACE(stmt) stmt -#else /* #ifdef CONFIG_RCU_TRACE */ -#define RCU_TRACE(stmt) -#endif /* #else #ifdef CONFIG_RCU_TRACE */ - -/* - * Process-level increment to ->dynticks_nesting field. This allows for - * architectures that use half-interrupts and half-exceptions from - * process context. - * - * DYNTICK_TASK_NEST_MASK defines a field of width DYNTICK_TASK_NEST_WIDTH - * that counts the number of process-based reasons why RCU cannot - * consider the corresponding CPU to be idle, and DYNTICK_TASK_NEST_VALUE - * is the value used to increment or decrement this field. - * - * The rest of the bits could in principle be used to count interrupts, - * but this would mean that a negative-one value in the interrupt - * field could incorrectly zero out the DYNTICK_TASK_NEST_MASK field. - * We therefore provide a two-bit guard field defined by DYNTICK_TASK_MASK - * that is set to DYNTICK_TASK_FLAG upon initial exit from idle. - * The DYNTICK_TASK_EXIT_IDLE value is thus the combined value used upon - * initial exit from idle. - */ -#define DYNTICK_TASK_NEST_WIDTH 7 -#define DYNTICK_TASK_NEST_VALUE ((LLONG_MAX >> DYNTICK_TASK_NEST_WIDTH) + 1) -#define DYNTICK_TASK_NEST_MASK (LLONG_MAX - DYNTICK_TASK_NEST_VALUE + 1) -#define DYNTICK_TASK_FLAG ((DYNTICK_TASK_NEST_VALUE / 8) * 2) -#define DYNTICK_TASK_MASK ((DYNTICK_TASK_NEST_VALUE / 8) * 3) -#define DYNTICK_TASK_EXIT_IDLE (DYNTICK_TASK_NEST_VALUE + \ - DYNTICK_TASK_FLAG) - -/* - * debug_rcu_head_queue()/debug_rcu_head_unqueue() are used internally - * by call_rcu() and rcu callback execution, and are therefore not part of the - * RCU API. Leaving in rcupdate.h because they are used by all RCU flavors. - */ - -#ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD -# define STATE_RCU_HEAD_READY 0 -# define STATE_RCU_HEAD_QUEUED 1 - -extern struct debug_obj_descr rcuhead_debug_descr; - -static inline int debug_rcu_head_queue(struct rcu_head *head) -{ - int r1; - - r1 = debug_object_activate(head, &rcuhead_debug_descr); - debug_object_active_state(head, &rcuhead_debug_descr, - STATE_RCU_HEAD_READY, - STATE_RCU_HEAD_QUEUED); - return r1; -} - -static inline void debug_rcu_head_unqueue(struct rcu_head *head) -{ - debug_object_active_state(head, &rcuhead_debug_descr, - STATE_RCU_HEAD_QUEUED, - STATE_RCU_HEAD_READY); - debug_object_deactivate(head, &rcuhead_debug_descr); -} -#else /* !CONFIG_DEBUG_OBJECTS_RCU_HEAD */ -static inline int debug_rcu_head_queue(struct rcu_head *head) -{ - return 0; -} - -static inline void debug_rcu_head_unqueue(struct rcu_head *head) -{ -} -#endif /* #else !CONFIG_DEBUG_OBJECTS_RCU_HEAD */ - -extern void kfree(const void *); - -static inline bool __rcu_reclaim(const char *rn, struct rcu_head *head) -{ - unsigned long offset = (unsigned long)head->func; - - if (__is_kfree_rcu_offset(offset)) { - RCU_TRACE(trace_rcu_invoke_kfree_callback(rn, head, offset)); - kfree((void *)head - offset); - return 1; - } else { - RCU_TRACE(trace_rcu_invoke_callback(rn, head)); - head->func(head); - return 0; - } -} - -extern int rcu_expedited; - -#ifdef CONFIG_RCU_STALL_COMMON - -extern int rcu_cpu_stall_suppress; -int rcu_jiffies_till_stall_check(void); - -#endif /* #ifdef CONFIG_RCU_STALL_COMMON */ - -/* - * Strings used in tracepoints need to be exported via the - * tracing system such that tools like perf and trace-cmd can - * translate the string address pointers to actual text. - */ -#define TPS(x) tracepoint_string(x) - -#endif /* __LINUX_RCU_H */ diff --git a/kernel/rcu/Makefile b/kernel/rcu/Makefile new file mode 100644 index 0000000..01e9ec3 --- /dev/null +++ b/kernel/rcu/Makefile @@ -0,0 +1,6 @@ +obj-y += update.o srcu.o +obj-$(CONFIG_RCU_TORTURE_TEST) += torture.o +obj-$(CONFIG_TREE_RCU) += tree.o +obj-$(CONFIG_TREE_PREEMPT_RCU) += tree.o +obj-$(CONFIG_TREE_RCU_TRACE) += tree_trace.o +obj-$(CONFIG_TINY_RCU) += tiny.o diff --git a/kernel/rcu/rcu.h b/kernel/rcu/rcu.h new file mode 100644 index 0000000..7859a0a --- /dev/null +++ b/kernel/rcu/rcu.h @@ -0,0 +1,132 @@ +/* + * Read-Copy Update definitions shared among RCU implementations. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright IBM Corporation, 2011 + * + * Author: Paul E. McKenney + */ + +#ifndef __LINUX_RCU_H +#define __LINUX_RCU_H + +#ifdef CONFIG_RCU_TRACE +#define RCU_TRACE(stmt) stmt +#else /* #ifdef CONFIG_RCU_TRACE */ +#define RCU_TRACE(stmt) +#endif /* #else #ifdef CONFIG_RCU_TRACE */ + +/* + * Process-level increment to ->dynticks_nesting field. This allows for + * architectures that use half-interrupts and half-exceptions from + * process context. + * + * DYNTICK_TASK_NEST_MASK defines a field of width DYNTICK_TASK_NEST_WIDTH + * that counts the number of process-based reasons why RCU cannot + * consider the corresponding CPU to be idle, and DYNTICK_TASK_NEST_VALUE + * is the value used to increment or decrement this field. + * + * The rest of the bits could in principle be used to count interrupts, + * but this would mean that a negative-one value in the interrupt + * field could incorrectly zero out the DYNTICK_TASK_NEST_MASK field. + * We therefore provide a two-bit guard field defined by DYNTICK_TASK_MASK + * that is set to DYNTICK_TASK_FLAG upon initial exit from idle. + * The DYNTICK_TASK_EXIT_IDLE value is thus the combined value used upon + * initial exit from idle. + */ +#define DYNTICK_TASK_NEST_WIDTH 7 +#define DYNTICK_TASK_NEST_VALUE ((LLONG_MAX >> DYNTICK_TASK_NEST_WIDTH) + 1) +#define DYNTICK_TASK_NEST_MASK (LLONG_MAX - DYNTICK_TASK_NEST_VALUE + 1) +#define DYNTICK_TASK_FLAG ((DYNTICK_TASK_NEST_VALUE / 8) * 2) +#define DYNTICK_TASK_MASK ((DYNTICK_TASK_NEST_VALUE / 8) * 3) +#define DYNTICK_TASK_EXIT_IDLE (DYNTICK_TASK_NEST_VALUE + \ + DYNTICK_TASK_FLAG) + +/* + * debug_rcu_head_queue()/debug_rcu_head_unqueue() are used internally + * by call_rcu() and rcu callback execution, and are therefore not part of the + * RCU API. Leaving in rcupdate.h because they are used by all RCU flavors. + */ + +#ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD +# define STATE_RCU_HEAD_READY 0 +# define STATE_RCU_HEAD_QUEUED 1 + +extern struct debug_obj_descr rcuhead_debug_descr; + +static inline int debug_rcu_head_queue(struct rcu_head *head) +{ + int r1; + + r1 = debug_object_activate(head, &rcuhead_debug_descr); + debug_object_active_state(head, &rcuhead_debug_descr, + STATE_RCU_HEAD_READY, + STATE_RCU_HEAD_QUEUED); + return r1; +} + +static inline void debug_rcu_head_unqueue(struct rcu_head *head) +{ + debug_object_active_state(head, &rcuhead_debug_descr, + STATE_RCU_HEAD_QUEUED, + STATE_RCU_HEAD_READY); + debug_object_deactivate(head, &rcuhead_debug_descr); +} +#else /* !CONFIG_DEBUG_OBJECTS_RCU_HEAD */ +static inline int debug_rcu_head_queue(struct rcu_head *head) +{ + return 0; +} + +static inline void debug_rcu_head_unqueue(struct rcu_head *head) +{ +} +#endif /* #else !CONFIG_DEBUG_OBJECTS_RCU_HEAD */ + +extern void kfree(const void *); + +static inline bool __rcu_reclaim(const char *rn, struct rcu_head *head) +{ + unsigned long offset = (unsigned long)head->func; + + if (__is_kfree_rcu_offset(offset)) { + RCU_TRACE(trace_rcu_invoke_kfree_callback(rn, head, offset)); + kfree((void *)head - offset); + return 1; + } else { + RCU_TRACE(trace_rcu_invoke_callback(rn, head)); + head->func(head); + return 0; + } +} + +extern int rcu_expedited; + +#ifdef CONFIG_RCU_STALL_COMMON + +extern int rcu_cpu_stall_suppress; +int rcu_jiffies_till_stall_check(void); + +#endif /* #ifdef CONFIG_RCU_STALL_COMMON */ + +/* + * Strings used in tracepoints need to be exported via the + * tracing system such that tools like perf and trace-cmd can + * translate the string address pointers to actual text. + */ +#define TPS(x) tracepoint_string(x) + +#endif /* __LINUX_RCU_H */ diff --git a/kernel/rcu/srcu.c b/kernel/rcu/srcu.c new file mode 100644 index 0000000..01d5ccb --- /dev/null +++ b/kernel/rcu/srcu.c @@ -0,0 +1,651 @@ +/* + * Sleepable Read-Copy Update mechanism for mutual exclusion. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2006 + * Copyright (C) Fujitsu, 2012 + * + * Author: Paul McKenney + * Lai Jiangshan + * + * For detailed explanation of Read-Copy Update mechanism see - + * Documentation/RCU/ *.txt + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "rcu.h" + +/* + * Initialize an rcu_batch structure to empty. + */ +static inline void rcu_batch_init(struct rcu_batch *b) +{ + b->head = NULL; + b->tail = &b->head; +} + +/* + * Enqueue a callback onto the tail of the specified rcu_batch structure. + */ +static inline void rcu_batch_queue(struct rcu_batch *b, struct rcu_head *head) +{ + *b->tail = head; + b->tail = &head->next; +} + +/* + * Is the specified rcu_batch structure empty? + */ +static inline bool rcu_batch_empty(struct rcu_batch *b) +{ + return b->tail == &b->head; +} + +/* + * Remove the callback at the head of the specified rcu_batch structure + * and return a pointer to it, or return NULL if the structure is empty. + */ +static inline struct rcu_head *rcu_batch_dequeue(struct rcu_batch *b) +{ + struct rcu_head *head; + + if (rcu_batch_empty(b)) + return NULL; + + head = b->head; + b->head = head->next; + if (b->tail == &head->next) + rcu_batch_init(b); + + return head; +} + +/* + * Move all callbacks from the rcu_batch structure specified by "from" to + * the structure specified by "to". + */ +static inline void rcu_batch_move(struct rcu_batch *to, struct rcu_batch *from) +{ + if (!rcu_batch_empty(from)) { + *to->tail = from->head; + to->tail = from->tail; + rcu_batch_init(from); + } +} + +static int init_srcu_struct_fields(struct srcu_struct *sp) +{ + sp->completed = 0; + spin_lock_init(&sp->queue_lock); + sp->running = false; + rcu_batch_init(&sp->batch_queue); + rcu_batch_init(&sp->batch_check0); + rcu_batch_init(&sp->batch_check1); + rcu_batch_init(&sp->batch_done); + INIT_DELAYED_WORK(&sp->work, process_srcu); + sp->per_cpu_ref = alloc_percpu(struct srcu_struct_array); + return sp->per_cpu_ref ? 0 : -ENOMEM; +} + +#ifdef CONFIG_DEBUG_LOCK_ALLOC + +int __init_srcu_struct(struct srcu_struct *sp, const char *name, + struct lock_class_key *key) +{ + /* Don't re-initialize a lock while it is held. */ + debug_check_no_locks_freed((void *)sp, sizeof(*sp)); + lockdep_init_map(&sp->dep_map, name, key, 0); + return init_srcu_struct_fields(sp); +} +EXPORT_SYMBOL_GPL(__init_srcu_struct); + +#else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ + +/** + * init_srcu_struct - initialize a sleep-RCU structure + * @sp: structure to initialize. + * + * Must invoke this on a given srcu_struct before passing that srcu_struct + * to any other function. Each srcu_struct represents a separate domain + * of SRCU protection. + */ +int init_srcu_struct(struct srcu_struct *sp) +{ + return init_srcu_struct_fields(sp); +} +EXPORT_SYMBOL_GPL(init_srcu_struct); + +#endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */ + +/* + * Returns approximate total of the readers' ->seq[] values for the + * rank of per-CPU counters specified by idx. + */ +static unsigned long srcu_readers_seq_idx(struct srcu_struct *sp, int idx) +{ + int cpu; + unsigned long sum = 0; + unsigned long t; + + for_each_possible_cpu(cpu) { + t = ACCESS_ONCE(per_cpu_ptr(sp->per_cpu_ref, cpu)->seq[idx]); + sum += t; + } + return sum; +} + +/* + * Returns approximate number of readers active on the specified rank + * of the per-CPU ->c[] counters. + */ +static unsigned long srcu_readers_active_idx(struct srcu_struct *sp, int idx) +{ + int cpu; + unsigned long sum = 0; + unsigned long t; + + for_each_possible_cpu(cpu) { + t = ACCESS_ONCE(per_cpu_ptr(sp->per_cpu_ref, cpu)->c[idx]); + sum += t; + } + return sum; +} + +/* + * Return true if the number of pre-existing readers is determined to + * be stably zero. An example unstable zero can occur if the call + * to srcu_readers_active_idx() misses an __srcu_read_lock() increment, + * but due to task migration, sees the corresponding __srcu_read_unlock() + * decrement. This can happen because srcu_readers_active_idx() takes + * time to sum the array, and might in fact be interrupted or preempted + * partway through the summation. + */ +static bool srcu_readers_active_idx_check(struct srcu_struct *sp, int idx) +{ + unsigned long seq; + + seq = srcu_readers_seq_idx(sp, idx); + + /* + * The following smp_mb() A pairs with the smp_mb() B located in + * __srcu_read_lock(). This pairing ensures that if an + * __srcu_read_lock() increments its counter after the summation + * in srcu_readers_active_idx(), then the corresponding SRCU read-side + * critical section will see any changes made prior to the start + * of the current SRCU grace period. + * + * Also, if the above call to srcu_readers_seq_idx() saw the + * increment of ->seq[], then the call to srcu_readers_active_idx() + * must see the increment of ->c[]. + */ + smp_mb(); /* A */ + + /* + * Note that srcu_readers_active_idx() can incorrectly return + * zero even though there is a pre-existing reader throughout. + * To see this, suppose that task A is in a very long SRCU + * read-side critical section that started on CPU 0, and that + * no other reader exists, so that the sum of the counters + * is equal to one. Then suppose that task B starts executing + * srcu_readers_active_idx(), summing up to CPU 1, and then that + * task C starts reading on CPU 0, so that its increment is not + * summed, but finishes reading on CPU 2, so that its decrement + * -is- summed. Then when task B completes its sum, it will + * incorrectly get zero, despite the fact that task A has been + * in its SRCU read-side critical section the whole time. + * + * We therefore do a validation step should srcu_readers_active_idx() + * return zero. + */ + if (srcu_readers_active_idx(sp, idx) != 0) + return false; + + /* + * The remainder of this function is the validation step. + * The following smp_mb() D pairs with the smp_mb() C in + * __srcu_read_unlock(). If the __srcu_read_unlock() was seen + * by srcu_readers_active_idx() above, then any destructive + * operation performed after the grace period will happen after + * the corresponding SRCU read-side critical section. + * + * Note that there can be at most NR_CPUS worth of readers using + * the old index, which is not enough to overflow even a 32-bit + * integer. (Yes, this does mean that systems having more than + * a billion or so CPUs need to be 64-bit systems.) Therefore, + * the sum of the ->seq[] counters cannot possibly overflow. + * Therefore, the only way that the return values of the two + * calls to srcu_readers_seq_idx() can be equal is if there were + * no increments of the corresponding rank of ->seq[] counts + * in the interim. But the missed-increment scenario laid out + * above includes an increment of the ->seq[] counter by + * the corresponding __srcu_read_lock(). Therefore, if this + * scenario occurs, the return values from the two calls to + * srcu_readers_seq_idx() will differ, and thus the validation + * step below suffices. + */ + smp_mb(); /* D */ + + return srcu_readers_seq_idx(sp, idx) == seq; +} + +/** + * srcu_readers_active - returns approximate number of readers. + * @sp: which srcu_struct to count active readers (holding srcu_read_lock). + * + * Note that this is not an atomic primitive, and can therefore suffer + * severe errors when invoked on an active srcu_struct. That said, it + * can be useful as an error check at cleanup time. + */ +static int srcu_readers_active(struct srcu_struct *sp) +{ + int cpu; + unsigned long sum = 0; + + for_each_possible_cpu(cpu) { + sum += ACCESS_ONCE(per_cpu_ptr(sp->per_cpu_ref, cpu)->c[0]); + sum += ACCESS_ONCE(per_cpu_ptr(sp->per_cpu_ref, cpu)->c[1]); + } + return sum; +} + +/** + * cleanup_srcu_struct - deconstruct a sleep-RCU structure + * @sp: structure to clean up. + * + * Must invoke this after you are finished using a given srcu_struct that + * was initialized via init_srcu_struct(), else you leak memory. + */ +void cleanup_srcu_struct(struct srcu_struct *sp) +{ + if (WARN_ON(srcu_readers_active(sp))) + return; /* Leakage unless caller handles error. */ + free_percpu(sp->per_cpu_ref); + sp->per_cpu_ref = NULL; +} +EXPORT_SYMBOL_GPL(cleanup_srcu_struct); + +/* + * Counts the new reader in the appropriate per-CPU element of the + * srcu_struct. Must be called from process context. + * Returns an index that must be passed to the matching srcu_read_unlock(). + */ +int __srcu_read_lock(struct srcu_struct *sp) +{ + int idx; + + idx = ACCESS_ONCE(sp->completed) & 0x1; + preempt_disable(); + ACCESS_ONCE(this_cpu_ptr(sp->per_cpu_ref)->c[idx]) += 1; + smp_mb(); /* B */ /* Avoid leaking the critical section. */ + ACCESS_ONCE(this_cpu_ptr(sp->per_cpu_ref)->seq[idx]) += 1; + preempt_enable(); + return idx; +} +EXPORT_SYMBOL_GPL(__srcu_read_lock); + +/* + * Removes the count for the old reader from the appropriate per-CPU + * element of the srcu_struct. Note that this may well be a different + * CPU than that which was incremented by the corresponding srcu_read_lock(). + * Must be called from process context. + */ +void __srcu_read_unlock(struct srcu_struct *sp, int idx) +{ + smp_mb(); /* C */ /* Avoid leaking the critical section. */ + this_cpu_dec(sp->per_cpu_ref->c[idx]); +} +EXPORT_SYMBOL_GPL(__srcu_read_unlock); + +/* + * We use an adaptive strategy for synchronize_srcu() and especially for + * synchronize_srcu_expedited(). We spin for a fixed time period + * (defined below) to allow SRCU readers to exit their read-side critical + * sections. If there are still some readers after 10 microseconds, + * we repeatedly block for 1-millisecond time periods. This approach + * has done well in testing, so there is no need for a config parameter. + */ +#define SRCU_RETRY_CHECK_DELAY 5 +#define SYNCHRONIZE_SRCU_TRYCOUNT 2 +#define SYNCHRONIZE_SRCU_EXP_TRYCOUNT 12 + +/* + * @@@ Wait until all pre-existing readers complete. Such readers + * will have used the index specified by "idx". + * the caller should ensures the ->completed is not changed while checking + * and idx = (->completed & 1) ^ 1 + */ +static bool try_check_zero(struct srcu_struct *sp, int idx, int trycount) +{ + for (;;) { + if (srcu_readers_active_idx_check(sp, idx)) + return true; + if (--trycount <= 0) + return false; + udelay(SRCU_RETRY_CHECK_DELAY); + } +} + +/* + * Increment the ->completed counter so that future SRCU readers will + * use the other rank of the ->c[] and ->seq[] arrays. This allows + * us to wait for pre-existing readers in a starvation-free manner. + */ +static void srcu_flip(struct srcu_struct *sp) +{ + sp->completed++; +} + +/* + * Enqueue an SRCU callback on the specified srcu_struct structure, + * initiating grace-period processing if it is not already running. + */ +void call_srcu(struct srcu_struct *sp, struct rcu_head *head, + void (*func)(struct rcu_head *head)) +{ + unsigned long flags; + + head->next = NULL; + head->func = func; + spin_lock_irqsave(&sp->queue_lock, flags); + rcu_batch_queue(&sp->batch_queue, head); + if (!sp->running) { + sp->running = true; + schedule_delayed_work(&sp->work, 0); + } + spin_unlock_irqrestore(&sp->queue_lock, flags); +} +EXPORT_SYMBOL_GPL(call_srcu); + +struct rcu_synchronize { + struct rcu_head head; + struct completion completion; +}; + +/* + * Awaken the corresponding synchronize_srcu() instance now that a + * grace period has elapsed. + */ +static void wakeme_after_rcu(struct rcu_head *head) +{ + struct rcu_synchronize *rcu; + + rcu = container_of(head, struct rcu_synchronize, head); + complete(&rcu->completion); +} + +static void srcu_advance_batches(struct srcu_struct *sp, int trycount); +static void srcu_reschedule(struct srcu_struct *sp); + +/* + * Helper function for synchronize_srcu() and synchronize_srcu_expedited(). + */ +static void __synchronize_srcu(struct srcu_struct *sp, int trycount) +{ + struct rcu_synchronize rcu; + struct rcu_head *head = &rcu.head; + bool done = false; + + rcu_lockdep_assert(!lock_is_held(&sp->dep_map) && + !lock_is_held(&rcu_bh_lock_map) && + !lock_is_held(&rcu_lock_map) && + !lock_is_held(&rcu_sched_lock_map), + "Illegal synchronize_srcu() in same-type SRCU (or RCU) read-side critical section"); + + might_sleep(); + init_completion(&rcu.completion); + + head->next = NULL; + head->func = wakeme_after_rcu; + spin_lock_irq(&sp->queue_lock); + if (!sp->running) { + /* steal the processing owner */ + sp->running = true; + rcu_batch_queue(&sp->batch_check0, head); + spin_unlock_irq(&sp->queue_lock); + + srcu_advance_batches(sp, trycount); + if (!rcu_batch_empty(&sp->batch_done)) { + BUG_ON(sp->batch_done.head != head); + rcu_batch_dequeue(&sp->batch_done); + done = true; + } + /* give the processing owner to work_struct */ + srcu_reschedule(sp); + } else { + rcu_batch_queue(&sp->batch_queue, head); + spin_unlock_irq(&sp->queue_lock); + } + + if (!done) + wait_for_completion(&rcu.completion); +} + +/** + * synchronize_srcu - wait for prior SRCU read-side critical-section completion + * @sp: srcu_struct with which to synchronize. + * + * Wait for the count to drain to zero of both indexes. To avoid the + * possible starvation of synchronize_srcu(), it waits for the count of + * the index=((->completed & 1) ^ 1) to drain to zero at first, + * and then flip the completed and wait for the count of the other index. + * + * Can block; must be called from process context. + * + * Note that it is illegal to call synchronize_srcu() from the corresponding + * SRCU read-side critical section; doing so will result in deadlock. + * However, it is perfectly legal to call synchronize_srcu() on one + * srcu_struct from some other srcu_struct's read-side critical section. + */ +void synchronize_srcu(struct srcu_struct *sp) +{ + __synchronize_srcu(sp, rcu_expedited + ? SYNCHRONIZE_SRCU_EXP_TRYCOUNT + : SYNCHRONIZE_SRCU_TRYCOUNT); +} +EXPORT_SYMBOL_GPL(synchronize_srcu); + +/** + * synchronize_srcu_expedited - Brute-force SRCU grace period + * @sp: srcu_struct with which to synchronize. + * + * Wait for an SRCU grace period to elapse, but be more aggressive about + * spinning rather than blocking when waiting. + * + * Note that it is also illegal to call synchronize_srcu_expedited() + * from the corresponding SRCU read-side critical section; + * doing so will result in deadlock. However, it is perfectly legal + * to call synchronize_srcu_expedited() on one srcu_struct from some + * other srcu_struct's read-side critical section, as long as + * the resulting graph of srcu_structs is acyclic. + */ +void synchronize_srcu_expedited(struct srcu_struct *sp) +{ + __synchronize_srcu(sp, SYNCHRONIZE_SRCU_EXP_TRYCOUNT); +} +EXPORT_SYMBOL_GPL(synchronize_srcu_expedited); + +/** + * srcu_barrier - Wait until all in-flight call_srcu() callbacks complete. + */ +void srcu_barrier(struct srcu_struct *sp) +{ + synchronize_srcu(sp); +} +EXPORT_SYMBOL_GPL(srcu_barrier); + +/** + * srcu_batches_completed - return batches completed. + * @sp: srcu_struct on which to report batch completion. + * + * Report the number of batches, correlated with, but not necessarily + * precisely the same as, the number of grace periods that have elapsed. + */ +long srcu_batches_completed(struct srcu_struct *sp) +{ + return sp->completed; +} +EXPORT_SYMBOL_GPL(srcu_batches_completed); + +#define SRCU_CALLBACK_BATCH 10 +#define SRCU_INTERVAL 1 + +/* + * Move any new SRCU callbacks to the first stage of the SRCU grace + * period pipeline. + */ +static void srcu_collect_new(struct srcu_struct *sp) +{ + if (!rcu_batch_empty(&sp->batch_queue)) { + spin_lock_irq(&sp->queue_lock); + rcu_batch_move(&sp->batch_check0, &sp->batch_queue); + spin_unlock_irq(&sp->queue_lock); + } +} + +/* + * Core SRCU state machine. Advance callbacks from ->batch_check0 to + * ->batch_check1 and then to ->batch_done as readers drain. + */ +static void srcu_advance_batches(struct srcu_struct *sp, int trycount) +{ + int idx = 1 ^ (sp->completed & 1); + + /* + * Because readers might be delayed for an extended period after + * fetching ->completed for their index, at any point in time there + * might well be readers using both idx=0 and idx=1. We therefore + * need to wait for readers to clear from both index values before + * invoking a callback. + */ + + if (rcu_batch_empty(&sp->batch_check0) && + rcu_batch_empty(&sp->batch_check1)) + return; /* no callbacks need to be advanced */ + + if (!try_check_zero(sp, idx, trycount)) + return; /* failed to advance, will try after SRCU_INTERVAL */ + + /* + * The callbacks in ->batch_check1 have already done with their + * first zero check and flip back when they were enqueued on + * ->batch_check0 in a previous invocation of srcu_advance_batches(). + * (Presumably try_check_zero() returned false during that + * invocation, leaving the callbacks stranded on ->batch_check1.) + * They are therefore ready to invoke, so move them to ->batch_done. + */ + rcu_batch_move(&sp->batch_done, &sp->batch_check1); + + if (rcu_batch_empty(&sp->batch_check0)) + return; /* no callbacks need to be advanced */ + srcu_flip(sp); + + /* + * The callbacks in ->batch_check0 just finished their + * first check zero and flip, so move them to ->batch_check1 + * for future checking on the other idx. + */ + rcu_batch_move(&sp->batch_check1, &sp->batch_check0); + + /* + * SRCU read-side critical sections are normally short, so check + * at least twice in quick succession after a flip. + */ + trycount = trycount < 2 ? 2 : trycount; + if (!try_check_zero(sp, idx^1, trycount)) + return; /* failed to advance, will try after SRCU_INTERVAL */ + + /* + * The callbacks in ->batch_check1 have now waited for all + * pre-existing readers using both idx values. They are therefore + * ready to invoke, so move them to ->batch_done. + */ + rcu_batch_move(&sp->batch_done, &sp->batch_check1); +} + +/* + * Invoke a limited number of SRCU callbacks that have passed through + * their grace period. If there are more to do, SRCU will reschedule + * the workqueue. + */ +static void srcu_invoke_callbacks(struct srcu_struct *sp) +{ + int i; + struct rcu_head *head; + + for (i = 0; i < SRCU_CALLBACK_BATCH; i++) { + head = rcu_batch_dequeue(&sp->batch_done); + if (!head) + break; + local_bh_disable(); + head->func(head); + local_bh_enable(); + } +} + +/* + * Finished one round of SRCU grace period. Start another if there are + * more SRCU callbacks queued, otherwise put SRCU into not-running state. + */ +static void srcu_reschedule(struct srcu_struct *sp) +{ + bool pending = true; + + if (rcu_batch_empty(&sp->batch_done) && + rcu_batch_empty(&sp->batch_check1) && + rcu_batch_empty(&sp->batch_check0) && + rcu_batch_empty(&sp->batch_queue)) { + spin_lock_irq(&sp->queue_lock); + if (rcu_batch_empty(&sp->batch_done) && + rcu_batch_empty(&sp->batch_check1) && + rcu_batch_empty(&sp->batch_check0) && + rcu_batch_empty(&sp->batch_queue)) { + sp->running = false; + pending = false; + } + spin_unlock_irq(&sp->queue_lock); + } + + if (pending) + schedule_delayed_work(&sp->work, SRCU_INTERVAL); +} + +/* + * This is the work-queue function that handles SRCU grace periods. + */ +void process_srcu(struct work_struct *work) +{ + struct srcu_struct *sp; + + sp = container_of(work, struct srcu_struct, work.work); + + srcu_collect_new(sp); + srcu_advance_batches(sp, 1); + srcu_invoke_callbacks(sp); + srcu_reschedule(sp); +} +EXPORT_SYMBOL_GPL(process_srcu); diff --git a/kernel/rcu/tiny.c b/kernel/rcu/tiny.c new file mode 100644 index 0000000..0c9a934 --- /dev/null +++ b/kernel/rcu/tiny.c @@ -0,0 +1,388 @@ +/* + * Read-Copy Update mechanism for mutual exclusion, the Bloatwatch edition. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright IBM Corporation, 2008 + * + * Author: Paul E. McKenney + * + * For detailed explanation of Read-Copy Update mechanism see - + * Documentation/RCU + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_RCU_TRACE +#include +#endif /* #else #ifdef CONFIG_RCU_TRACE */ + +#include "rcu.h" + +/* Forward declarations for tiny_plugin.h. */ +struct rcu_ctrlblk; +static void __rcu_process_callbacks(struct rcu_ctrlblk *rcp); +static void rcu_process_callbacks(struct softirq_action *unused); +static void __call_rcu(struct rcu_head *head, + void (*func)(struct rcu_head *rcu), + struct rcu_ctrlblk *rcp); + +static long long rcu_dynticks_nesting = DYNTICK_TASK_EXIT_IDLE; + +#include "tiny_plugin.h" + +/* Common code for rcu_idle_enter() and rcu_irq_exit(), see kernel/rcutree.c. */ +static void rcu_idle_enter_common(long long newval) +{ + if (newval) { + RCU_TRACE(trace_rcu_dyntick(TPS("--="), + rcu_dynticks_nesting, newval)); + rcu_dynticks_nesting = newval; + return; + } + RCU_TRACE(trace_rcu_dyntick(TPS("Start"), + rcu_dynticks_nesting, newval)); + if (!is_idle_task(current)) { + struct task_struct *idle __maybe_unused = idle_task(smp_processor_id()); + + RCU_TRACE(trace_rcu_dyntick(TPS("Entry error: not idle task"), + rcu_dynticks_nesting, newval)); + ftrace_dump(DUMP_ALL); + WARN_ONCE(1, "Current pid: %d comm: %s / Idle pid: %d comm: %s", + current->pid, current->comm, + idle->pid, idle->comm); /* must be idle task! */ + } + rcu_sched_qs(0); /* implies rcu_bh_qsctr_inc(0) */ + barrier(); + rcu_dynticks_nesting = newval; +} + +/* + * Enter idle, which is an extended quiescent state if we have fully + * entered that mode (i.e., if the new value of dynticks_nesting is zero). + */ +void rcu_idle_enter(void) +{ + unsigned long flags; + long long newval; + + local_irq_save(flags); + WARN_ON_ONCE((rcu_dynticks_nesting & DYNTICK_TASK_NEST_MASK) == 0); + if ((rcu_dynticks_nesting & DYNTICK_TASK_NEST_MASK) == + DYNTICK_TASK_NEST_VALUE) + newval = 0; + else + newval = rcu_dynticks_nesting - DYNTICK_TASK_NEST_VALUE; + rcu_idle_enter_common(newval); + local_irq_restore(flags); +} +EXPORT_SYMBOL_GPL(rcu_idle_enter); + +/* + * Exit an interrupt handler towards idle. + */ +void rcu_irq_exit(void) +{ + unsigned long flags; + long long newval; + + local_irq_save(flags); + newval = rcu_dynticks_nesting - 1; + WARN_ON_ONCE(newval < 0); + rcu_idle_enter_common(newval); + local_irq_restore(flags); +} +EXPORT_SYMBOL_GPL(rcu_irq_exit); + +/* Common code for rcu_idle_exit() and rcu_irq_enter(), see kernel/rcutree.c. */ +static void rcu_idle_exit_common(long long oldval) +{ + if (oldval) { + RCU_TRACE(trace_rcu_dyntick(TPS("++="), + oldval, rcu_dynticks_nesting)); + return; + } + RCU_TRACE(trace_rcu_dyntick(TPS("End"), oldval, rcu_dynticks_nesting)); + if (!is_idle_task(current)) { + struct task_struct *idle __maybe_unused = idle_task(smp_processor_id()); + + RCU_TRACE(trace_rcu_dyntick(TPS("Exit error: not idle task"), + oldval, rcu_dynticks_nesting)); + ftrace_dump(DUMP_ALL); + WARN_ONCE(1, "Current pid: %d comm: %s / Idle pid: %d comm: %s", + current->pid, current->comm, + idle->pid, idle->comm); /* must be idle task! */ + } +} + +/* + * Exit idle, so that we are no longer in an extended quiescent state. + */ +void rcu_idle_exit(void) +{ + unsigned long flags; + long long oldval; + + local_irq_save(flags); + oldval = rcu_dynticks_nesting; + WARN_ON_ONCE(rcu_dynticks_nesting < 0); + if (rcu_dynticks_nesting & DYNTICK_TASK_NEST_MASK) + rcu_dynticks_nesting += DYNTICK_TASK_NEST_VALUE; + else + rcu_dynticks_nesting = DYNTICK_TASK_EXIT_IDLE; + rcu_idle_exit_common(oldval); + local_irq_restore(flags); +} +EXPORT_SYMBOL_GPL(rcu_idle_exit); + +/* + * Enter an interrupt handler, moving away from idle. + */ +void rcu_irq_enter(void) +{ + unsigned long flags; + long long oldval; + + local_irq_save(flags); + oldval = rcu_dynticks_nesting; + rcu_dynticks_nesting++; + WARN_ON_ONCE(rcu_dynticks_nesting == 0); + rcu_idle_exit_common(oldval); + local_irq_restore(flags); +} +EXPORT_SYMBOL_GPL(rcu_irq_enter); + +#if defined(CONFIG_DEBUG_LOCK_ALLOC) || defined(CONFIG_RCU_TRACE) + +/* + * Test whether RCU thinks that the current CPU is idle. + */ +bool __rcu_is_watching(void) +{ + return rcu_dynticks_nesting; +} +EXPORT_SYMBOL(__rcu_is_watching); + +#endif /* defined(CONFIG_DEBUG_LOCK_ALLOC) || defined(CONFIG_RCU_TRACE) */ + +/* + * Test whether the current CPU was interrupted from idle. Nested + * interrupts don't count, we must be running at the first interrupt + * level. + */ +static int rcu_is_cpu_rrupt_from_idle(void) +{ + return rcu_dynticks_nesting <= 1; +} + +/* + * Helper function for rcu_sched_qs() and rcu_bh_qs(). + * Also irqs are disabled to avoid confusion due to interrupt handlers + * invoking call_rcu(). + */ +static int rcu_qsctr_help(struct rcu_ctrlblk *rcp) +{ + RCU_TRACE(reset_cpu_stall_ticks(rcp)); + if (rcp->rcucblist != NULL && + rcp->donetail != rcp->curtail) { + rcp->donetail = rcp->curtail; + return 1; + } + + return 0; +} + +/* + * Record an rcu quiescent state. And an rcu_bh quiescent state while we + * are at it, given that any rcu quiescent state is also an rcu_bh + * quiescent state. Use "+" instead of "||" to defeat short circuiting. + */ +void rcu_sched_qs(int cpu) +{ + unsigned long flags; + + local_irq_save(flags); + if (rcu_qsctr_help(&rcu_sched_ctrlblk) + + rcu_qsctr_help(&rcu_bh_ctrlblk)) + raise_softirq(RCU_SOFTIRQ); + local_irq_restore(flags); +} + +/* + * Record an rcu_bh quiescent state. + */ +void rcu_bh_qs(int cpu) +{ + unsigned long flags; + + local_irq_save(flags); + if (rcu_qsctr_help(&rcu_bh_ctrlblk)) + raise_softirq(RCU_SOFTIRQ); + local_irq_restore(flags); +} + +/* + * Check to see if the scheduling-clock interrupt came from an extended + * quiescent state, and, if so, tell RCU about it. This function must + * be called from hardirq context. It is normally called from the + * scheduling-clock interrupt. + */ +void rcu_check_callbacks(int cpu, int user) +{ + RCU_TRACE(check_cpu_stalls()); + if (user || rcu_is_cpu_rrupt_from_idle()) + rcu_sched_qs(cpu); + else if (!in_softirq()) + rcu_bh_qs(cpu); +} + +/* + * Invoke the RCU callbacks on the specified rcu_ctrlkblk structure + * whose grace period has elapsed. + */ +static void __rcu_process_callbacks(struct rcu_ctrlblk *rcp) +{ + const char *rn = NULL; + struct rcu_head *next, *list; + unsigned long flags; + RCU_TRACE(int cb_count = 0); + + /* If no RCU callbacks ready to invoke, just return. */ + if (&rcp->rcucblist == rcp->donetail) { + RCU_TRACE(trace_rcu_batch_start(rcp->name, 0, 0, -1)); + RCU_TRACE(trace_rcu_batch_end(rcp->name, 0, + !!ACCESS_ONCE(rcp->rcucblist), + need_resched(), + is_idle_task(current), + false)); + return; + } + + /* Move the ready-to-invoke callbacks to a local list. */ + local_irq_save(flags); + RCU_TRACE(trace_rcu_batch_start(rcp->name, 0, rcp->qlen, -1)); + list = rcp->rcucblist; + rcp->rcucblist = *rcp->donetail; + *rcp->donetail = NULL; + if (rcp->curtail == rcp->donetail) + rcp->curtail = &rcp->rcucblist; + rcp->donetail = &rcp->rcucblist; + local_irq_restore(flags); + + /* Invoke the callbacks on the local list. */ + RCU_TRACE(rn = rcp->name); + while (list) { + next = list->next; + prefetch(next); + debug_rcu_head_unqueue(list); + local_bh_disable(); + __rcu_reclaim(rn, list); + local_bh_enable(); + list = next; + RCU_TRACE(cb_count++); + } + RCU_TRACE(rcu_trace_sub_qlen(rcp, cb_count)); + RCU_TRACE(trace_rcu_batch_end(rcp->name, + cb_count, 0, need_resched(), + is_idle_task(current), + false)); +} + +static void rcu_process_callbacks(struct softirq_action *unused) +{ + __rcu_process_callbacks(&rcu_sched_ctrlblk); + __rcu_process_callbacks(&rcu_bh_ctrlblk); +} + +/* + * Wait for a grace period to elapse. But it is illegal to invoke + * synchronize_sched() from within an RCU read-side critical section. + * Therefore, any legal call to synchronize_sched() is a quiescent + * state, and so on a UP system, synchronize_sched() need do nothing. + * Ditto for synchronize_rcu_bh(). (But Lai Jiangshan points out the + * benefits of doing might_sleep() to reduce latency.) + * + * Cool, huh? (Due to Josh Triplett.) + * + * But we want to make this a static inline later. The cond_resched() + * currently makes this problematic. + */ +void synchronize_sched(void) +{ + rcu_lockdep_assert(!lock_is_held(&rcu_bh_lock_map) && + !lock_is_held(&rcu_lock_map) && + !lock_is_held(&rcu_sched_lock_map), + "Illegal synchronize_sched() in RCU read-side critical section"); + cond_resched(); +} +EXPORT_SYMBOL_GPL(synchronize_sched); + +/* + * Helper function for call_rcu() and call_rcu_bh(). + */ +static void __call_rcu(struct rcu_head *head, + void (*func)(struct rcu_head *rcu), + struct rcu_ctrlblk *rcp) +{ + unsigned long flags; + + debug_rcu_head_queue(head); + head->func = func; + head->next = NULL; + + local_irq_save(flags); + *rcp->curtail = head; + rcp->curtail = &head->next; + RCU_TRACE(rcp->qlen++); + local_irq_restore(flags); +} + +/* + * Post an RCU callback to be invoked after the end of an RCU-sched grace + * period. But since we have but one CPU, that would be after any + * quiescent state. + */ +void call_rcu_sched(struct rcu_head *head, void (*func)(struct rcu_head *rcu)) +{ + __call_rcu(head, func, &rcu_sched_ctrlblk); +} +EXPORT_SYMBOL_GPL(call_rcu_sched); + +/* + * Post an RCU bottom-half callback to be invoked after any subsequent + * quiescent state. + */ +void call_rcu_bh(struct rcu_head *head, void (*func)(struct rcu_head *rcu)) +{ + __call_rcu(head, func, &rcu_bh_ctrlblk); +} +EXPORT_SYMBOL_GPL(call_rcu_bh); + +void rcu_init(void) +{ + open_softirq(RCU_SOFTIRQ, rcu_process_callbacks); +} diff --git a/kernel/rcu/tiny_plugin.h b/kernel/rcu/tiny_plugin.h new file mode 100644 index 0000000..280d06c --- /dev/null +++ b/kernel/rcu/tiny_plugin.h @@ -0,0 +1,174 @@ +/* + * Read-Copy Update mechanism for mutual exclusion, the Bloatwatch edition + * Internal non-public definitions that provide either classic + * or preemptible semantics. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (c) 2010 Linaro + * + * Author: Paul E. McKenney + */ + +#include +#include +#include +#include + +/* Global control variables for rcupdate callback mechanism. */ +struct rcu_ctrlblk { + struct rcu_head *rcucblist; /* List of pending callbacks (CBs). */ + struct rcu_head **donetail; /* ->next pointer of last "done" CB. */ + struct rcu_head **curtail; /* ->next pointer of last CB. */ + RCU_TRACE(long qlen); /* Number of pending CBs. */ + RCU_TRACE(unsigned long gp_start); /* Start time for stalls. */ + RCU_TRACE(unsigned long ticks_this_gp); /* Statistic for stalls. */ + RCU_TRACE(unsigned long jiffies_stall); /* Jiffies at next stall. */ + RCU_TRACE(const char *name); /* Name of RCU type. */ +}; + +/* Definition for rcupdate control block. */ +static struct rcu_ctrlblk rcu_sched_ctrlblk = { + .donetail = &rcu_sched_ctrlblk.rcucblist, + .curtail = &rcu_sched_ctrlblk.rcucblist, + RCU_TRACE(.name = "rcu_sched") +}; + +static struct rcu_ctrlblk rcu_bh_ctrlblk = { + .donetail = &rcu_bh_ctrlblk.rcucblist, + .curtail = &rcu_bh_ctrlblk.rcucblist, + RCU_TRACE(.name = "rcu_bh") +}; + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +#include + +int rcu_scheduler_active __read_mostly; +EXPORT_SYMBOL_GPL(rcu_scheduler_active); + +/* + * During boot, we forgive RCU lockdep issues. After this function is + * invoked, we start taking RCU lockdep issues seriously. + */ +void __init rcu_scheduler_starting(void) +{ + WARN_ON(nr_context_switches() > 0); + rcu_scheduler_active = 1; +} + +#endif /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ + +#ifdef CONFIG_RCU_TRACE + +static void rcu_trace_sub_qlen(struct rcu_ctrlblk *rcp, int n) +{ + unsigned long flags; + + local_irq_save(flags); + rcp->qlen -= n; + local_irq_restore(flags); +} + +/* + * Dump statistics for TINY_RCU, such as they are. + */ +static int show_tiny_stats(struct seq_file *m, void *unused) +{ + seq_printf(m, "rcu_sched: qlen: %ld\n", rcu_sched_ctrlblk.qlen); + seq_printf(m, "rcu_bh: qlen: %ld\n", rcu_bh_ctrlblk.qlen); + return 0; +} + +static int show_tiny_stats_open(struct inode *inode, struct file *file) +{ + return single_open(file, show_tiny_stats, NULL); +} + +static const struct file_operations show_tiny_stats_fops = { + .owner = THIS_MODULE, + .open = show_tiny_stats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct dentry *rcudir; + +static int __init rcutiny_trace_init(void) +{ + struct dentry *retval; + + rcudir = debugfs_create_dir("rcu", NULL); + if (!rcudir) + goto free_out; + retval = debugfs_create_file("rcudata", 0444, rcudir, + NULL, &show_tiny_stats_fops); + if (!retval) + goto free_out; + return 0; +free_out: + debugfs_remove_recursive(rcudir); + return 1; +} + +static void __exit rcutiny_trace_cleanup(void) +{ + debugfs_remove_recursive(rcudir); +} + +module_init(rcutiny_trace_init); +module_exit(rcutiny_trace_cleanup); + +MODULE_AUTHOR("Paul E. McKenney"); +MODULE_DESCRIPTION("Read-Copy Update tracing for tiny implementation"); +MODULE_LICENSE("GPL"); + +static void check_cpu_stall(struct rcu_ctrlblk *rcp) +{ + unsigned long j; + unsigned long js; + + if (rcu_cpu_stall_suppress) + return; + rcp->ticks_this_gp++; + j = jiffies; + js = rcp->jiffies_stall; + if (*rcp->curtail && ULONG_CMP_GE(j, js)) { + pr_err("INFO: %s stall on CPU (%lu ticks this GP) idle=%llx (t=%lu jiffies q=%ld)\n", + rcp->name, rcp->ticks_this_gp, rcu_dynticks_nesting, + jiffies - rcp->gp_start, rcp->qlen); + dump_stack(); + } + if (*rcp->curtail && ULONG_CMP_GE(j, js)) + rcp->jiffies_stall = jiffies + + 3 * rcu_jiffies_till_stall_check() + 3; + else if (ULONG_CMP_GE(j, js)) + rcp->jiffies_stall = jiffies + rcu_jiffies_till_stall_check(); +} + +static void reset_cpu_stall_ticks(struct rcu_ctrlblk *rcp) +{ + rcp->ticks_this_gp = 0; + rcp->gp_start = jiffies; + rcp->jiffies_stall = jiffies + rcu_jiffies_till_stall_check(); +} + +static void check_cpu_stalls(void) +{ + RCU_TRACE(check_cpu_stall(&rcu_bh_ctrlblk)); + RCU_TRACE(check_cpu_stall(&rcu_sched_ctrlblk)); +} + +#endif /* #ifdef CONFIG_RCU_TRACE */ diff --git a/kernel/rcu/torture.c b/kernel/rcu/torture.c new file mode 100644 index 0000000..3929cd4 --- /dev/null +++ b/kernel/rcu/torture.c @@ -0,0 +1,2145 @@ +/* + * Read-Copy Update module-based torture test facility + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2005, 2006 + * + * Authors: Paul E. McKenney + * Josh Triplett + * + * See also: Documentation/RCU/torture.txt + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Paul E. McKenney and Josh Triplett "); + +MODULE_ALIAS("rcutorture"); +#ifdef MODULE_PARAM_PREFIX +#undef MODULE_PARAM_PREFIX +#endif +#define MODULE_PARAM_PREFIX "rcutorture." + +static int fqs_duration; +module_param(fqs_duration, int, 0444); +MODULE_PARM_DESC(fqs_duration, "Duration of fqs bursts (us), 0 to disable"); +static int fqs_holdoff; +module_param(fqs_holdoff, int, 0444); +MODULE_PARM_DESC(fqs_holdoff, "Holdoff time within fqs bursts (us)"); +static int fqs_stutter = 3; +module_param(fqs_stutter, int, 0444); +MODULE_PARM_DESC(fqs_stutter, "Wait time between fqs bursts (s)"); +static bool gp_exp; +module_param(gp_exp, bool, 0444); +MODULE_PARM_DESC(gp_exp, "Use expedited GP wait primitives"); +static bool gp_normal; +module_param(gp_normal, bool, 0444); +MODULE_PARM_DESC(gp_normal, "Use normal (non-expedited) GP wait primitives"); +static int irqreader = 1; +module_param(irqreader, int, 0444); +MODULE_PARM_DESC(irqreader, "Allow RCU readers from irq handlers"); +static int n_barrier_cbs; +module_param(n_barrier_cbs, int, 0444); +MODULE_PARM_DESC(n_barrier_cbs, "# of callbacks/kthreads for barrier testing"); +static int nfakewriters = 4; +module_param(nfakewriters, int, 0444); +MODULE_PARM_DESC(nfakewriters, "Number of RCU fake writer threads"); +static int nreaders = -1; +module_param(nreaders, int, 0444); +MODULE_PARM_DESC(nreaders, "Number of RCU reader threads"); +static int object_debug; +module_param(object_debug, int, 0444); +MODULE_PARM_DESC(object_debug, "Enable debug-object double call_rcu() testing"); +static int onoff_holdoff; +module_param(onoff_holdoff, int, 0444); +MODULE_PARM_DESC(onoff_holdoff, "Time after boot before CPU hotplugs (s)"); +static int onoff_interval; +module_param(onoff_interval, int, 0444); +MODULE_PARM_DESC(onoff_interval, "Time between CPU hotplugs (s), 0=disable"); +static int shuffle_interval = 3; +module_param(shuffle_interval, int, 0444); +MODULE_PARM_DESC(shuffle_interval, "Number of seconds between shuffles"); +static int shutdown_secs; +module_param(shutdown_secs, int, 0444); +MODULE_PARM_DESC(shutdown_secs, "Shutdown time (s), <= zero to disable."); +static int stall_cpu; +module_param(stall_cpu, int, 0444); +MODULE_PARM_DESC(stall_cpu, "Stall duration (s), zero to disable."); +static int stall_cpu_holdoff = 10; +module_param(stall_cpu_holdoff, int, 0444); +MODULE_PARM_DESC(stall_cpu_holdoff, "Time to wait before starting stall (s)."); +static int stat_interval = 60; +module_param(stat_interval, int, 0644); +MODULE_PARM_DESC(stat_interval, "Number of seconds between stats printk()s"); +static int stutter = 5; +module_param(stutter, int, 0444); +MODULE_PARM_DESC(stutter, "Number of seconds to run/halt test"); +static int test_boost = 1; +module_param(test_boost, int, 0444); +MODULE_PARM_DESC(test_boost, "Test RCU prio boost: 0=no, 1=maybe, 2=yes."); +static int test_boost_duration = 4; +module_param(test_boost_duration, int, 0444); +MODULE_PARM_DESC(test_boost_duration, "Duration of each boost test, seconds."); +static int test_boost_interval = 7; +module_param(test_boost_interval, int, 0444); +MODULE_PARM_DESC(test_boost_interval, "Interval between boost tests, seconds."); +static bool test_no_idle_hz = true; +module_param(test_no_idle_hz, bool, 0444); +MODULE_PARM_DESC(test_no_idle_hz, "Test support for tickless idle CPUs"); +static char *torture_type = "rcu"; +module_param(torture_type, charp, 0444); +MODULE_PARM_DESC(torture_type, "Type of RCU to torture (rcu, rcu_bh, ...)"); +static bool verbose; +module_param(verbose, bool, 0444); +MODULE_PARM_DESC(verbose, "Enable verbose debugging printk()s"); + +#define TORTURE_FLAG "-torture:" +#define PRINTK_STRING(s) \ + do { pr_alert("%s" TORTURE_FLAG s "\n", torture_type); } while (0) +#define VERBOSE_PRINTK_STRING(s) \ + do { if (verbose) pr_alert("%s" TORTURE_FLAG s "\n", torture_type); } while (0) +#define VERBOSE_PRINTK_ERRSTRING(s) \ + do { if (verbose) pr_alert("%s" TORTURE_FLAG "!!! " s "\n", torture_type); } while (0) + +static char printk_buf[4096]; + +static int nrealreaders; +static struct task_struct *writer_task; +static struct task_struct **fakewriter_tasks; +static struct task_struct **reader_tasks; +static struct task_struct *stats_task; +static struct task_struct *shuffler_task; +static struct task_struct *stutter_task; +static struct task_struct *fqs_task; +static struct task_struct *boost_tasks[NR_CPUS]; +static struct task_struct *shutdown_task; +#ifdef CONFIG_HOTPLUG_CPU +static struct task_struct *onoff_task; +#endif /* #ifdef CONFIG_HOTPLUG_CPU */ +static struct task_struct *stall_task; +static struct task_struct **barrier_cbs_tasks; +static struct task_struct *barrier_task; + +#define RCU_TORTURE_PIPE_LEN 10 + +struct rcu_torture { + struct rcu_head rtort_rcu; + int rtort_pipe_count; + struct list_head rtort_free; + int rtort_mbtest; +}; + +static LIST_HEAD(rcu_torture_freelist); +static struct rcu_torture __rcu *rcu_torture_current; +static unsigned long rcu_torture_current_version; +static struct rcu_torture rcu_tortures[10 * RCU_TORTURE_PIPE_LEN]; +static DEFINE_SPINLOCK(rcu_torture_lock); +static DEFINE_PER_CPU(long [RCU_TORTURE_PIPE_LEN + 1], rcu_torture_count) = + { 0 }; +static DEFINE_PER_CPU(long [RCU_TORTURE_PIPE_LEN + 1], rcu_torture_batch) = + { 0 }; +static atomic_t rcu_torture_wcount[RCU_TORTURE_PIPE_LEN + 1]; +static atomic_t n_rcu_torture_alloc; +static atomic_t n_rcu_torture_alloc_fail; +static atomic_t n_rcu_torture_free; +static atomic_t n_rcu_torture_mberror; +static atomic_t n_rcu_torture_error; +static long n_rcu_torture_barrier_error; +static long n_rcu_torture_boost_ktrerror; +static long n_rcu_torture_boost_rterror; +static long n_rcu_torture_boost_failure; +static long n_rcu_torture_boosts; +static long n_rcu_torture_timers; +static long n_offline_attempts; +static long n_offline_successes; +static unsigned long sum_offline; +static int min_offline = -1; +static int max_offline; +static long n_online_attempts; +static long n_online_successes; +static unsigned long sum_online; +static int min_online = -1; +static int max_online; +static long n_barrier_attempts; +static long n_barrier_successes; +static struct list_head rcu_torture_removed; +static cpumask_var_t shuffle_tmp_mask; + +static int stutter_pause_test; + +#if defined(MODULE) || defined(CONFIG_RCU_TORTURE_TEST_RUNNABLE) +#define RCUTORTURE_RUNNABLE_INIT 1 +#else +#define RCUTORTURE_RUNNABLE_INIT 0 +#endif +int rcutorture_runnable = RCUTORTURE_RUNNABLE_INIT; +module_param(rcutorture_runnable, int, 0444); +MODULE_PARM_DESC(rcutorture_runnable, "Start rcutorture at boot"); + +#if defined(CONFIG_RCU_BOOST) && !defined(CONFIG_HOTPLUG_CPU) +#define rcu_can_boost() 1 +#else /* #if defined(CONFIG_RCU_BOOST) && !defined(CONFIG_HOTPLUG_CPU) */ +#define rcu_can_boost() 0 +#endif /* #else #if defined(CONFIG_RCU_BOOST) && !defined(CONFIG_HOTPLUG_CPU) */ + +#ifdef CONFIG_RCU_TRACE +static u64 notrace rcu_trace_clock_local(void) +{ + u64 ts = trace_clock_local(); + unsigned long __maybe_unused ts_rem = do_div(ts, NSEC_PER_USEC); + return ts; +} +#else /* #ifdef CONFIG_RCU_TRACE */ +static u64 notrace rcu_trace_clock_local(void) +{ + return 0ULL; +} +#endif /* #else #ifdef CONFIG_RCU_TRACE */ + +static unsigned long shutdown_time; /* jiffies to system shutdown. */ +static unsigned long boost_starttime; /* jiffies of next boost test start. */ +DEFINE_MUTEX(boost_mutex); /* protect setting boost_starttime */ + /* and boost task create/destroy. */ +static atomic_t barrier_cbs_count; /* Barrier callbacks registered. */ +static bool barrier_phase; /* Test phase. */ +static atomic_t barrier_cbs_invoked; /* Barrier callbacks invoked. */ +static wait_queue_head_t *barrier_cbs_wq; /* Coordinate barrier testing. */ +static DECLARE_WAIT_QUEUE_HEAD(barrier_wq); + +/* Mediate rmmod and system shutdown. Concurrent rmmod & shutdown illegal! */ + +#define FULLSTOP_DONTSTOP 0 /* Normal operation. */ +#define FULLSTOP_SHUTDOWN 1 /* System shutdown with rcutorture running. */ +#define FULLSTOP_RMMOD 2 /* Normal rmmod of rcutorture. */ +static int fullstop = FULLSTOP_RMMOD; +/* + * Protect fullstop transitions and spawning of kthreads. + */ +static DEFINE_MUTEX(fullstop_mutex); + +/* Forward reference. */ +static void rcu_torture_cleanup(void); + +/* + * Detect and respond to a system shutdown. + */ +static int +rcutorture_shutdown_notify(struct notifier_block *unused1, + unsigned long unused2, void *unused3) +{ + mutex_lock(&fullstop_mutex); + if (fullstop == FULLSTOP_DONTSTOP) + fullstop = FULLSTOP_SHUTDOWN; + else + pr_warn(/* but going down anyway, so... */ + "Concurrent 'rmmod rcutorture' and shutdown illegal!\n"); + mutex_unlock(&fullstop_mutex); + return NOTIFY_DONE; +} + +/* + * Absorb kthreads into a kernel function that won't return, so that + * they won't ever access module text or data again. + */ +static void rcutorture_shutdown_absorb(const char *title) +{ + if (ACCESS_ONCE(fullstop) == FULLSTOP_SHUTDOWN) { + pr_notice( + "rcutorture thread %s parking due to system shutdown\n", + title); + schedule_timeout_uninterruptible(MAX_SCHEDULE_TIMEOUT); + } +} + +/* + * Allocate an element from the rcu_tortures pool. + */ +static struct rcu_torture * +rcu_torture_alloc(void) +{ + struct list_head *p; + + spin_lock_bh(&rcu_torture_lock); + if (list_empty(&rcu_torture_freelist)) { + atomic_inc(&n_rcu_torture_alloc_fail); + spin_unlock_bh(&rcu_torture_lock); + return NULL; + } + atomic_inc(&n_rcu_torture_alloc); + p = rcu_torture_freelist.next; + list_del_init(p); + spin_unlock_bh(&rcu_torture_lock); + return container_of(p, struct rcu_torture, rtort_free); +} + +/* + * Free an element to the rcu_tortures pool. + */ +static void +rcu_torture_free(struct rcu_torture *p) +{ + atomic_inc(&n_rcu_torture_free); + spin_lock_bh(&rcu_torture_lock); + list_add_tail(&p->rtort_free, &rcu_torture_freelist); + spin_unlock_bh(&rcu_torture_lock); +} + +struct rcu_random_state { + unsigned long rrs_state; + long rrs_count; +}; + +#define RCU_RANDOM_MULT 39916801 /* prime */ +#define RCU_RANDOM_ADD 479001701 /* prime */ +#define RCU_RANDOM_REFRESH 10000 + +#define DEFINE_RCU_RANDOM(name) struct rcu_random_state name = { 0, 0 } + +/* + * Crude but fast random-number generator. Uses a linear congruential + * generator, with occasional help from cpu_clock(). + */ +static unsigned long +rcu_random(struct rcu_random_state *rrsp) +{ + if (--rrsp->rrs_count < 0) { + rrsp->rrs_state += (unsigned long)local_clock(); + rrsp->rrs_count = RCU_RANDOM_REFRESH; + } + rrsp->rrs_state = rrsp->rrs_state * RCU_RANDOM_MULT + RCU_RANDOM_ADD; + return swahw32(rrsp->rrs_state); +} + +static void +rcu_stutter_wait(const char *title) +{ + while (stutter_pause_test || !rcutorture_runnable) { + if (rcutorture_runnable) + schedule_timeout_interruptible(1); + else + schedule_timeout_interruptible(round_jiffies_relative(HZ)); + rcutorture_shutdown_absorb(title); + } +} + +/* + * Operations vector for selecting different types of tests. + */ + +struct rcu_torture_ops { + void (*init)(void); + int (*readlock)(void); + void (*read_delay)(struct rcu_random_state *rrsp); + void (*readunlock)(int idx); + int (*completed)(void); + void (*deferred_free)(struct rcu_torture *p); + void (*sync)(void); + void (*exp_sync)(void); + void (*call)(struct rcu_head *head, void (*func)(struct rcu_head *rcu)); + void (*cb_barrier)(void); + void (*fqs)(void); + int (*stats)(char *page); + int irq_capable; + int can_boost; + const char *name; +}; + +static struct rcu_torture_ops *cur_ops; + +/* + * Definitions for rcu torture testing. + */ + +static int rcu_torture_read_lock(void) __acquires(RCU) +{ + rcu_read_lock(); + return 0; +} + +static void rcu_read_delay(struct rcu_random_state *rrsp) +{ + const unsigned long shortdelay_us = 200; + const unsigned long longdelay_ms = 50; + + /* We want a short delay sometimes to make a reader delay the grace + * period, and we want a long delay occasionally to trigger + * force_quiescent_state. */ + + if (!(rcu_random(rrsp) % (nrealreaders * 2000 * longdelay_ms))) + mdelay(longdelay_ms); + if (!(rcu_random(rrsp) % (nrealreaders * 2 * shortdelay_us))) + udelay(shortdelay_us); +#ifdef CONFIG_PREEMPT + if (!preempt_count() && !(rcu_random(rrsp) % (nrealreaders * 20000))) + preempt_schedule(); /* No QS if preempt_disable() in effect */ +#endif +} + +static void rcu_torture_read_unlock(int idx) __releases(RCU) +{ + rcu_read_unlock(); +} + +static int rcu_torture_completed(void) +{ + return rcu_batches_completed(); +} + +static void +rcu_torture_cb(struct rcu_head *p) +{ + int i; + struct rcu_torture *rp = container_of(p, struct rcu_torture, rtort_rcu); + + if (fullstop != FULLSTOP_DONTSTOP) { + /* Test is ending, just drop callbacks on the floor. */ + /* The next initialization will pick up the pieces. */ + return; + } + i = rp->rtort_pipe_count; + if (i > RCU_TORTURE_PIPE_LEN) + i = RCU_TORTURE_PIPE_LEN; + atomic_inc(&rcu_torture_wcount[i]); + if (++rp->rtort_pipe_count >= RCU_TORTURE_PIPE_LEN) { + rp->rtort_mbtest = 0; + rcu_torture_free(rp); + } else { + cur_ops->deferred_free(rp); + } +} + +static int rcu_no_completed(void) +{ + return 0; +} + +static void rcu_torture_deferred_free(struct rcu_torture *p) +{ + call_rcu(&p->rtort_rcu, rcu_torture_cb); +} + +static void rcu_sync_torture_init(void) +{ + INIT_LIST_HEAD(&rcu_torture_removed); +} + +static struct rcu_torture_ops rcu_ops = { + .init = rcu_sync_torture_init, + .readlock = rcu_torture_read_lock, + .read_delay = rcu_read_delay, + .readunlock = rcu_torture_read_unlock, + .completed = rcu_torture_completed, + .deferred_free = rcu_torture_deferred_free, + .sync = synchronize_rcu, + .exp_sync = synchronize_rcu_expedited, + .call = call_rcu, + .cb_barrier = rcu_barrier, + .fqs = rcu_force_quiescent_state, + .stats = NULL, + .irq_capable = 1, + .can_boost = rcu_can_boost(), + .name = "rcu" +}; + +/* + * Definitions for rcu_bh torture testing. + */ + +static int rcu_bh_torture_read_lock(void) __acquires(RCU_BH) +{ + rcu_read_lock_bh(); + return 0; +} + +static void rcu_bh_torture_read_unlock(int idx) __releases(RCU_BH) +{ + rcu_read_unlock_bh(); +} + +static int rcu_bh_torture_completed(void) +{ + return rcu_batches_completed_bh(); +} + +static void rcu_bh_torture_deferred_free(struct rcu_torture *p) +{ + call_rcu_bh(&p->rtort_rcu, rcu_torture_cb); +} + +static struct rcu_torture_ops rcu_bh_ops = { + .init = rcu_sync_torture_init, + .readlock = rcu_bh_torture_read_lock, + .read_delay = rcu_read_delay, /* just reuse rcu's version. */ + .readunlock = rcu_bh_torture_read_unlock, + .completed = rcu_bh_torture_completed, + .deferred_free = rcu_bh_torture_deferred_free, + .sync = synchronize_rcu_bh, + .exp_sync = synchronize_rcu_bh_expedited, + .call = call_rcu_bh, + .cb_barrier = rcu_barrier_bh, + .fqs = rcu_bh_force_quiescent_state, + .stats = NULL, + .irq_capable = 1, + .name = "rcu_bh" +}; + +/* + * Definitions for srcu torture testing. + */ + +DEFINE_STATIC_SRCU(srcu_ctl); + +static int srcu_torture_read_lock(void) __acquires(&srcu_ctl) +{ + return srcu_read_lock(&srcu_ctl); +} + +static void srcu_read_delay(struct rcu_random_state *rrsp) +{ + long delay; + const long uspertick = 1000000 / HZ; + const long longdelay = 10; + + /* We want there to be long-running readers, but not all the time. */ + + delay = rcu_random(rrsp) % (nrealreaders * 2 * longdelay * uspertick); + if (!delay) + schedule_timeout_interruptible(longdelay); + else + rcu_read_delay(rrsp); +} + +static void srcu_torture_read_unlock(int idx) __releases(&srcu_ctl) +{ + srcu_read_unlock(&srcu_ctl, idx); +} + +static int srcu_torture_completed(void) +{ + return srcu_batches_completed(&srcu_ctl); +} + +static void srcu_torture_deferred_free(struct rcu_torture *rp) +{ + call_srcu(&srcu_ctl, &rp->rtort_rcu, rcu_torture_cb); +} + +static void srcu_torture_synchronize(void) +{ + synchronize_srcu(&srcu_ctl); +} + +static void srcu_torture_call(struct rcu_head *head, + void (*func)(struct rcu_head *head)) +{ + call_srcu(&srcu_ctl, head, func); +} + +static void srcu_torture_barrier(void) +{ + srcu_barrier(&srcu_ctl); +} + +static int srcu_torture_stats(char *page) +{ + int cnt = 0; + int cpu; + int idx = srcu_ctl.completed & 0x1; + + cnt += sprintf(&page[cnt], "%s%s per-CPU(idx=%d):", + torture_type, TORTURE_FLAG, idx); + for_each_possible_cpu(cpu) { + cnt += sprintf(&page[cnt], " %d(%lu,%lu)", cpu, + per_cpu_ptr(srcu_ctl.per_cpu_ref, cpu)->c[!idx], + per_cpu_ptr(srcu_ctl.per_cpu_ref, cpu)->c[idx]); + } + cnt += sprintf(&page[cnt], "\n"); + return cnt; +} + +static void srcu_torture_synchronize_expedited(void) +{ + synchronize_srcu_expedited(&srcu_ctl); +} + +static struct rcu_torture_ops srcu_ops = { + .init = rcu_sync_torture_init, + .readlock = srcu_torture_read_lock, + .read_delay = srcu_read_delay, + .readunlock = srcu_torture_read_unlock, + .completed = srcu_torture_completed, + .deferred_free = srcu_torture_deferred_free, + .sync = srcu_torture_synchronize, + .exp_sync = srcu_torture_synchronize_expedited, + .call = srcu_torture_call, + .cb_barrier = srcu_torture_barrier, + .stats = srcu_torture_stats, + .name = "srcu" +}; + +/* + * Definitions for sched torture testing. + */ + +static int sched_torture_read_lock(void) +{ + preempt_disable(); + return 0; +} + +static void sched_torture_read_unlock(int idx) +{ + preempt_enable(); +} + +static void rcu_sched_torture_deferred_free(struct rcu_torture *p) +{ + call_rcu_sched(&p->rtort_rcu, rcu_torture_cb); +} + +static struct rcu_torture_ops sched_ops = { + .init = rcu_sync_torture_init, + .readlock = sched_torture_read_lock, + .read_delay = rcu_read_delay, /* just reuse rcu's version. */ + .readunlock = sched_torture_read_unlock, + .completed = rcu_no_completed, + .deferred_free = rcu_sched_torture_deferred_free, + .sync = synchronize_sched, + .exp_sync = synchronize_sched_expedited, + .call = call_rcu_sched, + .cb_barrier = rcu_barrier_sched, + .fqs = rcu_sched_force_quiescent_state, + .stats = NULL, + .irq_capable = 1, + .name = "sched" +}; + +/* + * RCU torture priority-boost testing. Runs one real-time thread per + * CPU for moderate bursts, repeatedly registering RCU callbacks and + * spinning waiting for them to be invoked. If a given callback takes + * too long to be invoked, we assume that priority inversion has occurred. + */ + +struct rcu_boost_inflight { + struct rcu_head rcu; + int inflight; +}; + +static void rcu_torture_boost_cb(struct rcu_head *head) +{ + struct rcu_boost_inflight *rbip = + container_of(head, struct rcu_boost_inflight, rcu); + + smp_mb(); /* Ensure RCU-core accesses precede clearing ->inflight */ + rbip->inflight = 0; +} + +static int rcu_torture_boost(void *arg) +{ + unsigned long call_rcu_time; + unsigned long endtime; + unsigned long oldstarttime; + struct rcu_boost_inflight rbi = { .inflight = 0 }; + struct sched_param sp; + + VERBOSE_PRINTK_STRING("rcu_torture_boost started"); + + /* Set real-time priority. */ + sp.sched_priority = 1; + if (sched_setscheduler(current, SCHED_FIFO, &sp) < 0) { + VERBOSE_PRINTK_STRING("rcu_torture_boost RT prio failed!"); + n_rcu_torture_boost_rterror++; + } + + init_rcu_head_on_stack(&rbi.rcu); + /* Each pass through the following loop does one boost-test cycle. */ + do { + /* Wait for the next test interval. */ + oldstarttime = boost_starttime; + while (ULONG_CMP_LT(jiffies, oldstarttime)) { + schedule_timeout_interruptible(oldstarttime - jiffies); + rcu_stutter_wait("rcu_torture_boost"); + if (kthread_should_stop() || + fullstop != FULLSTOP_DONTSTOP) + goto checkwait; + } + + /* Do one boost-test interval. */ + endtime = oldstarttime + test_boost_duration * HZ; + call_rcu_time = jiffies; + while (ULONG_CMP_LT(jiffies, endtime)) { + /* If we don't have a callback in flight, post one. */ + if (!rbi.inflight) { + smp_mb(); /* RCU core before ->inflight = 1. */ + rbi.inflight = 1; + call_rcu(&rbi.rcu, rcu_torture_boost_cb); + if (jiffies - call_rcu_time > + test_boost_duration * HZ - HZ / 2) { + VERBOSE_PRINTK_STRING("rcu_torture_boost boosting failed"); + n_rcu_torture_boost_failure++; + } + call_rcu_time = jiffies; + } + cond_resched(); + rcu_stutter_wait("rcu_torture_boost"); + if (kthread_should_stop() || + fullstop != FULLSTOP_DONTSTOP) + goto checkwait; + } + + /* + * Set the start time of the next test interval. + * Yes, this is vulnerable to long delays, but such + * delays simply cause a false negative for the next + * interval. Besides, we are running at RT priority, + * so delays should be relatively rare. + */ + while (oldstarttime == boost_starttime && + !kthread_should_stop()) { + if (mutex_trylock(&boost_mutex)) { + boost_starttime = jiffies + + test_boost_interval * HZ; + n_rcu_torture_boosts++; + mutex_unlock(&boost_mutex); + break; + } + schedule_timeout_uninterruptible(1); + } + + /* Go do the stutter. */ +checkwait: rcu_stutter_wait("rcu_torture_boost"); + } while (!kthread_should_stop() && fullstop == FULLSTOP_DONTSTOP); + + /* Clean up and exit. */ + VERBOSE_PRINTK_STRING("rcu_torture_boost task stopping"); + rcutorture_shutdown_absorb("rcu_torture_boost"); + while (!kthread_should_stop() || rbi.inflight) + schedule_timeout_uninterruptible(1); + smp_mb(); /* order accesses to ->inflight before stack-frame death. */ + destroy_rcu_head_on_stack(&rbi.rcu); + return 0; +} + +/* + * RCU torture force-quiescent-state kthread. Repeatedly induces + * bursts of calls to force_quiescent_state(), increasing the probability + * of occurrence of some important types of race conditions. + */ +static int +rcu_torture_fqs(void *arg) +{ + unsigned long fqs_resume_time; + int fqs_burst_remaining; + + VERBOSE_PRINTK_STRING("rcu_torture_fqs task started"); + do { + fqs_resume_time = jiffies + fqs_stutter * HZ; + while (ULONG_CMP_LT(jiffies, fqs_resume_time) && + !kthread_should_stop()) { + schedule_timeout_interruptible(1); + } + fqs_burst_remaining = fqs_duration; + while (fqs_burst_remaining > 0 && + !kthread_should_stop()) { + cur_ops->fqs(); + udelay(fqs_holdoff); + fqs_burst_remaining -= fqs_holdoff; + } + rcu_stutter_wait("rcu_torture_fqs"); + } while (!kthread_should_stop() && fullstop == FULLSTOP_DONTSTOP); + VERBOSE_PRINTK_STRING("rcu_torture_fqs task stopping"); + rcutorture_shutdown_absorb("rcu_torture_fqs"); + while (!kthread_should_stop()) + schedule_timeout_uninterruptible(1); + return 0; +} + +/* + * RCU torture writer kthread. Repeatedly substitutes a new structure + * for that pointed to by rcu_torture_current, freeing the old structure + * after a series of grace periods (the "pipeline"). + */ +static int +rcu_torture_writer(void *arg) +{ + bool exp; + int i; + struct rcu_torture *rp; + struct rcu_torture *rp1; + struct rcu_torture *old_rp; + static DEFINE_RCU_RANDOM(rand); + + VERBOSE_PRINTK_STRING("rcu_torture_writer task started"); + set_user_nice(current, 19); + + do { + schedule_timeout_uninterruptible(1); + rp = rcu_torture_alloc(); + if (rp == NULL) + continue; + rp->rtort_pipe_count = 0; + udelay(rcu_random(&rand) & 0x3ff); + old_rp = rcu_dereference_check(rcu_torture_current, + current == writer_task); + rp->rtort_mbtest = 1; + rcu_assign_pointer(rcu_torture_current, rp); + smp_wmb(); /* Mods to old_rp must follow rcu_assign_pointer() */ + if (old_rp) { + i = old_rp->rtort_pipe_count; + if (i > RCU_TORTURE_PIPE_LEN) + i = RCU_TORTURE_PIPE_LEN; + atomic_inc(&rcu_torture_wcount[i]); + old_rp->rtort_pipe_count++; + if (gp_normal == gp_exp) + exp = !!(rcu_random(&rand) & 0x80); + else + exp = gp_exp; + if (!exp) { + cur_ops->deferred_free(old_rp); + } else { + cur_ops->exp_sync(); + list_add(&old_rp->rtort_free, + &rcu_torture_removed); + list_for_each_entry_safe(rp, rp1, + &rcu_torture_removed, + rtort_free) { + i = rp->rtort_pipe_count; + if (i > RCU_TORTURE_PIPE_LEN) + i = RCU_TORTURE_PIPE_LEN; + atomic_inc(&rcu_torture_wcount[i]); + if (++rp->rtort_pipe_count >= + RCU_TORTURE_PIPE_LEN) { + rp->rtort_mbtest = 0; + list_del(&rp->rtort_free); + rcu_torture_free(rp); + } + } + } + } + rcutorture_record_progress(++rcu_torture_current_version); + rcu_stutter_wait("rcu_torture_writer"); + } while (!kthread_should_stop() && fullstop == FULLSTOP_DONTSTOP); + VERBOSE_PRINTK_STRING("rcu_torture_writer task stopping"); + rcutorture_shutdown_absorb("rcu_torture_writer"); + while (!kthread_should_stop()) + schedule_timeout_uninterruptible(1); + return 0; +} + +/* + * RCU torture fake writer kthread. Repeatedly calls sync, with a random + * delay between calls. + */ +static int +rcu_torture_fakewriter(void *arg) +{ + DEFINE_RCU_RANDOM(rand); + + VERBOSE_PRINTK_STRING("rcu_torture_fakewriter task started"); + set_user_nice(current, 19); + + do { + schedule_timeout_uninterruptible(1 + rcu_random(&rand)%10); + udelay(rcu_random(&rand) & 0x3ff); + if (cur_ops->cb_barrier != NULL && + rcu_random(&rand) % (nfakewriters * 8) == 0) { + cur_ops->cb_barrier(); + } else if (gp_normal == gp_exp) { + if (rcu_random(&rand) & 0x80) + cur_ops->sync(); + else + cur_ops->exp_sync(); + } else if (gp_normal) { + cur_ops->sync(); + } else { + cur_ops->exp_sync(); + } + rcu_stutter_wait("rcu_torture_fakewriter"); + } while (!kthread_should_stop() && fullstop == FULLSTOP_DONTSTOP); + + VERBOSE_PRINTK_STRING("rcu_torture_fakewriter task stopping"); + rcutorture_shutdown_absorb("rcu_torture_fakewriter"); + while (!kthread_should_stop()) + schedule_timeout_uninterruptible(1); + return 0; +} + +void rcutorture_trace_dump(void) +{ + static atomic_t beenhere = ATOMIC_INIT(0); + + if (atomic_read(&beenhere)) + return; + if (atomic_xchg(&beenhere, 1) != 0) + return; + ftrace_dump(DUMP_ALL); +} + +/* + * RCU torture reader from timer handler. Dereferences rcu_torture_current, + * incrementing the corresponding element of the pipeline array. The + * counter in the element should never be greater than 1, otherwise, the + * RCU implementation is broken. + */ +static void rcu_torture_timer(unsigned long unused) +{ + int idx; + int completed; + int completed_end; + static DEFINE_RCU_RANDOM(rand); + static DEFINE_SPINLOCK(rand_lock); + struct rcu_torture *p; + int pipe_count; + unsigned long long ts; + + idx = cur_ops->readlock(); + completed = cur_ops->completed(); + ts = rcu_trace_clock_local(); + p = rcu_dereference_check(rcu_torture_current, + rcu_read_lock_bh_held() || + rcu_read_lock_sched_held() || + srcu_read_lock_held(&srcu_ctl)); + if (p == NULL) { + /* Leave because rcu_torture_writer is not yet underway */ + cur_ops->readunlock(idx); + return; + } + if (p->rtort_mbtest == 0) + atomic_inc(&n_rcu_torture_mberror); + spin_lock(&rand_lock); + cur_ops->read_delay(&rand); + n_rcu_torture_timers++; + spin_unlock(&rand_lock); + preempt_disable(); + pipe_count = p->rtort_pipe_count; + if (pipe_count > RCU_TORTURE_PIPE_LEN) { + /* Should not happen, but... */ + pipe_count = RCU_TORTURE_PIPE_LEN; + } + completed_end = cur_ops->completed(); + if (pipe_count > 1) { + do_trace_rcu_torture_read(cur_ops->name, &p->rtort_rcu, ts, + completed, completed_end); + rcutorture_trace_dump(); + } + __this_cpu_inc(rcu_torture_count[pipe_count]); + completed = completed_end - completed; + if (completed > RCU_TORTURE_PIPE_LEN) { + /* Should not happen, but... */ + completed = RCU_TORTURE_PIPE_LEN; + } + __this_cpu_inc(rcu_torture_batch[completed]); + preempt_enable(); + cur_ops->readunlock(idx); +} + +/* + * RCU torture reader kthread. Repeatedly dereferences rcu_torture_current, + * incrementing the corresponding element of the pipeline array. The + * counter in the element should never be greater than 1, otherwise, the + * RCU implementation is broken. + */ +static int +rcu_torture_reader(void *arg) +{ + int completed; + int completed_end; + int idx; + DEFINE_RCU_RANDOM(rand); + struct rcu_torture *p; + int pipe_count; + struct timer_list t; + unsigned long long ts; + + VERBOSE_PRINTK_STRING("rcu_torture_reader task started"); + set_user_nice(current, 19); + if (irqreader && cur_ops->irq_capable) + setup_timer_on_stack(&t, rcu_torture_timer, 0); + + do { + if (irqreader && cur_ops->irq_capable) { + if (!timer_pending(&t)) + mod_timer(&t, jiffies + 1); + } + idx = cur_ops->readlock(); + completed = cur_ops->completed(); + ts = rcu_trace_clock_local(); + p = rcu_dereference_check(rcu_torture_current, + rcu_read_lock_bh_held() || + rcu_read_lock_sched_held() || + srcu_read_lock_held(&srcu_ctl)); + if (p == NULL) { + /* Wait for rcu_torture_writer to get underway */ + cur_ops->readunlock(idx); + schedule_timeout_interruptible(HZ); + continue; + } + if (p->rtort_mbtest == 0) + atomic_inc(&n_rcu_torture_mberror); + cur_ops->read_delay(&rand); + preempt_disable(); + pipe_count = p->rtort_pipe_count; + if (pipe_count > RCU_TORTURE_PIPE_LEN) { + /* Should not happen, but... */ + pipe_count = RCU_TORTURE_PIPE_LEN; + } + completed_end = cur_ops->completed(); + if (pipe_count > 1) { + do_trace_rcu_torture_read(cur_ops->name, &p->rtort_rcu, + ts, completed, completed_end); + rcutorture_trace_dump(); + } + __this_cpu_inc(rcu_torture_count[pipe_count]); + completed = completed_end - completed; + if (completed > RCU_TORTURE_PIPE_LEN) { + /* Should not happen, but... */ + completed = RCU_TORTURE_PIPE_LEN; + } + __this_cpu_inc(rcu_torture_batch[completed]); + preempt_enable(); + cur_ops->readunlock(idx); + schedule(); + rcu_stutter_wait("rcu_torture_reader"); + } while (!kthread_should_stop() && fullstop == FULLSTOP_DONTSTOP); + VERBOSE_PRINTK_STRING("rcu_torture_reader task stopping"); + rcutorture_shutdown_absorb("rcu_torture_reader"); + if (irqreader && cur_ops->irq_capable) + del_timer_sync(&t); + while (!kthread_should_stop()) + schedule_timeout_uninterruptible(1); + return 0; +} + +/* + * Create an RCU-torture statistics message in the specified buffer. + */ +static int +rcu_torture_printk(char *page) +{ + int cnt = 0; + int cpu; + int i; + long pipesummary[RCU_TORTURE_PIPE_LEN + 1] = { 0 }; + long batchsummary[RCU_TORTURE_PIPE_LEN + 1] = { 0 }; + + for_each_possible_cpu(cpu) { + for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) { + pipesummary[i] += per_cpu(rcu_torture_count, cpu)[i]; + batchsummary[i] += per_cpu(rcu_torture_batch, cpu)[i]; + } + } + for (i = RCU_TORTURE_PIPE_LEN - 1; i >= 0; i--) { + if (pipesummary[i] != 0) + break; + } + cnt += sprintf(&page[cnt], "%s%s ", torture_type, TORTURE_FLAG); + cnt += sprintf(&page[cnt], + "rtc: %p ver: %lu tfle: %d rta: %d rtaf: %d rtf: %d ", + rcu_torture_current, + rcu_torture_current_version, + list_empty(&rcu_torture_freelist), + atomic_read(&n_rcu_torture_alloc), + atomic_read(&n_rcu_torture_alloc_fail), + atomic_read(&n_rcu_torture_free)); + cnt += sprintf(&page[cnt], "rtmbe: %d rtbke: %ld rtbre: %ld ", + atomic_read(&n_rcu_torture_mberror), + n_rcu_torture_boost_ktrerror, + n_rcu_torture_boost_rterror); + cnt += sprintf(&page[cnt], "rtbf: %ld rtb: %ld nt: %ld ", + n_rcu_torture_boost_failure, + n_rcu_torture_boosts, + n_rcu_torture_timers); + cnt += sprintf(&page[cnt], + "onoff: %ld/%ld:%ld/%ld %d,%d:%d,%d %lu:%lu (HZ=%d) ", + n_online_successes, n_online_attempts, + n_offline_successes, n_offline_attempts, + min_online, max_online, + min_offline, max_offline, + sum_online, sum_offline, HZ); + cnt += sprintf(&page[cnt], "barrier: %ld/%ld:%ld", + n_barrier_successes, + n_barrier_attempts, + n_rcu_torture_barrier_error); + cnt += sprintf(&page[cnt], "\n%s%s ", torture_type, TORTURE_FLAG); + if (atomic_read(&n_rcu_torture_mberror) != 0 || + n_rcu_torture_barrier_error != 0 || + n_rcu_torture_boost_ktrerror != 0 || + n_rcu_torture_boost_rterror != 0 || + n_rcu_torture_boost_failure != 0 || + i > 1) { + cnt += sprintf(&page[cnt], "!!! "); + atomic_inc(&n_rcu_torture_error); + WARN_ON_ONCE(1); + } + cnt += sprintf(&page[cnt], "Reader Pipe: "); + for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) + cnt += sprintf(&page[cnt], " %ld", pipesummary[i]); + cnt += sprintf(&page[cnt], "\n%s%s ", torture_type, TORTURE_FLAG); + cnt += sprintf(&page[cnt], "Reader Batch: "); + for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) + cnt += sprintf(&page[cnt], " %ld", batchsummary[i]); + cnt += sprintf(&page[cnt], "\n%s%s ", torture_type, TORTURE_FLAG); + cnt += sprintf(&page[cnt], "Free-Block Circulation: "); + for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) { + cnt += sprintf(&page[cnt], " %d", + atomic_read(&rcu_torture_wcount[i])); + } + cnt += sprintf(&page[cnt], "\n"); + if (cur_ops->stats) + cnt += cur_ops->stats(&page[cnt]); + return cnt; +} + +/* + * Print torture statistics. Caller must ensure that there is only + * one call to this function at a given time!!! This is normally + * accomplished by relying on the module system to only have one copy + * of the module loaded, and then by giving the rcu_torture_stats + * kthread full control (or the init/cleanup functions when rcu_torture_stats + * thread is not running). + */ +static void +rcu_torture_stats_print(void) +{ + int cnt; + + cnt = rcu_torture_printk(printk_buf); + pr_alert("%s", printk_buf); +} + +/* + * Periodically prints torture statistics, if periodic statistics printing + * was specified via the stat_interval module parameter. + * + * No need to worry about fullstop here, since this one doesn't reference + * volatile state or register callbacks. + */ +static int +rcu_torture_stats(void *arg) +{ + VERBOSE_PRINTK_STRING("rcu_torture_stats task started"); + do { + schedule_timeout_interruptible(stat_interval * HZ); + rcu_torture_stats_print(); + rcutorture_shutdown_absorb("rcu_torture_stats"); + } while (!kthread_should_stop()); + VERBOSE_PRINTK_STRING("rcu_torture_stats task stopping"); + return 0; +} + +static int rcu_idle_cpu; /* Force all torture tasks off this CPU */ + +/* Shuffle tasks such that we allow @rcu_idle_cpu to become idle. A special case + * is when @rcu_idle_cpu = -1, when we allow the tasks to run on all CPUs. + */ +static void rcu_torture_shuffle_tasks(void) +{ + int i; + + cpumask_setall(shuffle_tmp_mask); + get_online_cpus(); + + /* No point in shuffling if there is only one online CPU (ex: UP) */ + if (num_online_cpus() == 1) { + put_online_cpus(); + return; + } + + if (rcu_idle_cpu != -1) + cpumask_clear_cpu(rcu_idle_cpu, shuffle_tmp_mask); + + set_cpus_allowed_ptr(current, shuffle_tmp_mask); + + if (reader_tasks) { + for (i = 0; i < nrealreaders; i++) + if (reader_tasks[i]) + set_cpus_allowed_ptr(reader_tasks[i], + shuffle_tmp_mask); + } + if (fakewriter_tasks) { + for (i = 0; i < nfakewriters; i++) + if (fakewriter_tasks[i]) + set_cpus_allowed_ptr(fakewriter_tasks[i], + shuffle_tmp_mask); + } + if (writer_task) + set_cpus_allowed_ptr(writer_task, shuffle_tmp_mask); + if (stats_task) + set_cpus_allowed_ptr(stats_task, shuffle_tmp_mask); + if (stutter_task) + set_cpus_allowed_ptr(stutter_task, shuffle_tmp_mask); + if (fqs_task) + set_cpus_allowed_ptr(fqs_task, shuffle_tmp_mask); + if (shutdown_task) + set_cpus_allowed_ptr(shutdown_task, shuffle_tmp_mask); +#ifdef CONFIG_HOTPLUG_CPU + if (onoff_task) + set_cpus_allowed_ptr(onoff_task, shuffle_tmp_mask); +#endif /* #ifdef CONFIG_HOTPLUG_CPU */ + if (stall_task) + set_cpus_allowed_ptr(stall_task, shuffle_tmp_mask); + if (barrier_cbs_tasks) + for (i = 0; i < n_barrier_cbs; i++) + if (barrier_cbs_tasks[i]) + set_cpus_allowed_ptr(barrier_cbs_tasks[i], + shuffle_tmp_mask); + if (barrier_task) + set_cpus_allowed_ptr(barrier_task, shuffle_tmp_mask); + + if (rcu_idle_cpu == -1) + rcu_idle_cpu = num_online_cpus() - 1; + else + rcu_idle_cpu--; + + put_online_cpus(); +} + +/* Shuffle tasks across CPUs, with the intent of allowing each CPU in the + * system to become idle at a time and cut off its timer ticks. This is meant + * to test the support for such tickless idle CPU in RCU. + */ +static int +rcu_torture_shuffle(void *arg) +{ + VERBOSE_PRINTK_STRING("rcu_torture_shuffle task started"); + do { + schedule_timeout_interruptible(shuffle_interval * HZ); + rcu_torture_shuffle_tasks(); + rcutorture_shutdown_absorb("rcu_torture_shuffle"); + } while (!kthread_should_stop()); + VERBOSE_PRINTK_STRING("rcu_torture_shuffle task stopping"); + return 0; +} + +/* Cause the rcutorture test to "stutter", starting and stopping all + * threads periodically. + */ +static int +rcu_torture_stutter(void *arg) +{ + VERBOSE_PRINTK_STRING("rcu_torture_stutter task started"); + do { + schedule_timeout_interruptible(stutter * HZ); + stutter_pause_test = 1; + if (!kthread_should_stop()) + schedule_timeout_interruptible(stutter * HZ); + stutter_pause_test = 0; + rcutorture_shutdown_absorb("rcu_torture_stutter"); + } while (!kthread_should_stop()); + VERBOSE_PRINTK_STRING("rcu_torture_stutter task stopping"); + return 0; +} + +static inline void +rcu_torture_print_module_parms(struct rcu_torture_ops *cur_ops, const char *tag) +{ + pr_alert("%s" TORTURE_FLAG + "--- %s: nreaders=%d nfakewriters=%d " + "stat_interval=%d verbose=%d test_no_idle_hz=%d " + "shuffle_interval=%d stutter=%d irqreader=%d " + "fqs_duration=%d fqs_holdoff=%d fqs_stutter=%d " + "test_boost=%d/%d test_boost_interval=%d " + "test_boost_duration=%d shutdown_secs=%d " + "stall_cpu=%d stall_cpu_holdoff=%d " + "n_barrier_cbs=%d " + "onoff_interval=%d onoff_holdoff=%d\n", + torture_type, tag, nrealreaders, nfakewriters, + stat_interval, verbose, test_no_idle_hz, shuffle_interval, + stutter, irqreader, fqs_duration, fqs_holdoff, fqs_stutter, + test_boost, cur_ops->can_boost, + test_boost_interval, test_boost_duration, shutdown_secs, + stall_cpu, stall_cpu_holdoff, + n_barrier_cbs, + onoff_interval, onoff_holdoff); +} + +static struct notifier_block rcutorture_shutdown_nb = { + .notifier_call = rcutorture_shutdown_notify, +}; + +static void rcutorture_booster_cleanup(int cpu) +{ + struct task_struct *t; + + if (boost_tasks[cpu] == NULL) + return; + mutex_lock(&boost_mutex); + VERBOSE_PRINTK_STRING("Stopping rcu_torture_boost task"); + t = boost_tasks[cpu]; + boost_tasks[cpu] = NULL; + mutex_unlock(&boost_mutex); + + /* This must be outside of the mutex, otherwise deadlock! */ + kthread_stop(t); + boost_tasks[cpu] = NULL; +} + +static int rcutorture_booster_init(int cpu) +{ + int retval; + + if (boost_tasks[cpu] != NULL) + return 0; /* Already created, nothing more to do. */ + + /* Don't allow time recalculation while creating a new task. */ + mutex_lock(&boost_mutex); + VERBOSE_PRINTK_STRING("Creating rcu_torture_boost task"); + boost_tasks[cpu] = kthread_create_on_node(rcu_torture_boost, NULL, + cpu_to_node(cpu), + "rcu_torture_boost"); + if (IS_ERR(boost_tasks[cpu])) { + retval = PTR_ERR(boost_tasks[cpu]); + VERBOSE_PRINTK_STRING("rcu_torture_boost task create failed"); + n_rcu_torture_boost_ktrerror++; + boost_tasks[cpu] = NULL; + mutex_unlock(&boost_mutex); + return retval; + } + kthread_bind(boost_tasks[cpu], cpu); + wake_up_process(boost_tasks[cpu]); + mutex_unlock(&boost_mutex); + return 0; +} + +/* + * Cause the rcutorture test to shutdown the system after the test has + * run for the time specified by the shutdown_secs module parameter. + */ +static int +rcu_torture_shutdown(void *arg) +{ + long delta; + unsigned long jiffies_snap; + + VERBOSE_PRINTK_STRING("rcu_torture_shutdown task started"); + jiffies_snap = ACCESS_ONCE(jiffies); + while (ULONG_CMP_LT(jiffies_snap, shutdown_time) && + !kthread_should_stop()) { + delta = shutdown_time - jiffies_snap; + if (verbose) + pr_alert("%s" TORTURE_FLAG + "rcu_torture_shutdown task: %lu jiffies remaining\n", + torture_type, delta); + schedule_timeout_interruptible(delta); + jiffies_snap = ACCESS_ONCE(jiffies); + } + if (kthread_should_stop()) { + VERBOSE_PRINTK_STRING("rcu_torture_shutdown task stopping"); + return 0; + } + + /* OK, shut down the system. */ + + VERBOSE_PRINTK_STRING("rcu_torture_shutdown task shutting down system"); + shutdown_task = NULL; /* Avoid self-kill deadlock. */ + rcu_torture_cleanup(); /* Get the success/failure message. */ + kernel_power_off(); /* Shut down the system. */ + return 0; +} + +#ifdef CONFIG_HOTPLUG_CPU + +/* + * Execute random CPU-hotplug operations at the interval specified + * by the onoff_interval. + */ +static int +rcu_torture_onoff(void *arg) +{ + int cpu; + unsigned long delta; + int maxcpu = -1; + DEFINE_RCU_RANDOM(rand); + int ret; + unsigned long starttime; + + VERBOSE_PRINTK_STRING("rcu_torture_onoff task started"); + for_each_online_cpu(cpu) + maxcpu = cpu; + WARN_ON(maxcpu < 0); + if (onoff_holdoff > 0) { + VERBOSE_PRINTK_STRING("rcu_torture_onoff begin holdoff"); + schedule_timeout_interruptible(onoff_holdoff * HZ); + VERBOSE_PRINTK_STRING("rcu_torture_onoff end holdoff"); + } + while (!kthread_should_stop()) { + cpu = (rcu_random(&rand) >> 4) % (maxcpu + 1); + if (cpu_online(cpu) && cpu_is_hotpluggable(cpu)) { + if (verbose) + pr_alert("%s" TORTURE_FLAG + "rcu_torture_onoff task: offlining %d\n", + torture_type, cpu); + starttime = jiffies; + n_offline_attempts++; + ret = cpu_down(cpu); + if (ret) { + if (verbose) + pr_alert("%s" TORTURE_FLAG + "rcu_torture_onoff task: offline %d failed: errno %d\n", + torture_type, cpu, ret); + } else { + if (verbose) + pr_alert("%s" TORTURE_FLAG + "rcu_torture_onoff task: offlined %d\n", + torture_type, cpu); + n_offline_successes++; + delta = jiffies - starttime; + sum_offline += delta; + if (min_offline < 0) { + min_offline = delta; + max_offline = delta; + } + if (min_offline > delta) + min_offline = delta; + if (max_offline < delta) + max_offline = delta; + } + } else if (cpu_is_hotpluggable(cpu)) { + if (verbose) + pr_alert("%s" TORTURE_FLAG + "rcu_torture_onoff task: onlining %d\n", + torture_type, cpu); + starttime = jiffies; + n_online_attempts++; + ret = cpu_up(cpu); + if (ret) { + if (verbose) + pr_alert("%s" TORTURE_FLAG + "rcu_torture_onoff task: online %d failed: errno %d\n", + torture_type, cpu, ret); + } else { + if (verbose) + pr_alert("%s" TORTURE_FLAG + "rcu_torture_onoff task: onlined %d\n", + torture_type, cpu); + n_online_successes++; + delta = jiffies - starttime; + sum_online += delta; + if (min_online < 0) { + min_online = delta; + max_online = delta; + } + if (min_online > delta) + min_online = delta; + if (max_online < delta) + max_online = delta; + } + } + schedule_timeout_interruptible(onoff_interval * HZ); + } + VERBOSE_PRINTK_STRING("rcu_torture_onoff task stopping"); + return 0; +} + +static int +rcu_torture_onoff_init(void) +{ + int ret; + + if (onoff_interval <= 0) + return 0; + onoff_task = kthread_run(rcu_torture_onoff, NULL, "rcu_torture_onoff"); + if (IS_ERR(onoff_task)) { + ret = PTR_ERR(onoff_task); + onoff_task = NULL; + return ret; + } + return 0; +} + +static void rcu_torture_onoff_cleanup(void) +{ + if (onoff_task == NULL) + return; + VERBOSE_PRINTK_STRING("Stopping rcu_torture_onoff task"); + kthread_stop(onoff_task); + onoff_task = NULL; +} + +#else /* #ifdef CONFIG_HOTPLUG_CPU */ + +static int +rcu_torture_onoff_init(void) +{ + return 0; +} + +static void rcu_torture_onoff_cleanup(void) +{ +} + +#endif /* #else #ifdef CONFIG_HOTPLUG_CPU */ + +/* + * CPU-stall kthread. It waits as specified by stall_cpu_holdoff, then + * induces a CPU stall for the time specified by stall_cpu. + */ +static int rcu_torture_stall(void *args) +{ + unsigned long stop_at; + + VERBOSE_PRINTK_STRING("rcu_torture_stall task started"); + if (stall_cpu_holdoff > 0) { + VERBOSE_PRINTK_STRING("rcu_torture_stall begin holdoff"); + schedule_timeout_interruptible(stall_cpu_holdoff * HZ); + VERBOSE_PRINTK_STRING("rcu_torture_stall end holdoff"); + } + if (!kthread_should_stop()) { + stop_at = get_seconds() + stall_cpu; + /* RCU CPU stall is expected behavior in following code. */ + pr_alert("rcu_torture_stall start.\n"); + rcu_read_lock(); + preempt_disable(); + while (ULONG_CMP_LT(get_seconds(), stop_at)) + continue; /* Induce RCU CPU stall warning. */ + preempt_enable(); + rcu_read_unlock(); + pr_alert("rcu_torture_stall end.\n"); + } + rcutorture_shutdown_absorb("rcu_torture_stall"); + while (!kthread_should_stop()) + schedule_timeout_interruptible(10 * HZ); + return 0; +} + +/* Spawn CPU-stall kthread, if stall_cpu specified. */ +static int __init rcu_torture_stall_init(void) +{ + int ret; + + if (stall_cpu <= 0) + return 0; + stall_task = kthread_run(rcu_torture_stall, NULL, "rcu_torture_stall"); + if (IS_ERR(stall_task)) { + ret = PTR_ERR(stall_task); + stall_task = NULL; + return ret; + } + return 0; +} + +/* Clean up after the CPU-stall kthread, if one was spawned. */ +static void rcu_torture_stall_cleanup(void) +{ + if (stall_task == NULL) + return; + VERBOSE_PRINTK_STRING("Stopping rcu_torture_stall_task."); + kthread_stop(stall_task); + stall_task = NULL; +} + +/* Callback function for RCU barrier testing. */ +void rcu_torture_barrier_cbf(struct rcu_head *rcu) +{ + atomic_inc(&barrier_cbs_invoked); +} + +/* kthread function to register callbacks used to test RCU barriers. */ +static int rcu_torture_barrier_cbs(void *arg) +{ + long myid = (long)arg; + bool lastphase = 0; + struct rcu_head rcu; + + init_rcu_head_on_stack(&rcu); + VERBOSE_PRINTK_STRING("rcu_torture_barrier_cbs task started"); + set_user_nice(current, 19); + do { + wait_event(barrier_cbs_wq[myid], + barrier_phase != lastphase || + kthread_should_stop() || + fullstop != FULLSTOP_DONTSTOP); + lastphase = barrier_phase; + smp_mb(); /* ensure barrier_phase load before ->call(). */ + if (kthread_should_stop() || fullstop != FULLSTOP_DONTSTOP) + break; + cur_ops->call(&rcu, rcu_torture_barrier_cbf); + if (atomic_dec_and_test(&barrier_cbs_count)) + wake_up(&barrier_wq); + } while (!kthread_should_stop() && fullstop == FULLSTOP_DONTSTOP); + VERBOSE_PRINTK_STRING("rcu_torture_barrier_cbs task stopping"); + rcutorture_shutdown_absorb("rcu_torture_barrier_cbs"); + while (!kthread_should_stop()) + schedule_timeout_interruptible(1); + cur_ops->cb_barrier(); + destroy_rcu_head_on_stack(&rcu); + return 0; +} + +/* kthread function to drive and coordinate RCU barrier testing. */ +static int rcu_torture_barrier(void *arg) +{ + int i; + + VERBOSE_PRINTK_STRING("rcu_torture_barrier task starting"); + do { + atomic_set(&barrier_cbs_invoked, 0); + atomic_set(&barrier_cbs_count, n_barrier_cbs); + smp_mb(); /* Ensure barrier_phase after prior assignments. */ + barrier_phase = !barrier_phase; + for (i = 0; i < n_barrier_cbs; i++) + wake_up(&barrier_cbs_wq[i]); + wait_event(barrier_wq, + atomic_read(&barrier_cbs_count) == 0 || + kthread_should_stop() || + fullstop != FULLSTOP_DONTSTOP); + if (kthread_should_stop() || fullstop != FULLSTOP_DONTSTOP) + break; + n_barrier_attempts++; + cur_ops->cb_barrier(); + if (atomic_read(&barrier_cbs_invoked) != n_barrier_cbs) { + n_rcu_torture_barrier_error++; + WARN_ON_ONCE(1); + } + n_barrier_successes++; + schedule_timeout_interruptible(HZ / 10); + } while (!kthread_should_stop() && fullstop == FULLSTOP_DONTSTOP); + VERBOSE_PRINTK_STRING("rcu_torture_barrier task stopping"); + rcutorture_shutdown_absorb("rcu_torture_barrier"); + while (!kthread_should_stop()) + schedule_timeout_interruptible(1); + return 0; +} + +/* Initialize RCU barrier testing. */ +static int rcu_torture_barrier_init(void) +{ + int i; + int ret; + + if (n_barrier_cbs == 0) + return 0; + if (cur_ops->call == NULL || cur_ops->cb_barrier == NULL) { + pr_alert("%s" TORTURE_FLAG + " Call or barrier ops missing for %s,\n", + torture_type, cur_ops->name); + pr_alert("%s" TORTURE_FLAG + " RCU barrier testing omitted from run.\n", + torture_type); + return 0; + } + atomic_set(&barrier_cbs_count, 0); + atomic_set(&barrier_cbs_invoked, 0); + barrier_cbs_tasks = + kzalloc(n_barrier_cbs * sizeof(barrier_cbs_tasks[0]), + GFP_KERNEL); + barrier_cbs_wq = + kzalloc(n_barrier_cbs * sizeof(barrier_cbs_wq[0]), + GFP_KERNEL); + if (barrier_cbs_tasks == NULL || !barrier_cbs_wq) + return -ENOMEM; + for (i = 0; i < n_barrier_cbs; i++) { + init_waitqueue_head(&barrier_cbs_wq[i]); + barrier_cbs_tasks[i] = kthread_run(rcu_torture_barrier_cbs, + (void *)(long)i, + "rcu_torture_barrier_cbs"); + if (IS_ERR(barrier_cbs_tasks[i])) { + ret = PTR_ERR(barrier_cbs_tasks[i]); + VERBOSE_PRINTK_ERRSTRING("Failed to create rcu_torture_barrier_cbs"); + barrier_cbs_tasks[i] = NULL; + return ret; + } + } + barrier_task = kthread_run(rcu_torture_barrier, NULL, + "rcu_torture_barrier"); + if (IS_ERR(barrier_task)) { + ret = PTR_ERR(barrier_task); + VERBOSE_PRINTK_ERRSTRING("Failed to create rcu_torture_barrier"); + barrier_task = NULL; + } + return 0; +} + +/* Clean up after RCU barrier testing. */ +static void rcu_torture_barrier_cleanup(void) +{ + int i; + + if (barrier_task != NULL) { + VERBOSE_PRINTK_STRING("Stopping rcu_torture_barrier task"); + kthread_stop(barrier_task); + barrier_task = NULL; + } + if (barrier_cbs_tasks != NULL) { + for (i = 0; i < n_barrier_cbs; i++) { + if (barrier_cbs_tasks[i] != NULL) { + VERBOSE_PRINTK_STRING("Stopping rcu_torture_barrier_cbs task"); + kthread_stop(barrier_cbs_tasks[i]); + barrier_cbs_tasks[i] = NULL; + } + } + kfree(barrier_cbs_tasks); + barrier_cbs_tasks = NULL; + } + if (barrier_cbs_wq != NULL) { + kfree(barrier_cbs_wq); + barrier_cbs_wq = NULL; + } +} + +static int rcutorture_cpu_notify(struct notifier_block *self, + unsigned long action, void *hcpu) +{ + long cpu = (long)hcpu; + + switch (action) { + case CPU_ONLINE: + case CPU_DOWN_FAILED: + (void)rcutorture_booster_init(cpu); + break; + case CPU_DOWN_PREPARE: + rcutorture_booster_cleanup(cpu); + break; + default: + break; + } + return NOTIFY_OK; +} + +static struct notifier_block rcutorture_cpu_nb = { + .notifier_call = rcutorture_cpu_notify, +}; + +static void +rcu_torture_cleanup(void) +{ + int i; + + mutex_lock(&fullstop_mutex); + rcutorture_record_test_transition(); + if (fullstop == FULLSTOP_SHUTDOWN) { + pr_warn(/* but going down anyway, so... */ + "Concurrent 'rmmod rcutorture' and shutdown illegal!\n"); + mutex_unlock(&fullstop_mutex); + schedule_timeout_uninterruptible(10); + if (cur_ops->cb_barrier != NULL) + cur_ops->cb_barrier(); + return; + } + fullstop = FULLSTOP_RMMOD; + mutex_unlock(&fullstop_mutex); + unregister_reboot_notifier(&rcutorture_shutdown_nb); + rcu_torture_barrier_cleanup(); + rcu_torture_stall_cleanup(); + if (stutter_task) { + VERBOSE_PRINTK_STRING("Stopping rcu_torture_stutter task"); + kthread_stop(stutter_task); + } + stutter_task = NULL; + if (shuffler_task) { + VERBOSE_PRINTK_STRING("Stopping rcu_torture_shuffle task"); + kthread_stop(shuffler_task); + free_cpumask_var(shuffle_tmp_mask); + } + shuffler_task = NULL; + + if (writer_task) { + VERBOSE_PRINTK_STRING("Stopping rcu_torture_writer task"); + kthread_stop(writer_task); + } + writer_task = NULL; + + if (reader_tasks) { + for (i = 0; i < nrealreaders; i++) { + if (reader_tasks[i]) { + VERBOSE_PRINTK_STRING( + "Stopping rcu_torture_reader task"); + kthread_stop(reader_tasks[i]); + } + reader_tasks[i] = NULL; + } + kfree(reader_tasks); + reader_tasks = NULL; + } + rcu_torture_current = NULL; + + if (fakewriter_tasks) { + for (i = 0; i < nfakewriters; i++) { + if (fakewriter_tasks[i]) { + VERBOSE_PRINTK_STRING( + "Stopping rcu_torture_fakewriter task"); + kthread_stop(fakewriter_tasks[i]); + } + fakewriter_tasks[i] = NULL; + } + kfree(fakewriter_tasks); + fakewriter_tasks = NULL; + } + + if (stats_task) { + VERBOSE_PRINTK_STRING("Stopping rcu_torture_stats task"); + kthread_stop(stats_task); + } + stats_task = NULL; + + if (fqs_task) { + VERBOSE_PRINTK_STRING("Stopping rcu_torture_fqs task"); + kthread_stop(fqs_task); + } + fqs_task = NULL; + if ((test_boost == 1 && cur_ops->can_boost) || + test_boost == 2) { + unregister_cpu_notifier(&rcutorture_cpu_nb); + for_each_possible_cpu(i) + rcutorture_booster_cleanup(i); + } + if (shutdown_task != NULL) { + VERBOSE_PRINTK_STRING("Stopping rcu_torture_shutdown task"); + kthread_stop(shutdown_task); + } + shutdown_task = NULL; + rcu_torture_onoff_cleanup(); + + /* Wait for all RCU callbacks to fire. */ + + if (cur_ops->cb_barrier != NULL) + cur_ops->cb_barrier(); + + rcu_torture_stats_print(); /* -After- the stats thread is stopped! */ + + if (atomic_read(&n_rcu_torture_error) || n_rcu_torture_barrier_error) + rcu_torture_print_module_parms(cur_ops, "End of test: FAILURE"); + else if (n_online_successes != n_online_attempts || + n_offline_successes != n_offline_attempts) + rcu_torture_print_module_parms(cur_ops, + "End of test: RCU_HOTPLUG"); + else + rcu_torture_print_module_parms(cur_ops, "End of test: SUCCESS"); +} + +#ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD +static void rcu_torture_leak_cb(struct rcu_head *rhp) +{ +} + +static void rcu_torture_err_cb(struct rcu_head *rhp) +{ + /* + * This -might- happen due to race conditions, but is unlikely. + * The scenario that leads to this happening is that the + * first of the pair of duplicate callbacks is queued, + * someone else starts a grace period that includes that + * callback, then the second of the pair must wait for the + * next grace period. Unlikely, but can happen. If it + * does happen, the debug-objects subsystem won't have splatted. + */ + pr_alert("rcutorture: duplicated callback was invoked.\n"); +} +#endif /* #ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD */ + +/* + * Verify that double-free causes debug-objects to complain, but only + * if CONFIG_DEBUG_OBJECTS_RCU_HEAD=y. Otherwise, say that the test + * cannot be carried out. + */ +static void rcu_test_debug_objects(void) +{ +#ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD + struct rcu_head rh1; + struct rcu_head rh2; + + init_rcu_head_on_stack(&rh1); + init_rcu_head_on_stack(&rh2); + pr_alert("rcutorture: WARN: Duplicate call_rcu() test starting.\n"); + + /* Try to queue the rh2 pair of callbacks for the same grace period. */ + preempt_disable(); /* Prevent preemption from interrupting test. */ + rcu_read_lock(); /* Make it impossible to finish a grace period. */ + call_rcu(&rh1, rcu_torture_leak_cb); /* Start grace period. */ + local_irq_disable(); /* Make it harder to start a new grace period. */ + call_rcu(&rh2, rcu_torture_leak_cb); + call_rcu(&rh2, rcu_torture_err_cb); /* Duplicate callback. */ + local_irq_enable(); + rcu_read_unlock(); + preempt_enable(); + + /* Wait for them all to get done so we can safely return. */ + rcu_barrier(); + pr_alert("rcutorture: WARN: Duplicate call_rcu() test complete.\n"); + destroy_rcu_head_on_stack(&rh1); + destroy_rcu_head_on_stack(&rh2); +#else /* #ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD */ + pr_alert("rcutorture: !CONFIG_DEBUG_OBJECTS_RCU_HEAD, not testing duplicate call_rcu()\n"); +#endif /* #else #ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD */ +} + +static int __init +rcu_torture_init(void) +{ + int i; + int cpu; + int firsterr = 0; + int retval; + static struct rcu_torture_ops *torture_ops[] = { + &rcu_ops, &rcu_bh_ops, &srcu_ops, &sched_ops, + }; + + mutex_lock(&fullstop_mutex); + + /* Process args and tell the world that the torturer is on the job. */ + for (i = 0; i < ARRAY_SIZE(torture_ops); i++) { + cur_ops = torture_ops[i]; + if (strcmp(torture_type, cur_ops->name) == 0) + break; + } + if (i == ARRAY_SIZE(torture_ops)) { + pr_alert("rcu-torture: invalid torture type: \"%s\"\n", + torture_type); + pr_alert("rcu-torture types:"); + for (i = 0; i < ARRAY_SIZE(torture_ops); i++) + pr_alert(" %s", torture_ops[i]->name); + pr_alert("\n"); + mutex_unlock(&fullstop_mutex); + return -EINVAL; + } + if (cur_ops->fqs == NULL && fqs_duration != 0) { + pr_alert("rcu-torture: ->fqs NULL and non-zero fqs_duration, fqs disabled.\n"); + fqs_duration = 0; + } + if (cur_ops->init) + cur_ops->init(); /* no "goto unwind" prior to this point!!! */ + + if (nreaders >= 0) + nrealreaders = nreaders; + else + nrealreaders = 2 * num_online_cpus(); + rcu_torture_print_module_parms(cur_ops, "Start of test"); + fullstop = FULLSTOP_DONTSTOP; + + /* Set up the freelist. */ + + INIT_LIST_HEAD(&rcu_torture_freelist); + for (i = 0; i < ARRAY_SIZE(rcu_tortures); i++) { + rcu_tortures[i].rtort_mbtest = 0; + list_add_tail(&rcu_tortures[i].rtort_free, + &rcu_torture_freelist); + } + + /* Initialize the statistics so that each run gets its own numbers. */ + + rcu_torture_current = NULL; + rcu_torture_current_version = 0; + atomic_set(&n_rcu_torture_alloc, 0); + atomic_set(&n_rcu_torture_alloc_fail, 0); + atomic_set(&n_rcu_torture_free, 0); + atomic_set(&n_rcu_torture_mberror, 0); + atomic_set(&n_rcu_torture_error, 0); + n_rcu_torture_barrier_error = 0; + n_rcu_torture_boost_ktrerror = 0; + n_rcu_torture_boost_rterror = 0; + n_rcu_torture_boost_failure = 0; + n_rcu_torture_boosts = 0; + for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) + atomic_set(&rcu_torture_wcount[i], 0); + for_each_possible_cpu(cpu) { + for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) { + per_cpu(rcu_torture_count, cpu)[i] = 0; + per_cpu(rcu_torture_batch, cpu)[i] = 0; + } + } + + /* Start up the kthreads. */ + + VERBOSE_PRINTK_STRING("Creating rcu_torture_writer task"); + writer_task = kthread_create(rcu_torture_writer, NULL, + "rcu_torture_writer"); + if (IS_ERR(writer_task)) { + firsterr = PTR_ERR(writer_task); + VERBOSE_PRINTK_ERRSTRING("Failed to create writer"); + writer_task = NULL; + goto unwind; + } + wake_up_process(writer_task); + fakewriter_tasks = kzalloc(nfakewriters * sizeof(fakewriter_tasks[0]), + GFP_KERNEL); + if (fakewriter_tasks == NULL) { + VERBOSE_PRINTK_ERRSTRING("out of memory"); + firsterr = -ENOMEM; + goto unwind; + } + for (i = 0; i < nfakewriters; i++) { + VERBOSE_PRINTK_STRING("Creating rcu_torture_fakewriter task"); + fakewriter_tasks[i] = kthread_run(rcu_torture_fakewriter, NULL, + "rcu_torture_fakewriter"); + if (IS_ERR(fakewriter_tasks[i])) { + firsterr = PTR_ERR(fakewriter_tasks[i]); + VERBOSE_PRINTK_ERRSTRING("Failed to create fakewriter"); + fakewriter_tasks[i] = NULL; + goto unwind; + } + } + reader_tasks = kzalloc(nrealreaders * sizeof(reader_tasks[0]), + GFP_KERNEL); + if (reader_tasks == NULL) { + VERBOSE_PRINTK_ERRSTRING("out of memory"); + firsterr = -ENOMEM; + goto unwind; + } + for (i = 0; i < nrealreaders; i++) { + VERBOSE_PRINTK_STRING("Creating rcu_torture_reader task"); + reader_tasks[i] = kthread_run(rcu_torture_reader, NULL, + "rcu_torture_reader"); + if (IS_ERR(reader_tasks[i])) { + firsterr = PTR_ERR(reader_tasks[i]); + VERBOSE_PRINTK_ERRSTRING("Failed to create reader"); + reader_tasks[i] = NULL; + goto unwind; + } + } + if (stat_interval > 0) { + VERBOSE_PRINTK_STRING("Creating rcu_torture_stats task"); + stats_task = kthread_run(rcu_torture_stats, NULL, + "rcu_torture_stats"); + if (IS_ERR(stats_task)) { + firsterr = PTR_ERR(stats_task); + VERBOSE_PRINTK_ERRSTRING("Failed to create stats"); + stats_task = NULL; + goto unwind; + } + } + if (test_no_idle_hz) { + rcu_idle_cpu = num_online_cpus() - 1; + + if (!alloc_cpumask_var(&shuffle_tmp_mask, GFP_KERNEL)) { + firsterr = -ENOMEM; + VERBOSE_PRINTK_ERRSTRING("Failed to alloc mask"); + goto unwind; + } + + /* Create the shuffler thread */ + shuffler_task = kthread_run(rcu_torture_shuffle, NULL, + "rcu_torture_shuffle"); + if (IS_ERR(shuffler_task)) { + free_cpumask_var(shuffle_tmp_mask); + firsterr = PTR_ERR(shuffler_task); + VERBOSE_PRINTK_ERRSTRING("Failed to create shuffler"); + shuffler_task = NULL; + goto unwind; + } + } + if (stutter < 0) + stutter = 0; + if (stutter) { + /* Create the stutter thread */ + stutter_task = kthread_run(rcu_torture_stutter, NULL, + "rcu_torture_stutter"); + if (IS_ERR(stutter_task)) { + firsterr = PTR_ERR(stutter_task); + VERBOSE_PRINTK_ERRSTRING("Failed to create stutter"); + stutter_task = NULL; + goto unwind; + } + } + if (fqs_duration < 0) + fqs_duration = 0; + if (fqs_duration) { + /* Create the stutter thread */ + fqs_task = kthread_run(rcu_torture_fqs, NULL, + "rcu_torture_fqs"); + if (IS_ERR(fqs_task)) { + firsterr = PTR_ERR(fqs_task); + VERBOSE_PRINTK_ERRSTRING("Failed to create fqs"); + fqs_task = NULL; + goto unwind; + } + } + if (test_boost_interval < 1) + test_boost_interval = 1; + if (test_boost_duration < 2) + test_boost_duration = 2; + if ((test_boost == 1 && cur_ops->can_boost) || + test_boost == 2) { + + boost_starttime = jiffies + test_boost_interval * HZ; + register_cpu_notifier(&rcutorture_cpu_nb); + for_each_possible_cpu(i) { + if (cpu_is_offline(i)) + continue; /* Heuristic: CPU can go offline. */ + retval = rcutorture_booster_init(i); + if (retval < 0) { + firsterr = retval; + goto unwind; + } + } + } + if (shutdown_secs > 0) { + shutdown_time = jiffies + shutdown_secs * HZ; + shutdown_task = kthread_create(rcu_torture_shutdown, NULL, + "rcu_torture_shutdown"); + if (IS_ERR(shutdown_task)) { + firsterr = PTR_ERR(shutdown_task); + VERBOSE_PRINTK_ERRSTRING("Failed to create shutdown"); + shutdown_task = NULL; + goto unwind; + } + wake_up_process(shutdown_task); + } + i = rcu_torture_onoff_init(); + if (i != 0) { + firsterr = i; + goto unwind; + } + register_reboot_notifier(&rcutorture_shutdown_nb); + i = rcu_torture_stall_init(); + if (i != 0) { + firsterr = i; + goto unwind; + } + retval = rcu_torture_barrier_init(); + if (retval != 0) { + firsterr = retval; + goto unwind; + } + if (object_debug) + rcu_test_debug_objects(); + rcutorture_record_test_transition(); + mutex_unlock(&fullstop_mutex); + return 0; + +unwind: + mutex_unlock(&fullstop_mutex); + rcu_torture_cleanup(); + return firsterr; +} + +module_init(rcu_torture_init); +module_exit(rcu_torture_cleanup); diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c new file mode 100644 index 0000000..8a2c81e --- /dev/null +++ b/kernel/rcu/tree.c @@ -0,0 +1,3403 @@ +/* + * Read-Copy Update mechanism for mutual exclusion + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright IBM Corporation, 2008 + * + * Authors: Dipankar Sarma + * Manfred Spraul + * Paul E. McKenney Hierarchical version + * + * Based on the original work by Paul McKenney + * and inputs from Rusty Russell, Andrea Arcangeli and Andi Kleen. + * + * For detailed explanation of Read-Copy Update mechanism see - + * Documentation/RCU + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tree.h" +#include + +#include "rcu.h" + +MODULE_ALIAS("rcutree"); +#ifdef MODULE_PARAM_PREFIX +#undef MODULE_PARAM_PREFIX +#endif +#define MODULE_PARAM_PREFIX "rcutree." + +/* Data structures. */ + +static struct lock_class_key rcu_node_class[RCU_NUM_LVLS]; +static struct lock_class_key rcu_fqs_class[RCU_NUM_LVLS]; + +/* + * In order to export the rcu_state name to the tracing tools, it + * needs to be added in the __tracepoint_string section. + * This requires defining a separate variable tp__varname + * that points to the string being used, and this will allow + * the tracing userspace tools to be able to decipher the string + * address to the matching string. + */ +#define RCU_STATE_INITIALIZER(sname, sabbr, cr) \ +static char sname##_varname[] = #sname; \ +static const char *tp_##sname##_varname __used __tracepoint_string = sname##_varname; \ +struct rcu_state sname##_state = { \ + .level = { &sname##_state.node[0] }, \ + .call = cr, \ + .fqs_state = RCU_GP_IDLE, \ + .gpnum = 0UL - 300UL, \ + .completed = 0UL - 300UL, \ + .orphan_lock = __RAW_SPIN_LOCK_UNLOCKED(&sname##_state.orphan_lock), \ + .orphan_nxttail = &sname##_state.orphan_nxtlist, \ + .orphan_donetail = &sname##_state.orphan_donelist, \ + .barrier_mutex = __MUTEX_INITIALIZER(sname##_state.barrier_mutex), \ + .onoff_mutex = __MUTEX_INITIALIZER(sname##_state.onoff_mutex), \ + .name = sname##_varname, \ + .abbr = sabbr, \ +}; \ +DEFINE_PER_CPU(struct rcu_data, sname##_data) + +RCU_STATE_INITIALIZER(rcu_sched, 's', call_rcu_sched); +RCU_STATE_INITIALIZER(rcu_bh, 'b', call_rcu_bh); + +static struct rcu_state *rcu_state; +LIST_HEAD(rcu_struct_flavors); + +/* Increase (but not decrease) the CONFIG_RCU_FANOUT_LEAF at boot time. */ +static int rcu_fanout_leaf = CONFIG_RCU_FANOUT_LEAF; +module_param(rcu_fanout_leaf, int, 0444); +int rcu_num_lvls __read_mostly = RCU_NUM_LVLS; +static int num_rcu_lvl[] = { /* Number of rcu_nodes at specified level. */ + NUM_RCU_LVL_0, + NUM_RCU_LVL_1, + NUM_RCU_LVL_2, + NUM_RCU_LVL_3, + NUM_RCU_LVL_4, +}; +int rcu_num_nodes __read_mostly = NUM_RCU_NODES; /* Total # rcu_nodes in use. */ + +/* + * The rcu_scheduler_active variable transitions from zero to one just + * before the first task is spawned. So when this variable is zero, RCU + * can assume that there is but one task, allowing RCU to (for example) + * optimize synchronize_sched() to a simple barrier(). When this variable + * is one, RCU must actually do all the hard work required to detect real + * grace periods. This variable is also used to suppress boot-time false + * positives from lockdep-RCU error checking. + */ +int rcu_scheduler_active __read_mostly; +EXPORT_SYMBOL_GPL(rcu_scheduler_active); + +/* + * The rcu_scheduler_fully_active variable transitions from zero to one + * during the early_initcall() processing, which is after the scheduler + * is capable of creating new tasks. So RCU processing (for example, + * creating tasks for RCU priority boosting) must be delayed until after + * rcu_scheduler_fully_active transitions from zero to one. We also + * currently delay invocation of any RCU callbacks until after this point. + * + * It might later prove better for people registering RCU callbacks during + * early boot to take responsibility for these callbacks, but one step at + * a time. + */ +static int rcu_scheduler_fully_active __read_mostly; + +#ifdef CONFIG_RCU_BOOST + +/* + * Control variables for per-CPU and per-rcu_node kthreads. These + * handle all flavors of RCU. + */ +static DEFINE_PER_CPU(struct task_struct *, rcu_cpu_kthread_task); +DEFINE_PER_CPU(unsigned int, rcu_cpu_kthread_status); +DEFINE_PER_CPU(unsigned int, rcu_cpu_kthread_loops); +DEFINE_PER_CPU(char, rcu_cpu_has_work); + +#endif /* #ifdef CONFIG_RCU_BOOST */ + +static void rcu_boost_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu); +static void invoke_rcu_core(void); +static void invoke_rcu_callbacks(struct rcu_state *rsp, struct rcu_data *rdp); + +/* + * Track the rcutorture test sequence number and the update version + * number within a given test. The rcutorture_testseq is incremented + * on every rcutorture module load and unload, so has an odd value + * when a test is running. The rcutorture_vernum is set to zero + * when rcutorture starts and is incremented on each rcutorture update. + * These variables enable correlating rcutorture output with the + * RCU tracing information. + */ +unsigned long rcutorture_testseq; +unsigned long rcutorture_vernum; + +/* + * Return true if an RCU grace period is in progress. The ACCESS_ONCE()s + * permit this function to be invoked without holding the root rcu_node + * structure's ->lock, but of course results can be subject to change. + */ +static int rcu_gp_in_progress(struct rcu_state *rsp) +{ + return ACCESS_ONCE(rsp->completed) != ACCESS_ONCE(rsp->gpnum); +} + +/* + * Note a quiescent state. Because we do not need to know + * how many quiescent states passed, just if there was at least + * one since the start of the grace period, this just sets a flag. + * The caller must have disabled preemption. + */ +void rcu_sched_qs(int cpu) +{ + struct rcu_data *rdp = &per_cpu(rcu_sched_data, cpu); + + if (rdp->passed_quiesce == 0) + trace_rcu_grace_period(TPS("rcu_sched"), rdp->gpnum, TPS("cpuqs")); + rdp->passed_quiesce = 1; +} + +void rcu_bh_qs(int cpu) +{ + struct rcu_data *rdp = &per_cpu(rcu_bh_data, cpu); + + if (rdp->passed_quiesce == 0) + trace_rcu_grace_period(TPS("rcu_bh"), rdp->gpnum, TPS("cpuqs")); + rdp->passed_quiesce = 1; +} + +/* + * Note a context switch. This is a quiescent state for RCU-sched, + * and requires special handling for preemptible RCU. + * The caller must have disabled preemption. + */ +void rcu_note_context_switch(int cpu) +{ + trace_rcu_utilization(TPS("Start context switch")); + rcu_sched_qs(cpu); + rcu_preempt_note_context_switch(cpu); + trace_rcu_utilization(TPS("End context switch")); +} +EXPORT_SYMBOL_GPL(rcu_note_context_switch); + +static DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks) = { + .dynticks_nesting = DYNTICK_TASK_EXIT_IDLE, + .dynticks = ATOMIC_INIT(1), +#ifdef CONFIG_NO_HZ_FULL_SYSIDLE + .dynticks_idle_nesting = DYNTICK_TASK_NEST_VALUE, + .dynticks_idle = ATOMIC_INIT(1), +#endif /* #ifdef CONFIG_NO_HZ_FULL_SYSIDLE */ +}; + +static long blimit = 10; /* Maximum callbacks per rcu_do_batch. */ +static long qhimark = 10000; /* If this many pending, ignore blimit. */ +static long qlowmark = 100; /* Once only this many pending, use blimit. */ + +module_param(blimit, long, 0444); +module_param(qhimark, long, 0444); +module_param(qlowmark, long, 0444); + +static ulong jiffies_till_first_fqs = ULONG_MAX; +static ulong jiffies_till_next_fqs = ULONG_MAX; + +module_param(jiffies_till_first_fqs, ulong, 0644); +module_param(jiffies_till_next_fqs, ulong, 0644); + +static void rcu_start_gp_advanced(struct rcu_state *rsp, struct rcu_node *rnp, + struct rcu_data *rdp); +static void force_qs_rnp(struct rcu_state *rsp, + int (*f)(struct rcu_data *rsp, bool *isidle, + unsigned long *maxj), + bool *isidle, unsigned long *maxj); +static void force_quiescent_state(struct rcu_state *rsp); +static int rcu_pending(int cpu); + +/* + * Return the number of RCU-sched batches processed thus far for debug & stats. + */ +long rcu_batches_completed_sched(void) +{ + return rcu_sched_state.completed; +} +EXPORT_SYMBOL_GPL(rcu_batches_completed_sched); + +/* + * Return the number of RCU BH batches processed thus far for debug & stats. + */ +long rcu_batches_completed_bh(void) +{ + return rcu_bh_state.completed; +} +EXPORT_SYMBOL_GPL(rcu_batches_completed_bh); + +/* + * Force a quiescent state for RCU BH. + */ +void rcu_bh_force_quiescent_state(void) +{ + force_quiescent_state(&rcu_bh_state); +} +EXPORT_SYMBOL_GPL(rcu_bh_force_quiescent_state); + +/* + * Record the number of times rcutorture tests have been initiated and + * terminated. This information allows the debugfs tracing stats to be + * correlated to the rcutorture messages, even when the rcutorture module + * is being repeatedly loaded and unloaded. In other words, we cannot + * store this state in rcutorture itself. + */ +void rcutorture_record_test_transition(void) +{ + rcutorture_testseq++; + rcutorture_vernum = 0; +} +EXPORT_SYMBOL_GPL(rcutorture_record_test_transition); + +/* + * Record the number of writer passes through the current rcutorture test. + * This is also used to correlate debugfs tracing stats with the rcutorture + * messages. + */ +void rcutorture_record_progress(unsigned long vernum) +{ + rcutorture_vernum++; +} +EXPORT_SYMBOL_GPL(rcutorture_record_progress); + +/* + * Force a quiescent state for RCU-sched. + */ +void rcu_sched_force_quiescent_state(void) +{ + force_quiescent_state(&rcu_sched_state); +} +EXPORT_SYMBOL_GPL(rcu_sched_force_quiescent_state); + +/* + * Does the CPU have callbacks ready to be invoked? + */ +static int +cpu_has_callbacks_ready_to_invoke(struct rcu_data *rdp) +{ + return &rdp->nxtlist != rdp->nxttail[RCU_DONE_TAIL] && + rdp->nxttail[RCU_DONE_TAIL] != NULL; +} + +/* + * Does the current CPU require a not-yet-started grace period? + * The caller must have disabled interrupts to prevent races with + * normal callback registry. + */ +static int +cpu_needs_another_gp(struct rcu_state *rsp, struct rcu_data *rdp) +{ + int i; + + if (rcu_gp_in_progress(rsp)) + return 0; /* No, a grace period is already in progress. */ + if (rcu_nocb_needs_gp(rsp)) + return 1; /* Yes, a no-CBs CPU needs one. */ + if (!rdp->nxttail[RCU_NEXT_TAIL]) + return 0; /* No, this is a no-CBs (or offline) CPU. */ + if (*rdp->nxttail[RCU_NEXT_READY_TAIL]) + return 1; /* Yes, this CPU has newly registered callbacks. */ + for (i = RCU_WAIT_TAIL; i < RCU_NEXT_TAIL; i++) + if (rdp->nxttail[i - 1] != rdp->nxttail[i] && + ULONG_CMP_LT(ACCESS_ONCE(rsp->completed), + rdp->nxtcompleted[i])) + return 1; /* Yes, CBs for future grace period. */ + return 0; /* No grace period needed. */ +} + +/* + * Return the root node of the specified rcu_state structure. + */ +static struct rcu_node *rcu_get_root(struct rcu_state *rsp) +{ + return &rsp->node[0]; +} + +/* + * rcu_eqs_enter_common - current CPU is moving towards extended quiescent state + * + * If the new value of the ->dynticks_nesting counter now is zero, + * we really have entered idle, and must do the appropriate accounting. + * The caller must have disabled interrupts. + */ +static void rcu_eqs_enter_common(struct rcu_dynticks *rdtp, long long oldval, + bool user) +{ + trace_rcu_dyntick(TPS("Start"), oldval, rdtp->dynticks_nesting); + if (!user && !is_idle_task(current)) { + struct task_struct *idle __maybe_unused = + idle_task(smp_processor_id()); + + trace_rcu_dyntick(TPS("Error on entry: not idle task"), oldval, 0); + ftrace_dump(DUMP_ORIG); + WARN_ONCE(1, "Current pid: %d comm: %s / Idle pid: %d comm: %s", + current->pid, current->comm, + idle->pid, idle->comm); /* must be idle task! */ + } + rcu_prepare_for_idle(smp_processor_id()); + /* CPUs seeing atomic_inc() must see prior RCU read-side crit sects */ + smp_mb__before_atomic_inc(); /* See above. */ + atomic_inc(&rdtp->dynticks); + smp_mb__after_atomic_inc(); /* Force ordering with next sojourn. */ + WARN_ON_ONCE(atomic_read(&rdtp->dynticks) & 0x1); + + /* + * It is illegal to enter an extended quiescent state while + * in an RCU read-side critical section. + */ + rcu_lockdep_assert(!lock_is_held(&rcu_lock_map), + "Illegal idle entry in RCU read-side critical section."); + rcu_lockdep_assert(!lock_is_held(&rcu_bh_lock_map), + "Illegal idle entry in RCU-bh read-side critical section."); + rcu_lockdep_assert(!lock_is_held(&rcu_sched_lock_map), + "Illegal idle entry in RCU-sched read-side critical section."); +} + +/* + * Enter an RCU extended quiescent state, which can be either the + * idle loop or adaptive-tickless usermode execution. + */ +static void rcu_eqs_enter(bool user) +{ + long long oldval; + struct rcu_dynticks *rdtp; + + rdtp = this_cpu_ptr(&rcu_dynticks); + oldval = rdtp->dynticks_nesting; + WARN_ON_ONCE((oldval & DYNTICK_TASK_NEST_MASK) == 0); + if ((oldval & DYNTICK_TASK_NEST_MASK) == DYNTICK_TASK_NEST_VALUE) + rdtp->dynticks_nesting = 0; + else + rdtp->dynticks_nesting -= DYNTICK_TASK_NEST_VALUE; + rcu_eqs_enter_common(rdtp, oldval, user); +} + +/** + * rcu_idle_enter - inform RCU that current CPU is entering idle + * + * Enter idle mode, in other words, -leave- the mode in which RCU + * read-side critical sections can occur. (Though RCU read-side + * critical sections can occur in irq handlers in idle, a possibility + * handled by irq_enter() and irq_exit().) + * + * We crowbar the ->dynticks_nesting field to zero to allow for + * the possibility of usermode upcalls having messed up our count + * of interrupt nesting level during the prior busy period. + */ +void rcu_idle_enter(void) +{ + unsigned long flags; + + local_irq_save(flags); + rcu_eqs_enter(false); + rcu_sysidle_enter(this_cpu_ptr(&rcu_dynticks), 0); + local_irq_restore(flags); +} +EXPORT_SYMBOL_GPL(rcu_idle_enter); + +#ifdef CONFIG_RCU_USER_QS +/** + * rcu_user_enter - inform RCU that we are resuming userspace. + * + * Enter RCU idle mode right before resuming userspace. No use of RCU + * is permitted between this call and rcu_user_exit(). This way the + * CPU doesn't need to maintain the tick for RCU maintenance purposes + * when the CPU runs in userspace. + */ +void rcu_user_enter(void) +{ + rcu_eqs_enter(1); +} +#endif /* CONFIG_RCU_USER_QS */ + +/** + * rcu_irq_exit - inform RCU that current CPU is exiting irq towards idle + * + * Exit from an interrupt handler, which might possibly result in entering + * idle mode, in other words, leaving the mode in which read-side critical + * sections can occur. + * + * This code assumes that the idle loop never does anything that might + * result in unbalanced calls to irq_enter() and irq_exit(). If your + * architecture violates this assumption, RCU will give you what you + * deserve, good and hard. But very infrequently and irreproducibly. + * + * Use things like work queues to work around this limitation. + * + * You have been warned. + */ +void rcu_irq_exit(void) +{ + unsigned long flags; + long long oldval; + struct rcu_dynticks *rdtp; + + local_irq_save(flags); + rdtp = this_cpu_ptr(&rcu_dynticks); + oldval = rdtp->dynticks_nesting; + rdtp->dynticks_nesting--; + WARN_ON_ONCE(rdtp->dynticks_nesting < 0); + if (rdtp->dynticks_nesting) + trace_rcu_dyntick(TPS("--="), oldval, rdtp->dynticks_nesting); + else + rcu_eqs_enter_common(rdtp, oldval, true); + rcu_sysidle_enter(rdtp, 1); + local_irq_restore(flags); +} + +/* + * rcu_eqs_exit_common - current CPU moving away from extended quiescent state + * + * If the new value of the ->dynticks_nesting counter was previously zero, + * we really have exited idle, and must do the appropriate accounting. + * The caller must have disabled interrupts. + */ +static void rcu_eqs_exit_common(struct rcu_dynticks *rdtp, long long oldval, + int user) +{ + smp_mb__before_atomic_inc(); /* Force ordering w/previous sojourn. */ + atomic_inc(&rdtp->dynticks); + /* CPUs seeing atomic_inc() must see later RCU read-side crit sects */ + smp_mb__after_atomic_inc(); /* See above. */ + WARN_ON_ONCE(!(atomic_read(&rdtp->dynticks) & 0x1)); + rcu_cleanup_after_idle(smp_processor_id()); + trace_rcu_dyntick(TPS("End"), oldval, rdtp->dynticks_nesting); + if (!user && !is_idle_task(current)) { + struct task_struct *idle __maybe_unused = + idle_task(smp_processor_id()); + + trace_rcu_dyntick(TPS("Error on exit: not idle task"), + oldval, rdtp->dynticks_nesting); + ftrace_dump(DUMP_ORIG); + WARN_ONCE(1, "Current pid: %d comm: %s / Idle pid: %d comm: %s", + current->pid, current->comm, + idle->pid, idle->comm); /* must be idle task! */ + } +} + +/* + * Exit an RCU extended quiescent state, which can be either the + * idle loop or adaptive-tickless usermode execution. + */ +static void rcu_eqs_exit(bool user) +{ + struct rcu_dynticks *rdtp; + long long oldval; + + rdtp = this_cpu_ptr(&rcu_dynticks); + oldval = rdtp->dynticks_nesting; + WARN_ON_ONCE(oldval < 0); + if (oldval & DYNTICK_TASK_NEST_MASK) + rdtp->dynticks_nesting += DYNTICK_TASK_NEST_VALUE; + else + rdtp->dynticks_nesting = DYNTICK_TASK_EXIT_IDLE; + rcu_eqs_exit_common(rdtp, oldval, user); +} + +/** + * rcu_idle_exit - inform RCU that current CPU is leaving idle + * + * Exit idle mode, in other words, -enter- the mode in which RCU + * read-side critical sections can occur. + * + * We crowbar the ->dynticks_nesting field to DYNTICK_TASK_NEST to + * allow for the possibility of usermode upcalls messing up our count + * of interrupt nesting level during the busy period that is just + * now starting. + */ +void rcu_idle_exit(void) +{ + unsigned long flags; + + local_irq_save(flags); + rcu_eqs_exit(false); + rcu_sysidle_exit(this_cpu_ptr(&rcu_dynticks), 0); + local_irq_restore(flags); +} +EXPORT_SYMBOL_GPL(rcu_idle_exit); + +#ifdef CONFIG_RCU_USER_QS +/** + * rcu_user_exit - inform RCU that we are exiting userspace. + * + * Exit RCU idle mode while entering the kernel because it can + * run a RCU read side critical section anytime. + */ +void rcu_user_exit(void) +{ + rcu_eqs_exit(1); +} +#endif /* CONFIG_RCU_USER_QS */ + +/** + * rcu_irq_enter - inform RCU that current CPU is entering irq away from idle + * + * Enter an interrupt handler, which might possibly result in exiting + * idle mode, in other words, entering the mode in which read-side critical + * sections can occur. + * + * Note that the Linux kernel is fully capable of entering an interrupt + * handler that it never exits, for example when doing upcalls to + * user mode! This code assumes that the idle loop never does upcalls to + * user mode. If your architecture does do upcalls from the idle loop (or + * does anything else that results in unbalanced calls to the irq_enter() + * and irq_exit() functions), RCU will give you what you deserve, good + * and hard. But very infrequently and irreproducibly. + * + * Use things like work queues to work around this limitation. + * + * You have been warned. + */ +void rcu_irq_enter(void) +{ + unsigned long flags; + struct rcu_dynticks *rdtp; + long long oldval; + + local_irq_save(flags); + rdtp = this_cpu_ptr(&rcu_dynticks); + oldval = rdtp->dynticks_nesting; + rdtp->dynticks_nesting++; + WARN_ON_ONCE(rdtp->dynticks_nesting == 0); + if (oldval) + trace_rcu_dyntick(TPS("++="), oldval, rdtp->dynticks_nesting); + else + rcu_eqs_exit_common(rdtp, oldval, true); + rcu_sysidle_exit(rdtp, 1); + local_irq_restore(flags); +} + +/** + * rcu_nmi_enter - inform RCU of entry to NMI context + * + * If the CPU was idle with dynamic ticks active, and there is no + * irq handler running, this updates rdtp->dynticks_nmi to let the + * RCU grace-period handling know that the CPU is active. + */ +void rcu_nmi_enter(void) +{ + struct rcu_dynticks *rdtp = this_cpu_ptr(&rcu_dynticks); + + if (rdtp->dynticks_nmi_nesting == 0 && + (atomic_read(&rdtp->dynticks) & 0x1)) + return; + rdtp->dynticks_nmi_nesting++; + smp_mb__before_atomic_inc(); /* Force delay from prior write. */ + atomic_inc(&rdtp->dynticks); + /* CPUs seeing atomic_inc() must see later RCU read-side crit sects */ + smp_mb__after_atomic_inc(); /* See above. */ + WARN_ON_ONCE(!(atomic_read(&rdtp->dynticks) & 0x1)); +} + +/** + * rcu_nmi_exit - inform RCU of exit from NMI context + * + * If the CPU was idle with dynamic ticks active, and there is no + * irq handler running, this updates rdtp->dynticks_nmi to let the + * RCU grace-period handling know that the CPU is no longer active. + */ +void rcu_nmi_exit(void) +{ + struct rcu_dynticks *rdtp = this_cpu_ptr(&rcu_dynticks); + + if (rdtp->dynticks_nmi_nesting == 0 || + --rdtp->dynticks_nmi_nesting != 0) + return; + /* CPUs seeing atomic_inc() must see prior RCU read-side crit sects */ + smp_mb__before_atomic_inc(); /* See above. */ + atomic_inc(&rdtp->dynticks); + smp_mb__after_atomic_inc(); /* Force delay to next write. */ + WARN_ON_ONCE(atomic_read(&rdtp->dynticks) & 0x1); +} + +/** + * __rcu_is_watching - are RCU read-side critical sections safe? + * + * Return true if RCU is watching the running CPU, which means that + * this CPU can safely enter RCU read-side critical sections. Unlike + * rcu_is_watching(), the caller of __rcu_is_watching() must have at + * least disabled preemption. + */ +bool __rcu_is_watching(void) +{ + return atomic_read(this_cpu_ptr(&rcu_dynticks.dynticks)) & 0x1; +} + +/** + * rcu_is_watching - see if RCU thinks that the current CPU is idle + * + * If the current CPU is in its idle loop and is neither in an interrupt + * or NMI handler, return true. + */ +bool rcu_is_watching(void) +{ + int ret; + + preempt_disable(); + ret = __rcu_is_watching(); + preempt_enable(); + return ret; +} +EXPORT_SYMBOL_GPL(rcu_is_watching); + +#if defined(CONFIG_PROVE_RCU) && defined(CONFIG_HOTPLUG_CPU) + +/* + * Is the current CPU online? Disable preemption to avoid false positives + * that could otherwise happen due to the current CPU number being sampled, + * this task being preempted, its old CPU being taken offline, resuming + * on some other CPU, then determining that its old CPU is now offline. + * It is OK to use RCU on an offline processor during initial boot, hence + * the check for rcu_scheduler_fully_active. Note also that it is OK + * for a CPU coming online to use RCU for one jiffy prior to marking itself + * online in the cpu_online_mask. Similarly, it is OK for a CPU going + * offline to continue to use RCU for one jiffy after marking itself + * offline in the cpu_online_mask. This leniency is necessary given the + * non-atomic nature of the online and offline processing, for example, + * the fact that a CPU enters the scheduler after completing the CPU_DYING + * notifiers. + * + * This is also why RCU internally marks CPUs online during the + * CPU_UP_PREPARE phase and offline during the CPU_DEAD phase. + * + * Disable checking if in an NMI handler because we cannot safely report + * errors from NMI handlers anyway. + */ +bool rcu_lockdep_current_cpu_online(void) +{ + struct rcu_data *rdp; + struct rcu_node *rnp; + bool ret; + + if (in_nmi()) + return 1; + preempt_disable(); + rdp = this_cpu_ptr(&rcu_sched_data); + rnp = rdp->mynode; + ret = (rdp->grpmask & rnp->qsmaskinit) || + !rcu_scheduler_fully_active; + preempt_enable(); + return ret; +} +EXPORT_SYMBOL_GPL(rcu_lockdep_current_cpu_online); + +#endif /* #if defined(CONFIG_PROVE_RCU) && defined(CONFIG_HOTPLUG_CPU) */ + +/** + * rcu_is_cpu_rrupt_from_idle - see if idle or immediately interrupted from idle + * + * If the current CPU is idle or running at a first-level (not nested) + * interrupt from idle, return true. The caller must have at least + * disabled preemption. + */ +static int rcu_is_cpu_rrupt_from_idle(void) +{ + return __this_cpu_read(rcu_dynticks.dynticks_nesting) <= 1; +} + +/* + * Snapshot the specified CPU's dynticks counter so that we can later + * credit them with an implicit quiescent state. Return 1 if this CPU + * is in dynticks idle mode, which is an extended quiescent state. + */ +static int dyntick_save_progress_counter(struct rcu_data *rdp, + bool *isidle, unsigned long *maxj) +{ + rdp->dynticks_snap = atomic_add_return(0, &rdp->dynticks->dynticks); + rcu_sysidle_check_cpu(rdp, isidle, maxj); + return (rdp->dynticks_snap & 0x1) == 0; +} + +/* + * Return true if the specified CPU has passed through a quiescent + * state by virtue of being in or having passed through an dynticks + * idle state since the last call to dyntick_save_progress_counter() + * for this same CPU, or by virtue of having been offline. + */ +static int rcu_implicit_dynticks_qs(struct rcu_data *rdp, + bool *isidle, unsigned long *maxj) +{ + unsigned int curr; + unsigned int snap; + + curr = (unsigned int)atomic_add_return(0, &rdp->dynticks->dynticks); + snap = (unsigned int)rdp->dynticks_snap; + + /* + * If the CPU passed through or entered a dynticks idle phase with + * no active irq/NMI handlers, then we can safely pretend that the CPU + * already acknowledged the request to pass through a quiescent + * state. Either way, that CPU cannot possibly be in an RCU + * read-side critical section that started before the beginning + * of the current RCU grace period. + */ + if ((curr & 0x1) == 0 || UINT_CMP_GE(curr, snap + 2)) { + trace_rcu_fqs(rdp->rsp->name, rdp->gpnum, rdp->cpu, TPS("dti")); + rdp->dynticks_fqs++; + return 1; + } + + /* + * Check for the CPU being offline, but only if the grace period + * is old enough. We don't need to worry about the CPU changing + * state: If we see it offline even once, it has been through a + * quiescent state. + * + * The reason for insisting that the grace period be at least + * one jiffy old is that CPUs that are not quite online and that + * have just gone offline can still execute RCU read-side critical + * sections. + */ + if (ULONG_CMP_GE(rdp->rsp->gp_start + 2, jiffies)) + return 0; /* Grace period is not old enough. */ + barrier(); + if (cpu_is_offline(rdp->cpu)) { + trace_rcu_fqs(rdp->rsp->name, rdp->gpnum, rdp->cpu, TPS("ofl")); + rdp->offline_fqs++; + return 1; + } + + /* + * There is a possibility that a CPU in adaptive-ticks state + * might run in the kernel with the scheduling-clock tick disabled + * for an extended time period. Invoke rcu_kick_nohz_cpu() to + * force the CPU to restart the scheduling-clock tick in this + * CPU is in this state. + */ + rcu_kick_nohz_cpu(rdp->cpu); + + return 0; +} + +static void record_gp_stall_check_time(struct rcu_state *rsp) +{ + unsigned long j = ACCESS_ONCE(jiffies); + + rsp->gp_start = j; + smp_wmb(); /* Record start time before stall time. */ + rsp->jiffies_stall = j + rcu_jiffies_till_stall_check(); +} + +/* + * Dump stacks of all tasks running on stalled CPUs. This is a fallback + * for architectures that do not implement trigger_all_cpu_backtrace(). + * The NMI-triggered stack traces are more accurate because they are + * printed by the target CPU. + */ +static void rcu_dump_cpu_stacks(struct rcu_state *rsp) +{ + int cpu; + unsigned long flags; + struct rcu_node *rnp; + + rcu_for_each_leaf_node(rsp, rnp) { + raw_spin_lock_irqsave(&rnp->lock, flags); + if (rnp->qsmask != 0) { + for (cpu = 0; cpu <= rnp->grphi - rnp->grplo; cpu++) + if (rnp->qsmask & (1UL << cpu)) + dump_cpu_task(rnp->grplo + cpu); + } + raw_spin_unlock_irqrestore(&rnp->lock, flags); + } +} + +static void print_other_cpu_stall(struct rcu_state *rsp) +{ + int cpu; + long delta; + unsigned long flags; + int ndetected = 0; + struct rcu_node *rnp = rcu_get_root(rsp); + long totqlen = 0; + + /* Only let one CPU complain about others per time interval. */ + + raw_spin_lock_irqsave(&rnp->lock, flags); + delta = jiffies - rsp->jiffies_stall; + if (delta < RCU_STALL_RAT_DELAY || !rcu_gp_in_progress(rsp)) { + raw_spin_unlock_irqrestore(&rnp->lock, flags); + return; + } + rsp->jiffies_stall = jiffies + 3 * rcu_jiffies_till_stall_check() + 3; + raw_spin_unlock_irqrestore(&rnp->lock, flags); + + /* + * OK, time to rat on our buddy... + * See Documentation/RCU/stallwarn.txt for info on how to debug + * RCU CPU stall warnings. + */ + pr_err("INFO: %s detected stalls on CPUs/tasks:", + rsp->name); + print_cpu_stall_info_begin(); + rcu_for_each_leaf_node(rsp, rnp) { + raw_spin_lock_irqsave(&rnp->lock, flags); + ndetected += rcu_print_task_stall(rnp); + if (rnp->qsmask != 0) { + for (cpu = 0; cpu <= rnp->grphi - rnp->grplo; cpu++) + if (rnp->qsmask & (1UL << cpu)) { + print_cpu_stall_info(rsp, + rnp->grplo + cpu); + ndetected++; + } + } + raw_spin_unlock_irqrestore(&rnp->lock, flags); + } + + /* + * Now rat on any tasks that got kicked up to the root rcu_node + * due to CPU offlining. + */ + rnp = rcu_get_root(rsp); + raw_spin_lock_irqsave(&rnp->lock, flags); + ndetected += rcu_print_task_stall(rnp); + raw_spin_unlock_irqrestore(&rnp->lock, flags); + + print_cpu_stall_info_end(); + for_each_possible_cpu(cpu) + totqlen += per_cpu_ptr(rsp->rda, cpu)->qlen; + pr_cont("(detected by %d, t=%ld jiffies, g=%lu, c=%lu, q=%lu)\n", + smp_processor_id(), (long)(jiffies - rsp->gp_start), + rsp->gpnum, rsp->completed, totqlen); + if (ndetected == 0) + pr_err("INFO: Stall ended before state dump start\n"); + else if (!trigger_all_cpu_backtrace()) + rcu_dump_cpu_stacks(rsp); + + /* Complain about tasks blocking the grace period. */ + + rcu_print_detail_task_stall(rsp); + + force_quiescent_state(rsp); /* Kick them all. */ +} + +static void print_cpu_stall(struct rcu_state *rsp) +{ + int cpu; + unsigned long flags; + struct rcu_node *rnp = rcu_get_root(rsp); + long totqlen = 0; + + /* + * OK, time to rat on ourselves... + * See Documentation/RCU/stallwarn.txt for info on how to debug + * RCU CPU stall warnings. + */ + pr_err("INFO: %s self-detected stall on CPU", rsp->name); + print_cpu_stall_info_begin(); + print_cpu_stall_info(rsp, smp_processor_id()); + print_cpu_stall_info_end(); + for_each_possible_cpu(cpu) + totqlen += per_cpu_ptr(rsp->rda, cpu)->qlen; + pr_cont(" (t=%lu jiffies g=%lu c=%lu q=%lu)\n", + jiffies - rsp->gp_start, rsp->gpnum, rsp->completed, totqlen); + if (!trigger_all_cpu_backtrace()) + dump_stack(); + + raw_spin_lock_irqsave(&rnp->lock, flags); + if (ULONG_CMP_GE(jiffies, rsp->jiffies_stall)) + rsp->jiffies_stall = jiffies + + 3 * rcu_jiffies_till_stall_check() + 3; + raw_spin_unlock_irqrestore(&rnp->lock, flags); + + set_need_resched(); /* kick ourselves to get things going. */ +} + +static void check_cpu_stall(struct rcu_state *rsp, struct rcu_data *rdp) +{ + unsigned long completed; + unsigned long gpnum; + unsigned long gps; + unsigned long j; + unsigned long js; + struct rcu_node *rnp; + + if (rcu_cpu_stall_suppress || !rcu_gp_in_progress(rsp)) + return; + j = ACCESS_ONCE(jiffies); + + /* + * Lots of memory barriers to reject false positives. + * + * The idea is to pick up rsp->gpnum, then rsp->jiffies_stall, + * then rsp->gp_start, and finally rsp->completed. These values + * are updated in the opposite order with memory barriers (or + * equivalent) during grace-period initialization and cleanup. + * Now, a false positive can occur if we get an new value of + * rsp->gp_start and a old value of rsp->jiffies_stall. But given + * the memory barriers, the only way that this can happen is if one + * grace period ends and another starts between these two fetches. + * Detect this by comparing rsp->completed with the previous fetch + * from rsp->gpnum. + * + * Given this check, comparisons of jiffies, rsp->jiffies_stall, + * and rsp->gp_start suffice to forestall false positives. + */ + gpnum = ACCESS_ONCE(rsp->gpnum); + smp_rmb(); /* Pick up ->gpnum first... */ + js = ACCESS_ONCE(rsp->jiffies_stall); + smp_rmb(); /* ...then ->jiffies_stall before the rest... */ + gps = ACCESS_ONCE(rsp->gp_start); + smp_rmb(); /* ...and finally ->gp_start before ->completed. */ + completed = ACCESS_ONCE(rsp->completed); + if (ULONG_CMP_GE(completed, gpnum) || + ULONG_CMP_LT(j, js) || + ULONG_CMP_GE(gps, js)) + return; /* No stall or GP completed since entering function. */ + rnp = rdp->mynode; + if (rcu_gp_in_progress(rsp) && + (ACCESS_ONCE(rnp->qsmask) & rdp->grpmask)) { + + /* We haven't checked in, so go dump stack. */ + print_cpu_stall(rsp); + + } else if (rcu_gp_in_progress(rsp) && + ULONG_CMP_GE(j, js + RCU_STALL_RAT_DELAY)) { + + /* They had a few time units to dump stack, so complain. */ + print_other_cpu_stall(rsp); + } +} + +/** + * rcu_cpu_stall_reset - prevent further stall warnings in current grace period + * + * Set the stall-warning timeout way off into the future, thus preventing + * any RCU CPU stall-warning messages from appearing in the current set of + * RCU grace periods. + * + * The caller must disable hard irqs. + */ +void rcu_cpu_stall_reset(void) +{ + struct rcu_state *rsp; + + for_each_rcu_flavor(rsp) + rsp->jiffies_stall = jiffies + ULONG_MAX / 2; +} + +/* + * Initialize the specified rcu_data structure's callback list to empty. + */ +static void init_callback_list(struct rcu_data *rdp) +{ + int i; + + if (init_nocb_callback_list(rdp)) + return; + rdp->nxtlist = NULL; + for (i = 0; i < RCU_NEXT_SIZE; i++) + rdp->nxttail[i] = &rdp->nxtlist; +} + +/* + * Determine the value that ->completed will have at the end of the + * next subsequent grace period. This is used to tag callbacks so that + * a CPU can invoke callbacks in a timely fashion even if that CPU has + * been dyntick-idle for an extended period with callbacks under the + * influence of RCU_FAST_NO_HZ. + * + * The caller must hold rnp->lock with interrupts disabled. + */ +static unsigned long rcu_cbs_completed(struct rcu_state *rsp, + struct rcu_node *rnp) +{ + /* + * If RCU is idle, we just wait for the next grace period. + * But we can only be sure that RCU is idle if we are looking + * at the root rcu_node structure -- otherwise, a new grace + * period might have started, but just not yet gotten around + * to initializing the current non-root rcu_node structure. + */ + if (rcu_get_root(rsp) == rnp && rnp->gpnum == rnp->completed) + return rnp->completed + 1; + + /* + * Otherwise, wait for a possible partial grace period and + * then the subsequent full grace period. + */ + return rnp->completed + 2; +} + +/* + * Trace-event helper function for rcu_start_future_gp() and + * rcu_nocb_wait_gp(). + */ +static void trace_rcu_future_gp(struct rcu_node *rnp, struct rcu_data *rdp, + unsigned long c, const char *s) +{ + trace_rcu_future_grace_period(rdp->rsp->name, rnp->gpnum, + rnp->completed, c, rnp->level, + rnp->grplo, rnp->grphi, s); +} + +/* + * Start some future grace period, as needed to handle newly arrived + * callbacks. The required future grace periods are recorded in each + * rcu_node structure's ->need_future_gp field. + * + * The caller must hold the specified rcu_node structure's ->lock. + */ +static unsigned long __maybe_unused +rcu_start_future_gp(struct rcu_node *rnp, struct rcu_data *rdp) +{ + unsigned long c; + int i; + struct rcu_node *rnp_root = rcu_get_root(rdp->rsp); + + /* + * Pick up grace-period number for new callbacks. If this + * grace period is already marked as needed, return to the caller. + */ + c = rcu_cbs_completed(rdp->rsp, rnp); + trace_rcu_future_gp(rnp, rdp, c, TPS("Startleaf")); + if (rnp->need_future_gp[c & 0x1]) { + trace_rcu_future_gp(rnp, rdp, c, TPS("Prestartleaf")); + return c; + } + + /* + * If either this rcu_node structure or the root rcu_node structure + * believe that a grace period is in progress, then we must wait + * for the one following, which is in "c". Because our request + * will be noticed at the end of the current grace period, we don't + * need to explicitly start one. + */ + if (rnp->gpnum != rnp->completed || + ACCESS_ONCE(rnp->gpnum) != ACCESS_ONCE(rnp->completed)) { + rnp->need_future_gp[c & 0x1]++; + trace_rcu_future_gp(rnp, rdp, c, TPS("Startedleaf")); + return c; + } + + /* + * There might be no grace period in progress. If we don't already + * hold it, acquire the root rcu_node structure's lock in order to + * start one (if needed). + */ + if (rnp != rnp_root) + raw_spin_lock(&rnp_root->lock); + + /* + * Get a new grace-period number. If there really is no grace + * period in progress, it will be smaller than the one we obtained + * earlier. Adjust callbacks as needed. Note that even no-CBs + * CPUs have a ->nxtcompleted[] array, so no no-CBs checks needed. + */ + c = rcu_cbs_completed(rdp->rsp, rnp_root); + for (i = RCU_DONE_TAIL; i < RCU_NEXT_TAIL; i++) + if (ULONG_CMP_LT(c, rdp->nxtcompleted[i])) + rdp->nxtcompleted[i] = c; + + /* + * If the needed for the required grace period is already + * recorded, trace and leave. + */ + if (rnp_root->need_future_gp[c & 0x1]) { + trace_rcu_future_gp(rnp, rdp, c, TPS("Prestartedroot")); + goto unlock_out; + } + + /* Record the need for the future grace period. */ + rnp_root->need_future_gp[c & 0x1]++; + + /* If a grace period is not already in progress, start one. */ + if (rnp_root->gpnum != rnp_root->completed) { + trace_rcu_future_gp(rnp, rdp, c, TPS("Startedleafroot")); + } else { + trace_rcu_future_gp(rnp, rdp, c, TPS("Startedroot")); + rcu_start_gp_advanced(rdp->rsp, rnp_root, rdp); + } +unlock_out: + if (rnp != rnp_root) + raw_spin_unlock(&rnp_root->lock); + return c; +} + +/* + * Clean up any old requests for the just-ended grace period. Also return + * whether any additional grace periods have been requested. Also invoke + * rcu_nocb_gp_cleanup() in order to wake up any no-callbacks kthreads + * waiting for this grace period to complete. + */ +static int rcu_future_gp_cleanup(struct rcu_state *rsp, struct rcu_node *rnp) +{ + int c = rnp->completed; + int needmore; + struct rcu_data *rdp = this_cpu_ptr(rsp->rda); + + rcu_nocb_gp_cleanup(rsp, rnp); + rnp->need_future_gp[c & 0x1] = 0; + needmore = rnp->need_future_gp[(c + 1) & 0x1]; + trace_rcu_future_gp(rnp, rdp, c, + needmore ? TPS("CleanupMore") : TPS("Cleanup")); + return needmore; +} + +/* + * If there is room, assign a ->completed number to any callbacks on + * this CPU that have not already been assigned. Also accelerate any + * callbacks that were previously assigned a ->completed number that has + * since proven to be too conservative, which can happen if callbacks get + * assigned a ->completed number while RCU is idle, but with reference to + * a non-root rcu_node structure. This function is idempotent, so it does + * not hurt to call it repeatedly. + * + * The caller must hold rnp->lock with interrupts disabled. + */ +static void rcu_accelerate_cbs(struct rcu_state *rsp, struct rcu_node *rnp, + struct rcu_data *rdp) +{ + unsigned long c; + int i; + + /* If the CPU has no callbacks, nothing to do. */ + if (!rdp->nxttail[RCU_NEXT_TAIL] || !*rdp->nxttail[RCU_DONE_TAIL]) + return; + + /* + * Starting from the sublist containing the callbacks most + * recently assigned a ->completed number and working down, find the + * first sublist that is not assignable to an upcoming grace period. + * Such a sublist has something in it (first two tests) and has + * a ->completed number assigned that will complete sooner than + * the ->completed number for newly arrived callbacks (last test). + * + * The key point is that any later sublist can be assigned the + * same ->completed number as the newly arrived callbacks, which + * means that the callbacks in any of these later sublist can be + * grouped into a single sublist, whether or not they have already + * been assigned a ->completed number. + */ + c = rcu_cbs_completed(rsp, rnp); + for (i = RCU_NEXT_TAIL - 1; i > RCU_DONE_TAIL; i--) + if (rdp->nxttail[i] != rdp->nxttail[i - 1] && + !ULONG_CMP_GE(rdp->nxtcompleted[i], c)) + break; + + /* + * If there are no sublist for unassigned callbacks, leave. + * At the same time, advance "i" one sublist, so that "i" will + * index into the sublist where all the remaining callbacks should + * be grouped into. + */ + if (++i >= RCU_NEXT_TAIL) + return; + + /* + * Assign all subsequent callbacks' ->completed number to the next + * full grace period and group them all in the sublist initially + * indexed by "i". + */ + for (; i <= RCU_NEXT_TAIL; i++) { + rdp->nxttail[i] = rdp->nxttail[RCU_NEXT_TAIL]; + rdp->nxtcompleted[i] = c; + } + /* Record any needed additional grace periods. */ + rcu_start_future_gp(rnp, rdp); + + /* Trace depending on how much we were able to accelerate. */ + if (!*rdp->nxttail[RCU_WAIT_TAIL]) + trace_rcu_grace_period(rsp->name, rdp->gpnum, TPS("AccWaitCB")); + else + trace_rcu_grace_period(rsp->name, rdp->gpnum, TPS("AccReadyCB")); +} + +/* + * Move any callbacks whose grace period has completed to the + * RCU_DONE_TAIL sublist, then compact the remaining sublists and + * assign ->completed numbers to any callbacks in the RCU_NEXT_TAIL + * sublist. This function is idempotent, so it does not hurt to + * invoke it repeatedly. As long as it is not invoked -too- often... + * + * The caller must hold rnp->lock with interrupts disabled. + */ +static void rcu_advance_cbs(struct rcu_state *rsp, struct rcu_node *rnp, + struct rcu_data *rdp) +{ + int i, j; + + /* If the CPU has no callbacks, nothing to do. */ + if (!rdp->nxttail[RCU_NEXT_TAIL] || !*rdp->nxttail[RCU_DONE_TAIL]) + return; + + /* + * Find all callbacks whose ->completed numbers indicate that they + * are ready to invoke, and put them into the RCU_DONE_TAIL sublist. + */ + for (i = RCU_WAIT_TAIL; i < RCU_NEXT_TAIL; i++) { + if (ULONG_CMP_LT(rnp->completed, rdp->nxtcompleted[i])) + break; + rdp->nxttail[RCU_DONE_TAIL] = rdp->nxttail[i]; + } + /* Clean up any sublist tail pointers that were misordered above. */ + for (j = RCU_WAIT_TAIL; j < i; j++) + rdp->nxttail[j] = rdp->nxttail[RCU_DONE_TAIL]; + + /* Copy down callbacks to fill in empty sublists. */ + for (j = RCU_WAIT_TAIL; i < RCU_NEXT_TAIL; i++, j++) { + if (rdp->nxttail[j] == rdp->nxttail[RCU_NEXT_TAIL]) + break; + rdp->nxttail[j] = rdp->nxttail[i]; + rdp->nxtcompleted[j] = rdp->nxtcompleted[i]; + } + + /* Classify any remaining callbacks. */ + rcu_accelerate_cbs(rsp, rnp, rdp); +} + +/* + * Update CPU-local rcu_data state to record the beginnings and ends of + * grace periods. The caller must hold the ->lock of the leaf rcu_node + * structure corresponding to the current CPU, and must have irqs disabled. + */ +static void __note_gp_changes(struct rcu_state *rsp, struct rcu_node *rnp, struct rcu_data *rdp) +{ + /* Handle the ends of any preceding grace periods first. */ + if (rdp->completed == rnp->completed) { + + /* No grace period end, so just accelerate recent callbacks. */ + rcu_accelerate_cbs(rsp, rnp, rdp); + + } else { + + /* Advance callbacks. */ + rcu_advance_cbs(rsp, rnp, rdp); + + /* Remember that we saw this grace-period completion. */ + rdp->completed = rnp->completed; + trace_rcu_grace_period(rsp->name, rdp->gpnum, TPS("cpuend")); + } + + if (rdp->gpnum != rnp->gpnum) { + /* + * If the current grace period is waiting for this CPU, + * set up to detect a quiescent state, otherwise don't + * go looking for one. + */ + rdp->gpnum = rnp->gpnum; + trace_rcu_grace_period(rsp->name, rdp->gpnum, TPS("cpustart")); + rdp->passed_quiesce = 0; + rdp->qs_pending = !!(rnp->qsmask & rdp->grpmask); + zero_cpu_stall_ticks(rdp); + } +} + +static void note_gp_changes(struct rcu_state *rsp, struct rcu_data *rdp) +{ + unsigned long flags; + struct rcu_node *rnp; + + local_irq_save(flags); + rnp = rdp->mynode; + if ((rdp->gpnum == ACCESS_ONCE(rnp->gpnum) && + rdp->completed == ACCESS_ONCE(rnp->completed)) || /* w/out lock. */ + !raw_spin_trylock(&rnp->lock)) { /* irqs already off, so later. */ + local_irq_restore(flags); + return; + } + __note_gp_changes(rsp, rnp, rdp); + raw_spin_unlock_irqrestore(&rnp->lock, flags); +} + +/* + * Initialize a new grace period. Return 0 if no grace period required. + */ +static int rcu_gp_init(struct rcu_state *rsp) +{ + struct rcu_data *rdp; + struct rcu_node *rnp = rcu_get_root(rsp); + + rcu_bind_gp_kthread(); + raw_spin_lock_irq(&rnp->lock); + if (rsp->gp_flags == 0) { + /* Spurious wakeup, tell caller to go back to sleep. */ + raw_spin_unlock_irq(&rnp->lock); + return 0; + } + rsp->gp_flags = 0; /* Clear all flags: New grace period. */ + + if (WARN_ON_ONCE(rcu_gp_in_progress(rsp))) { + /* + * Grace period already in progress, don't start another. + * Not supposed to be able to happen. + */ + raw_spin_unlock_irq(&rnp->lock); + return 0; + } + + /* Advance to a new grace period and initialize state. */ + record_gp_stall_check_time(rsp); + smp_wmb(); /* Record GP times before starting GP. */ + rsp->gpnum++; + trace_rcu_grace_period(rsp->name, rsp->gpnum, TPS("start")); + raw_spin_unlock_irq(&rnp->lock); + + /* Exclude any concurrent CPU-hotplug operations. */ + mutex_lock(&rsp->onoff_mutex); + + /* + * Set the quiescent-state-needed bits in all the rcu_node + * structures for all currently online CPUs in breadth-first order, + * starting from the root rcu_node structure, relying on the layout + * of the tree within the rsp->node[] array. Note that other CPUs + * will access only the leaves of the hierarchy, thus seeing that no + * grace period is in progress, at least until the corresponding + * leaf node has been initialized. In addition, we have excluded + * CPU-hotplug operations. + * + * The grace period cannot complete until the initialization + * process finishes, because this kthread handles both. + */ + rcu_for_each_node_breadth_first(rsp, rnp) { + raw_spin_lock_irq(&rnp->lock); + rdp = this_cpu_ptr(rsp->rda); + rcu_preempt_check_blocked_tasks(rnp); + rnp->qsmask = rnp->qsmaskinit; + ACCESS_ONCE(rnp->gpnum) = rsp->gpnum; + WARN_ON_ONCE(rnp->completed != rsp->completed); + ACCESS_ONCE(rnp->completed) = rsp->completed; + if (rnp == rdp->mynode) + __note_gp_changes(rsp, rnp, rdp); + rcu_preempt_boost_start_gp(rnp); + trace_rcu_grace_period_init(rsp->name, rnp->gpnum, + rnp->level, rnp->grplo, + rnp->grphi, rnp->qsmask); + raw_spin_unlock_irq(&rnp->lock); +#ifdef CONFIG_PROVE_RCU_DELAY + if ((prandom_u32() % (rcu_num_nodes + 1)) == 0 && + system_state == SYSTEM_RUNNING) + udelay(200); +#endif /* #ifdef CONFIG_PROVE_RCU_DELAY */ + cond_resched(); + } + + mutex_unlock(&rsp->onoff_mutex); + return 1; +} + +/* + * Do one round of quiescent-state forcing. + */ +static int rcu_gp_fqs(struct rcu_state *rsp, int fqs_state_in) +{ + int fqs_state = fqs_state_in; + bool isidle = false; + unsigned long maxj; + struct rcu_node *rnp = rcu_get_root(rsp); + + rsp->n_force_qs++; + if (fqs_state == RCU_SAVE_DYNTICK) { + /* Collect dyntick-idle snapshots. */ + if (is_sysidle_rcu_state(rsp)) { + isidle = 1; + maxj = jiffies - ULONG_MAX / 4; + } + force_qs_rnp(rsp, dyntick_save_progress_counter, + &isidle, &maxj); + rcu_sysidle_report_gp(rsp, isidle, maxj); + fqs_state = RCU_FORCE_QS; + } else { + /* Handle dyntick-idle and offline CPUs. */ + isidle = 0; + force_qs_rnp(rsp, rcu_implicit_dynticks_qs, &isidle, &maxj); + } + /* Clear flag to prevent immediate re-entry. */ + if (ACCESS_ONCE(rsp->gp_flags) & RCU_GP_FLAG_FQS) { + raw_spin_lock_irq(&rnp->lock); + rsp->gp_flags &= ~RCU_GP_FLAG_FQS; + raw_spin_unlock_irq(&rnp->lock); + } + return fqs_state; +} + +/* + * Clean up after the old grace period. + */ +static void rcu_gp_cleanup(struct rcu_state *rsp) +{ + unsigned long gp_duration; + int nocb = 0; + struct rcu_data *rdp; + struct rcu_node *rnp = rcu_get_root(rsp); + + raw_spin_lock_irq(&rnp->lock); + gp_duration = jiffies - rsp->gp_start; + if (gp_duration > rsp->gp_max) + rsp->gp_max = gp_duration; + + /* + * We know the grace period is complete, but to everyone else + * it appears to still be ongoing. But it is also the case + * that to everyone else it looks like there is nothing that + * they can do to advance the grace period. It is therefore + * safe for us to drop the lock in order to mark the grace + * period as completed in all of the rcu_node structures. + */ + raw_spin_unlock_irq(&rnp->lock); + + /* + * Propagate new ->completed value to rcu_node structures so + * that other CPUs don't have to wait until the start of the next + * grace period to process their callbacks. This also avoids + * some nasty RCU grace-period initialization races by forcing + * the end of the current grace period to be completely recorded in + * all of the rcu_node structures before the beginning of the next + * grace period is recorded in any of the rcu_node structures. + */ + rcu_for_each_node_breadth_first(rsp, rnp) { + raw_spin_lock_irq(&rnp->lock); + ACCESS_ONCE(rnp->completed) = rsp->gpnum; + rdp = this_cpu_ptr(rsp->rda); + if (rnp == rdp->mynode) + __note_gp_changes(rsp, rnp, rdp); + nocb += rcu_future_gp_cleanup(rsp, rnp); + raw_spin_unlock_irq(&rnp->lock); + cond_resched(); + } + rnp = rcu_get_root(rsp); + raw_spin_lock_irq(&rnp->lock); + rcu_nocb_gp_set(rnp, nocb); + + rsp->completed = rsp->gpnum; /* Declare grace period done. */ + trace_rcu_grace_period(rsp->name, rsp->completed, TPS("end")); + rsp->fqs_state = RCU_GP_IDLE; + rdp = this_cpu_ptr(rsp->rda); + rcu_advance_cbs(rsp, rnp, rdp); /* Reduce false positives below. */ + if (cpu_needs_another_gp(rsp, rdp)) { + rsp->gp_flags = RCU_GP_FLAG_INIT; + trace_rcu_grace_period(rsp->name, + ACCESS_ONCE(rsp->gpnum), + TPS("newreq")); + } + raw_spin_unlock_irq(&rnp->lock); +} + +/* + * Body of kthread that handles grace periods. + */ +static int __noreturn rcu_gp_kthread(void *arg) +{ + int fqs_state; + int gf; + unsigned long j; + int ret; + struct rcu_state *rsp = arg; + struct rcu_node *rnp = rcu_get_root(rsp); + + for (;;) { + + /* Handle grace-period start. */ + for (;;) { + trace_rcu_grace_period(rsp->name, + ACCESS_ONCE(rsp->gpnum), + TPS("reqwait")); + wait_event_interruptible(rsp->gp_wq, + ACCESS_ONCE(rsp->gp_flags) & + RCU_GP_FLAG_INIT); + if (rcu_gp_init(rsp)) + break; + cond_resched(); + flush_signals(current); + trace_rcu_grace_period(rsp->name, + ACCESS_ONCE(rsp->gpnum), + TPS("reqwaitsig")); + } + + /* Handle quiescent-state forcing. */ + fqs_state = RCU_SAVE_DYNTICK; + j = jiffies_till_first_fqs; + if (j > HZ) { + j = HZ; + jiffies_till_first_fqs = HZ; + } + ret = 0; + for (;;) { + if (!ret) + rsp->jiffies_force_qs = jiffies + j; + trace_rcu_grace_period(rsp->name, + ACCESS_ONCE(rsp->gpnum), + TPS("fqswait")); + ret = wait_event_interruptible_timeout(rsp->gp_wq, + ((gf = ACCESS_ONCE(rsp->gp_flags)) & + RCU_GP_FLAG_FQS) || + (!ACCESS_ONCE(rnp->qsmask) && + !rcu_preempt_blocked_readers_cgp(rnp)), + j); + /* If grace period done, leave loop. */ + if (!ACCESS_ONCE(rnp->qsmask) && + !rcu_preempt_blocked_readers_cgp(rnp)) + break; + /* If time for quiescent-state forcing, do it. */ + if (ULONG_CMP_GE(jiffies, rsp->jiffies_force_qs) || + (gf & RCU_GP_FLAG_FQS)) { + trace_rcu_grace_period(rsp->name, + ACCESS_ONCE(rsp->gpnum), + TPS("fqsstart")); + fqs_state = rcu_gp_fqs(rsp, fqs_state); + trace_rcu_grace_period(rsp->name, + ACCESS_ONCE(rsp->gpnum), + TPS("fqsend")); + cond_resched(); + } else { + /* Deal with stray signal. */ + cond_resched(); + flush_signals(current); + trace_rcu_grace_period(rsp->name, + ACCESS_ONCE(rsp->gpnum), + TPS("fqswaitsig")); + } + j = jiffies_till_next_fqs; + if (j > HZ) { + j = HZ; + jiffies_till_next_fqs = HZ; + } else if (j < 1) { + j = 1; + jiffies_till_next_fqs = 1; + } + } + + /* Handle grace-period end. */ + rcu_gp_cleanup(rsp); + } +} + +static void rsp_wakeup(struct irq_work *work) +{ + struct rcu_state *rsp = container_of(work, struct rcu_state, wakeup_work); + + /* Wake up rcu_gp_kthread() to start the grace period. */ + wake_up(&rsp->gp_wq); +} + +/* + * Start a new RCU grace period if warranted, re-initializing the hierarchy + * in preparation for detecting the next grace period. The caller must hold + * the root node's ->lock and hard irqs must be disabled. + * + * Note that it is legal for a dying CPU (which is marked as offline) to + * invoke this function. This can happen when the dying CPU reports its + * quiescent state. + */ +static void +rcu_start_gp_advanced(struct rcu_state *rsp, struct rcu_node *rnp, + struct rcu_data *rdp) +{ + if (!rsp->gp_kthread || !cpu_needs_another_gp(rsp, rdp)) { + /* + * Either we have not yet spawned the grace-period + * task, this CPU does not need another grace period, + * or a grace period is already in progress. + * Either way, don't start a new grace period. + */ + return; + } + rsp->gp_flags = RCU_GP_FLAG_INIT; + trace_rcu_grace_period(rsp->name, ACCESS_ONCE(rsp->gpnum), + TPS("newreq")); + + /* + * We can't do wakeups while holding the rnp->lock, as that + * could cause possible deadlocks with the rq->lock. Defer + * the wakeup to interrupt context. And don't bother waking + * up the running kthread. + */ + if (current != rsp->gp_kthread) + irq_work_queue(&rsp->wakeup_work); +} + +/* + * Similar to rcu_start_gp_advanced(), but also advance the calling CPU's + * callbacks. Note that rcu_start_gp_advanced() cannot do this because it + * is invoked indirectly from rcu_advance_cbs(), which would result in + * endless recursion -- or would do so if it wasn't for the self-deadlock + * that is encountered beforehand. + */ +static void +rcu_start_gp(struct rcu_state *rsp) +{ + struct rcu_data *rdp = this_cpu_ptr(rsp->rda); + struct rcu_node *rnp = rcu_get_root(rsp); + + /* + * If there is no grace period in progress right now, any + * callbacks we have up to this point will be satisfied by the + * next grace period. Also, advancing the callbacks reduces the + * probability of false positives from cpu_needs_another_gp() + * resulting in pointless grace periods. So, advance callbacks + * then start the grace period! + */ + rcu_advance_cbs(rsp, rnp, rdp); + rcu_start_gp_advanced(rsp, rnp, rdp); +} + +/* + * Report a full set of quiescent states to the specified rcu_state + * data structure. This involves cleaning up after the prior grace + * period and letting rcu_start_gp() start up the next grace period + * if one is needed. Note that the caller must hold rnp->lock, which + * is released before return. + */ +static void rcu_report_qs_rsp(struct rcu_state *rsp, unsigned long flags) + __releases(rcu_get_root(rsp)->lock) +{ + WARN_ON_ONCE(!rcu_gp_in_progress(rsp)); + raw_spin_unlock_irqrestore(&rcu_get_root(rsp)->lock, flags); + wake_up(&rsp->gp_wq); /* Memory barrier implied by wake_up() path. */ +} + +/* + * Similar to rcu_report_qs_rdp(), for which it is a helper function. + * Allows quiescent states for a group of CPUs to be reported at one go + * to the specified rcu_node structure, though all the CPUs in the group + * must be represented by the same rcu_node structure (which need not be + * a leaf rcu_node structure, though it often will be). That structure's + * lock must be held upon entry, and it is released before return. + */ +static void +rcu_report_qs_rnp(unsigned long mask, struct rcu_state *rsp, + struct rcu_node *rnp, unsigned long flags) + __releases(rnp->lock) +{ + struct rcu_node *rnp_c; + + /* Walk up the rcu_node hierarchy. */ + for (;;) { + if (!(rnp->qsmask & mask)) { + + /* Our bit has already been cleared, so done. */ + raw_spin_unlock_irqrestore(&rnp->lock, flags); + return; + } + rnp->qsmask &= ~mask; + trace_rcu_quiescent_state_report(rsp->name, rnp->gpnum, + mask, rnp->qsmask, rnp->level, + rnp->grplo, rnp->grphi, + !!rnp->gp_tasks); + if (rnp->qsmask != 0 || rcu_preempt_blocked_readers_cgp(rnp)) { + + /* Other bits still set at this level, so done. */ + raw_spin_unlock_irqrestore(&rnp->lock, flags); + return; + } + mask = rnp->grpmask; + if (rnp->parent == NULL) { + + /* No more levels. Exit loop holding root lock. */ + + break; + } + raw_spin_unlock_irqrestore(&rnp->lock, flags); + rnp_c = rnp; + rnp = rnp->parent; + raw_spin_lock_irqsave(&rnp->lock, flags); + WARN_ON_ONCE(rnp_c->qsmask); + } + + /* + * Get here if we are the last CPU to pass through a quiescent + * state for this grace period. Invoke rcu_report_qs_rsp() + * to clean up and start the next grace period if one is needed. + */ + rcu_report_qs_rsp(rsp, flags); /* releases rnp->lock. */ +} + +/* + * Record a quiescent state for the specified CPU to that CPU's rcu_data + * structure. This must be either called from the specified CPU, or + * called when the specified CPU is known to be offline (and when it is + * also known that no other CPU is concurrently trying to help the offline + * CPU). The lastcomp argument is used to make sure we are still in the + * grace period of interest. We don't want to end the current grace period + * based on quiescent states detected in an earlier grace period! + */ +static void +rcu_report_qs_rdp(int cpu, struct rcu_state *rsp, struct rcu_data *rdp) +{ + unsigned long flags; + unsigned long mask; + struct rcu_node *rnp; + + rnp = rdp->mynode; + raw_spin_lock_irqsave(&rnp->lock, flags); + if (rdp->passed_quiesce == 0 || rdp->gpnum != rnp->gpnum || + rnp->completed == rnp->gpnum) { + + /* + * The grace period in which this quiescent state was + * recorded has ended, so don't report it upwards. + * We will instead need a new quiescent state that lies + * within the current grace period. + */ + rdp->passed_quiesce = 0; /* need qs for new gp. */ + raw_spin_unlock_irqrestore(&rnp->lock, flags); + return; + } + mask = rdp->grpmask; + if ((rnp->qsmask & mask) == 0) { + raw_spin_unlock_irqrestore(&rnp->lock, flags); + } else { + rdp->qs_pending = 0; + + /* + * This GP can't end until cpu checks in, so all of our + * callbacks can be processed during the next GP. + */ + rcu_accelerate_cbs(rsp, rnp, rdp); + + rcu_report_qs_rnp(mask, rsp, rnp, flags); /* rlses rnp->lock */ + } +} + +/* + * Check to see if there is a new grace period of which this CPU + * is not yet aware, and if so, set up local rcu_data state for it. + * Otherwise, see if this CPU has just passed through its first + * quiescent state for this grace period, and record that fact if so. + */ +static void +rcu_check_quiescent_state(struct rcu_state *rsp, struct rcu_data *rdp) +{ + /* Check for grace-period ends and beginnings. */ + note_gp_changes(rsp, rdp); + + /* + * Does this CPU still need to do its part for current grace period? + * If no, return and let the other CPUs do their part as well. + */ + if (!rdp->qs_pending) + return; + + /* + * Was there a quiescent state since the beginning of the grace + * period? If no, then exit and wait for the next call. + */ + if (!rdp->passed_quiesce) + return; + + /* + * Tell RCU we are done (but rcu_report_qs_rdp() will be the + * judge of that). + */ + rcu_report_qs_rdp(rdp->cpu, rsp, rdp); +} + +#ifdef CONFIG_HOTPLUG_CPU + +/* + * Send the specified CPU's RCU callbacks to the orphanage. The + * specified CPU must be offline, and the caller must hold the + * ->orphan_lock. + */ +static void +rcu_send_cbs_to_orphanage(int cpu, struct rcu_state *rsp, + struct rcu_node *rnp, struct rcu_data *rdp) +{ + /* No-CBs CPUs do not have orphanable callbacks. */ + if (rcu_is_nocb_cpu(rdp->cpu)) + return; + + /* + * Orphan the callbacks. First adjust the counts. This is safe + * because _rcu_barrier() excludes CPU-hotplug operations, so it + * cannot be running now. Thus no memory barrier is required. + */ + if (rdp->nxtlist != NULL) { + rsp->qlen_lazy += rdp->qlen_lazy; + rsp->qlen += rdp->qlen; + rdp->n_cbs_orphaned += rdp->qlen; + rdp->qlen_lazy = 0; + ACCESS_ONCE(rdp->qlen) = 0; + } + + /* + * Next, move those callbacks still needing a grace period to + * the orphanage, where some other CPU will pick them up. + * Some of the callbacks might have gone partway through a grace + * period, but that is too bad. They get to start over because we + * cannot assume that grace periods are synchronized across CPUs. + * We don't bother updating the ->nxttail[] array yet, instead + * we just reset the whole thing later on. + */ + if (*rdp->nxttail[RCU_DONE_TAIL] != NULL) { + *rsp->orphan_nxttail = *rdp->nxttail[RCU_DONE_TAIL]; + rsp->orphan_nxttail = rdp->nxttail[RCU_NEXT_TAIL]; + *rdp->nxttail[RCU_DONE_TAIL] = NULL; + } + + /* + * Then move the ready-to-invoke callbacks to the orphanage, + * where some other CPU will pick them up. These will not be + * required to pass though another grace period: They are done. + */ + if (rdp->nxtlist != NULL) { + *rsp->orphan_donetail = rdp->nxtlist; + rsp->orphan_donetail = rdp->nxttail[RCU_DONE_TAIL]; + } + + /* Finally, initialize the rcu_data structure's list to empty. */ + init_callback_list(rdp); +} + +/* + * Adopt the RCU callbacks from the specified rcu_state structure's + * orphanage. The caller must hold the ->orphan_lock. + */ +static void rcu_adopt_orphan_cbs(struct rcu_state *rsp) +{ + int i; + struct rcu_data *rdp = __this_cpu_ptr(rsp->rda); + + /* No-CBs CPUs are handled specially. */ + if (rcu_nocb_adopt_orphan_cbs(rsp, rdp)) + return; + + /* Do the accounting first. */ + rdp->qlen_lazy += rsp->qlen_lazy; + rdp->qlen += rsp->qlen; + rdp->n_cbs_adopted += rsp->qlen; + if (rsp->qlen_lazy != rsp->qlen) + rcu_idle_count_callbacks_posted(); + rsp->qlen_lazy = 0; + rsp->qlen = 0; + + /* + * We do not need a memory barrier here because the only way we + * can get here if there is an rcu_barrier() in flight is if + * we are the task doing the rcu_barrier(). + */ + + /* First adopt the ready-to-invoke callbacks. */ + if (rsp->orphan_donelist != NULL) { + *rsp->orphan_donetail = *rdp->nxttail[RCU_DONE_TAIL]; + *rdp->nxttail[RCU_DONE_TAIL] = rsp->orphan_donelist; + for (i = RCU_NEXT_SIZE - 1; i >= RCU_DONE_TAIL; i--) + if (rdp->nxttail[i] == rdp->nxttail[RCU_DONE_TAIL]) + rdp->nxttail[i] = rsp->orphan_donetail; + rsp->orphan_donelist = NULL; + rsp->orphan_donetail = &rsp->orphan_donelist; + } + + /* And then adopt the callbacks that still need a grace period. */ + if (rsp->orphan_nxtlist != NULL) { + *rdp->nxttail[RCU_NEXT_TAIL] = rsp->orphan_nxtlist; + rdp->nxttail[RCU_NEXT_TAIL] = rsp->orphan_nxttail; + rsp->orphan_nxtlist = NULL; + rsp->orphan_nxttail = &rsp->orphan_nxtlist; + } +} + +/* + * Trace the fact that this CPU is going offline. + */ +static void rcu_cleanup_dying_cpu(struct rcu_state *rsp) +{ + RCU_TRACE(unsigned long mask); + RCU_TRACE(struct rcu_data *rdp = this_cpu_ptr(rsp->rda)); + RCU_TRACE(struct rcu_node *rnp = rdp->mynode); + + RCU_TRACE(mask = rdp->grpmask); + trace_rcu_grace_period(rsp->name, + rnp->gpnum + 1 - !!(rnp->qsmask & mask), + TPS("cpuofl")); +} + +/* + * The CPU has been completely removed, and some other CPU is reporting + * this fact from process context. Do the remainder of the cleanup, + * including orphaning the outgoing CPU's RCU callbacks, and also + * adopting them. There can only be one CPU hotplug operation at a time, + * so no other CPU can be attempting to update rcu_cpu_kthread_task. + */ +static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp) +{ + unsigned long flags; + unsigned long mask; + int need_report = 0; + struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu); + struct rcu_node *rnp = rdp->mynode; /* Outgoing CPU's rdp & rnp. */ + + /* Adjust any no-longer-needed kthreads. */ + rcu_boost_kthread_setaffinity(rnp, -1); + + /* Remove the dead CPU from the bitmasks in the rcu_node hierarchy. */ + + /* Exclude any attempts to start a new grace period. */ + mutex_lock(&rsp->onoff_mutex); + raw_spin_lock_irqsave(&rsp->orphan_lock, flags); + + /* Orphan the dead CPU's callbacks, and adopt them if appropriate. */ + rcu_send_cbs_to_orphanage(cpu, rsp, rnp, rdp); + rcu_adopt_orphan_cbs(rsp); + + /* Remove the outgoing CPU from the masks in the rcu_node hierarchy. */ + mask = rdp->grpmask; /* rnp->grplo is constant. */ + do { + raw_spin_lock(&rnp->lock); /* irqs already disabled. */ + rnp->qsmaskinit &= ~mask; + if (rnp->qsmaskinit != 0) { + if (rnp != rdp->mynode) + raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */ + break; + } + if (rnp == rdp->mynode) + need_report = rcu_preempt_offline_tasks(rsp, rnp, rdp); + else + raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */ + mask = rnp->grpmask; + rnp = rnp->parent; + } while (rnp != NULL); + + /* + * We still hold the leaf rcu_node structure lock here, and + * irqs are still disabled. The reason for this subterfuge is + * because invoking rcu_report_unblock_qs_rnp() with ->orphan_lock + * held leads to deadlock. + */ + raw_spin_unlock(&rsp->orphan_lock); /* irqs remain disabled. */ + rnp = rdp->mynode; + if (need_report & RCU_OFL_TASKS_NORM_GP) + rcu_report_unblock_qs_rnp(rnp, flags); + else + raw_spin_unlock_irqrestore(&rnp->lock, flags); + if (need_report & RCU_OFL_TASKS_EXP_GP) + rcu_report_exp_rnp(rsp, rnp, true); + WARN_ONCE(rdp->qlen != 0 || rdp->nxtlist != NULL, + "rcu_cleanup_dead_cpu: Callbacks on offline CPU %d: qlen=%lu, nxtlist=%p\n", + cpu, rdp->qlen, rdp->nxtlist); + init_callback_list(rdp); + /* Disallow further callbacks on this CPU. */ + rdp->nxttail[RCU_NEXT_TAIL] = NULL; + mutex_unlock(&rsp->onoff_mutex); +} + +#else /* #ifdef CONFIG_HOTPLUG_CPU */ + +static void rcu_cleanup_dying_cpu(struct rcu_state *rsp) +{ +} + +static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp) +{ +} + +#endif /* #else #ifdef CONFIG_HOTPLUG_CPU */ + +/* + * Invoke any RCU callbacks that have made it to the end of their grace + * period. Thottle as specified by rdp->blimit. + */ +static void rcu_do_batch(struct rcu_state *rsp, struct rcu_data *rdp) +{ + unsigned long flags; + struct rcu_head *next, *list, **tail; + long bl, count, count_lazy; + int i; + + /* If no callbacks are ready, just return. */ + if (!cpu_has_callbacks_ready_to_invoke(rdp)) { + trace_rcu_batch_start(rsp->name, rdp->qlen_lazy, rdp->qlen, 0); + trace_rcu_batch_end(rsp->name, 0, !!ACCESS_ONCE(rdp->nxtlist), + need_resched(), is_idle_task(current), + rcu_is_callbacks_kthread()); + return; + } + + /* + * Extract the list of ready callbacks, disabling to prevent + * races with call_rcu() from interrupt handlers. + */ + local_irq_save(flags); + WARN_ON_ONCE(cpu_is_offline(smp_processor_id())); + bl = rdp->blimit; + trace_rcu_batch_start(rsp->name, rdp->qlen_lazy, rdp->qlen, bl); + list = rdp->nxtlist; + rdp->nxtlist = *rdp->nxttail[RCU_DONE_TAIL]; + *rdp->nxttail[RCU_DONE_TAIL] = NULL; + tail = rdp->nxttail[RCU_DONE_TAIL]; + for (i = RCU_NEXT_SIZE - 1; i >= 0; i--) + if (rdp->nxttail[i] == rdp->nxttail[RCU_DONE_TAIL]) + rdp->nxttail[i] = &rdp->nxtlist; + local_irq_restore(flags); + + /* Invoke callbacks. */ + count = count_lazy = 0; + while (list) { + next = list->next; + prefetch(next); + debug_rcu_head_unqueue(list); + if (__rcu_reclaim(rsp->name, list)) + count_lazy++; + list = next; + /* Stop only if limit reached and CPU has something to do. */ + if (++count >= bl && + (need_resched() || + (!is_idle_task(current) && !rcu_is_callbacks_kthread()))) + break; + } + + local_irq_save(flags); + trace_rcu_batch_end(rsp->name, count, !!list, need_resched(), + is_idle_task(current), + rcu_is_callbacks_kthread()); + + /* Update count, and requeue any remaining callbacks. */ + if (list != NULL) { + *tail = rdp->nxtlist; + rdp->nxtlist = list; + for (i = 0; i < RCU_NEXT_SIZE; i++) + if (&rdp->nxtlist == rdp->nxttail[i]) + rdp->nxttail[i] = tail; + else + break; + } + smp_mb(); /* List handling before counting for rcu_barrier(). */ + rdp->qlen_lazy -= count_lazy; + ACCESS_ONCE(rdp->qlen) -= count; + rdp->n_cbs_invoked += count; + + /* Reinstate batch limit if we have worked down the excess. */ + if (rdp->blimit == LONG_MAX && rdp->qlen <= qlowmark) + rdp->blimit = blimit; + + /* Reset ->qlen_last_fqs_check trigger if enough CBs have drained. */ + if (rdp->qlen == 0 && rdp->qlen_last_fqs_check != 0) { + rdp->qlen_last_fqs_check = 0; + rdp->n_force_qs_snap = rsp->n_force_qs; + } else if (rdp->qlen < rdp->qlen_last_fqs_check - qhimark) + rdp->qlen_last_fqs_check = rdp->qlen; + WARN_ON_ONCE((rdp->nxtlist == NULL) != (rdp->qlen == 0)); + + local_irq_restore(flags); + + /* Re-invoke RCU core processing if there are callbacks remaining. */ + if (cpu_has_callbacks_ready_to_invoke(rdp)) + invoke_rcu_core(); +} + +/* + * Check to see if this CPU is in a non-context-switch quiescent state + * (user mode or idle loop for rcu, non-softirq execution for rcu_bh). + * Also schedule RCU core processing. + * + * This function must be called from hardirq context. It is normally + * invoked from the scheduling-clock interrupt. If rcu_pending returns + * false, there is no point in invoking rcu_check_callbacks(). + */ +void rcu_check_callbacks(int cpu, int user) +{ + trace_rcu_utilization(TPS("Start scheduler-tick")); + increment_cpu_stall_ticks(); + if (user || rcu_is_cpu_rrupt_from_idle()) { + + /* + * Get here if this CPU took its interrupt from user + * mode or from the idle loop, and if this is not a + * nested interrupt. In this case, the CPU is in + * a quiescent state, so note it. + * + * No memory barrier is required here because both + * rcu_sched_qs() and rcu_bh_qs() reference only CPU-local + * variables that other CPUs neither access nor modify, + * at least not while the corresponding CPU is online. + */ + + rcu_sched_qs(cpu); + rcu_bh_qs(cpu); + + } else if (!in_softirq()) { + + /* + * Get here if this CPU did not take its interrupt from + * softirq, in other words, if it is not interrupting + * a rcu_bh read-side critical section. This is an _bh + * critical section, so note it. + */ + + rcu_bh_qs(cpu); + } + rcu_preempt_check_callbacks(cpu); + if (rcu_pending(cpu)) + invoke_rcu_core(); + trace_rcu_utilization(TPS("End scheduler-tick")); +} + +/* + * Scan the leaf rcu_node structures, processing dyntick state for any that + * have not yet encountered a quiescent state, using the function specified. + * Also initiate boosting for any threads blocked on the root rcu_node. + * + * The caller must have suppressed start of new grace periods. + */ +static void force_qs_rnp(struct rcu_state *rsp, + int (*f)(struct rcu_data *rsp, bool *isidle, + unsigned long *maxj), + bool *isidle, unsigned long *maxj) +{ + unsigned long bit; + int cpu; + unsigned long flags; + unsigned long mask; + struct rcu_node *rnp; + + rcu_for_each_leaf_node(rsp, rnp) { + cond_resched(); + mask = 0; + raw_spin_lock_irqsave(&rnp->lock, flags); + if (!rcu_gp_in_progress(rsp)) { + raw_spin_unlock_irqrestore(&rnp->lock, flags); + return; + } + if (rnp->qsmask == 0) { + rcu_initiate_boost(rnp, flags); /* releases rnp->lock */ + continue; + } + cpu = rnp->grplo; + bit = 1; + for (; cpu <= rnp->grphi; cpu++, bit <<= 1) { + if ((rnp->qsmask & bit) != 0) { + if ((rnp->qsmaskinit & bit) != 0) + *isidle = 0; + if (f(per_cpu_ptr(rsp->rda, cpu), isidle, maxj)) + mask |= bit; + } + } + if (mask != 0) { + + /* rcu_report_qs_rnp() releases rnp->lock. */ + rcu_report_qs_rnp(mask, rsp, rnp, flags); + continue; + } + raw_spin_unlock_irqrestore(&rnp->lock, flags); + } + rnp = rcu_get_root(rsp); + if (rnp->qsmask == 0) { + raw_spin_lock_irqsave(&rnp->lock, flags); + rcu_initiate_boost(rnp, flags); /* releases rnp->lock. */ + } +} + +/* + * Force quiescent states on reluctant CPUs, and also detect which + * CPUs are in dyntick-idle mode. + */ +static void force_quiescent_state(struct rcu_state *rsp) +{ + unsigned long flags; + bool ret; + struct rcu_node *rnp; + struct rcu_node *rnp_old = NULL; + + /* Funnel through hierarchy to reduce memory contention. */ + rnp = per_cpu_ptr(rsp->rda, raw_smp_processor_id())->mynode; + for (; rnp != NULL; rnp = rnp->parent) { + ret = (ACCESS_ONCE(rsp->gp_flags) & RCU_GP_FLAG_FQS) || + !raw_spin_trylock(&rnp->fqslock); + if (rnp_old != NULL) + raw_spin_unlock(&rnp_old->fqslock); + if (ret) { + rsp->n_force_qs_lh++; + return; + } + rnp_old = rnp; + } + /* rnp_old == rcu_get_root(rsp), rnp == NULL. */ + + /* Reached the root of the rcu_node tree, acquire lock. */ + raw_spin_lock_irqsave(&rnp_old->lock, flags); + raw_spin_unlock(&rnp_old->fqslock); + if (ACCESS_ONCE(rsp->gp_flags) & RCU_GP_FLAG_FQS) { + rsp->n_force_qs_lh++; + raw_spin_unlock_irqrestore(&rnp_old->lock, flags); + return; /* Someone beat us to it. */ + } + rsp->gp_flags |= RCU_GP_FLAG_FQS; + raw_spin_unlock_irqrestore(&rnp_old->lock, flags); + wake_up(&rsp->gp_wq); /* Memory barrier implied by wake_up() path. */ +} + +/* + * This does the RCU core processing work for the specified rcu_state + * and rcu_data structures. This may be called only from the CPU to + * whom the rdp belongs. + */ +static void +__rcu_process_callbacks(struct rcu_state *rsp) +{ + unsigned long flags; + struct rcu_data *rdp = __this_cpu_ptr(rsp->rda); + + WARN_ON_ONCE(rdp->beenonline == 0); + + /* Update RCU state based on any recent quiescent states. */ + rcu_check_quiescent_state(rsp, rdp); + + /* Does this CPU require a not-yet-started grace period? */ + local_irq_save(flags); + if (cpu_needs_another_gp(rsp, rdp)) { + raw_spin_lock(&rcu_get_root(rsp)->lock); /* irqs disabled. */ + rcu_start_gp(rsp); + raw_spin_unlock_irqrestore(&rcu_get_root(rsp)->lock, flags); + } else { + local_irq_restore(flags); + } + + /* If there are callbacks ready, invoke them. */ + if (cpu_has_callbacks_ready_to_invoke(rdp)) + invoke_rcu_callbacks(rsp, rdp); +} + +/* + * Do RCU core processing for the current CPU. + */ +static void rcu_process_callbacks(struct softirq_action *unused) +{ + struct rcu_state *rsp; + + if (cpu_is_offline(smp_processor_id())) + return; + trace_rcu_utilization(TPS("Start RCU core")); + for_each_rcu_flavor(rsp) + __rcu_process_callbacks(rsp); + trace_rcu_utilization(TPS("End RCU core")); +} + +/* + * Schedule RCU callback invocation. If the specified type of RCU + * does not support RCU priority boosting, just do a direct call, + * otherwise wake up the per-CPU kernel kthread. Note that because we + * are running on the current CPU with interrupts disabled, the + * rcu_cpu_kthread_task cannot disappear out from under us. + */ +static void invoke_rcu_callbacks(struct rcu_state *rsp, struct rcu_data *rdp) +{ + if (unlikely(!ACCESS_ONCE(rcu_scheduler_fully_active))) + return; + if (likely(!rsp->boost)) { + rcu_do_batch(rsp, rdp); + return; + } + invoke_rcu_callbacks_kthread(); +} + +static void invoke_rcu_core(void) +{ + if (cpu_online(smp_processor_id())) + raise_softirq(RCU_SOFTIRQ); +} + +/* + * Handle any core-RCU processing required by a call_rcu() invocation. + */ +static void __call_rcu_core(struct rcu_state *rsp, struct rcu_data *rdp, + struct rcu_head *head, unsigned long flags) +{ + /* + * If called from an extended quiescent state, invoke the RCU + * core in order to force a re-evaluation of RCU's idleness. + */ + if (!rcu_is_watching() && cpu_online(smp_processor_id())) + invoke_rcu_core(); + + /* If interrupts were disabled or CPU offline, don't invoke RCU core. */ + if (irqs_disabled_flags(flags) || cpu_is_offline(smp_processor_id())) + return; + + /* + * Force the grace period if too many callbacks or too long waiting. + * Enforce hysteresis, and don't invoke force_quiescent_state() + * if some other CPU has recently done so. Also, don't bother + * invoking force_quiescent_state() if the newly enqueued callback + * is the only one waiting for a grace period to complete. + */ + if (unlikely(rdp->qlen > rdp->qlen_last_fqs_check + qhimark)) { + + /* Are we ignoring a completed grace period? */ + note_gp_changes(rsp, rdp); + + /* Start a new grace period if one not already started. */ + if (!rcu_gp_in_progress(rsp)) { + struct rcu_node *rnp_root = rcu_get_root(rsp); + + raw_spin_lock(&rnp_root->lock); + rcu_start_gp(rsp); + raw_spin_unlock(&rnp_root->lock); + } else { + /* Give the grace period a kick. */ + rdp->blimit = LONG_MAX; + if (rsp->n_force_qs == rdp->n_force_qs_snap && + *rdp->nxttail[RCU_DONE_TAIL] != head) + force_quiescent_state(rsp); + rdp->n_force_qs_snap = rsp->n_force_qs; + rdp->qlen_last_fqs_check = rdp->qlen; + } + } +} + +/* + * RCU callback function to leak a callback. + */ +static void rcu_leak_callback(struct rcu_head *rhp) +{ +} + +/* + * Helper function for call_rcu() and friends. The cpu argument will + * normally be -1, indicating "currently running CPU". It may specify + * a CPU only if that CPU is a no-CBs CPU. Currently, only _rcu_barrier() + * is expected to specify a CPU. + */ +static void +__call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu), + struct rcu_state *rsp, int cpu, bool lazy) +{ + unsigned long flags; + struct rcu_data *rdp; + + WARN_ON_ONCE((unsigned long)head & 0x3); /* Misaligned rcu_head! */ + if (debug_rcu_head_queue(head)) { + /* Probable double call_rcu(), so leak the callback. */ + ACCESS_ONCE(head->func) = rcu_leak_callback; + WARN_ONCE(1, "__call_rcu(): Leaked duplicate callback\n"); + return; + } + head->func = func; + head->next = NULL; + + /* + * Opportunistically note grace-period endings and beginnings. + * Note that we might see a beginning right after we see an + * end, but never vice versa, since this CPU has to pass through + * a quiescent state betweentimes. + */ + local_irq_save(flags); + rdp = this_cpu_ptr(rsp->rda); + + /* Add the callback to our list. */ + if (unlikely(rdp->nxttail[RCU_NEXT_TAIL] == NULL) || cpu != -1) { + int offline; + + if (cpu != -1) + rdp = per_cpu_ptr(rsp->rda, cpu); + offline = !__call_rcu_nocb(rdp, head, lazy); + WARN_ON_ONCE(offline); + /* _call_rcu() is illegal on offline CPU; leak the callback. */ + local_irq_restore(flags); + return; + } + ACCESS_ONCE(rdp->qlen)++; + if (lazy) + rdp->qlen_lazy++; + else + rcu_idle_count_callbacks_posted(); + smp_mb(); /* Count before adding callback for rcu_barrier(). */ + *rdp->nxttail[RCU_NEXT_TAIL] = head; + rdp->nxttail[RCU_NEXT_TAIL] = &head->next; + + if (__is_kfree_rcu_offset((unsigned long)func)) + trace_rcu_kfree_callback(rsp->name, head, (unsigned long)func, + rdp->qlen_lazy, rdp->qlen); + else + trace_rcu_callback(rsp->name, head, rdp->qlen_lazy, rdp->qlen); + + /* Go handle any RCU core processing required. */ + __call_rcu_core(rsp, rdp, head, flags); + local_irq_restore(flags); +} + +/* + * Queue an RCU-sched callback for invocation after a grace period. + */ +void call_rcu_sched(struct rcu_head *head, void (*func)(struct rcu_head *rcu)) +{ + __call_rcu(head, func, &rcu_sched_state, -1, 0); +} +EXPORT_SYMBOL_GPL(call_rcu_sched); + +/* + * Queue an RCU callback for invocation after a quicker grace period. + */ +void call_rcu_bh(struct rcu_head *head, void (*func)(struct rcu_head *rcu)) +{ + __call_rcu(head, func, &rcu_bh_state, -1, 0); +} +EXPORT_SYMBOL_GPL(call_rcu_bh); + +/* + * Because a context switch is a grace period for RCU-sched and RCU-bh, + * any blocking grace-period wait automatically implies a grace period + * if there is only one CPU online at any point time during execution + * of either synchronize_sched() or synchronize_rcu_bh(). It is OK to + * occasionally incorrectly indicate that there are multiple CPUs online + * when there was in fact only one the whole time, as this just adds + * some overhead: RCU still operates correctly. + */ +static inline int rcu_blocking_is_gp(void) +{ + int ret; + + might_sleep(); /* Check for RCU read-side critical section. */ + preempt_disable(); + ret = num_online_cpus() <= 1; + preempt_enable(); + return ret; +} + +/** + * synchronize_sched - wait until an rcu-sched grace period has elapsed. + * + * Control will return to the caller some time after a full rcu-sched + * grace period has elapsed, in other words after all currently executing + * rcu-sched read-side critical sections have completed. These read-side + * critical sections are delimited by rcu_read_lock_sched() and + * rcu_read_unlock_sched(), and may be nested. Note that preempt_disable(), + * local_irq_disable(), and so on may be used in place of + * rcu_read_lock_sched(). + * + * This means that all preempt_disable code sequences, including NMI and + * non-threaded hardware-interrupt handlers, in progress on entry will + * have completed before this primitive returns. However, this does not + * guarantee that softirq handlers will have completed, since in some + * kernels, these handlers can run in process context, and can block. + * + * Note that this guarantee implies further memory-ordering guarantees. + * On systems with more than one CPU, when synchronize_sched() returns, + * each CPU is guaranteed to have executed a full memory barrier since the + * end of its last RCU-sched read-side critical section whose beginning + * preceded the call to synchronize_sched(). In addition, each CPU having + * an RCU read-side critical section that extends beyond the return from + * synchronize_sched() is guaranteed to have executed a full memory barrier + * after the beginning of synchronize_sched() and before the beginning of + * that RCU read-side critical section. Note that these guarantees include + * CPUs that are offline, idle, or executing in user mode, as well as CPUs + * that are executing in the kernel. + * + * Furthermore, if CPU A invoked synchronize_sched(), which returned + * to its caller on CPU B, then both CPU A and CPU B are guaranteed + * to have executed a full memory barrier during the execution of + * synchronize_sched() -- even if CPU A and CPU B are the same CPU (but + * again only if the system has more than one CPU). + * + * This primitive provides the guarantees made by the (now removed) + * synchronize_kernel() API. In contrast, synchronize_rcu() only + * guarantees that rcu_read_lock() sections will have completed. + * In "classic RCU", these two guarantees happen to be one and + * the same, but can differ in realtime RCU implementations. + */ +void synchronize_sched(void) +{ + rcu_lockdep_assert(!lock_is_held(&rcu_bh_lock_map) && + !lock_is_held(&rcu_lock_map) && + !lock_is_held(&rcu_sched_lock_map), + "Illegal synchronize_sched() in RCU-sched read-side critical section"); + if (rcu_blocking_is_gp()) + return; + if (rcu_expedited) + synchronize_sched_expedited(); + else + wait_rcu_gp(call_rcu_sched); +} +EXPORT_SYMBOL_GPL(synchronize_sched); + +/** + * synchronize_rcu_bh - wait until an rcu_bh grace period has elapsed. + * + * Control will return to the caller some time after a full rcu_bh grace + * period has elapsed, in other words after all currently executing rcu_bh + * read-side critical sections have completed. RCU read-side critical + * sections are delimited by rcu_read_lock_bh() and rcu_read_unlock_bh(), + * and may be nested. + * + * See the description of synchronize_sched() for more detailed information + * on memory ordering guarantees. + */ +void synchronize_rcu_bh(void) +{ + rcu_lockdep_assert(!lock_is_held(&rcu_bh_lock_map) && + !lock_is_held(&rcu_lock_map) && + !lock_is_held(&rcu_sched_lock_map), + "Illegal synchronize_rcu_bh() in RCU-bh read-side critical section"); + if (rcu_blocking_is_gp()) + return; + if (rcu_expedited) + synchronize_rcu_bh_expedited(); + else + wait_rcu_gp(call_rcu_bh); +} +EXPORT_SYMBOL_GPL(synchronize_rcu_bh); + +static int synchronize_sched_expedited_cpu_stop(void *data) +{ + /* + * There must be a full memory barrier on each affected CPU + * between the time that try_stop_cpus() is called and the + * time that it returns. + * + * In the current initial implementation of cpu_stop, the + * above condition is already met when the control reaches + * this point and the following smp_mb() is not strictly + * necessary. Do smp_mb() anyway for documentation and + * robustness against future implementation changes. + */ + smp_mb(); /* See above comment block. */ + return 0; +} + +/** + * synchronize_sched_expedited - Brute-force RCU-sched grace period + * + * Wait for an RCU-sched grace period to elapse, but use a "big hammer" + * approach to force the grace period to end quickly. This consumes + * significant time on all CPUs and is unfriendly to real-time workloads, + * so is thus not recommended for any sort of common-case code. In fact, + * if you are using synchronize_sched_expedited() in a loop, please + * restructure your code to batch your updates, and then use a single + * synchronize_sched() instead. + * + * Note that it is illegal to call this function while holding any lock + * that is acquired by a CPU-hotplug notifier. And yes, it is also illegal + * to call this function from a CPU-hotplug notifier. Failing to observe + * these restriction will result in deadlock. + * + * This implementation can be thought of as an application of ticket + * locking to RCU, with sync_sched_expedited_started and + * sync_sched_expedited_done taking on the roles of the halves + * of the ticket-lock word. Each task atomically increments + * sync_sched_expedited_started upon entry, snapshotting the old value, + * then attempts to stop all the CPUs. If this succeeds, then each + * CPU will have executed a context switch, resulting in an RCU-sched + * grace period. We are then done, so we use atomic_cmpxchg() to + * update sync_sched_expedited_done to match our snapshot -- but + * only if someone else has not already advanced past our snapshot. + * + * On the other hand, if try_stop_cpus() fails, we check the value + * of sync_sched_expedited_done. If it has advanced past our + * initial snapshot, then someone else must have forced a grace period + * some time after we took our snapshot. In this case, our work is + * done for us, and we can simply return. Otherwise, we try again, + * but keep our initial snapshot for purposes of checking for someone + * doing our work for us. + * + * If we fail too many times in a row, we fall back to synchronize_sched(). + */ +void synchronize_sched_expedited(void) +{ + long firstsnap, s, snap; + int trycount = 0; + struct rcu_state *rsp = &rcu_sched_state; + + /* + * If we are in danger of counter wrap, just do synchronize_sched(). + * By allowing sync_sched_expedited_started to advance no more than + * ULONG_MAX/8 ahead of sync_sched_expedited_done, we are ensuring + * that more than 3.5 billion CPUs would be required to force a + * counter wrap on a 32-bit system. Quite a few more CPUs would of + * course be required on a 64-bit system. + */ + if (ULONG_CMP_GE((ulong)atomic_long_read(&rsp->expedited_start), + (ulong)atomic_long_read(&rsp->expedited_done) + + ULONG_MAX / 8)) { + synchronize_sched(); + atomic_long_inc(&rsp->expedited_wrap); + return; + } + + /* + * Take a ticket. Note that atomic_inc_return() implies a + * full memory barrier. + */ + snap = atomic_long_inc_return(&rsp->expedited_start); + firstsnap = snap; + get_online_cpus(); + WARN_ON_ONCE(cpu_is_offline(raw_smp_processor_id())); + + /* + * Each pass through the following loop attempts to force a + * context switch on each CPU. + */ + while (try_stop_cpus(cpu_online_mask, + synchronize_sched_expedited_cpu_stop, + NULL) == -EAGAIN) { + put_online_cpus(); + atomic_long_inc(&rsp->expedited_tryfail); + + /* Check to see if someone else did our work for us. */ + s = atomic_long_read(&rsp->expedited_done); + if (ULONG_CMP_GE((ulong)s, (ulong)firstsnap)) { + /* ensure test happens before caller kfree */ + smp_mb__before_atomic_inc(); /* ^^^ */ + atomic_long_inc(&rsp->expedited_workdone1); + return; + } + + /* No joy, try again later. Or just synchronize_sched(). */ + if (trycount++ < 10) { + udelay(trycount * num_online_cpus()); + } else { + wait_rcu_gp(call_rcu_sched); + atomic_long_inc(&rsp->expedited_normal); + return; + } + + /* Recheck to see if someone else did our work for us. */ + s = atomic_long_read(&rsp->expedited_done); + if (ULONG_CMP_GE((ulong)s, (ulong)firstsnap)) { + /* ensure test happens before caller kfree */ + smp_mb__before_atomic_inc(); /* ^^^ */ + atomic_long_inc(&rsp->expedited_workdone2); + return; + } + + /* + * Refetching sync_sched_expedited_started allows later + * callers to piggyback on our grace period. We retry + * after they started, so our grace period works for them, + * and they started after our first try, so their grace + * period works for us. + */ + get_online_cpus(); + snap = atomic_long_read(&rsp->expedited_start); + smp_mb(); /* ensure read is before try_stop_cpus(). */ + } + atomic_long_inc(&rsp->expedited_stoppedcpus); + + /* + * Everyone up to our most recent fetch is covered by our grace + * period. Update the counter, but only if our work is still + * relevant -- which it won't be if someone who started later + * than we did already did their update. + */ + do { + atomic_long_inc(&rsp->expedited_done_tries); + s = atomic_long_read(&rsp->expedited_done); + if (ULONG_CMP_GE((ulong)s, (ulong)snap)) { + /* ensure test happens before caller kfree */ + smp_mb__before_atomic_inc(); /* ^^^ */ + atomic_long_inc(&rsp->expedited_done_lost); + break; + } + } while (atomic_long_cmpxchg(&rsp->expedited_done, s, snap) != s); + atomic_long_inc(&rsp->expedited_done_exit); + + put_online_cpus(); +} +EXPORT_SYMBOL_GPL(synchronize_sched_expedited); + +/* + * Check to see if there is any immediate RCU-related work to be done + * by the current CPU, for the specified type of RCU, returning 1 if so. + * The checks are in order of increasing expense: checks that can be + * carried out against CPU-local state are performed first. However, + * we must check for CPU stalls first, else we might not get a chance. + */ +static int __rcu_pending(struct rcu_state *rsp, struct rcu_data *rdp) +{ + struct rcu_node *rnp = rdp->mynode; + + rdp->n_rcu_pending++; + + /* Check for CPU stalls, if enabled. */ + check_cpu_stall(rsp, rdp); + + /* Is the RCU core waiting for a quiescent state from this CPU? */ + if (rcu_scheduler_fully_active && + rdp->qs_pending && !rdp->passed_quiesce) { + rdp->n_rp_qs_pending++; + } else if (rdp->qs_pending && rdp->passed_quiesce) { + rdp->n_rp_report_qs++; + return 1; + } + + /* Does this CPU have callbacks ready to invoke? */ + if (cpu_has_callbacks_ready_to_invoke(rdp)) { + rdp->n_rp_cb_ready++; + return 1; + } + + /* Has RCU gone idle with this CPU needing another grace period? */ + if (cpu_needs_another_gp(rsp, rdp)) { + rdp->n_rp_cpu_needs_gp++; + return 1; + } + + /* Has another RCU grace period completed? */ + if (ACCESS_ONCE(rnp->completed) != rdp->completed) { /* outside lock */ + rdp->n_rp_gp_completed++; + return 1; + } + + /* Has a new RCU grace period started? */ + if (ACCESS_ONCE(rnp->gpnum) != rdp->gpnum) { /* outside lock */ + rdp->n_rp_gp_started++; + return 1; + } + + /* nothing to do */ + rdp->n_rp_need_nothing++; + return 0; +} + +/* + * Check to see if there is any immediate RCU-related work to be done + * by the current CPU, returning 1 if so. This function is part of the + * RCU implementation; it is -not- an exported member of the RCU API. + */ +static int rcu_pending(int cpu) +{ + struct rcu_state *rsp; + + for_each_rcu_flavor(rsp) + if (__rcu_pending(rsp, per_cpu_ptr(rsp->rda, cpu))) + return 1; + return 0; +} + +/* + * Return true if the specified CPU has any callback. If all_lazy is + * non-NULL, store an indication of whether all callbacks are lazy. + * (If there are no callbacks, all of them are deemed to be lazy.) + */ +static int rcu_cpu_has_callbacks(int cpu, bool *all_lazy) +{ + bool al = true; + bool hc = false; + struct rcu_data *rdp; + struct rcu_state *rsp; + + for_each_rcu_flavor(rsp) { + rdp = per_cpu_ptr(rsp->rda, cpu); + if (!rdp->nxtlist) + continue; + hc = true; + if (rdp->qlen != rdp->qlen_lazy || !all_lazy) { + al = false; + break; + } + } + if (all_lazy) + *all_lazy = al; + return hc; +} + +/* + * Helper function for _rcu_barrier() tracing. If tracing is disabled, + * the compiler is expected to optimize this away. + */ +static void _rcu_barrier_trace(struct rcu_state *rsp, const char *s, + int cpu, unsigned long done) +{ + trace_rcu_barrier(rsp->name, s, cpu, + atomic_read(&rsp->barrier_cpu_count), done); +} + +/* + * RCU callback function for _rcu_barrier(). If we are last, wake + * up the task executing _rcu_barrier(). + */ +static void rcu_barrier_callback(struct rcu_head *rhp) +{ + struct rcu_data *rdp = container_of(rhp, struct rcu_data, barrier_head); + struct rcu_state *rsp = rdp->rsp; + + if (atomic_dec_and_test(&rsp->barrier_cpu_count)) { + _rcu_barrier_trace(rsp, "LastCB", -1, rsp->n_barrier_done); + complete(&rsp->barrier_completion); + } else { + _rcu_barrier_trace(rsp, "CB", -1, rsp->n_barrier_done); + } +} + +/* + * Called with preemption disabled, and from cross-cpu IRQ context. + */ +static void rcu_barrier_func(void *type) +{ + struct rcu_state *rsp = type; + struct rcu_data *rdp = __this_cpu_ptr(rsp->rda); + + _rcu_barrier_trace(rsp, "IRQ", -1, rsp->n_barrier_done); + atomic_inc(&rsp->barrier_cpu_count); + rsp->call(&rdp->barrier_head, rcu_barrier_callback); +} + +/* + * Orchestrate the specified type of RCU barrier, waiting for all + * RCU callbacks of the specified type to complete. + */ +static void _rcu_barrier(struct rcu_state *rsp) +{ + int cpu; + struct rcu_data *rdp; + unsigned long snap = ACCESS_ONCE(rsp->n_barrier_done); + unsigned long snap_done; + + _rcu_barrier_trace(rsp, "Begin", -1, snap); + + /* Take mutex to serialize concurrent rcu_barrier() requests. */ + mutex_lock(&rsp->barrier_mutex); + + /* + * Ensure that all prior references, including to ->n_barrier_done, + * are ordered before the _rcu_barrier() machinery. + */ + smp_mb(); /* See above block comment. */ + + /* + * Recheck ->n_barrier_done to see if others did our work for us. + * This means checking ->n_barrier_done for an even-to-odd-to-even + * transition. The "if" expression below therefore rounds the old + * value up to the next even number and adds two before comparing. + */ + snap_done = rsp->n_barrier_done; + _rcu_barrier_trace(rsp, "Check", -1, snap_done); + + /* + * If the value in snap is odd, we needed to wait for the current + * rcu_barrier() to complete, then wait for the next one, in other + * words, we need the value of snap_done to be three larger than + * the value of snap. On the other hand, if the value in snap is + * even, we only had to wait for the next rcu_barrier() to complete, + * in other words, we need the value of snap_done to be only two + * greater than the value of snap. The "(snap + 3) & ~0x1" computes + * this for us (thank you, Linus!). + */ + if (ULONG_CMP_GE(snap_done, (snap + 3) & ~0x1)) { + _rcu_barrier_trace(rsp, "EarlyExit", -1, snap_done); + smp_mb(); /* caller's subsequent code after above check. */ + mutex_unlock(&rsp->barrier_mutex); + return; + } + + /* + * Increment ->n_barrier_done to avoid duplicate work. Use + * ACCESS_ONCE() to prevent the compiler from speculating + * the increment to precede the early-exit check. + */ + ACCESS_ONCE(rsp->n_barrier_done)++; + WARN_ON_ONCE((rsp->n_barrier_done & 0x1) != 1); + _rcu_barrier_trace(rsp, "Inc1", -1, rsp->n_barrier_done); + smp_mb(); /* Order ->n_barrier_done increment with below mechanism. */ + + /* + * Initialize the count to one rather than to zero in order to + * avoid a too-soon return to zero in case of a short grace period + * (or preemption of this task). Exclude CPU-hotplug operations + * to ensure that no offline CPU has callbacks queued. + */ + init_completion(&rsp->barrier_completion); + atomic_set(&rsp->barrier_cpu_count, 1); + get_online_cpus(); + + /* + * Force each CPU with callbacks to register a new callback. + * When that callback is invoked, we will know that all of the + * corresponding CPU's preceding callbacks have been invoked. + */ + for_each_possible_cpu(cpu) { + if (!cpu_online(cpu) && !rcu_is_nocb_cpu(cpu)) + continue; + rdp = per_cpu_ptr(rsp->rda, cpu); + if (rcu_is_nocb_cpu(cpu)) { + _rcu_barrier_trace(rsp, "OnlineNoCB", cpu, + rsp->n_barrier_done); + atomic_inc(&rsp->barrier_cpu_count); + __call_rcu(&rdp->barrier_head, rcu_barrier_callback, + rsp, cpu, 0); + } else if (ACCESS_ONCE(rdp->qlen)) { + _rcu_barrier_trace(rsp, "OnlineQ", cpu, + rsp->n_barrier_done); + smp_call_function_single(cpu, rcu_barrier_func, rsp, 1); + } else { + _rcu_barrier_trace(rsp, "OnlineNQ", cpu, + rsp->n_barrier_done); + } + } + put_online_cpus(); + + /* + * Now that we have an rcu_barrier_callback() callback on each + * CPU, and thus each counted, remove the initial count. + */ + if (atomic_dec_and_test(&rsp->barrier_cpu_count)) + complete(&rsp->barrier_completion); + + /* Increment ->n_barrier_done to prevent duplicate work. */ + smp_mb(); /* Keep increment after above mechanism. */ + ACCESS_ONCE(rsp->n_barrier_done)++; + WARN_ON_ONCE((rsp->n_barrier_done & 0x1) != 0); + _rcu_barrier_trace(rsp, "Inc2", -1, rsp->n_barrier_done); + smp_mb(); /* Keep increment before caller's subsequent code. */ + + /* Wait for all rcu_barrier_callback() callbacks to be invoked. */ + wait_for_completion(&rsp->barrier_completion); + + /* Other rcu_barrier() invocations can now safely proceed. */ + mutex_unlock(&rsp->barrier_mutex); +} + +/** + * rcu_barrier_bh - Wait until all in-flight call_rcu_bh() callbacks complete. + */ +void rcu_barrier_bh(void) +{ + _rcu_barrier(&rcu_bh_state); +} +EXPORT_SYMBOL_GPL(rcu_barrier_bh); + +/** + * rcu_barrier_sched - Wait for in-flight call_rcu_sched() callbacks. + */ +void rcu_barrier_sched(void) +{ + _rcu_barrier(&rcu_sched_state); +} +EXPORT_SYMBOL_GPL(rcu_barrier_sched); + +/* + * Do boot-time initialization of a CPU's per-CPU RCU data. + */ +static void __init +rcu_boot_init_percpu_data(int cpu, struct rcu_state *rsp) +{ + unsigned long flags; + struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu); + struct rcu_node *rnp = rcu_get_root(rsp); + + /* Set up local state, ensuring consistent view of global state. */ + raw_spin_lock_irqsave(&rnp->lock, flags); + rdp->grpmask = 1UL << (cpu - rdp->mynode->grplo); + init_callback_list(rdp); + rdp->qlen_lazy = 0; + ACCESS_ONCE(rdp->qlen) = 0; + rdp->dynticks = &per_cpu(rcu_dynticks, cpu); + WARN_ON_ONCE(rdp->dynticks->dynticks_nesting != DYNTICK_TASK_EXIT_IDLE); + WARN_ON_ONCE(atomic_read(&rdp->dynticks->dynticks) != 1); + rdp->cpu = cpu; + rdp->rsp = rsp; + rcu_boot_init_nocb_percpu_data(rdp); + raw_spin_unlock_irqrestore(&rnp->lock, flags); +} + +/* + * Initialize a CPU's per-CPU RCU data. Note that only one online or + * offline event can be happening at a given time. Note also that we + * can accept some slop in the rsp->completed access due to the fact + * that this CPU cannot possibly have any RCU callbacks in flight yet. + */ +static void +rcu_init_percpu_data(int cpu, struct rcu_state *rsp, int preemptible) +{ + unsigned long flags; + unsigned long mask; + struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu); + struct rcu_node *rnp = rcu_get_root(rsp); + + /* Exclude new grace periods. */ + mutex_lock(&rsp->onoff_mutex); + + /* Set up local state, ensuring consistent view of global state. */ + raw_spin_lock_irqsave(&rnp->lock, flags); + rdp->beenonline = 1; /* We have now been online. */ + rdp->preemptible = preemptible; + rdp->qlen_last_fqs_check = 0; + rdp->n_force_qs_snap = rsp->n_force_qs; + rdp->blimit = blimit; + init_callback_list(rdp); /* Re-enable callbacks on this CPU. */ + rdp->dynticks->dynticks_nesting = DYNTICK_TASK_EXIT_IDLE; + rcu_sysidle_init_percpu_data(rdp->dynticks); + atomic_set(&rdp->dynticks->dynticks, + (atomic_read(&rdp->dynticks->dynticks) & ~0x1) + 1); + raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */ + + /* Add CPU to rcu_node bitmasks. */ + rnp = rdp->mynode; + mask = rdp->grpmask; + do { + /* Exclude any attempts to start a new GP on small systems. */ + raw_spin_lock(&rnp->lock); /* irqs already disabled. */ + rnp->qsmaskinit |= mask; + mask = rnp->grpmask; + if (rnp == rdp->mynode) { + /* + * If there is a grace period in progress, we will + * set up to wait for it next time we run the + * RCU core code. + */ + rdp->gpnum = rnp->completed; + rdp->completed = rnp->completed; + rdp->passed_quiesce = 0; + rdp->qs_pending = 0; + trace_rcu_grace_period(rsp->name, rdp->gpnum, TPS("cpuonl")); + } + raw_spin_unlock(&rnp->lock); /* irqs already disabled. */ + rnp = rnp->parent; + } while (rnp != NULL && !(rnp->qsmaskinit & mask)); + local_irq_restore(flags); + + mutex_unlock(&rsp->onoff_mutex); +} + +static void rcu_prepare_cpu(int cpu) +{ + struct rcu_state *rsp; + + for_each_rcu_flavor(rsp) + rcu_init_percpu_data(cpu, rsp, + strcmp(rsp->name, "rcu_preempt") == 0); +} + +/* + * Handle CPU online/offline notification events. + */ +static int rcu_cpu_notify(struct notifier_block *self, + unsigned long action, void *hcpu) +{ + long cpu = (long)hcpu; + struct rcu_data *rdp = per_cpu_ptr(rcu_state->rda, cpu); + struct rcu_node *rnp = rdp->mynode; + struct rcu_state *rsp; + + trace_rcu_utilization(TPS("Start CPU hotplug")); + switch (action) { + case CPU_UP_PREPARE: + case CPU_UP_PREPARE_FROZEN: + rcu_prepare_cpu(cpu); + rcu_prepare_kthreads(cpu); + break; + case CPU_ONLINE: + case CPU_DOWN_FAILED: + rcu_boost_kthread_setaffinity(rnp, -1); + break; + case CPU_DOWN_PREPARE: + rcu_boost_kthread_setaffinity(rnp, cpu); + break; + case CPU_DYING: + case CPU_DYING_FROZEN: + for_each_rcu_flavor(rsp) + rcu_cleanup_dying_cpu(rsp); + break; + case CPU_DEAD: + case CPU_DEAD_FROZEN: + case CPU_UP_CANCELED: + case CPU_UP_CANCELED_FROZEN: + for_each_rcu_flavor(rsp) + rcu_cleanup_dead_cpu(cpu, rsp); + break; + default: + break; + } + trace_rcu_utilization(TPS("End CPU hotplug")); + return NOTIFY_OK; +} + +static int rcu_pm_notify(struct notifier_block *self, + unsigned long action, void *hcpu) +{ + switch (action) { + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + if (nr_cpu_ids <= 256) /* Expediting bad for large systems. */ + rcu_expedited = 1; + break; + case PM_POST_HIBERNATION: + case PM_POST_SUSPEND: + rcu_expedited = 0; + break; + default: + break; + } + return NOTIFY_OK; +} + +/* + * Spawn the kthread that handles this RCU flavor's grace periods. + */ +static int __init rcu_spawn_gp_kthread(void) +{ + unsigned long flags; + struct rcu_node *rnp; + struct rcu_state *rsp; + struct task_struct *t; + + for_each_rcu_flavor(rsp) { + t = kthread_run(rcu_gp_kthread, rsp, "%s", rsp->name); + BUG_ON(IS_ERR(t)); + rnp = rcu_get_root(rsp); + raw_spin_lock_irqsave(&rnp->lock, flags); + rsp->gp_kthread = t; + raw_spin_unlock_irqrestore(&rnp->lock, flags); + rcu_spawn_nocb_kthreads(rsp); + } + return 0; +} +early_initcall(rcu_spawn_gp_kthread); + +/* + * This function is invoked towards the end of the scheduler's initialization + * process. Before this is called, the idle task might contain + * RCU read-side critical sections (during which time, this idle + * task is booting the system). After this function is called, the + * idle tasks are prohibited from containing RCU read-side critical + * sections. This function also enables RCU lockdep checking. + */ +void rcu_scheduler_starting(void) +{ + WARN_ON(num_online_cpus() != 1); + WARN_ON(nr_context_switches() > 0); + rcu_scheduler_active = 1; +} + +/* + * Compute the per-level fanout, either using the exact fanout specified + * or balancing the tree, depending on CONFIG_RCU_FANOUT_EXACT. + */ +#ifdef CONFIG_RCU_FANOUT_EXACT +static void __init rcu_init_levelspread(struct rcu_state *rsp) +{ + int i; + + for (i = rcu_num_lvls - 1; i > 0; i--) + rsp->levelspread[i] = CONFIG_RCU_FANOUT; + rsp->levelspread[0] = rcu_fanout_leaf; +} +#else /* #ifdef CONFIG_RCU_FANOUT_EXACT */ +static void __init rcu_init_levelspread(struct rcu_state *rsp) +{ + int ccur; + int cprv; + int i; + + cprv = nr_cpu_ids; + for (i = rcu_num_lvls - 1; i >= 0; i--) { + ccur = rsp->levelcnt[i]; + rsp->levelspread[i] = (cprv + ccur - 1) / ccur; + cprv = ccur; + } +} +#endif /* #else #ifdef CONFIG_RCU_FANOUT_EXACT */ + +/* + * Helper function for rcu_init() that initializes one rcu_state structure. + */ +static void __init rcu_init_one(struct rcu_state *rsp, + struct rcu_data __percpu *rda) +{ + static char *buf[] = { "rcu_node_0", + "rcu_node_1", + "rcu_node_2", + "rcu_node_3" }; /* Match MAX_RCU_LVLS */ + static char *fqs[] = { "rcu_node_fqs_0", + "rcu_node_fqs_1", + "rcu_node_fqs_2", + "rcu_node_fqs_3" }; /* Match MAX_RCU_LVLS */ + int cpustride = 1; + int i; + int j; + struct rcu_node *rnp; + + BUILD_BUG_ON(MAX_RCU_LVLS > ARRAY_SIZE(buf)); /* Fix buf[] init! */ + + /* Silence gcc 4.8 warning about array index out of range. */ + if (rcu_num_lvls > RCU_NUM_LVLS) + panic("rcu_init_one: rcu_num_lvls overflow"); + + /* Initialize the level-tracking arrays. */ + + for (i = 0; i < rcu_num_lvls; i++) + rsp->levelcnt[i] = num_rcu_lvl[i]; + for (i = 1; i < rcu_num_lvls; i++) + rsp->level[i] = rsp->level[i - 1] + rsp->levelcnt[i - 1]; + rcu_init_levelspread(rsp); + + /* Initialize the elements themselves, starting from the leaves. */ + + for (i = rcu_num_lvls - 1; i >= 0; i--) { + cpustride *= rsp->levelspread[i]; + rnp = rsp->level[i]; + for (j = 0; j < rsp->levelcnt[i]; j++, rnp++) { + raw_spin_lock_init(&rnp->lock); + lockdep_set_class_and_name(&rnp->lock, + &rcu_node_class[i], buf[i]); + raw_spin_lock_init(&rnp->fqslock); + lockdep_set_class_and_name(&rnp->fqslock, + &rcu_fqs_class[i], fqs[i]); + rnp->gpnum = rsp->gpnum; + rnp->completed = rsp->completed; + rnp->qsmask = 0; + rnp->qsmaskinit = 0; + rnp->grplo = j * cpustride; + rnp->grphi = (j + 1) * cpustride - 1; + if (rnp->grphi >= NR_CPUS) + rnp->grphi = NR_CPUS - 1; + if (i == 0) { + rnp->grpnum = 0; + rnp->grpmask = 0; + rnp->parent = NULL; + } else { + rnp->grpnum = j % rsp->levelspread[i - 1]; + rnp->grpmask = 1UL << rnp->grpnum; + rnp->parent = rsp->level[i - 1] + + j / rsp->levelspread[i - 1]; + } + rnp->level = i; + INIT_LIST_HEAD(&rnp->blkd_tasks); + rcu_init_one_nocb(rnp); + } + } + + rsp->rda = rda; + init_waitqueue_head(&rsp->gp_wq); + init_irq_work(&rsp->wakeup_work, rsp_wakeup); + rnp = rsp->level[rcu_num_lvls - 1]; + for_each_possible_cpu(i) { + while (i > rnp->grphi) + rnp++; + per_cpu_ptr(rsp->rda, i)->mynode = rnp; + rcu_boot_init_percpu_data(i, rsp); + } + list_add(&rsp->flavors, &rcu_struct_flavors); +} + +/* + * Compute the rcu_node tree geometry from kernel parameters. This cannot + * replace the definitions in tree.h because those are needed to size + * the ->node array in the rcu_state structure. + */ +static void __init rcu_init_geometry(void) +{ + ulong d; + int i; + int j; + int n = nr_cpu_ids; + int rcu_capacity[MAX_RCU_LVLS + 1]; + + /* + * Initialize any unspecified boot parameters. + * The default values of jiffies_till_first_fqs and + * jiffies_till_next_fqs are set to the RCU_JIFFIES_TILL_FORCE_QS + * value, which is a function of HZ, then adding one for each + * RCU_JIFFIES_FQS_DIV CPUs that might be on the system. + */ + d = RCU_JIFFIES_TILL_FORCE_QS + nr_cpu_ids / RCU_JIFFIES_FQS_DIV; + if (jiffies_till_first_fqs == ULONG_MAX) + jiffies_till_first_fqs = d; + if (jiffies_till_next_fqs == ULONG_MAX) + jiffies_till_next_fqs = d; + + /* If the compile-time values are accurate, just leave. */ + if (rcu_fanout_leaf == CONFIG_RCU_FANOUT_LEAF && + nr_cpu_ids == NR_CPUS) + return; + + /* + * Compute number of nodes that can be handled an rcu_node tree + * with the given number of levels. Setting rcu_capacity[0] makes + * some of the arithmetic easier. + */ + rcu_capacity[0] = 1; + rcu_capacity[1] = rcu_fanout_leaf; + for (i = 2; i <= MAX_RCU_LVLS; i++) + rcu_capacity[i] = rcu_capacity[i - 1] * CONFIG_RCU_FANOUT; + + /* + * The boot-time rcu_fanout_leaf parameter is only permitted + * to increase the leaf-level fanout, not decrease it. Of course, + * the leaf-level fanout cannot exceed the number of bits in + * the rcu_node masks. Finally, the tree must be able to accommodate + * the configured number of CPUs. Complain and fall back to the + * compile-time values if these limits are exceeded. + */ + if (rcu_fanout_leaf < CONFIG_RCU_FANOUT_LEAF || + rcu_fanout_leaf > sizeof(unsigned long) * 8 || + n > rcu_capacity[MAX_RCU_LVLS]) { + WARN_ON(1); + return; + } + + /* Calculate the number of rcu_nodes at each level of the tree. */ + for (i = 1; i <= MAX_RCU_LVLS; i++) + if (n <= rcu_capacity[i]) { + for (j = 0; j <= i; j++) + num_rcu_lvl[j] = + DIV_ROUND_UP(n, rcu_capacity[i - j]); + rcu_num_lvls = i; + for (j = i + 1; j <= MAX_RCU_LVLS; j++) + num_rcu_lvl[j] = 0; + break; + } + + /* Calculate the total number of rcu_node structures. */ + rcu_num_nodes = 0; + for (i = 0; i <= MAX_RCU_LVLS; i++) + rcu_num_nodes += num_rcu_lvl[i]; + rcu_num_nodes -= n; +} + +void __init rcu_init(void) +{ + int cpu; + + rcu_bootup_announce(); + rcu_init_geometry(); + rcu_init_one(&rcu_bh_state, &rcu_bh_data); + rcu_init_one(&rcu_sched_state, &rcu_sched_data); + __rcu_init_preempt(); + open_softirq(RCU_SOFTIRQ, rcu_process_callbacks); + + /* + * We don't need protection against CPU-hotplug here because + * this is called early in boot, before either interrupts + * or the scheduler are operational. + */ + cpu_notifier(rcu_cpu_notify, 0); + pm_notifier(rcu_pm_notify, 0); + for_each_online_cpu(cpu) + rcu_cpu_notify(NULL, CPU_UP_PREPARE, (void *)(long)cpu); +} + +#include "tree_plugin.h" diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h new file mode 100644 index 0000000..52be957 --- /dev/null +++ b/kernel/rcu/tree.h @@ -0,0 +1,585 @@ +/* + * Read-Copy Update mechanism for mutual exclusion (tree-based version) + * Internal non-public definitions. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright IBM Corporation, 2008 + * + * Author: Ingo Molnar + * Paul E. McKenney + */ + +#include +#include +#include +#include +#include +#include + +/* + * Define shape of hierarchy based on NR_CPUS, CONFIG_RCU_FANOUT, and + * CONFIG_RCU_FANOUT_LEAF. + * In theory, it should be possible to add more levels straightforwardly. + * In practice, this did work well going from three levels to four. + * Of course, your mileage may vary. + */ +#define MAX_RCU_LVLS 4 +#define RCU_FANOUT_1 (CONFIG_RCU_FANOUT_LEAF) +#define RCU_FANOUT_2 (RCU_FANOUT_1 * CONFIG_RCU_FANOUT) +#define RCU_FANOUT_3 (RCU_FANOUT_2 * CONFIG_RCU_FANOUT) +#define RCU_FANOUT_4 (RCU_FANOUT_3 * CONFIG_RCU_FANOUT) + +#if NR_CPUS <= RCU_FANOUT_1 +# define RCU_NUM_LVLS 1 +# define NUM_RCU_LVL_0 1 +# define NUM_RCU_LVL_1 (NR_CPUS) +# define NUM_RCU_LVL_2 0 +# define NUM_RCU_LVL_3 0 +# define NUM_RCU_LVL_4 0 +#elif NR_CPUS <= RCU_FANOUT_2 +# define RCU_NUM_LVLS 2 +# define NUM_RCU_LVL_0 1 +# define NUM_RCU_LVL_1 DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_1) +# define NUM_RCU_LVL_2 (NR_CPUS) +# define NUM_RCU_LVL_3 0 +# define NUM_RCU_LVL_4 0 +#elif NR_CPUS <= RCU_FANOUT_3 +# define RCU_NUM_LVLS 3 +# define NUM_RCU_LVL_0 1 +# define NUM_RCU_LVL_1 DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_2) +# define NUM_RCU_LVL_2 DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_1) +# define NUM_RCU_LVL_3 (NR_CPUS) +# define NUM_RCU_LVL_4 0 +#elif NR_CPUS <= RCU_FANOUT_4 +# define RCU_NUM_LVLS 4 +# define NUM_RCU_LVL_0 1 +# define NUM_RCU_LVL_1 DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_3) +# define NUM_RCU_LVL_2 DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_2) +# define NUM_RCU_LVL_3 DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_1) +# define NUM_RCU_LVL_4 (NR_CPUS) +#else +# error "CONFIG_RCU_FANOUT insufficient for NR_CPUS" +#endif /* #if (NR_CPUS) <= RCU_FANOUT_1 */ + +#define RCU_SUM (NUM_RCU_LVL_0 + NUM_RCU_LVL_1 + NUM_RCU_LVL_2 + NUM_RCU_LVL_3 + NUM_RCU_LVL_4) +#define NUM_RCU_NODES (RCU_SUM - NR_CPUS) + +extern int rcu_num_lvls; +extern int rcu_num_nodes; + +/* + * Dynticks per-CPU state. + */ +struct rcu_dynticks { + long long dynticks_nesting; /* Track irq/process nesting level. */ + /* Process level is worth LLONG_MAX/2. */ + int dynticks_nmi_nesting; /* Track NMI nesting level. */ + atomic_t dynticks; /* Even value for idle, else odd. */ +#ifdef CONFIG_NO_HZ_FULL_SYSIDLE + long long dynticks_idle_nesting; + /* irq/process nesting level from idle. */ + atomic_t dynticks_idle; /* Even value for idle, else odd. */ + /* "Idle" excludes userspace execution. */ + unsigned long dynticks_idle_jiffies; + /* End of last non-NMI non-idle period. */ +#endif /* #ifdef CONFIG_NO_HZ_FULL_SYSIDLE */ +#ifdef CONFIG_RCU_FAST_NO_HZ + bool all_lazy; /* Are all CPU's CBs lazy? */ + unsigned long nonlazy_posted; + /* # times non-lazy CBs posted to CPU. */ + unsigned long nonlazy_posted_snap; + /* idle-period nonlazy_posted snapshot. */ + unsigned long last_accelerate; + /* Last jiffy CBs were accelerated. */ + unsigned long last_advance_all; + /* Last jiffy CBs were all advanced. */ + int tick_nohz_enabled_snap; /* Previously seen value from sysfs. */ +#endif /* #ifdef CONFIG_RCU_FAST_NO_HZ */ +}; + +/* RCU's kthread states for tracing. */ +#define RCU_KTHREAD_STOPPED 0 +#define RCU_KTHREAD_RUNNING 1 +#define RCU_KTHREAD_WAITING 2 +#define RCU_KTHREAD_OFFCPU 3 +#define RCU_KTHREAD_YIELDING 4 +#define RCU_KTHREAD_MAX 4 + +/* + * Definition for node within the RCU grace-period-detection hierarchy. + */ +struct rcu_node { + raw_spinlock_t lock; /* Root rcu_node's lock protects some */ + /* rcu_state fields as well as following. */ + unsigned long gpnum; /* Current grace period for this node. */ + /* This will either be equal to or one */ + /* behind the root rcu_node's gpnum. */ + unsigned long completed; /* Last GP completed for this node. */ + /* This will either be equal to or one */ + /* behind the root rcu_node's gpnum. */ + unsigned long qsmask; /* CPUs or groups that need to switch in */ + /* order for current grace period to proceed.*/ + /* In leaf rcu_node, each bit corresponds to */ + /* an rcu_data structure, otherwise, each */ + /* bit corresponds to a child rcu_node */ + /* structure. */ + unsigned long expmask; /* Groups that have ->blkd_tasks */ + /* elements that need to drain to allow the */ + /* current expedited grace period to */ + /* complete (only for TREE_PREEMPT_RCU). */ + unsigned long qsmaskinit; + /* Per-GP initial value for qsmask & expmask. */ + unsigned long grpmask; /* Mask to apply to parent qsmask. */ + /* Only one bit will be set in this mask. */ + int grplo; /* lowest-numbered CPU or group here. */ + int grphi; /* highest-numbered CPU or group here. */ + u8 grpnum; /* CPU/group number for next level up. */ + u8 level; /* root is at level 0. */ + struct rcu_node *parent; + struct list_head blkd_tasks; + /* Tasks blocked in RCU read-side critical */ + /* section. Tasks are placed at the head */ + /* of this list and age towards the tail. */ + struct list_head *gp_tasks; + /* Pointer to the first task blocking the */ + /* current grace period, or NULL if there */ + /* is no such task. */ + struct list_head *exp_tasks; + /* Pointer to the first task blocking the */ + /* current expedited grace period, or NULL */ + /* if there is no such task. If there */ + /* is no current expedited grace period, */ + /* then there can cannot be any such task. */ +#ifdef CONFIG_RCU_BOOST + struct list_head *boost_tasks; + /* Pointer to first task that needs to be */ + /* priority boosted, or NULL if no priority */ + /* boosting is needed for this rcu_node */ + /* structure. If there are no tasks */ + /* queued on this rcu_node structure that */ + /* are blocking the current grace period, */ + /* there can be no such task. */ + unsigned long boost_time; + /* When to start boosting (jiffies). */ + struct task_struct *boost_kthread_task; + /* kthread that takes care of priority */ + /* boosting for this rcu_node structure. */ + unsigned int boost_kthread_status; + /* State of boost_kthread_task for tracing. */ + unsigned long n_tasks_boosted; + /* Total number of tasks boosted. */ + unsigned long n_exp_boosts; + /* Number of tasks boosted for expedited GP. */ + unsigned long n_normal_boosts; + /* Number of tasks boosted for normal GP. */ + unsigned long n_balk_blkd_tasks; + /* Refused to boost: no blocked tasks. */ + unsigned long n_balk_exp_gp_tasks; + /* Refused to boost: nothing blocking GP. */ + unsigned long n_balk_boost_tasks; + /* Refused to boost: already boosting. */ + unsigned long n_balk_notblocked; + /* Refused to boost: RCU RS CS still running. */ + unsigned long n_balk_notyet; + /* Refused to boost: not yet time. */ + unsigned long n_balk_nos; + /* Refused to boost: not sure why, though. */ + /* This can happen due to race conditions. */ +#endif /* #ifdef CONFIG_RCU_BOOST */ +#ifdef CONFIG_RCU_NOCB_CPU + wait_queue_head_t nocb_gp_wq[2]; + /* Place for rcu_nocb_kthread() to wait GP. */ +#endif /* #ifdef CONFIG_RCU_NOCB_CPU */ + int need_future_gp[2]; + /* Counts of upcoming no-CB GP requests. */ + raw_spinlock_t fqslock ____cacheline_internodealigned_in_smp; +} ____cacheline_internodealigned_in_smp; + +/* + * Do a full breadth-first scan of the rcu_node structures for the + * specified rcu_state structure. + */ +#define rcu_for_each_node_breadth_first(rsp, rnp) \ + for ((rnp) = &(rsp)->node[0]; \ + (rnp) < &(rsp)->node[rcu_num_nodes]; (rnp)++) + +/* + * Do a breadth-first scan of the non-leaf rcu_node structures for the + * specified rcu_state structure. Note that if there is a singleton + * rcu_node tree with but one rcu_node structure, this loop is a no-op. + */ +#define rcu_for_each_nonleaf_node_breadth_first(rsp, rnp) \ + for ((rnp) = &(rsp)->node[0]; \ + (rnp) < (rsp)->level[rcu_num_lvls - 1]; (rnp)++) + +/* + * Scan the leaves of the rcu_node hierarchy for the specified rcu_state + * structure. Note that if there is a singleton rcu_node tree with but + * one rcu_node structure, this loop -will- visit the rcu_node structure. + * It is still a leaf node, even if it is also the root node. + */ +#define rcu_for_each_leaf_node(rsp, rnp) \ + for ((rnp) = (rsp)->level[rcu_num_lvls - 1]; \ + (rnp) < &(rsp)->node[rcu_num_nodes]; (rnp)++) + +/* Index values for nxttail array in struct rcu_data. */ +#define RCU_DONE_TAIL 0 /* Also RCU_WAIT head. */ +#define RCU_WAIT_TAIL 1 /* Also RCU_NEXT_READY head. */ +#define RCU_NEXT_READY_TAIL 2 /* Also RCU_NEXT head. */ +#define RCU_NEXT_TAIL 3 +#define RCU_NEXT_SIZE 4 + +/* Per-CPU data for read-copy update. */ +struct rcu_data { + /* 1) quiescent-state and grace-period handling : */ + unsigned long completed; /* Track rsp->completed gp number */ + /* in order to detect GP end. */ + unsigned long gpnum; /* Highest gp number that this CPU */ + /* is aware of having started. */ + bool passed_quiesce; /* User-mode/idle loop etc. */ + bool qs_pending; /* Core waits for quiesc state. */ + bool beenonline; /* CPU online at least once. */ + bool preemptible; /* Preemptible RCU? */ + struct rcu_node *mynode; /* This CPU's leaf of hierarchy */ + unsigned long grpmask; /* Mask to apply to leaf qsmask. */ +#ifdef CONFIG_RCU_CPU_STALL_INFO + unsigned long ticks_this_gp; /* The number of scheduling-clock */ + /* ticks this CPU has handled */ + /* during and after the last grace */ + /* period it is aware of. */ +#endif /* #ifdef CONFIG_RCU_CPU_STALL_INFO */ + + /* 2) batch handling */ + /* + * If nxtlist is not NULL, it is partitioned as follows. + * Any of the partitions might be empty, in which case the + * pointer to that partition will be equal to the pointer for + * the following partition. When the list is empty, all of + * the nxttail elements point to the ->nxtlist pointer itself, + * which in that case is NULL. + * + * [nxtlist, *nxttail[RCU_DONE_TAIL]): + * Entries that batch # <= ->completed + * The grace period for these entries has completed, and + * the other grace-period-completed entries may be moved + * here temporarily in rcu_process_callbacks(). + * [*nxttail[RCU_DONE_TAIL], *nxttail[RCU_WAIT_TAIL]): + * Entries that batch # <= ->completed - 1: waiting for current GP + * [*nxttail[RCU_WAIT_TAIL], *nxttail[RCU_NEXT_READY_TAIL]): + * Entries known to have arrived before current GP ended + * [*nxttail[RCU_NEXT_READY_TAIL], *nxttail[RCU_NEXT_TAIL]): + * Entries that might have arrived after current GP ended + * Note that the value of *nxttail[RCU_NEXT_TAIL] will + * always be NULL, as this is the end of the list. + */ + struct rcu_head *nxtlist; + struct rcu_head **nxttail[RCU_NEXT_SIZE]; + unsigned long nxtcompleted[RCU_NEXT_SIZE]; + /* grace periods for sublists. */ + long qlen_lazy; /* # of lazy queued callbacks */ + long qlen; /* # of queued callbacks, incl lazy */ + long qlen_last_fqs_check; + /* qlen at last check for QS forcing */ + unsigned long n_cbs_invoked; /* count of RCU cbs invoked. */ + unsigned long n_nocbs_invoked; /* count of no-CBs RCU cbs invoked. */ + unsigned long n_cbs_orphaned; /* RCU cbs orphaned by dying CPU */ + unsigned long n_cbs_adopted; /* RCU cbs adopted from dying CPU */ + unsigned long n_force_qs_snap; + /* did other CPU force QS recently? */ + long blimit; /* Upper limit on a processed batch */ + + /* 3) dynticks interface. */ + struct rcu_dynticks *dynticks; /* Shared per-CPU dynticks state. */ + int dynticks_snap; /* Per-GP tracking for dynticks. */ + + /* 4) reasons this CPU needed to be kicked by force_quiescent_state */ + unsigned long dynticks_fqs; /* Kicked due to dynticks idle. */ + unsigned long offline_fqs; /* Kicked due to being offline. */ + + /* 5) __rcu_pending() statistics. */ + unsigned long n_rcu_pending; /* rcu_pending() calls since boot. */ + unsigned long n_rp_qs_pending; + unsigned long n_rp_report_qs; + unsigned long n_rp_cb_ready; + unsigned long n_rp_cpu_needs_gp; + unsigned long n_rp_gp_completed; + unsigned long n_rp_gp_started; + unsigned long n_rp_need_nothing; + + /* 6) _rcu_barrier() and OOM callbacks. */ + struct rcu_head barrier_head; +#ifdef CONFIG_RCU_FAST_NO_HZ + struct rcu_head oom_head; +#endif /* #ifdef CONFIG_RCU_FAST_NO_HZ */ + + /* 7) Callback offloading. */ +#ifdef CONFIG_RCU_NOCB_CPU + struct rcu_head *nocb_head; /* CBs waiting for kthread. */ + struct rcu_head **nocb_tail; + atomic_long_t nocb_q_count; /* # CBs waiting for kthread */ + atomic_long_t nocb_q_count_lazy; /* (approximate). */ + int nocb_p_count; /* # CBs being invoked by kthread */ + int nocb_p_count_lazy; /* (approximate). */ + wait_queue_head_t nocb_wq; /* For nocb kthreads to sleep on. */ + struct task_struct *nocb_kthread; +#endif /* #ifdef CONFIG_RCU_NOCB_CPU */ + + /* 8) RCU CPU stall data. */ +#ifdef CONFIG_RCU_CPU_STALL_INFO + unsigned int softirq_snap; /* Snapshot of softirq activity. */ +#endif /* #ifdef CONFIG_RCU_CPU_STALL_INFO */ + + int cpu; + struct rcu_state *rsp; +}; + +/* Values for fqs_state field in struct rcu_state. */ +#define RCU_GP_IDLE 0 /* No grace period in progress. */ +#define RCU_GP_INIT 1 /* Grace period being initialized. */ +#define RCU_SAVE_DYNTICK 2 /* Need to scan dyntick state. */ +#define RCU_FORCE_QS 3 /* Need to force quiescent state. */ +#define RCU_SIGNAL_INIT RCU_SAVE_DYNTICK + +#define RCU_JIFFIES_TILL_FORCE_QS (1 + (HZ > 250) + (HZ > 500)) + /* For jiffies_till_first_fqs and */ + /* and jiffies_till_next_fqs. */ + +#define RCU_JIFFIES_FQS_DIV 256 /* Very large systems need more */ + /* delay between bouts of */ + /* quiescent-state forcing. */ + +#define RCU_STALL_RAT_DELAY 2 /* Allow other CPUs time to take */ + /* at least one scheduling clock */ + /* irq before ratting on them. */ + +#define rcu_wait(cond) \ +do { \ + for (;;) { \ + set_current_state(TASK_INTERRUPTIBLE); \ + if (cond) \ + break; \ + schedule(); \ + } \ + __set_current_state(TASK_RUNNING); \ +} while (0) + +/* + * RCU global state, including node hierarchy. This hierarchy is + * represented in "heap" form in a dense array. The root (first level) + * of the hierarchy is in ->node[0] (referenced by ->level[0]), the second + * level in ->node[1] through ->node[m] (->node[1] referenced by ->level[1]), + * and the third level in ->node[m+1] and following (->node[m+1] referenced + * by ->level[2]). The number of levels is determined by the number of + * CPUs and by CONFIG_RCU_FANOUT. Small systems will have a "hierarchy" + * consisting of a single rcu_node. + */ +struct rcu_state { + struct rcu_node node[NUM_RCU_NODES]; /* Hierarchy. */ + struct rcu_node *level[RCU_NUM_LVLS]; /* Hierarchy levels. */ + u32 levelcnt[MAX_RCU_LVLS + 1]; /* # nodes in each level. */ + u8 levelspread[RCU_NUM_LVLS]; /* kids/node in each level. */ + struct rcu_data __percpu *rda; /* pointer of percu rcu_data. */ + void (*call)(struct rcu_head *head, /* call_rcu() flavor. */ + void (*func)(struct rcu_head *head)); + + /* The following fields are guarded by the root rcu_node's lock. */ + + u8 fqs_state ____cacheline_internodealigned_in_smp; + /* Force QS state. */ + u8 boost; /* Subject to priority boost. */ + unsigned long gpnum; /* Current gp number. */ + unsigned long completed; /* # of last completed gp. */ + struct task_struct *gp_kthread; /* Task for grace periods. */ + wait_queue_head_t gp_wq; /* Where GP task waits. */ + int gp_flags; /* Commands for GP task. */ + + /* End of fields guarded by root rcu_node's lock. */ + + raw_spinlock_t orphan_lock ____cacheline_internodealigned_in_smp; + /* Protect following fields. */ + struct rcu_head *orphan_nxtlist; /* Orphaned callbacks that */ + /* need a grace period. */ + struct rcu_head **orphan_nxttail; /* Tail of above. */ + struct rcu_head *orphan_donelist; /* Orphaned callbacks that */ + /* are ready to invoke. */ + struct rcu_head **orphan_donetail; /* Tail of above. */ + long qlen_lazy; /* Number of lazy callbacks. */ + long qlen; /* Total number of callbacks. */ + /* End of fields guarded by orphan_lock. */ + + struct mutex onoff_mutex; /* Coordinate hotplug & GPs. */ + + struct mutex barrier_mutex; /* Guards barrier fields. */ + atomic_t barrier_cpu_count; /* # CPUs waiting on. */ + struct completion barrier_completion; /* Wake at barrier end. */ + unsigned long n_barrier_done; /* ++ at start and end of */ + /* _rcu_barrier(). */ + /* End of fields guarded by barrier_mutex. */ + + atomic_long_t expedited_start; /* Starting ticket. */ + atomic_long_t expedited_done; /* Done ticket. */ + atomic_long_t expedited_wrap; /* # near-wrap incidents. */ + atomic_long_t expedited_tryfail; /* # acquisition failures. */ + atomic_long_t expedited_workdone1; /* # done by others #1. */ + atomic_long_t expedited_workdone2; /* # done by others #2. */ + atomic_long_t expedited_normal; /* # fallbacks to normal. */ + atomic_long_t expedited_stoppedcpus; /* # successful stop_cpus. */ + atomic_long_t expedited_done_tries; /* # tries to update _done. */ + atomic_long_t expedited_done_lost; /* # times beaten to _done. */ + atomic_long_t expedited_done_exit; /* # times exited _done loop. */ + + unsigned long jiffies_force_qs; /* Time at which to invoke */ + /* force_quiescent_state(). */ + unsigned long n_force_qs; /* Number of calls to */ + /* force_quiescent_state(). */ + unsigned long n_force_qs_lh; /* ~Number of calls leaving */ + /* due to lock unavailable. */ + unsigned long n_force_qs_ngp; /* Number of calls leaving */ + /* due to no GP active. */ + unsigned long gp_start; /* Time at which GP started, */ + /* but in jiffies. */ + unsigned long jiffies_stall; /* Time at which to check */ + /* for CPU stalls. */ + unsigned long gp_max; /* Maximum GP duration in */ + /* jiffies. */ + const char *name; /* Name of structure. */ + char abbr; /* Abbreviated name. */ + struct list_head flavors; /* List of RCU flavors. */ + struct irq_work wakeup_work; /* Postponed wakeups */ +}; + +/* Values for rcu_state structure's gp_flags field. */ +#define RCU_GP_FLAG_INIT 0x1 /* Need grace-period initialization. */ +#define RCU_GP_FLAG_FQS 0x2 /* Need grace-period quiescent-state forcing. */ + +extern struct list_head rcu_struct_flavors; + +/* Sequence through rcu_state structures for each RCU flavor. */ +#define for_each_rcu_flavor(rsp) \ + list_for_each_entry((rsp), &rcu_struct_flavors, flavors) + +/* Return values for rcu_preempt_offline_tasks(). */ + +#define RCU_OFL_TASKS_NORM_GP 0x1 /* Tasks blocking normal */ + /* GP were moved to root. */ +#define RCU_OFL_TASKS_EXP_GP 0x2 /* Tasks blocking expedited */ + /* GP were moved to root. */ + +/* + * RCU implementation internal declarations: + */ +extern struct rcu_state rcu_sched_state; +DECLARE_PER_CPU(struct rcu_data, rcu_sched_data); + +extern struct rcu_state rcu_bh_state; +DECLARE_PER_CPU(struct rcu_data, rcu_bh_data); + +#ifdef CONFIG_TREE_PREEMPT_RCU +extern struct rcu_state rcu_preempt_state; +DECLARE_PER_CPU(struct rcu_data, rcu_preempt_data); +#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */ + +#ifdef CONFIG_RCU_BOOST +DECLARE_PER_CPU(unsigned int, rcu_cpu_kthread_status); +DECLARE_PER_CPU(int, rcu_cpu_kthread_cpu); +DECLARE_PER_CPU(unsigned int, rcu_cpu_kthread_loops); +DECLARE_PER_CPU(char, rcu_cpu_has_work); +#endif /* #ifdef CONFIG_RCU_BOOST */ + +#ifndef RCU_TREE_NONCORE + +/* Forward declarations for rcutree_plugin.h */ +static void rcu_bootup_announce(void); +long rcu_batches_completed(void); +static void rcu_preempt_note_context_switch(int cpu); +static int rcu_preempt_blocked_readers_cgp(struct rcu_node *rnp); +#ifdef CONFIG_HOTPLUG_CPU +static void rcu_report_unblock_qs_rnp(struct rcu_node *rnp, + unsigned long flags); +#endif /* #ifdef CONFIG_HOTPLUG_CPU */ +static void rcu_print_detail_task_stall(struct rcu_state *rsp); +static int rcu_print_task_stall(struct rcu_node *rnp); +static void rcu_preempt_check_blocked_tasks(struct rcu_node *rnp); +#ifdef CONFIG_HOTPLUG_CPU +static int rcu_preempt_offline_tasks(struct rcu_state *rsp, + struct rcu_node *rnp, + struct rcu_data *rdp); +#endif /* #ifdef CONFIG_HOTPLUG_CPU */ +static void rcu_preempt_check_callbacks(int cpu); +void call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu)); +#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_TREE_PREEMPT_RCU) +static void rcu_report_exp_rnp(struct rcu_state *rsp, struct rcu_node *rnp, + bool wake); +#endif /* #if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_TREE_PREEMPT_RCU) */ +static void __init __rcu_init_preempt(void); +static void rcu_initiate_boost(struct rcu_node *rnp, unsigned long flags); +static void rcu_preempt_boost_start_gp(struct rcu_node *rnp); +static void invoke_rcu_callbacks_kthread(void); +static bool rcu_is_callbacks_kthread(void); +#ifdef CONFIG_RCU_BOOST +static void rcu_preempt_do_callbacks(void); +static int rcu_spawn_one_boost_kthread(struct rcu_state *rsp, + struct rcu_node *rnp); +#endif /* #ifdef CONFIG_RCU_BOOST */ +static void rcu_prepare_kthreads(int cpu); +static void rcu_cleanup_after_idle(int cpu); +static void rcu_prepare_for_idle(int cpu); +static void rcu_idle_count_callbacks_posted(void); +static void print_cpu_stall_info_begin(void); +static void print_cpu_stall_info(struct rcu_state *rsp, int cpu); +static void print_cpu_stall_info_end(void); +static void zero_cpu_stall_ticks(struct rcu_data *rdp); +static void increment_cpu_stall_ticks(void); +static int rcu_nocb_needs_gp(struct rcu_state *rsp); +static void rcu_nocb_gp_set(struct rcu_node *rnp, int nrq); +static void rcu_nocb_gp_cleanup(struct rcu_state *rsp, struct rcu_node *rnp); +static void rcu_init_one_nocb(struct rcu_node *rnp); +static bool __call_rcu_nocb(struct rcu_data *rdp, struct rcu_head *rhp, + bool lazy); +static bool rcu_nocb_adopt_orphan_cbs(struct rcu_state *rsp, + struct rcu_data *rdp); +static void rcu_boot_init_nocb_percpu_data(struct rcu_data *rdp); +static void rcu_spawn_nocb_kthreads(struct rcu_state *rsp); +static void rcu_kick_nohz_cpu(int cpu); +static bool init_nocb_callback_list(struct rcu_data *rdp); +static void rcu_sysidle_enter(struct rcu_dynticks *rdtp, int irq); +static void rcu_sysidle_exit(struct rcu_dynticks *rdtp, int irq); +static void rcu_sysidle_check_cpu(struct rcu_data *rdp, bool *isidle, + unsigned long *maxj); +static bool is_sysidle_rcu_state(struct rcu_state *rsp); +static void rcu_sysidle_report_gp(struct rcu_state *rsp, int isidle, + unsigned long maxj); +static void rcu_bind_gp_kthread(void); +static void rcu_sysidle_init_percpu_data(struct rcu_dynticks *rdtp); + +#endif /* #ifndef RCU_TREE_NONCORE */ + +#ifdef CONFIG_RCU_TRACE +#ifdef CONFIG_RCU_NOCB_CPU +/* Sum up queue lengths for tracing. */ +static inline void rcu_nocb_q_lengths(struct rcu_data *rdp, long *ql, long *qll) +{ + *ql = atomic_long_read(&rdp->nocb_q_count) + rdp->nocb_p_count; + *qll = atomic_long_read(&rdp->nocb_q_count_lazy) + rdp->nocb_p_count_lazy; +} +#else /* #ifdef CONFIG_RCU_NOCB_CPU */ +static inline void rcu_nocb_q_lengths(struct rcu_data *rdp, long *ql, long *qll) +{ + *ql = 0; + *qll = 0; +} +#endif /* #else #ifdef CONFIG_RCU_NOCB_CPU */ +#endif /* #ifdef CONFIG_RCU_TRACE */ diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h new file mode 100644 index 0000000..3822ac0 --- /dev/null +++ b/kernel/rcu/tree_plugin.h @@ -0,0 +1,2831 @@ +/* + * Read-Copy Update mechanism for mutual exclusion (tree-based version) + * Internal non-public definitions that provide either classic + * or preemptible semantics. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright Red Hat, 2009 + * Copyright IBM Corporation, 2009 + * + * Author: Ingo Molnar + * Paul E. McKenney + */ + +#include +#include +#include +#include +#include "../time/tick-internal.h" + +#define RCU_KTHREAD_PRIO 1 + +#ifdef CONFIG_RCU_BOOST +#define RCU_BOOST_PRIO CONFIG_RCU_BOOST_PRIO +#else +#define RCU_BOOST_PRIO RCU_KTHREAD_PRIO +#endif + +#ifdef CONFIG_RCU_NOCB_CPU +static cpumask_var_t rcu_nocb_mask; /* CPUs to have callbacks offloaded. */ +static bool have_rcu_nocb_mask; /* Was rcu_nocb_mask allocated? */ +static bool __read_mostly rcu_nocb_poll; /* Offload kthread are to poll. */ +static char __initdata nocb_buf[NR_CPUS * 5]; +#endif /* #ifdef CONFIG_RCU_NOCB_CPU */ + +/* + * Check the RCU kernel configuration parameters and print informative + * messages about anything out of the ordinary. If you like #ifdef, you + * will love this function. + */ +static void __init rcu_bootup_announce_oddness(void) +{ +#ifdef CONFIG_RCU_TRACE + pr_info("\tRCU debugfs-based tracing is enabled.\n"); +#endif +#if (defined(CONFIG_64BIT) && CONFIG_RCU_FANOUT != 64) || (!defined(CONFIG_64BIT) && CONFIG_RCU_FANOUT != 32) + pr_info("\tCONFIG_RCU_FANOUT set to non-default value of %d\n", + CONFIG_RCU_FANOUT); +#endif +#ifdef CONFIG_RCU_FANOUT_EXACT + pr_info("\tHierarchical RCU autobalancing is disabled.\n"); +#endif +#ifdef CONFIG_RCU_FAST_NO_HZ + pr_info("\tRCU dyntick-idle grace-period acceleration is enabled.\n"); +#endif +#ifdef CONFIG_PROVE_RCU + pr_info("\tRCU lockdep checking is enabled.\n"); +#endif +#ifdef CONFIG_RCU_TORTURE_TEST_RUNNABLE + pr_info("\tRCU torture testing starts during boot.\n"); +#endif +#if defined(CONFIG_TREE_PREEMPT_RCU) && !defined(CONFIG_RCU_CPU_STALL_VERBOSE) + pr_info("\tDump stacks of tasks blocking RCU-preempt GP.\n"); +#endif +#if defined(CONFIG_RCU_CPU_STALL_INFO) + pr_info("\tAdditional per-CPU info printed with stalls.\n"); +#endif +#if NUM_RCU_LVL_4 != 0 + pr_info("\tFour-level hierarchy is enabled.\n"); +#endif + if (rcu_fanout_leaf != CONFIG_RCU_FANOUT_LEAF) + pr_info("\tBoot-time adjustment of leaf fanout to %d.\n", rcu_fanout_leaf); + if (nr_cpu_ids != NR_CPUS) + pr_info("\tRCU restricting CPUs from NR_CPUS=%d to nr_cpu_ids=%d.\n", NR_CPUS, nr_cpu_ids); +#ifdef CONFIG_RCU_NOCB_CPU +#ifndef CONFIG_RCU_NOCB_CPU_NONE + if (!have_rcu_nocb_mask) { + zalloc_cpumask_var(&rcu_nocb_mask, GFP_KERNEL); + have_rcu_nocb_mask = true; + } +#ifdef CONFIG_RCU_NOCB_CPU_ZERO + pr_info("\tOffload RCU callbacks from CPU 0\n"); + cpumask_set_cpu(0, rcu_nocb_mask); +#endif /* #ifdef CONFIG_RCU_NOCB_CPU_ZERO */ +#ifdef CONFIG_RCU_NOCB_CPU_ALL + pr_info("\tOffload RCU callbacks from all CPUs\n"); + cpumask_copy(rcu_nocb_mask, cpu_possible_mask); +#endif /* #ifdef CONFIG_RCU_NOCB_CPU_ALL */ +#endif /* #ifndef CONFIG_RCU_NOCB_CPU_NONE */ + if (have_rcu_nocb_mask) { + if (!cpumask_subset(rcu_nocb_mask, cpu_possible_mask)) { + pr_info("\tNote: kernel parameter 'rcu_nocbs=' contains nonexistent CPUs.\n"); + cpumask_and(rcu_nocb_mask, cpu_possible_mask, + rcu_nocb_mask); + } + cpulist_scnprintf(nocb_buf, sizeof(nocb_buf), rcu_nocb_mask); + pr_info("\tOffload RCU callbacks from CPUs: %s.\n", nocb_buf); + if (rcu_nocb_poll) + pr_info("\tPoll for callbacks from no-CBs CPUs.\n"); + } +#endif /* #ifdef CONFIG_RCU_NOCB_CPU */ +} + +#ifdef CONFIG_TREE_PREEMPT_RCU + +RCU_STATE_INITIALIZER(rcu_preempt, 'p', call_rcu); +static struct rcu_state *rcu_state = &rcu_preempt_state; + +static int rcu_preempted_readers_exp(struct rcu_node *rnp); + +/* + * Tell them what RCU they are running. + */ +static void __init rcu_bootup_announce(void) +{ + pr_info("Preemptible hierarchical RCU implementation.\n"); + rcu_bootup_announce_oddness(); +} + +/* + * Return the number of RCU-preempt batches processed thus far + * for debug and statistics. + */ +long rcu_batches_completed_preempt(void) +{ + return rcu_preempt_state.completed; +} +EXPORT_SYMBOL_GPL(rcu_batches_completed_preempt); + +/* + * Return the number of RCU batches processed thus far for debug & stats. + */ +long rcu_batches_completed(void) +{ + return rcu_batches_completed_preempt(); +} +EXPORT_SYMBOL_GPL(rcu_batches_completed); + +/* + * Force a quiescent state for preemptible RCU. + */ +void rcu_force_quiescent_state(void) +{ + force_quiescent_state(&rcu_preempt_state); +} +EXPORT_SYMBOL_GPL(rcu_force_quiescent_state); + +/* + * Record a preemptible-RCU quiescent state for the specified CPU. Note + * that this just means that the task currently running on the CPU is + * not in a quiescent state. There might be any number of tasks blocked + * while in an RCU read-side critical section. + * + * Unlike the other rcu_*_qs() functions, callers to this function + * must disable irqs in order to protect the assignment to + * ->rcu_read_unlock_special. + */ +static void rcu_preempt_qs(int cpu) +{ + struct rcu_data *rdp = &per_cpu(rcu_preempt_data, cpu); + + if (rdp->passed_quiesce == 0) + trace_rcu_grace_period(TPS("rcu_preempt"), rdp->gpnum, TPS("cpuqs")); + rdp->passed_quiesce = 1; + current->rcu_read_unlock_special &= ~RCU_READ_UNLOCK_NEED_QS; +} + +/* + * We have entered the scheduler, and the current task might soon be + * context-switched away from. If this task is in an RCU read-side + * critical section, we will no longer be able to rely on the CPU to + * record that fact, so we enqueue the task on the blkd_tasks list. + * The task will dequeue itself when it exits the outermost enclosing + * RCU read-side critical section. Therefore, the current grace period + * cannot be permitted to complete until the blkd_tasks list entries + * predating the current grace period drain, in other words, until + * rnp->gp_tasks becomes NULL. + * + * Caller must disable preemption. + */ +static void rcu_preempt_note_context_switch(int cpu) +{ + struct task_struct *t = current; + unsigned long flags; + struct rcu_data *rdp; + struct rcu_node *rnp; + + if (t->rcu_read_lock_nesting > 0 && + (t->rcu_read_unlock_special & RCU_READ_UNLOCK_BLOCKED) == 0) { + + /* Possibly blocking in an RCU read-side critical section. */ + rdp = per_cpu_ptr(rcu_preempt_state.rda, cpu); + rnp = rdp->mynode; + raw_spin_lock_irqsave(&rnp->lock, flags); + t->rcu_read_unlock_special |= RCU_READ_UNLOCK_BLOCKED; + t->rcu_blocked_node = rnp; + + /* + * If this CPU has already checked in, then this task + * will hold up the next grace period rather than the + * current grace period. Queue the task accordingly. + * If the task is queued for the current grace period + * (i.e., this CPU has not yet passed through a quiescent + * state for the current grace period), then as long + * as that task remains queued, the current grace period + * cannot end. Note that there is some uncertainty as + * to exactly when the current grace period started. + * We take a conservative approach, which can result + * in unnecessarily waiting on tasks that started very + * slightly after the current grace period began. C'est + * la vie!!! + * + * But first, note that the current CPU must still be + * on line! + */ + WARN_ON_ONCE((rdp->grpmask & rnp->qsmaskinit) == 0); + WARN_ON_ONCE(!list_empty(&t->rcu_node_entry)); + if ((rnp->qsmask & rdp->grpmask) && rnp->gp_tasks != NULL) { + list_add(&t->rcu_node_entry, rnp->gp_tasks->prev); + rnp->gp_tasks = &t->rcu_node_entry; +#ifdef CONFIG_RCU_BOOST + if (rnp->boost_tasks != NULL) + rnp->boost_tasks = rnp->gp_tasks; +#endif /* #ifdef CONFIG_RCU_BOOST */ + } else { + list_add(&t->rcu_node_entry, &rnp->blkd_tasks); + if (rnp->qsmask & rdp->grpmask) + rnp->gp_tasks = &t->rcu_node_entry; + } + trace_rcu_preempt_task(rdp->rsp->name, + t->pid, + (rnp->qsmask & rdp->grpmask) + ? rnp->gpnum + : rnp->gpnum + 1); + raw_spin_unlock_irqrestore(&rnp->lock, flags); + } else if (t->rcu_read_lock_nesting < 0 && + t->rcu_read_unlock_special) { + + /* + * Complete exit from RCU read-side critical section on + * behalf of preempted instance of __rcu_read_unlock(). + */ + rcu_read_unlock_special(t); + } + + /* + * Either we were not in an RCU read-side critical section to + * begin with, or we have now recorded that critical section + * globally. Either way, we can now note a quiescent state + * for this CPU. Again, if we were in an RCU read-side critical + * section, and if that critical section was blocking the current + * grace period, then the fact that the task has been enqueued + * means that we continue to block the current grace period. + */ + local_irq_save(flags); + rcu_preempt_qs(cpu); + local_irq_restore(flags); +} + +/* + * Check for preempted RCU readers blocking the current grace period + * for the specified rcu_node structure. If the caller needs a reliable + * answer, it must hold the rcu_node's ->lock. + */ +static int rcu_preempt_blocked_readers_cgp(struct rcu_node *rnp) +{ + return rnp->gp_tasks != NULL; +} + +/* + * Record a quiescent state for all tasks that were previously queued + * on the specified rcu_node structure and that were blocking the current + * RCU grace period. The caller must hold the specified rnp->lock with + * irqs disabled, and this lock is released upon return, but irqs remain + * disabled. + */ +static void rcu_report_unblock_qs_rnp(struct rcu_node *rnp, unsigned long flags) + __releases(rnp->lock) +{ + unsigned long mask; + struct rcu_node *rnp_p; + + if (rnp->qsmask != 0 || rcu_preempt_blocked_readers_cgp(rnp)) { + raw_spin_unlock_irqrestore(&rnp->lock, flags); + return; /* Still need more quiescent states! */ + } + + rnp_p = rnp->parent; + if (rnp_p == NULL) { + /* + * Either there is only one rcu_node in the tree, + * or tasks were kicked up to root rcu_node due to + * CPUs going offline. + */ + rcu_report_qs_rsp(&rcu_preempt_state, flags); + return; + } + + /* Report up the rest of the hierarchy. */ + mask = rnp->grpmask; + raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */ + raw_spin_lock(&rnp_p->lock); /* irqs already disabled. */ + rcu_report_qs_rnp(mask, &rcu_preempt_state, rnp_p, flags); +} + +/* + * Advance a ->blkd_tasks-list pointer to the next entry, instead + * returning NULL if at the end of the list. + */ +static struct list_head *rcu_next_node_entry(struct task_struct *t, + struct rcu_node *rnp) +{ + struct list_head *np; + + np = t->rcu_node_entry.next; + if (np == &rnp->blkd_tasks) + np = NULL; + return np; +} + +/* + * Handle special cases during rcu_read_unlock(), such as needing to + * notify RCU core processing or task having blocked during the RCU + * read-side critical section. + */ +void rcu_read_unlock_special(struct task_struct *t) +{ + int empty; + int empty_exp; + int empty_exp_now; + unsigned long flags; + struct list_head *np; +#ifdef CONFIG_RCU_BOOST + struct rt_mutex *rbmp = NULL; +#endif /* #ifdef CONFIG_RCU_BOOST */ + struct rcu_node *rnp; + int special; + + /* NMI handlers cannot block and cannot safely manipulate state. */ + if (in_nmi()) + return; + + local_irq_save(flags); + + /* + * If RCU core is waiting for this CPU to exit critical section, + * let it know that we have done so. + */ + special = t->rcu_read_unlock_special; + if (special & RCU_READ_UNLOCK_NEED_QS) { + rcu_preempt_qs(smp_processor_id()); + } + + /* Hardware IRQ handlers cannot block. */ + if (in_irq() || in_serving_softirq()) { + local_irq_restore(flags); + return; + } + + /* Clean up if blocked during RCU read-side critical section. */ + if (special & RCU_READ_UNLOCK_BLOCKED) { + t->rcu_read_unlock_special &= ~RCU_READ_UNLOCK_BLOCKED; + + /* + * Remove this task from the list it blocked on. The + * task can migrate while we acquire the lock, but at + * most one time. So at most two passes through loop. + */ + for (;;) { + rnp = t->rcu_blocked_node; + raw_spin_lock(&rnp->lock); /* irqs already disabled. */ + if (rnp == t->rcu_blocked_node) + break; + raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */ + } + empty = !rcu_preempt_blocked_readers_cgp(rnp); + empty_exp = !rcu_preempted_readers_exp(rnp); + smp_mb(); /* ensure expedited fastpath sees end of RCU c-s. */ + np = rcu_next_node_entry(t, rnp); + list_del_init(&t->rcu_node_entry); + t->rcu_blocked_node = NULL; + trace_rcu_unlock_preempted_task(TPS("rcu_preempt"), + rnp->gpnum, t->pid); + if (&t->rcu_node_entry == rnp->gp_tasks) + rnp->gp_tasks = np; + if (&t->rcu_node_entry == rnp->exp_tasks) + rnp->exp_tasks = np; +#ifdef CONFIG_RCU_BOOST + if (&t->rcu_node_entry == rnp->boost_tasks) + rnp->boost_tasks = np; + /* Snapshot/clear ->rcu_boost_mutex with rcu_node lock held. */ + if (t->rcu_boost_mutex) { + rbmp = t->rcu_boost_mutex; + t->rcu_boost_mutex = NULL; + } +#endif /* #ifdef CONFIG_RCU_BOOST */ + + /* + * If this was the last task on the current list, and if + * we aren't waiting on any CPUs, report the quiescent state. + * Note that rcu_report_unblock_qs_rnp() releases rnp->lock, + * so we must take a snapshot of the expedited state. + */ + empty_exp_now = !rcu_preempted_readers_exp(rnp); + if (!empty && !rcu_preempt_blocked_readers_cgp(rnp)) { + trace_rcu_quiescent_state_report(TPS("preempt_rcu"), + rnp->gpnum, + 0, rnp->qsmask, + rnp->level, + rnp->grplo, + rnp->grphi, + !!rnp->gp_tasks); + rcu_report_unblock_qs_rnp(rnp, flags); + } else { + raw_spin_unlock_irqrestore(&rnp->lock, flags); + } + +#ifdef CONFIG_RCU_BOOST + /* Unboost if we were boosted. */ + if (rbmp) + rt_mutex_unlock(rbmp); +#endif /* #ifdef CONFIG_RCU_BOOST */ + + /* + * If this was the last task on the expedited lists, + * then we need to report up the rcu_node hierarchy. + */ + if (!empty_exp && empty_exp_now) + rcu_report_exp_rnp(&rcu_preempt_state, rnp, true); + } else { + local_irq_restore(flags); + } +} + +#ifdef CONFIG_RCU_CPU_STALL_VERBOSE + +/* + * Dump detailed information for all tasks blocking the current RCU + * grace period on the specified rcu_node structure. + */ +static void rcu_print_detail_task_stall_rnp(struct rcu_node *rnp) +{ + unsigned long flags; + struct task_struct *t; + + raw_spin_lock_irqsave(&rnp->lock, flags); + if (!rcu_preempt_blocked_readers_cgp(rnp)) { + raw_spin_unlock_irqrestore(&rnp->lock, flags); + return; + } + t = list_entry(rnp->gp_tasks, + struct task_struct, rcu_node_entry); + list_for_each_entry_continue(t, &rnp->blkd_tasks, rcu_node_entry) + sched_show_task(t); + raw_spin_unlock_irqrestore(&rnp->lock, flags); +} + +/* + * Dump detailed information for all tasks blocking the current RCU + * grace period. + */ +static void rcu_print_detail_task_stall(struct rcu_state *rsp) +{ + struct rcu_node *rnp = rcu_get_root(rsp); + + rcu_print_detail_task_stall_rnp(rnp); + rcu_for_each_leaf_node(rsp, rnp) + rcu_print_detail_task_stall_rnp(rnp); +} + +#else /* #ifdef CONFIG_RCU_CPU_STALL_VERBOSE */ + +static void rcu_print_detail_task_stall(struct rcu_state *rsp) +{ +} + +#endif /* #else #ifdef CONFIG_RCU_CPU_STALL_VERBOSE */ + +#ifdef CONFIG_RCU_CPU_STALL_INFO + +static void rcu_print_task_stall_begin(struct rcu_node *rnp) +{ + pr_err("\tTasks blocked on level-%d rcu_node (CPUs %d-%d):", + rnp->level, rnp->grplo, rnp->grphi); +} + +static void rcu_print_task_stall_end(void) +{ + pr_cont("\n"); +} + +#else /* #ifdef CONFIG_RCU_CPU_STALL_INFO */ + +static void rcu_print_task_stall_begin(struct rcu_node *rnp) +{ +} + +static void rcu_print_task_stall_end(void) +{ +} + +#endif /* #else #ifdef CONFIG_RCU_CPU_STALL_INFO */ + +/* + * Scan the current list of tasks blocked within RCU read-side critical + * sections, printing out the tid of each. + */ +static int rcu_print_task_stall(struct rcu_node *rnp) +{ + struct task_struct *t; + int ndetected = 0; + + if (!rcu_preempt_blocked_readers_cgp(rnp)) + return 0; + rcu_print_task_stall_begin(rnp); + t = list_entry(rnp->gp_tasks, + struct task_struct, rcu_node_entry); + list_for_each_entry_continue(t, &rnp->blkd_tasks, rcu_node_entry) { + pr_cont(" P%d", t->pid); + ndetected++; + } + rcu_print_task_stall_end(); + return ndetected; +} + +/* + * Check that the list of blocked tasks for the newly completed grace + * period is in fact empty. It is a serious bug to complete a grace + * period that still has RCU readers blocked! This function must be + * invoked -before- updating this rnp's ->gpnum, and the rnp's ->lock + * must be held by the caller. + * + * Also, if there are blocked tasks on the list, they automatically + * block the newly created grace period, so set up ->gp_tasks accordingly. + */ +static void rcu_preempt_check_blocked_tasks(struct rcu_node *rnp) +{ + WARN_ON_ONCE(rcu_preempt_blocked_readers_cgp(rnp)); + if (!list_empty(&rnp->blkd_tasks)) + rnp->gp_tasks = rnp->blkd_tasks.next; + WARN_ON_ONCE(rnp->qsmask); +} + +#ifdef CONFIG_HOTPLUG_CPU + +/* + * Handle tasklist migration for case in which all CPUs covered by the + * specified rcu_node have gone offline. Move them up to the root + * rcu_node. The reason for not just moving them to the immediate + * parent is to remove the need for rcu_read_unlock_special() to + * make more than two attempts to acquire the target rcu_node's lock. + * Returns true if there were tasks blocking the current RCU grace + * period. + * + * Returns 1 if there was previously a task blocking the current grace + * period on the specified rcu_node structure. + * + * The caller must hold rnp->lock with irqs disabled. + */ +static int rcu_preempt_offline_tasks(struct rcu_state *rsp, + struct rcu_node *rnp, + struct rcu_data *rdp) +{ + struct list_head *lp; + struct list_head *lp_root; + int retval = 0; + struct rcu_node *rnp_root = rcu_get_root(rsp); + struct task_struct *t; + + if (rnp == rnp_root) { + WARN_ONCE(1, "Last CPU thought to be offlined?"); + return 0; /* Shouldn't happen: at least one CPU online. */ + } + + /* If we are on an internal node, complain bitterly. */ + WARN_ON_ONCE(rnp != rdp->mynode); + + /* + * Move tasks up to root rcu_node. Don't try to get fancy for + * this corner-case operation -- just put this node's tasks + * at the head of the root node's list, and update the root node's + * ->gp_tasks and ->exp_tasks pointers to those of this node's, + * if non-NULL. This might result in waiting for more tasks than + * absolutely necessary, but this is a good performance/complexity + * tradeoff. + */ + if (rcu_preempt_blocked_readers_cgp(rnp) && rnp->qsmask == 0) + retval |= RCU_OFL_TASKS_NORM_GP; + if (rcu_preempted_readers_exp(rnp)) + retval |= RCU_OFL_TASKS_EXP_GP; + lp = &rnp->blkd_tasks; + lp_root = &rnp_root->blkd_tasks; + while (!list_empty(lp)) { + t = list_entry(lp->next, typeof(*t), rcu_node_entry); + raw_spin_lock(&rnp_root->lock); /* irqs already disabled */ + list_del(&t->rcu_node_entry); + t->rcu_blocked_node = rnp_root; + list_add(&t->rcu_node_entry, lp_root); + if (&t->rcu_node_entry == rnp->gp_tasks) + rnp_root->gp_tasks = rnp->gp_tasks; + if (&t->rcu_node_entry == rnp->exp_tasks) + rnp_root->exp_tasks = rnp->exp_tasks; +#ifdef CONFIG_RCU_BOOST + if (&t->rcu_node_entry == rnp->boost_tasks) + rnp_root->boost_tasks = rnp->boost_tasks; +#endif /* #ifdef CONFIG_RCU_BOOST */ + raw_spin_unlock(&rnp_root->lock); /* irqs still disabled */ + } + + rnp->gp_tasks = NULL; + rnp->exp_tasks = NULL; +#ifdef CONFIG_RCU_BOOST + rnp->boost_tasks = NULL; + /* + * In case root is being boosted and leaf was not. Make sure + * that we boost the tasks blocking the current grace period + * in this case. + */ + raw_spin_lock(&rnp_root->lock); /* irqs already disabled */ + if (rnp_root->boost_tasks != NULL && + rnp_root->boost_tasks != rnp_root->gp_tasks && + rnp_root->boost_tasks != rnp_root->exp_tasks) + rnp_root->boost_tasks = rnp_root->gp_tasks; + raw_spin_unlock(&rnp_root->lock); /* irqs still disabled */ +#endif /* #ifdef CONFIG_RCU_BOOST */ + + return retval; +} + +#endif /* #ifdef CONFIG_HOTPLUG_CPU */ + +/* + * Check for a quiescent state from the current CPU. When a task blocks, + * the task is recorded in the corresponding CPU's rcu_node structure, + * which is checked elsewhere. + * + * Caller must disable hard irqs. + */ +static void rcu_preempt_check_callbacks(int cpu) +{ + struct task_struct *t = current; + + if (t->rcu_read_lock_nesting == 0) { + rcu_preempt_qs(cpu); + return; + } + if (t->rcu_read_lock_nesting > 0 && + per_cpu(rcu_preempt_data, cpu).qs_pending) + t->rcu_read_unlock_special |= RCU_READ_UNLOCK_NEED_QS; +} + +#ifdef CONFIG_RCU_BOOST + +static void rcu_preempt_do_callbacks(void) +{ + rcu_do_batch(&rcu_preempt_state, this_cpu_ptr(&rcu_preempt_data)); +} + +#endif /* #ifdef CONFIG_RCU_BOOST */ + +/* + * Queue a preemptible-RCU callback for invocation after a grace period. + */ +void call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu)) +{ + __call_rcu(head, func, &rcu_preempt_state, -1, 0); +} +EXPORT_SYMBOL_GPL(call_rcu); + +/* + * Queue an RCU callback for lazy invocation after a grace period. + * This will likely be later named something like "call_rcu_lazy()", + * but this change will require some way of tagging the lazy RCU + * callbacks in the list of pending callbacks. Until then, this + * function may only be called from __kfree_rcu(). + */ +void kfree_call_rcu(struct rcu_head *head, + void (*func)(struct rcu_head *rcu)) +{ + __call_rcu(head, func, &rcu_preempt_state, -1, 1); +} +EXPORT_SYMBOL_GPL(kfree_call_rcu); + +/** + * synchronize_rcu - wait until a grace period has elapsed. + * + * Control will return to the caller some time after a full grace + * period has elapsed, in other words after all currently executing RCU + * read-side critical sections have completed. Note, however, that + * upon return from synchronize_rcu(), the caller might well be executing + * concurrently with new RCU read-side critical sections that began while + * synchronize_rcu() was waiting. RCU read-side critical sections are + * delimited by rcu_read_lock() and rcu_read_unlock(), and may be nested. + * + * See the description of synchronize_sched() for more detailed information + * on memory ordering guarantees. + */ +void synchronize_rcu(void) +{ + rcu_lockdep_assert(!lock_is_held(&rcu_bh_lock_map) && + !lock_is_held(&rcu_lock_map) && + !lock_is_held(&rcu_sched_lock_map), + "Illegal synchronize_rcu() in RCU read-side critical section"); + if (!rcu_scheduler_active) + return; + if (rcu_expedited) + synchronize_rcu_expedited(); + else + wait_rcu_gp(call_rcu); +} +EXPORT_SYMBOL_GPL(synchronize_rcu); + +static DECLARE_WAIT_QUEUE_HEAD(sync_rcu_preempt_exp_wq); +static unsigned long sync_rcu_preempt_exp_count; +static DEFINE_MUTEX(sync_rcu_preempt_exp_mutex); + +/* + * Return non-zero if there are any tasks in RCU read-side critical + * sections blocking the current preemptible-RCU expedited grace period. + * If there is no preemptible-RCU expedited grace period currently in + * progress, returns zero unconditionally. + */ +static int rcu_preempted_readers_exp(struct rcu_node *rnp) +{ + return rnp->exp_tasks != NULL; +} + +/* + * return non-zero if there is no RCU expedited grace period in progress + * for the specified rcu_node structure, in other words, if all CPUs and + * tasks covered by the specified rcu_node structure have done their bit + * for the current expedited grace period. Works only for preemptible + * RCU -- other RCU implementation use other means. + * + * Caller must hold sync_rcu_preempt_exp_mutex. + */ +static int sync_rcu_preempt_exp_done(struct rcu_node *rnp) +{ + return !rcu_preempted_readers_exp(rnp) && + ACCESS_ONCE(rnp->expmask) == 0; +} + +/* + * Report the exit from RCU read-side critical section for the last task + * that queued itself during or before the current expedited preemptible-RCU + * grace period. This event is reported either to the rcu_node structure on + * which the task was queued or to one of that rcu_node structure's ancestors, + * recursively up the tree. (Calm down, calm down, we do the recursion + * iteratively!) + * + * Most callers will set the "wake" flag, but the task initiating the + * expedited grace period need not wake itself. + * + * Caller must hold sync_rcu_preempt_exp_mutex. + */ +static void rcu_report_exp_rnp(struct rcu_state *rsp, struct rcu_node *rnp, + bool wake) +{ + unsigned long flags; + unsigned long mask; + + raw_spin_lock_irqsave(&rnp->lock, flags); + for (;;) { + if (!sync_rcu_preempt_exp_done(rnp)) { + raw_spin_unlock_irqrestore(&rnp->lock, flags); + break; + } + if (rnp->parent == NULL) { + raw_spin_unlock_irqrestore(&rnp->lock, flags); + if (wake) + wake_up(&sync_rcu_preempt_exp_wq); + break; + } + mask = rnp->grpmask; + raw_spin_unlock(&rnp->lock); /* irqs remain disabled */ + rnp = rnp->parent; + raw_spin_lock(&rnp->lock); /* irqs already disabled */ + rnp->expmask &= ~mask; + } +} + +/* + * Snapshot the tasks blocking the newly started preemptible-RCU expedited + * grace period for the specified rcu_node structure. If there are no such + * tasks, report it up the rcu_node hierarchy. + * + * Caller must hold sync_rcu_preempt_exp_mutex and must exclude + * CPU hotplug operations. + */ +static void +sync_rcu_preempt_exp_init(struct rcu_state *rsp, struct rcu_node *rnp) +{ + unsigned long flags; + int must_wait = 0; + + raw_spin_lock_irqsave(&rnp->lock, flags); + if (list_empty(&rnp->blkd_tasks)) { + raw_spin_unlock_irqrestore(&rnp->lock, flags); + } else { + rnp->exp_tasks = rnp->blkd_tasks.next; + rcu_initiate_boost(rnp, flags); /* releases rnp->lock */ + must_wait = 1; + } + if (!must_wait) + rcu_report_exp_rnp(rsp, rnp, false); /* Don't wake self. */ +} + +/** + * synchronize_rcu_expedited - Brute-force RCU grace period + * + * Wait for an RCU-preempt grace period, but expedite it. The basic + * idea is to invoke synchronize_sched_expedited() to push all the tasks to + * the ->blkd_tasks lists and wait for this list to drain. This consumes + * significant time on all CPUs and is unfriendly to real-time workloads, + * so is thus not recommended for any sort of common-case code. + * In fact, if you are using synchronize_rcu_expedited() in a loop, + * please restructure your code to batch your updates, and then Use a + * single synchronize_rcu() instead. + * + * Note that it is illegal to call this function while holding any lock + * that is acquired by a CPU-hotplug notifier. And yes, it is also illegal + * to call this function from a CPU-hotplug notifier. Failing to observe + * these restriction will result in deadlock. + */ +void synchronize_rcu_expedited(void) +{ + unsigned long flags; + struct rcu_node *rnp; + struct rcu_state *rsp = &rcu_preempt_state; + unsigned long snap; + int trycount = 0; + + smp_mb(); /* Caller's modifications seen first by other CPUs. */ + snap = ACCESS_ONCE(sync_rcu_preempt_exp_count) + 1; + smp_mb(); /* Above access cannot bleed into critical section. */ + + /* + * Block CPU-hotplug operations. This means that any CPU-hotplug + * operation that finds an rcu_node structure with tasks in the + * process of being boosted will know that all tasks blocking + * this expedited grace period will already be in the process of + * being boosted. This simplifies the process of moving tasks + * from leaf to root rcu_node structures. + */ + get_online_cpus(); + + /* + * Acquire lock, falling back to synchronize_rcu() if too many + * lock-acquisition failures. Of course, if someone does the + * expedited grace period for us, just leave. + */ + while (!mutex_trylock(&sync_rcu_preempt_exp_mutex)) { + if (ULONG_CMP_LT(snap, + ACCESS_ONCE(sync_rcu_preempt_exp_count))) { + put_online_cpus(); + goto mb_ret; /* Others did our work for us. */ + } + if (trycount++ < 10) { + udelay(trycount * num_online_cpus()); + } else { + put_online_cpus(); + wait_rcu_gp(call_rcu); + return; + } + } + if (ULONG_CMP_LT(snap, ACCESS_ONCE(sync_rcu_preempt_exp_count))) { + put_online_cpus(); + goto unlock_mb_ret; /* Others did our work for us. */ + } + + /* force all RCU readers onto ->blkd_tasks lists. */ + synchronize_sched_expedited(); + + /* Initialize ->expmask for all non-leaf rcu_node structures. */ + rcu_for_each_nonleaf_node_breadth_first(rsp, rnp) { + raw_spin_lock_irqsave(&rnp->lock, flags); + rnp->expmask = rnp->qsmaskinit; + raw_spin_unlock_irqrestore(&rnp->lock, flags); + } + + /* Snapshot current state of ->blkd_tasks lists. */ + rcu_for_each_leaf_node(rsp, rnp) + sync_rcu_preempt_exp_init(rsp, rnp); + if (NUM_RCU_NODES > 1) + sync_rcu_preempt_exp_init(rsp, rcu_get_root(rsp)); + + put_online_cpus(); + + /* Wait for snapshotted ->blkd_tasks lists to drain. */ + rnp = rcu_get_root(rsp); + wait_event(sync_rcu_preempt_exp_wq, + sync_rcu_preempt_exp_done(rnp)); + + /* Clean up and exit. */ + smp_mb(); /* ensure expedited GP seen before counter increment. */ + ACCESS_ONCE(sync_rcu_preempt_exp_count)++; +unlock_mb_ret: + mutex_unlock(&sync_rcu_preempt_exp_mutex); +mb_ret: + smp_mb(); /* ensure subsequent action seen after grace period. */ +} +EXPORT_SYMBOL_GPL(synchronize_rcu_expedited); + +/** + * rcu_barrier - Wait until all in-flight call_rcu() callbacks complete. + * + * Note that this primitive does not necessarily wait for an RCU grace period + * to complete. For example, if there are no RCU callbacks queued anywhere + * in the system, then rcu_barrier() is within its rights to return + * immediately, without waiting for anything, much less an RCU grace period. + */ +void rcu_barrier(void) +{ + _rcu_barrier(&rcu_preempt_state); +} +EXPORT_SYMBOL_GPL(rcu_barrier); + +/* + * Initialize preemptible RCU's state structures. + */ +static void __init __rcu_init_preempt(void) +{ + rcu_init_one(&rcu_preempt_state, &rcu_preempt_data); +} + +/* + * Check for a task exiting while in a preemptible-RCU read-side + * critical section, clean up if so. No need to issue warnings, + * as debug_check_no_locks_held() already does this if lockdep + * is enabled. + */ +void exit_rcu(void) +{ + struct task_struct *t = current; + + if (likely(list_empty(¤t->rcu_node_entry))) + return; + t->rcu_read_lock_nesting = 1; + barrier(); + t->rcu_read_unlock_special = RCU_READ_UNLOCK_BLOCKED; + __rcu_read_unlock(); +} + +#else /* #ifdef CONFIG_TREE_PREEMPT_RCU */ + +static struct rcu_state *rcu_state = &rcu_sched_state; + +/* + * Tell them what RCU they are running. + */ +static void __init rcu_bootup_announce(void) +{ + pr_info("Hierarchical RCU implementation.\n"); + rcu_bootup_announce_oddness(); +} + +/* + * Return the number of RCU batches processed thus far for debug & stats. + */ +long rcu_batches_completed(void) +{ + return rcu_batches_completed_sched(); +} +EXPORT_SYMBOL_GPL(rcu_batches_completed); + +/* + * Force a quiescent state for RCU, which, because there is no preemptible + * RCU, becomes the same as rcu-sched. + */ +void rcu_force_quiescent_state(void) +{ + rcu_sched_force_quiescent_state(); +} +EXPORT_SYMBOL_GPL(rcu_force_quiescent_state); + +/* + * Because preemptible RCU does not exist, we never have to check for + * CPUs being in quiescent states. + */ +static void rcu_preempt_note_context_switch(int cpu) +{ +} + +/* + * Because preemptible RCU does not exist, there are never any preempted + * RCU readers. + */ +static int rcu_preempt_blocked_readers_cgp(struct rcu_node *rnp) +{ + return 0; +} + +#ifdef CONFIG_HOTPLUG_CPU + +/* Because preemptible RCU does not exist, no quieting of tasks. */ +static void rcu_report_unblock_qs_rnp(struct rcu_node *rnp, unsigned long flags) +{ + raw_spin_unlock_irqrestore(&rnp->lock, flags); +} + +#endif /* #ifdef CONFIG_HOTPLUG_CPU */ + +/* + * Because preemptible RCU does not exist, we never have to check for + * tasks blocked within RCU read-side critical sections. + */ +static void rcu_print_detail_task_stall(struct rcu_state *rsp) +{ +} + +/* + * Because preemptible RCU does not exist, we never have to check for + * tasks blocked within RCU read-side critical sections. + */ +static int rcu_print_task_stall(struct rcu_node *rnp) +{ + return 0; +} + +/* + * Because there is no preemptible RCU, there can be no readers blocked, + * so there is no need to check for blocked tasks. So check only for + * bogus qsmask values. + */ +static void rcu_preempt_check_blocked_tasks(struct rcu_node *rnp) +{ + WARN_ON_ONCE(rnp->qsmask); +} + +#ifdef CONFIG_HOTPLUG_CPU + +/* + * Because preemptible RCU does not exist, it never needs to migrate + * tasks that were blocked within RCU read-side critical sections, and + * such non-existent tasks cannot possibly have been blocking the current + * grace period. + */ +static int rcu_preempt_offline_tasks(struct rcu_state *rsp, + struct rcu_node *rnp, + struct rcu_data *rdp) +{ + return 0; +} + +#endif /* #ifdef CONFIG_HOTPLUG_CPU */ + +/* + * Because preemptible RCU does not exist, it never has any callbacks + * to check. + */ +static void rcu_preempt_check_callbacks(int cpu) +{ +} + +/* + * Queue an RCU callback for lazy invocation after a grace period. + * This will likely be later named something like "call_rcu_lazy()", + * but this change will require some way of tagging the lazy RCU + * callbacks in the list of pending callbacks. Until then, this + * function may only be called from __kfree_rcu(). + * + * Because there is no preemptible RCU, we use RCU-sched instead. + */ +void kfree_call_rcu(struct rcu_head *head, + void (*func)(struct rcu_head *rcu)) +{ + __call_rcu(head, func, &rcu_sched_state, -1, 1); +} +EXPORT_SYMBOL_GPL(kfree_call_rcu); + +/* + * Wait for an rcu-preempt grace period, but make it happen quickly. + * But because preemptible RCU does not exist, map to rcu-sched. + */ +void synchronize_rcu_expedited(void) +{ + synchronize_sched_expedited(); +} +EXPORT_SYMBOL_GPL(synchronize_rcu_expedited); + +#ifdef CONFIG_HOTPLUG_CPU + +/* + * Because preemptible RCU does not exist, there is never any need to + * report on tasks preempted in RCU read-side critical sections during + * expedited RCU grace periods. + */ +static void rcu_report_exp_rnp(struct rcu_state *rsp, struct rcu_node *rnp, + bool wake) +{ +} + +#endif /* #ifdef CONFIG_HOTPLUG_CPU */ + +/* + * Because preemptible RCU does not exist, rcu_barrier() is just + * another name for rcu_barrier_sched(). + */ +void rcu_barrier(void) +{ + rcu_barrier_sched(); +} +EXPORT_SYMBOL_GPL(rcu_barrier); + +/* + * Because preemptible RCU does not exist, it need not be initialized. + */ +static void __init __rcu_init_preempt(void) +{ +} + +/* + * Because preemptible RCU does not exist, tasks cannot possibly exit + * while in preemptible RCU read-side critical sections. + */ +void exit_rcu(void) +{ +} + +#endif /* #else #ifdef CONFIG_TREE_PREEMPT_RCU */ + +#ifdef CONFIG_RCU_BOOST + +#include "../rtmutex_common.h" + +#ifdef CONFIG_RCU_TRACE + +static void rcu_initiate_boost_trace(struct rcu_node *rnp) +{ + if (list_empty(&rnp->blkd_tasks)) + rnp->n_balk_blkd_tasks++; + else if (rnp->exp_tasks == NULL && rnp->gp_tasks == NULL) + rnp->n_balk_exp_gp_tasks++; + else if (rnp->gp_tasks != NULL && rnp->boost_tasks != NULL) + rnp->n_balk_boost_tasks++; + else if (rnp->gp_tasks != NULL && rnp->qsmask != 0) + rnp->n_balk_notblocked++; + else if (rnp->gp_tasks != NULL && + ULONG_CMP_LT(jiffies, rnp->boost_time)) + rnp->n_balk_notyet++; + else + rnp->n_balk_nos++; +} + +#else /* #ifdef CONFIG_RCU_TRACE */ + +static void rcu_initiate_boost_trace(struct rcu_node *rnp) +{ +} + +#endif /* #else #ifdef CONFIG_RCU_TRACE */ + +static void rcu_wake_cond(struct task_struct *t, int status) +{ + /* + * If the thread is yielding, only wake it when this + * is invoked from idle + */ + if (status != RCU_KTHREAD_YIELDING || is_idle_task(current)) + wake_up_process(t); +} + +/* + * Carry out RCU priority boosting on the task indicated by ->exp_tasks + * or ->boost_tasks, advancing the pointer to the next task in the + * ->blkd_tasks list. + * + * Note that irqs must be enabled: boosting the task can block. + * Returns 1 if there are more tasks needing to be boosted. + */ +static int rcu_boost(struct rcu_node *rnp) +{ + unsigned long flags; + struct rt_mutex mtx; + struct task_struct *t; + struct list_head *tb; + + if (rnp->exp_tasks == NULL && rnp->boost_tasks == NULL) + return 0; /* Nothing left to boost. */ + + raw_spin_lock_irqsave(&rnp->lock, flags); + + /* + * Recheck under the lock: all tasks in need of boosting + * might exit their RCU read-side critical sections on their own. + */ + if (rnp->exp_tasks == NULL && rnp->boost_tasks == NULL) { + raw_spin_unlock_irqrestore(&rnp->lock, flags); + return 0; + } + + /* + * Preferentially boost tasks blocking expedited grace periods. + * This cannot starve the normal grace periods because a second + * expedited grace period must boost all blocked tasks, including + * those blocking the pre-existing normal grace period. + */ + if (rnp->exp_tasks != NULL) { + tb = rnp->exp_tasks; + rnp->n_exp_boosts++; + } else { + tb = rnp->boost_tasks; + rnp->n_normal_boosts++; + } + rnp->n_tasks_boosted++; + + /* + * We boost task t by manufacturing an rt_mutex that appears to + * be held by task t. We leave a pointer to that rt_mutex where + * task t can find it, and task t will release the mutex when it + * exits its outermost RCU read-side critical section. Then + * simply acquiring this artificial rt_mutex will boost task + * t's priority. (Thanks to tglx for suggesting this approach!) + * + * Note that task t must acquire rnp->lock to remove itself from + * the ->blkd_tasks list, which it will do from exit() if from + * nowhere else. We therefore are guaranteed that task t will + * stay around at least until we drop rnp->lock. Note that + * rnp->lock also resolves races between our priority boosting + * and task t's exiting its outermost RCU read-side critical + * section. + */ + t = container_of(tb, struct task_struct, rcu_node_entry); + rt_mutex_init_proxy_locked(&mtx, t); + t->rcu_boost_mutex = &mtx; + raw_spin_unlock_irqrestore(&rnp->lock, flags); + rt_mutex_lock(&mtx); /* Side effect: boosts task t's priority. */ + rt_mutex_unlock(&mtx); /* Keep lockdep happy. */ + + return ACCESS_ONCE(rnp->exp_tasks) != NULL || + ACCESS_ONCE(rnp->boost_tasks) != NULL; +} + +/* + * Priority-boosting kthread. One per leaf rcu_node and one for the + * root rcu_node. + */ +static int rcu_boost_kthread(void *arg) +{ + struct rcu_node *rnp = (struct rcu_node *)arg; + int spincnt = 0; + int more2boost; + + trace_rcu_utilization(TPS("Start boost kthread@init")); + for (;;) { + rnp->boost_kthread_status = RCU_KTHREAD_WAITING; + trace_rcu_utilization(TPS("End boost kthread@rcu_wait")); + rcu_wait(rnp->boost_tasks || rnp->exp_tasks); + trace_rcu_utilization(TPS("Start boost kthread@rcu_wait")); + rnp->boost_kthread_status = RCU_KTHREAD_RUNNING; + more2boost = rcu_boost(rnp); + if (more2boost) + spincnt++; + else + spincnt = 0; + if (spincnt > 10) { + rnp->boost_kthread_status = RCU_KTHREAD_YIELDING; + trace_rcu_utilization(TPS("End boost kthread@rcu_yield")); + schedule_timeout_interruptible(2); + trace_rcu_utilization(TPS("Start boost kthread@rcu_yield")); + spincnt = 0; + } + } + /* NOTREACHED */ + trace_rcu_utilization(TPS("End boost kthread@notreached")); + return 0; +} + +/* + * Check to see if it is time to start boosting RCU readers that are + * blocking the current grace period, and, if so, tell the per-rcu_node + * kthread to start boosting them. If there is an expedited grace + * period in progress, it is always time to boost. + * + * The caller must hold rnp->lock, which this function releases. + * The ->boost_kthread_task is immortal, so we don't need to worry + * about it going away. + */ +static void rcu_initiate_boost(struct rcu_node *rnp, unsigned long flags) +{ + struct task_struct *t; + + if (!rcu_preempt_blocked_readers_cgp(rnp) && rnp->exp_tasks == NULL) { + rnp->n_balk_exp_gp_tasks++; + raw_spin_unlock_irqrestore(&rnp->lock, flags); + return; + } + if (rnp->exp_tasks != NULL || + (rnp->gp_tasks != NULL && + rnp->boost_tasks == NULL && + rnp->qsmask == 0 && + ULONG_CMP_GE(jiffies, rnp->boost_time))) { + if (rnp->exp_tasks == NULL) + rnp->boost_tasks = rnp->gp_tasks; + raw_spin_unlock_irqrestore(&rnp->lock, flags); + t = rnp->boost_kthread_task; + if (t) + rcu_wake_cond(t, rnp->boost_kthread_status); + } else { + rcu_initiate_boost_trace(rnp); + raw_spin_unlock_irqrestore(&rnp->lock, flags); + } +} + +/* + * Wake up the per-CPU kthread to invoke RCU callbacks. + */ +static void invoke_rcu_callbacks_kthread(void) +{ + unsigned long flags; + + local_irq_save(flags); + __this_cpu_write(rcu_cpu_has_work, 1); + if (__this_cpu_read(rcu_cpu_kthread_task) != NULL && + current != __this_cpu_read(rcu_cpu_kthread_task)) { + rcu_wake_cond(__this_cpu_read(rcu_cpu_kthread_task), + __this_cpu_read(rcu_cpu_kthread_status)); + } + local_irq_restore(flags); +} + +/* + * Is the current CPU running the RCU-callbacks kthread? + * Caller must have preemption disabled. + */ +static bool rcu_is_callbacks_kthread(void) +{ + return __this_cpu_read(rcu_cpu_kthread_task) == current; +} + +#define RCU_BOOST_DELAY_JIFFIES DIV_ROUND_UP(CONFIG_RCU_BOOST_DELAY * HZ, 1000) + +/* + * Do priority-boost accounting for the start of a new grace period. + */ +static void rcu_preempt_boost_start_gp(struct rcu_node *rnp) +{ + rnp->boost_time = jiffies + RCU_BOOST_DELAY_JIFFIES; +} + +/* + * Create an RCU-boost kthread for the specified node if one does not + * already exist. We only create this kthread for preemptible RCU. + * Returns zero if all is well, a negated errno otherwise. + */ +static int rcu_spawn_one_boost_kthread(struct rcu_state *rsp, + struct rcu_node *rnp) +{ + int rnp_index = rnp - &rsp->node[0]; + unsigned long flags; + struct sched_param sp; + struct task_struct *t; + + if (&rcu_preempt_state != rsp) + return 0; + + if (!rcu_scheduler_fully_active || rnp->qsmaskinit == 0) + return 0; + + rsp->boost = 1; + if (rnp->boost_kthread_task != NULL) + return 0; + t = kthread_create(rcu_boost_kthread, (void *)rnp, + "rcub/%d", rnp_index); + if (IS_ERR(t)) + return PTR_ERR(t); + raw_spin_lock_irqsave(&rnp->lock, flags); + rnp->boost_kthread_task = t; + raw_spin_unlock_irqrestore(&rnp->lock, flags); + sp.sched_priority = RCU_BOOST_PRIO; + sched_setscheduler_nocheck(t, SCHED_FIFO, &sp); + wake_up_process(t); /* get to TASK_INTERRUPTIBLE quickly. */ + return 0; +} + +static void rcu_kthread_do_work(void) +{ + rcu_do_batch(&rcu_sched_state, this_cpu_ptr(&rcu_sched_data)); + rcu_do_batch(&rcu_bh_state, this_cpu_ptr(&rcu_bh_data)); + rcu_preempt_do_callbacks(); +} + +static void rcu_cpu_kthread_setup(unsigned int cpu) +{ + struct sched_param sp; + + sp.sched_priority = RCU_KTHREAD_PRIO; + sched_setscheduler_nocheck(current, SCHED_FIFO, &sp); +} + +static void rcu_cpu_kthread_park(unsigned int cpu) +{ + per_cpu(rcu_cpu_kthread_status, cpu) = RCU_KTHREAD_OFFCPU; +} + +static int rcu_cpu_kthread_should_run(unsigned int cpu) +{ + return __this_cpu_read(rcu_cpu_has_work); +} + +/* + * Per-CPU kernel thread that invokes RCU callbacks. This replaces the + * RCU softirq used in flavors and configurations of RCU that do not + * support RCU priority boosting. + */ +static void rcu_cpu_kthread(unsigned int cpu) +{ + unsigned int *statusp = this_cpu_ptr(&rcu_cpu_kthread_status); + char work, *workp = this_cpu_ptr(&rcu_cpu_has_work); + int spincnt; + + for (spincnt = 0; spincnt < 10; spincnt++) { + trace_rcu_utilization(TPS("Start CPU kthread@rcu_wait")); + local_bh_disable(); + *statusp = RCU_KTHREAD_RUNNING; + this_cpu_inc(rcu_cpu_kthread_loops); + local_irq_disable(); + work = *workp; + *workp = 0; + local_irq_enable(); + if (work) + rcu_kthread_do_work(); + local_bh_enable(); + if (*workp == 0) { + trace_rcu_utilization(TPS("End CPU kthread@rcu_wait")); + *statusp = RCU_KTHREAD_WAITING; + return; + } + } + *statusp = RCU_KTHREAD_YIELDING; + trace_rcu_utilization(TPS("Start CPU kthread@rcu_yield")); + schedule_timeout_interruptible(2); + trace_rcu_utilization(TPS("End CPU kthread@rcu_yield")); + *statusp = RCU_KTHREAD_WAITING; +} + +/* + * Set the per-rcu_node kthread's affinity to cover all CPUs that are + * served by the rcu_node in question. The CPU hotplug lock is still + * held, so the value of rnp->qsmaskinit will be stable. + * + * We don't include outgoingcpu in the affinity set, use -1 if there is + * no outgoing CPU. If there are no CPUs left in the affinity set, + * this function allows the kthread to execute on any CPU. + */ +static void rcu_boost_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu) +{ + struct task_struct *t = rnp->boost_kthread_task; + unsigned long mask = rnp->qsmaskinit; + cpumask_var_t cm; + int cpu; + + if (!t) + return; + if (!zalloc_cpumask_var(&cm, GFP_KERNEL)) + return; + for (cpu = rnp->grplo; cpu <= rnp->grphi; cpu++, mask >>= 1) + if ((mask & 0x1) && cpu != outgoingcpu) + cpumask_set_cpu(cpu, cm); + if (cpumask_weight(cm) == 0) { + cpumask_setall(cm); + for (cpu = rnp->grplo; cpu <= rnp->grphi; cpu++) + cpumask_clear_cpu(cpu, cm); + WARN_ON_ONCE(cpumask_weight(cm) == 0); + } + set_cpus_allowed_ptr(t, cm); + free_cpumask_var(cm); +} + +static struct smp_hotplug_thread rcu_cpu_thread_spec = { + .store = &rcu_cpu_kthread_task, + .thread_should_run = rcu_cpu_kthread_should_run, + .thread_fn = rcu_cpu_kthread, + .thread_comm = "rcuc/%u", + .setup = rcu_cpu_kthread_setup, + .park = rcu_cpu_kthread_park, +}; + +/* + * Spawn all kthreads -- called as soon as the scheduler is running. + */ +static int __init rcu_spawn_kthreads(void) +{ + struct rcu_node *rnp; + int cpu; + + rcu_scheduler_fully_active = 1; + for_each_possible_cpu(cpu) + per_cpu(rcu_cpu_has_work, cpu) = 0; + BUG_ON(smpboot_register_percpu_thread(&rcu_cpu_thread_spec)); + rnp = rcu_get_root(rcu_state); + (void)rcu_spawn_one_boost_kthread(rcu_state, rnp); + if (NUM_RCU_NODES > 1) { + rcu_for_each_leaf_node(rcu_state, rnp) + (void)rcu_spawn_one_boost_kthread(rcu_state, rnp); + } + return 0; +} +early_initcall(rcu_spawn_kthreads); + +static void rcu_prepare_kthreads(int cpu) +{ + struct rcu_data *rdp = per_cpu_ptr(rcu_state->rda, cpu); + struct rcu_node *rnp = rdp->mynode; + + /* Fire up the incoming CPU's kthread and leaf rcu_node kthread. */ + if (rcu_scheduler_fully_active) + (void)rcu_spawn_one_boost_kthread(rcu_state, rnp); +} + +#else /* #ifdef CONFIG_RCU_BOOST */ + +static void rcu_initiate_boost(struct rcu_node *rnp, unsigned long flags) +{ + raw_spin_unlock_irqrestore(&rnp->lock, flags); +} + +static void invoke_rcu_callbacks_kthread(void) +{ + WARN_ON_ONCE(1); +} + +static bool rcu_is_callbacks_kthread(void) +{ + return false; +} + +static void rcu_preempt_boost_start_gp(struct rcu_node *rnp) +{ +} + +static void rcu_boost_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu) +{ +} + +static int __init rcu_scheduler_really_started(void) +{ + rcu_scheduler_fully_active = 1; + return 0; +} +early_initcall(rcu_scheduler_really_started); + +static void rcu_prepare_kthreads(int cpu) +{ +} + +#endif /* #else #ifdef CONFIG_RCU_BOOST */ + +#if !defined(CONFIG_RCU_FAST_NO_HZ) + +/* + * Check to see if any future RCU-related work will need to be done + * by the current CPU, even if none need be done immediately, returning + * 1 if so. This function is part of the RCU implementation; it is -not- + * an exported member of the RCU API. + * + * Because we not have RCU_FAST_NO_HZ, just check whether this CPU needs + * any flavor of RCU. + */ +int rcu_needs_cpu(int cpu, unsigned long *delta_jiffies) +{ + *delta_jiffies = ULONG_MAX; + return rcu_cpu_has_callbacks(cpu, NULL); +} + +/* + * Because we do not have RCU_FAST_NO_HZ, don't bother cleaning up + * after it. + */ +static void rcu_cleanup_after_idle(int cpu) +{ +} + +/* + * Do the idle-entry grace-period work, which, because CONFIG_RCU_FAST_NO_HZ=n, + * is nothing. + */ +static void rcu_prepare_for_idle(int cpu) +{ +} + +/* + * Don't bother keeping a running count of the number of RCU callbacks + * posted because CONFIG_RCU_FAST_NO_HZ=n. + */ +static void rcu_idle_count_callbacks_posted(void) +{ +} + +#else /* #if !defined(CONFIG_RCU_FAST_NO_HZ) */ + +/* + * This code is invoked when a CPU goes idle, at which point we want + * to have the CPU do everything required for RCU so that it can enter + * the energy-efficient dyntick-idle mode. This is handled by a + * state machine implemented by rcu_prepare_for_idle() below. + * + * The following three proprocessor symbols control this state machine: + * + * RCU_IDLE_GP_DELAY gives the number of jiffies that a CPU is permitted + * to sleep in dyntick-idle mode with RCU callbacks pending. This + * is sized to be roughly one RCU grace period. Those energy-efficiency + * benchmarkers who might otherwise be tempted to set this to a large + * number, be warned: Setting RCU_IDLE_GP_DELAY too high can hang your + * system. And if you are -that- concerned about energy efficiency, + * just power the system down and be done with it! + * RCU_IDLE_LAZY_GP_DELAY gives the number of jiffies that a CPU is + * permitted to sleep in dyntick-idle mode with only lazy RCU + * callbacks pending. Setting this too high can OOM your system. + * + * The values below work well in practice. If future workloads require + * adjustment, they can be converted into kernel config parameters, though + * making the state machine smarter might be a better option. + */ +#define RCU_IDLE_GP_DELAY 4 /* Roughly one grace period. */ +#define RCU_IDLE_LAZY_GP_DELAY (6 * HZ) /* Roughly six seconds. */ + +static int rcu_idle_gp_delay = RCU_IDLE_GP_DELAY; +module_param(rcu_idle_gp_delay, int, 0644); +static int rcu_idle_lazy_gp_delay = RCU_IDLE_LAZY_GP_DELAY; +module_param(rcu_idle_lazy_gp_delay, int, 0644); + +extern int tick_nohz_enabled; + +/* + * Try to advance callbacks for all flavors of RCU on the current CPU, but + * only if it has been awhile since the last time we did so. Afterwards, + * if there are any callbacks ready for immediate invocation, return true. + */ +static bool rcu_try_advance_all_cbs(void) +{ + bool cbs_ready = false; + struct rcu_data *rdp; + struct rcu_dynticks *rdtp = this_cpu_ptr(&rcu_dynticks); + struct rcu_node *rnp; + struct rcu_state *rsp; + + /* Exit early if we advanced recently. */ + if (jiffies == rdtp->last_advance_all) + return 0; + rdtp->last_advance_all = jiffies; + + for_each_rcu_flavor(rsp) { + rdp = this_cpu_ptr(rsp->rda); + rnp = rdp->mynode; + + /* + * Don't bother checking unless a grace period has + * completed since we last checked and there are + * callbacks not yet ready to invoke. + */ + if (rdp->completed != rnp->completed && + rdp->nxttail[RCU_DONE_TAIL] != rdp->nxttail[RCU_NEXT_TAIL]) + note_gp_changes(rsp, rdp); + + if (cpu_has_callbacks_ready_to_invoke(rdp)) + cbs_ready = true; + } + return cbs_ready; +} + +/* + * Allow the CPU to enter dyntick-idle mode unless it has callbacks ready + * to invoke. If the CPU has callbacks, try to advance them. Tell the + * caller to set the timeout based on whether or not there are non-lazy + * callbacks. + * + * The caller must have disabled interrupts. + */ +int rcu_needs_cpu(int cpu, unsigned long *dj) +{ + struct rcu_dynticks *rdtp = &per_cpu(rcu_dynticks, cpu); + + /* Snapshot to detect later posting of non-lazy callback. */ + rdtp->nonlazy_posted_snap = rdtp->nonlazy_posted; + + /* If no callbacks, RCU doesn't need the CPU. */ + if (!rcu_cpu_has_callbacks(cpu, &rdtp->all_lazy)) { + *dj = ULONG_MAX; + return 0; + } + + /* Attempt to advance callbacks. */ + if (rcu_try_advance_all_cbs()) { + /* Some ready to invoke, so initiate later invocation. */ + invoke_rcu_core(); + return 1; + } + rdtp->last_accelerate = jiffies; + + /* Request timer delay depending on laziness, and round. */ + if (!rdtp->all_lazy) { + *dj = round_up(rcu_idle_gp_delay + jiffies, + rcu_idle_gp_delay) - jiffies; + } else { + *dj = round_jiffies(rcu_idle_lazy_gp_delay + jiffies) - jiffies; + } + return 0; +} + +/* + * Prepare a CPU for idle from an RCU perspective. The first major task + * is to sense whether nohz mode has been enabled or disabled via sysfs. + * The second major task is to check to see if a non-lazy callback has + * arrived at a CPU that previously had only lazy callbacks. The third + * major task is to accelerate (that is, assign grace-period numbers to) + * any recently arrived callbacks. + * + * The caller must have disabled interrupts. + */ +static void rcu_prepare_for_idle(int cpu) +{ + struct rcu_data *rdp; + struct rcu_dynticks *rdtp = &per_cpu(rcu_dynticks, cpu); + struct rcu_node *rnp; + struct rcu_state *rsp; + int tne; + + /* Handle nohz enablement switches conservatively. */ + tne = ACCESS_ONCE(tick_nohz_enabled); + if (tne != rdtp->tick_nohz_enabled_snap) { + if (rcu_cpu_has_callbacks(cpu, NULL)) + invoke_rcu_core(); /* force nohz to see update. */ + rdtp->tick_nohz_enabled_snap = tne; + return; + } + if (!tne) + return; + + /* If this is a no-CBs CPU, no callbacks, just return. */ + if (rcu_is_nocb_cpu(cpu)) + return; + + /* + * If a non-lazy callback arrived at a CPU having only lazy + * callbacks, invoke RCU core for the side-effect of recalculating + * idle duration on re-entry to idle. + */ + if (rdtp->all_lazy && + rdtp->nonlazy_posted != rdtp->nonlazy_posted_snap) { + rdtp->all_lazy = false; + rdtp->nonlazy_posted_snap = rdtp->nonlazy_posted; + invoke_rcu_core(); + return; + } + + /* + * If we have not yet accelerated this jiffy, accelerate all + * callbacks on this CPU. + */ + if (rdtp->last_accelerate == jiffies) + return; + rdtp->last_accelerate = jiffies; + for_each_rcu_flavor(rsp) { + rdp = per_cpu_ptr(rsp->rda, cpu); + if (!*rdp->nxttail[RCU_DONE_TAIL]) + continue; + rnp = rdp->mynode; + raw_spin_lock(&rnp->lock); /* irqs already disabled. */ + rcu_accelerate_cbs(rsp, rnp, rdp); + raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */ + } +} + +/* + * Clean up for exit from idle. Attempt to advance callbacks based on + * any grace periods that elapsed while the CPU was idle, and if any + * callbacks are now ready to invoke, initiate invocation. + */ +static void rcu_cleanup_after_idle(int cpu) +{ + + if (rcu_is_nocb_cpu(cpu)) + return; + if (rcu_try_advance_all_cbs()) + invoke_rcu_core(); +} + +/* + * Keep a running count of the number of non-lazy callbacks posted + * on this CPU. This running counter (which is never decremented) allows + * rcu_prepare_for_idle() to detect when something out of the idle loop + * posts a callback, even if an equal number of callbacks are invoked. + * Of course, callbacks should only be posted from within a trace event + * designed to be called from idle or from within RCU_NONIDLE(). + */ +static void rcu_idle_count_callbacks_posted(void) +{ + __this_cpu_add(rcu_dynticks.nonlazy_posted, 1); +} + +/* + * Data for flushing lazy RCU callbacks at OOM time. + */ +static atomic_t oom_callback_count; +static DECLARE_WAIT_QUEUE_HEAD(oom_callback_wq); + +/* + * RCU OOM callback -- decrement the outstanding count and deliver the + * wake-up if we are the last one. + */ +static void rcu_oom_callback(struct rcu_head *rhp) +{ + if (atomic_dec_and_test(&oom_callback_count)) + wake_up(&oom_callback_wq); +} + +/* + * Post an rcu_oom_notify callback on the current CPU if it has at + * least one lazy callback. This will unnecessarily post callbacks + * to CPUs that already have a non-lazy callback at the end of their + * callback list, but this is an infrequent operation, so accept some + * extra overhead to keep things simple. + */ +static void rcu_oom_notify_cpu(void *unused) +{ + struct rcu_state *rsp; + struct rcu_data *rdp; + + for_each_rcu_flavor(rsp) { + rdp = __this_cpu_ptr(rsp->rda); + if (rdp->qlen_lazy != 0) { + atomic_inc(&oom_callback_count); + rsp->call(&rdp->oom_head, rcu_oom_callback); + } + } +} + +/* + * If low on memory, ensure that each CPU has a non-lazy callback. + * This will wake up CPUs that have only lazy callbacks, in turn + * ensuring that they free up the corresponding memory in a timely manner. + * Because an uncertain amount of memory will be freed in some uncertain + * timeframe, we do not claim to have freed anything. + */ +static int rcu_oom_notify(struct notifier_block *self, + unsigned long notused, void *nfreed) +{ + int cpu; + + /* Wait for callbacks from earlier instance to complete. */ + wait_event(oom_callback_wq, atomic_read(&oom_callback_count) == 0); + + /* + * Prevent premature wakeup: ensure that all increments happen + * before there is a chance of the counter reaching zero. + */ + atomic_set(&oom_callback_count, 1); + + get_online_cpus(); + for_each_online_cpu(cpu) { + smp_call_function_single(cpu, rcu_oom_notify_cpu, NULL, 1); + cond_resched(); + } + put_online_cpus(); + + /* Unconditionally decrement: no need to wake ourselves up. */ + atomic_dec(&oom_callback_count); + + return NOTIFY_OK; +} + +static struct notifier_block rcu_oom_nb = { + .notifier_call = rcu_oom_notify +}; + +static int __init rcu_register_oom_notifier(void) +{ + register_oom_notifier(&rcu_oom_nb); + return 0; +} +early_initcall(rcu_register_oom_notifier); + +#endif /* #else #if !defined(CONFIG_RCU_FAST_NO_HZ) */ + +#ifdef CONFIG_RCU_CPU_STALL_INFO + +#ifdef CONFIG_RCU_FAST_NO_HZ + +static void print_cpu_stall_fast_no_hz(char *cp, int cpu) +{ + struct rcu_dynticks *rdtp = &per_cpu(rcu_dynticks, cpu); + unsigned long nlpd = rdtp->nonlazy_posted - rdtp->nonlazy_posted_snap; + + sprintf(cp, "last_accelerate: %04lx/%04lx, nonlazy_posted: %ld, %c%c", + rdtp->last_accelerate & 0xffff, jiffies & 0xffff, + ulong2long(nlpd), + rdtp->all_lazy ? 'L' : '.', + rdtp->tick_nohz_enabled_snap ? '.' : 'D'); +} + +#else /* #ifdef CONFIG_RCU_FAST_NO_HZ */ + +static void print_cpu_stall_fast_no_hz(char *cp, int cpu) +{ + *cp = '\0'; +} + +#endif /* #else #ifdef CONFIG_RCU_FAST_NO_HZ */ + +/* Initiate the stall-info list. */ +static void print_cpu_stall_info_begin(void) +{ + pr_cont("\n"); +} + +/* + * Print out diagnostic information for the specified stalled CPU. + * + * If the specified CPU is aware of the current RCU grace period + * (flavor specified by rsp), then print the number of scheduling + * clock interrupts the CPU has taken during the time that it has + * been aware. Otherwise, print the number of RCU grace periods + * that this CPU is ignorant of, for example, "1" if the CPU was + * aware of the previous grace period. + * + * Also print out idle and (if CONFIG_RCU_FAST_NO_HZ) idle-entry info. + */ +static void print_cpu_stall_info(struct rcu_state *rsp, int cpu) +{ + char fast_no_hz[72]; + struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu); + struct rcu_dynticks *rdtp = rdp->dynticks; + char *ticks_title; + unsigned long ticks_value; + + if (rsp->gpnum == rdp->gpnum) { + ticks_title = "ticks this GP"; + ticks_value = rdp->ticks_this_gp; + } else { + ticks_title = "GPs behind"; + ticks_value = rsp->gpnum - rdp->gpnum; + } + print_cpu_stall_fast_no_hz(fast_no_hz, cpu); + pr_err("\t%d: (%lu %s) idle=%03x/%llx/%d softirq=%u/%u %s\n", + cpu, ticks_value, ticks_title, + atomic_read(&rdtp->dynticks) & 0xfff, + rdtp->dynticks_nesting, rdtp->dynticks_nmi_nesting, + rdp->softirq_snap, kstat_softirqs_cpu(RCU_SOFTIRQ, cpu), + fast_no_hz); +} + +/* Terminate the stall-info list. */ +static void print_cpu_stall_info_end(void) +{ + pr_err("\t"); +} + +/* Zero ->ticks_this_gp for all flavors of RCU. */ +static void zero_cpu_stall_ticks(struct rcu_data *rdp) +{ + rdp->ticks_this_gp = 0; + rdp->softirq_snap = kstat_softirqs_cpu(RCU_SOFTIRQ, smp_processor_id()); +} + +/* Increment ->ticks_this_gp for all flavors of RCU. */ +static void increment_cpu_stall_ticks(void) +{ + struct rcu_state *rsp; + + for_each_rcu_flavor(rsp) + __this_cpu_ptr(rsp->rda)->ticks_this_gp++; +} + +#else /* #ifdef CONFIG_RCU_CPU_STALL_INFO */ + +static void print_cpu_stall_info_begin(void) +{ + pr_cont(" {"); +} + +static void print_cpu_stall_info(struct rcu_state *rsp, int cpu) +{ + pr_cont(" %d", cpu); +} + +static void print_cpu_stall_info_end(void) +{ + pr_cont("} "); +} + +static void zero_cpu_stall_ticks(struct rcu_data *rdp) +{ +} + +static void increment_cpu_stall_ticks(void) +{ +} + +#endif /* #else #ifdef CONFIG_RCU_CPU_STALL_INFO */ + +#ifdef CONFIG_RCU_NOCB_CPU + +/* + * Offload callback processing from the boot-time-specified set of CPUs + * specified by rcu_nocb_mask. For each CPU in the set, there is a + * kthread created that pulls the callbacks from the corresponding CPU, + * waits for a grace period to elapse, and invokes the callbacks. + * The no-CBs CPUs do a wake_up() on their kthread when they insert + * a callback into any empty list, unless the rcu_nocb_poll boot parameter + * has been specified, in which case each kthread actively polls its + * CPU. (Which isn't so great for energy efficiency, but which does + * reduce RCU's overhead on that CPU.) + * + * This is intended to be used in conjunction with Frederic Weisbecker's + * adaptive-idle work, which would seriously reduce OS jitter on CPUs + * running CPU-bound user-mode computations. + * + * Offloading of callback processing could also in theory be used as + * an energy-efficiency measure because CPUs with no RCU callbacks + * queued are more aggressive about entering dyntick-idle mode. + */ + + +/* Parse the boot-time rcu_nocb_mask CPU list from the kernel parameters. */ +static int __init rcu_nocb_setup(char *str) +{ + alloc_bootmem_cpumask_var(&rcu_nocb_mask); + have_rcu_nocb_mask = true; + cpulist_parse(str, rcu_nocb_mask); + return 1; +} +__setup("rcu_nocbs=", rcu_nocb_setup); + +static int __init parse_rcu_nocb_poll(char *arg) +{ + rcu_nocb_poll = 1; + return 0; +} +early_param("rcu_nocb_poll", parse_rcu_nocb_poll); + +/* + * Do any no-CBs CPUs need another grace period? + * + * Interrupts must be disabled. If the caller does not hold the root + * rnp_node structure's ->lock, the results are advisory only. + */ +static int rcu_nocb_needs_gp(struct rcu_state *rsp) +{ + struct rcu_node *rnp = rcu_get_root(rsp); + + return rnp->need_future_gp[(ACCESS_ONCE(rnp->completed) + 1) & 0x1]; +} + +/* + * Wake up any no-CBs CPUs' kthreads that were waiting on the just-ended + * grace period. + */ +static void rcu_nocb_gp_cleanup(struct rcu_state *rsp, struct rcu_node *rnp) +{ + wake_up_all(&rnp->nocb_gp_wq[rnp->completed & 0x1]); +} + +/* + * Set the root rcu_node structure's ->need_future_gp field + * based on the sum of those of all rcu_node structures. This does + * double-count the root rcu_node structure's requests, but this + * is necessary to handle the possibility of a rcu_nocb_kthread() + * having awakened during the time that the rcu_node structures + * were being updated for the end of the previous grace period. + */ +static void rcu_nocb_gp_set(struct rcu_node *rnp, int nrq) +{ + rnp->need_future_gp[(rnp->completed + 1) & 0x1] += nrq; +} + +static void rcu_init_one_nocb(struct rcu_node *rnp) +{ + init_waitqueue_head(&rnp->nocb_gp_wq[0]); + init_waitqueue_head(&rnp->nocb_gp_wq[1]); +} + +/* Is the specified CPU a no-CPUs CPU? */ +bool rcu_is_nocb_cpu(int cpu) +{ + if (have_rcu_nocb_mask) + return cpumask_test_cpu(cpu, rcu_nocb_mask); + return false; +} + +/* + * Enqueue the specified string of rcu_head structures onto the specified + * CPU's no-CBs lists. The CPU is specified by rdp, the head of the + * string by rhp, and the tail of the string by rhtp. The non-lazy/lazy + * counts are supplied by rhcount and rhcount_lazy. + * + * If warranted, also wake up the kthread servicing this CPUs queues. + */ +static void __call_rcu_nocb_enqueue(struct rcu_data *rdp, + struct rcu_head *rhp, + struct rcu_head **rhtp, + int rhcount, int rhcount_lazy) +{ + int len; + struct rcu_head **old_rhpp; + struct task_struct *t; + + /* Enqueue the callback on the nocb list and update counts. */ + old_rhpp = xchg(&rdp->nocb_tail, rhtp); + ACCESS_ONCE(*old_rhpp) = rhp; + atomic_long_add(rhcount, &rdp->nocb_q_count); + atomic_long_add(rhcount_lazy, &rdp->nocb_q_count_lazy); + + /* If we are not being polled and there is a kthread, awaken it ... */ + t = ACCESS_ONCE(rdp->nocb_kthread); + if (rcu_nocb_poll || !t) { + trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, + TPS("WakeNotPoll")); + return; + } + len = atomic_long_read(&rdp->nocb_q_count); + if (old_rhpp == &rdp->nocb_head) { + wake_up(&rdp->nocb_wq); /* ... only if queue was empty ... */ + rdp->qlen_last_fqs_check = 0; + trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, TPS("WakeEmpty")); + } else if (len > rdp->qlen_last_fqs_check + qhimark) { + wake_up_process(t); /* ... or if many callbacks queued. */ + rdp->qlen_last_fqs_check = LONG_MAX / 2; + trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, TPS("WakeOvf")); + } else { + trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, TPS("WakeNot")); + } + return; +} + +/* + * This is a helper for __call_rcu(), which invokes this when the normal + * callback queue is inoperable. If this is not a no-CBs CPU, this + * function returns failure back to __call_rcu(), which can complain + * appropriately. + * + * Otherwise, this function queues the callback where the corresponding + * "rcuo" kthread can find it. + */ +static bool __call_rcu_nocb(struct rcu_data *rdp, struct rcu_head *rhp, + bool lazy) +{ + + if (!rcu_is_nocb_cpu(rdp->cpu)) + return 0; + __call_rcu_nocb_enqueue(rdp, rhp, &rhp->next, 1, lazy); + if (__is_kfree_rcu_offset((unsigned long)rhp->func)) + trace_rcu_kfree_callback(rdp->rsp->name, rhp, + (unsigned long)rhp->func, + -atomic_long_read(&rdp->nocb_q_count_lazy), + -atomic_long_read(&rdp->nocb_q_count)); + else + trace_rcu_callback(rdp->rsp->name, rhp, + -atomic_long_read(&rdp->nocb_q_count_lazy), + -atomic_long_read(&rdp->nocb_q_count)); + return 1; +} + +/* + * Adopt orphaned callbacks on a no-CBs CPU, or return 0 if this is + * not a no-CBs CPU. + */ +static bool __maybe_unused rcu_nocb_adopt_orphan_cbs(struct rcu_state *rsp, + struct rcu_data *rdp) +{ + long ql = rsp->qlen; + long qll = rsp->qlen_lazy; + + /* If this is not a no-CBs CPU, tell the caller to do it the old way. */ + if (!rcu_is_nocb_cpu(smp_processor_id())) + return 0; + rsp->qlen = 0; + rsp->qlen_lazy = 0; + + /* First, enqueue the donelist, if any. This preserves CB ordering. */ + if (rsp->orphan_donelist != NULL) { + __call_rcu_nocb_enqueue(rdp, rsp->orphan_donelist, + rsp->orphan_donetail, ql, qll); + ql = qll = 0; + rsp->orphan_donelist = NULL; + rsp->orphan_donetail = &rsp->orphan_donelist; + } + if (rsp->orphan_nxtlist != NULL) { + __call_rcu_nocb_enqueue(rdp, rsp->orphan_nxtlist, + rsp->orphan_nxttail, ql, qll); + ql = qll = 0; + rsp->orphan_nxtlist = NULL; + rsp->orphan_nxttail = &rsp->orphan_nxtlist; + } + return 1; +} + +/* + * If necessary, kick off a new grace period, and either way wait + * for a subsequent grace period to complete. + */ +static void rcu_nocb_wait_gp(struct rcu_data *rdp) +{ + unsigned long c; + bool d; + unsigned long flags; + struct rcu_node *rnp = rdp->mynode; + + raw_spin_lock_irqsave(&rnp->lock, flags); + c = rcu_start_future_gp(rnp, rdp); + raw_spin_unlock_irqrestore(&rnp->lock, flags); + + /* + * Wait for the grace period. Do so interruptibly to avoid messing + * up the load average. + */ + trace_rcu_future_gp(rnp, rdp, c, TPS("StartWait")); + for (;;) { + wait_event_interruptible( + rnp->nocb_gp_wq[c & 0x1], + (d = ULONG_CMP_GE(ACCESS_ONCE(rnp->completed), c))); + if (likely(d)) + break; + flush_signals(current); + trace_rcu_future_gp(rnp, rdp, c, TPS("ResumeWait")); + } + trace_rcu_future_gp(rnp, rdp, c, TPS("EndWait")); + smp_mb(); /* Ensure that CB invocation happens after GP end. */ +} + +/* + * Per-rcu_data kthread, but only for no-CBs CPUs. Each kthread invokes + * callbacks queued by the corresponding no-CBs CPU. + */ +static int rcu_nocb_kthread(void *arg) +{ + int c, cl; + bool firsttime = 1; + struct rcu_head *list; + struct rcu_head *next; + struct rcu_head **tail; + struct rcu_data *rdp = arg; + + /* Each pass through this loop invokes one batch of callbacks */ + for (;;) { + /* If not polling, wait for next batch of callbacks. */ + if (!rcu_nocb_poll) { + trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, + TPS("Sleep")); + wait_event_interruptible(rdp->nocb_wq, rdp->nocb_head); + } else if (firsttime) { + firsttime = 0; + trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, + TPS("Poll")); + } + list = ACCESS_ONCE(rdp->nocb_head); + if (!list) { + if (!rcu_nocb_poll) + trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, + TPS("WokeEmpty")); + schedule_timeout_interruptible(1); + flush_signals(current); + continue; + } + firsttime = 1; + trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, + TPS("WokeNonEmpty")); + + /* + * Extract queued callbacks, update counts, and wait + * for a grace period to elapse. + */ + ACCESS_ONCE(rdp->nocb_head) = NULL; + tail = xchg(&rdp->nocb_tail, &rdp->nocb_head); + c = atomic_long_xchg(&rdp->nocb_q_count, 0); + cl = atomic_long_xchg(&rdp->nocb_q_count_lazy, 0); + ACCESS_ONCE(rdp->nocb_p_count) += c; + ACCESS_ONCE(rdp->nocb_p_count_lazy) += cl; + rcu_nocb_wait_gp(rdp); + + /* Each pass through the following loop invokes a callback. */ + trace_rcu_batch_start(rdp->rsp->name, cl, c, -1); + c = cl = 0; + while (list) { + next = list->next; + /* Wait for enqueuing to complete, if needed. */ + while (next == NULL && &list->next != tail) { + trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, + TPS("WaitQueue")); + schedule_timeout_interruptible(1); + trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, + TPS("WokeQueue")); + next = list->next; + } + debug_rcu_head_unqueue(list); + local_bh_disable(); + if (__rcu_reclaim(rdp->rsp->name, list)) + cl++; + c++; + local_bh_enable(); + list = next; + } + trace_rcu_batch_end(rdp->rsp->name, c, !!list, 0, 0, 1); + ACCESS_ONCE(rdp->nocb_p_count) -= c; + ACCESS_ONCE(rdp->nocb_p_count_lazy) -= cl; + rdp->n_nocbs_invoked += c; + } + return 0; +} + +/* Initialize per-rcu_data variables for no-CBs CPUs. */ +static void __init rcu_boot_init_nocb_percpu_data(struct rcu_data *rdp) +{ + rdp->nocb_tail = &rdp->nocb_head; + init_waitqueue_head(&rdp->nocb_wq); +} + +/* Create a kthread for each RCU flavor for each no-CBs CPU. */ +static void __init rcu_spawn_nocb_kthreads(struct rcu_state *rsp) +{ + int cpu; + struct rcu_data *rdp; + struct task_struct *t; + + if (rcu_nocb_mask == NULL) + return; + for_each_cpu(cpu, rcu_nocb_mask) { + rdp = per_cpu_ptr(rsp->rda, cpu); + t = kthread_run(rcu_nocb_kthread, rdp, + "rcuo%c/%d", rsp->abbr, cpu); + BUG_ON(IS_ERR(t)); + ACCESS_ONCE(rdp->nocb_kthread) = t; + } +} + +/* Prevent __call_rcu() from enqueuing callbacks on no-CBs CPUs */ +static bool init_nocb_callback_list(struct rcu_data *rdp) +{ + if (rcu_nocb_mask == NULL || + !cpumask_test_cpu(rdp->cpu, rcu_nocb_mask)) + return false; + rdp->nxttail[RCU_NEXT_TAIL] = NULL; + return true; +} + +#else /* #ifdef CONFIG_RCU_NOCB_CPU */ + +static int rcu_nocb_needs_gp(struct rcu_state *rsp) +{ + return 0; +} + +static void rcu_nocb_gp_cleanup(struct rcu_state *rsp, struct rcu_node *rnp) +{ +} + +static void rcu_nocb_gp_set(struct rcu_node *rnp, int nrq) +{ +} + +static void rcu_init_one_nocb(struct rcu_node *rnp) +{ +} + +static bool __call_rcu_nocb(struct rcu_data *rdp, struct rcu_head *rhp, + bool lazy) +{ + return 0; +} + +static bool __maybe_unused rcu_nocb_adopt_orphan_cbs(struct rcu_state *rsp, + struct rcu_data *rdp) +{ + return 0; +} + +static void __init rcu_boot_init_nocb_percpu_data(struct rcu_data *rdp) +{ +} + +static void __init rcu_spawn_nocb_kthreads(struct rcu_state *rsp) +{ +} + +static bool init_nocb_callback_list(struct rcu_data *rdp) +{ + return false; +} + +#endif /* #else #ifdef CONFIG_RCU_NOCB_CPU */ + +/* + * An adaptive-ticks CPU can potentially execute in kernel mode for an + * arbitrarily long period of time with the scheduling-clock tick turned + * off. RCU will be paying attention to this CPU because it is in the + * kernel, but the CPU cannot be guaranteed to be executing the RCU state + * machine because the scheduling-clock tick has been disabled. Therefore, + * if an adaptive-ticks CPU is failing to respond to the current grace + * period and has not be idle from an RCU perspective, kick it. + */ +static void rcu_kick_nohz_cpu(int cpu) +{ +#ifdef CONFIG_NO_HZ_FULL + if (tick_nohz_full_cpu(cpu)) + smp_send_reschedule(cpu); +#endif /* #ifdef CONFIG_NO_HZ_FULL */ +} + + +#ifdef CONFIG_NO_HZ_FULL_SYSIDLE + +/* + * Define RCU flavor that holds sysidle state. This needs to be the + * most active flavor of RCU. + */ +#ifdef CONFIG_PREEMPT_RCU +static struct rcu_state *rcu_sysidle_state = &rcu_preempt_state; +#else /* #ifdef CONFIG_PREEMPT_RCU */ +static struct rcu_state *rcu_sysidle_state = &rcu_sched_state; +#endif /* #else #ifdef CONFIG_PREEMPT_RCU */ + +static int full_sysidle_state; /* Current system-idle state. */ +#define RCU_SYSIDLE_NOT 0 /* Some CPU is not idle. */ +#define RCU_SYSIDLE_SHORT 1 /* All CPUs idle for brief period. */ +#define RCU_SYSIDLE_LONG 2 /* All CPUs idle for long enough. */ +#define RCU_SYSIDLE_FULL 3 /* All CPUs idle, ready for sysidle. */ +#define RCU_SYSIDLE_FULL_NOTED 4 /* Actually entered sysidle state. */ + +/* + * Invoked to note exit from irq or task transition to idle. Note that + * usermode execution does -not- count as idle here! After all, we want + * to detect full-system idle states, not RCU quiescent states and grace + * periods. The caller must have disabled interrupts. + */ +static void rcu_sysidle_enter(struct rcu_dynticks *rdtp, int irq) +{ + unsigned long j; + + /* Adjust nesting, check for fully idle. */ + if (irq) { + rdtp->dynticks_idle_nesting--; + WARN_ON_ONCE(rdtp->dynticks_idle_nesting < 0); + if (rdtp->dynticks_idle_nesting != 0) + return; /* Still not fully idle. */ + } else { + if ((rdtp->dynticks_idle_nesting & DYNTICK_TASK_NEST_MASK) == + DYNTICK_TASK_NEST_VALUE) { + rdtp->dynticks_idle_nesting = 0; + } else { + rdtp->dynticks_idle_nesting -= DYNTICK_TASK_NEST_VALUE; + WARN_ON_ONCE(rdtp->dynticks_idle_nesting < 0); + return; /* Still not fully idle. */ + } + } + + /* Record start of fully idle period. */ + j = jiffies; + ACCESS_ONCE(rdtp->dynticks_idle_jiffies) = j; + smp_mb__before_atomic_inc(); + atomic_inc(&rdtp->dynticks_idle); + smp_mb__after_atomic_inc(); + WARN_ON_ONCE(atomic_read(&rdtp->dynticks_idle) & 0x1); +} + +/* + * Unconditionally force exit from full system-idle state. This is + * invoked when a normal CPU exits idle, but must be called separately + * for the timekeeping CPU (tick_do_timer_cpu). The reason for this + * is that the timekeeping CPU is permitted to take scheduling-clock + * interrupts while the system is in system-idle state, and of course + * rcu_sysidle_exit() has no way of distinguishing a scheduling-clock + * interrupt from any other type of interrupt. + */ +void rcu_sysidle_force_exit(void) +{ + int oldstate = ACCESS_ONCE(full_sysidle_state); + int newoldstate; + + /* + * Each pass through the following loop attempts to exit full + * system-idle state. If contention proves to be a problem, + * a trylock-based contention tree could be used here. + */ + while (oldstate > RCU_SYSIDLE_SHORT) { + newoldstate = cmpxchg(&full_sysidle_state, + oldstate, RCU_SYSIDLE_NOT); + if (oldstate == newoldstate && + oldstate == RCU_SYSIDLE_FULL_NOTED) { + rcu_kick_nohz_cpu(tick_do_timer_cpu); + return; /* We cleared it, done! */ + } + oldstate = newoldstate; + } + smp_mb(); /* Order initial oldstate fetch vs. later non-idle work. */ +} + +/* + * Invoked to note entry to irq or task transition from idle. Note that + * usermode execution does -not- count as idle here! The caller must + * have disabled interrupts. + */ +static void rcu_sysidle_exit(struct rcu_dynticks *rdtp, int irq) +{ + /* Adjust nesting, check for already non-idle. */ + if (irq) { + rdtp->dynticks_idle_nesting++; + WARN_ON_ONCE(rdtp->dynticks_idle_nesting <= 0); + if (rdtp->dynticks_idle_nesting != 1) + return; /* Already non-idle. */ + } else { + /* + * Allow for irq misnesting. Yes, it really is possible + * to enter an irq handler then never leave it, and maybe + * also vice versa. Handle both possibilities. + */ + if (rdtp->dynticks_idle_nesting & DYNTICK_TASK_NEST_MASK) { + rdtp->dynticks_idle_nesting += DYNTICK_TASK_NEST_VALUE; + WARN_ON_ONCE(rdtp->dynticks_idle_nesting <= 0); + return; /* Already non-idle. */ + } else { + rdtp->dynticks_idle_nesting = DYNTICK_TASK_EXIT_IDLE; + } + } + + /* Record end of idle period. */ + smp_mb__before_atomic_inc(); + atomic_inc(&rdtp->dynticks_idle); + smp_mb__after_atomic_inc(); + WARN_ON_ONCE(!(atomic_read(&rdtp->dynticks_idle) & 0x1)); + + /* + * If we are the timekeeping CPU, we are permitted to be non-idle + * during a system-idle state. This must be the case, because + * the timekeeping CPU has to take scheduling-clock interrupts + * during the time that the system is transitioning to full + * system-idle state. This means that the timekeeping CPU must + * invoke rcu_sysidle_force_exit() directly if it does anything + * more than take a scheduling-clock interrupt. + */ + if (smp_processor_id() == tick_do_timer_cpu) + return; + + /* Update system-idle state: We are clearly no longer fully idle! */ + rcu_sysidle_force_exit(); +} + +/* + * Check to see if the current CPU is idle. Note that usermode execution + * does not count as idle. The caller must have disabled interrupts. + */ +static void rcu_sysidle_check_cpu(struct rcu_data *rdp, bool *isidle, + unsigned long *maxj) +{ + int cur; + unsigned long j; + struct rcu_dynticks *rdtp = rdp->dynticks; + + /* + * If some other CPU has already reported non-idle, if this is + * not the flavor of RCU that tracks sysidle state, or if this + * is an offline or the timekeeping CPU, nothing to do. + */ + if (!*isidle || rdp->rsp != rcu_sysidle_state || + cpu_is_offline(rdp->cpu) || rdp->cpu == tick_do_timer_cpu) + return; + if (rcu_gp_in_progress(rdp->rsp)) + WARN_ON_ONCE(smp_processor_id() != tick_do_timer_cpu); + + /* Pick up current idle and NMI-nesting counter and check. */ + cur = atomic_read(&rdtp->dynticks_idle); + if (cur & 0x1) { + *isidle = false; /* We are not idle! */ + return; + } + smp_mb(); /* Read counters before timestamps. */ + + /* Pick up timestamps. */ + j = ACCESS_ONCE(rdtp->dynticks_idle_jiffies); + /* If this CPU entered idle more recently, update maxj timestamp. */ + if (ULONG_CMP_LT(*maxj, j)) + *maxj = j; +} + +/* + * Is this the flavor of RCU that is handling full-system idle? + */ +static bool is_sysidle_rcu_state(struct rcu_state *rsp) +{ + return rsp == rcu_sysidle_state; +} + +/* + * Bind the grace-period kthread for the sysidle flavor of RCU to the + * timekeeping CPU. + */ +static void rcu_bind_gp_kthread(void) +{ + int cpu = ACCESS_ONCE(tick_do_timer_cpu); + + if (cpu < 0 || cpu >= nr_cpu_ids) + return; + if (raw_smp_processor_id() != cpu) + set_cpus_allowed_ptr(current, cpumask_of(cpu)); +} + +/* + * Return a delay in jiffies based on the number of CPUs, rcu_node + * leaf fanout, and jiffies tick rate. The idea is to allow larger + * systems more time to transition to full-idle state in order to + * avoid the cache thrashing that otherwise occur on the state variable. + * Really small systems (less than a couple of tens of CPUs) should + * instead use a single global atomically incremented counter, and later + * versions of this will automatically reconfigure themselves accordingly. + */ +static unsigned long rcu_sysidle_delay(void) +{ + if (nr_cpu_ids <= CONFIG_NO_HZ_FULL_SYSIDLE_SMALL) + return 0; + return DIV_ROUND_UP(nr_cpu_ids * HZ, rcu_fanout_leaf * 1000); +} + +/* + * Advance the full-system-idle state. This is invoked when all of + * the non-timekeeping CPUs are idle. + */ +static void rcu_sysidle(unsigned long j) +{ + /* Check the current state. */ + switch (ACCESS_ONCE(full_sysidle_state)) { + case RCU_SYSIDLE_NOT: + + /* First time all are idle, so note a short idle period. */ + ACCESS_ONCE(full_sysidle_state) = RCU_SYSIDLE_SHORT; + break; + + case RCU_SYSIDLE_SHORT: + + /* + * Idle for a bit, time to advance to next state? + * cmpxchg failure means race with non-idle, let them win. + */ + if (ULONG_CMP_GE(jiffies, j + rcu_sysidle_delay())) + (void)cmpxchg(&full_sysidle_state, + RCU_SYSIDLE_SHORT, RCU_SYSIDLE_LONG); + break; + + case RCU_SYSIDLE_LONG: + + /* + * Do an additional check pass before advancing to full. + * cmpxchg failure means race with non-idle, let them win. + */ + if (ULONG_CMP_GE(jiffies, j + rcu_sysidle_delay())) + (void)cmpxchg(&full_sysidle_state, + RCU_SYSIDLE_LONG, RCU_SYSIDLE_FULL); + break; + + default: + break; + } +} + +/* + * Found a non-idle non-timekeeping CPU, so kick the system-idle state + * back to the beginning. + */ +static void rcu_sysidle_cancel(void) +{ + smp_mb(); + ACCESS_ONCE(full_sysidle_state) = RCU_SYSIDLE_NOT; +} + +/* + * Update the sysidle state based on the results of a force-quiescent-state + * scan of the CPUs' dyntick-idle state. + */ +static void rcu_sysidle_report(struct rcu_state *rsp, int isidle, + unsigned long maxj, bool gpkt) +{ + if (rsp != rcu_sysidle_state) + return; /* Wrong flavor, ignore. */ + if (gpkt && nr_cpu_ids <= CONFIG_NO_HZ_FULL_SYSIDLE_SMALL) + return; /* Running state machine from timekeeping CPU. */ + if (isidle) + rcu_sysidle(maxj); /* More idle! */ + else + rcu_sysidle_cancel(); /* Idle is over. */ +} + +/* + * Wrapper for rcu_sysidle_report() when called from the grace-period + * kthread's context. + */ +static void rcu_sysidle_report_gp(struct rcu_state *rsp, int isidle, + unsigned long maxj) +{ + rcu_sysidle_report(rsp, isidle, maxj, true); +} + +/* Callback and function for forcing an RCU grace period. */ +struct rcu_sysidle_head { + struct rcu_head rh; + int inuse; +}; + +static void rcu_sysidle_cb(struct rcu_head *rhp) +{ + struct rcu_sysidle_head *rshp; + + /* + * The following memory barrier is needed to replace the + * memory barriers that would normally be in the memory + * allocator. + */ + smp_mb(); /* grace period precedes setting inuse. */ + + rshp = container_of(rhp, struct rcu_sysidle_head, rh); + ACCESS_ONCE(rshp->inuse) = 0; +} + +/* + * Check to see if the system is fully idle, other than the timekeeping CPU. + * The caller must have disabled interrupts. + */ +bool rcu_sys_is_idle(void) +{ + static struct rcu_sysidle_head rsh; + int rss = ACCESS_ONCE(full_sysidle_state); + + if (WARN_ON_ONCE(smp_processor_id() != tick_do_timer_cpu)) + return false; + + /* Handle small-system case by doing a full scan of CPUs. */ + if (nr_cpu_ids <= CONFIG_NO_HZ_FULL_SYSIDLE_SMALL) { + int oldrss = rss - 1; + + /* + * One pass to advance to each state up to _FULL. + * Give up if any pass fails to advance the state. + */ + while (rss < RCU_SYSIDLE_FULL && oldrss < rss) { + int cpu; + bool isidle = true; + unsigned long maxj = jiffies - ULONG_MAX / 4; + struct rcu_data *rdp; + + /* Scan all the CPUs looking for nonidle CPUs. */ + for_each_possible_cpu(cpu) { + rdp = per_cpu_ptr(rcu_sysidle_state->rda, cpu); + rcu_sysidle_check_cpu(rdp, &isidle, &maxj); + if (!isidle) + break; + } + rcu_sysidle_report(rcu_sysidle_state, + isidle, maxj, false); + oldrss = rss; + rss = ACCESS_ONCE(full_sysidle_state); + } + } + + /* If this is the first observation of an idle period, record it. */ + if (rss == RCU_SYSIDLE_FULL) { + rss = cmpxchg(&full_sysidle_state, + RCU_SYSIDLE_FULL, RCU_SYSIDLE_FULL_NOTED); + return rss == RCU_SYSIDLE_FULL; + } + + smp_mb(); /* ensure rss load happens before later caller actions. */ + + /* If already fully idle, tell the caller (in case of races). */ + if (rss == RCU_SYSIDLE_FULL_NOTED) + return true; + + /* + * If we aren't there yet, and a grace period is not in flight, + * initiate a grace period. Either way, tell the caller that + * we are not there yet. We use an xchg() rather than an assignment + * to make up for the memory barriers that would otherwise be + * provided by the memory allocator. + */ + if (nr_cpu_ids > CONFIG_NO_HZ_FULL_SYSIDLE_SMALL && + !rcu_gp_in_progress(rcu_sysidle_state) && + !rsh.inuse && xchg(&rsh.inuse, 1) == 0) + call_rcu(&rsh.rh, rcu_sysidle_cb); + return false; +} + +/* + * Initialize dynticks sysidle state for CPUs coming online. + */ +static void rcu_sysidle_init_percpu_data(struct rcu_dynticks *rdtp) +{ + rdtp->dynticks_idle_nesting = DYNTICK_TASK_NEST_VALUE; +} + +#else /* #ifdef CONFIG_NO_HZ_FULL_SYSIDLE */ + +static void rcu_sysidle_enter(struct rcu_dynticks *rdtp, int irq) +{ +} + +static void rcu_sysidle_exit(struct rcu_dynticks *rdtp, int irq) +{ +} + +static void rcu_sysidle_check_cpu(struct rcu_data *rdp, bool *isidle, + unsigned long *maxj) +{ +} + +static bool is_sysidle_rcu_state(struct rcu_state *rsp) +{ + return false; +} + +static void rcu_bind_gp_kthread(void) +{ +} + +static void rcu_sysidle_report_gp(struct rcu_state *rsp, int isidle, + unsigned long maxj) +{ +} + +static void rcu_sysidle_init_percpu_data(struct rcu_dynticks *rdtp) +{ +} + +#endif /* #else #ifdef CONFIG_NO_HZ_FULL_SYSIDLE */ diff --git a/kernel/rcu/tree_trace.c b/kernel/rcu/tree_trace.c new file mode 100644 index 0000000..3596797 --- /dev/null +++ b/kernel/rcu/tree_trace.c @@ -0,0 +1,500 @@ +/* + * Read-Copy Update tracing for classic implementation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright IBM Corporation, 2008 + * + * Papers: http://www.rdrop.com/users/paulmck/RCU + * + * For detailed explanation of Read-Copy Update mechanism see - + * Documentation/RCU + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RCU_TREE_NONCORE +#include "tree.h" + +static int r_open(struct inode *inode, struct file *file, + const struct seq_operations *op) +{ + int ret = seq_open(file, op); + if (!ret) { + struct seq_file *m = (struct seq_file *)file->private_data; + m->private = inode->i_private; + } + return ret; +} + +static void *r_start(struct seq_file *m, loff_t *pos) +{ + struct rcu_state *rsp = (struct rcu_state *)m->private; + *pos = cpumask_next(*pos - 1, cpu_possible_mask); + if ((*pos) < nr_cpu_ids) + return per_cpu_ptr(rsp->rda, *pos); + return NULL; +} + +static void *r_next(struct seq_file *m, void *v, loff_t *pos) +{ + (*pos)++; + return r_start(m, pos); +} + +static void r_stop(struct seq_file *m, void *v) +{ +} + +static int show_rcubarrier(struct seq_file *m, void *v) +{ + struct rcu_state *rsp = (struct rcu_state *)m->private; + seq_printf(m, "bcc: %d nbd: %lu\n", + atomic_read(&rsp->barrier_cpu_count), + rsp->n_barrier_done); + return 0; +} + +static int rcubarrier_open(struct inode *inode, struct file *file) +{ + return single_open(file, show_rcubarrier, inode->i_private); +} + +static const struct file_operations rcubarrier_fops = { + .owner = THIS_MODULE, + .open = rcubarrier_open, + .read = seq_read, + .llseek = no_llseek, + .release = single_release, +}; + +#ifdef CONFIG_RCU_BOOST + +static char convert_kthread_status(unsigned int kthread_status) +{ + if (kthread_status > RCU_KTHREAD_MAX) + return '?'; + return "SRWOY"[kthread_status]; +} + +#endif /* #ifdef CONFIG_RCU_BOOST */ + +static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp) +{ + long ql, qll; + + if (!rdp->beenonline) + return; + seq_printf(m, "%3d%cc=%ld g=%ld pq=%d qp=%d", + rdp->cpu, + cpu_is_offline(rdp->cpu) ? '!' : ' ', + ulong2long(rdp->completed), ulong2long(rdp->gpnum), + rdp->passed_quiesce, rdp->qs_pending); + seq_printf(m, " dt=%d/%llx/%d df=%lu", + atomic_read(&rdp->dynticks->dynticks), + rdp->dynticks->dynticks_nesting, + rdp->dynticks->dynticks_nmi_nesting, + rdp->dynticks_fqs); + seq_printf(m, " of=%lu", rdp->offline_fqs); + rcu_nocb_q_lengths(rdp, &ql, &qll); + qll += rdp->qlen_lazy; + ql += rdp->qlen; + seq_printf(m, " ql=%ld/%ld qs=%c%c%c%c", + qll, ql, + ".N"[rdp->nxttail[RCU_NEXT_READY_TAIL] != + rdp->nxttail[RCU_NEXT_TAIL]], + ".R"[rdp->nxttail[RCU_WAIT_TAIL] != + rdp->nxttail[RCU_NEXT_READY_TAIL]], + ".W"[rdp->nxttail[RCU_DONE_TAIL] != + rdp->nxttail[RCU_WAIT_TAIL]], + ".D"[&rdp->nxtlist != rdp->nxttail[RCU_DONE_TAIL]]); +#ifdef CONFIG_RCU_BOOST + seq_printf(m, " kt=%d/%c ktl=%x", + per_cpu(rcu_cpu_has_work, rdp->cpu), + convert_kthread_status(per_cpu(rcu_cpu_kthread_status, + rdp->cpu)), + per_cpu(rcu_cpu_kthread_loops, rdp->cpu) & 0xffff); +#endif /* #ifdef CONFIG_RCU_BOOST */ + seq_printf(m, " b=%ld", rdp->blimit); + seq_printf(m, " ci=%lu nci=%lu co=%lu ca=%lu\n", + rdp->n_cbs_invoked, rdp->n_nocbs_invoked, + rdp->n_cbs_orphaned, rdp->n_cbs_adopted); +} + +static int show_rcudata(struct seq_file *m, void *v) +{ + print_one_rcu_data(m, (struct rcu_data *)v); + return 0; +} + +static const struct seq_operations rcudate_op = { + .start = r_start, + .next = r_next, + .stop = r_stop, + .show = show_rcudata, +}; + +static int rcudata_open(struct inode *inode, struct file *file) +{ + return r_open(inode, file, &rcudate_op); +} + +static const struct file_operations rcudata_fops = { + .owner = THIS_MODULE, + .open = rcudata_open, + .read = seq_read, + .llseek = no_llseek, + .release = seq_release, +}; + +static int show_rcuexp(struct seq_file *m, void *v) +{ + struct rcu_state *rsp = (struct rcu_state *)m->private; + + seq_printf(m, "s=%lu d=%lu w=%lu tf=%lu wd1=%lu wd2=%lu n=%lu sc=%lu dt=%lu dl=%lu dx=%lu\n", + atomic_long_read(&rsp->expedited_start), + atomic_long_read(&rsp->expedited_done), + atomic_long_read(&rsp->expedited_wrap), + atomic_long_read(&rsp->expedited_tryfail), + atomic_long_read(&rsp->expedited_workdone1), + atomic_long_read(&rsp->expedited_workdone2), + atomic_long_read(&rsp->expedited_normal), + atomic_long_read(&rsp->expedited_stoppedcpus), + atomic_long_read(&rsp->expedited_done_tries), + atomic_long_read(&rsp->expedited_done_lost), + atomic_long_read(&rsp->expedited_done_exit)); + return 0; +} + +static int rcuexp_open(struct inode *inode, struct file *file) +{ + return single_open(file, show_rcuexp, inode->i_private); +} + +static const struct file_operations rcuexp_fops = { + .owner = THIS_MODULE, + .open = rcuexp_open, + .read = seq_read, + .llseek = no_llseek, + .release = single_release, +}; + +#ifdef CONFIG_RCU_BOOST + +static void print_one_rcu_node_boost(struct seq_file *m, struct rcu_node *rnp) +{ + seq_printf(m, "%d:%d tasks=%c%c%c%c kt=%c ntb=%lu neb=%lu nnb=%lu ", + rnp->grplo, rnp->grphi, + "T."[list_empty(&rnp->blkd_tasks)], + "N."[!rnp->gp_tasks], + "E."[!rnp->exp_tasks], + "B."[!rnp->boost_tasks], + convert_kthread_status(rnp->boost_kthread_status), + rnp->n_tasks_boosted, rnp->n_exp_boosts, + rnp->n_normal_boosts); + seq_printf(m, "j=%04x bt=%04x\n", + (int)(jiffies & 0xffff), + (int)(rnp->boost_time & 0xffff)); + seq_printf(m, " balk: nt=%lu egt=%lu bt=%lu nb=%lu ny=%lu nos=%lu\n", + rnp->n_balk_blkd_tasks, + rnp->n_balk_exp_gp_tasks, + rnp->n_balk_boost_tasks, + rnp->n_balk_notblocked, + rnp->n_balk_notyet, + rnp->n_balk_nos); +} + +static int show_rcu_node_boost(struct seq_file *m, void *unused) +{ + struct rcu_node *rnp; + + rcu_for_each_leaf_node(&rcu_preempt_state, rnp) + print_one_rcu_node_boost(m, rnp); + return 0; +} + +static int rcu_node_boost_open(struct inode *inode, struct file *file) +{ + return single_open(file, show_rcu_node_boost, NULL); +} + +static const struct file_operations rcu_node_boost_fops = { + .owner = THIS_MODULE, + .open = rcu_node_boost_open, + .read = seq_read, + .llseek = no_llseek, + .release = single_release, +}; + +#endif /* #ifdef CONFIG_RCU_BOOST */ + +static void print_one_rcu_state(struct seq_file *m, struct rcu_state *rsp) +{ + unsigned long gpnum; + int level = 0; + struct rcu_node *rnp; + + gpnum = rsp->gpnum; + seq_printf(m, "c=%ld g=%ld s=%d jfq=%ld j=%x ", + ulong2long(rsp->completed), ulong2long(gpnum), + rsp->fqs_state, + (long)(rsp->jiffies_force_qs - jiffies), + (int)(jiffies & 0xffff)); + seq_printf(m, "nfqs=%lu/nfqsng=%lu(%lu) fqlh=%lu oqlen=%ld/%ld\n", + rsp->n_force_qs, rsp->n_force_qs_ngp, + rsp->n_force_qs - rsp->n_force_qs_ngp, + rsp->n_force_qs_lh, rsp->qlen_lazy, rsp->qlen); + for (rnp = &rsp->node[0]; rnp - &rsp->node[0] < rcu_num_nodes; rnp++) { + if (rnp->level != level) { + seq_puts(m, "\n"); + level = rnp->level; + } + seq_printf(m, "%lx/%lx %c%c>%c %d:%d ^%d ", + rnp->qsmask, rnp->qsmaskinit, + ".G"[rnp->gp_tasks != NULL], + ".E"[rnp->exp_tasks != NULL], + ".T"[!list_empty(&rnp->blkd_tasks)], + rnp->grplo, rnp->grphi, rnp->grpnum); + } + seq_puts(m, "\n"); +} + +static int show_rcuhier(struct seq_file *m, void *v) +{ + struct rcu_state *rsp = (struct rcu_state *)m->private; + print_one_rcu_state(m, rsp); + return 0; +} + +static int rcuhier_open(struct inode *inode, struct file *file) +{ + return single_open(file, show_rcuhier, inode->i_private); +} + +static const struct file_operations rcuhier_fops = { + .owner = THIS_MODULE, + .open = rcuhier_open, + .read = seq_read, + .llseek = no_llseek, + .release = single_release, +}; + +static void show_one_rcugp(struct seq_file *m, struct rcu_state *rsp) +{ + unsigned long flags; + unsigned long completed; + unsigned long gpnum; + unsigned long gpage; + unsigned long gpmax; + struct rcu_node *rnp = &rsp->node[0]; + + raw_spin_lock_irqsave(&rnp->lock, flags); + completed = ACCESS_ONCE(rsp->completed); + gpnum = ACCESS_ONCE(rsp->gpnum); + if (completed == gpnum) + gpage = 0; + else + gpage = jiffies - rsp->gp_start; + gpmax = rsp->gp_max; + raw_spin_unlock_irqrestore(&rnp->lock, flags); + seq_printf(m, "completed=%ld gpnum=%ld age=%ld max=%ld\n", + ulong2long(completed), ulong2long(gpnum), gpage, gpmax); +} + +static int show_rcugp(struct seq_file *m, void *v) +{ + struct rcu_state *rsp = (struct rcu_state *)m->private; + show_one_rcugp(m, rsp); + return 0; +} + +static int rcugp_open(struct inode *inode, struct file *file) +{ + return single_open(file, show_rcugp, inode->i_private); +} + +static const struct file_operations rcugp_fops = { + .owner = THIS_MODULE, + .open = rcugp_open, + .read = seq_read, + .llseek = no_llseek, + .release = single_release, +}; + +static void print_one_rcu_pending(struct seq_file *m, struct rcu_data *rdp) +{ + if (!rdp->beenonline) + return; + seq_printf(m, "%3d%cnp=%ld ", + rdp->cpu, + cpu_is_offline(rdp->cpu) ? '!' : ' ', + rdp->n_rcu_pending); + seq_printf(m, "qsp=%ld rpq=%ld cbr=%ld cng=%ld ", + rdp->n_rp_qs_pending, + rdp->n_rp_report_qs, + rdp->n_rp_cb_ready, + rdp->n_rp_cpu_needs_gp); + seq_printf(m, "gpc=%ld gps=%ld nn=%ld\n", + rdp->n_rp_gp_completed, + rdp->n_rp_gp_started, + rdp->n_rp_need_nothing); +} + +static int show_rcu_pending(struct seq_file *m, void *v) +{ + print_one_rcu_pending(m, (struct rcu_data *)v); + return 0; +} + +static const struct seq_operations rcu_pending_op = { + .start = r_start, + .next = r_next, + .stop = r_stop, + .show = show_rcu_pending, +}; + +static int rcu_pending_open(struct inode *inode, struct file *file) +{ + return r_open(inode, file, &rcu_pending_op); +} + +static const struct file_operations rcu_pending_fops = { + .owner = THIS_MODULE, + .open = rcu_pending_open, + .read = seq_read, + .llseek = no_llseek, + .release = seq_release, +}; + +static int show_rcutorture(struct seq_file *m, void *unused) +{ + seq_printf(m, "rcutorture test sequence: %lu %s\n", + rcutorture_testseq >> 1, + (rcutorture_testseq & 0x1) ? "(test in progress)" : ""); + seq_printf(m, "rcutorture update version number: %lu\n", + rcutorture_vernum); + return 0; +} + +static int rcutorture_open(struct inode *inode, struct file *file) +{ + return single_open(file, show_rcutorture, NULL); +} + +static const struct file_operations rcutorture_fops = { + .owner = THIS_MODULE, + .open = rcutorture_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct dentry *rcudir; + +static int __init rcutree_trace_init(void) +{ + struct rcu_state *rsp; + struct dentry *retval; + struct dentry *rspdir; + + rcudir = debugfs_create_dir("rcu", NULL); + if (!rcudir) + goto free_out; + + for_each_rcu_flavor(rsp) { + rspdir = debugfs_create_dir(rsp->name, rcudir); + if (!rspdir) + goto free_out; + + retval = debugfs_create_file("rcudata", 0444, + rspdir, rsp, &rcudata_fops); + if (!retval) + goto free_out; + + retval = debugfs_create_file("rcuexp", 0444, + rspdir, rsp, &rcuexp_fops); + if (!retval) + goto free_out; + + retval = debugfs_create_file("rcu_pending", 0444, + rspdir, rsp, &rcu_pending_fops); + if (!retval) + goto free_out; + + retval = debugfs_create_file("rcubarrier", 0444, + rspdir, rsp, &rcubarrier_fops); + if (!retval) + goto free_out; + +#ifdef CONFIG_RCU_BOOST + if (rsp == &rcu_preempt_state) { + retval = debugfs_create_file("rcuboost", 0444, + rspdir, NULL, &rcu_node_boost_fops); + if (!retval) + goto free_out; + } +#endif + + retval = debugfs_create_file("rcugp", 0444, + rspdir, rsp, &rcugp_fops); + if (!retval) + goto free_out; + + retval = debugfs_create_file("rcuhier", 0444, + rspdir, rsp, &rcuhier_fops); + if (!retval) + goto free_out; + } + + retval = debugfs_create_file("rcutorture", 0444, rcudir, + NULL, &rcutorture_fops); + if (!retval) + goto free_out; + return 0; +free_out: + debugfs_remove_recursive(rcudir); + return 1; +} + +static void __exit rcutree_trace_cleanup(void) +{ + debugfs_remove_recursive(rcudir); +} + + +module_init(rcutree_trace_init); +module_exit(rcutree_trace_cleanup); + +MODULE_AUTHOR("Paul E. McKenney"); +MODULE_DESCRIPTION("Read-Copy Update tracing for hierarchical implementation"); +MODULE_LICENSE("GPL"); diff --git a/kernel/rcu/update.c b/kernel/rcu/update.c new file mode 100644 index 0000000..6cb3dff --- /dev/null +++ b/kernel/rcu/update.c @@ -0,0 +1,347 @@ +/* + * Read-Copy Update mechanism for mutual exclusion + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright IBM Corporation, 2001 + * + * Authors: Dipankar Sarma + * Manfred Spraul + * + * Based on the original work by Paul McKenney + * and inputs from Rusty Russell, Andrea Arcangeli and Andi Kleen. + * Papers: + * http://www.rdrop.com/users/paulmck/paper/rclockpdcsproof.pdf + * http://lse.sourceforge.net/locking/rclock_OLS.2001.05.01c.sc.pdf (OLS2001) + * + * For detailed explanation of Read-Copy Update mechanism see - + * http://lse.sourceforge.net/locking/rcupdate.html + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CREATE_TRACE_POINTS +#include + +#include "rcu.h" + +MODULE_ALIAS("rcupdate"); +#ifdef MODULE_PARAM_PREFIX +#undef MODULE_PARAM_PREFIX +#endif +#define MODULE_PARAM_PREFIX "rcupdate." + +module_param(rcu_expedited, int, 0); + +#ifdef CONFIG_PREEMPT_RCU + +/* + * Preemptible RCU implementation for rcu_read_lock(). + * Just increment ->rcu_read_lock_nesting, shared state will be updated + * if we block. + */ +void __rcu_read_lock(void) +{ + current->rcu_read_lock_nesting++; + barrier(); /* critical section after entry code. */ +} +EXPORT_SYMBOL_GPL(__rcu_read_lock); + +/* + * Preemptible RCU implementation for rcu_read_unlock(). + * Decrement ->rcu_read_lock_nesting. If the result is zero (outermost + * rcu_read_unlock()) and ->rcu_read_unlock_special is non-zero, then + * invoke rcu_read_unlock_special() to clean up after a context switch + * in an RCU read-side critical section and other special cases. + */ +void __rcu_read_unlock(void) +{ + struct task_struct *t = current; + + if (t->rcu_read_lock_nesting != 1) { + --t->rcu_read_lock_nesting; + } else { + barrier(); /* critical section before exit code. */ + t->rcu_read_lock_nesting = INT_MIN; +#ifdef CONFIG_PROVE_RCU_DELAY + udelay(10); /* Make preemption more probable. */ +#endif /* #ifdef CONFIG_PROVE_RCU_DELAY */ + barrier(); /* assign before ->rcu_read_unlock_special load */ + if (unlikely(ACCESS_ONCE(t->rcu_read_unlock_special))) + rcu_read_unlock_special(t); + barrier(); /* ->rcu_read_unlock_special load before assign */ + t->rcu_read_lock_nesting = 0; + } +#ifdef CONFIG_PROVE_LOCKING + { + int rrln = ACCESS_ONCE(t->rcu_read_lock_nesting); + + WARN_ON_ONCE(rrln < 0 && rrln > INT_MIN / 2); + } +#endif /* #ifdef CONFIG_PROVE_LOCKING */ +} +EXPORT_SYMBOL_GPL(__rcu_read_unlock); + +#endif /* #ifdef CONFIG_PREEMPT_RCU */ + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +static struct lock_class_key rcu_lock_key; +struct lockdep_map rcu_lock_map = + STATIC_LOCKDEP_MAP_INIT("rcu_read_lock", &rcu_lock_key); +EXPORT_SYMBOL_GPL(rcu_lock_map); + +static struct lock_class_key rcu_bh_lock_key; +struct lockdep_map rcu_bh_lock_map = + STATIC_LOCKDEP_MAP_INIT("rcu_read_lock_bh", &rcu_bh_lock_key); +EXPORT_SYMBOL_GPL(rcu_bh_lock_map); + +static struct lock_class_key rcu_sched_lock_key; +struct lockdep_map rcu_sched_lock_map = + STATIC_LOCKDEP_MAP_INIT("rcu_read_lock_sched", &rcu_sched_lock_key); +EXPORT_SYMBOL_GPL(rcu_sched_lock_map); + +int notrace debug_lockdep_rcu_enabled(void) +{ + return rcu_scheduler_active && debug_locks && + current->lockdep_recursion == 0; +} +EXPORT_SYMBOL_GPL(debug_lockdep_rcu_enabled); + +/** + * rcu_read_lock_bh_held() - might we be in RCU-bh read-side critical section? + * + * Check for bottom half being disabled, which covers both the + * CONFIG_PROVE_RCU and not cases. Note that if someone uses + * rcu_read_lock_bh(), but then later enables BH, lockdep (if enabled) + * will show the situation. This is useful for debug checks in functions + * that require that they be called within an RCU read-side critical + * section. + * + * Check debug_lockdep_rcu_enabled() to prevent false positives during boot. + * + * Note that rcu_read_lock() is disallowed if the CPU is either idle or + * offline from an RCU perspective, so check for those as well. + */ +int rcu_read_lock_bh_held(void) +{ + if (!debug_lockdep_rcu_enabled()) + return 1; + if (!rcu_is_watching()) + return 0; + if (!rcu_lockdep_current_cpu_online()) + return 0; + return in_softirq() || irqs_disabled(); +} +EXPORT_SYMBOL_GPL(rcu_read_lock_bh_held); + +#endif /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ + +struct rcu_synchronize { + struct rcu_head head; + struct completion completion; +}; + +/* + * Awaken the corresponding synchronize_rcu() instance now that a + * grace period has elapsed. + */ +static void wakeme_after_rcu(struct rcu_head *head) +{ + struct rcu_synchronize *rcu; + + rcu = container_of(head, struct rcu_synchronize, head); + complete(&rcu->completion); +} + +void wait_rcu_gp(call_rcu_func_t crf) +{ + struct rcu_synchronize rcu; + + init_rcu_head_on_stack(&rcu.head); + init_completion(&rcu.completion); + /* Will wake me after RCU finished. */ + crf(&rcu.head, wakeme_after_rcu); + /* Wait for it. */ + wait_for_completion(&rcu.completion); + destroy_rcu_head_on_stack(&rcu.head); +} +EXPORT_SYMBOL_GPL(wait_rcu_gp); + +#ifdef CONFIG_PROVE_RCU +/* + * wrapper function to avoid #include problems. + */ +int rcu_my_thread_group_empty(void) +{ + return thread_group_empty(current); +} +EXPORT_SYMBOL_GPL(rcu_my_thread_group_empty); +#endif /* #ifdef CONFIG_PROVE_RCU */ + +#ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD +static inline void debug_init_rcu_head(struct rcu_head *head) +{ + debug_object_init(head, &rcuhead_debug_descr); +} + +static inline void debug_rcu_head_free(struct rcu_head *head) +{ + debug_object_free(head, &rcuhead_debug_descr); +} + +/* + * fixup_activate is called when: + * - an active object is activated + * - an unknown object is activated (might be a statically initialized object) + * Activation is performed internally by call_rcu(). + */ +static int rcuhead_fixup_activate(void *addr, enum debug_obj_state state) +{ + struct rcu_head *head = addr; + + switch (state) { + + case ODEBUG_STATE_NOTAVAILABLE: + /* + * This is not really a fixup. We just make sure that it is + * tracked in the object tracker. + */ + debug_object_init(head, &rcuhead_debug_descr); + debug_object_activate(head, &rcuhead_debug_descr); + return 0; + default: + return 1; + } +} + +/** + * init_rcu_head_on_stack() - initialize on-stack rcu_head for debugobjects + * @head: pointer to rcu_head structure to be initialized + * + * This function informs debugobjects of a new rcu_head structure that + * has been allocated as an auto variable on the stack. This function + * is not required for rcu_head structures that are statically defined or + * that are dynamically allocated on the heap. This function has no + * effect for !CONFIG_DEBUG_OBJECTS_RCU_HEAD kernel builds. + */ +void init_rcu_head_on_stack(struct rcu_head *head) +{ + debug_object_init_on_stack(head, &rcuhead_debug_descr); +} +EXPORT_SYMBOL_GPL(init_rcu_head_on_stack); + +/** + * destroy_rcu_head_on_stack() - destroy on-stack rcu_head for debugobjects + * @head: pointer to rcu_head structure to be initialized + * + * This function informs debugobjects that an on-stack rcu_head structure + * is about to go out of scope. As with init_rcu_head_on_stack(), this + * function is not required for rcu_head structures that are statically + * defined or that are dynamically allocated on the heap. Also as with + * init_rcu_head_on_stack(), this function has no effect for + * !CONFIG_DEBUG_OBJECTS_RCU_HEAD kernel builds. + */ +void destroy_rcu_head_on_stack(struct rcu_head *head) +{ + debug_object_free(head, &rcuhead_debug_descr); +} +EXPORT_SYMBOL_GPL(destroy_rcu_head_on_stack); + +struct debug_obj_descr rcuhead_debug_descr = { + .name = "rcu_head", + .fixup_activate = rcuhead_fixup_activate, +}; +EXPORT_SYMBOL_GPL(rcuhead_debug_descr); +#endif /* #ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD */ + +#if defined(CONFIG_TREE_RCU) || defined(CONFIG_TREE_PREEMPT_RCU) || defined(CONFIG_RCU_TRACE) +void do_trace_rcu_torture_read(const char *rcutorturename, struct rcu_head *rhp, + unsigned long secs, + unsigned long c_old, unsigned long c) +{ + trace_rcu_torture_read(rcutorturename, rhp, secs, c_old, c); +} +EXPORT_SYMBOL_GPL(do_trace_rcu_torture_read); +#else +#define do_trace_rcu_torture_read(rcutorturename, rhp, secs, c_old, c) \ + do { } while (0) +#endif + +#ifdef CONFIG_RCU_STALL_COMMON + +#ifdef CONFIG_PROVE_RCU +#define RCU_STALL_DELAY_DELTA (5 * HZ) +#else +#define RCU_STALL_DELAY_DELTA 0 +#endif + +int rcu_cpu_stall_suppress __read_mostly; /* 1 = suppress stall warnings. */ +static int rcu_cpu_stall_timeout __read_mostly = CONFIG_RCU_CPU_STALL_TIMEOUT; + +module_param(rcu_cpu_stall_suppress, int, 0644); +module_param(rcu_cpu_stall_timeout, int, 0644); + +int rcu_jiffies_till_stall_check(void) +{ + int till_stall_check = ACCESS_ONCE(rcu_cpu_stall_timeout); + + /* + * Limit check must be consistent with the Kconfig limits + * for CONFIG_RCU_CPU_STALL_TIMEOUT. + */ + if (till_stall_check < 3) { + ACCESS_ONCE(rcu_cpu_stall_timeout) = 3; + till_stall_check = 3; + } else if (till_stall_check > 300) { + ACCESS_ONCE(rcu_cpu_stall_timeout) = 300; + till_stall_check = 300; + } + return till_stall_check * HZ + RCU_STALL_DELAY_DELTA; +} + +static int rcu_panic(struct notifier_block *this, unsigned long ev, void *ptr) +{ + rcu_cpu_stall_suppress = 1; + return NOTIFY_DONE; +} + +static struct notifier_block rcu_panic_block = { + .notifier_call = rcu_panic, +}; + +static int __init check_cpu_stall_init(void) +{ + atomic_notifier_chain_register(&panic_notifier_list, &rcu_panic_block); + return 0; +} +early_initcall(check_cpu_stall_init); + +#endif /* #ifdef CONFIG_RCU_STALL_COMMON */ diff --git a/kernel/rcupdate.c b/kernel/rcupdate.c deleted file mode 100644 index c07af1c..0000000 --- a/kernel/rcupdate.c +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Read-Copy Update mechanism for mutual exclusion - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Copyright IBM Corporation, 2001 - * - * Authors: Dipankar Sarma - * Manfred Spraul - * - * Based on the original work by Paul McKenney - * and inputs from Rusty Russell, Andrea Arcangeli and Andi Kleen. - * Papers: - * http://www.rdrop.com/users/paulmck/paper/rclockpdcsproof.pdf - * http://lse.sourceforge.net/locking/rclock_OLS.2001.05.01c.sc.pdf (OLS2001) - * - * For detailed explanation of Read-Copy Update mechanism see - - * http://lse.sourceforge.net/locking/rcupdate.html - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define CREATE_TRACE_POINTS -#include - -#include "rcu.h" - -module_param(rcu_expedited, int, 0); - -#ifdef CONFIG_PREEMPT_RCU - -/* - * Preemptible RCU implementation for rcu_read_lock(). - * Just increment ->rcu_read_lock_nesting, shared state will be updated - * if we block. - */ -void __rcu_read_lock(void) -{ - current->rcu_read_lock_nesting++; - barrier(); /* critical section after entry code. */ -} -EXPORT_SYMBOL_GPL(__rcu_read_lock); - -/* - * Preemptible RCU implementation for rcu_read_unlock(). - * Decrement ->rcu_read_lock_nesting. If the result is zero (outermost - * rcu_read_unlock()) and ->rcu_read_unlock_special is non-zero, then - * invoke rcu_read_unlock_special() to clean up after a context switch - * in an RCU read-side critical section and other special cases. - */ -void __rcu_read_unlock(void) -{ - struct task_struct *t = current; - - if (t->rcu_read_lock_nesting != 1) { - --t->rcu_read_lock_nesting; - } else { - barrier(); /* critical section before exit code. */ - t->rcu_read_lock_nesting = INT_MIN; -#ifdef CONFIG_PROVE_RCU_DELAY - udelay(10); /* Make preemption more probable. */ -#endif /* #ifdef CONFIG_PROVE_RCU_DELAY */ - barrier(); /* assign before ->rcu_read_unlock_special load */ - if (unlikely(ACCESS_ONCE(t->rcu_read_unlock_special))) - rcu_read_unlock_special(t); - barrier(); /* ->rcu_read_unlock_special load before assign */ - t->rcu_read_lock_nesting = 0; - } -#ifdef CONFIG_PROVE_LOCKING - { - int rrln = ACCESS_ONCE(t->rcu_read_lock_nesting); - - WARN_ON_ONCE(rrln < 0 && rrln > INT_MIN / 2); - } -#endif /* #ifdef CONFIG_PROVE_LOCKING */ -} -EXPORT_SYMBOL_GPL(__rcu_read_unlock); - -#endif /* #ifdef CONFIG_PREEMPT_RCU */ - -#ifdef CONFIG_DEBUG_LOCK_ALLOC -static struct lock_class_key rcu_lock_key; -struct lockdep_map rcu_lock_map = - STATIC_LOCKDEP_MAP_INIT("rcu_read_lock", &rcu_lock_key); -EXPORT_SYMBOL_GPL(rcu_lock_map); - -static struct lock_class_key rcu_bh_lock_key; -struct lockdep_map rcu_bh_lock_map = - STATIC_LOCKDEP_MAP_INIT("rcu_read_lock_bh", &rcu_bh_lock_key); -EXPORT_SYMBOL_GPL(rcu_bh_lock_map); - -static struct lock_class_key rcu_sched_lock_key; -struct lockdep_map rcu_sched_lock_map = - STATIC_LOCKDEP_MAP_INIT("rcu_read_lock_sched", &rcu_sched_lock_key); -EXPORT_SYMBOL_GPL(rcu_sched_lock_map); - -int notrace debug_lockdep_rcu_enabled(void) -{ - return rcu_scheduler_active && debug_locks && - current->lockdep_recursion == 0; -} -EXPORT_SYMBOL_GPL(debug_lockdep_rcu_enabled); - -/** - * rcu_read_lock_bh_held() - might we be in RCU-bh read-side critical section? - * - * Check for bottom half being disabled, which covers both the - * CONFIG_PROVE_RCU and not cases. Note that if someone uses - * rcu_read_lock_bh(), but then later enables BH, lockdep (if enabled) - * will show the situation. This is useful for debug checks in functions - * that require that they be called within an RCU read-side critical - * section. - * - * Check debug_lockdep_rcu_enabled() to prevent false positives during boot. - * - * Note that rcu_read_lock() is disallowed if the CPU is either idle or - * offline from an RCU perspective, so check for those as well. - */ -int rcu_read_lock_bh_held(void) -{ - if (!debug_lockdep_rcu_enabled()) - return 1; - if (!rcu_is_watching()) - return 0; - if (!rcu_lockdep_current_cpu_online()) - return 0; - return in_softirq() || irqs_disabled(); -} -EXPORT_SYMBOL_GPL(rcu_read_lock_bh_held); - -#endif /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ - -struct rcu_synchronize { - struct rcu_head head; - struct completion completion; -}; - -/* - * Awaken the corresponding synchronize_rcu() instance now that a - * grace period has elapsed. - */ -static void wakeme_after_rcu(struct rcu_head *head) -{ - struct rcu_synchronize *rcu; - - rcu = container_of(head, struct rcu_synchronize, head); - complete(&rcu->completion); -} - -void wait_rcu_gp(call_rcu_func_t crf) -{ - struct rcu_synchronize rcu; - - init_rcu_head_on_stack(&rcu.head); - init_completion(&rcu.completion); - /* Will wake me after RCU finished. */ - crf(&rcu.head, wakeme_after_rcu); - /* Wait for it. */ - wait_for_completion(&rcu.completion); - destroy_rcu_head_on_stack(&rcu.head); -} -EXPORT_SYMBOL_GPL(wait_rcu_gp); - -#ifdef CONFIG_PROVE_RCU -/* - * wrapper function to avoid #include problems. - */ -int rcu_my_thread_group_empty(void) -{ - return thread_group_empty(current); -} -EXPORT_SYMBOL_GPL(rcu_my_thread_group_empty); -#endif /* #ifdef CONFIG_PROVE_RCU */ - -#ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD -static inline void debug_init_rcu_head(struct rcu_head *head) -{ - debug_object_init(head, &rcuhead_debug_descr); -} - -static inline void debug_rcu_head_free(struct rcu_head *head) -{ - debug_object_free(head, &rcuhead_debug_descr); -} - -/* - * fixup_activate is called when: - * - an active object is activated - * - an unknown object is activated (might be a statically initialized object) - * Activation is performed internally by call_rcu(). - */ -static int rcuhead_fixup_activate(void *addr, enum debug_obj_state state) -{ - struct rcu_head *head = addr; - - switch (state) { - - case ODEBUG_STATE_NOTAVAILABLE: - /* - * This is not really a fixup. We just make sure that it is - * tracked in the object tracker. - */ - debug_object_init(head, &rcuhead_debug_descr); - debug_object_activate(head, &rcuhead_debug_descr); - return 0; - default: - return 1; - } -} - -/** - * init_rcu_head_on_stack() - initialize on-stack rcu_head for debugobjects - * @head: pointer to rcu_head structure to be initialized - * - * This function informs debugobjects of a new rcu_head structure that - * has been allocated as an auto variable on the stack. This function - * is not required for rcu_head structures that are statically defined or - * that are dynamically allocated on the heap. This function has no - * effect for !CONFIG_DEBUG_OBJECTS_RCU_HEAD kernel builds. - */ -void init_rcu_head_on_stack(struct rcu_head *head) -{ - debug_object_init_on_stack(head, &rcuhead_debug_descr); -} -EXPORT_SYMBOL_GPL(init_rcu_head_on_stack); - -/** - * destroy_rcu_head_on_stack() - destroy on-stack rcu_head for debugobjects - * @head: pointer to rcu_head structure to be initialized - * - * This function informs debugobjects that an on-stack rcu_head structure - * is about to go out of scope. As with init_rcu_head_on_stack(), this - * function is not required for rcu_head structures that are statically - * defined or that are dynamically allocated on the heap. Also as with - * init_rcu_head_on_stack(), this function has no effect for - * !CONFIG_DEBUG_OBJECTS_RCU_HEAD kernel builds. - */ -void destroy_rcu_head_on_stack(struct rcu_head *head) -{ - debug_object_free(head, &rcuhead_debug_descr); -} -EXPORT_SYMBOL_GPL(destroy_rcu_head_on_stack); - -struct debug_obj_descr rcuhead_debug_descr = { - .name = "rcu_head", - .fixup_activate = rcuhead_fixup_activate, -}; -EXPORT_SYMBOL_GPL(rcuhead_debug_descr); -#endif /* #ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD */ - -#if defined(CONFIG_TREE_RCU) || defined(CONFIG_TREE_PREEMPT_RCU) || defined(CONFIG_RCU_TRACE) -void do_trace_rcu_torture_read(const char *rcutorturename, struct rcu_head *rhp, - unsigned long secs, - unsigned long c_old, unsigned long c) -{ - trace_rcu_torture_read(rcutorturename, rhp, secs, c_old, c); -} -EXPORT_SYMBOL_GPL(do_trace_rcu_torture_read); -#else -#define do_trace_rcu_torture_read(rcutorturename, rhp, secs, c_old, c) \ - do { } while (0) -#endif - -#ifdef CONFIG_RCU_STALL_COMMON - -#ifdef CONFIG_PROVE_RCU -#define RCU_STALL_DELAY_DELTA (5 * HZ) -#else -#define RCU_STALL_DELAY_DELTA 0 -#endif - -int rcu_cpu_stall_suppress __read_mostly; /* 1 = suppress stall warnings. */ -static int rcu_cpu_stall_timeout __read_mostly = CONFIG_RCU_CPU_STALL_TIMEOUT; - -module_param(rcu_cpu_stall_suppress, int, 0644); -module_param(rcu_cpu_stall_timeout, int, 0644); - -int rcu_jiffies_till_stall_check(void) -{ - int till_stall_check = ACCESS_ONCE(rcu_cpu_stall_timeout); - - /* - * Limit check must be consistent with the Kconfig limits - * for CONFIG_RCU_CPU_STALL_TIMEOUT. - */ - if (till_stall_check < 3) { - ACCESS_ONCE(rcu_cpu_stall_timeout) = 3; - till_stall_check = 3; - } else if (till_stall_check > 300) { - ACCESS_ONCE(rcu_cpu_stall_timeout) = 300; - till_stall_check = 300; - } - return till_stall_check * HZ + RCU_STALL_DELAY_DELTA; -} - -static int rcu_panic(struct notifier_block *this, unsigned long ev, void *ptr) -{ - rcu_cpu_stall_suppress = 1; - return NOTIFY_DONE; -} - -static struct notifier_block rcu_panic_block = { - .notifier_call = rcu_panic, -}; - -static int __init check_cpu_stall_init(void) -{ - atomic_notifier_chain_register(&panic_notifier_list, &rcu_panic_block); - return 0; -} -early_initcall(check_cpu_stall_init); - -#endif /* #ifdef CONFIG_RCU_STALL_COMMON */ diff --git a/kernel/rcutiny.c b/kernel/rcutiny.c deleted file mode 100644 index 312e9709..0000000 --- a/kernel/rcutiny.c +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Read-Copy Update mechanism for mutual exclusion, the Bloatwatch edition. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Copyright IBM Corporation, 2008 - * - * Author: Paul E. McKenney - * - * For detailed explanation of Read-Copy Update mechanism see - - * Documentation/RCU - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CONFIG_RCU_TRACE -#include -#endif /* #else #ifdef CONFIG_RCU_TRACE */ - -#include "rcu.h" - -/* Forward declarations for rcutiny_plugin.h. */ -struct rcu_ctrlblk; -static void __rcu_process_callbacks(struct rcu_ctrlblk *rcp); -static void rcu_process_callbacks(struct softirq_action *unused); -static void __call_rcu(struct rcu_head *head, - void (*func)(struct rcu_head *rcu), - struct rcu_ctrlblk *rcp); - -static long long rcu_dynticks_nesting = DYNTICK_TASK_EXIT_IDLE; - -#include "rcutiny_plugin.h" - -/* Common code for rcu_idle_enter() and rcu_irq_exit(), see kernel/rcutree.c. */ -static void rcu_idle_enter_common(long long newval) -{ - if (newval) { - RCU_TRACE(trace_rcu_dyntick(TPS("--="), - rcu_dynticks_nesting, newval)); - rcu_dynticks_nesting = newval; - return; - } - RCU_TRACE(trace_rcu_dyntick(TPS("Start"), - rcu_dynticks_nesting, newval)); - if (!is_idle_task(current)) { - struct task_struct *idle = idle_task(smp_processor_id()); - - RCU_TRACE(trace_rcu_dyntick(TPS("Entry error: not idle task"), - rcu_dynticks_nesting, newval)); - ftrace_dump(DUMP_ALL); - WARN_ONCE(1, "Current pid: %d comm: %s / Idle pid: %d comm: %s", - current->pid, current->comm, - idle->pid, idle->comm); /* must be idle task! */ - } - rcu_sched_qs(0); /* implies rcu_bh_qsctr_inc(0) */ - barrier(); - rcu_dynticks_nesting = newval; -} - -/* - * Enter idle, which is an extended quiescent state if we have fully - * entered that mode (i.e., if the new value of dynticks_nesting is zero). - */ -void rcu_idle_enter(void) -{ - unsigned long flags; - long long newval; - - local_irq_save(flags); - WARN_ON_ONCE((rcu_dynticks_nesting & DYNTICK_TASK_NEST_MASK) == 0); - if ((rcu_dynticks_nesting & DYNTICK_TASK_NEST_MASK) == - DYNTICK_TASK_NEST_VALUE) - newval = 0; - else - newval = rcu_dynticks_nesting - DYNTICK_TASK_NEST_VALUE; - rcu_idle_enter_common(newval); - local_irq_restore(flags); -} -EXPORT_SYMBOL_GPL(rcu_idle_enter); - -/* - * Exit an interrupt handler towards idle. - */ -void rcu_irq_exit(void) -{ - unsigned long flags; - long long newval; - - local_irq_save(flags); - newval = rcu_dynticks_nesting - 1; - WARN_ON_ONCE(newval < 0); - rcu_idle_enter_common(newval); - local_irq_restore(flags); -} -EXPORT_SYMBOL_GPL(rcu_irq_exit); - -/* Common code for rcu_idle_exit() and rcu_irq_enter(), see kernel/rcutree.c. */ -static void rcu_idle_exit_common(long long oldval) -{ - if (oldval) { - RCU_TRACE(trace_rcu_dyntick(TPS("++="), - oldval, rcu_dynticks_nesting)); - return; - } - RCU_TRACE(trace_rcu_dyntick(TPS("End"), oldval, rcu_dynticks_nesting)); - if (!is_idle_task(current)) { - struct task_struct *idle = idle_task(smp_processor_id()); - - RCU_TRACE(trace_rcu_dyntick(TPS("Exit error: not idle task"), - oldval, rcu_dynticks_nesting)); - ftrace_dump(DUMP_ALL); - WARN_ONCE(1, "Current pid: %d comm: %s / Idle pid: %d comm: %s", - current->pid, current->comm, - idle->pid, idle->comm); /* must be idle task! */ - } -} - -/* - * Exit idle, so that we are no longer in an extended quiescent state. - */ -void rcu_idle_exit(void) -{ - unsigned long flags; - long long oldval; - - local_irq_save(flags); - oldval = rcu_dynticks_nesting; - WARN_ON_ONCE(rcu_dynticks_nesting < 0); - if (rcu_dynticks_nesting & DYNTICK_TASK_NEST_MASK) - rcu_dynticks_nesting += DYNTICK_TASK_NEST_VALUE; - else - rcu_dynticks_nesting = DYNTICK_TASK_EXIT_IDLE; - rcu_idle_exit_common(oldval); - local_irq_restore(flags); -} -EXPORT_SYMBOL_GPL(rcu_idle_exit); - -/* - * Enter an interrupt handler, moving away from idle. - */ -void rcu_irq_enter(void) -{ - unsigned long flags; - long long oldval; - - local_irq_save(flags); - oldval = rcu_dynticks_nesting; - rcu_dynticks_nesting++; - WARN_ON_ONCE(rcu_dynticks_nesting == 0); - rcu_idle_exit_common(oldval); - local_irq_restore(flags); -} -EXPORT_SYMBOL_GPL(rcu_irq_enter); - -#if defined(CONFIG_DEBUG_LOCK_ALLOC) || defined(CONFIG_RCU_TRACE) - -/* - * Test whether RCU thinks that the current CPU is idle. - */ -bool __rcu_is_watching(void) -{ - return rcu_dynticks_nesting; -} -EXPORT_SYMBOL(__rcu_is_watching); - -#endif /* defined(CONFIG_DEBUG_LOCK_ALLOC) || defined(CONFIG_RCU_TRACE) */ - -/* - * Test whether the current CPU was interrupted from idle. Nested - * interrupts don't count, we must be running at the first interrupt - * level. - */ -static int rcu_is_cpu_rrupt_from_idle(void) -{ - return rcu_dynticks_nesting <= 1; -} - -/* - * Helper function for rcu_sched_qs() and rcu_bh_qs(). - * Also irqs are disabled to avoid confusion due to interrupt handlers - * invoking call_rcu(). - */ -static int rcu_qsctr_help(struct rcu_ctrlblk *rcp) -{ - RCU_TRACE(reset_cpu_stall_ticks(rcp)); - if (rcp->rcucblist != NULL && - rcp->donetail != rcp->curtail) { - rcp->donetail = rcp->curtail; - return 1; - } - - return 0; -} - -/* - * Record an rcu quiescent state. And an rcu_bh quiescent state while we - * are at it, given that any rcu quiescent state is also an rcu_bh - * quiescent state. Use "+" instead of "||" to defeat short circuiting. - */ -void rcu_sched_qs(int cpu) -{ - unsigned long flags; - - local_irq_save(flags); - if (rcu_qsctr_help(&rcu_sched_ctrlblk) + - rcu_qsctr_help(&rcu_bh_ctrlblk)) - raise_softirq(RCU_SOFTIRQ); - local_irq_restore(flags); -} - -/* - * Record an rcu_bh quiescent state. - */ -void rcu_bh_qs(int cpu) -{ - unsigned long flags; - - local_irq_save(flags); - if (rcu_qsctr_help(&rcu_bh_ctrlblk)) - raise_softirq(RCU_SOFTIRQ); - local_irq_restore(flags); -} - -/* - * Check to see if the scheduling-clock interrupt came from an extended - * quiescent state, and, if so, tell RCU about it. This function must - * be called from hardirq context. It is normally called from the - * scheduling-clock interrupt. - */ -void rcu_check_callbacks(int cpu, int user) -{ - RCU_TRACE(check_cpu_stalls()); - if (user || rcu_is_cpu_rrupt_from_idle()) - rcu_sched_qs(cpu); - else if (!in_softirq()) - rcu_bh_qs(cpu); -} - -/* - * Invoke the RCU callbacks on the specified rcu_ctrlkblk structure - * whose grace period has elapsed. - */ -static void __rcu_process_callbacks(struct rcu_ctrlblk *rcp) -{ - const char *rn = NULL; - struct rcu_head *next, *list; - unsigned long flags; - RCU_TRACE(int cb_count = 0); - - /* If no RCU callbacks ready to invoke, just return. */ - if (&rcp->rcucblist == rcp->donetail) { - RCU_TRACE(trace_rcu_batch_start(rcp->name, 0, 0, -1)); - RCU_TRACE(trace_rcu_batch_end(rcp->name, 0, - !!ACCESS_ONCE(rcp->rcucblist), - need_resched(), - is_idle_task(current), - false)); - return; - } - - /* Move the ready-to-invoke callbacks to a local list. */ - local_irq_save(flags); - RCU_TRACE(trace_rcu_batch_start(rcp->name, 0, rcp->qlen, -1)); - list = rcp->rcucblist; - rcp->rcucblist = *rcp->donetail; - *rcp->donetail = NULL; - if (rcp->curtail == rcp->donetail) - rcp->curtail = &rcp->rcucblist; - rcp->donetail = &rcp->rcucblist; - local_irq_restore(flags); - - /* Invoke the callbacks on the local list. */ - RCU_TRACE(rn = rcp->name); - while (list) { - next = list->next; - prefetch(next); - debug_rcu_head_unqueue(list); - local_bh_disable(); - __rcu_reclaim(rn, list); - local_bh_enable(); - list = next; - RCU_TRACE(cb_count++); - } - RCU_TRACE(rcu_trace_sub_qlen(rcp, cb_count)); - RCU_TRACE(trace_rcu_batch_end(rcp->name, - cb_count, 0, need_resched(), - is_idle_task(current), - false)); -} - -static void rcu_process_callbacks(struct softirq_action *unused) -{ - __rcu_process_callbacks(&rcu_sched_ctrlblk); - __rcu_process_callbacks(&rcu_bh_ctrlblk); -} - -/* - * Wait for a grace period to elapse. But it is illegal to invoke - * synchronize_sched() from within an RCU read-side critical section. - * Therefore, any legal call to synchronize_sched() is a quiescent - * state, and so on a UP system, synchronize_sched() need do nothing. - * Ditto for synchronize_rcu_bh(). (But Lai Jiangshan points out the - * benefits of doing might_sleep() to reduce latency.) - * - * Cool, huh? (Due to Josh Triplett.) - * - * But we want to make this a static inline later. The cond_resched() - * currently makes this problematic. - */ -void synchronize_sched(void) -{ - rcu_lockdep_assert(!lock_is_held(&rcu_bh_lock_map) && - !lock_is_held(&rcu_lock_map) && - !lock_is_held(&rcu_sched_lock_map), - "Illegal synchronize_sched() in RCU read-side critical section"); - cond_resched(); -} -EXPORT_SYMBOL_GPL(synchronize_sched); - -/* - * Helper function for call_rcu() and call_rcu_bh(). - */ -static void __call_rcu(struct rcu_head *head, - void (*func)(struct rcu_head *rcu), - struct rcu_ctrlblk *rcp) -{ - unsigned long flags; - - debug_rcu_head_queue(head); - head->func = func; - head->next = NULL; - - local_irq_save(flags); - *rcp->curtail = head; - rcp->curtail = &head->next; - RCU_TRACE(rcp->qlen++); - local_irq_restore(flags); -} - -/* - * Post an RCU callback to be invoked after the end of an RCU-sched grace - * period. But since we have but one CPU, that would be after any - * quiescent state. - */ -void call_rcu_sched(struct rcu_head *head, void (*func)(struct rcu_head *rcu)) -{ - __call_rcu(head, func, &rcu_sched_ctrlblk); -} -EXPORT_SYMBOL_GPL(call_rcu_sched); - -/* - * Post an RCU bottom-half callback to be invoked after any subsequent - * quiescent state. - */ -void call_rcu_bh(struct rcu_head *head, void (*func)(struct rcu_head *rcu)) -{ - __call_rcu(head, func, &rcu_bh_ctrlblk); -} -EXPORT_SYMBOL_GPL(call_rcu_bh); - -void rcu_init(void) -{ - open_softirq(RCU_SOFTIRQ, rcu_process_callbacks); -} diff --git a/kernel/rcutiny_plugin.h b/kernel/rcutiny_plugin.h deleted file mode 100644 index 280d06c..0000000 --- a/kernel/rcutiny_plugin.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Read-Copy Update mechanism for mutual exclusion, the Bloatwatch edition - * Internal non-public definitions that provide either classic - * or preemptible semantics. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Copyright (c) 2010 Linaro - * - * Author: Paul E. McKenney - */ - -#include -#include -#include -#include - -/* Global control variables for rcupdate callback mechanism. */ -struct rcu_ctrlblk { - struct rcu_head *rcucblist; /* List of pending callbacks (CBs). */ - struct rcu_head **donetail; /* ->next pointer of last "done" CB. */ - struct rcu_head **curtail; /* ->next pointer of last CB. */ - RCU_TRACE(long qlen); /* Number of pending CBs. */ - RCU_TRACE(unsigned long gp_start); /* Start time for stalls. */ - RCU_TRACE(unsigned long ticks_this_gp); /* Statistic for stalls. */ - RCU_TRACE(unsigned long jiffies_stall); /* Jiffies at next stall. */ - RCU_TRACE(const char *name); /* Name of RCU type. */ -}; - -/* Definition for rcupdate control block. */ -static struct rcu_ctrlblk rcu_sched_ctrlblk = { - .donetail = &rcu_sched_ctrlblk.rcucblist, - .curtail = &rcu_sched_ctrlblk.rcucblist, - RCU_TRACE(.name = "rcu_sched") -}; - -static struct rcu_ctrlblk rcu_bh_ctrlblk = { - .donetail = &rcu_bh_ctrlblk.rcucblist, - .curtail = &rcu_bh_ctrlblk.rcucblist, - RCU_TRACE(.name = "rcu_bh") -}; - -#ifdef CONFIG_DEBUG_LOCK_ALLOC -#include - -int rcu_scheduler_active __read_mostly; -EXPORT_SYMBOL_GPL(rcu_scheduler_active); - -/* - * During boot, we forgive RCU lockdep issues. After this function is - * invoked, we start taking RCU lockdep issues seriously. - */ -void __init rcu_scheduler_starting(void) -{ - WARN_ON(nr_context_switches() > 0); - rcu_scheduler_active = 1; -} - -#endif /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ - -#ifdef CONFIG_RCU_TRACE - -static void rcu_trace_sub_qlen(struct rcu_ctrlblk *rcp, int n) -{ - unsigned long flags; - - local_irq_save(flags); - rcp->qlen -= n; - local_irq_restore(flags); -} - -/* - * Dump statistics for TINY_RCU, such as they are. - */ -static int show_tiny_stats(struct seq_file *m, void *unused) -{ - seq_printf(m, "rcu_sched: qlen: %ld\n", rcu_sched_ctrlblk.qlen); - seq_printf(m, "rcu_bh: qlen: %ld\n", rcu_bh_ctrlblk.qlen); - return 0; -} - -static int show_tiny_stats_open(struct inode *inode, struct file *file) -{ - return single_open(file, show_tiny_stats, NULL); -} - -static const struct file_operations show_tiny_stats_fops = { - .owner = THIS_MODULE, - .open = show_tiny_stats_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static struct dentry *rcudir; - -static int __init rcutiny_trace_init(void) -{ - struct dentry *retval; - - rcudir = debugfs_create_dir("rcu", NULL); - if (!rcudir) - goto free_out; - retval = debugfs_create_file("rcudata", 0444, rcudir, - NULL, &show_tiny_stats_fops); - if (!retval) - goto free_out; - return 0; -free_out: - debugfs_remove_recursive(rcudir); - return 1; -} - -static void __exit rcutiny_trace_cleanup(void) -{ - debugfs_remove_recursive(rcudir); -} - -module_init(rcutiny_trace_init); -module_exit(rcutiny_trace_cleanup); - -MODULE_AUTHOR("Paul E. McKenney"); -MODULE_DESCRIPTION("Read-Copy Update tracing for tiny implementation"); -MODULE_LICENSE("GPL"); - -static void check_cpu_stall(struct rcu_ctrlblk *rcp) -{ - unsigned long j; - unsigned long js; - - if (rcu_cpu_stall_suppress) - return; - rcp->ticks_this_gp++; - j = jiffies; - js = rcp->jiffies_stall; - if (*rcp->curtail && ULONG_CMP_GE(j, js)) { - pr_err("INFO: %s stall on CPU (%lu ticks this GP) idle=%llx (t=%lu jiffies q=%ld)\n", - rcp->name, rcp->ticks_this_gp, rcu_dynticks_nesting, - jiffies - rcp->gp_start, rcp->qlen); - dump_stack(); - } - if (*rcp->curtail && ULONG_CMP_GE(j, js)) - rcp->jiffies_stall = jiffies + - 3 * rcu_jiffies_till_stall_check() + 3; - else if (ULONG_CMP_GE(j, js)) - rcp->jiffies_stall = jiffies + rcu_jiffies_till_stall_check(); -} - -static void reset_cpu_stall_ticks(struct rcu_ctrlblk *rcp) -{ - rcp->ticks_this_gp = 0; - rcp->gp_start = jiffies; - rcp->jiffies_stall = jiffies + rcu_jiffies_till_stall_check(); -} - -static void check_cpu_stalls(void) -{ - RCU_TRACE(check_cpu_stall(&rcu_bh_ctrlblk)); - RCU_TRACE(check_cpu_stall(&rcu_sched_ctrlblk)); -} - -#endif /* #ifdef CONFIG_RCU_TRACE */ diff --git a/kernel/rcutorture.c b/kernel/rcutorture.c deleted file mode 100644 index be63101..0000000 --- a/kernel/rcutorture.c +++ /dev/null @@ -1,2139 +0,0 @@ -/* - * Read-Copy Update module-based torture test facility - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Copyright (C) IBM Corporation, 2005, 2006 - * - * Authors: Paul E. McKenney - * Josh Triplett - * - * See also: Documentation/RCU/torture.txt - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Paul E. McKenney and Josh Triplett "); - -static int fqs_duration; -module_param(fqs_duration, int, 0444); -MODULE_PARM_DESC(fqs_duration, "Duration of fqs bursts (us), 0 to disable"); -static int fqs_holdoff; -module_param(fqs_holdoff, int, 0444); -MODULE_PARM_DESC(fqs_holdoff, "Holdoff time within fqs bursts (us)"); -static int fqs_stutter = 3; -module_param(fqs_stutter, int, 0444); -MODULE_PARM_DESC(fqs_stutter, "Wait time between fqs bursts (s)"); -static bool gp_exp; -module_param(gp_exp, bool, 0444); -MODULE_PARM_DESC(gp_exp, "Use expedited GP wait primitives"); -static bool gp_normal; -module_param(gp_normal, bool, 0444); -MODULE_PARM_DESC(gp_normal, "Use normal (non-expedited) GP wait primitives"); -static int irqreader = 1; -module_param(irqreader, int, 0444); -MODULE_PARM_DESC(irqreader, "Allow RCU readers from irq handlers"); -static int n_barrier_cbs; -module_param(n_barrier_cbs, int, 0444); -MODULE_PARM_DESC(n_barrier_cbs, "# of callbacks/kthreads for barrier testing"); -static int nfakewriters = 4; -module_param(nfakewriters, int, 0444); -MODULE_PARM_DESC(nfakewriters, "Number of RCU fake writer threads"); -static int nreaders = -1; -module_param(nreaders, int, 0444); -MODULE_PARM_DESC(nreaders, "Number of RCU reader threads"); -static int object_debug; -module_param(object_debug, int, 0444); -MODULE_PARM_DESC(object_debug, "Enable debug-object double call_rcu() testing"); -static int onoff_holdoff; -module_param(onoff_holdoff, int, 0444); -MODULE_PARM_DESC(onoff_holdoff, "Time after boot before CPU hotplugs (s)"); -static int onoff_interval; -module_param(onoff_interval, int, 0444); -MODULE_PARM_DESC(onoff_interval, "Time between CPU hotplugs (s), 0=disable"); -static int shuffle_interval = 3; -module_param(shuffle_interval, int, 0444); -MODULE_PARM_DESC(shuffle_interval, "Number of seconds between shuffles"); -static int shutdown_secs; -module_param(shutdown_secs, int, 0444); -MODULE_PARM_DESC(shutdown_secs, "Shutdown time (s), <= zero to disable."); -static int stall_cpu; -module_param(stall_cpu, int, 0444); -MODULE_PARM_DESC(stall_cpu, "Stall duration (s), zero to disable."); -static int stall_cpu_holdoff = 10; -module_param(stall_cpu_holdoff, int, 0444); -MODULE_PARM_DESC(stall_cpu_holdoff, "Time to wait before starting stall (s)."); -static int stat_interval = 60; -module_param(stat_interval, int, 0644); -MODULE_PARM_DESC(stat_interval, "Number of seconds between stats printk()s"); -static int stutter = 5; -module_param(stutter, int, 0444); -MODULE_PARM_DESC(stutter, "Number of seconds to run/halt test"); -static int test_boost = 1; -module_param(test_boost, int, 0444); -MODULE_PARM_DESC(test_boost, "Test RCU prio boost: 0=no, 1=maybe, 2=yes."); -static int test_boost_duration = 4; -module_param(test_boost_duration, int, 0444); -MODULE_PARM_DESC(test_boost_duration, "Duration of each boost test, seconds."); -static int test_boost_interval = 7; -module_param(test_boost_interval, int, 0444); -MODULE_PARM_DESC(test_boost_interval, "Interval between boost tests, seconds."); -static bool test_no_idle_hz = true; -module_param(test_no_idle_hz, bool, 0444); -MODULE_PARM_DESC(test_no_idle_hz, "Test support for tickless idle CPUs"); -static char *torture_type = "rcu"; -module_param(torture_type, charp, 0444); -MODULE_PARM_DESC(torture_type, "Type of RCU to torture (rcu, rcu_bh, ...)"); -static bool verbose; -module_param(verbose, bool, 0444); -MODULE_PARM_DESC(verbose, "Enable verbose debugging printk()s"); - -#define TORTURE_FLAG "-torture:" -#define PRINTK_STRING(s) \ - do { pr_alert("%s" TORTURE_FLAG s "\n", torture_type); } while (0) -#define VERBOSE_PRINTK_STRING(s) \ - do { if (verbose) pr_alert("%s" TORTURE_FLAG s "\n", torture_type); } while (0) -#define VERBOSE_PRINTK_ERRSTRING(s) \ - do { if (verbose) pr_alert("%s" TORTURE_FLAG "!!! " s "\n", torture_type); } while (0) - -static char printk_buf[4096]; - -static int nrealreaders; -static struct task_struct *writer_task; -static struct task_struct **fakewriter_tasks; -static struct task_struct **reader_tasks; -static struct task_struct *stats_task; -static struct task_struct *shuffler_task; -static struct task_struct *stutter_task; -static struct task_struct *fqs_task; -static struct task_struct *boost_tasks[NR_CPUS]; -static struct task_struct *shutdown_task; -#ifdef CONFIG_HOTPLUG_CPU -static struct task_struct *onoff_task; -#endif /* #ifdef CONFIG_HOTPLUG_CPU */ -static struct task_struct *stall_task; -static struct task_struct **barrier_cbs_tasks; -static struct task_struct *barrier_task; - -#define RCU_TORTURE_PIPE_LEN 10 - -struct rcu_torture { - struct rcu_head rtort_rcu; - int rtort_pipe_count; - struct list_head rtort_free; - int rtort_mbtest; -}; - -static LIST_HEAD(rcu_torture_freelist); -static struct rcu_torture __rcu *rcu_torture_current; -static unsigned long rcu_torture_current_version; -static struct rcu_torture rcu_tortures[10 * RCU_TORTURE_PIPE_LEN]; -static DEFINE_SPINLOCK(rcu_torture_lock); -static DEFINE_PER_CPU(long [RCU_TORTURE_PIPE_LEN + 1], rcu_torture_count) = - { 0 }; -static DEFINE_PER_CPU(long [RCU_TORTURE_PIPE_LEN + 1], rcu_torture_batch) = - { 0 }; -static atomic_t rcu_torture_wcount[RCU_TORTURE_PIPE_LEN + 1]; -static atomic_t n_rcu_torture_alloc; -static atomic_t n_rcu_torture_alloc_fail; -static atomic_t n_rcu_torture_free; -static atomic_t n_rcu_torture_mberror; -static atomic_t n_rcu_torture_error; -static long n_rcu_torture_barrier_error; -static long n_rcu_torture_boost_ktrerror; -static long n_rcu_torture_boost_rterror; -static long n_rcu_torture_boost_failure; -static long n_rcu_torture_boosts; -static long n_rcu_torture_timers; -static long n_offline_attempts; -static long n_offline_successes; -static unsigned long sum_offline; -static int min_offline = -1; -static int max_offline; -static long n_online_attempts; -static long n_online_successes; -static unsigned long sum_online; -static int min_online = -1; -static int max_online; -static long n_barrier_attempts; -static long n_barrier_successes; -static struct list_head rcu_torture_removed; -static cpumask_var_t shuffle_tmp_mask; - -static int stutter_pause_test; - -#if defined(MODULE) || defined(CONFIG_RCU_TORTURE_TEST_RUNNABLE) -#define RCUTORTURE_RUNNABLE_INIT 1 -#else -#define RCUTORTURE_RUNNABLE_INIT 0 -#endif -int rcutorture_runnable = RCUTORTURE_RUNNABLE_INIT; -module_param(rcutorture_runnable, int, 0444); -MODULE_PARM_DESC(rcutorture_runnable, "Start rcutorture at boot"); - -#if defined(CONFIG_RCU_BOOST) && !defined(CONFIG_HOTPLUG_CPU) -#define rcu_can_boost() 1 -#else /* #if defined(CONFIG_RCU_BOOST) && !defined(CONFIG_HOTPLUG_CPU) */ -#define rcu_can_boost() 0 -#endif /* #else #if defined(CONFIG_RCU_BOOST) && !defined(CONFIG_HOTPLUG_CPU) */ - -#ifdef CONFIG_RCU_TRACE -static u64 notrace rcu_trace_clock_local(void) -{ - u64 ts = trace_clock_local(); - unsigned long __maybe_unused ts_rem = do_div(ts, NSEC_PER_USEC); - return ts; -} -#else /* #ifdef CONFIG_RCU_TRACE */ -static u64 notrace rcu_trace_clock_local(void) -{ - return 0ULL; -} -#endif /* #else #ifdef CONFIG_RCU_TRACE */ - -static unsigned long shutdown_time; /* jiffies to system shutdown. */ -static unsigned long boost_starttime; /* jiffies of next boost test start. */ -DEFINE_MUTEX(boost_mutex); /* protect setting boost_starttime */ - /* and boost task create/destroy. */ -static atomic_t barrier_cbs_count; /* Barrier callbacks registered. */ -static bool barrier_phase; /* Test phase. */ -static atomic_t barrier_cbs_invoked; /* Barrier callbacks invoked. */ -static wait_queue_head_t *barrier_cbs_wq; /* Coordinate barrier testing. */ -static DECLARE_WAIT_QUEUE_HEAD(barrier_wq); - -/* Mediate rmmod and system shutdown. Concurrent rmmod & shutdown illegal! */ - -#define FULLSTOP_DONTSTOP 0 /* Normal operation. */ -#define FULLSTOP_SHUTDOWN 1 /* System shutdown with rcutorture running. */ -#define FULLSTOP_RMMOD 2 /* Normal rmmod of rcutorture. */ -static int fullstop = FULLSTOP_RMMOD; -/* - * Protect fullstop transitions and spawning of kthreads. - */ -static DEFINE_MUTEX(fullstop_mutex); - -/* Forward reference. */ -static void rcu_torture_cleanup(void); - -/* - * Detect and respond to a system shutdown. - */ -static int -rcutorture_shutdown_notify(struct notifier_block *unused1, - unsigned long unused2, void *unused3) -{ - mutex_lock(&fullstop_mutex); - if (fullstop == FULLSTOP_DONTSTOP) - fullstop = FULLSTOP_SHUTDOWN; - else - pr_warn(/* but going down anyway, so... */ - "Concurrent 'rmmod rcutorture' and shutdown illegal!\n"); - mutex_unlock(&fullstop_mutex); - return NOTIFY_DONE; -} - -/* - * Absorb kthreads into a kernel function that won't return, so that - * they won't ever access module text or data again. - */ -static void rcutorture_shutdown_absorb(const char *title) -{ - if (ACCESS_ONCE(fullstop) == FULLSTOP_SHUTDOWN) { - pr_notice( - "rcutorture thread %s parking due to system shutdown\n", - title); - schedule_timeout_uninterruptible(MAX_SCHEDULE_TIMEOUT); - } -} - -/* - * Allocate an element from the rcu_tortures pool. - */ -static struct rcu_torture * -rcu_torture_alloc(void) -{ - struct list_head *p; - - spin_lock_bh(&rcu_torture_lock); - if (list_empty(&rcu_torture_freelist)) { - atomic_inc(&n_rcu_torture_alloc_fail); - spin_unlock_bh(&rcu_torture_lock); - return NULL; - } - atomic_inc(&n_rcu_torture_alloc); - p = rcu_torture_freelist.next; - list_del_init(p); - spin_unlock_bh(&rcu_torture_lock); - return container_of(p, struct rcu_torture, rtort_free); -} - -/* - * Free an element to the rcu_tortures pool. - */ -static void -rcu_torture_free(struct rcu_torture *p) -{ - atomic_inc(&n_rcu_torture_free); - spin_lock_bh(&rcu_torture_lock); - list_add_tail(&p->rtort_free, &rcu_torture_freelist); - spin_unlock_bh(&rcu_torture_lock); -} - -struct rcu_random_state { - unsigned long rrs_state; - long rrs_count; -}; - -#define RCU_RANDOM_MULT 39916801 /* prime */ -#define RCU_RANDOM_ADD 479001701 /* prime */ -#define RCU_RANDOM_REFRESH 10000 - -#define DEFINE_RCU_RANDOM(name) struct rcu_random_state name = { 0, 0 } - -/* - * Crude but fast random-number generator. Uses a linear congruential - * generator, with occasional help from cpu_clock(). - */ -static unsigned long -rcu_random(struct rcu_random_state *rrsp) -{ - if (--rrsp->rrs_count < 0) { - rrsp->rrs_state += (unsigned long)local_clock(); - rrsp->rrs_count = RCU_RANDOM_REFRESH; - } - rrsp->rrs_state = rrsp->rrs_state * RCU_RANDOM_MULT + RCU_RANDOM_ADD; - return swahw32(rrsp->rrs_state); -} - -static void -rcu_stutter_wait(const char *title) -{ - while (stutter_pause_test || !rcutorture_runnable) { - if (rcutorture_runnable) - schedule_timeout_interruptible(1); - else - schedule_timeout_interruptible(round_jiffies_relative(HZ)); - rcutorture_shutdown_absorb(title); - } -} - -/* - * Operations vector for selecting different types of tests. - */ - -struct rcu_torture_ops { - void (*init)(void); - int (*readlock)(void); - void (*read_delay)(struct rcu_random_state *rrsp); - void (*readunlock)(int idx); - int (*completed)(void); - void (*deferred_free)(struct rcu_torture *p); - void (*sync)(void); - void (*exp_sync)(void); - void (*call)(struct rcu_head *head, void (*func)(struct rcu_head *rcu)); - void (*cb_barrier)(void); - void (*fqs)(void); - int (*stats)(char *page); - int irq_capable; - int can_boost; - const char *name; -}; - -static struct rcu_torture_ops *cur_ops; - -/* - * Definitions for rcu torture testing. - */ - -static int rcu_torture_read_lock(void) __acquires(RCU) -{ - rcu_read_lock(); - return 0; -} - -static void rcu_read_delay(struct rcu_random_state *rrsp) -{ - const unsigned long shortdelay_us = 200; - const unsigned long longdelay_ms = 50; - - /* We want a short delay sometimes to make a reader delay the grace - * period, and we want a long delay occasionally to trigger - * force_quiescent_state. */ - - if (!(rcu_random(rrsp) % (nrealreaders * 2000 * longdelay_ms))) - mdelay(longdelay_ms); - if (!(rcu_random(rrsp) % (nrealreaders * 2 * shortdelay_us))) - udelay(shortdelay_us); -#ifdef CONFIG_PREEMPT - if (!preempt_count() && !(rcu_random(rrsp) % (nrealreaders * 20000))) - preempt_schedule(); /* No QS if preempt_disable() in effect */ -#endif -} - -static void rcu_torture_read_unlock(int idx) __releases(RCU) -{ - rcu_read_unlock(); -} - -static int rcu_torture_completed(void) -{ - return rcu_batches_completed(); -} - -static void -rcu_torture_cb(struct rcu_head *p) -{ - int i; - struct rcu_torture *rp = container_of(p, struct rcu_torture, rtort_rcu); - - if (fullstop != FULLSTOP_DONTSTOP) { - /* Test is ending, just drop callbacks on the floor. */ - /* The next initialization will pick up the pieces. */ - return; - } - i = rp->rtort_pipe_count; - if (i > RCU_TORTURE_PIPE_LEN) - i = RCU_TORTURE_PIPE_LEN; - atomic_inc(&rcu_torture_wcount[i]); - if (++rp->rtort_pipe_count >= RCU_TORTURE_PIPE_LEN) { - rp->rtort_mbtest = 0; - rcu_torture_free(rp); - } else { - cur_ops->deferred_free(rp); - } -} - -static int rcu_no_completed(void) -{ - return 0; -} - -static void rcu_torture_deferred_free(struct rcu_torture *p) -{ - call_rcu(&p->rtort_rcu, rcu_torture_cb); -} - -static void rcu_sync_torture_init(void) -{ - INIT_LIST_HEAD(&rcu_torture_removed); -} - -static struct rcu_torture_ops rcu_ops = { - .init = rcu_sync_torture_init, - .readlock = rcu_torture_read_lock, - .read_delay = rcu_read_delay, - .readunlock = rcu_torture_read_unlock, - .completed = rcu_torture_completed, - .deferred_free = rcu_torture_deferred_free, - .sync = synchronize_rcu, - .exp_sync = synchronize_rcu_expedited, - .call = call_rcu, - .cb_barrier = rcu_barrier, - .fqs = rcu_force_quiescent_state, - .stats = NULL, - .irq_capable = 1, - .can_boost = rcu_can_boost(), - .name = "rcu" -}; - -/* - * Definitions for rcu_bh torture testing. - */ - -static int rcu_bh_torture_read_lock(void) __acquires(RCU_BH) -{ - rcu_read_lock_bh(); - return 0; -} - -static void rcu_bh_torture_read_unlock(int idx) __releases(RCU_BH) -{ - rcu_read_unlock_bh(); -} - -static int rcu_bh_torture_completed(void) -{ - return rcu_batches_completed_bh(); -} - -static void rcu_bh_torture_deferred_free(struct rcu_torture *p) -{ - call_rcu_bh(&p->rtort_rcu, rcu_torture_cb); -} - -static struct rcu_torture_ops rcu_bh_ops = { - .init = rcu_sync_torture_init, - .readlock = rcu_bh_torture_read_lock, - .read_delay = rcu_read_delay, /* just reuse rcu's version. */ - .readunlock = rcu_bh_torture_read_unlock, - .completed = rcu_bh_torture_completed, - .deferred_free = rcu_bh_torture_deferred_free, - .sync = synchronize_rcu_bh, - .exp_sync = synchronize_rcu_bh_expedited, - .call = call_rcu_bh, - .cb_barrier = rcu_barrier_bh, - .fqs = rcu_bh_force_quiescent_state, - .stats = NULL, - .irq_capable = 1, - .name = "rcu_bh" -}; - -/* - * Definitions for srcu torture testing. - */ - -DEFINE_STATIC_SRCU(srcu_ctl); - -static int srcu_torture_read_lock(void) __acquires(&srcu_ctl) -{ - return srcu_read_lock(&srcu_ctl); -} - -static void srcu_read_delay(struct rcu_random_state *rrsp) -{ - long delay; - const long uspertick = 1000000 / HZ; - const long longdelay = 10; - - /* We want there to be long-running readers, but not all the time. */ - - delay = rcu_random(rrsp) % (nrealreaders * 2 * longdelay * uspertick); - if (!delay) - schedule_timeout_interruptible(longdelay); - else - rcu_read_delay(rrsp); -} - -static void srcu_torture_read_unlock(int idx) __releases(&srcu_ctl) -{ - srcu_read_unlock(&srcu_ctl, idx); -} - -static int srcu_torture_completed(void) -{ - return srcu_batches_completed(&srcu_ctl); -} - -static void srcu_torture_deferred_free(struct rcu_torture *rp) -{ - call_srcu(&srcu_ctl, &rp->rtort_rcu, rcu_torture_cb); -} - -static void srcu_torture_synchronize(void) -{ - synchronize_srcu(&srcu_ctl); -} - -static void srcu_torture_call(struct rcu_head *head, - void (*func)(struct rcu_head *head)) -{ - call_srcu(&srcu_ctl, head, func); -} - -static void srcu_torture_barrier(void) -{ - srcu_barrier(&srcu_ctl); -} - -static int srcu_torture_stats(char *page) -{ - int cnt = 0; - int cpu; - int idx = srcu_ctl.completed & 0x1; - - cnt += sprintf(&page[cnt], "%s%s per-CPU(idx=%d):", - torture_type, TORTURE_FLAG, idx); - for_each_possible_cpu(cpu) { - cnt += sprintf(&page[cnt], " %d(%lu,%lu)", cpu, - per_cpu_ptr(srcu_ctl.per_cpu_ref, cpu)->c[!idx], - per_cpu_ptr(srcu_ctl.per_cpu_ref, cpu)->c[idx]); - } - cnt += sprintf(&page[cnt], "\n"); - return cnt; -} - -static void srcu_torture_synchronize_expedited(void) -{ - synchronize_srcu_expedited(&srcu_ctl); -} - -static struct rcu_torture_ops srcu_ops = { - .init = rcu_sync_torture_init, - .readlock = srcu_torture_read_lock, - .read_delay = srcu_read_delay, - .readunlock = srcu_torture_read_unlock, - .completed = srcu_torture_completed, - .deferred_free = srcu_torture_deferred_free, - .sync = srcu_torture_synchronize, - .exp_sync = srcu_torture_synchronize_expedited, - .call = srcu_torture_call, - .cb_barrier = srcu_torture_barrier, - .stats = srcu_torture_stats, - .name = "srcu" -}; - -/* - * Definitions for sched torture testing. - */ - -static int sched_torture_read_lock(void) -{ - preempt_disable(); - return 0; -} - -static void sched_torture_read_unlock(int idx) -{ - preempt_enable(); -} - -static void rcu_sched_torture_deferred_free(struct rcu_torture *p) -{ - call_rcu_sched(&p->rtort_rcu, rcu_torture_cb); -} - -static struct rcu_torture_ops sched_ops = { - .init = rcu_sync_torture_init, - .readlock = sched_torture_read_lock, - .read_delay = rcu_read_delay, /* just reuse rcu's version. */ - .readunlock = sched_torture_read_unlock, - .completed = rcu_no_completed, - .deferred_free = rcu_sched_torture_deferred_free, - .sync = synchronize_sched, - .exp_sync = synchronize_sched_expedited, - .call = call_rcu_sched, - .cb_barrier = rcu_barrier_sched, - .fqs = rcu_sched_force_quiescent_state, - .stats = NULL, - .irq_capable = 1, - .name = "sched" -}; - -/* - * RCU torture priority-boost testing. Runs one real-time thread per - * CPU for moderate bursts, repeatedly registering RCU callbacks and - * spinning waiting for them to be invoked. If a given callback takes - * too long to be invoked, we assume that priority inversion has occurred. - */ - -struct rcu_boost_inflight { - struct rcu_head rcu; - int inflight; -}; - -static void rcu_torture_boost_cb(struct rcu_head *head) -{ - struct rcu_boost_inflight *rbip = - container_of(head, struct rcu_boost_inflight, rcu); - - smp_mb(); /* Ensure RCU-core accesses precede clearing ->inflight */ - rbip->inflight = 0; -} - -static int rcu_torture_boost(void *arg) -{ - unsigned long call_rcu_time; - unsigned long endtime; - unsigned long oldstarttime; - struct rcu_boost_inflight rbi = { .inflight = 0 }; - struct sched_param sp; - - VERBOSE_PRINTK_STRING("rcu_torture_boost started"); - - /* Set real-time priority. */ - sp.sched_priority = 1; - if (sched_setscheduler(current, SCHED_FIFO, &sp) < 0) { - VERBOSE_PRINTK_STRING("rcu_torture_boost RT prio failed!"); - n_rcu_torture_boost_rterror++; - } - - init_rcu_head_on_stack(&rbi.rcu); - /* Each pass through the following loop does one boost-test cycle. */ - do { - /* Wait for the next test interval. */ - oldstarttime = boost_starttime; - while (ULONG_CMP_LT(jiffies, oldstarttime)) { - schedule_timeout_interruptible(oldstarttime - jiffies); - rcu_stutter_wait("rcu_torture_boost"); - if (kthread_should_stop() || - fullstop != FULLSTOP_DONTSTOP) - goto checkwait; - } - - /* Do one boost-test interval. */ - endtime = oldstarttime + test_boost_duration * HZ; - call_rcu_time = jiffies; - while (ULONG_CMP_LT(jiffies, endtime)) { - /* If we don't have a callback in flight, post one. */ - if (!rbi.inflight) { - smp_mb(); /* RCU core before ->inflight = 1. */ - rbi.inflight = 1; - call_rcu(&rbi.rcu, rcu_torture_boost_cb); - if (jiffies - call_rcu_time > - test_boost_duration * HZ - HZ / 2) { - VERBOSE_PRINTK_STRING("rcu_torture_boost boosting failed"); - n_rcu_torture_boost_failure++; - } - call_rcu_time = jiffies; - } - cond_resched(); - rcu_stutter_wait("rcu_torture_boost"); - if (kthread_should_stop() || - fullstop != FULLSTOP_DONTSTOP) - goto checkwait; - } - - /* - * Set the start time of the next test interval. - * Yes, this is vulnerable to long delays, but such - * delays simply cause a false negative for the next - * interval. Besides, we are running at RT priority, - * so delays should be relatively rare. - */ - while (oldstarttime == boost_starttime && - !kthread_should_stop()) { - if (mutex_trylock(&boost_mutex)) { - boost_starttime = jiffies + - test_boost_interval * HZ; - n_rcu_torture_boosts++; - mutex_unlock(&boost_mutex); - break; - } - schedule_timeout_uninterruptible(1); - } - - /* Go do the stutter. */ -checkwait: rcu_stutter_wait("rcu_torture_boost"); - } while (!kthread_should_stop() && fullstop == FULLSTOP_DONTSTOP); - - /* Clean up and exit. */ - VERBOSE_PRINTK_STRING("rcu_torture_boost task stopping"); - rcutorture_shutdown_absorb("rcu_torture_boost"); - while (!kthread_should_stop() || rbi.inflight) - schedule_timeout_uninterruptible(1); - smp_mb(); /* order accesses to ->inflight before stack-frame death. */ - destroy_rcu_head_on_stack(&rbi.rcu); - return 0; -} - -/* - * RCU torture force-quiescent-state kthread. Repeatedly induces - * bursts of calls to force_quiescent_state(), increasing the probability - * of occurrence of some important types of race conditions. - */ -static int -rcu_torture_fqs(void *arg) -{ - unsigned long fqs_resume_time; - int fqs_burst_remaining; - - VERBOSE_PRINTK_STRING("rcu_torture_fqs task started"); - do { - fqs_resume_time = jiffies + fqs_stutter * HZ; - while (ULONG_CMP_LT(jiffies, fqs_resume_time) && - !kthread_should_stop()) { - schedule_timeout_interruptible(1); - } - fqs_burst_remaining = fqs_duration; - while (fqs_burst_remaining > 0 && - !kthread_should_stop()) { - cur_ops->fqs(); - udelay(fqs_holdoff); - fqs_burst_remaining -= fqs_holdoff; - } - rcu_stutter_wait("rcu_torture_fqs"); - } while (!kthread_should_stop() && fullstop == FULLSTOP_DONTSTOP); - VERBOSE_PRINTK_STRING("rcu_torture_fqs task stopping"); - rcutorture_shutdown_absorb("rcu_torture_fqs"); - while (!kthread_should_stop()) - schedule_timeout_uninterruptible(1); - return 0; -} - -/* - * RCU torture writer kthread. Repeatedly substitutes a new structure - * for that pointed to by rcu_torture_current, freeing the old structure - * after a series of grace periods (the "pipeline"). - */ -static int -rcu_torture_writer(void *arg) -{ - bool exp; - int i; - struct rcu_torture *rp; - struct rcu_torture *rp1; - struct rcu_torture *old_rp; - static DEFINE_RCU_RANDOM(rand); - - VERBOSE_PRINTK_STRING("rcu_torture_writer task started"); - set_user_nice(current, 19); - - do { - schedule_timeout_uninterruptible(1); - rp = rcu_torture_alloc(); - if (rp == NULL) - continue; - rp->rtort_pipe_count = 0; - udelay(rcu_random(&rand) & 0x3ff); - old_rp = rcu_dereference_check(rcu_torture_current, - current == writer_task); - rp->rtort_mbtest = 1; - rcu_assign_pointer(rcu_torture_current, rp); - smp_wmb(); /* Mods to old_rp must follow rcu_assign_pointer() */ - if (old_rp) { - i = old_rp->rtort_pipe_count; - if (i > RCU_TORTURE_PIPE_LEN) - i = RCU_TORTURE_PIPE_LEN; - atomic_inc(&rcu_torture_wcount[i]); - old_rp->rtort_pipe_count++; - if (gp_normal == gp_exp) - exp = !!(rcu_random(&rand) & 0x80); - else - exp = gp_exp; - if (!exp) { - cur_ops->deferred_free(old_rp); - } else { - cur_ops->exp_sync(); - list_add(&old_rp->rtort_free, - &rcu_torture_removed); - list_for_each_entry_safe(rp, rp1, - &rcu_torture_removed, - rtort_free) { - i = rp->rtort_pipe_count; - if (i > RCU_TORTURE_PIPE_LEN) - i = RCU_TORTURE_PIPE_LEN; - atomic_inc(&rcu_torture_wcount[i]); - if (++rp->rtort_pipe_count >= - RCU_TORTURE_PIPE_LEN) { - rp->rtort_mbtest = 0; - list_del(&rp->rtort_free); - rcu_torture_free(rp); - } - } - } - } - rcutorture_record_progress(++rcu_torture_current_version); - rcu_stutter_wait("rcu_torture_writer"); - } while (!kthread_should_stop() && fullstop == FULLSTOP_DONTSTOP); - VERBOSE_PRINTK_STRING("rcu_torture_writer task stopping"); - rcutorture_shutdown_absorb("rcu_torture_writer"); - while (!kthread_should_stop()) - schedule_timeout_uninterruptible(1); - return 0; -} - -/* - * RCU torture fake writer kthread. Repeatedly calls sync, with a random - * delay between calls. - */ -static int -rcu_torture_fakewriter(void *arg) -{ - DEFINE_RCU_RANDOM(rand); - - VERBOSE_PRINTK_STRING("rcu_torture_fakewriter task started"); - set_user_nice(current, 19); - - do { - schedule_timeout_uninterruptible(1 + rcu_random(&rand)%10); - udelay(rcu_random(&rand) & 0x3ff); - if (cur_ops->cb_barrier != NULL && - rcu_random(&rand) % (nfakewriters * 8) == 0) { - cur_ops->cb_barrier(); - } else if (gp_normal == gp_exp) { - if (rcu_random(&rand) & 0x80) - cur_ops->sync(); - else - cur_ops->exp_sync(); - } else if (gp_normal) { - cur_ops->sync(); - } else { - cur_ops->exp_sync(); - } - rcu_stutter_wait("rcu_torture_fakewriter"); - } while (!kthread_should_stop() && fullstop == FULLSTOP_DONTSTOP); - - VERBOSE_PRINTK_STRING("rcu_torture_fakewriter task stopping"); - rcutorture_shutdown_absorb("rcu_torture_fakewriter"); - while (!kthread_should_stop()) - schedule_timeout_uninterruptible(1); - return 0; -} - -void rcutorture_trace_dump(void) -{ - static atomic_t beenhere = ATOMIC_INIT(0); - - if (atomic_read(&beenhere)) - return; - if (atomic_xchg(&beenhere, 1) != 0) - return; - ftrace_dump(DUMP_ALL); -} - -/* - * RCU torture reader from timer handler. Dereferences rcu_torture_current, - * incrementing the corresponding element of the pipeline array. The - * counter in the element should never be greater than 1, otherwise, the - * RCU implementation is broken. - */ -static void rcu_torture_timer(unsigned long unused) -{ - int idx; - int completed; - int completed_end; - static DEFINE_RCU_RANDOM(rand); - static DEFINE_SPINLOCK(rand_lock); - struct rcu_torture *p; - int pipe_count; - unsigned long long ts; - - idx = cur_ops->readlock(); - completed = cur_ops->completed(); - ts = rcu_trace_clock_local(); - p = rcu_dereference_check(rcu_torture_current, - rcu_read_lock_bh_held() || - rcu_read_lock_sched_held() || - srcu_read_lock_held(&srcu_ctl)); - if (p == NULL) { - /* Leave because rcu_torture_writer is not yet underway */ - cur_ops->readunlock(idx); - return; - } - if (p->rtort_mbtest == 0) - atomic_inc(&n_rcu_torture_mberror); - spin_lock(&rand_lock); - cur_ops->read_delay(&rand); - n_rcu_torture_timers++; - spin_unlock(&rand_lock); - preempt_disable(); - pipe_count = p->rtort_pipe_count; - if (pipe_count > RCU_TORTURE_PIPE_LEN) { - /* Should not happen, but... */ - pipe_count = RCU_TORTURE_PIPE_LEN; - } - completed_end = cur_ops->completed(); - if (pipe_count > 1) { - do_trace_rcu_torture_read(cur_ops->name, &p->rtort_rcu, ts, - completed, completed_end); - rcutorture_trace_dump(); - } - __this_cpu_inc(rcu_torture_count[pipe_count]); - completed = completed_end - completed; - if (completed > RCU_TORTURE_PIPE_LEN) { - /* Should not happen, but... */ - completed = RCU_TORTURE_PIPE_LEN; - } - __this_cpu_inc(rcu_torture_batch[completed]); - preempt_enable(); - cur_ops->readunlock(idx); -} - -/* - * RCU torture reader kthread. Repeatedly dereferences rcu_torture_current, - * incrementing the corresponding element of the pipeline array. The - * counter in the element should never be greater than 1, otherwise, the - * RCU implementation is broken. - */ -static int -rcu_torture_reader(void *arg) -{ - int completed; - int completed_end; - int idx; - DEFINE_RCU_RANDOM(rand); - struct rcu_torture *p; - int pipe_count; - struct timer_list t; - unsigned long long ts; - - VERBOSE_PRINTK_STRING("rcu_torture_reader task started"); - set_user_nice(current, 19); - if (irqreader && cur_ops->irq_capable) - setup_timer_on_stack(&t, rcu_torture_timer, 0); - - do { - if (irqreader && cur_ops->irq_capable) { - if (!timer_pending(&t)) - mod_timer(&t, jiffies + 1); - } - idx = cur_ops->readlock(); - completed = cur_ops->completed(); - ts = rcu_trace_clock_local(); - p = rcu_dereference_check(rcu_torture_current, - rcu_read_lock_bh_held() || - rcu_read_lock_sched_held() || - srcu_read_lock_held(&srcu_ctl)); - if (p == NULL) { - /* Wait for rcu_torture_writer to get underway */ - cur_ops->readunlock(idx); - schedule_timeout_interruptible(HZ); - continue; - } - if (p->rtort_mbtest == 0) - atomic_inc(&n_rcu_torture_mberror); - cur_ops->read_delay(&rand); - preempt_disable(); - pipe_count = p->rtort_pipe_count; - if (pipe_count > RCU_TORTURE_PIPE_LEN) { - /* Should not happen, but... */ - pipe_count = RCU_TORTURE_PIPE_LEN; - } - completed_end = cur_ops->completed(); - if (pipe_count > 1) { - do_trace_rcu_torture_read(cur_ops->name, &p->rtort_rcu, - ts, completed, completed_end); - rcutorture_trace_dump(); - } - __this_cpu_inc(rcu_torture_count[pipe_count]); - completed = completed_end - completed; - if (completed > RCU_TORTURE_PIPE_LEN) { - /* Should not happen, but... */ - completed = RCU_TORTURE_PIPE_LEN; - } - __this_cpu_inc(rcu_torture_batch[completed]); - preempt_enable(); - cur_ops->readunlock(idx); - schedule(); - rcu_stutter_wait("rcu_torture_reader"); - } while (!kthread_should_stop() && fullstop == FULLSTOP_DONTSTOP); - VERBOSE_PRINTK_STRING("rcu_torture_reader task stopping"); - rcutorture_shutdown_absorb("rcu_torture_reader"); - if (irqreader && cur_ops->irq_capable) - del_timer_sync(&t); - while (!kthread_should_stop()) - schedule_timeout_uninterruptible(1); - return 0; -} - -/* - * Create an RCU-torture statistics message in the specified buffer. - */ -static int -rcu_torture_printk(char *page) -{ - int cnt = 0; - int cpu; - int i; - long pipesummary[RCU_TORTURE_PIPE_LEN + 1] = { 0 }; - long batchsummary[RCU_TORTURE_PIPE_LEN + 1] = { 0 }; - - for_each_possible_cpu(cpu) { - for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) { - pipesummary[i] += per_cpu(rcu_torture_count, cpu)[i]; - batchsummary[i] += per_cpu(rcu_torture_batch, cpu)[i]; - } - } - for (i = RCU_TORTURE_PIPE_LEN - 1; i >= 0; i--) { - if (pipesummary[i] != 0) - break; - } - cnt += sprintf(&page[cnt], "%s%s ", torture_type, TORTURE_FLAG); - cnt += sprintf(&page[cnt], - "rtc: %p ver: %lu tfle: %d rta: %d rtaf: %d rtf: %d ", - rcu_torture_current, - rcu_torture_current_version, - list_empty(&rcu_torture_freelist), - atomic_read(&n_rcu_torture_alloc), - atomic_read(&n_rcu_torture_alloc_fail), - atomic_read(&n_rcu_torture_free)); - cnt += sprintf(&page[cnt], "rtmbe: %d rtbke: %ld rtbre: %ld ", - atomic_read(&n_rcu_torture_mberror), - n_rcu_torture_boost_ktrerror, - n_rcu_torture_boost_rterror); - cnt += sprintf(&page[cnt], "rtbf: %ld rtb: %ld nt: %ld ", - n_rcu_torture_boost_failure, - n_rcu_torture_boosts, - n_rcu_torture_timers); - cnt += sprintf(&page[cnt], - "onoff: %ld/%ld:%ld/%ld %d,%d:%d,%d %lu:%lu (HZ=%d) ", - n_online_successes, n_online_attempts, - n_offline_successes, n_offline_attempts, - min_online, max_online, - min_offline, max_offline, - sum_online, sum_offline, HZ); - cnt += sprintf(&page[cnt], "barrier: %ld/%ld:%ld", - n_barrier_successes, - n_barrier_attempts, - n_rcu_torture_barrier_error); - cnt += sprintf(&page[cnt], "\n%s%s ", torture_type, TORTURE_FLAG); - if (atomic_read(&n_rcu_torture_mberror) != 0 || - n_rcu_torture_barrier_error != 0 || - n_rcu_torture_boost_ktrerror != 0 || - n_rcu_torture_boost_rterror != 0 || - n_rcu_torture_boost_failure != 0 || - i > 1) { - cnt += sprintf(&page[cnt], "!!! "); - atomic_inc(&n_rcu_torture_error); - WARN_ON_ONCE(1); - } - cnt += sprintf(&page[cnt], "Reader Pipe: "); - for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) - cnt += sprintf(&page[cnt], " %ld", pipesummary[i]); - cnt += sprintf(&page[cnt], "\n%s%s ", torture_type, TORTURE_FLAG); - cnt += sprintf(&page[cnt], "Reader Batch: "); - for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) - cnt += sprintf(&page[cnt], " %ld", batchsummary[i]); - cnt += sprintf(&page[cnt], "\n%s%s ", torture_type, TORTURE_FLAG); - cnt += sprintf(&page[cnt], "Free-Block Circulation: "); - for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) { - cnt += sprintf(&page[cnt], " %d", - atomic_read(&rcu_torture_wcount[i])); - } - cnt += sprintf(&page[cnt], "\n"); - if (cur_ops->stats) - cnt += cur_ops->stats(&page[cnt]); - return cnt; -} - -/* - * Print torture statistics. Caller must ensure that there is only - * one call to this function at a given time!!! This is normally - * accomplished by relying on the module system to only have one copy - * of the module loaded, and then by giving the rcu_torture_stats - * kthread full control (or the init/cleanup functions when rcu_torture_stats - * thread is not running). - */ -static void -rcu_torture_stats_print(void) -{ - int cnt; - - cnt = rcu_torture_printk(printk_buf); - pr_alert("%s", printk_buf); -} - -/* - * Periodically prints torture statistics, if periodic statistics printing - * was specified via the stat_interval module parameter. - * - * No need to worry about fullstop here, since this one doesn't reference - * volatile state or register callbacks. - */ -static int -rcu_torture_stats(void *arg) -{ - VERBOSE_PRINTK_STRING("rcu_torture_stats task started"); - do { - schedule_timeout_interruptible(stat_interval * HZ); - rcu_torture_stats_print(); - rcutorture_shutdown_absorb("rcu_torture_stats"); - } while (!kthread_should_stop()); - VERBOSE_PRINTK_STRING("rcu_torture_stats task stopping"); - return 0; -} - -static int rcu_idle_cpu; /* Force all torture tasks off this CPU */ - -/* Shuffle tasks such that we allow @rcu_idle_cpu to become idle. A special case - * is when @rcu_idle_cpu = -1, when we allow the tasks to run on all CPUs. - */ -static void rcu_torture_shuffle_tasks(void) -{ - int i; - - cpumask_setall(shuffle_tmp_mask); - get_online_cpus(); - - /* No point in shuffling if there is only one online CPU (ex: UP) */ - if (num_online_cpus() == 1) { - put_online_cpus(); - return; - } - - if (rcu_idle_cpu != -1) - cpumask_clear_cpu(rcu_idle_cpu, shuffle_tmp_mask); - - set_cpus_allowed_ptr(current, shuffle_tmp_mask); - - if (reader_tasks) { - for (i = 0; i < nrealreaders; i++) - if (reader_tasks[i]) - set_cpus_allowed_ptr(reader_tasks[i], - shuffle_tmp_mask); - } - if (fakewriter_tasks) { - for (i = 0; i < nfakewriters; i++) - if (fakewriter_tasks[i]) - set_cpus_allowed_ptr(fakewriter_tasks[i], - shuffle_tmp_mask); - } - if (writer_task) - set_cpus_allowed_ptr(writer_task, shuffle_tmp_mask); - if (stats_task) - set_cpus_allowed_ptr(stats_task, shuffle_tmp_mask); - if (stutter_task) - set_cpus_allowed_ptr(stutter_task, shuffle_tmp_mask); - if (fqs_task) - set_cpus_allowed_ptr(fqs_task, shuffle_tmp_mask); - if (shutdown_task) - set_cpus_allowed_ptr(shutdown_task, shuffle_tmp_mask); -#ifdef CONFIG_HOTPLUG_CPU - if (onoff_task) - set_cpus_allowed_ptr(onoff_task, shuffle_tmp_mask); -#endif /* #ifdef CONFIG_HOTPLUG_CPU */ - if (stall_task) - set_cpus_allowed_ptr(stall_task, shuffle_tmp_mask); - if (barrier_cbs_tasks) - for (i = 0; i < n_barrier_cbs; i++) - if (barrier_cbs_tasks[i]) - set_cpus_allowed_ptr(barrier_cbs_tasks[i], - shuffle_tmp_mask); - if (barrier_task) - set_cpus_allowed_ptr(barrier_task, shuffle_tmp_mask); - - if (rcu_idle_cpu == -1) - rcu_idle_cpu = num_online_cpus() - 1; - else - rcu_idle_cpu--; - - put_online_cpus(); -} - -/* Shuffle tasks across CPUs, with the intent of allowing each CPU in the - * system to become idle at a time and cut off its timer ticks. This is meant - * to test the support for such tickless idle CPU in RCU. - */ -static int -rcu_torture_shuffle(void *arg) -{ - VERBOSE_PRINTK_STRING("rcu_torture_shuffle task started"); - do { - schedule_timeout_interruptible(shuffle_interval * HZ); - rcu_torture_shuffle_tasks(); - rcutorture_shutdown_absorb("rcu_torture_shuffle"); - } while (!kthread_should_stop()); - VERBOSE_PRINTK_STRING("rcu_torture_shuffle task stopping"); - return 0; -} - -/* Cause the rcutorture test to "stutter", starting and stopping all - * threads periodically. - */ -static int -rcu_torture_stutter(void *arg) -{ - VERBOSE_PRINTK_STRING("rcu_torture_stutter task started"); - do { - schedule_timeout_interruptible(stutter * HZ); - stutter_pause_test = 1; - if (!kthread_should_stop()) - schedule_timeout_interruptible(stutter * HZ); - stutter_pause_test = 0; - rcutorture_shutdown_absorb("rcu_torture_stutter"); - } while (!kthread_should_stop()); - VERBOSE_PRINTK_STRING("rcu_torture_stutter task stopping"); - return 0; -} - -static inline void -rcu_torture_print_module_parms(struct rcu_torture_ops *cur_ops, const char *tag) -{ - pr_alert("%s" TORTURE_FLAG - "--- %s: nreaders=%d nfakewriters=%d " - "stat_interval=%d verbose=%d test_no_idle_hz=%d " - "shuffle_interval=%d stutter=%d irqreader=%d " - "fqs_duration=%d fqs_holdoff=%d fqs_stutter=%d " - "test_boost=%d/%d test_boost_interval=%d " - "test_boost_duration=%d shutdown_secs=%d " - "stall_cpu=%d stall_cpu_holdoff=%d " - "n_barrier_cbs=%d " - "onoff_interval=%d onoff_holdoff=%d\n", - torture_type, tag, nrealreaders, nfakewriters, - stat_interval, verbose, test_no_idle_hz, shuffle_interval, - stutter, irqreader, fqs_duration, fqs_holdoff, fqs_stutter, - test_boost, cur_ops->can_boost, - test_boost_interval, test_boost_duration, shutdown_secs, - stall_cpu, stall_cpu_holdoff, - n_barrier_cbs, - onoff_interval, onoff_holdoff); -} - -static struct notifier_block rcutorture_shutdown_nb = { - .notifier_call = rcutorture_shutdown_notify, -}; - -static void rcutorture_booster_cleanup(int cpu) -{ - struct task_struct *t; - - if (boost_tasks[cpu] == NULL) - return; - mutex_lock(&boost_mutex); - VERBOSE_PRINTK_STRING("Stopping rcu_torture_boost task"); - t = boost_tasks[cpu]; - boost_tasks[cpu] = NULL; - mutex_unlock(&boost_mutex); - - /* This must be outside of the mutex, otherwise deadlock! */ - kthread_stop(t); - boost_tasks[cpu] = NULL; -} - -static int rcutorture_booster_init(int cpu) -{ - int retval; - - if (boost_tasks[cpu] != NULL) - return 0; /* Already created, nothing more to do. */ - - /* Don't allow time recalculation while creating a new task. */ - mutex_lock(&boost_mutex); - VERBOSE_PRINTK_STRING("Creating rcu_torture_boost task"); - boost_tasks[cpu] = kthread_create_on_node(rcu_torture_boost, NULL, - cpu_to_node(cpu), - "rcu_torture_boost"); - if (IS_ERR(boost_tasks[cpu])) { - retval = PTR_ERR(boost_tasks[cpu]); - VERBOSE_PRINTK_STRING("rcu_torture_boost task create failed"); - n_rcu_torture_boost_ktrerror++; - boost_tasks[cpu] = NULL; - mutex_unlock(&boost_mutex); - return retval; - } - kthread_bind(boost_tasks[cpu], cpu); - wake_up_process(boost_tasks[cpu]); - mutex_unlock(&boost_mutex); - return 0; -} - -/* - * Cause the rcutorture test to shutdown the system after the test has - * run for the time specified by the shutdown_secs module parameter. - */ -static int -rcu_torture_shutdown(void *arg) -{ - long delta; - unsigned long jiffies_snap; - - VERBOSE_PRINTK_STRING("rcu_torture_shutdown task started"); - jiffies_snap = ACCESS_ONCE(jiffies); - while (ULONG_CMP_LT(jiffies_snap, shutdown_time) && - !kthread_should_stop()) { - delta = shutdown_time - jiffies_snap; - if (verbose) - pr_alert("%s" TORTURE_FLAG - "rcu_torture_shutdown task: %lu jiffies remaining\n", - torture_type, delta); - schedule_timeout_interruptible(delta); - jiffies_snap = ACCESS_ONCE(jiffies); - } - if (kthread_should_stop()) { - VERBOSE_PRINTK_STRING("rcu_torture_shutdown task stopping"); - return 0; - } - - /* OK, shut down the system. */ - - VERBOSE_PRINTK_STRING("rcu_torture_shutdown task shutting down system"); - shutdown_task = NULL; /* Avoid self-kill deadlock. */ - rcu_torture_cleanup(); /* Get the success/failure message. */ - kernel_power_off(); /* Shut down the system. */ - return 0; -} - -#ifdef CONFIG_HOTPLUG_CPU - -/* - * Execute random CPU-hotplug operations at the interval specified - * by the onoff_interval. - */ -static int -rcu_torture_onoff(void *arg) -{ - int cpu; - unsigned long delta; - int maxcpu = -1; - DEFINE_RCU_RANDOM(rand); - int ret; - unsigned long starttime; - - VERBOSE_PRINTK_STRING("rcu_torture_onoff task started"); - for_each_online_cpu(cpu) - maxcpu = cpu; - WARN_ON(maxcpu < 0); - if (onoff_holdoff > 0) { - VERBOSE_PRINTK_STRING("rcu_torture_onoff begin holdoff"); - schedule_timeout_interruptible(onoff_holdoff * HZ); - VERBOSE_PRINTK_STRING("rcu_torture_onoff end holdoff"); - } - while (!kthread_should_stop()) { - cpu = (rcu_random(&rand) >> 4) % (maxcpu + 1); - if (cpu_online(cpu) && cpu_is_hotpluggable(cpu)) { - if (verbose) - pr_alert("%s" TORTURE_FLAG - "rcu_torture_onoff task: offlining %d\n", - torture_type, cpu); - starttime = jiffies; - n_offline_attempts++; - ret = cpu_down(cpu); - if (ret) { - if (verbose) - pr_alert("%s" TORTURE_FLAG - "rcu_torture_onoff task: offline %d failed: errno %d\n", - torture_type, cpu, ret); - } else { - if (verbose) - pr_alert("%s" TORTURE_FLAG - "rcu_torture_onoff task: offlined %d\n", - torture_type, cpu); - n_offline_successes++; - delta = jiffies - starttime; - sum_offline += delta; - if (min_offline < 0) { - min_offline = delta; - max_offline = delta; - } - if (min_offline > delta) - min_offline = delta; - if (max_offline < delta) - max_offline = delta; - } - } else if (cpu_is_hotpluggable(cpu)) { - if (verbose) - pr_alert("%s" TORTURE_FLAG - "rcu_torture_onoff task: onlining %d\n", - torture_type, cpu); - starttime = jiffies; - n_online_attempts++; - ret = cpu_up(cpu); - if (ret) { - if (verbose) - pr_alert("%s" TORTURE_FLAG - "rcu_torture_onoff task: online %d failed: errno %d\n", - torture_type, cpu, ret); - } else { - if (verbose) - pr_alert("%s" TORTURE_FLAG - "rcu_torture_onoff task: onlined %d\n", - torture_type, cpu); - n_online_successes++; - delta = jiffies - starttime; - sum_online += delta; - if (min_online < 0) { - min_online = delta; - max_online = delta; - } - if (min_online > delta) - min_online = delta; - if (max_online < delta) - max_online = delta; - } - } - schedule_timeout_interruptible(onoff_interval * HZ); - } - VERBOSE_PRINTK_STRING("rcu_torture_onoff task stopping"); - return 0; -} - -static int -rcu_torture_onoff_init(void) -{ - int ret; - - if (onoff_interval <= 0) - return 0; - onoff_task = kthread_run(rcu_torture_onoff, NULL, "rcu_torture_onoff"); - if (IS_ERR(onoff_task)) { - ret = PTR_ERR(onoff_task); - onoff_task = NULL; - return ret; - } - return 0; -} - -static void rcu_torture_onoff_cleanup(void) -{ - if (onoff_task == NULL) - return; - VERBOSE_PRINTK_STRING("Stopping rcu_torture_onoff task"); - kthread_stop(onoff_task); - onoff_task = NULL; -} - -#else /* #ifdef CONFIG_HOTPLUG_CPU */ - -static int -rcu_torture_onoff_init(void) -{ - return 0; -} - -static void rcu_torture_onoff_cleanup(void) -{ -} - -#endif /* #else #ifdef CONFIG_HOTPLUG_CPU */ - -/* - * CPU-stall kthread. It waits as specified by stall_cpu_holdoff, then - * induces a CPU stall for the time specified by stall_cpu. - */ -static int rcu_torture_stall(void *args) -{ - unsigned long stop_at; - - VERBOSE_PRINTK_STRING("rcu_torture_stall task started"); - if (stall_cpu_holdoff > 0) { - VERBOSE_PRINTK_STRING("rcu_torture_stall begin holdoff"); - schedule_timeout_interruptible(stall_cpu_holdoff * HZ); - VERBOSE_PRINTK_STRING("rcu_torture_stall end holdoff"); - } - if (!kthread_should_stop()) { - stop_at = get_seconds() + stall_cpu; - /* RCU CPU stall is expected behavior in following code. */ - pr_alert("rcu_torture_stall start.\n"); - rcu_read_lock(); - preempt_disable(); - while (ULONG_CMP_LT(get_seconds(), stop_at)) - continue; /* Induce RCU CPU stall warning. */ - preempt_enable(); - rcu_read_unlock(); - pr_alert("rcu_torture_stall end.\n"); - } - rcutorture_shutdown_absorb("rcu_torture_stall"); - while (!kthread_should_stop()) - schedule_timeout_interruptible(10 * HZ); - return 0; -} - -/* Spawn CPU-stall kthread, if stall_cpu specified. */ -static int __init rcu_torture_stall_init(void) -{ - int ret; - - if (stall_cpu <= 0) - return 0; - stall_task = kthread_run(rcu_torture_stall, NULL, "rcu_torture_stall"); - if (IS_ERR(stall_task)) { - ret = PTR_ERR(stall_task); - stall_task = NULL; - return ret; - } - return 0; -} - -/* Clean up after the CPU-stall kthread, if one was spawned. */ -static void rcu_torture_stall_cleanup(void) -{ - if (stall_task == NULL) - return; - VERBOSE_PRINTK_STRING("Stopping rcu_torture_stall_task."); - kthread_stop(stall_task); - stall_task = NULL; -} - -/* Callback function for RCU barrier testing. */ -void rcu_torture_barrier_cbf(struct rcu_head *rcu) -{ - atomic_inc(&barrier_cbs_invoked); -} - -/* kthread function to register callbacks used to test RCU barriers. */ -static int rcu_torture_barrier_cbs(void *arg) -{ - long myid = (long)arg; - bool lastphase = 0; - struct rcu_head rcu; - - init_rcu_head_on_stack(&rcu); - VERBOSE_PRINTK_STRING("rcu_torture_barrier_cbs task started"); - set_user_nice(current, 19); - do { - wait_event(barrier_cbs_wq[myid], - barrier_phase != lastphase || - kthread_should_stop() || - fullstop != FULLSTOP_DONTSTOP); - lastphase = barrier_phase; - smp_mb(); /* ensure barrier_phase load before ->call(). */ - if (kthread_should_stop() || fullstop != FULLSTOP_DONTSTOP) - break; - cur_ops->call(&rcu, rcu_torture_barrier_cbf); - if (atomic_dec_and_test(&barrier_cbs_count)) - wake_up(&barrier_wq); - } while (!kthread_should_stop() && fullstop == FULLSTOP_DONTSTOP); - VERBOSE_PRINTK_STRING("rcu_torture_barrier_cbs task stopping"); - rcutorture_shutdown_absorb("rcu_torture_barrier_cbs"); - while (!kthread_should_stop()) - schedule_timeout_interruptible(1); - cur_ops->cb_barrier(); - destroy_rcu_head_on_stack(&rcu); - return 0; -} - -/* kthread function to drive and coordinate RCU barrier testing. */ -static int rcu_torture_barrier(void *arg) -{ - int i; - - VERBOSE_PRINTK_STRING("rcu_torture_barrier task starting"); - do { - atomic_set(&barrier_cbs_invoked, 0); - atomic_set(&barrier_cbs_count, n_barrier_cbs); - smp_mb(); /* Ensure barrier_phase after prior assignments. */ - barrier_phase = !barrier_phase; - for (i = 0; i < n_barrier_cbs; i++) - wake_up(&barrier_cbs_wq[i]); - wait_event(barrier_wq, - atomic_read(&barrier_cbs_count) == 0 || - kthread_should_stop() || - fullstop != FULLSTOP_DONTSTOP); - if (kthread_should_stop() || fullstop != FULLSTOP_DONTSTOP) - break; - n_barrier_attempts++; - cur_ops->cb_barrier(); - if (atomic_read(&barrier_cbs_invoked) != n_barrier_cbs) { - n_rcu_torture_barrier_error++; - WARN_ON_ONCE(1); - } - n_barrier_successes++; - schedule_timeout_interruptible(HZ / 10); - } while (!kthread_should_stop() && fullstop == FULLSTOP_DONTSTOP); - VERBOSE_PRINTK_STRING("rcu_torture_barrier task stopping"); - rcutorture_shutdown_absorb("rcu_torture_barrier"); - while (!kthread_should_stop()) - schedule_timeout_interruptible(1); - return 0; -} - -/* Initialize RCU barrier testing. */ -static int rcu_torture_barrier_init(void) -{ - int i; - int ret; - - if (n_barrier_cbs == 0) - return 0; - if (cur_ops->call == NULL || cur_ops->cb_barrier == NULL) { - pr_alert("%s" TORTURE_FLAG - " Call or barrier ops missing for %s,\n", - torture_type, cur_ops->name); - pr_alert("%s" TORTURE_FLAG - " RCU barrier testing omitted from run.\n", - torture_type); - return 0; - } - atomic_set(&barrier_cbs_count, 0); - atomic_set(&barrier_cbs_invoked, 0); - barrier_cbs_tasks = - kzalloc(n_barrier_cbs * sizeof(barrier_cbs_tasks[0]), - GFP_KERNEL); - barrier_cbs_wq = - kzalloc(n_barrier_cbs * sizeof(barrier_cbs_wq[0]), - GFP_KERNEL); - if (barrier_cbs_tasks == NULL || !barrier_cbs_wq) - return -ENOMEM; - for (i = 0; i < n_barrier_cbs; i++) { - init_waitqueue_head(&barrier_cbs_wq[i]); - barrier_cbs_tasks[i] = kthread_run(rcu_torture_barrier_cbs, - (void *)(long)i, - "rcu_torture_barrier_cbs"); - if (IS_ERR(barrier_cbs_tasks[i])) { - ret = PTR_ERR(barrier_cbs_tasks[i]); - VERBOSE_PRINTK_ERRSTRING("Failed to create rcu_torture_barrier_cbs"); - barrier_cbs_tasks[i] = NULL; - return ret; - } - } - barrier_task = kthread_run(rcu_torture_barrier, NULL, - "rcu_torture_barrier"); - if (IS_ERR(barrier_task)) { - ret = PTR_ERR(barrier_task); - VERBOSE_PRINTK_ERRSTRING("Failed to create rcu_torture_barrier"); - barrier_task = NULL; - } - return 0; -} - -/* Clean up after RCU barrier testing. */ -static void rcu_torture_barrier_cleanup(void) -{ - int i; - - if (barrier_task != NULL) { - VERBOSE_PRINTK_STRING("Stopping rcu_torture_barrier task"); - kthread_stop(barrier_task); - barrier_task = NULL; - } - if (barrier_cbs_tasks != NULL) { - for (i = 0; i < n_barrier_cbs; i++) { - if (barrier_cbs_tasks[i] != NULL) { - VERBOSE_PRINTK_STRING("Stopping rcu_torture_barrier_cbs task"); - kthread_stop(barrier_cbs_tasks[i]); - barrier_cbs_tasks[i] = NULL; - } - } - kfree(barrier_cbs_tasks); - barrier_cbs_tasks = NULL; - } - if (barrier_cbs_wq != NULL) { - kfree(barrier_cbs_wq); - barrier_cbs_wq = NULL; - } -} - -static int rcutorture_cpu_notify(struct notifier_block *self, - unsigned long action, void *hcpu) -{ - long cpu = (long)hcpu; - - switch (action) { - case CPU_ONLINE: - case CPU_DOWN_FAILED: - (void)rcutorture_booster_init(cpu); - break; - case CPU_DOWN_PREPARE: - rcutorture_booster_cleanup(cpu); - break; - default: - break; - } - return NOTIFY_OK; -} - -static struct notifier_block rcutorture_cpu_nb = { - .notifier_call = rcutorture_cpu_notify, -}; - -static void -rcu_torture_cleanup(void) -{ - int i; - - mutex_lock(&fullstop_mutex); - rcutorture_record_test_transition(); - if (fullstop == FULLSTOP_SHUTDOWN) { - pr_warn(/* but going down anyway, so... */ - "Concurrent 'rmmod rcutorture' and shutdown illegal!\n"); - mutex_unlock(&fullstop_mutex); - schedule_timeout_uninterruptible(10); - if (cur_ops->cb_barrier != NULL) - cur_ops->cb_barrier(); - return; - } - fullstop = FULLSTOP_RMMOD; - mutex_unlock(&fullstop_mutex); - unregister_reboot_notifier(&rcutorture_shutdown_nb); - rcu_torture_barrier_cleanup(); - rcu_torture_stall_cleanup(); - if (stutter_task) { - VERBOSE_PRINTK_STRING("Stopping rcu_torture_stutter task"); - kthread_stop(stutter_task); - } - stutter_task = NULL; - if (shuffler_task) { - VERBOSE_PRINTK_STRING("Stopping rcu_torture_shuffle task"); - kthread_stop(shuffler_task); - free_cpumask_var(shuffle_tmp_mask); - } - shuffler_task = NULL; - - if (writer_task) { - VERBOSE_PRINTK_STRING("Stopping rcu_torture_writer task"); - kthread_stop(writer_task); - } - writer_task = NULL; - - if (reader_tasks) { - for (i = 0; i < nrealreaders; i++) { - if (reader_tasks[i]) { - VERBOSE_PRINTK_STRING( - "Stopping rcu_torture_reader task"); - kthread_stop(reader_tasks[i]); - } - reader_tasks[i] = NULL; - } - kfree(reader_tasks); - reader_tasks = NULL; - } - rcu_torture_current = NULL; - - if (fakewriter_tasks) { - for (i = 0; i < nfakewriters; i++) { - if (fakewriter_tasks[i]) { - VERBOSE_PRINTK_STRING( - "Stopping rcu_torture_fakewriter task"); - kthread_stop(fakewriter_tasks[i]); - } - fakewriter_tasks[i] = NULL; - } - kfree(fakewriter_tasks); - fakewriter_tasks = NULL; - } - - if (stats_task) { - VERBOSE_PRINTK_STRING("Stopping rcu_torture_stats task"); - kthread_stop(stats_task); - } - stats_task = NULL; - - if (fqs_task) { - VERBOSE_PRINTK_STRING("Stopping rcu_torture_fqs task"); - kthread_stop(fqs_task); - } - fqs_task = NULL; - if ((test_boost == 1 && cur_ops->can_boost) || - test_boost == 2) { - unregister_cpu_notifier(&rcutorture_cpu_nb); - for_each_possible_cpu(i) - rcutorture_booster_cleanup(i); - } - if (shutdown_task != NULL) { - VERBOSE_PRINTK_STRING("Stopping rcu_torture_shutdown task"); - kthread_stop(shutdown_task); - } - shutdown_task = NULL; - rcu_torture_onoff_cleanup(); - - /* Wait for all RCU callbacks to fire. */ - - if (cur_ops->cb_barrier != NULL) - cur_ops->cb_barrier(); - - rcu_torture_stats_print(); /* -After- the stats thread is stopped! */ - - if (atomic_read(&n_rcu_torture_error) || n_rcu_torture_barrier_error) - rcu_torture_print_module_parms(cur_ops, "End of test: FAILURE"); - else if (n_online_successes != n_online_attempts || - n_offline_successes != n_offline_attempts) - rcu_torture_print_module_parms(cur_ops, - "End of test: RCU_HOTPLUG"); - else - rcu_torture_print_module_parms(cur_ops, "End of test: SUCCESS"); -} - -#ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD -static void rcu_torture_leak_cb(struct rcu_head *rhp) -{ -} - -static void rcu_torture_err_cb(struct rcu_head *rhp) -{ - /* - * This -might- happen due to race conditions, but is unlikely. - * The scenario that leads to this happening is that the - * first of the pair of duplicate callbacks is queued, - * someone else starts a grace period that includes that - * callback, then the second of the pair must wait for the - * next grace period. Unlikely, but can happen. If it - * does happen, the debug-objects subsystem won't have splatted. - */ - pr_alert("rcutorture: duplicated callback was invoked.\n"); -} -#endif /* #ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD */ - -/* - * Verify that double-free causes debug-objects to complain, but only - * if CONFIG_DEBUG_OBJECTS_RCU_HEAD=y. Otherwise, say that the test - * cannot be carried out. - */ -static void rcu_test_debug_objects(void) -{ -#ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD - struct rcu_head rh1; - struct rcu_head rh2; - - init_rcu_head_on_stack(&rh1); - init_rcu_head_on_stack(&rh2); - pr_alert("rcutorture: WARN: Duplicate call_rcu() test starting.\n"); - - /* Try to queue the rh2 pair of callbacks for the same grace period. */ - preempt_disable(); /* Prevent preemption from interrupting test. */ - rcu_read_lock(); /* Make it impossible to finish a grace period. */ - call_rcu(&rh1, rcu_torture_leak_cb); /* Start grace period. */ - local_irq_disable(); /* Make it harder to start a new grace period. */ - call_rcu(&rh2, rcu_torture_leak_cb); - call_rcu(&rh2, rcu_torture_err_cb); /* Duplicate callback. */ - local_irq_enable(); - rcu_read_unlock(); - preempt_enable(); - - /* Wait for them all to get done so we can safely return. */ - rcu_barrier(); - pr_alert("rcutorture: WARN: Duplicate call_rcu() test complete.\n"); - destroy_rcu_head_on_stack(&rh1); - destroy_rcu_head_on_stack(&rh2); -#else /* #ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD */ - pr_alert("rcutorture: !CONFIG_DEBUG_OBJECTS_RCU_HEAD, not testing duplicate call_rcu()\n"); -#endif /* #else #ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD */ -} - -static int __init -rcu_torture_init(void) -{ - int i; - int cpu; - int firsterr = 0; - int retval; - static struct rcu_torture_ops *torture_ops[] = { - &rcu_ops, &rcu_bh_ops, &srcu_ops, &sched_ops, - }; - - mutex_lock(&fullstop_mutex); - - /* Process args and tell the world that the torturer is on the job. */ - for (i = 0; i < ARRAY_SIZE(torture_ops); i++) { - cur_ops = torture_ops[i]; - if (strcmp(torture_type, cur_ops->name) == 0) - break; - } - if (i == ARRAY_SIZE(torture_ops)) { - pr_alert("rcu-torture: invalid torture type: \"%s\"\n", - torture_type); - pr_alert("rcu-torture types:"); - for (i = 0; i < ARRAY_SIZE(torture_ops); i++) - pr_alert(" %s", torture_ops[i]->name); - pr_alert("\n"); - mutex_unlock(&fullstop_mutex); - return -EINVAL; - } - if (cur_ops->fqs == NULL && fqs_duration != 0) { - pr_alert("rcu-torture: ->fqs NULL and non-zero fqs_duration, fqs disabled.\n"); - fqs_duration = 0; - } - if (cur_ops->init) - cur_ops->init(); /* no "goto unwind" prior to this point!!! */ - - if (nreaders >= 0) - nrealreaders = nreaders; - else - nrealreaders = 2 * num_online_cpus(); - rcu_torture_print_module_parms(cur_ops, "Start of test"); - fullstop = FULLSTOP_DONTSTOP; - - /* Set up the freelist. */ - - INIT_LIST_HEAD(&rcu_torture_freelist); - for (i = 0; i < ARRAY_SIZE(rcu_tortures); i++) { - rcu_tortures[i].rtort_mbtest = 0; - list_add_tail(&rcu_tortures[i].rtort_free, - &rcu_torture_freelist); - } - - /* Initialize the statistics so that each run gets its own numbers. */ - - rcu_torture_current = NULL; - rcu_torture_current_version = 0; - atomic_set(&n_rcu_torture_alloc, 0); - atomic_set(&n_rcu_torture_alloc_fail, 0); - atomic_set(&n_rcu_torture_free, 0); - atomic_set(&n_rcu_torture_mberror, 0); - atomic_set(&n_rcu_torture_error, 0); - n_rcu_torture_barrier_error = 0; - n_rcu_torture_boost_ktrerror = 0; - n_rcu_torture_boost_rterror = 0; - n_rcu_torture_boost_failure = 0; - n_rcu_torture_boosts = 0; - for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) - atomic_set(&rcu_torture_wcount[i], 0); - for_each_possible_cpu(cpu) { - for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) { - per_cpu(rcu_torture_count, cpu)[i] = 0; - per_cpu(rcu_torture_batch, cpu)[i] = 0; - } - } - - /* Start up the kthreads. */ - - VERBOSE_PRINTK_STRING("Creating rcu_torture_writer task"); - writer_task = kthread_create(rcu_torture_writer, NULL, - "rcu_torture_writer"); - if (IS_ERR(writer_task)) { - firsterr = PTR_ERR(writer_task); - VERBOSE_PRINTK_ERRSTRING("Failed to create writer"); - writer_task = NULL; - goto unwind; - } - wake_up_process(writer_task); - fakewriter_tasks = kzalloc(nfakewriters * sizeof(fakewriter_tasks[0]), - GFP_KERNEL); - if (fakewriter_tasks == NULL) { - VERBOSE_PRINTK_ERRSTRING("out of memory"); - firsterr = -ENOMEM; - goto unwind; - } - for (i = 0; i < nfakewriters; i++) { - VERBOSE_PRINTK_STRING("Creating rcu_torture_fakewriter task"); - fakewriter_tasks[i] = kthread_run(rcu_torture_fakewriter, NULL, - "rcu_torture_fakewriter"); - if (IS_ERR(fakewriter_tasks[i])) { - firsterr = PTR_ERR(fakewriter_tasks[i]); - VERBOSE_PRINTK_ERRSTRING("Failed to create fakewriter"); - fakewriter_tasks[i] = NULL; - goto unwind; - } - } - reader_tasks = kzalloc(nrealreaders * sizeof(reader_tasks[0]), - GFP_KERNEL); - if (reader_tasks == NULL) { - VERBOSE_PRINTK_ERRSTRING("out of memory"); - firsterr = -ENOMEM; - goto unwind; - } - for (i = 0; i < nrealreaders; i++) { - VERBOSE_PRINTK_STRING("Creating rcu_torture_reader task"); - reader_tasks[i] = kthread_run(rcu_torture_reader, NULL, - "rcu_torture_reader"); - if (IS_ERR(reader_tasks[i])) { - firsterr = PTR_ERR(reader_tasks[i]); - VERBOSE_PRINTK_ERRSTRING("Failed to create reader"); - reader_tasks[i] = NULL; - goto unwind; - } - } - if (stat_interval > 0) { - VERBOSE_PRINTK_STRING("Creating rcu_torture_stats task"); - stats_task = kthread_run(rcu_torture_stats, NULL, - "rcu_torture_stats"); - if (IS_ERR(stats_task)) { - firsterr = PTR_ERR(stats_task); - VERBOSE_PRINTK_ERRSTRING("Failed to create stats"); - stats_task = NULL; - goto unwind; - } - } - if (test_no_idle_hz) { - rcu_idle_cpu = num_online_cpus() - 1; - - if (!alloc_cpumask_var(&shuffle_tmp_mask, GFP_KERNEL)) { - firsterr = -ENOMEM; - VERBOSE_PRINTK_ERRSTRING("Failed to alloc mask"); - goto unwind; - } - - /* Create the shuffler thread */ - shuffler_task = kthread_run(rcu_torture_shuffle, NULL, - "rcu_torture_shuffle"); - if (IS_ERR(shuffler_task)) { - free_cpumask_var(shuffle_tmp_mask); - firsterr = PTR_ERR(shuffler_task); - VERBOSE_PRINTK_ERRSTRING("Failed to create shuffler"); - shuffler_task = NULL; - goto unwind; - } - } - if (stutter < 0) - stutter = 0; - if (stutter) { - /* Create the stutter thread */ - stutter_task = kthread_run(rcu_torture_stutter, NULL, - "rcu_torture_stutter"); - if (IS_ERR(stutter_task)) { - firsterr = PTR_ERR(stutter_task); - VERBOSE_PRINTK_ERRSTRING("Failed to create stutter"); - stutter_task = NULL; - goto unwind; - } - } - if (fqs_duration < 0) - fqs_duration = 0; - if (fqs_duration) { - /* Create the stutter thread */ - fqs_task = kthread_run(rcu_torture_fqs, NULL, - "rcu_torture_fqs"); - if (IS_ERR(fqs_task)) { - firsterr = PTR_ERR(fqs_task); - VERBOSE_PRINTK_ERRSTRING("Failed to create fqs"); - fqs_task = NULL; - goto unwind; - } - } - if (test_boost_interval < 1) - test_boost_interval = 1; - if (test_boost_duration < 2) - test_boost_duration = 2; - if ((test_boost == 1 && cur_ops->can_boost) || - test_boost == 2) { - - boost_starttime = jiffies + test_boost_interval * HZ; - register_cpu_notifier(&rcutorture_cpu_nb); - for_each_possible_cpu(i) { - if (cpu_is_offline(i)) - continue; /* Heuristic: CPU can go offline. */ - retval = rcutorture_booster_init(i); - if (retval < 0) { - firsterr = retval; - goto unwind; - } - } - } - if (shutdown_secs > 0) { - shutdown_time = jiffies + shutdown_secs * HZ; - shutdown_task = kthread_create(rcu_torture_shutdown, NULL, - "rcu_torture_shutdown"); - if (IS_ERR(shutdown_task)) { - firsterr = PTR_ERR(shutdown_task); - VERBOSE_PRINTK_ERRSTRING("Failed to create shutdown"); - shutdown_task = NULL; - goto unwind; - } - wake_up_process(shutdown_task); - } - i = rcu_torture_onoff_init(); - if (i != 0) { - firsterr = i; - goto unwind; - } - register_reboot_notifier(&rcutorture_shutdown_nb); - i = rcu_torture_stall_init(); - if (i != 0) { - firsterr = i; - goto unwind; - } - retval = rcu_torture_barrier_init(); - if (retval != 0) { - firsterr = retval; - goto unwind; - } - if (object_debug) - rcu_test_debug_objects(); - rcutorture_record_test_transition(); - mutex_unlock(&fullstop_mutex); - return 0; - -unwind: - mutex_unlock(&fullstop_mutex); - rcu_torture_cleanup(); - return firsterr; -} - -module_init(rcu_torture_init); -module_exit(rcu_torture_cleanup); diff --git a/kernel/rcutree.c b/kernel/rcutree.c deleted file mode 100644 index 240604a..0000000 --- a/kernel/rcutree.c +++ /dev/null @@ -1,3396 +0,0 @@ -/* - * Read-Copy Update mechanism for mutual exclusion - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Copyright IBM Corporation, 2008 - * - * Authors: Dipankar Sarma - * Manfred Spraul - * Paul E. McKenney Hierarchical version - * - * Based on the original work by Paul McKenney - * and inputs from Rusty Russell, Andrea Arcangeli and Andi Kleen. - * - * For detailed explanation of Read-Copy Update mechanism see - - * Documentation/RCU - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "rcutree.h" -#include - -#include "rcu.h" - -/* Data structures. */ - -static struct lock_class_key rcu_node_class[RCU_NUM_LVLS]; -static struct lock_class_key rcu_fqs_class[RCU_NUM_LVLS]; - -/* - * In order to export the rcu_state name to the tracing tools, it - * needs to be added in the __tracepoint_string section. - * This requires defining a separate variable tp__varname - * that points to the string being used, and this will allow - * the tracing userspace tools to be able to decipher the string - * address to the matching string. - */ -#define RCU_STATE_INITIALIZER(sname, sabbr, cr) \ -static char sname##_varname[] = #sname; \ -static const char *tp_##sname##_varname __used __tracepoint_string = sname##_varname; \ -struct rcu_state sname##_state = { \ - .level = { &sname##_state.node[0] }, \ - .call = cr, \ - .fqs_state = RCU_GP_IDLE, \ - .gpnum = 0UL - 300UL, \ - .completed = 0UL - 300UL, \ - .orphan_lock = __RAW_SPIN_LOCK_UNLOCKED(&sname##_state.orphan_lock), \ - .orphan_nxttail = &sname##_state.orphan_nxtlist, \ - .orphan_donetail = &sname##_state.orphan_donelist, \ - .barrier_mutex = __MUTEX_INITIALIZER(sname##_state.barrier_mutex), \ - .onoff_mutex = __MUTEX_INITIALIZER(sname##_state.onoff_mutex), \ - .name = sname##_varname, \ - .abbr = sabbr, \ -}; \ -DEFINE_PER_CPU(struct rcu_data, sname##_data) - -RCU_STATE_INITIALIZER(rcu_sched, 's', call_rcu_sched); -RCU_STATE_INITIALIZER(rcu_bh, 'b', call_rcu_bh); - -static struct rcu_state *rcu_state; -LIST_HEAD(rcu_struct_flavors); - -/* Increase (but not decrease) the CONFIG_RCU_FANOUT_LEAF at boot time. */ -static int rcu_fanout_leaf = CONFIG_RCU_FANOUT_LEAF; -module_param(rcu_fanout_leaf, int, 0444); -int rcu_num_lvls __read_mostly = RCU_NUM_LVLS; -static int num_rcu_lvl[] = { /* Number of rcu_nodes at specified level. */ - NUM_RCU_LVL_0, - NUM_RCU_LVL_1, - NUM_RCU_LVL_2, - NUM_RCU_LVL_3, - NUM_RCU_LVL_4, -}; -int rcu_num_nodes __read_mostly = NUM_RCU_NODES; /* Total # rcu_nodes in use. */ - -/* - * The rcu_scheduler_active variable transitions from zero to one just - * before the first task is spawned. So when this variable is zero, RCU - * can assume that there is but one task, allowing RCU to (for example) - * optimize synchronize_sched() to a simple barrier(). When this variable - * is one, RCU must actually do all the hard work required to detect real - * grace periods. This variable is also used to suppress boot-time false - * positives from lockdep-RCU error checking. - */ -int rcu_scheduler_active __read_mostly; -EXPORT_SYMBOL_GPL(rcu_scheduler_active); - -/* - * The rcu_scheduler_fully_active variable transitions from zero to one - * during the early_initcall() processing, which is after the scheduler - * is capable of creating new tasks. So RCU processing (for example, - * creating tasks for RCU priority boosting) must be delayed until after - * rcu_scheduler_fully_active transitions from zero to one. We also - * currently delay invocation of any RCU callbacks until after this point. - * - * It might later prove better for people registering RCU callbacks during - * early boot to take responsibility for these callbacks, but one step at - * a time. - */ -static int rcu_scheduler_fully_active __read_mostly; - -#ifdef CONFIG_RCU_BOOST - -/* - * Control variables for per-CPU and per-rcu_node kthreads. These - * handle all flavors of RCU. - */ -static DEFINE_PER_CPU(struct task_struct *, rcu_cpu_kthread_task); -DEFINE_PER_CPU(unsigned int, rcu_cpu_kthread_status); -DEFINE_PER_CPU(unsigned int, rcu_cpu_kthread_loops); -DEFINE_PER_CPU(char, rcu_cpu_has_work); - -#endif /* #ifdef CONFIG_RCU_BOOST */ - -static void rcu_boost_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu); -static void invoke_rcu_core(void); -static void invoke_rcu_callbacks(struct rcu_state *rsp, struct rcu_data *rdp); - -/* - * Track the rcutorture test sequence number and the update version - * number within a given test. The rcutorture_testseq is incremented - * on every rcutorture module load and unload, so has an odd value - * when a test is running. The rcutorture_vernum is set to zero - * when rcutorture starts and is incremented on each rcutorture update. - * These variables enable correlating rcutorture output with the - * RCU tracing information. - */ -unsigned long rcutorture_testseq; -unsigned long rcutorture_vernum; - -/* - * Return true if an RCU grace period is in progress. The ACCESS_ONCE()s - * permit this function to be invoked without holding the root rcu_node - * structure's ->lock, but of course results can be subject to change. - */ -static int rcu_gp_in_progress(struct rcu_state *rsp) -{ - return ACCESS_ONCE(rsp->completed) != ACCESS_ONCE(rsp->gpnum); -} - -/* - * Note a quiescent state. Because we do not need to know - * how many quiescent states passed, just if there was at least - * one since the start of the grace period, this just sets a flag. - * The caller must have disabled preemption. - */ -void rcu_sched_qs(int cpu) -{ - struct rcu_data *rdp = &per_cpu(rcu_sched_data, cpu); - - if (rdp->passed_quiesce == 0) - trace_rcu_grace_period(TPS("rcu_sched"), rdp->gpnum, TPS("cpuqs")); - rdp->passed_quiesce = 1; -} - -void rcu_bh_qs(int cpu) -{ - struct rcu_data *rdp = &per_cpu(rcu_bh_data, cpu); - - if (rdp->passed_quiesce == 0) - trace_rcu_grace_period(TPS("rcu_bh"), rdp->gpnum, TPS("cpuqs")); - rdp->passed_quiesce = 1; -} - -/* - * Note a context switch. This is a quiescent state for RCU-sched, - * and requires special handling for preemptible RCU. - * The caller must have disabled preemption. - */ -void rcu_note_context_switch(int cpu) -{ - trace_rcu_utilization(TPS("Start context switch")); - rcu_sched_qs(cpu); - rcu_preempt_note_context_switch(cpu); - trace_rcu_utilization(TPS("End context switch")); -} -EXPORT_SYMBOL_GPL(rcu_note_context_switch); - -static DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks) = { - .dynticks_nesting = DYNTICK_TASK_EXIT_IDLE, - .dynticks = ATOMIC_INIT(1), -#ifdef CONFIG_NO_HZ_FULL_SYSIDLE - .dynticks_idle_nesting = DYNTICK_TASK_NEST_VALUE, - .dynticks_idle = ATOMIC_INIT(1), -#endif /* #ifdef CONFIG_NO_HZ_FULL_SYSIDLE */ -}; - -static long blimit = 10; /* Maximum callbacks per rcu_do_batch. */ -static long qhimark = 10000; /* If this many pending, ignore blimit. */ -static long qlowmark = 100; /* Once only this many pending, use blimit. */ - -module_param(blimit, long, 0444); -module_param(qhimark, long, 0444); -module_param(qlowmark, long, 0444); - -static ulong jiffies_till_first_fqs = ULONG_MAX; -static ulong jiffies_till_next_fqs = ULONG_MAX; - -module_param(jiffies_till_first_fqs, ulong, 0644); -module_param(jiffies_till_next_fqs, ulong, 0644); - -static void rcu_start_gp_advanced(struct rcu_state *rsp, struct rcu_node *rnp, - struct rcu_data *rdp); -static void force_qs_rnp(struct rcu_state *rsp, - int (*f)(struct rcu_data *rsp, bool *isidle, - unsigned long *maxj), - bool *isidle, unsigned long *maxj); -static void force_quiescent_state(struct rcu_state *rsp); -static int rcu_pending(int cpu); - -/* - * Return the number of RCU-sched batches processed thus far for debug & stats. - */ -long rcu_batches_completed_sched(void) -{ - return rcu_sched_state.completed; -} -EXPORT_SYMBOL_GPL(rcu_batches_completed_sched); - -/* - * Return the number of RCU BH batches processed thus far for debug & stats. - */ -long rcu_batches_completed_bh(void) -{ - return rcu_bh_state.completed; -} -EXPORT_SYMBOL_GPL(rcu_batches_completed_bh); - -/* - * Force a quiescent state for RCU BH. - */ -void rcu_bh_force_quiescent_state(void) -{ - force_quiescent_state(&rcu_bh_state); -} -EXPORT_SYMBOL_GPL(rcu_bh_force_quiescent_state); - -/* - * Record the number of times rcutorture tests have been initiated and - * terminated. This information allows the debugfs tracing stats to be - * correlated to the rcutorture messages, even when the rcutorture module - * is being repeatedly loaded and unloaded. In other words, we cannot - * store this state in rcutorture itself. - */ -void rcutorture_record_test_transition(void) -{ - rcutorture_testseq++; - rcutorture_vernum = 0; -} -EXPORT_SYMBOL_GPL(rcutorture_record_test_transition); - -/* - * Record the number of writer passes through the current rcutorture test. - * This is also used to correlate debugfs tracing stats with the rcutorture - * messages. - */ -void rcutorture_record_progress(unsigned long vernum) -{ - rcutorture_vernum++; -} -EXPORT_SYMBOL_GPL(rcutorture_record_progress); - -/* - * Force a quiescent state for RCU-sched. - */ -void rcu_sched_force_quiescent_state(void) -{ - force_quiescent_state(&rcu_sched_state); -} -EXPORT_SYMBOL_GPL(rcu_sched_force_quiescent_state); - -/* - * Does the CPU have callbacks ready to be invoked? - */ -static int -cpu_has_callbacks_ready_to_invoke(struct rcu_data *rdp) -{ - return &rdp->nxtlist != rdp->nxttail[RCU_DONE_TAIL] && - rdp->nxttail[RCU_DONE_TAIL] != NULL; -} - -/* - * Does the current CPU require a not-yet-started grace period? - * The caller must have disabled interrupts to prevent races with - * normal callback registry. - */ -static int -cpu_needs_another_gp(struct rcu_state *rsp, struct rcu_data *rdp) -{ - int i; - - if (rcu_gp_in_progress(rsp)) - return 0; /* No, a grace period is already in progress. */ - if (rcu_nocb_needs_gp(rsp)) - return 1; /* Yes, a no-CBs CPU needs one. */ - if (!rdp->nxttail[RCU_NEXT_TAIL]) - return 0; /* No, this is a no-CBs (or offline) CPU. */ - if (*rdp->nxttail[RCU_NEXT_READY_TAIL]) - return 1; /* Yes, this CPU has newly registered callbacks. */ - for (i = RCU_WAIT_TAIL; i < RCU_NEXT_TAIL; i++) - if (rdp->nxttail[i - 1] != rdp->nxttail[i] && - ULONG_CMP_LT(ACCESS_ONCE(rsp->completed), - rdp->nxtcompleted[i])) - return 1; /* Yes, CBs for future grace period. */ - return 0; /* No grace period needed. */ -} - -/* - * Return the root node of the specified rcu_state structure. - */ -static struct rcu_node *rcu_get_root(struct rcu_state *rsp) -{ - return &rsp->node[0]; -} - -/* - * rcu_eqs_enter_common - current CPU is moving towards extended quiescent state - * - * If the new value of the ->dynticks_nesting counter now is zero, - * we really have entered idle, and must do the appropriate accounting. - * The caller must have disabled interrupts. - */ -static void rcu_eqs_enter_common(struct rcu_dynticks *rdtp, long long oldval, - bool user) -{ - trace_rcu_dyntick(TPS("Start"), oldval, rdtp->dynticks_nesting); - if (!user && !is_idle_task(current)) { - struct task_struct *idle __maybe_unused = - idle_task(smp_processor_id()); - - trace_rcu_dyntick(TPS("Error on entry: not idle task"), oldval, 0); - ftrace_dump(DUMP_ORIG); - WARN_ONCE(1, "Current pid: %d comm: %s / Idle pid: %d comm: %s", - current->pid, current->comm, - idle->pid, idle->comm); /* must be idle task! */ - } - rcu_prepare_for_idle(smp_processor_id()); - /* CPUs seeing atomic_inc() must see prior RCU read-side crit sects */ - smp_mb__before_atomic_inc(); /* See above. */ - atomic_inc(&rdtp->dynticks); - smp_mb__after_atomic_inc(); /* Force ordering with next sojourn. */ - WARN_ON_ONCE(atomic_read(&rdtp->dynticks) & 0x1); - - /* - * It is illegal to enter an extended quiescent state while - * in an RCU read-side critical section. - */ - rcu_lockdep_assert(!lock_is_held(&rcu_lock_map), - "Illegal idle entry in RCU read-side critical section."); - rcu_lockdep_assert(!lock_is_held(&rcu_bh_lock_map), - "Illegal idle entry in RCU-bh read-side critical section."); - rcu_lockdep_assert(!lock_is_held(&rcu_sched_lock_map), - "Illegal idle entry in RCU-sched read-side critical section."); -} - -/* - * Enter an RCU extended quiescent state, which can be either the - * idle loop or adaptive-tickless usermode execution. - */ -static void rcu_eqs_enter(bool user) -{ - long long oldval; - struct rcu_dynticks *rdtp; - - rdtp = this_cpu_ptr(&rcu_dynticks); - oldval = rdtp->dynticks_nesting; - WARN_ON_ONCE((oldval & DYNTICK_TASK_NEST_MASK) == 0); - if ((oldval & DYNTICK_TASK_NEST_MASK) == DYNTICK_TASK_NEST_VALUE) - rdtp->dynticks_nesting = 0; - else - rdtp->dynticks_nesting -= DYNTICK_TASK_NEST_VALUE; - rcu_eqs_enter_common(rdtp, oldval, user); -} - -/** - * rcu_idle_enter - inform RCU that current CPU is entering idle - * - * Enter idle mode, in other words, -leave- the mode in which RCU - * read-side critical sections can occur. (Though RCU read-side - * critical sections can occur in irq handlers in idle, a possibility - * handled by irq_enter() and irq_exit().) - * - * We crowbar the ->dynticks_nesting field to zero to allow for - * the possibility of usermode upcalls having messed up our count - * of interrupt nesting level during the prior busy period. - */ -void rcu_idle_enter(void) -{ - unsigned long flags; - - local_irq_save(flags); - rcu_eqs_enter(false); - rcu_sysidle_enter(this_cpu_ptr(&rcu_dynticks), 0); - local_irq_restore(flags); -} -EXPORT_SYMBOL_GPL(rcu_idle_enter); - -#ifdef CONFIG_RCU_USER_QS -/** - * rcu_user_enter - inform RCU that we are resuming userspace. - * - * Enter RCU idle mode right before resuming userspace. No use of RCU - * is permitted between this call and rcu_user_exit(). This way the - * CPU doesn't need to maintain the tick for RCU maintenance purposes - * when the CPU runs in userspace. - */ -void rcu_user_enter(void) -{ - rcu_eqs_enter(1); -} -#endif /* CONFIG_RCU_USER_QS */ - -/** - * rcu_irq_exit - inform RCU that current CPU is exiting irq towards idle - * - * Exit from an interrupt handler, which might possibly result in entering - * idle mode, in other words, leaving the mode in which read-side critical - * sections can occur. - * - * This code assumes that the idle loop never does anything that might - * result in unbalanced calls to irq_enter() and irq_exit(). If your - * architecture violates this assumption, RCU will give you what you - * deserve, good and hard. But very infrequently and irreproducibly. - * - * Use things like work queues to work around this limitation. - * - * You have been warned. - */ -void rcu_irq_exit(void) -{ - unsigned long flags; - long long oldval; - struct rcu_dynticks *rdtp; - - local_irq_save(flags); - rdtp = this_cpu_ptr(&rcu_dynticks); - oldval = rdtp->dynticks_nesting; - rdtp->dynticks_nesting--; - WARN_ON_ONCE(rdtp->dynticks_nesting < 0); - if (rdtp->dynticks_nesting) - trace_rcu_dyntick(TPS("--="), oldval, rdtp->dynticks_nesting); - else - rcu_eqs_enter_common(rdtp, oldval, true); - rcu_sysidle_enter(rdtp, 1); - local_irq_restore(flags); -} - -/* - * rcu_eqs_exit_common - current CPU moving away from extended quiescent state - * - * If the new value of the ->dynticks_nesting counter was previously zero, - * we really have exited idle, and must do the appropriate accounting. - * The caller must have disabled interrupts. - */ -static void rcu_eqs_exit_common(struct rcu_dynticks *rdtp, long long oldval, - int user) -{ - smp_mb__before_atomic_inc(); /* Force ordering w/previous sojourn. */ - atomic_inc(&rdtp->dynticks); - /* CPUs seeing atomic_inc() must see later RCU read-side crit sects */ - smp_mb__after_atomic_inc(); /* See above. */ - WARN_ON_ONCE(!(atomic_read(&rdtp->dynticks) & 0x1)); - rcu_cleanup_after_idle(smp_processor_id()); - trace_rcu_dyntick(TPS("End"), oldval, rdtp->dynticks_nesting); - if (!user && !is_idle_task(current)) { - struct task_struct *idle __maybe_unused = - idle_task(smp_processor_id()); - - trace_rcu_dyntick(TPS("Error on exit: not idle task"), - oldval, rdtp->dynticks_nesting); - ftrace_dump(DUMP_ORIG); - WARN_ONCE(1, "Current pid: %d comm: %s / Idle pid: %d comm: %s", - current->pid, current->comm, - idle->pid, idle->comm); /* must be idle task! */ - } -} - -/* - * Exit an RCU extended quiescent state, which can be either the - * idle loop or adaptive-tickless usermode execution. - */ -static void rcu_eqs_exit(bool user) -{ - struct rcu_dynticks *rdtp; - long long oldval; - - rdtp = this_cpu_ptr(&rcu_dynticks); - oldval = rdtp->dynticks_nesting; - WARN_ON_ONCE(oldval < 0); - if (oldval & DYNTICK_TASK_NEST_MASK) - rdtp->dynticks_nesting += DYNTICK_TASK_NEST_VALUE; - else - rdtp->dynticks_nesting = DYNTICK_TASK_EXIT_IDLE; - rcu_eqs_exit_common(rdtp, oldval, user); -} - -/** - * rcu_idle_exit - inform RCU that current CPU is leaving idle - * - * Exit idle mode, in other words, -enter- the mode in which RCU - * read-side critical sections can occur. - * - * We crowbar the ->dynticks_nesting field to DYNTICK_TASK_NEST to - * allow for the possibility of usermode upcalls messing up our count - * of interrupt nesting level during the busy period that is just - * now starting. - */ -void rcu_idle_exit(void) -{ - unsigned long flags; - - local_irq_save(flags); - rcu_eqs_exit(false); - rcu_sysidle_exit(this_cpu_ptr(&rcu_dynticks), 0); - local_irq_restore(flags); -} -EXPORT_SYMBOL_GPL(rcu_idle_exit); - -#ifdef CONFIG_RCU_USER_QS -/** - * rcu_user_exit - inform RCU that we are exiting userspace. - * - * Exit RCU idle mode while entering the kernel because it can - * run a RCU read side critical section anytime. - */ -void rcu_user_exit(void) -{ - rcu_eqs_exit(1); -} -#endif /* CONFIG_RCU_USER_QS */ - -/** - * rcu_irq_enter - inform RCU that current CPU is entering irq away from idle - * - * Enter an interrupt handler, which might possibly result in exiting - * idle mode, in other words, entering the mode in which read-side critical - * sections can occur. - * - * Note that the Linux kernel is fully capable of entering an interrupt - * handler that it never exits, for example when doing upcalls to - * user mode! This code assumes that the idle loop never does upcalls to - * user mode. If your architecture does do upcalls from the idle loop (or - * does anything else that results in unbalanced calls to the irq_enter() - * and irq_exit() functions), RCU will give you what you deserve, good - * and hard. But very infrequently and irreproducibly. - * - * Use things like work queues to work around this limitation. - * - * You have been warned. - */ -void rcu_irq_enter(void) -{ - unsigned long flags; - struct rcu_dynticks *rdtp; - long long oldval; - - local_irq_save(flags); - rdtp = this_cpu_ptr(&rcu_dynticks); - oldval = rdtp->dynticks_nesting; - rdtp->dynticks_nesting++; - WARN_ON_ONCE(rdtp->dynticks_nesting == 0); - if (oldval) - trace_rcu_dyntick(TPS("++="), oldval, rdtp->dynticks_nesting); - else - rcu_eqs_exit_common(rdtp, oldval, true); - rcu_sysidle_exit(rdtp, 1); - local_irq_restore(flags); -} - -/** - * rcu_nmi_enter - inform RCU of entry to NMI context - * - * If the CPU was idle with dynamic ticks active, and there is no - * irq handler running, this updates rdtp->dynticks_nmi to let the - * RCU grace-period handling know that the CPU is active. - */ -void rcu_nmi_enter(void) -{ - struct rcu_dynticks *rdtp = this_cpu_ptr(&rcu_dynticks); - - if (rdtp->dynticks_nmi_nesting == 0 && - (atomic_read(&rdtp->dynticks) & 0x1)) - return; - rdtp->dynticks_nmi_nesting++; - smp_mb__before_atomic_inc(); /* Force delay from prior write. */ - atomic_inc(&rdtp->dynticks); - /* CPUs seeing atomic_inc() must see later RCU read-side crit sects */ - smp_mb__after_atomic_inc(); /* See above. */ - WARN_ON_ONCE(!(atomic_read(&rdtp->dynticks) & 0x1)); -} - -/** - * rcu_nmi_exit - inform RCU of exit from NMI context - * - * If the CPU was idle with dynamic ticks active, and there is no - * irq handler running, this updates rdtp->dynticks_nmi to let the - * RCU grace-period handling know that the CPU is no longer active. - */ -void rcu_nmi_exit(void) -{ - struct rcu_dynticks *rdtp = this_cpu_ptr(&rcu_dynticks); - - if (rdtp->dynticks_nmi_nesting == 0 || - --rdtp->dynticks_nmi_nesting != 0) - return; - /* CPUs seeing atomic_inc() must see prior RCU read-side crit sects */ - smp_mb__before_atomic_inc(); /* See above. */ - atomic_inc(&rdtp->dynticks); - smp_mb__after_atomic_inc(); /* Force delay to next write. */ - WARN_ON_ONCE(atomic_read(&rdtp->dynticks) & 0x1); -} - -/** - * __rcu_is_watching - are RCU read-side critical sections safe? - * - * Return true if RCU is watching the running CPU, which means that - * this CPU can safely enter RCU read-side critical sections. Unlike - * rcu_is_watching(), the caller of __rcu_is_watching() must have at - * least disabled preemption. - */ -bool __rcu_is_watching(void) -{ - return atomic_read(this_cpu_ptr(&rcu_dynticks.dynticks)) & 0x1; -} - -/** - * rcu_is_watching - see if RCU thinks that the current CPU is idle - * - * If the current CPU is in its idle loop and is neither in an interrupt - * or NMI handler, return true. - */ -bool rcu_is_watching(void) -{ - int ret; - - preempt_disable(); - ret = __rcu_is_watching(); - preempt_enable(); - return ret; -} -EXPORT_SYMBOL_GPL(rcu_is_watching); - -#if defined(CONFIG_PROVE_RCU) && defined(CONFIG_HOTPLUG_CPU) - -/* - * Is the current CPU online? Disable preemption to avoid false positives - * that could otherwise happen due to the current CPU number being sampled, - * this task being preempted, its old CPU being taken offline, resuming - * on some other CPU, then determining that its old CPU is now offline. - * It is OK to use RCU on an offline processor during initial boot, hence - * the check for rcu_scheduler_fully_active. Note also that it is OK - * for a CPU coming online to use RCU for one jiffy prior to marking itself - * online in the cpu_online_mask. Similarly, it is OK for a CPU going - * offline to continue to use RCU for one jiffy after marking itself - * offline in the cpu_online_mask. This leniency is necessary given the - * non-atomic nature of the online and offline processing, for example, - * the fact that a CPU enters the scheduler after completing the CPU_DYING - * notifiers. - * - * This is also why RCU internally marks CPUs online during the - * CPU_UP_PREPARE phase and offline during the CPU_DEAD phase. - * - * Disable checking if in an NMI handler because we cannot safely report - * errors from NMI handlers anyway. - */ -bool rcu_lockdep_current_cpu_online(void) -{ - struct rcu_data *rdp; - struct rcu_node *rnp; - bool ret; - - if (in_nmi()) - return 1; - preempt_disable(); - rdp = this_cpu_ptr(&rcu_sched_data); - rnp = rdp->mynode; - ret = (rdp->grpmask & rnp->qsmaskinit) || - !rcu_scheduler_fully_active; - preempt_enable(); - return ret; -} -EXPORT_SYMBOL_GPL(rcu_lockdep_current_cpu_online); - -#endif /* #if defined(CONFIG_PROVE_RCU) && defined(CONFIG_HOTPLUG_CPU) */ - -/** - * rcu_is_cpu_rrupt_from_idle - see if idle or immediately interrupted from idle - * - * If the current CPU is idle or running at a first-level (not nested) - * interrupt from idle, return true. The caller must have at least - * disabled preemption. - */ -static int rcu_is_cpu_rrupt_from_idle(void) -{ - return __this_cpu_read(rcu_dynticks.dynticks_nesting) <= 1; -} - -/* - * Snapshot the specified CPU's dynticks counter so that we can later - * credit them with an implicit quiescent state. Return 1 if this CPU - * is in dynticks idle mode, which is an extended quiescent state. - */ -static int dyntick_save_progress_counter(struct rcu_data *rdp, - bool *isidle, unsigned long *maxj) -{ - rdp->dynticks_snap = atomic_add_return(0, &rdp->dynticks->dynticks); - rcu_sysidle_check_cpu(rdp, isidle, maxj); - return (rdp->dynticks_snap & 0x1) == 0; -} - -/* - * Return true if the specified CPU has passed through a quiescent - * state by virtue of being in or having passed through an dynticks - * idle state since the last call to dyntick_save_progress_counter() - * for this same CPU, or by virtue of having been offline. - */ -static int rcu_implicit_dynticks_qs(struct rcu_data *rdp, - bool *isidle, unsigned long *maxj) -{ - unsigned int curr; - unsigned int snap; - - curr = (unsigned int)atomic_add_return(0, &rdp->dynticks->dynticks); - snap = (unsigned int)rdp->dynticks_snap; - - /* - * If the CPU passed through or entered a dynticks idle phase with - * no active irq/NMI handlers, then we can safely pretend that the CPU - * already acknowledged the request to pass through a quiescent - * state. Either way, that CPU cannot possibly be in an RCU - * read-side critical section that started before the beginning - * of the current RCU grace period. - */ - if ((curr & 0x1) == 0 || UINT_CMP_GE(curr, snap + 2)) { - trace_rcu_fqs(rdp->rsp->name, rdp->gpnum, rdp->cpu, TPS("dti")); - rdp->dynticks_fqs++; - return 1; - } - - /* - * Check for the CPU being offline, but only if the grace period - * is old enough. We don't need to worry about the CPU changing - * state: If we see it offline even once, it has been through a - * quiescent state. - * - * The reason for insisting that the grace period be at least - * one jiffy old is that CPUs that are not quite online and that - * have just gone offline can still execute RCU read-side critical - * sections. - */ - if (ULONG_CMP_GE(rdp->rsp->gp_start + 2, jiffies)) - return 0; /* Grace period is not old enough. */ - barrier(); - if (cpu_is_offline(rdp->cpu)) { - trace_rcu_fqs(rdp->rsp->name, rdp->gpnum, rdp->cpu, TPS("ofl")); - rdp->offline_fqs++; - return 1; - } - - /* - * There is a possibility that a CPU in adaptive-ticks state - * might run in the kernel with the scheduling-clock tick disabled - * for an extended time period. Invoke rcu_kick_nohz_cpu() to - * force the CPU to restart the scheduling-clock tick in this - * CPU is in this state. - */ - rcu_kick_nohz_cpu(rdp->cpu); - - return 0; -} - -static void record_gp_stall_check_time(struct rcu_state *rsp) -{ - unsigned long j = ACCESS_ONCE(jiffies); - - rsp->gp_start = j; - smp_wmb(); /* Record start time before stall time. */ - rsp->jiffies_stall = j + rcu_jiffies_till_stall_check(); -} - -/* - * Dump stacks of all tasks running on stalled CPUs. This is a fallback - * for architectures that do not implement trigger_all_cpu_backtrace(). - * The NMI-triggered stack traces are more accurate because they are - * printed by the target CPU. - */ -static void rcu_dump_cpu_stacks(struct rcu_state *rsp) -{ - int cpu; - unsigned long flags; - struct rcu_node *rnp; - - rcu_for_each_leaf_node(rsp, rnp) { - raw_spin_lock_irqsave(&rnp->lock, flags); - if (rnp->qsmask != 0) { - for (cpu = 0; cpu <= rnp->grphi - rnp->grplo; cpu++) - if (rnp->qsmask & (1UL << cpu)) - dump_cpu_task(rnp->grplo + cpu); - } - raw_spin_unlock_irqrestore(&rnp->lock, flags); - } -} - -static void print_other_cpu_stall(struct rcu_state *rsp) -{ - int cpu; - long delta; - unsigned long flags; - int ndetected = 0; - struct rcu_node *rnp = rcu_get_root(rsp); - long totqlen = 0; - - /* Only let one CPU complain about others per time interval. */ - - raw_spin_lock_irqsave(&rnp->lock, flags); - delta = jiffies - rsp->jiffies_stall; - if (delta < RCU_STALL_RAT_DELAY || !rcu_gp_in_progress(rsp)) { - raw_spin_unlock_irqrestore(&rnp->lock, flags); - return; - } - rsp->jiffies_stall = jiffies + 3 * rcu_jiffies_till_stall_check() + 3; - raw_spin_unlock_irqrestore(&rnp->lock, flags); - - /* - * OK, time to rat on our buddy... - * See Documentation/RCU/stallwarn.txt for info on how to debug - * RCU CPU stall warnings. - */ - pr_err("INFO: %s detected stalls on CPUs/tasks:", - rsp->name); - print_cpu_stall_info_begin(); - rcu_for_each_leaf_node(rsp, rnp) { - raw_spin_lock_irqsave(&rnp->lock, flags); - ndetected += rcu_print_task_stall(rnp); - if (rnp->qsmask != 0) { - for (cpu = 0; cpu <= rnp->grphi - rnp->grplo; cpu++) - if (rnp->qsmask & (1UL << cpu)) { - print_cpu_stall_info(rsp, - rnp->grplo + cpu); - ndetected++; - } - } - raw_spin_unlock_irqrestore(&rnp->lock, flags); - } - - /* - * Now rat on any tasks that got kicked up to the root rcu_node - * due to CPU offlining. - */ - rnp = rcu_get_root(rsp); - raw_spin_lock_irqsave(&rnp->lock, flags); - ndetected += rcu_print_task_stall(rnp); - raw_spin_unlock_irqrestore(&rnp->lock, flags); - - print_cpu_stall_info_end(); - for_each_possible_cpu(cpu) - totqlen += per_cpu_ptr(rsp->rda, cpu)->qlen; - pr_cont("(detected by %d, t=%ld jiffies, g=%lu, c=%lu, q=%lu)\n", - smp_processor_id(), (long)(jiffies - rsp->gp_start), - rsp->gpnum, rsp->completed, totqlen); - if (ndetected == 0) - pr_err("INFO: Stall ended before state dump start\n"); - else if (!trigger_all_cpu_backtrace()) - rcu_dump_cpu_stacks(rsp); - - /* Complain about tasks blocking the grace period. */ - - rcu_print_detail_task_stall(rsp); - - force_quiescent_state(rsp); /* Kick them all. */ -} - -static void print_cpu_stall(struct rcu_state *rsp) -{ - int cpu; - unsigned long flags; - struct rcu_node *rnp = rcu_get_root(rsp); - long totqlen = 0; - - /* - * OK, time to rat on ourselves... - * See Documentation/RCU/stallwarn.txt for info on how to debug - * RCU CPU stall warnings. - */ - pr_err("INFO: %s self-detected stall on CPU", rsp->name); - print_cpu_stall_info_begin(); - print_cpu_stall_info(rsp, smp_processor_id()); - print_cpu_stall_info_end(); - for_each_possible_cpu(cpu) - totqlen += per_cpu_ptr(rsp->rda, cpu)->qlen; - pr_cont(" (t=%lu jiffies g=%lu c=%lu q=%lu)\n", - jiffies - rsp->gp_start, rsp->gpnum, rsp->completed, totqlen); - if (!trigger_all_cpu_backtrace()) - dump_stack(); - - raw_spin_lock_irqsave(&rnp->lock, flags); - if (ULONG_CMP_GE(jiffies, rsp->jiffies_stall)) - rsp->jiffies_stall = jiffies + - 3 * rcu_jiffies_till_stall_check() + 3; - raw_spin_unlock_irqrestore(&rnp->lock, flags); - - set_need_resched(); /* kick ourselves to get things going. */ -} - -static void check_cpu_stall(struct rcu_state *rsp, struct rcu_data *rdp) -{ - unsigned long completed; - unsigned long gpnum; - unsigned long gps; - unsigned long j; - unsigned long js; - struct rcu_node *rnp; - - if (rcu_cpu_stall_suppress || !rcu_gp_in_progress(rsp)) - return; - j = ACCESS_ONCE(jiffies); - - /* - * Lots of memory barriers to reject false positives. - * - * The idea is to pick up rsp->gpnum, then rsp->jiffies_stall, - * then rsp->gp_start, and finally rsp->completed. These values - * are updated in the opposite order with memory barriers (or - * equivalent) during grace-period initialization and cleanup. - * Now, a false positive can occur if we get an new value of - * rsp->gp_start and a old value of rsp->jiffies_stall. But given - * the memory barriers, the only way that this can happen is if one - * grace period ends and another starts between these two fetches. - * Detect this by comparing rsp->completed with the previous fetch - * from rsp->gpnum. - * - * Given this check, comparisons of jiffies, rsp->jiffies_stall, - * and rsp->gp_start suffice to forestall false positives. - */ - gpnum = ACCESS_ONCE(rsp->gpnum); - smp_rmb(); /* Pick up ->gpnum first... */ - js = ACCESS_ONCE(rsp->jiffies_stall); - smp_rmb(); /* ...then ->jiffies_stall before the rest... */ - gps = ACCESS_ONCE(rsp->gp_start); - smp_rmb(); /* ...and finally ->gp_start before ->completed. */ - completed = ACCESS_ONCE(rsp->completed); - if (ULONG_CMP_GE(completed, gpnum) || - ULONG_CMP_LT(j, js) || - ULONG_CMP_GE(gps, js)) - return; /* No stall or GP completed since entering function. */ - rnp = rdp->mynode; - if (rcu_gp_in_progress(rsp) && - (ACCESS_ONCE(rnp->qsmask) & rdp->grpmask)) { - - /* We haven't checked in, so go dump stack. */ - print_cpu_stall(rsp); - - } else if (rcu_gp_in_progress(rsp) && - ULONG_CMP_GE(j, js + RCU_STALL_RAT_DELAY)) { - - /* They had a few time units to dump stack, so complain. */ - print_other_cpu_stall(rsp); - } -} - -/** - * rcu_cpu_stall_reset - prevent further stall warnings in current grace period - * - * Set the stall-warning timeout way off into the future, thus preventing - * any RCU CPU stall-warning messages from appearing in the current set of - * RCU grace periods. - * - * The caller must disable hard irqs. - */ -void rcu_cpu_stall_reset(void) -{ - struct rcu_state *rsp; - - for_each_rcu_flavor(rsp) - rsp->jiffies_stall = jiffies + ULONG_MAX / 2; -} - -/* - * Initialize the specified rcu_data structure's callback list to empty. - */ -static void init_callback_list(struct rcu_data *rdp) -{ - int i; - - if (init_nocb_callback_list(rdp)) - return; - rdp->nxtlist = NULL; - for (i = 0; i < RCU_NEXT_SIZE; i++) - rdp->nxttail[i] = &rdp->nxtlist; -} - -/* - * Determine the value that ->completed will have at the end of the - * next subsequent grace period. This is used to tag callbacks so that - * a CPU can invoke callbacks in a timely fashion even if that CPU has - * been dyntick-idle for an extended period with callbacks under the - * influence of RCU_FAST_NO_HZ. - * - * The caller must hold rnp->lock with interrupts disabled. - */ -static unsigned long rcu_cbs_completed(struct rcu_state *rsp, - struct rcu_node *rnp) -{ - /* - * If RCU is idle, we just wait for the next grace period. - * But we can only be sure that RCU is idle if we are looking - * at the root rcu_node structure -- otherwise, a new grace - * period might have started, but just not yet gotten around - * to initializing the current non-root rcu_node structure. - */ - if (rcu_get_root(rsp) == rnp && rnp->gpnum == rnp->completed) - return rnp->completed + 1; - - /* - * Otherwise, wait for a possible partial grace period and - * then the subsequent full grace period. - */ - return rnp->completed + 2; -} - -/* - * Trace-event helper function for rcu_start_future_gp() and - * rcu_nocb_wait_gp(). - */ -static void trace_rcu_future_gp(struct rcu_node *rnp, struct rcu_data *rdp, - unsigned long c, const char *s) -{ - trace_rcu_future_grace_period(rdp->rsp->name, rnp->gpnum, - rnp->completed, c, rnp->level, - rnp->grplo, rnp->grphi, s); -} - -/* - * Start some future grace period, as needed to handle newly arrived - * callbacks. The required future grace periods are recorded in each - * rcu_node structure's ->need_future_gp field. - * - * The caller must hold the specified rcu_node structure's ->lock. - */ -static unsigned long __maybe_unused -rcu_start_future_gp(struct rcu_node *rnp, struct rcu_data *rdp) -{ - unsigned long c; - int i; - struct rcu_node *rnp_root = rcu_get_root(rdp->rsp); - - /* - * Pick up grace-period number for new callbacks. If this - * grace period is already marked as needed, return to the caller. - */ - c = rcu_cbs_completed(rdp->rsp, rnp); - trace_rcu_future_gp(rnp, rdp, c, TPS("Startleaf")); - if (rnp->need_future_gp[c & 0x1]) { - trace_rcu_future_gp(rnp, rdp, c, TPS("Prestartleaf")); - return c; - } - - /* - * If either this rcu_node structure or the root rcu_node structure - * believe that a grace period is in progress, then we must wait - * for the one following, which is in "c". Because our request - * will be noticed at the end of the current grace period, we don't - * need to explicitly start one. - */ - if (rnp->gpnum != rnp->completed || - ACCESS_ONCE(rnp->gpnum) != ACCESS_ONCE(rnp->completed)) { - rnp->need_future_gp[c & 0x1]++; - trace_rcu_future_gp(rnp, rdp, c, TPS("Startedleaf")); - return c; - } - - /* - * There might be no grace period in progress. If we don't already - * hold it, acquire the root rcu_node structure's lock in order to - * start one (if needed). - */ - if (rnp != rnp_root) - raw_spin_lock(&rnp_root->lock); - - /* - * Get a new grace-period number. If there really is no grace - * period in progress, it will be smaller than the one we obtained - * earlier. Adjust callbacks as needed. Note that even no-CBs - * CPUs have a ->nxtcompleted[] array, so no no-CBs checks needed. - */ - c = rcu_cbs_completed(rdp->rsp, rnp_root); - for (i = RCU_DONE_TAIL; i < RCU_NEXT_TAIL; i++) - if (ULONG_CMP_LT(c, rdp->nxtcompleted[i])) - rdp->nxtcompleted[i] = c; - - /* - * If the needed for the required grace period is already - * recorded, trace and leave. - */ - if (rnp_root->need_future_gp[c & 0x1]) { - trace_rcu_future_gp(rnp, rdp, c, TPS("Prestartedroot")); - goto unlock_out; - } - - /* Record the need for the future grace period. */ - rnp_root->need_future_gp[c & 0x1]++; - - /* If a grace period is not already in progress, start one. */ - if (rnp_root->gpnum != rnp_root->completed) { - trace_rcu_future_gp(rnp, rdp, c, TPS("Startedleafroot")); - } else { - trace_rcu_future_gp(rnp, rdp, c, TPS("Startedroot")); - rcu_start_gp_advanced(rdp->rsp, rnp_root, rdp); - } -unlock_out: - if (rnp != rnp_root) - raw_spin_unlock(&rnp_root->lock); - return c; -} - -/* - * Clean up any old requests for the just-ended grace period. Also return - * whether any additional grace periods have been requested. Also invoke - * rcu_nocb_gp_cleanup() in order to wake up any no-callbacks kthreads - * waiting for this grace period to complete. - */ -static int rcu_future_gp_cleanup(struct rcu_state *rsp, struct rcu_node *rnp) -{ - int c = rnp->completed; - int needmore; - struct rcu_data *rdp = this_cpu_ptr(rsp->rda); - - rcu_nocb_gp_cleanup(rsp, rnp); - rnp->need_future_gp[c & 0x1] = 0; - needmore = rnp->need_future_gp[(c + 1) & 0x1]; - trace_rcu_future_gp(rnp, rdp, c, - needmore ? TPS("CleanupMore") : TPS("Cleanup")); - return needmore; -} - -/* - * If there is room, assign a ->completed number to any callbacks on - * this CPU that have not already been assigned. Also accelerate any - * callbacks that were previously assigned a ->completed number that has - * since proven to be too conservative, which can happen if callbacks get - * assigned a ->completed number while RCU is idle, but with reference to - * a non-root rcu_node structure. This function is idempotent, so it does - * not hurt to call it repeatedly. - * - * The caller must hold rnp->lock with interrupts disabled. - */ -static void rcu_accelerate_cbs(struct rcu_state *rsp, struct rcu_node *rnp, - struct rcu_data *rdp) -{ - unsigned long c; - int i; - - /* If the CPU has no callbacks, nothing to do. */ - if (!rdp->nxttail[RCU_NEXT_TAIL] || !*rdp->nxttail[RCU_DONE_TAIL]) - return; - - /* - * Starting from the sublist containing the callbacks most - * recently assigned a ->completed number and working down, find the - * first sublist that is not assignable to an upcoming grace period. - * Such a sublist has something in it (first two tests) and has - * a ->completed number assigned that will complete sooner than - * the ->completed number for newly arrived callbacks (last test). - * - * The key point is that any later sublist can be assigned the - * same ->completed number as the newly arrived callbacks, which - * means that the callbacks in any of these later sublist can be - * grouped into a single sublist, whether or not they have already - * been assigned a ->completed number. - */ - c = rcu_cbs_completed(rsp, rnp); - for (i = RCU_NEXT_TAIL - 1; i > RCU_DONE_TAIL; i--) - if (rdp->nxttail[i] != rdp->nxttail[i - 1] && - !ULONG_CMP_GE(rdp->nxtcompleted[i], c)) - break; - - /* - * If there are no sublist for unassigned callbacks, leave. - * At the same time, advance "i" one sublist, so that "i" will - * index into the sublist where all the remaining callbacks should - * be grouped into. - */ - if (++i >= RCU_NEXT_TAIL) - return; - - /* - * Assign all subsequent callbacks' ->completed number to the next - * full grace period and group them all in the sublist initially - * indexed by "i". - */ - for (; i <= RCU_NEXT_TAIL; i++) { - rdp->nxttail[i] = rdp->nxttail[RCU_NEXT_TAIL]; - rdp->nxtcompleted[i] = c; - } - /* Record any needed additional grace periods. */ - rcu_start_future_gp(rnp, rdp); - - /* Trace depending on how much we were able to accelerate. */ - if (!*rdp->nxttail[RCU_WAIT_TAIL]) - trace_rcu_grace_period(rsp->name, rdp->gpnum, TPS("AccWaitCB")); - else - trace_rcu_grace_period(rsp->name, rdp->gpnum, TPS("AccReadyCB")); -} - -/* - * Move any callbacks whose grace period has completed to the - * RCU_DONE_TAIL sublist, then compact the remaining sublists and - * assign ->completed numbers to any callbacks in the RCU_NEXT_TAIL - * sublist. This function is idempotent, so it does not hurt to - * invoke it repeatedly. As long as it is not invoked -too- often... - * - * The caller must hold rnp->lock with interrupts disabled. - */ -static void rcu_advance_cbs(struct rcu_state *rsp, struct rcu_node *rnp, - struct rcu_data *rdp) -{ - int i, j; - - /* If the CPU has no callbacks, nothing to do. */ - if (!rdp->nxttail[RCU_NEXT_TAIL] || !*rdp->nxttail[RCU_DONE_TAIL]) - return; - - /* - * Find all callbacks whose ->completed numbers indicate that they - * are ready to invoke, and put them into the RCU_DONE_TAIL sublist. - */ - for (i = RCU_WAIT_TAIL; i < RCU_NEXT_TAIL; i++) { - if (ULONG_CMP_LT(rnp->completed, rdp->nxtcompleted[i])) - break; - rdp->nxttail[RCU_DONE_TAIL] = rdp->nxttail[i]; - } - /* Clean up any sublist tail pointers that were misordered above. */ - for (j = RCU_WAIT_TAIL; j < i; j++) - rdp->nxttail[j] = rdp->nxttail[RCU_DONE_TAIL]; - - /* Copy down callbacks to fill in empty sublists. */ - for (j = RCU_WAIT_TAIL; i < RCU_NEXT_TAIL; i++, j++) { - if (rdp->nxttail[j] == rdp->nxttail[RCU_NEXT_TAIL]) - break; - rdp->nxttail[j] = rdp->nxttail[i]; - rdp->nxtcompleted[j] = rdp->nxtcompleted[i]; - } - - /* Classify any remaining callbacks. */ - rcu_accelerate_cbs(rsp, rnp, rdp); -} - -/* - * Update CPU-local rcu_data state to record the beginnings and ends of - * grace periods. The caller must hold the ->lock of the leaf rcu_node - * structure corresponding to the current CPU, and must have irqs disabled. - */ -static void __note_gp_changes(struct rcu_state *rsp, struct rcu_node *rnp, struct rcu_data *rdp) -{ - /* Handle the ends of any preceding grace periods first. */ - if (rdp->completed == rnp->completed) { - - /* No grace period end, so just accelerate recent callbacks. */ - rcu_accelerate_cbs(rsp, rnp, rdp); - - } else { - - /* Advance callbacks. */ - rcu_advance_cbs(rsp, rnp, rdp); - - /* Remember that we saw this grace-period completion. */ - rdp->completed = rnp->completed; - trace_rcu_grace_period(rsp->name, rdp->gpnum, TPS("cpuend")); - } - - if (rdp->gpnum != rnp->gpnum) { - /* - * If the current grace period is waiting for this CPU, - * set up to detect a quiescent state, otherwise don't - * go looking for one. - */ - rdp->gpnum = rnp->gpnum; - trace_rcu_grace_period(rsp->name, rdp->gpnum, TPS("cpustart")); - rdp->passed_quiesce = 0; - rdp->qs_pending = !!(rnp->qsmask & rdp->grpmask); - zero_cpu_stall_ticks(rdp); - } -} - -static void note_gp_changes(struct rcu_state *rsp, struct rcu_data *rdp) -{ - unsigned long flags; - struct rcu_node *rnp; - - local_irq_save(flags); - rnp = rdp->mynode; - if ((rdp->gpnum == ACCESS_ONCE(rnp->gpnum) && - rdp->completed == ACCESS_ONCE(rnp->completed)) || /* w/out lock. */ - !raw_spin_trylock(&rnp->lock)) { /* irqs already off, so later. */ - local_irq_restore(flags); - return; - } - __note_gp_changes(rsp, rnp, rdp); - raw_spin_unlock_irqrestore(&rnp->lock, flags); -} - -/* - * Initialize a new grace period. Return 0 if no grace period required. - */ -static int rcu_gp_init(struct rcu_state *rsp) -{ - struct rcu_data *rdp; - struct rcu_node *rnp = rcu_get_root(rsp); - - rcu_bind_gp_kthread(); - raw_spin_lock_irq(&rnp->lock); - if (rsp->gp_flags == 0) { - /* Spurious wakeup, tell caller to go back to sleep. */ - raw_spin_unlock_irq(&rnp->lock); - return 0; - } - rsp->gp_flags = 0; /* Clear all flags: New grace period. */ - - if (WARN_ON_ONCE(rcu_gp_in_progress(rsp))) { - /* - * Grace period already in progress, don't start another. - * Not supposed to be able to happen. - */ - raw_spin_unlock_irq(&rnp->lock); - return 0; - } - - /* Advance to a new grace period and initialize state. */ - record_gp_stall_check_time(rsp); - smp_wmb(); /* Record GP times before starting GP. */ - rsp->gpnum++; - trace_rcu_grace_period(rsp->name, rsp->gpnum, TPS("start")); - raw_spin_unlock_irq(&rnp->lock); - - /* Exclude any concurrent CPU-hotplug operations. */ - mutex_lock(&rsp->onoff_mutex); - - /* - * Set the quiescent-state-needed bits in all the rcu_node - * structures for all currently online CPUs in breadth-first order, - * starting from the root rcu_node structure, relying on the layout - * of the tree within the rsp->node[] array. Note that other CPUs - * will access only the leaves of the hierarchy, thus seeing that no - * grace period is in progress, at least until the corresponding - * leaf node has been initialized. In addition, we have excluded - * CPU-hotplug operations. - * - * The grace period cannot complete until the initialization - * process finishes, because this kthread handles both. - */ - rcu_for_each_node_breadth_first(rsp, rnp) { - raw_spin_lock_irq(&rnp->lock); - rdp = this_cpu_ptr(rsp->rda); - rcu_preempt_check_blocked_tasks(rnp); - rnp->qsmask = rnp->qsmaskinit; - ACCESS_ONCE(rnp->gpnum) = rsp->gpnum; - WARN_ON_ONCE(rnp->completed != rsp->completed); - ACCESS_ONCE(rnp->completed) = rsp->completed; - if (rnp == rdp->mynode) - __note_gp_changes(rsp, rnp, rdp); - rcu_preempt_boost_start_gp(rnp); - trace_rcu_grace_period_init(rsp->name, rnp->gpnum, - rnp->level, rnp->grplo, - rnp->grphi, rnp->qsmask); - raw_spin_unlock_irq(&rnp->lock); -#ifdef CONFIG_PROVE_RCU_DELAY - if ((prandom_u32() % (rcu_num_nodes + 1)) == 0 && - system_state == SYSTEM_RUNNING) - udelay(200); -#endif /* #ifdef CONFIG_PROVE_RCU_DELAY */ - cond_resched(); - } - - mutex_unlock(&rsp->onoff_mutex); - return 1; -} - -/* - * Do one round of quiescent-state forcing. - */ -static int rcu_gp_fqs(struct rcu_state *rsp, int fqs_state_in) -{ - int fqs_state = fqs_state_in; - bool isidle = false; - unsigned long maxj; - struct rcu_node *rnp = rcu_get_root(rsp); - - rsp->n_force_qs++; - if (fqs_state == RCU_SAVE_DYNTICK) { - /* Collect dyntick-idle snapshots. */ - if (is_sysidle_rcu_state(rsp)) { - isidle = 1; - maxj = jiffies - ULONG_MAX / 4; - } - force_qs_rnp(rsp, dyntick_save_progress_counter, - &isidle, &maxj); - rcu_sysidle_report_gp(rsp, isidle, maxj); - fqs_state = RCU_FORCE_QS; - } else { - /* Handle dyntick-idle and offline CPUs. */ - isidle = 0; - force_qs_rnp(rsp, rcu_implicit_dynticks_qs, &isidle, &maxj); - } - /* Clear flag to prevent immediate re-entry. */ - if (ACCESS_ONCE(rsp->gp_flags) & RCU_GP_FLAG_FQS) { - raw_spin_lock_irq(&rnp->lock); - rsp->gp_flags &= ~RCU_GP_FLAG_FQS; - raw_spin_unlock_irq(&rnp->lock); - } - return fqs_state; -} - -/* - * Clean up after the old grace period. - */ -static void rcu_gp_cleanup(struct rcu_state *rsp) -{ - unsigned long gp_duration; - int nocb = 0; - struct rcu_data *rdp; - struct rcu_node *rnp = rcu_get_root(rsp); - - raw_spin_lock_irq(&rnp->lock); - gp_duration = jiffies - rsp->gp_start; - if (gp_duration > rsp->gp_max) - rsp->gp_max = gp_duration; - - /* - * We know the grace period is complete, but to everyone else - * it appears to still be ongoing. But it is also the case - * that to everyone else it looks like there is nothing that - * they can do to advance the grace period. It is therefore - * safe for us to drop the lock in order to mark the grace - * period as completed in all of the rcu_node structures. - */ - raw_spin_unlock_irq(&rnp->lock); - - /* - * Propagate new ->completed value to rcu_node structures so - * that other CPUs don't have to wait until the start of the next - * grace period to process their callbacks. This also avoids - * some nasty RCU grace-period initialization races by forcing - * the end of the current grace period to be completely recorded in - * all of the rcu_node structures before the beginning of the next - * grace period is recorded in any of the rcu_node structures. - */ - rcu_for_each_node_breadth_first(rsp, rnp) { - raw_spin_lock_irq(&rnp->lock); - ACCESS_ONCE(rnp->completed) = rsp->gpnum; - rdp = this_cpu_ptr(rsp->rda); - if (rnp == rdp->mynode) - __note_gp_changes(rsp, rnp, rdp); - nocb += rcu_future_gp_cleanup(rsp, rnp); - raw_spin_unlock_irq(&rnp->lock); - cond_resched(); - } - rnp = rcu_get_root(rsp); - raw_spin_lock_irq(&rnp->lock); - rcu_nocb_gp_set(rnp, nocb); - - rsp->completed = rsp->gpnum; /* Declare grace period done. */ - trace_rcu_grace_period(rsp->name, rsp->completed, TPS("end")); - rsp->fqs_state = RCU_GP_IDLE; - rdp = this_cpu_ptr(rsp->rda); - rcu_advance_cbs(rsp, rnp, rdp); /* Reduce false positives below. */ - if (cpu_needs_another_gp(rsp, rdp)) { - rsp->gp_flags = RCU_GP_FLAG_INIT; - trace_rcu_grace_period(rsp->name, - ACCESS_ONCE(rsp->gpnum), - TPS("newreq")); - } - raw_spin_unlock_irq(&rnp->lock); -} - -/* - * Body of kthread that handles grace periods. - */ -static int __noreturn rcu_gp_kthread(void *arg) -{ - int fqs_state; - int gf; - unsigned long j; - int ret; - struct rcu_state *rsp = arg; - struct rcu_node *rnp = rcu_get_root(rsp); - - for (;;) { - - /* Handle grace-period start. */ - for (;;) { - trace_rcu_grace_period(rsp->name, - ACCESS_ONCE(rsp->gpnum), - TPS("reqwait")); - wait_event_interruptible(rsp->gp_wq, - ACCESS_ONCE(rsp->gp_flags) & - RCU_GP_FLAG_INIT); - if (rcu_gp_init(rsp)) - break; - cond_resched(); - flush_signals(current); - trace_rcu_grace_period(rsp->name, - ACCESS_ONCE(rsp->gpnum), - TPS("reqwaitsig")); - } - - /* Handle quiescent-state forcing. */ - fqs_state = RCU_SAVE_DYNTICK; - j = jiffies_till_first_fqs; - if (j > HZ) { - j = HZ; - jiffies_till_first_fqs = HZ; - } - ret = 0; - for (;;) { - if (!ret) - rsp->jiffies_force_qs = jiffies + j; - trace_rcu_grace_period(rsp->name, - ACCESS_ONCE(rsp->gpnum), - TPS("fqswait")); - ret = wait_event_interruptible_timeout(rsp->gp_wq, - ((gf = ACCESS_ONCE(rsp->gp_flags)) & - RCU_GP_FLAG_FQS) || - (!ACCESS_ONCE(rnp->qsmask) && - !rcu_preempt_blocked_readers_cgp(rnp)), - j); - /* If grace period done, leave loop. */ - if (!ACCESS_ONCE(rnp->qsmask) && - !rcu_preempt_blocked_readers_cgp(rnp)) - break; - /* If time for quiescent-state forcing, do it. */ - if (ULONG_CMP_GE(jiffies, rsp->jiffies_force_qs) || - (gf & RCU_GP_FLAG_FQS)) { - trace_rcu_grace_period(rsp->name, - ACCESS_ONCE(rsp->gpnum), - TPS("fqsstart")); - fqs_state = rcu_gp_fqs(rsp, fqs_state); - trace_rcu_grace_period(rsp->name, - ACCESS_ONCE(rsp->gpnum), - TPS("fqsend")); - cond_resched(); - } else { - /* Deal with stray signal. */ - cond_resched(); - flush_signals(current); - trace_rcu_grace_period(rsp->name, - ACCESS_ONCE(rsp->gpnum), - TPS("fqswaitsig")); - } - j = jiffies_till_next_fqs; - if (j > HZ) { - j = HZ; - jiffies_till_next_fqs = HZ; - } else if (j < 1) { - j = 1; - jiffies_till_next_fqs = 1; - } - } - - /* Handle grace-period end. */ - rcu_gp_cleanup(rsp); - } -} - -static void rsp_wakeup(struct irq_work *work) -{ - struct rcu_state *rsp = container_of(work, struct rcu_state, wakeup_work); - - /* Wake up rcu_gp_kthread() to start the grace period. */ - wake_up(&rsp->gp_wq); -} - -/* - * Start a new RCU grace period if warranted, re-initializing the hierarchy - * in preparation for detecting the next grace period. The caller must hold - * the root node's ->lock and hard irqs must be disabled. - * - * Note that it is legal for a dying CPU (which is marked as offline) to - * invoke this function. This can happen when the dying CPU reports its - * quiescent state. - */ -static void -rcu_start_gp_advanced(struct rcu_state *rsp, struct rcu_node *rnp, - struct rcu_data *rdp) -{ - if (!rsp->gp_kthread || !cpu_needs_another_gp(rsp, rdp)) { - /* - * Either we have not yet spawned the grace-period - * task, this CPU does not need another grace period, - * or a grace period is already in progress. - * Either way, don't start a new grace period. - */ - return; - } - rsp->gp_flags = RCU_GP_FLAG_INIT; - trace_rcu_grace_period(rsp->name, ACCESS_ONCE(rsp->gpnum), - TPS("newreq")); - - /* - * We can't do wakeups while holding the rnp->lock, as that - * could cause possible deadlocks with the rq->lock. Defer - * the wakeup to interrupt context. And don't bother waking - * up the running kthread. - */ - if (current != rsp->gp_kthread) - irq_work_queue(&rsp->wakeup_work); -} - -/* - * Similar to rcu_start_gp_advanced(), but also advance the calling CPU's - * callbacks. Note that rcu_start_gp_advanced() cannot do this because it - * is invoked indirectly from rcu_advance_cbs(), which would result in - * endless recursion -- or would do so if it wasn't for the self-deadlock - * that is encountered beforehand. - */ -static void -rcu_start_gp(struct rcu_state *rsp) -{ - struct rcu_data *rdp = this_cpu_ptr(rsp->rda); - struct rcu_node *rnp = rcu_get_root(rsp); - - /* - * If there is no grace period in progress right now, any - * callbacks we have up to this point will be satisfied by the - * next grace period. Also, advancing the callbacks reduces the - * probability of false positives from cpu_needs_another_gp() - * resulting in pointless grace periods. So, advance callbacks - * then start the grace period! - */ - rcu_advance_cbs(rsp, rnp, rdp); - rcu_start_gp_advanced(rsp, rnp, rdp); -} - -/* - * Report a full set of quiescent states to the specified rcu_state - * data structure. This involves cleaning up after the prior grace - * period and letting rcu_start_gp() start up the next grace period - * if one is needed. Note that the caller must hold rnp->lock, which - * is released before return. - */ -static void rcu_report_qs_rsp(struct rcu_state *rsp, unsigned long flags) - __releases(rcu_get_root(rsp)->lock) -{ - WARN_ON_ONCE(!rcu_gp_in_progress(rsp)); - raw_spin_unlock_irqrestore(&rcu_get_root(rsp)->lock, flags); - wake_up(&rsp->gp_wq); /* Memory barrier implied by wake_up() path. */ -} - -/* - * Similar to rcu_report_qs_rdp(), for which it is a helper function. - * Allows quiescent states for a group of CPUs to be reported at one go - * to the specified rcu_node structure, though all the CPUs in the group - * must be represented by the same rcu_node structure (which need not be - * a leaf rcu_node structure, though it often will be). That structure's - * lock must be held upon entry, and it is released before return. - */ -static void -rcu_report_qs_rnp(unsigned long mask, struct rcu_state *rsp, - struct rcu_node *rnp, unsigned long flags) - __releases(rnp->lock) -{ - struct rcu_node *rnp_c; - - /* Walk up the rcu_node hierarchy. */ - for (;;) { - if (!(rnp->qsmask & mask)) { - - /* Our bit has already been cleared, so done. */ - raw_spin_unlock_irqrestore(&rnp->lock, flags); - return; - } - rnp->qsmask &= ~mask; - trace_rcu_quiescent_state_report(rsp->name, rnp->gpnum, - mask, rnp->qsmask, rnp->level, - rnp->grplo, rnp->grphi, - !!rnp->gp_tasks); - if (rnp->qsmask != 0 || rcu_preempt_blocked_readers_cgp(rnp)) { - - /* Other bits still set at this level, so done. */ - raw_spin_unlock_irqrestore(&rnp->lock, flags); - return; - } - mask = rnp->grpmask; - if (rnp->parent == NULL) { - - /* No more levels. Exit loop holding root lock. */ - - break; - } - raw_spin_unlock_irqrestore(&rnp->lock, flags); - rnp_c = rnp; - rnp = rnp->parent; - raw_spin_lock_irqsave(&rnp->lock, flags); - WARN_ON_ONCE(rnp_c->qsmask); - } - - /* - * Get here if we are the last CPU to pass through a quiescent - * state for this grace period. Invoke rcu_report_qs_rsp() - * to clean up and start the next grace period if one is needed. - */ - rcu_report_qs_rsp(rsp, flags); /* releases rnp->lock. */ -} - -/* - * Record a quiescent state for the specified CPU to that CPU's rcu_data - * structure. This must be either called from the specified CPU, or - * called when the specified CPU is known to be offline (and when it is - * also known that no other CPU is concurrently trying to help the offline - * CPU). The lastcomp argument is used to make sure we are still in the - * grace period of interest. We don't want to end the current grace period - * based on quiescent states detected in an earlier grace period! - */ -static void -rcu_report_qs_rdp(int cpu, struct rcu_state *rsp, struct rcu_data *rdp) -{ - unsigned long flags; - unsigned long mask; - struct rcu_node *rnp; - - rnp = rdp->mynode; - raw_spin_lock_irqsave(&rnp->lock, flags); - if (rdp->passed_quiesce == 0 || rdp->gpnum != rnp->gpnum || - rnp->completed == rnp->gpnum) { - - /* - * The grace period in which this quiescent state was - * recorded has ended, so don't report it upwards. - * We will instead need a new quiescent state that lies - * within the current grace period. - */ - rdp->passed_quiesce = 0; /* need qs for new gp. */ - raw_spin_unlock_irqrestore(&rnp->lock, flags); - return; - } - mask = rdp->grpmask; - if ((rnp->qsmask & mask) == 0) { - raw_spin_unlock_irqrestore(&rnp->lock, flags); - } else { - rdp->qs_pending = 0; - - /* - * This GP can't end until cpu checks in, so all of our - * callbacks can be processed during the next GP. - */ - rcu_accelerate_cbs(rsp, rnp, rdp); - - rcu_report_qs_rnp(mask, rsp, rnp, flags); /* rlses rnp->lock */ - } -} - -/* - * Check to see if there is a new grace period of which this CPU - * is not yet aware, and if so, set up local rcu_data state for it. - * Otherwise, see if this CPU has just passed through its first - * quiescent state for this grace period, and record that fact if so. - */ -static void -rcu_check_quiescent_state(struct rcu_state *rsp, struct rcu_data *rdp) -{ - /* Check for grace-period ends and beginnings. */ - note_gp_changes(rsp, rdp); - - /* - * Does this CPU still need to do its part for current grace period? - * If no, return and let the other CPUs do their part as well. - */ - if (!rdp->qs_pending) - return; - - /* - * Was there a quiescent state since the beginning of the grace - * period? If no, then exit and wait for the next call. - */ - if (!rdp->passed_quiesce) - return; - - /* - * Tell RCU we are done (but rcu_report_qs_rdp() will be the - * judge of that). - */ - rcu_report_qs_rdp(rdp->cpu, rsp, rdp); -} - -#ifdef CONFIG_HOTPLUG_CPU - -/* - * Send the specified CPU's RCU callbacks to the orphanage. The - * specified CPU must be offline, and the caller must hold the - * ->orphan_lock. - */ -static void -rcu_send_cbs_to_orphanage(int cpu, struct rcu_state *rsp, - struct rcu_node *rnp, struct rcu_data *rdp) -{ - /* No-CBs CPUs do not have orphanable callbacks. */ - if (rcu_is_nocb_cpu(rdp->cpu)) - return; - - /* - * Orphan the callbacks. First adjust the counts. This is safe - * because _rcu_barrier() excludes CPU-hotplug operations, so it - * cannot be running now. Thus no memory barrier is required. - */ - if (rdp->nxtlist != NULL) { - rsp->qlen_lazy += rdp->qlen_lazy; - rsp->qlen += rdp->qlen; - rdp->n_cbs_orphaned += rdp->qlen; - rdp->qlen_lazy = 0; - ACCESS_ONCE(rdp->qlen) = 0; - } - - /* - * Next, move those callbacks still needing a grace period to - * the orphanage, where some other CPU will pick them up. - * Some of the callbacks might have gone partway through a grace - * period, but that is too bad. They get to start over because we - * cannot assume that grace periods are synchronized across CPUs. - * We don't bother updating the ->nxttail[] array yet, instead - * we just reset the whole thing later on. - */ - if (*rdp->nxttail[RCU_DONE_TAIL] != NULL) { - *rsp->orphan_nxttail = *rdp->nxttail[RCU_DONE_TAIL]; - rsp->orphan_nxttail = rdp->nxttail[RCU_NEXT_TAIL]; - *rdp->nxttail[RCU_DONE_TAIL] = NULL; - } - - /* - * Then move the ready-to-invoke callbacks to the orphanage, - * where some other CPU will pick them up. These will not be - * required to pass though another grace period: They are done. - */ - if (rdp->nxtlist != NULL) { - *rsp->orphan_donetail = rdp->nxtlist; - rsp->orphan_donetail = rdp->nxttail[RCU_DONE_TAIL]; - } - - /* Finally, initialize the rcu_data structure's list to empty. */ - init_callback_list(rdp); -} - -/* - * Adopt the RCU callbacks from the specified rcu_state structure's - * orphanage. The caller must hold the ->orphan_lock. - */ -static void rcu_adopt_orphan_cbs(struct rcu_state *rsp) -{ - int i; - struct rcu_data *rdp = __this_cpu_ptr(rsp->rda); - - /* No-CBs CPUs are handled specially. */ - if (rcu_nocb_adopt_orphan_cbs(rsp, rdp)) - return; - - /* Do the accounting first. */ - rdp->qlen_lazy += rsp->qlen_lazy; - rdp->qlen += rsp->qlen; - rdp->n_cbs_adopted += rsp->qlen; - if (rsp->qlen_lazy != rsp->qlen) - rcu_idle_count_callbacks_posted(); - rsp->qlen_lazy = 0; - rsp->qlen = 0; - - /* - * We do not need a memory barrier here because the only way we - * can get here if there is an rcu_barrier() in flight is if - * we are the task doing the rcu_barrier(). - */ - - /* First adopt the ready-to-invoke callbacks. */ - if (rsp->orphan_donelist != NULL) { - *rsp->orphan_donetail = *rdp->nxttail[RCU_DONE_TAIL]; - *rdp->nxttail[RCU_DONE_TAIL] = rsp->orphan_donelist; - for (i = RCU_NEXT_SIZE - 1; i >= RCU_DONE_TAIL; i--) - if (rdp->nxttail[i] == rdp->nxttail[RCU_DONE_TAIL]) - rdp->nxttail[i] = rsp->orphan_donetail; - rsp->orphan_donelist = NULL; - rsp->orphan_donetail = &rsp->orphan_donelist; - } - - /* And then adopt the callbacks that still need a grace period. */ - if (rsp->orphan_nxtlist != NULL) { - *rdp->nxttail[RCU_NEXT_TAIL] = rsp->orphan_nxtlist; - rdp->nxttail[RCU_NEXT_TAIL] = rsp->orphan_nxttail; - rsp->orphan_nxtlist = NULL; - rsp->orphan_nxttail = &rsp->orphan_nxtlist; - } -} - -/* - * Trace the fact that this CPU is going offline. - */ -static void rcu_cleanup_dying_cpu(struct rcu_state *rsp) -{ - RCU_TRACE(unsigned long mask); - RCU_TRACE(struct rcu_data *rdp = this_cpu_ptr(rsp->rda)); - RCU_TRACE(struct rcu_node *rnp = rdp->mynode); - - RCU_TRACE(mask = rdp->grpmask); - trace_rcu_grace_period(rsp->name, - rnp->gpnum + 1 - !!(rnp->qsmask & mask), - TPS("cpuofl")); -} - -/* - * The CPU has been completely removed, and some other CPU is reporting - * this fact from process context. Do the remainder of the cleanup, - * including orphaning the outgoing CPU's RCU callbacks, and also - * adopting them. There can only be one CPU hotplug operation at a time, - * so no other CPU can be attempting to update rcu_cpu_kthread_task. - */ -static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp) -{ - unsigned long flags; - unsigned long mask; - int need_report = 0; - struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu); - struct rcu_node *rnp = rdp->mynode; /* Outgoing CPU's rdp & rnp. */ - - /* Adjust any no-longer-needed kthreads. */ - rcu_boost_kthread_setaffinity(rnp, -1); - - /* Remove the dead CPU from the bitmasks in the rcu_node hierarchy. */ - - /* Exclude any attempts to start a new grace period. */ - mutex_lock(&rsp->onoff_mutex); - raw_spin_lock_irqsave(&rsp->orphan_lock, flags); - - /* Orphan the dead CPU's callbacks, and adopt them if appropriate. */ - rcu_send_cbs_to_orphanage(cpu, rsp, rnp, rdp); - rcu_adopt_orphan_cbs(rsp); - - /* Remove the outgoing CPU from the masks in the rcu_node hierarchy. */ - mask = rdp->grpmask; /* rnp->grplo is constant. */ - do { - raw_spin_lock(&rnp->lock); /* irqs already disabled. */ - rnp->qsmaskinit &= ~mask; - if (rnp->qsmaskinit != 0) { - if (rnp != rdp->mynode) - raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */ - break; - } - if (rnp == rdp->mynode) - need_report = rcu_preempt_offline_tasks(rsp, rnp, rdp); - else - raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */ - mask = rnp->grpmask; - rnp = rnp->parent; - } while (rnp != NULL); - - /* - * We still hold the leaf rcu_node structure lock here, and - * irqs are still disabled. The reason for this subterfuge is - * because invoking rcu_report_unblock_qs_rnp() with ->orphan_lock - * held leads to deadlock. - */ - raw_spin_unlock(&rsp->orphan_lock); /* irqs remain disabled. */ - rnp = rdp->mynode; - if (need_report & RCU_OFL_TASKS_NORM_GP) - rcu_report_unblock_qs_rnp(rnp, flags); - else - raw_spin_unlock_irqrestore(&rnp->lock, flags); - if (need_report & RCU_OFL_TASKS_EXP_GP) - rcu_report_exp_rnp(rsp, rnp, true); - WARN_ONCE(rdp->qlen != 0 || rdp->nxtlist != NULL, - "rcu_cleanup_dead_cpu: Callbacks on offline CPU %d: qlen=%lu, nxtlist=%p\n", - cpu, rdp->qlen, rdp->nxtlist); - init_callback_list(rdp); - /* Disallow further callbacks on this CPU. */ - rdp->nxttail[RCU_NEXT_TAIL] = NULL; - mutex_unlock(&rsp->onoff_mutex); -} - -#else /* #ifdef CONFIG_HOTPLUG_CPU */ - -static void rcu_cleanup_dying_cpu(struct rcu_state *rsp) -{ -} - -static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp) -{ -} - -#endif /* #else #ifdef CONFIG_HOTPLUG_CPU */ - -/* - * Invoke any RCU callbacks that have made it to the end of their grace - * period. Thottle as specified by rdp->blimit. - */ -static void rcu_do_batch(struct rcu_state *rsp, struct rcu_data *rdp) -{ - unsigned long flags; - struct rcu_head *next, *list, **tail; - long bl, count, count_lazy; - int i; - - /* If no callbacks are ready, just return. */ - if (!cpu_has_callbacks_ready_to_invoke(rdp)) { - trace_rcu_batch_start(rsp->name, rdp->qlen_lazy, rdp->qlen, 0); - trace_rcu_batch_end(rsp->name, 0, !!ACCESS_ONCE(rdp->nxtlist), - need_resched(), is_idle_task(current), - rcu_is_callbacks_kthread()); - return; - } - - /* - * Extract the list of ready callbacks, disabling to prevent - * races with call_rcu() from interrupt handlers. - */ - local_irq_save(flags); - WARN_ON_ONCE(cpu_is_offline(smp_processor_id())); - bl = rdp->blimit; - trace_rcu_batch_start(rsp->name, rdp->qlen_lazy, rdp->qlen, bl); - list = rdp->nxtlist; - rdp->nxtlist = *rdp->nxttail[RCU_DONE_TAIL]; - *rdp->nxttail[RCU_DONE_TAIL] = NULL; - tail = rdp->nxttail[RCU_DONE_TAIL]; - for (i = RCU_NEXT_SIZE - 1; i >= 0; i--) - if (rdp->nxttail[i] == rdp->nxttail[RCU_DONE_TAIL]) - rdp->nxttail[i] = &rdp->nxtlist; - local_irq_restore(flags); - - /* Invoke callbacks. */ - count = count_lazy = 0; - while (list) { - next = list->next; - prefetch(next); - debug_rcu_head_unqueue(list); - if (__rcu_reclaim(rsp->name, list)) - count_lazy++; - list = next; - /* Stop only if limit reached and CPU has something to do. */ - if (++count >= bl && - (need_resched() || - (!is_idle_task(current) && !rcu_is_callbacks_kthread()))) - break; - } - - local_irq_save(flags); - trace_rcu_batch_end(rsp->name, count, !!list, need_resched(), - is_idle_task(current), - rcu_is_callbacks_kthread()); - - /* Update count, and requeue any remaining callbacks. */ - if (list != NULL) { - *tail = rdp->nxtlist; - rdp->nxtlist = list; - for (i = 0; i < RCU_NEXT_SIZE; i++) - if (&rdp->nxtlist == rdp->nxttail[i]) - rdp->nxttail[i] = tail; - else - break; - } - smp_mb(); /* List handling before counting for rcu_barrier(). */ - rdp->qlen_lazy -= count_lazy; - ACCESS_ONCE(rdp->qlen) -= count; - rdp->n_cbs_invoked += count; - - /* Reinstate batch limit if we have worked down the excess. */ - if (rdp->blimit == LONG_MAX && rdp->qlen <= qlowmark) - rdp->blimit = blimit; - - /* Reset ->qlen_last_fqs_check trigger if enough CBs have drained. */ - if (rdp->qlen == 0 && rdp->qlen_last_fqs_check != 0) { - rdp->qlen_last_fqs_check = 0; - rdp->n_force_qs_snap = rsp->n_force_qs; - } else if (rdp->qlen < rdp->qlen_last_fqs_check - qhimark) - rdp->qlen_last_fqs_check = rdp->qlen; - WARN_ON_ONCE((rdp->nxtlist == NULL) != (rdp->qlen == 0)); - - local_irq_restore(flags); - - /* Re-invoke RCU core processing if there are callbacks remaining. */ - if (cpu_has_callbacks_ready_to_invoke(rdp)) - invoke_rcu_core(); -} - -/* - * Check to see if this CPU is in a non-context-switch quiescent state - * (user mode or idle loop for rcu, non-softirq execution for rcu_bh). - * Also schedule RCU core processing. - * - * This function must be called from hardirq context. It is normally - * invoked from the scheduling-clock interrupt. If rcu_pending returns - * false, there is no point in invoking rcu_check_callbacks(). - */ -void rcu_check_callbacks(int cpu, int user) -{ - trace_rcu_utilization(TPS("Start scheduler-tick")); - increment_cpu_stall_ticks(); - if (user || rcu_is_cpu_rrupt_from_idle()) { - - /* - * Get here if this CPU took its interrupt from user - * mode or from the idle loop, and if this is not a - * nested interrupt. In this case, the CPU is in - * a quiescent state, so note it. - * - * No memory barrier is required here because both - * rcu_sched_qs() and rcu_bh_qs() reference only CPU-local - * variables that other CPUs neither access nor modify, - * at least not while the corresponding CPU is online. - */ - - rcu_sched_qs(cpu); - rcu_bh_qs(cpu); - - } else if (!in_softirq()) { - - /* - * Get here if this CPU did not take its interrupt from - * softirq, in other words, if it is not interrupting - * a rcu_bh read-side critical section. This is an _bh - * critical section, so note it. - */ - - rcu_bh_qs(cpu); - } - rcu_preempt_check_callbacks(cpu); - if (rcu_pending(cpu)) - invoke_rcu_core(); - trace_rcu_utilization(TPS("End scheduler-tick")); -} - -/* - * Scan the leaf rcu_node structures, processing dyntick state for any that - * have not yet encountered a quiescent state, using the function specified. - * Also initiate boosting for any threads blocked on the root rcu_node. - * - * The caller must have suppressed start of new grace periods. - */ -static void force_qs_rnp(struct rcu_state *rsp, - int (*f)(struct rcu_data *rsp, bool *isidle, - unsigned long *maxj), - bool *isidle, unsigned long *maxj) -{ - unsigned long bit; - int cpu; - unsigned long flags; - unsigned long mask; - struct rcu_node *rnp; - - rcu_for_each_leaf_node(rsp, rnp) { - cond_resched(); - mask = 0; - raw_spin_lock_irqsave(&rnp->lock, flags); - if (!rcu_gp_in_progress(rsp)) { - raw_spin_unlock_irqrestore(&rnp->lock, flags); - return; - } - if (rnp->qsmask == 0) { - rcu_initiate_boost(rnp, flags); /* releases rnp->lock */ - continue; - } - cpu = rnp->grplo; - bit = 1; - for (; cpu <= rnp->grphi; cpu++, bit <<= 1) { - if ((rnp->qsmask & bit) != 0) { - if ((rnp->qsmaskinit & bit) != 0) - *isidle = 0; - if (f(per_cpu_ptr(rsp->rda, cpu), isidle, maxj)) - mask |= bit; - } - } - if (mask != 0) { - - /* rcu_report_qs_rnp() releases rnp->lock. */ - rcu_report_qs_rnp(mask, rsp, rnp, flags); - continue; - } - raw_spin_unlock_irqrestore(&rnp->lock, flags); - } - rnp = rcu_get_root(rsp); - if (rnp->qsmask == 0) { - raw_spin_lock_irqsave(&rnp->lock, flags); - rcu_initiate_boost(rnp, flags); /* releases rnp->lock. */ - } -} - -/* - * Force quiescent states on reluctant CPUs, and also detect which - * CPUs are in dyntick-idle mode. - */ -static void force_quiescent_state(struct rcu_state *rsp) -{ - unsigned long flags; - bool ret; - struct rcu_node *rnp; - struct rcu_node *rnp_old = NULL; - - /* Funnel through hierarchy to reduce memory contention. */ - rnp = per_cpu_ptr(rsp->rda, raw_smp_processor_id())->mynode; - for (; rnp != NULL; rnp = rnp->parent) { - ret = (ACCESS_ONCE(rsp->gp_flags) & RCU_GP_FLAG_FQS) || - !raw_spin_trylock(&rnp->fqslock); - if (rnp_old != NULL) - raw_spin_unlock(&rnp_old->fqslock); - if (ret) { - rsp->n_force_qs_lh++; - return; - } - rnp_old = rnp; - } - /* rnp_old == rcu_get_root(rsp), rnp == NULL. */ - - /* Reached the root of the rcu_node tree, acquire lock. */ - raw_spin_lock_irqsave(&rnp_old->lock, flags); - raw_spin_unlock(&rnp_old->fqslock); - if (ACCESS_ONCE(rsp->gp_flags) & RCU_GP_FLAG_FQS) { - rsp->n_force_qs_lh++; - raw_spin_unlock_irqrestore(&rnp_old->lock, flags); - return; /* Someone beat us to it. */ - } - rsp->gp_flags |= RCU_GP_FLAG_FQS; - raw_spin_unlock_irqrestore(&rnp_old->lock, flags); - wake_up(&rsp->gp_wq); /* Memory barrier implied by wake_up() path. */ -} - -/* - * This does the RCU core processing work for the specified rcu_state - * and rcu_data structures. This may be called only from the CPU to - * whom the rdp belongs. - */ -static void -__rcu_process_callbacks(struct rcu_state *rsp) -{ - unsigned long flags; - struct rcu_data *rdp = __this_cpu_ptr(rsp->rda); - - WARN_ON_ONCE(rdp->beenonline == 0); - - /* Update RCU state based on any recent quiescent states. */ - rcu_check_quiescent_state(rsp, rdp); - - /* Does this CPU require a not-yet-started grace period? */ - local_irq_save(flags); - if (cpu_needs_another_gp(rsp, rdp)) { - raw_spin_lock(&rcu_get_root(rsp)->lock); /* irqs disabled. */ - rcu_start_gp(rsp); - raw_spin_unlock_irqrestore(&rcu_get_root(rsp)->lock, flags); - } else { - local_irq_restore(flags); - } - - /* If there are callbacks ready, invoke them. */ - if (cpu_has_callbacks_ready_to_invoke(rdp)) - invoke_rcu_callbacks(rsp, rdp); -} - -/* - * Do RCU core processing for the current CPU. - */ -static void rcu_process_callbacks(struct softirq_action *unused) -{ - struct rcu_state *rsp; - - if (cpu_is_offline(smp_processor_id())) - return; - trace_rcu_utilization(TPS("Start RCU core")); - for_each_rcu_flavor(rsp) - __rcu_process_callbacks(rsp); - trace_rcu_utilization(TPS("End RCU core")); -} - -/* - * Schedule RCU callback invocation. If the specified type of RCU - * does not support RCU priority boosting, just do a direct call, - * otherwise wake up the per-CPU kernel kthread. Note that because we - * are running on the current CPU with interrupts disabled, the - * rcu_cpu_kthread_task cannot disappear out from under us. - */ -static void invoke_rcu_callbacks(struct rcu_state *rsp, struct rcu_data *rdp) -{ - if (unlikely(!ACCESS_ONCE(rcu_scheduler_fully_active))) - return; - if (likely(!rsp->boost)) { - rcu_do_batch(rsp, rdp); - return; - } - invoke_rcu_callbacks_kthread(); -} - -static void invoke_rcu_core(void) -{ - if (cpu_online(smp_processor_id())) - raise_softirq(RCU_SOFTIRQ); -} - -/* - * Handle any core-RCU processing required by a call_rcu() invocation. - */ -static void __call_rcu_core(struct rcu_state *rsp, struct rcu_data *rdp, - struct rcu_head *head, unsigned long flags) -{ - /* - * If called from an extended quiescent state, invoke the RCU - * core in order to force a re-evaluation of RCU's idleness. - */ - if (!rcu_is_watching() && cpu_online(smp_processor_id())) - invoke_rcu_core(); - - /* If interrupts were disabled or CPU offline, don't invoke RCU core. */ - if (irqs_disabled_flags(flags) || cpu_is_offline(smp_processor_id())) - return; - - /* - * Force the grace period if too many callbacks or too long waiting. - * Enforce hysteresis, and don't invoke force_quiescent_state() - * if some other CPU has recently done so. Also, don't bother - * invoking force_quiescent_state() if the newly enqueued callback - * is the only one waiting for a grace period to complete. - */ - if (unlikely(rdp->qlen > rdp->qlen_last_fqs_check + qhimark)) { - - /* Are we ignoring a completed grace period? */ - note_gp_changes(rsp, rdp); - - /* Start a new grace period if one not already started. */ - if (!rcu_gp_in_progress(rsp)) { - struct rcu_node *rnp_root = rcu_get_root(rsp); - - raw_spin_lock(&rnp_root->lock); - rcu_start_gp(rsp); - raw_spin_unlock(&rnp_root->lock); - } else { - /* Give the grace period a kick. */ - rdp->blimit = LONG_MAX; - if (rsp->n_force_qs == rdp->n_force_qs_snap && - *rdp->nxttail[RCU_DONE_TAIL] != head) - force_quiescent_state(rsp); - rdp->n_force_qs_snap = rsp->n_force_qs; - rdp->qlen_last_fqs_check = rdp->qlen; - } - } -} - -/* - * RCU callback function to leak a callback. - */ -static void rcu_leak_callback(struct rcu_head *rhp) -{ -} - -/* - * Helper function for call_rcu() and friends. The cpu argument will - * normally be -1, indicating "currently running CPU". It may specify - * a CPU only if that CPU is a no-CBs CPU. Currently, only _rcu_barrier() - * is expected to specify a CPU. - */ -static void -__call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu), - struct rcu_state *rsp, int cpu, bool lazy) -{ - unsigned long flags; - struct rcu_data *rdp; - - WARN_ON_ONCE((unsigned long)head & 0x3); /* Misaligned rcu_head! */ - if (debug_rcu_head_queue(head)) { - /* Probable double call_rcu(), so leak the callback. */ - ACCESS_ONCE(head->func) = rcu_leak_callback; - WARN_ONCE(1, "__call_rcu(): Leaked duplicate callback\n"); - return; - } - head->func = func; - head->next = NULL; - - /* - * Opportunistically note grace-period endings and beginnings. - * Note that we might see a beginning right after we see an - * end, but never vice versa, since this CPU has to pass through - * a quiescent state betweentimes. - */ - local_irq_save(flags); - rdp = this_cpu_ptr(rsp->rda); - - /* Add the callback to our list. */ - if (unlikely(rdp->nxttail[RCU_NEXT_TAIL] == NULL) || cpu != -1) { - int offline; - - if (cpu != -1) - rdp = per_cpu_ptr(rsp->rda, cpu); - offline = !__call_rcu_nocb(rdp, head, lazy); - WARN_ON_ONCE(offline); - /* _call_rcu() is illegal on offline CPU; leak the callback. */ - local_irq_restore(flags); - return; - } - ACCESS_ONCE(rdp->qlen)++; - if (lazy) - rdp->qlen_lazy++; - else - rcu_idle_count_callbacks_posted(); - smp_mb(); /* Count before adding callback for rcu_barrier(). */ - *rdp->nxttail[RCU_NEXT_TAIL] = head; - rdp->nxttail[RCU_NEXT_TAIL] = &head->next; - - if (__is_kfree_rcu_offset((unsigned long)func)) - trace_rcu_kfree_callback(rsp->name, head, (unsigned long)func, - rdp->qlen_lazy, rdp->qlen); - else - trace_rcu_callback(rsp->name, head, rdp->qlen_lazy, rdp->qlen); - - /* Go handle any RCU core processing required. */ - __call_rcu_core(rsp, rdp, head, flags); - local_irq_restore(flags); -} - -/* - * Queue an RCU-sched callback for invocation after a grace period. - */ -void call_rcu_sched(struct rcu_head *head, void (*func)(struct rcu_head *rcu)) -{ - __call_rcu(head, func, &rcu_sched_state, -1, 0); -} -EXPORT_SYMBOL_GPL(call_rcu_sched); - -/* - * Queue an RCU callback for invocation after a quicker grace period. - */ -void call_rcu_bh(struct rcu_head *head, void (*func)(struct rcu_head *rcu)) -{ - __call_rcu(head, func, &rcu_bh_state, -1, 0); -} -EXPORT_SYMBOL_GPL(call_rcu_bh); - -/* - * Because a context switch is a grace period for RCU-sched and RCU-bh, - * any blocking grace-period wait automatically implies a grace period - * if there is only one CPU online at any point time during execution - * of either synchronize_sched() or synchronize_rcu_bh(). It is OK to - * occasionally incorrectly indicate that there are multiple CPUs online - * when there was in fact only one the whole time, as this just adds - * some overhead: RCU still operates correctly. - */ -static inline int rcu_blocking_is_gp(void) -{ - int ret; - - might_sleep(); /* Check for RCU read-side critical section. */ - preempt_disable(); - ret = num_online_cpus() <= 1; - preempt_enable(); - return ret; -} - -/** - * synchronize_sched - wait until an rcu-sched grace period has elapsed. - * - * Control will return to the caller some time after a full rcu-sched - * grace period has elapsed, in other words after all currently executing - * rcu-sched read-side critical sections have completed. These read-side - * critical sections are delimited by rcu_read_lock_sched() and - * rcu_read_unlock_sched(), and may be nested. Note that preempt_disable(), - * local_irq_disable(), and so on may be used in place of - * rcu_read_lock_sched(). - * - * This means that all preempt_disable code sequences, including NMI and - * non-threaded hardware-interrupt handlers, in progress on entry will - * have completed before this primitive returns. However, this does not - * guarantee that softirq handlers will have completed, since in some - * kernels, these handlers can run in process context, and can block. - * - * Note that this guarantee implies further memory-ordering guarantees. - * On systems with more than one CPU, when synchronize_sched() returns, - * each CPU is guaranteed to have executed a full memory barrier since the - * end of its last RCU-sched read-side critical section whose beginning - * preceded the call to synchronize_sched(). In addition, each CPU having - * an RCU read-side critical section that extends beyond the return from - * synchronize_sched() is guaranteed to have executed a full memory barrier - * after the beginning of synchronize_sched() and before the beginning of - * that RCU read-side critical section. Note that these guarantees include - * CPUs that are offline, idle, or executing in user mode, as well as CPUs - * that are executing in the kernel. - * - * Furthermore, if CPU A invoked synchronize_sched(), which returned - * to its caller on CPU B, then both CPU A and CPU B are guaranteed - * to have executed a full memory barrier during the execution of - * synchronize_sched() -- even if CPU A and CPU B are the same CPU (but - * again only if the system has more than one CPU). - * - * This primitive provides the guarantees made by the (now removed) - * synchronize_kernel() API. In contrast, synchronize_rcu() only - * guarantees that rcu_read_lock() sections will have completed. - * In "classic RCU", these two guarantees happen to be one and - * the same, but can differ in realtime RCU implementations. - */ -void synchronize_sched(void) -{ - rcu_lockdep_assert(!lock_is_held(&rcu_bh_lock_map) && - !lock_is_held(&rcu_lock_map) && - !lock_is_held(&rcu_sched_lock_map), - "Illegal synchronize_sched() in RCU-sched read-side critical section"); - if (rcu_blocking_is_gp()) - return; - if (rcu_expedited) - synchronize_sched_expedited(); - else - wait_rcu_gp(call_rcu_sched); -} -EXPORT_SYMBOL_GPL(synchronize_sched); - -/** - * synchronize_rcu_bh - wait until an rcu_bh grace period has elapsed. - * - * Control will return to the caller some time after a full rcu_bh grace - * period has elapsed, in other words after all currently executing rcu_bh - * read-side critical sections have completed. RCU read-side critical - * sections are delimited by rcu_read_lock_bh() and rcu_read_unlock_bh(), - * and may be nested. - * - * See the description of synchronize_sched() for more detailed information - * on memory ordering guarantees. - */ -void synchronize_rcu_bh(void) -{ - rcu_lockdep_assert(!lock_is_held(&rcu_bh_lock_map) && - !lock_is_held(&rcu_lock_map) && - !lock_is_held(&rcu_sched_lock_map), - "Illegal synchronize_rcu_bh() in RCU-bh read-side critical section"); - if (rcu_blocking_is_gp()) - return; - if (rcu_expedited) - synchronize_rcu_bh_expedited(); - else - wait_rcu_gp(call_rcu_bh); -} -EXPORT_SYMBOL_GPL(synchronize_rcu_bh); - -static int synchronize_sched_expedited_cpu_stop(void *data) -{ - /* - * There must be a full memory barrier on each affected CPU - * between the time that try_stop_cpus() is called and the - * time that it returns. - * - * In the current initial implementation of cpu_stop, the - * above condition is already met when the control reaches - * this point and the following smp_mb() is not strictly - * necessary. Do smp_mb() anyway for documentation and - * robustness against future implementation changes. - */ - smp_mb(); /* See above comment block. */ - return 0; -} - -/** - * synchronize_sched_expedited - Brute-force RCU-sched grace period - * - * Wait for an RCU-sched grace period to elapse, but use a "big hammer" - * approach to force the grace period to end quickly. This consumes - * significant time on all CPUs and is unfriendly to real-time workloads, - * so is thus not recommended for any sort of common-case code. In fact, - * if you are using synchronize_sched_expedited() in a loop, please - * restructure your code to batch your updates, and then use a single - * synchronize_sched() instead. - * - * Note that it is illegal to call this function while holding any lock - * that is acquired by a CPU-hotplug notifier. And yes, it is also illegal - * to call this function from a CPU-hotplug notifier. Failing to observe - * these restriction will result in deadlock. - * - * This implementation can be thought of as an application of ticket - * locking to RCU, with sync_sched_expedited_started and - * sync_sched_expedited_done taking on the roles of the halves - * of the ticket-lock word. Each task atomically increments - * sync_sched_expedited_started upon entry, snapshotting the old value, - * then attempts to stop all the CPUs. If this succeeds, then each - * CPU will have executed a context switch, resulting in an RCU-sched - * grace period. We are then done, so we use atomic_cmpxchg() to - * update sync_sched_expedited_done to match our snapshot -- but - * only if someone else has not already advanced past our snapshot. - * - * On the other hand, if try_stop_cpus() fails, we check the value - * of sync_sched_expedited_done. If it has advanced past our - * initial snapshot, then someone else must have forced a grace period - * some time after we took our snapshot. In this case, our work is - * done for us, and we can simply return. Otherwise, we try again, - * but keep our initial snapshot for purposes of checking for someone - * doing our work for us. - * - * If we fail too many times in a row, we fall back to synchronize_sched(). - */ -void synchronize_sched_expedited(void) -{ - long firstsnap, s, snap; - int trycount = 0; - struct rcu_state *rsp = &rcu_sched_state; - - /* - * If we are in danger of counter wrap, just do synchronize_sched(). - * By allowing sync_sched_expedited_started to advance no more than - * ULONG_MAX/8 ahead of sync_sched_expedited_done, we are ensuring - * that more than 3.5 billion CPUs would be required to force a - * counter wrap on a 32-bit system. Quite a few more CPUs would of - * course be required on a 64-bit system. - */ - if (ULONG_CMP_GE((ulong)atomic_long_read(&rsp->expedited_start), - (ulong)atomic_long_read(&rsp->expedited_done) + - ULONG_MAX / 8)) { - synchronize_sched(); - atomic_long_inc(&rsp->expedited_wrap); - return; - } - - /* - * Take a ticket. Note that atomic_inc_return() implies a - * full memory barrier. - */ - snap = atomic_long_inc_return(&rsp->expedited_start); - firstsnap = snap; - get_online_cpus(); - WARN_ON_ONCE(cpu_is_offline(raw_smp_processor_id())); - - /* - * Each pass through the following loop attempts to force a - * context switch on each CPU. - */ - while (try_stop_cpus(cpu_online_mask, - synchronize_sched_expedited_cpu_stop, - NULL) == -EAGAIN) { - put_online_cpus(); - atomic_long_inc(&rsp->expedited_tryfail); - - /* Check to see if someone else did our work for us. */ - s = atomic_long_read(&rsp->expedited_done); - if (ULONG_CMP_GE((ulong)s, (ulong)firstsnap)) { - /* ensure test happens before caller kfree */ - smp_mb__before_atomic_inc(); /* ^^^ */ - atomic_long_inc(&rsp->expedited_workdone1); - return; - } - - /* No joy, try again later. Or just synchronize_sched(). */ - if (trycount++ < 10) { - udelay(trycount * num_online_cpus()); - } else { - wait_rcu_gp(call_rcu_sched); - atomic_long_inc(&rsp->expedited_normal); - return; - } - - /* Recheck to see if someone else did our work for us. */ - s = atomic_long_read(&rsp->expedited_done); - if (ULONG_CMP_GE((ulong)s, (ulong)firstsnap)) { - /* ensure test happens before caller kfree */ - smp_mb__before_atomic_inc(); /* ^^^ */ - atomic_long_inc(&rsp->expedited_workdone2); - return; - } - - /* - * Refetching sync_sched_expedited_started allows later - * callers to piggyback on our grace period. We retry - * after they started, so our grace period works for them, - * and they started after our first try, so their grace - * period works for us. - */ - get_online_cpus(); - snap = atomic_long_read(&rsp->expedited_start); - smp_mb(); /* ensure read is before try_stop_cpus(). */ - } - atomic_long_inc(&rsp->expedited_stoppedcpus); - - /* - * Everyone up to our most recent fetch is covered by our grace - * period. Update the counter, but only if our work is still - * relevant -- which it won't be if someone who started later - * than we did already did their update. - */ - do { - atomic_long_inc(&rsp->expedited_done_tries); - s = atomic_long_read(&rsp->expedited_done); - if (ULONG_CMP_GE((ulong)s, (ulong)snap)) { - /* ensure test happens before caller kfree */ - smp_mb__before_atomic_inc(); /* ^^^ */ - atomic_long_inc(&rsp->expedited_done_lost); - break; - } - } while (atomic_long_cmpxchg(&rsp->expedited_done, s, snap) != s); - atomic_long_inc(&rsp->expedited_done_exit); - - put_online_cpus(); -} -EXPORT_SYMBOL_GPL(synchronize_sched_expedited); - -/* - * Check to see if there is any immediate RCU-related work to be done - * by the current CPU, for the specified type of RCU, returning 1 if so. - * The checks are in order of increasing expense: checks that can be - * carried out against CPU-local state are performed first. However, - * we must check for CPU stalls first, else we might not get a chance. - */ -static int __rcu_pending(struct rcu_state *rsp, struct rcu_data *rdp) -{ - struct rcu_node *rnp = rdp->mynode; - - rdp->n_rcu_pending++; - - /* Check for CPU stalls, if enabled. */ - check_cpu_stall(rsp, rdp); - - /* Is the RCU core waiting for a quiescent state from this CPU? */ - if (rcu_scheduler_fully_active && - rdp->qs_pending && !rdp->passed_quiesce) { - rdp->n_rp_qs_pending++; - } else if (rdp->qs_pending && rdp->passed_quiesce) { - rdp->n_rp_report_qs++; - return 1; - } - - /* Does this CPU have callbacks ready to invoke? */ - if (cpu_has_callbacks_ready_to_invoke(rdp)) { - rdp->n_rp_cb_ready++; - return 1; - } - - /* Has RCU gone idle with this CPU needing another grace period? */ - if (cpu_needs_another_gp(rsp, rdp)) { - rdp->n_rp_cpu_needs_gp++; - return 1; - } - - /* Has another RCU grace period completed? */ - if (ACCESS_ONCE(rnp->completed) != rdp->completed) { /* outside lock */ - rdp->n_rp_gp_completed++; - return 1; - } - - /* Has a new RCU grace period started? */ - if (ACCESS_ONCE(rnp->gpnum) != rdp->gpnum) { /* outside lock */ - rdp->n_rp_gp_started++; - return 1; - } - - /* nothing to do */ - rdp->n_rp_need_nothing++; - return 0; -} - -/* - * Check to see if there is any immediate RCU-related work to be done - * by the current CPU, returning 1 if so. This function is part of the - * RCU implementation; it is -not- an exported member of the RCU API. - */ -static int rcu_pending(int cpu) -{ - struct rcu_state *rsp; - - for_each_rcu_flavor(rsp) - if (__rcu_pending(rsp, per_cpu_ptr(rsp->rda, cpu))) - return 1; - return 0; -} - -/* - * Return true if the specified CPU has any callback. If all_lazy is - * non-NULL, store an indication of whether all callbacks are lazy. - * (If there are no callbacks, all of them are deemed to be lazy.) - */ -static int rcu_cpu_has_callbacks(int cpu, bool *all_lazy) -{ - bool al = true; - bool hc = false; - struct rcu_data *rdp; - struct rcu_state *rsp; - - for_each_rcu_flavor(rsp) { - rdp = per_cpu_ptr(rsp->rda, cpu); - if (!rdp->nxtlist) - continue; - hc = true; - if (rdp->qlen != rdp->qlen_lazy || !all_lazy) { - al = false; - break; - } - } - if (all_lazy) - *all_lazy = al; - return hc; -} - -/* - * Helper function for _rcu_barrier() tracing. If tracing is disabled, - * the compiler is expected to optimize this away. - */ -static void _rcu_barrier_trace(struct rcu_state *rsp, const char *s, - int cpu, unsigned long done) -{ - trace_rcu_barrier(rsp->name, s, cpu, - atomic_read(&rsp->barrier_cpu_count), done); -} - -/* - * RCU callback function for _rcu_barrier(). If we are last, wake - * up the task executing _rcu_barrier(). - */ -static void rcu_barrier_callback(struct rcu_head *rhp) -{ - struct rcu_data *rdp = container_of(rhp, struct rcu_data, barrier_head); - struct rcu_state *rsp = rdp->rsp; - - if (atomic_dec_and_test(&rsp->barrier_cpu_count)) { - _rcu_barrier_trace(rsp, "LastCB", -1, rsp->n_barrier_done); - complete(&rsp->barrier_completion); - } else { - _rcu_barrier_trace(rsp, "CB", -1, rsp->n_barrier_done); - } -} - -/* - * Called with preemption disabled, and from cross-cpu IRQ context. - */ -static void rcu_barrier_func(void *type) -{ - struct rcu_state *rsp = type; - struct rcu_data *rdp = __this_cpu_ptr(rsp->rda); - - _rcu_barrier_trace(rsp, "IRQ", -1, rsp->n_barrier_done); - atomic_inc(&rsp->barrier_cpu_count); - rsp->call(&rdp->barrier_head, rcu_barrier_callback); -} - -/* - * Orchestrate the specified type of RCU barrier, waiting for all - * RCU callbacks of the specified type to complete. - */ -static void _rcu_barrier(struct rcu_state *rsp) -{ - int cpu; - struct rcu_data *rdp; - unsigned long snap = ACCESS_ONCE(rsp->n_barrier_done); - unsigned long snap_done; - - _rcu_barrier_trace(rsp, "Begin", -1, snap); - - /* Take mutex to serialize concurrent rcu_barrier() requests. */ - mutex_lock(&rsp->barrier_mutex); - - /* - * Ensure that all prior references, including to ->n_barrier_done, - * are ordered before the _rcu_barrier() machinery. - */ - smp_mb(); /* See above block comment. */ - - /* - * Recheck ->n_barrier_done to see if others did our work for us. - * This means checking ->n_barrier_done for an even-to-odd-to-even - * transition. The "if" expression below therefore rounds the old - * value up to the next even number and adds two before comparing. - */ - snap_done = rsp->n_barrier_done; - _rcu_barrier_trace(rsp, "Check", -1, snap_done); - - /* - * If the value in snap is odd, we needed to wait for the current - * rcu_barrier() to complete, then wait for the next one, in other - * words, we need the value of snap_done to be three larger than - * the value of snap. On the other hand, if the value in snap is - * even, we only had to wait for the next rcu_barrier() to complete, - * in other words, we need the value of snap_done to be only two - * greater than the value of snap. The "(snap + 3) & ~0x1" computes - * this for us (thank you, Linus!). - */ - if (ULONG_CMP_GE(snap_done, (snap + 3) & ~0x1)) { - _rcu_barrier_trace(rsp, "EarlyExit", -1, snap_done); - smp_mb(); /* caller's subsequent code after above check. */ - mutex_unlock(&rsp->barrier_mutex); - return; - } - - /* - * Increment ->n_barrier_done to avoid duplicate work. Use - * ACCESS_ONCE() to prevent the compiler from speculating - * the increment to precede the early-exit check. - */ - ACCESS_ONCE(rsp->n_barrier_done)++; - WARN_ON_ONCE((rsp->n_barrier_done & 0x1) != 1); - _rcu_barrier_trace(rsp, "Inc1", -1, rsp->n_barrier_done); - smp_mb(); /* Order ->n_barrier_done increment with below mechanism. */ - - /* - * Initialize the count to one rather than to zero in order to - * avoid a too-soon return to zero in case of a short grace period - * (or preemption of this task). Exclude CPU-hotplug operations - * to ensure that no offline CPU has callbacks queued. - */ - init_completion(&rsp->barrier_completion); - atomic_set(&rsp->barrier_cpu_count, 1); - get_online_cpus(); - - /* - * Force each CPU with callbacks to register a new callback. - * When that callback is invoked, we will know that all of the - * corresponding CPU's preceding callbacks have been invoked. - */ - for_each_possible_cpu(cpu) { - if (!cpu_online(cpu) && !rcu_is_nocb_cpu(cpu)) - continue; - rdp = per_cpu_ptr(rsp->rda, cpu); - if (rcu_is_nocb_cpu(cpu)) { - _rcu_barrier_trace(rsp, "OnlineNoCB", cpu, - rsp->n_barrier_done); - atomic_inc(&rsp->barrier_cpu_count); - __call_rcu(&rdp->barrier_head, rcu_barrier_callback, - rsp, cpu, 0); - } else if (ACCESS_ONCE(rdp->qlen)) { - _rcu_barrier_trace(rsp, "OnlineQ", cpu, - rsp->n_barrier_done); - smp_call_function_single(cpu, rcu_barrier_func, rsp, 1); - } else { - _rcu_barrier_trace(rsp, "OnlineNQ", cpu, - rsp->n_barrier_done); - } - } - put_online_cpus(); - - /* - * Now that we have an rcu_barrier_callback() callback on each - * CPU, and thus each counted, remove the initial count. - */ - if (atomic_dec_and_test(&rsp->barrier_cpu_count)) - complete(&rsp->barrier_completion); - - /* Increment ->n_barrier_done to prevent duplicate work. */ - smp_mb(); /* Keep increment after above mechanism. */ - ACCESS_ONCE(rsp->n_barrier_done)++; - WARN_ON_ONCE((rsp->n_barrier_done & 0x1) != 0); - _rcu_barrier_trace(rsp, "Inc2", -1, rsp->n_barrier_done); - smp_mb(); /* Keep increment before caller's subsequent code. */ - - /* Wait for all rcu_barrier_callback() callbacks to be invoked. */ - wait_for_completion(&rsp->barrier_completion); - - /* Other rcu_barrier() invocations can now safely proceed. */ - mutex_unlock(&rsp->barrier_mutex); -} - -/** - * rcu_barrier_bh - Wait until all in-flight call_rcu_bh() callbacks complete. - */ -void rcu_barrier_bh(void) -{ - _rcu_barrier(&rcu_bh_state); -} -EXPORT_SYMBOL_GPL(rcu_barrier_bh); - -/** - * rcu_barrier_sched - Wait for in-flight call_rcu_sched() callbacks. - */ -void rcu_barrier_sched(void) -{ - _rcu_barrier(&rcu_sched_state); -} -EXPORT_SYMBOL_GPL(rcu_barrier_sched); - -/* - * Do boot-time initialization of a CPU's per-CPU RCU data. - */ -static void __init -rcu_boot_init_percpu_data(int cpu, struct rcu_state *rsp) -{ - unsigned long flags; - struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu); - struct rcu_node *rnp = rcu_get_root(rsp); - - /* Set up local state, ensuring consistent view of global state. */ - raw_spin_lock_irqsave(&rnp->lock, flags); - rdp->grpmask = 1UL << (cpu - rdp->mynode->grplo); - init_callback_list(rdp); - rdp->qlen_lazy = 0; - ACCESS_ONCE(rdp->qlen) = 0; - rdp->dynticks = &per_cpu(rcu_dynticks, cpu); - WARN_ON_ONCE(rdp->dynticks->dynticks_nesting != DYNTICK_TASK_EXIT_IDLE); - WARN_ON_ONCE(atomic_read(&rdp->dynticks->dynticks) != 1); - rdp->cpu = cpu; - rdp->rsp = rsp; - rcu_boot_init_nocb_percpu_data(rdp); - raw_spin_unlock_irqrestore(&rnp->lock, flags); -} - -/* - * Initialize a CPU's per-CPU RCU data. Note that only one online or - * offline event can be happening at a given time. Note also that we - * can accept some slop in the rsp->completed access due to the fact - * that this CPU cannot possibly have any RCU callbacks in flight yet. - */ -static void -rcu_init_percpu_data(int cpu, struct rcu_state *rsp, int preemptible) -{ - unsigned long flags; - unsigned long mask; - struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu); - struct rcu_node *rnp = rcu_get_root(rsp); - - /* Exclude new grace periods. */ - mutex_lock(&rsp->onoff_mutex); - - /* Set up local state, ensuring consistent view of global state. */ - raw_spin_lock_irqsave(&rnp->lock, flags); - rdp->beenonline = 1; /* We have now been online. */ - rdp->preemptible = preemptible; - rdp->qlen_last_fqs_check = 0; - rdp->n_force_qs_snap = rsp->n_force_qs; - rdp->blimit = blimit; - init_callback_list(rdp); /* Re-enable callbacks on this CPU. */ - rdp->dynticks->dynticks_nesting = DYNTICK_TASK_EXIT_IDLE; - rcu_sysidle_init_percpu_data(rdp->dynticks); - atomic_set(&rdp->dynticks->dynticks, - (atomic_read(&rdp->dynticks->dynticks) & ~0x1) + 1); - raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */ - - /* Add CPU to rcu_node bitmasks. */ - rnp = rdp->mynode; - mask = rdp->grpmask; - do { - /* Exclude any attempts to start a new GP on small systems. */ - raw_spin_lock(&rnp->lock); /* irqs already disabled. */ - rnp->qsmaskinit |= mask; - mask = rnp->grpmask; - if (rnp == rdp->mynode) { - /* - * If there is a grace period in progress, we will - * set up to wait for it next time we run the - * RCU core code. - */ - rdp->gpnum = rnp->completed; - rdp->completed = rnp->completed; - rdp->passed_quiesce = 0; - rdp->qs_pending = 0; - trace_rcu_grace_period(rsp->name, rdp->gpnum, TPS("cpuonl")); - } - raw_spin_unlock(&rnp->lock); /* irqs already disabled. */ - rnp = rnp->parent; - } while (rnp != NULL && !(rnp->qsmaskinit & mask)); - local_irq_restore(flags); - - mutex_unlock(&rsp->onoff_mutex); -} - -static void rcu_prepare_cpu(int cpu) -{ - struct rcu_state *rsp; - - for_each_rcu_flavor(rsp) - rcu_init_percpu_data(cpu, rsp, - strcmp(rsp->name, "rcu_preempt") == 0); -} - -/* - * Handle CPU online/offline notification events. - */ -static int rcu_cpu_notify(struct notifier_block *self, - unsigned long action, void *hcpu) -{ - long cpu = (long)hcpu; - struct rcu_data *rdp = per_cpu_ptr(rcu_state->rda, cpu); - struct rcu_node *rnp = rdp->mynode; - struct rcu_state *rsp; - - trace_rcu_utilization(TPS("Start CPU hotplug")); - switch (action) { - case CPU_UP_PREPARE: - case CPU_UP_PREPARE_FROZEN: - rcu_prepare_cpu(cpu); - rcu_prepare_kthreads(cpu); - break; - case CPU_ONLINE: - case CPU_DOWN_FAILED: - rcu_boost_kthread_setaffinity(rnp, -1); - break; - case CPU_DOWN_PREPARE: - rcu_boost_kthread_setaffinity(rnp, cpu); - break; - case CPU_DYING: - case CPU_DYING_FROZEN: - for_each_rcu_flavor(rsp) - rcu_cleanup_dying_cpu(rsp); - break; - case CPU_DEAD: - case CPU_DEAD_FROZEN: - case CPU_UP_CANCELED: - case CPU_UP_CANCELED_FROZEN: - for_each_rcu_flavor(rsp) - rcu_cleanup_dead_cpu(cpu, rsp); - break; - default: - break; - } - trace_rcu_utilization(TPS("End CPU hotplug")); - return NOTIFY_OK; -} - -static int rcu_pm_notify(struct notifier_block *self, - unsigned long action, void *hcpu) -{ - switch (action) { - case PM_HIBERNATION_PREPARE: - case PM_SUSPEND_PREPARE: - if (nr_cpu_ids <= 256) /* Expediting bad for large systems. */ - rcu_expedited = 1; - break; - case PM_POST_HIBERNATION: - case PM_POST_SUSPEND: - rcu_expedited = 0; - break; - default: - break; - } - return NOTIFY_OK; -} - -/* - * Spawn the kthread that handles this RCU flavor's grace periods. - */ -static int __init rcu_spawn_gp_kthread(void) -{ - unsigned long flags; - struct rcu_node *rnp; - struct rcu_state *rsp; - struct task_struct *t; - - for_each_rcu_flavor(rsp) { - t = kthread_run(rcu_gp_kthread, rsp, "%s", rsp->name); - BUG_ON(IS_ERR(t)); - rnp = rcu_get_root(rsp); - raw_spin_lock_irqsave(&rnp->lock, flags); - rsp->gp_kthread = t; - raw_spin_unlock_irqrestore(&rnp->lock, flags); - rcu_spawn_nocb_kthreads(rsp); - } - return 0; -} -early_initcall(rcu_spawn_gp_kthread); - -/* - * This function is invoked towards the end of the scheduler's initialization - * process. Before this is called, the idle task might contain - * RCU read-side critical sections (during which time, this idle - * task is booting the system). After this function is called, the - * idle tasks are prohibited from containing RCU read-side critical - * sections. This function also enables RCU lockdep checking. - */ -void rcu_scheduler_starting(void) -{ - WARN_ON(num_online_cpus() != 1); - WARN_ON(nr_context_switches() > 0); - rcu_scheduler_active = 1; -} - -/* - * Compute the per-level fanout, either using the exact fanout specified - * or balancing the tree, depending on CONFIG_RCU_FANOUT_EXACT. - */ -#ifdef CONFIG_RCU_FANOUT_EXACT -static void __init rcu_init_levelspread(struct rcu_state *rsp) -{ - int i; - - for (i = rcu_num_lvls - 1; i > 0; i--) - rsp->levelspread[i] = CONFIG_RCU_FANOUT; - rsp->levelspread[0] = rcu_fanout_leaf; -} -#else /* #ifdef CONFIG_RCU_FANOUT_EXACT */ -static void __init rcu_init_levelspread(struct rcu_state *rsp) -{ - int ccur; - int cprv; - int i; - - cprv = nr_cpu_ids; - for (i = rcu_num_lvls - 1; i >= 0; i--) { - ccur = rsp->levelcnt[i]; - rsp->levelspread[i] = (cprv + ccur - 1) / ccur; - cprv = ccur; - } -} -#endif /* #else #ifdef CONFIG_RCU_FANOUT_EXACT */ - -/* - * Helper function for rcu_init() that initializes one rcu_state structure. - */ -static void __init rcu_init_one(struct rcu_state *rsp, - struct rcu_data __percpu *rda) -{ - static char *buf[] = { "rcu_node_0", - "rcu_node_1", - "rcu_node_2", - "rcu_node_3" }; /* Match MAX_RCU_LVLS */ - static char *fqs[] = { "rcu_node_fqs_0", - "rcu_node_fqs_1", - "rcu_node_fqs_2", - "rcu_node_fqs_3" }; /* Match MAX_RCU_LVLS */ - int cpustride = 1; - int i; - int j; - struct rcu_node *rnp; - - BUILD_BUG_ON(MAX_RCU_LVLS > ARRAY_SIZE(buf)); /* Fix buf[] init! */ - - /* Silence gcc 4.8 warning about array index out of range. */ - if (rcu_num_lvls > RCU_NUM_LVLS) - panic("rcu_init_one: rcu_num_lvls overflow"); - - /* Initialize the level-tracking arrays. */ - - for (i = 0; i < rcu_num_lvls; i++) - rsp->levelcnt[i] = num_rcu_lvl[i]; - for (i = 1; i < rcu_num_lvls; i++) - rsp->level[i] = rsp->level[i - 1] + rsp->levelcnt[i - 1]; - rcu_init_levelspread(rsp); - - /* Initialize the elements themselves, starting from the leaves. */ - - for (i = rcu_num_lvls - 1; i >= 0; i--) { - cpustride *= rsp->levelspread[i]; - rnp = rsp->level[i]; - for (j = 0; j < rsp->levelcnt[i]; j++, rnp++) { - raw_spin_lock_init(&rnp->lock); - lockdep_set_class_and_name(&rnp->lock, - &rcu_node_class[i], buf[i]); - raw_spin_lock_init(&rnp->fqslock); - lockdep_set_class_and_name(&rnp->fqslock, - &rcu_fqs_class[i], fqs[i]); - rnp->gpnum = rsp->gpnum; - rnp->completed = rsp->completed; - rnp->qsmask = 0; - rnp->qsmaskinit = 0; - rnp->grplo = j * cpustride; - rnp->grphi = (j + 1) * cpustride - 1; - if (rnp->grphi >= NR_CPUS) - rnp->grphi = NR_CPUS - 1; - if (i == 0) { - rnp->grpnum = 0; - rnp->grpmask = 0; - rnp->parent = NULL; - } else { - rnp->grpnum = j % rsp->levelspread[i - 1]; - rnp->grpmask = 1UL << rnp->grpnum; - rnp->parent = rsp->level[i - 1] + - j / rsp->levelspread[i - 1]; - } - rnp->level = i; - INIT_LIST_HEAD(&rnp->blkd_tasks); - rcu_init_one_nocb(rnp); - } - } - - rsp->rda = rda; - init_waitqueue_head(&rsp->gp_wq); - init_irq_work(&rsp->wakeup_work, rsp_wakeup); - rnp = rsp->level[rcu_num_lvls - 1]; - for_each_possible_cpu(i) { - while (i > rnp->grphi) - rnp++; - per_cpu_ptr(rsp->rda, i)->mynode = rnp; - rcu_boot_init_percpu_data(i, rsp); - } - list_add(&rsp->flavors, &rcu_struct_flavors); -} - -/* - * Compute the rcu_node tree geometry from kernel parameters. This cannot - * replace the definitions in rcutree.h because those are needed to size - * the ->node array in the rcu_state structure. - */ -static void __init rcu_init_geometry(void) -{ - ulong d; - int i; - int j; - int n = nr_cpu_ids; - int rcu_capacity[MAX_RCU_LVLS + 1]; - - /* - * Initialize any unspecified boot parameters. - * The default values of jiffies_till_first_fqs and - * jiffies_till_next_fqs are set to the RCU_JIFFIES_TILL_FORCE_QS - * value, which is a function of HZ, then adding one for each - * RCU_JIFFIES_FQS_DIV CPUs that might be on the system. - */ - d = RCU_JIFFIES_TILL_FORCE_QS + nr_cpu_ids / RCU_JIFFIES_FQS_DIV; - if (jiffies_till_first_fqs == ULONG_MAX) - jiffies_till_first_fqs = d; - if (jiffies_till_next_fqs == ULONG_MAX) - jiffies_till_next_fqs = d; - - /* If the compile-time values are accurate, just leave. */ - if (rcu_fanout_leaf == CONFIG_RCU_FANOUT_LEAF && - nr_cpu_ids == NR_CPUS) - return; - - /* - * Compute number of nodes that can be handled an rcu_node tree - * with the given number of levels. Setting rcu_capacity[0] makes - * some of the arithmetic easier. - */ - rcu_capacity[0] = 1; - rcu_capacity[1] = rcu_fanout_leaf; - for (i = 2; i <= MAX_RCU_LVLS; i++) - rcu_capacity[i] = rcu_capacity[i - 1] * CONFIG_RCU_FANOUT; - - /* - * The boot-time rcu_fanout_leaf parameter is only permitted - * to increase the leaf-level fanout, not decrease it. Of course, - * the leaf-level fanout cannot exceed the number of bits in - * the rcu_node masks. Finally, the tree must be able to accommodate - * the configured number of CPUs. Complain and fall back to the - * compile-time values if these limits are exceeded. - */ - if (rcu_fanout_leaf < CONFIG_RCU_FANOUT_LEAF || - rcu_fanout_leaf > sizeof(unsigned long) * 8 || - n > rcu_capacity[MAX_RCU_LVLS]) { - WARN_ON(1); - return; - } - - /* Calculate the number of rcu_nodes at each level of the tree. */ - for (i = 1; i <= MAX_RCU_LVLS; i++) - if (n <= rcu_capacity[i]) { - for (j = 0; j <= i; j++) - num_rcu_lvl[j] = - DIV_ROUND_UP(n, rcu_capacity[i - j]); - rcu_num_lvls = i; - for (j = i + 1; j <= MAX_RCU_LVLS; j++) - num_rcu_lvl[j] = 0; - break; - } - - /* Calculate the total number of rcu_node structures. */ - rcu_num_nodes = 0; - for (i = 0; i <= MAX_RCU_LVLS; i++) - rcu_num_nodes += num_rcu_lvl[i]; - rcu_num_nodes -= n; -} - -void __init rcu_init(void) -{ - int cpu; - - rcu_bootup_announce(); - rcu_init_geometry(); - rcu_init_one(&rcu_bh_state, &rcu_bh_data); - rcu_init_one(&rcu_sched_state, &rcu_sched_data); - __rcu_init_preempt(); - open_softirq(RCU_SOFTIRQ, rcu_process_callbacks); - - /* - * We don't need protection against CPU-hotplug here because - * this is called early in boot, before either interrupts - * or the scheduler are operational. - */ - cpu_notifier(rcu_cpu_notify, 0); - pm_notifier(rcu_pm_notify, 0); - for_each_online_cpu(cpu) - rcu_cpu_notify(NULL, CPU_UP_PREPARE, (void *)(long)cpu); -} - -#include "rcutree_plugin.h" diff --git a/kernel/rcutree.h b/kernel/rcutree.h deleted file mode 100644 index 52be957..0000000 --- a/kernel/rcutree.h +++ /dev/null @@ -1,585 +0,0 @@ -/* - * Read-Copy Update mechanism for mutual exclusion (tree-based version) - * Internal non-public definitions. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Copyright IBM Corporation, 2008 - * - * Author: Ingo Molnar - * Paul E. McKenney - */ - -#include -#include -#include -#include -#include -#include - -/* - * Define shape of hierarchy based on NR_CPUS, CONFIG_RCU_FANOUT, and - * CONFIG_RCU_FANOUT_LEAF. - * In theory, it should be possible to add more levels straightforwardly. - * In practice, this did work well going from three levels to four. - * Of course, your mileage may vary. - */ -#define MAX_RCU_LVLS 4 -#define RCU_FANOUT_1 (CONFIG_RCU_FANOUT_LEAF) -#define RCU_FANOUT_2 (RCU_FANOUT_1 * CONFIG_RCU_FANOUT) -#define RCU_FANOUT_3 (RCU_FANOUT_2 * CONFIG_RCU_FANOUT) -#define RCU_FANOUT_4 (RCU_FANOUT_3 * CONFIG_RCU_FANOUT) - -#if NR_CPUS <= RCU_FANOUT_1 -# define RCU_NUM_LVLS 1 -# define NUM_RCU_LVL_0 1 -# define NUM_RCU_LVL_1 (NR_CPUS) -# define NUM_RCU_LVL_2 0 -# define NUM_RCU_LVL_3 0 -# define NUM_RCU_LVL_4 0 -#elif NR_CPUS <= RCU_FANOUT_2 -# define RCU_NUM_LVLS 2 -# define NUM_RCU_LVL_0 1 -# define NUM_RCU_LVL_1 DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_1) -# define NUM_RCU_LVL_2 (NR_CPUS) -# define NUM_RCU_LVL_3 0 -# define NUM_RCU_LVL_4 0 -#elif NR_CPUS <= RCU_FANOUT_3 -# define RCU_NUM_LVLS 3 -# define NUM_RCU_LVL_0 1 -# define NUM_RCU_LVL_1 DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_2) -# define NUM_RCU_LVL_2 DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_1) -# define NUM_RCU_LVL_3 (NR_CPUS) -# define NUM_RCU_LVL_4 0 -#elif NR_CPUS <= RCU_FANOUT_4 -# define RCU_NUM_LVLS 4 -# define NUM_RCU_LVL_0 1 -# define NUM_RCU_LVL_1 DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_3) -# define NUM_RCU_LVL_2 DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_2) -# define NUM_RCU_LVL_3 DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_1) -# define NUM_RCU_LVL_4 (NR_CPUS) -#else -# error "CONFIG_RCU_FANOUT insufficient for NR_CPUS" -#endif /* #if (NR_CPUS) <= RCU_FANOUT_1 */ - -#define RCU_SUM (NUM_RCU_LVL_0 + NUM_RCU_LVL_1 + NUM_RCU_LVL_2 + NUM_RCU_LVL_3 + NUM_RCU_LVL_4) -#define NUM_RCU_NODES (RCU_SUM - NR_CPUS) - -extern int rcu_num_lvls; -extern int rcu_num_nodes; - -/* - * Dynticks per-CPU state. - */ -struct rcu_dynticks { - long long dynticks_nesting; /* Track irq/process nesting level. */ - /* Process level is worth LLONG_MAX/2. */ - int dynticks_nmi_nesting; /* Track NMI nesting level. */ - atomic_t dynticks; /* Even value for idle, else odd. */ -#ifdef CONFIG_NO_HZ_FULL_SYSIDLE - long long dynticks_idle_nesting; - /* irq/process nesting level from idle. */ - atomic_t dynticks_idle; /* Even value for idle, else odd. */ - /* "Idle" excludes userspace execution. */ - unsigned long dynticks_idle_jiffies; - /* End of last non-NMI non-idle period. */ -#endif /* #ifdef CONFIG_NO_HZ_FULL_SYSIDLE */ -#ifdef CONFIG_RCU_FAST_NO_HZ - bool all_lazy; /* Are all CPU's CBs lazy? */ - unsigned long nonlazy_posted; - /* # times non-lazy CBs posted to CPU. */ - unsigned long nonlazy_posted_snap; - /* idle-period nonlazy_posted snapshot. */ - unsigned long last_accelerate; - /* Last jiffy CBs were accelerated. */ - unsigned long last_advance_all; - /* Last jiffy CBs were all advanced. */ - int tick_nohz_enabled_snap; /* Previously seen value from sysfs. */ -#endif /* #ifdef CONFIG_RCU_FAST_NO_HZ */ -}; - -/* RCU's kthread states for tracing. */ -#define RCU_KTHREAD_STOPPED 0 -#define RCU_KTHREAD_RUNNING 1 -#define RCU_KTHREAD_WAITING 2 -#define RCU_KTHREAD_OFFCPU 3 -#define RCU_KTHREAD_YIELDING 4 -#define RCU_KTHREAD_MAX 4 - -/* - * Definition for node within the RCU grace-period-detection hierarchy. - */ -struct rcu_node { - raw_spinlock_t lock; /* Root rcu_node's lock protects some */ - /* rcu_state fields as well as following. */ - unsigned long gpnum; /* Current grace period for this node. */ - /* This will either be equal to or one */ - /* behind the root rcu_node's gpnum. */ - unsigned long completed; /* Last GP completed for this node. */ - /* This will either be equal to or one */ - /* behind the root rcu_node's gpnum. */ - unsigned long qsmask; /* CPUs or groups that need to switch in */ - /* order for current grace period to proceed.*/ - /* In leaf rcu_node, each bit corresponds to */ - /* an rcu_data structure, otherwise, each */ - /* bit corresponds to a child rcu_node */ - /* structure. */ - unsigned long expmask; /* Groups that have ->blkd_tasks */ - /* elements that need to drain to allow the */ - /* current expedited grace period to */ - /* complete (only for TREE_PREEMPT_RCU). */ - unsigned long qsmaskinit; - /* Per-GP initial value for qsmask & expmask. */ - unsigned long grpmask; /* Mask to apply to parent qsmask. */ - /* Only one bit will be set in this mask. */ - int grplo; /* lowest-numbered CPU or group here. */ - int grphi; /* highest-numbered CPU or group here. */ - u8 grpnum; /* CPU/group number for next level up. */ - u8 level; /* root is at level 0. */ - struct rcu_node *parent; - struct list_head blkd_tasks; - /* Tasks blocked in RCU read-side critical */ - /* section. Tasks are placed at the head */ - /* of this list and age towards the tail. */ - struct list_head *gp_tasks; - /* Pointer to the first task blocking the */ - /* current grace period, or NULL if there */ - /* is no such task. */ - struct list_head *exp_tasks; - /* Pointer to the first task blocking the */ - /* current expedited grace period, or NULL */ - /* if there is no such task. If there */ - /* is no current expedited grace period, */ - /* then there can cannot be any such task. */ -#ifdef CONFIG_RCU_BOOST - struct list_head *boost_tasks; - /* Pointer to first task that needs to be */ - /* priority boosted, or NULL if no priority */ - /* boosting is needed for this rcu_node */ - /* structure. If there are no tasks */ - /* queued on this rcu_node structure that */ - /* are blocking the current grace period, */ - /* there can be no such task. */ - unsigned long boost_time; - /* When to start boosting (jiffies). */ - struct task_struct *boost_kthread_task; - /* kthread that takes care of priority */ - /* boosting for this rcu_node structure. */ - unsigned int boost_kthread_status; - /* State of boost_kthread_task for tracing. */ - unsigned long n_tasks_boosted; - /* Total number of tasks boosted. */ - unsigned long n_exp_boosts; - /* Number of tasks boosted for expedited GP. */ - unsigned long n_normal_boosts; - /* Number of tasks boosted for normal GP. */ - unsigned long n_balk_blkd_tasks; - /* Refused to boost: no blocked tasks. */ - unsigned long n_balk_exp_gp_tasks; - /* Refused to boost: nothing blocking GP. */ - unsigned long n_balk_boost_tasks; - /* Refused to boost: already boosting. */ - unsigned long n_balk_notblocked; - /* Refused to boost: RCU RS CS still running. */ - unsigned long n_balk_notyet; - /* Refused to boost: not yet time. */ - unsigned long n_balk_nos; - /* Refused to boost: not sure why, though. */ - /* This can happen due to race conditions. */ -#endif /* #ifdef CONFIG_RCU_BOOST */ -#ifdef CONFIG_RCU_NOCB_CPU - wait_queue_head_t nocb_gp_wq[2]; - /* Place for rcu_nocb_kthread() to wait GP. */ -#endif /* #ifdef CONFIG_RCU_NOCB_CPU */ - int need_future_gp[2]; - /* Counts of upcoming no-CB GP requests. */ - raw_spinlock_t fqslock ____cacheline_internodealigned_in_smp; -} ____cacheline_internodealigned_in_smp; - -/* - * Do a full breadth-first scan of the rcu_node structures for the - * specified rcu_state structure. - */ -#define rcu_for_each_node_breadth_first(rsp, rnp) \ - for ((rnp) = &(rsp)->node[0]; \ - (rnp) < &(rsp)->node[rcu_num_nodes]; (rnp)++) - -/* - * Do a breadth-first scan of the non-leaf rcu_node structures for the - * specified rcu_state structure. Note that if there is a singleton - * rcu_node tree with but one rcu_node structure, this loop is a no-op. - */ -#define rcu_for_each_nonleaf_node_breadth_first(rsp, rnp) \ - for ((rnp) = &(rsp)->node[0]; \ - (rnp) < (rsp)->level[rcu_num_lvls - 1]; (rnp)++) - -/* - * Scan the leaves of the rcu_node hierarchy for the specified rcu_state - * structure. Note that if there is a singleton rcu_node tree with but - * one rcu_node structure, this loop -will- visit the rcu_node structure. - * It is still a leaf node, even if it is also the root node. - */ -#define rcu_for_each_leaf_node(rsp, rnp) \ - for ((rnp) = (rsp)->level[rcu_num_lvls - 1]; \ - (rnp) < &(rsp)->node[rcu_num_nodes]; (rnp)++) - -/* Index values for nxttail array in struct rcu_data. */ -#define RCU_DONE_TAIL 0 /* Also RCU_WAIT head. */ -#define RCU_WAIT_TAIL 1 /* Also RCU_NEXT_READY head. */ -#define RCU_NEXT_READY_TAIL 2 /* Also RCU_NEXT head. */ -#define RCU_NEXT_TAIL 3 -#define RCU_NEXT_SIZE 4 - -/* Per-CPU data for read-copy update. */ -struct rcu_data { - /* 1) quiescent-state and grace-period handling : */ - unsigned long completed; /* Track rsp->completed gp number */ - /* in order to detect GP end. */ - unsigned long gpnum; /* Highest gp number that this CPU */ - /* is aware of having started. */ - bool passed_quiesce; /* User-mode/idle loop etc. */ - bool qs_pending; /* Core waits for quiesc state. */ - bool beenonline; /* CPU online at least once. */ - bool preemptible; /* Preemptible RCU? */ - struct rcu_node *mynode; /* This CPU's leaf of hierarchy */ - unsigned long grpmask; /* Mask to apply to leaf qsmask. */ -#ifdef CONFIG_RCU_CPU_STALL_INFO - unsigned long ticks_this_gp; /* The number of scheduling-clock */ - /* ticks this CPU has handled */ - /* during and after the last grace */ - /* period it is aware of. */ -#endif /* #ifdef CONFIG_RCU_CPU_STALL_INFO */ - - /* 2) batch handling */ - /* - * If nxtlist is not NULL, it is partitioned as follows. - * Any of the partitions might be empty, in which case the - * pointer to that partition will be equal to the pointer for - * the following partition. When the list is empty, all of - * the nxttail elements point to the ->nxtlist pointer itself, - * which in that case is NULL. - * - * [nxtlist, *nxttail[RCU_DONE_TAIL]): - * Entries that batch # <= ->completed - * The grace period for these entries has completed, and - * the other grace-period-completed entries may be moved - * here temporarily in rcu_process_callbacks(). - * [*nxttail[RCU_DONE_TAIL], *nxttail[RCU_WAIT_TAIL]): - * Entries that batch # <= ->completed - 1: waiting for current GP - * [*nxttail[RCU_WAIT_TAIL], *nxttail[RCU_NEXT_READY_TAIL]): - * Entries known to have arrived before current GP ended - * [*nxttail[RCU_NEXT_READY_TAIL], *nxttail[RCU_NEXT_TAIL]): - * Entries that might have arrived after current GP ended - * Note that the value of *nxttail[RCU_NEXT_TAIL] will - * always be NULL, as this is the end of the list. - */ - struct rcu_head *nxtlist; - struct rcu_head **nxttail[RCU_NEXT_SIZE]; - unsigned long nxtcompleted[RCU_NEXT_SIZE]; - /* grace periods for sublists. */ - long qlen_lazy; /* # of lazy queued callbacks */ - long qlen; /* # of queued callbacks, incl lazy */ - long qlen_last_fqs_check; - /* qlen at last check for QS forcing */ - unsigned long n_cbs_invoked; /* count of RCU cbs invoked. */ - unsigned long n_nocbs_invoked; /* count of no-CBs RCU cbs invoked. */ - unsigned long n_cbs_orphaned; /* RCU cbs orphaned by dying CPU */ - unsigned long n_cbs_adopted; /* RCU cbs adopted from dying CPU */ - unsigned long n_force_qs_snap; - /* did other CPU force QS recently? */ - long blimit; /* Upper limit on a processed batch */ - - /* 3) dynticks interface. */ - struct rcu_dynticks *dynticks; /* Shared per-CPU dynticks state. */ - int dynticks_snap; /* Per-GP tracking for dynticks. */ - - /* 4) reasons this CPU needed to be kicked by force_quiescent_state */ - unsigned long dynticks_fqs; /* Kicked due to dynticks idle. */ - unsigned long offline_fqs; /* Kicked due to being offline. */ - - /* 5) __rcu_pending() statistics. */ - unsigned long n_rcu_pending; /* rcu_pending() calls since boot. */ - unsigned long n_rp_qs_pending; - unsigned long n_rp_report_qs; - unsigned long n_rp_cb_ready; - unsigned long n_rp_cpu_needs_gp; - unsigned long n_rp_gp_completed; - unsigned long n_rp_gp_started; - unsigned long n_rp_need_nothing; - - /* 6) _rcu_barrier() and OOM callbacks. */ - struct rcu_head barrier_head; -#ifdef CONFIG_RCU_FAST_NO_HZ - struct rcu_head oom_head; -#endif /* #ifdef CONFIG_RCU_FAST_NO_HZ */ - - /* 7) Callback offloading. */ -#ifdef CONFIG_RCU_NOCB_CPU - struct rcu_head *nocb_head; /* CBs waiting for kthread. */ - struct rcu_head **nocb_tail; - atomic_long_t nocb_q_count; /* # CBs waiting for kthread */ - atomic_long_t nocb_q_count_lazy; /* (approximate). */ - int nocb_p_count; /* # CBs being invoked by kthread */ - int nocb_p_count_lazy; /* (approximate). */ - wait_queue_head_t nocb_wq; /* For nocb kthreads to sleep on. */ - struct task_struct *nocb_kthread; -#endif /* #ifdef CONFIG_RCU_NOCB_CPU */ - - /* 8) RCU CPU stall data. */ -#ifdef CONFIG_RCU_CPU_STALL_INFO - unsigned int softirq_snap; /* Snapshot of softirq activity. */ -#endif /* #ifdef CONFIG_RCU_CPU_STALL_INFO */ - - int cpu; - struct rcu_state *rsp; -}; - -/* Values for fqs_state field in struct rcu_state. */ -#define RCU_GP_IDLE 0 /* No grace period in progress. */ -#define RCU_GP_INIT 1 /* Grace period being initialized. */ -#define RCU_SAVE_DYNTICK 2 /* Need to scan dyntick state. */ -#define RCU_FORCE_QS 3 /* Need to force quiescent state. */ -#define RCU_SIGNAL_INIT RCU_SAVE_DYNTICK - -#define RCU_JIFFIES_TILL_FORCE_QS (1 + (HZ > 250) + (HZ > 500)) - /* For jiffies_till_first_fqs and */ - /* and jiffies_till_next_fqs. */ - -#define RCU_JIFFIES_FQS_DIV 256 /* Very large systems need more */ - /* delay between bouts of */ - /* quiescent-state forcing. */ - -#define RCU_STALL_RAT_DELAY 2 /* Allow other CPUs time to take */ - /* at least one scheduling clock */ - /* irq before ratting on them. */ - -#define rcu_wait(cond) \ -do { \ - for (;;) { \ - set_current_state(TASK_INTERRUPTIBLE); \ - if (cond) \ - break; \ - schedule(); \ - } \ - __set_current_state(TASK_RUNNING); \ -} while (0) - -/* - * RCU global state, including node hierarchy. This hierarchy is - * represented in "heap" form in a dense array. The root (first level) - * of the hierarchy is in ->node[0] (referenced by ->level[0]), the second - * level in ->node[1] through ->node[m] (->node[1] referenced by ->level[1]), - * and the third level in ->node[m+1] and following (->node[m+1] referenced - * by ->level[2]). The number of levels is determined by the number of - * CPUs and by CONFIG_RCU_FANOUT. Small systems will have a "hierarchy" - * consisting of a single rcu_node. - */ -struct rcu_state { - struct rcu_node node[NUM_RCU_NODES]; /* Hierarchy. */ - struct rcu_node *level[RCU_NUM_LVLS]; /* Hierarchy levels. */ - u32 levelcnt[MAX_RCU_LVLS + 1]; /* # nodes in each level. */ - u8 levelspread[RCU_NUM_LVLS]; /* kids/node in each level. */ - struct rcu_data __percpu *rda; /* pointer of percu rcu_data. */ - void (*call)(struct rcu_head *head, /* call_rcu() flavor. */ - void (*func)(struct rcu_head *head)); - - /* The following fields are guarded by the root rcu_node's lock. */ - - u8 fqs_state ____cacheline_internodealigned_in_smp; - /* Force QS state. */ - u8 boost; /* Subject to priority boost. */ - unsigned long gpnum; /* Current gp number. */ - unsigned long completed; /* # of last completed gp. */ - struct task_struct *gp_kthread; /* Task for grace periods. */ - wait_queue_head_t gp_wq; /* Where GP task waits. */ - int gp_flags; /* Commands for GP task. */ - - /* End of fields guarded by root rcu_node's lock. */ - - raw_spinlock_t orphan_lock ____cacheline_internodealigned_in_smp; - /* Protect following fields. */ - struct rcu_head *orphan_nxtlist; /* Orphaned callbacks that */ - /* need a grace period. */ - struct rcu_head **orphan_nxttail; /* Tail of above. */ - struct rcu_head *orphan_donelist; /* Orphaned callbacks that */ - /* are ready to invoke. */ - struct rcu_head **orphan_donetail; /* Tail of above. */ - long qlen_lazy; /* Number of lazy callbacks. */ - long qlen; /* Total number of callbacks. */ - /* End of fields guarded by orphan_lock. */ - - struct mutex onoff_mutex; /* Coordinate hotplug & GPs. */ - - struct mutex barrier_mutex; /* Guards barrier fields. */ - atomic_t barrier_cpu_count; /* # CPUs waiting on. */ - struct completion barrier_completion; /* Wake at barrier end. */ - unsigned long n_barrier_done; /* ++ at start and end of */ - /* _rcu_barrier(). */ - /* End of fields guarded by barrier_mutex. */ - - atomic_long_t expedited_start; /* Starting ticket. */ - atomic_long_t expedited_done; /* Done ticket. */ - atomic_long_t expedited_wrap; /* # near-wrap incidents. */ - atomic_long_t expedited_tryfail; /* # acquisition failures. */ - atomic_long_t expedited_workdone1; /* # done by others #1. */ - atomic_long_t expedited_workdone2; /* # done by others #2. */ - atomic_long_t expedited_normal; /* # fallbacks to normal. */ - atomic_long_t expedited_stoppedcpus; /* # successful stop_cpus. */ - atomic_long_t expedited_done_tries; /* # tries to update _done. */ - atomic_long_t expedited_done_lost; /* # times beaten to _done. */ - atomic_long_t expedited_done_exit; /* # times exited _done loop. */ - - unsigned long jiffies_force_qs; /* Time at which to invoke */ - /* force_quiescent_state(). */ - unsigned long n_force_qs; /* Number of calls to */ - /* force_quiescent_state(). */ - unsigned long n_force_qs_lh; /* ~Number of calls leaving */ - /* due to lock unavailable. */ - unsigned long n_force_qs_ngp; /* Number of calls leaving */ - /* due to no GP active. */ - unsigned long gp_start; /* Time at which GP started, */ - /* but in jiffies. */ - unsigned long jiffies_stall; /* Time at which to check */ - /* for CPU stalls. */ - unsigned long gp_max; /* Maximum GP duration in */ - /* jiffies. */ - const char *name; /* Name of structure. */ - char abbr; /* Abbreviated name. */ - struct list_head flavors; /* List of RCU flavors. */ - struct irq_work wakeup_work; /* Postponed wakeups */ -}; - -/* Values for rcu_state structure's gp_flags field. */ -#define RCU_GP_FLAG_INIT 0x1 /* Need grace-period initialization. */ -#define RCU_GP_FLAG_FQS 0x2 /* Need grace-period quiescent-state forcing. */ - -extern struct list_head rcu_struct_flavors; - -/* Sequence through rcu_state structures for each RCU flavor. */ -#define for_each_rcu_flavor(rsp) \ - list_for_each_entry((rsp), &rcu_struct_flavors, flavors) - -/* Return values for rcu_preempt_offline_tasks(). */ - -#define RCU_OFL_TASKS_NORM_GP 0x1 /* Tasks blocking normal */ - /* GP were moved to root. */ -#define RCU_OFL_TASKS_EXP_GP 0x2 /* Tasks blocking expedited */ - /* GP were moved to root. */ - -/* - * RCU implementation internal declarations: - */ -extern struct rcu_state rcu_sched_state; -DECLARE_PER_CPU(struct rcu_data, rcu_sched_data); - -extern struct rcu_state rcu_bh_state; -DECLARE_PER_CPU(struct rcu_data, rcu_bh_data); - -#ifdef CONFIG_TREE_PREEMPT_RCU -extern struct rcu_state rcu_preempt_state; -DECLARE_PER_CPU(struct rcu_data, rcu_preempt_data); -#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */ - -#ifdef CONFIG_RCU_BOOST -DECLARE_PER_CPU(unsigned int, rcu_cpu_kthread_status); -DECLARE_PER_CPU(int, rcu_cpu_kthread_cpu); -DECLARE_PER_CPU(unsigned int, rcu_cpu_kthread_loops); -DECLARE_PER_CPU(char, rcu_cpu_has_work); -#endif /* #ifdef CONFIG_RCU_BOOST */ - -#ifndef RCU_TREE_NONCORE - -/* Forward declarations for rcutree_plugin.h */ -static void rcu_bootup_announce(void); -long rcu_batches_completed(void); -static void rcu_preempt_note_context_switch(int cpu); -static int rcu_preempt_blocked_readers_cgp(struct rcu_node *rnp); -#ifdef CONFIG_HOTPLUG_CPU -static void rcu_report_unblock_qs_rnp(struct rcu_node *rnp, - unsigned long flags); -#endif /* #ifdef CONFIG_HOTPLUG_CPU */ -static void rcu_print_detail_task_stall(struct rcu_state *rsp); -static int rcu_print_task_stall(struct rcu_node *rnp); -static void rcu_preempt_check_blocked_tasks(struct rcu_node *rnp); -#ifdef CONFIG_HOTPLUG_CPU -static int rcu_preempt_offline_tasks(struct rcu_state *rsp, - struct rcu_node *rnp, - struct rcu_data *rdp); -#endif /* #ifdef CONFIG_HOTPLUG_CPU */ -static void rcu_preempt_check_callbacks(int cpu); -void call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu)); -#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_TREE_PREEMPT_RCU) -static void rcu_report_exp_rnp(struct rcu_state *rsp, struct rcu_node *rnp, - bool wake); -#endif /* #if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_TREE_PREEMPT_RCU) */ -static void __init __rcu_init_preempt(void); -static void rcu_initiate_boost(struct rcu_node *rnp, unsigned long flags); -static void rcu_preempt_boost_start_gp(struct rcu_node *rnp); -static void invoke_rcu_callbacks_kthread(void); -static bool rcu_is_callbacks_kthread(void); -#ifdef CONFIG_RCU_BOOST -static void rcu_preempt_do_callbacks(void); -static int rcu_spawn_one_boost_kthread(struct rcu_state *rsp, - struct rcu_node *rnp); -#endif /* #ifdef CONFIG_RCU_BOOST */ -static void rcu_prepare_kthreads(int cpu); -static void rcu_cleanup_after_idle(int cpu); -static void rcu_prepare_for_idle(int cpu); -static void rcu_idle_count_callbacks_posted(void); -static void print_cpu_stall_info_begin(void); -static void print_cpu_stall_info(struct rcu_state *rsp, int cpu); -static void print_cpu_stall_info_end(void); -static void zero_cpu_stall_ticks(struct rcu_data *rdp); -static void increment_cpu_stall_ticks(void); -static int rcu_nocb_needs_gp(struct rcu_state *rsp); -static void rcu_nocb_gp_set(struct rcu_node *rnp, int nrq); -static void rcu_nocb_gp_cleanup(struct rcu_state *rsp, struct rcu_node *rnp); -static void rcu_init_one_nocb(struct rcu_node *rnp); -static bool __call_rcu_nocb(struct rcu_data *rdp, struct rcu_head *rhp, - bool lazy); -static bool rcu_nocb_adopt_orphan_cbs(struct rcu_state *rsp, - struct rcu_data *rdp); -static void rcu_boot_init_nocb_percpu_data(struct rcu_data *rdp); -static void rcu_spawn_nocb_kthreads(struct rcu_state *rsp); -static void rcu_kick_nohz_cpu(int cpu); -static bool init_nocb_callback_list(struct rcu_data *rdp); -static void rcu_sysidle_enter(struct rcu_dynticks *rdtp, int irq); -static void rcu_sysidle_exit(struct rcu_dynticks *rdtp, int irq); -static void rcu_sysidle_check_cpu(struct rcu_data *rdp, bool *isidle, - unsigned long *maxj); -static bool is_sysidle_rcu_state(struct rcu_state *rsp); -static void rcu_sysidle_report_gp(struct rcu_state *rsp, int isidle, - unsigned long maxj); -static void rcu_bind_gp_kthread(void); -static void rcu_sysidle_init_percpu_data(struct rcu_dynticks *rdtp); - -#endif /* #ifndef RCU_TREE_NONCORE */ - -#ifdef CONFIG_RCU_TRACE -#ifdef CONFIG_RCU_NOCB_CPU -/* Sum up queue lengths for tracing. */ -static inline void rcu_nocb_q_lengths(struct rcu_data *rdp, long *ql, long *qll) -{ - *ql = atomic_long_read(&rdp->nocb_q_count) + rdp->nocb_p_count; - *qll = atomic_long_read(&rdp->nocb_q_count_lazy) + rdp->nocb_p_count_lazy; -} -#else /* #ifdef CONFIG_RCU_NOCB_CPU */ -static inline void rcu_nocb_q_lengths(struct rcu_data *rdp, long *ql, long *qll) -{ - *ql = 0; - *qll = 0; -} -#endif /* #else #ifdef CONFIG_RCU_NOCB_CPU */ -#endif /* #ifdef CONFIG_RCU_TRACE */ diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h deleted file mode 100644 index 8d85a5c..0000000 --- a/kernel/rcutree_plugin.h +++ /dev/null @@ -1,2831 +0,0 @@ -/* - * Read-Copy Update mechanism for mutual exclusion (tree-based version) - * Internal non-public definitions that provide either classic - * or preemptible semantics. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Copyright Red Hat, 2009 - * Copyright IBM Corporation, 2009 - * - * Author: Ingo Molnar - * Paul E. McKenney - */ - -#include -#include -#include -#include -#include "time/tick-internal.h" - -#define RCU_KTHREAD_PRIO 1 - -#ifdef CONFIG_RCU_BOOST -#define RCU_BOOST_PRIO CONFIG_RCU_BOOST_PRIO -#else -#define RCU_BOOST_PRIO RCU_KTHREAD_PRIO -#endif - -#ifdef CONFIG_RCU_NOCB_CPU -static cpumask_var_t rcu_nocb_mask; /* CPUs to have callbacks offloaded. */ -static bool have_rcu_nocb_mask; /* Was rcu_nocb_mask allocated? */ -static bool __read_mostly rcu_nocb_poll; /* Offload kthread are to poll. */ -static char __initdata nocb_buf[NR_CPUS * 5]; -#endif /* #ifdef CONFIG_RCU_NOCB_CPU */ - -/* - * Check the RCU kernel configuration parameters and print informative - * messages about anything out of the ordinary. If you like #ifdef, you - * will love this function. - */ -static void __init rcu_bootup_announce_oddness(void) -{ -#ifdef CONFIG_RCU_TRACE - pr_info("\tRCU debugfs-based tracing is enabled.\n"); -#endif -#if (defined(CONFIG_64BIT) && CONFIG_RCU_FANOUT != 64) || (!defined(CONFIG_64BIT) && CONFIG_RCU_FANOUT != 32) - pr_info("\tCONFIG_RCU_FANOUT set to non-default value of %d\n", - CONFIG_RCU_FANOUT); -#endif -#ifdef CONFIG_RCU_FANOUT_EXACT - pr_info("\tHierarchical RCU autobalancing is disabled.\n"); -#endif -#ifdef CONFIG_RCU_FAST_NO_HZ - pr_info("\tRCU dyntick-idle grace-period acceleration is enabled.\n"); -#endif -#ifdef CONFIG_PROVE_RCU - pr_info("\tRCU lockdep checking is enabled.\n"); -#endif -#ifdef CONFIG_RCU_TORTURE_TEST_RUNNABLE - pr_info("\tRCU torture testing starts during boot.\n"); -#endif -#if defined(CONFIG_TREE_PREEMPT_RCU) && !defined(CONFIG_RCU_CPU_STALL_VERBOSE) - pr_info("\tDump stacks of tasks blocking RCU-preempt GP.\n"); -#endif -#if defined(CONFIG_RCU_CPU_STALL_INFO) - pr_info("\tAdditional per-CPU info printed with stalls.\n"); -#endif -#if NUM_RCU_LVL_4 != 0 - pr_info("\tFour-level hierarchy is enabled.\n"); -#endif - if (rcu_fanout_leaf != CONFIG_RCU_FANOUT_LEAF) - pr_info("\tBoot-time adjustment of leaf fanout to %d.\n", rcu_fanout_leaf); - if (nr_cpu_ids != NR_CPUS) - pr_info("\tRCU restricting CPUs from NR_CPUS=%d to nr_cpu_ids=%d.\n", NR_CPUS, nr_cpu_ids); -#ifdef CONFIG_RCU_NOCB_CPU -#ifndef CONFIG_RCU_NOCB_CPU_NONE - if (!have_rcu_nocb_mask) { - zalloc_cpumask_var(&rcu_nocb_mask, GFP_KERNEL); - have_rcu_nocb_mask = true; - } -#ifdef CONFIG_RCU_NOCB_CPU_ZERO - pr_info("\tOffload RCU callbacks from CPU 0\n"); - cpumask_set_cpu(0, rcu_nocb_mask); -#endif /* #ifdef CONFIG_RCU_NOCB_CPU_ZERO */ -#ifdef CONFIG_RCU_NOCB_CPU_ALL - pr_info("\tOffload RCU callbacks from all CPUs\n"); - cpumask_copy(rcu_nocb_mask, cpu_possible_mask); -#endif /* #ifdef CONFIG_RCU_NOCB_CPU_ALL */ -#endif /* #ifndef CONFIG_RCU_NOCB_CPU_NONE */ - if (have_rcu_nocb_mask) { - if (!cpumask_subset(rcu_nocb_mask, cpu_possible_mask)) { - pr_info("\tNote: kernel parameter 'rcu_nocbs=' contains nonexistent CPUs.\n"); - cpumask_and(rcu_nocb_mask, cpu_possible_mask, - rcu_nocb_mask); - } - cpulist_scnprintf(nocb_buf, sizeof(nocb_buf), rcu_nocb_mask); - pr_info("\tOffload RCU callbacks from CPUs: %s.\n", nocb_buf); - if (rcu_nocb_poll) - pr_info("\tPoll for callbacks from no-CBs CPUs.\n"); - } -#endif /* #ifdef CONFIG_RCU_NOCB_CPU */ -} - -#ifdef CONFIG_TREE_PREEMPT_RCU - -RCU_STATE_INITIALIZER(rcu_preempt, 'p', call_rcu); -static struct rcu_state *rcu_state = &rcu_preempt_state; - -static int rcu_preempted_readers_exp(struct rcu_node *rnp); - -/* - * Tell them what RCU they are running. - */ -static void __init rcu_bootup_announce(void) -{ - pr_info("Preemptible hierarchical RCU implementation.\n"); - rcu_bootup_announce_oddness(); -} - -/* - * Return the number of RCU-preempt batches processed thus far - * for debug and statistics. - */ -long rcu_batches_completed_preempt(void) -{ - return rcu_preempt_state.completed; -} -EXPORT_SYMBOL_GPL(rcu_batches_completed_preempt); - -/* - * Return the number of RCU batches processed thus far for debug & stats. - */ -long rcu_batches_completed(void) -{ - return rcu_batches_completed_preempt(); -} -EXPORT_SYMBOL_GPL(rcu_batches_completed); - -/* - * Force a quiescent state for preemptible RCU. - */ -void rcu_force_quiescent_state(void) -{ - force_quiescent_state(&rcu_preempt_state); -} -EXPORT_SYMBOL_GPL(rcu_force_quiescent_state); - -/* - * Record a preemptible-RCU quiescent state for the specified CPU. Note - * that this just means that the task currently running on the CPU is - * not in a quiescent state. There might be any number of tasks blocked - * while in an RCU read-side critical section. - * - * Unlike the other rcu_*_qs() functions, callers to this function - * must disable irqs in order to protect the assignment to - * ->rcu_read_unlock_special. - */ -static void rcu_preempt_qs(int cpu) -{ - struct rcu_data *rdp = &per_cpu(rcu_preempt_data, cpu); - - if (rdp->passed_quiesce == 0) - trace_rcu_grace_period(TPS("rcu_preempt"), rdp->gpnum, TPS("cpuqs")); - rdp->passed_quiesce = 1; - current->rcu_read_unlock_special &= ~RCU_READ_UNLOCK_NEED_QS; -} - -/* - * We have entered the scheduler, and the current task might soon be - * context-switched away from. If this task is in an RCU read-side - * critical section, we will no longer be able to rely on the CPU to - * record that fact, so we enqueue the task on the blkd_tasks list. - * The task will dequeue itself when it exits the outermost enclosing - * RCU read-side critical section. Therefore, the current grace period - * cannot be permitted to complete until the blkd_tasks list entries - * predating the current grace period drain, in other words, until - * rnp->gp_tasks becomes NULL. - * - * Caller must disable preemption. - */ -static void rcu_preempt_note_context_switch(int cpu) -{ - struct task_struct *t = current; - unsigned long flags; - struct rcu_data *rdp; - struct rcu_node *rnp; - - if (t->rcu_read_lock_nesting > 0 && - (t->rcu_read_unlock_special & RCU_READ_UNLOCK_BLOCKED) == 0) { - - /* Possibly blocking in an RCU read-side critical section. */ - rdp = per_cpu_ptr(rcu_preempt_state.rda, cpu); - rnp = rdp->mynode; - raw_spin_lock_irqsave(&rnp->lock, flags); - t->rcu_read_unlock_special |= RCU_READ_UNLOCK_BLOCKED; - t->rcu_blocked_node = rnp; - - /* - * If this CPU has already checked in, then this task - * will hold up the next grace period rather than the - * current grace period. Queue the task accordingly. - * If the task is queued for the current grace period - * (i.e., this CPU has not yet passed through a quiescent - * state for the current grace period), then as long - * as that task remains queued, the current grace period - * cannot end. Note that there is some uncertainty as - * to exactly when the current grace period started. - * We take a conservative approach, which can result - * in unnecessarily waiting on tasks that started very - * slightly after the current grace period began. C'est - * la vie!!! - * - * But first, note that the current CPU must still be - * on line! - */ - WARN_ON_ONCE((rdp->grpmask & rnp->qsmaskinit) == 0); - WARN_ON_ONCE(!list_empty(&t->rcu_node_entry)); - if ((rnp->qsmask & rdp->grpmask) && rnp->gp_tasks != NULL) { - list_add(&t->rcu_node_entry, rnp->gp_tasks->prev); - rnp->gp_tasks = &t->rcu_node_entry; -#ifdef CONFIG_RCU_BOOST - if (rnp->boost_tasks != NULL) - rnp->boost_tasks = rnp->gp_tasks; -#endif /* #ifdef CONFIG_RCU_BOOST */ - } else { - list_add(&t->rcu_node_entry, &rnp->blkd_tasks); - if (rnp->qsmask & rdp->grpmask) - rnp->gp_tasks = &t->rcu_node_entry; - } - trace_rcu_preempt_task(rdp->rsp->name, - t->pid, - (rnp->qsmask & rdp->grpmask) - ? rnp->gpnum - : rnp->gpnum + 1); - raw_spin_unlock_irqrestore(&rnp->lock, flags); - } else if (t->rcu_read_lock_nesting < 0 && - t->rcu_read_unlock_special) { - - /* - * Complete exit from RCU read-side critical section on - * behalf of preempted instance of __rcu_read_unlock(). - */ - rcu_read_unlock_special(t); - } - - /* - * Either we were not in an RCU read-side critical section to - * begin with, or we have now recorded that critical section - * globally. Either way, we can now note a quiescent state - * for this CPU. Again, if we were in an RCU read-side critical - * section, and if that critical section was blocking the current - * grace period, then the fact that the task has been enqueued - * means that we continue to block the current grace period. - */ - local_irq_save(flags); - rcu_preempt_qs(cpu); - local_irq_restore(flags); -} - -/* - * Check for preempted RCU readers blocking the current grace period - * for the specified rcu_node structure. If the caller needs a reliable - * answer, it must hold the rcu_node's ->lock. - */ -static int rcu_preempt_blocked_readers_cgp(struct rcu_node *rnp) -{ - return rnp->gp_tasks != NULL; -} - -/* - * Record a quiescent state for all tasks that were previously queued - * on the specified rcu_node structure and that were blocking the current - * RCU grace period. The caller must hold the specified rnp->lock with - * irqs disabled, and this lock is released upon return, but irqs remain - * disabled. - */ -static void rcu_report_unblock_qs_rnp(struct rcu_node *rnp, unsigned long flags) - __releases(rnp->lock) -{ - unsigned long mask; - struct rcu_node *rnp_p; - - if (rnp->qsmask != 0 || rcu_preempt_blocked_readers_cgp(rnp)) { - raw_spin_unlock_irqrestore(&rnp->lock, flags); - return; /* Still need more quiescent states! */ - } - - rnp_p = rnp->parent; - if (rnp_p == NULL) { - /* - * Either there is only one rcu_node in the tree, - * or tasks were kicked up to root rcu_node due to - * CPUs going offline. - */ - rcu_report_qs_rsp(&rcu_preempt_state, flags); - return; - } - - /* Report up the rest of the hierarchy. */ - mask = rnp->grpmask; - raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */ - raw_spin_lock(&rnp_p->lock); /* irqs already disabled. */ - rcu_report_qs_rnp(mask, &rcu_preempt_state, rnp_p, flags); -} - -/* - * Advance a ->blkd_tasks-list pointer to the next entry, instead - * returning NULL if at the end of the list. - */ -static struct list_head *rcu_next_node_entry(struct task_struct *t, - struct rcu_node *rnp) -{ - struct list_head *np; - - np = t->rcu_node_entry.next; - if (np == &rnp->blkd_tasks) - np = NULL; - return np; -} - -/* - * Handle special cases during rcu_read_unlock(), such as needing to - * notify RCU core processing or task having blocked during the RCU - * read-side critical section. - */ -void rcu_read_unlock_special(struct task_struct *t) -{ - int empty; - int empty_exp; - int empty_exp_now; - unsigned long flags; - struct list_head *np; -#ifdef CONFIG_RCU_BOOST - struct rt_mutex *rbmp = NULL; -#endif /* #ifdef CONFIG_RCU_BOOST */ - struct rcu_node *rnp; - int special; - - /* NMI handlers cannot block and cannot safely manipulate state. */ - if (in_nmi()) - return; - - local_irq_save(flags); - - /* - * If RCU core is waiting for this CPU to exit critical section, - * let it know that we have done so. - */ - special = t->rcu_read_unlock_special; - if (special & RCU_READ_UNLOCK_NEED_QS) { - rcu_preempt_qs(smp_processor_id()); - } - - /* Hardware IRQ handlers cannot block. */ - if (in_irq() || in_serving_softirq()) { - local_irq_restore(flags); - return; - } - - /* Clean up if blocked during RCU read-side critical section. */ - if (special & RCU_READ_UNLOCK_BLOCKED) { - t->rcu_read_unlock_special &= ~RCU_READ_UNLOCK_BLOCKED; - - /* - * Remove this task from the list it blocked on. The - * task can migrate while we acquire the lock, but at - * most one time. So at most two passes through loop. - */ - for (;;) { - rnp = t->rcu_blocked_node; - raw_spin_lock(&rnp->lock); /* irqs already disabled. */ - if (rnp == t->rcu_blocked_node) - break; - raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */ - } - empty = !rcu_preempt_blocked_readers_cgp(rnp); - empty_exp = !rcu_preempted_readers_exp(rnp); - smp_mb(); /* ensure expedited fastpath sees end of RCU c-s. */ - np = rcu_next_node_entry(t, rnp); - list_del_init(&t->rcu_node_entry); - t->rcu_blocked_node = NULL; - trace_rcu_unlock_preempted_task(TPS("rcu_preempt"), - rnp->gpnum, t->pid); - if (&t->rcu_node_entry == rnp->gp_tasks) - rnp->gp_tasks = np; - if (&t->rcu_node_entry == rnp->exp_tasks) - rnp->exp_tasks = np; -#ifdef CONFIG_RCU_BOOST - if (&t->rcu_node_entry == rnp->boost_tasks) - rnp->boost_tasks = np; - /* Snapshot/clear ->rcu_boost_mutex with rcu_node lock held. */ - if (t->rcu_boost_mutex) { - rbmp = t->rcu_boost_mutex; - t->rcu_boost_mutex = NULL; - } -#endif /* #ifdef CONFIG_RCU_BOOST */ - - /* - * If this was the last task on the current list, and if - * we aren't waiting on any CPUs, report the quiescent state. - * Note that rcu_report_unblock_qs_rnp() releases rnp->lock, - * so we must take a snapshot of the expedited state. - */ - empty_exp_now = !rcu_preempted_readers_exp(rnp); - if (!empty && !rcu_preempt_blocked_readers_cgp(rnp)) { - trace_rcu_quiescent_state_report(TPS("preempt_rcu"), - rnp->gpnum, - 0, rnp->qsmask, - rnp->level, - rnp->grplo, - rnp->grphi, - !!rnp->gp_tasks); - rcu_report_unblock_qs_rnp(rnp, flags); - } else { - raw_spin_unlock_irqrestore(&rnp->lock, flags); - } - -#ifdef CONFIG_RCU_BOOST - /* Unboost if we were boosted. */ - if (rbmp) - rt_mutex_unlock(rbmp); -#endif /* #ifdef CONFIG_RCU_BOOST */ - - /* - * If this was the last task on the expedited lists, - * then we need to report up the rcu_node hierarchy. - */ - if (!empty_exp && empty_exp_now) - rcu_report_exp_rnp(&rcu_preempt_state, rnp, true); - } else { - local_irq_restore(flags); - } -} - -#ifdef CONFIG_RCU_CPU_STALL_VERBOSE - -/* - * Dump detailed information for all tasks blocking the current RCU - * grace period on the specified rcu_node structure. - */ -static void rcu_print_detail_task_stall_rnp(struct rcu_node *rnp) -{ - unsigned long flags; - struct task_struct *t; - - raw_spin_lock_irqsave(&rnp->lock, flags); - if (!rcu_preempt_blocked_readers_cgp(rnp)) { - raw_spin_unlock_irqrestore(&rnp->lock, flags); - return; - } - t = list_entry(rnp->gp_tasks, - struct task_struct, rcu_node_entry); - list_for_each_entry_continue(t, &rnp->blkd_tasks, rcu_node_entry) - sched_show_task(t); - raw_spin_unlock_irqrestore(&rnp->lock, flags); -} - -/* - * Dump detailed information for all tasks blocking the current RCU - * grace period. - */ -static void rcu_print_detail_task_stall(struct rcu_state *rsp) -{ - struct rcu_node *rnp = rcu_get_root(rsp); - - rcu_print_detail_task_stall_rnp(rnp); - rcu_for_each_leaf_node(rsp, rnp) - rcu_print_detail_task_stall_rnp(rnp); -} - -#else /* #ifdef CONFIG_RCU_CPU_STALL_VERBOSE */ - -static void rcu_print_detail_task_stall(struct rcu_state *rsp) -{ -} - -#endif /* #else #ifdef CONFIG_RCU_CPU_STALL_VERBOSE */ - -#ifdef CONFIG_RCU_CPU_STALL_INFO - -static void rcu_print_task_stall_begin(struct rcu_node *rnp) -{ - pr_err("\tTasks blocked on level-%d rcu_node (CPUs %d-%d):", - rnp->level, rnp->grplo, rnp->grphi); -} - -static void rcu_print_task_stall_end(void) -{ - pr_cont("\n"); -} - -#else /* #ifdef CONFIG_RCU_CPU_STALL_INFO */ - -static void rcu_print_task_stall_begin(struct rcu_node *rnp) -{ -} - -static void rcu_print_task_stall_end(void) -{ -} - -#endif /* #else #ifdef CONFIG_RCU_CPU_STALL_INFO */ - -/* - * Scan the current list of tasks blocked within RCU read-side critical - * sections, printing out the tid of each. - */ -static int rcu_print_task_stall(struct rcu_node *rnp) -{ - struct task_struct *t; - int ndetected = 0; - - if (!rcu_preempt_blocked_readers_cgp(rnp)) - return 0; - rcu_print_task_stall_begin(rnp); - t = list_entry(rnp->gp_tasks, - struct task_struct, rcu_node_entry); - list_for_each_entry_continue(t, &rnp->blkd_tasks, rcu_node_entry) { - pr_cont(" P%d", t->pid); - ndetected++; - } - rcu_print_task_stall_end(); - return ndetected; -} - -/* - * Check that the list of blocked tasks for the newly completed grace - * period is in fact empty. It is a serious bug to complete a grace - * period that still has RCU readers blocked! This function must be - * invoked -before- updating this rnp's ->gpnum, and the rnp's ->lock - * must be held by the caller. - * - * Also, if there are blocked tasks on the list, they automatically - * block the newly created grace period, so set up ->gp_tasks accordingly. - */ -static void rcu_preempt_check_blocked_tasks(struct rcu_node *rnp) -{ - WARN_ON_ONCE(rcu_preempt_blocked_readers_cgp(rnp)); - if (!list_empty(&rnp->blkd_tasks)) - rnp->gp_tasks = rnp->blkd_tasks.next; - WARN_ON_ONCE(rnp->qsmask); -} - -#ifdef CONFIG_HOTPLUG_CPU - -/* - * Handle tasklist migration for case in which all CPUs covered by the - * specified rcu_node have gone offline. Move them up to the root - * rcu_node. The reason for not just moving them to the immediate - * parent is to remove the need for rcu_read_unlock_special() to - * make more than two attempts to acquire the target rcu_node's lock. - * Returns true if there were tasks blocking the current RCU grace - * period. - * - * Returns 1 if there was previously a task blocking the current grace - * period on the specified rcu_node structure. - * - * The caller must hold rnp->lock with irqs disabled. - */ -static int rcu_preempt_offline_tasks(struct rcu_state *rsp, - struct rcu_node *rnp, - struct rcu_data *rdp) -{ - struct list_head *lp; - struct list_head *lp_root; - int retval = 0; - struct rcu_node *rnp_root = rcu_get_root(rsp); - struct task_struct *t; - - if (rnp == rnp_root) { - WARN_ONCE(1, "Last CPU thought to be offlined?"); - return 0; /* Shouldn't happen: at least one CPU online. */ - } - - /* If we are on an internal node, complain bitterly. */ - WARN_ON_ONCE(rnp != rdp->mynode); - - /* - * Move tasks up to root rcu_node. Don't try to get fancy for - * this corner-case operation -- just put this node's tasks - * at the head of the root node's list, and update the root node's - * ->gp_tasks and ->exp_tasks pointers to those of this node's, - * if non-NULL. This might result in waiting for more tasks than - * absolutely necessary, but this is a good performance/complexity - * tradeoff. - */ - if (rcu_preempt_blocked_readers_cgp(rnp) && rnp->qsmask == 0) - retval |= RCU_OFL_TASKS_NORM_GP; - if (rcu_preempted_readers_exp(rnp)) - retval |= RCU_OFL_TASKS_EXP_GP; - lp = &rnp->blkd_tasks; - lp_root = &rnp_root->blkd_tasks; - while (!list_empty(lp)) { - t = list_entry(lp->next, typeof(*t), rcu_node_entry); - raw_spin_lock(&rnp_root->lock); /* irqs already disabled */ - list_del(&t->rcu_node_entry); - t->rcu_blocked_node = rnp_root; - list_add(&t->rcu_node_entry, lp_root); - if (&t->rcu_node_entry == rnp->gp_tasks) - rnp_root->gp_tasks = rnp->gp_tasks; - if (&t->rcu_node_entry == rnp->exp_tasks) - rnp_root->exp_tasks = rnp->exp_tasks; -#ifdef CONFIG_RCU_BOOST - if (&t->rcu_node_entry == rnp->boost_tasks) - rnp_root->boost_tasks = rnp->boost_tasks; -#endif /* #ifdef CONFIG_RCU_BOOST */ - raw_spin_unlock(&rnp_root->lock); /* irqs still disabled */ - } - - rnp->gp_tasks = NULL; - rnp->exp_tasks = NULL; -#ifdef CONFIG_RCU_BOOST - rnp->boost_tasks = NULL; - /* - * In case root is being boosted and leaf was not. Make sure - * that we boost the tasks blocking the current grace period - * in this case. - */ - raw_spin_lock(&rnp_root->lock); /* irqs already disabled */ - if (rnp_root->boost_tasks != NULL && - rnp_root->boost_tasks != rnp_root->gp_tasks && - rnp_root->boost_tasks != rnp_root->exp_tasks) - rnp_root->boost_tasks = rnp_root->gp_tasks; - raw_spin_unlock(&rnp_root->lock); /* irqs still disabled */ -#endif /* #ifdef CONFIG_RCU_BOOST */ - - return retval; -} - -#endif /* #ifdef CONFIG_HOTPLUG_CPU */ - -/* - * Check for a quiescent state from the current CPU. When a task blocks, - * the task is recorded in the corresponding CPU's rcu_node structure, - * which is checked elsewhere. - * - * Caller must disable hard irqs. - */ -static void rcu_preempt_check_callbacks(int cpu) -{ - struct task_struct *t = current; - - if (t->rcu_read_lock_nesting == 0) { - rcu_preempt_qs(cpu); - return; - } - if (t->rcu_read_lock_nesting > 0 && - per_cpu(rcu_preempt_data, cpu).qs_pending) - t->rcu_read_unlock_special |= RCU_READ_UNLOCK_NEED_QS; -} - -#ifdef CONFIG_RCU_BOOST - -static void rcu_preempt_do_callbacks(void) -{ - rcu_do_batch(&rcu_preempt_state, this_cpu_ptr(&rcu_preempt_data)); -} - -#endif /* #ifdef CONFIG_RCU_BOOST */ - -/* - * Queue a preemptible-RCU callback for invocation after a grace period. - */ -void call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu)) -{ - __call_rcu(head, func, &rcu_preempt_state, -1, 0); -} -EXPORT_SYMBOL_GPL(call_rcu); - -/* - * Queue an RCU callback for lazy invocation after a grace period. - * This will likely be later named something like "call_rcu_lazy()", - * but this change will require some way of tagging the lazy RCU - * callbacks in the list of pending callbacks. Until then, this - * function may only be called from __kfree_rcu(). - */ -void kfree_call_rcu(struct rcu_head *head, - void (*func)(struct rcu_head *rcu)) -{ - __call_rcu(head, func, &rcu_preempt_state, -1, 1); -} -EXPORT_SYMBOL_GPL(kfree_call_rcu); - -/** - * synchronize_rcu - wait until a grace period has elapsed. - * - * Control will return to the caller some time after a full grace - * period has elapsed, in other words after all currently executing RCU - * read-side critical sections have completed. Note, however, that - * upon return from synchronize_rcu(), the caller might well be executing - * concurrently with new RCU read-side critical sections that began while - * synchronize_rcu() was waiting. RCU read-side critical sections are - * delimited by rcu_read_lock() and rcu_read_unlock(), and may be nested. - * - * See the description of synchronize_sched() for more detailed information - * on memory ordering guarantees. - */ -void synchronize_rcu(void) -{ - rcu_lockdep_assert(!lock_is_held(&rcu_bh_lock_map) && - !lock_is_held(&rcu_lock_map) && - !lock_is_held(&rcu_sched_lock_map), - "Illegal synchronize_rcu() in RCU read-side critical section"); - if (!rcu_scheduler_active) - return; - if (rcu_expedited) - synchronize_rcu_expedited(); - else - wait_rcu_gp(call_rcu); -} -EXPORT_SYMBOL_GPL(synchronize_rcu); - -static DECLARE_WAIT_QUEUE_HEAD(sync_rcu_preempt_exp_wq); -static unsigned long sync_rcu_preempt_exp_count; -static DEFINE_MUTEX(sync_rcu_preempt_exp_mutex); - -/* - * Return non-zero if there are any tasks in RCU read-side critical - * sections blocking the current preemptible-RCU expedited grace period. - * If there is no preemptible-RCU expedited grace period currently in - * progress, returns zero unconditionally. - */ -static int rcu_preempted_readers_exp(struct rcu_node *rnp) -{ - return rnp->exp_tasks != NULL; -} - -/* - * return non-zero if there is no RCU expedited grace period in progress - * for the specified rcu_node structure, in other words, if all CPUs and - * tasks covered by the specified rcu_node structure have done their bit - * for the current expedited grace period. Works only for preemptible - * RCU -- other RCU implementation use other means. - * - * Caller must hold sync_rcu_preempt_exp_mutex. - */ -static int sync_rcu_preempt_exp_done(struct rcu_node *rnp) -{ - return !rcu_preempted_readers_exp(rnp) && - ACCESS_ONCE(rnp->expmask) == 0; -} - -/* - * Report the exit from RCU read-side critical section for the last task - * that queued itself during or before the current expedited preemptible-RCU - * grace period. This event is reported either to the rcu_node structure on - * which the task was queued or to one of that rcu_node structure's ancestors, - * recursively up the tree. (Calm down, calm down, we do the recursion - * iteratively!) - * - * Most callers will set the "wake" flag, but the task initiating the - * expedited grace period need not wake itself. - * - * Caller must hold sync_rcu_preempt_exp_mutex. - */ -static void rcu_report_exp_rnp(struct rcu_state *rsp, struct rcu_node *rnp, - bool wake) -{ - unsigned long flags; - unsigned long mask; - - raw_spin_lock_irqsave(&rnp->lock, flags); - for (;;) { - if (!sync_rcu_preempt_exp_done(rnp)) { - raw_spin_unlock_irqrestore(&rnp->lock, flags); - break; - } - if (rnp->parent == NULL) { - raw_spin_unlock_irqrestore(&rnp->lock, flags); - if (wake) - wake_up(&sync_rcu_preempt_exp_wq); - break; - } - mask = rnp->grpmask; - raw_spin_unlock(&rnp->lock); /* irqs remain disabled */ - rnp = rnp->parent; - raw_spin_lock(&rnp->lock); /* irqs already disabled */ - rnp->expmask &= ~mask; - } -} - -/* - * Snapshot the tasks blocking the newly started preemptible-RCU expedited - * grace period for the specified rcu_node structure. If there are no such - * tasks, report it up the rcu_node hierarchy. - * - * Caller must hold sync_rcu_preempt_exp_mutex and must exclude - * CPU hotplug operations. - */ -static void -sync_rcu_preempt_exp_init(struct rcu_state *rsp, struct rcu_node *rnp) -{ - unsigned long flags; - int must_wait = 0; - - raw_spin_lock_irqsave(&rnp->lock, flags); - if (list_empty(&rnp->blkd_tasks)) { - raw_spin_unlock_irqrestore(&rnp->lock, flags); - } else { - rnp->exp_tasks = rnp->blkd_tasks.next; - rcu_initiate_boost(rnp, flags); /* releases rnp->lock */ - must_wait = 1; - } - if (!must_wait) - rcu_report_exp_rnp(rsp, rnp, false); /* Don't wake self. */ -} - -/** - * synchronize_rcu_expedited - Brute-force RCU grace period - * - * Wait for an RCU-preempt grace period, but expedite it. The basic - * idea is to invoke synchronize_sched_expedited() to push all the tasks to - * the ->blkd_tasks lists and wait for this list to drain. This consumes - * significant time on all CPUs and is unfriendly to real-time workloads, - * so is thus not recommended for any sort of common-case code. - * In fact, if you are using synchronize_rcu_expedited() in a loop, - * please restructure your code to batch your updates, and then Use a - * single synchronize_rcu() instead. - * - * Note that it is illegal to call this function while holding any lock - * that is acquired by a CPU-hotplug notifier. And yes, it is also illegal - * to call this function from a CPU-hotplug notifier. Failing to observe - * these restriction will result in deadlock. - */ -void synchronize_rcu_expedited(void) -{ - unsigned long flags; - struct rcu_node *rnp; - struct rcu_state *rsp = &rcu_preempt_state; - unsigned long snap; - int trycount = 0; - - smp_mb(); /* Caller's modifications seen first by other CPUs. */ - snap = ACCESS_ONCE(sync_rcu_preempt_exp_count) + 1; - smp_mb(); /* Above access cannot bleed into critical section. */ - - /* - * Block CPU-hotplug operations. This means that any CPU-hotplug - * operation that finds an rcu_node structure with tasks in the - * process of being boosted will know that all tasks blocking - * this expedited grace period will already be in the process of - * being boosted. This simplifies the process of moving tasks - * from leaf to root rcu_node structures. - */ - get_online_cpus(); - - /* - * Acquire lock, falling back to synchronize_rcu() if too many - * lock-acquisition failures. Of course, if someone does the - * expedited grace period for us, just leave. - */ - while (!mutex_trylock(&sync_rcu_preempt_exp_mutex)) { - if (ULONG_CMP_LT(snap, - ACCESS_ONCE(sync_rcu_preempt_exp_count))) { - put_online_cpus(); - goto mb_ret; /* Others did our work for us. */ - } - if (trycount++ < 10) { - udelay(trycount * num_online_cpus()); - } else { - put_online_cpus(); - wait_rcu_gp(call_rcu); - return; - } - } - if (ULONG_CMP_LT(snap, ACCESS_ONCE(sync_rcu_preempt_exp_count))) { - put_online_cpus(); - goto unlock_mb_ret; /* Others did our work for us. */ - } - - /* force all RCU readers onto ->blkd_tasks lists. */ - synchronize_sched_expedited(); - - /* Initialize ->expmask for all non-leaf rcu_node structures. */ - rcu_for_each_nonleaf_node_breadth_first(rsp, rnp) { - raw_spin_lock_irqsave(&rnp->lock, flags); - rnp->expmask = rnp->qsmaskinit; - raw_spin_unlock_irqrestore(&rnp->lock, flags); - } - - /* Snapshot current state of ->blkd_tasks lists. */ - rcu_for_each_leaf_node(rsp, rnp) - sync_rcu_preempt_exp_init(rsp, rnp); - if (NUM_RCU_NODES > 1) - sync_rcu_preempt_exp_init(rsp, rcu_get_root(rsp)); - - put_online_cpus(); - - /* Wait for snapshotted ->blkd_tasks lists to drain. */ - rnp = rcu_get_root(rsp); - wait_event(sync_rcu_preempt_exp_wq, - sync_rcu_preempt_exp_done(rnp)); - - /* Clean up and exit. */ - smp_mb(); /* ensure expedited GP seen before counter increment. */ - ACCESS_ONCE(sync_rcu_preempt_exp_count)++; -unlock_mb_ret: - mutex_unlock(&sync_rcu_preempt_exp_mutex); -mb_ret: - smp_mb(); /* ensure subsequent action seen after grace period. */ -} -EXPORT_SYMBOL_GPL(synchronize_rcu_expedited); - -/** - * rcu_barrier - Wait until all in-flight call_rcu() callbacks complete. - * - * Note that this primitive does not necessarily wait for an RCU grace period - * to complete. For example, if there are no RCU callbacks queued anywhere - * in the system, then rcu_barrier() is within its rights to return - * immediately, without waiting for anything, much less an RCU grace period. - */ -void rcu_barrier(void) -{ - _rcu_barrier(&rcu_preempt_state); -} -EXPORT_SYMBOL_GPL(rcu_barrier); - -/* - * Initialize preemptible RCU's state structures. - */ -static void __init __rcu_init_preempt(void) -{ - rcu_init_one(&rcu_preempt_state, &rcu_preempt_data); -} - -/* - * Check for a task exiting while in a preemptible-RCU read-side - * critical section, clean up if so. No need to issue warnings, - * as debug_check_no_locks_held() already does this if lockdep - * is enabled. - */ -void exit_rcu(void) -{ - struct task_struct *t = current; - - if (likely(list_empty(¤t->rcu_node_entry))) - return; - t->rcu_read_lock_nesting = 1; - barrier(); - t->rcu_read_unlock_special = RCU_READ_UNLOCK_BLOCKED; - __rcu_read_unlock(); -} - -#else /* #ifdef CONFIG_TREE_PREEMPT_RCU */ - -static struct rcu_state *rcu_state = &rcu_sched_state; - -/* - * Tell them what RCU they are running. - */ -static void __init rcu_bootup_announce(void) -{ - pr_info("Hierarchical RCU implementation.\n"); - rcu_bootup_announce_oddness(); -} - -/* - * Return the number of RCU batches processed thus far for debug & stats. - */ -long rcu_batches_completed(void) -{ - return rcu_batches_completed_sched(); -} -EXPORT_SYMBOL_GPL(rcu_batches_completed); - -/* - * Force a quiescent state for RCU, which, because there is no preemptible - * RCU, becomes the same as rcu-sched. - */ -void rcu_force_quiescent_state(void) -{ - rcu_sched_force_quiescent_state(); -} -EXPORT_SYMBOL_GPL(rcu_force_quiescent_state); - -/* - * Because preemptible RCU does not exist, we never have to check for - * CPUs being in quiescent states. - */ -static void rcu_preempt_note_context_switch(int cpu) -{ -} - -/* - * Because preemptible RCU does not exist, there are never any preempted - * RCU readers. - */ -static int rcu_preempt_blocked_readers_cgp(struct rcu_node *rnp) -{ - return 0; -} - -#ifdef CONFIG_HOTPLUG_CPU - -/* Because preemptible RCU does not exist, no quieting of tasks. */ -static void rcu_report_unblock_qs_rnp(struct rcu_node *rnp, unsigned long flags) -{ - raw_spin_unlock_irqrestore(&rnp->lock, flags); -} - -#endif /* #ifdef CONFIG_HOTPLUG_CPU */ - -/* - * Because preemptible RCU does not exist, we never have to check for - * tasks blocked within RCU read-side critical sections. - */ -static void rcu_print_detail_task_stall(struct rcu_state *rsp) -{ -} - -/* - * Because preemptible RCU does not exist, we never have to check for - * tasks blocked within RCU read-side critical sections. - */ -static int rcu_print_task_stall(struct rcu_node *rnp) -{ - return 0; -} - -/* - * Because there is no preemptible RCU, there can be no readers blocked, - * so there is no need to check for blocked tasks. So check only for - * bogus qsmask values. - */ -static void rcu_preempt_check_blocked_tasks(struct rcu_node *rnp) -{ - WARN_ON_ONCE(rnp->qsmask); -} - -#ifdef CONFIG_HOTPLUG_CPU - -/* - * Because preemptible RCU does not exist, it never needs to migrate - * tasks that were blocked within RCU read-side critical sections, and - * such non-existent tasks cannot possibly have been blocking the current - * grace period. - */ -static int rcu_preempt_offline_tasks(struct rcu_state *rsp, - struct rcu_node *rnp, - struct rcu_data *rdp) -{ - return 0; -} - -#endif /* #ifdef CONFIG_HOTPLUG_CPU */ - -/* - * Because preemptible RCU does not exist, it never has any callbacks - * to check. - */ -static void rcu_preempt_check_callbacks(int cpu) -{ -} - -/* - * Queue an RCU callback for lazy invocation after a grace period. - * This will likely be later named something like "call_rcu_lazy()", - * but this change will require some way of tagging the lazy RCU - * callbacks in the list of pending callbacks. Until then, this - * function may only be called from __kfree_rcu(). - * - * Because there is no preemptible RCU, we use RCU-sched instead. - */ -void kfree_call_rcu(struct rcu_head *head, - void (*func)(struct rcu_head *rcu)) -{ - __call_rcu(head, func, &rcu_sched_state, -1, 1); -} -EXPORT_SYMBOL_GPL(kfree_call_rcu); - -/* - * Wait for an rcu-preempt grace period, but make it happen quickly. - * But because preemptible RCU does not exist, map to rcu-sched. - */ -void synchronize_rcu_expedited(void) -{ - synchronize_sched_expedited(); -} -EXPORT_SYMBOL_GPL(synchronize_rcu_expedited); - -#ifdef CONFIG_HOTPLUG_CPU - -/* - * Because preemptible RCU does not exist, there is never any need to - * report on tasks preempted in RCU read-side critical sections during - * expedited RCU grace periods. - */ -static void rcu_report_exp_rnp(struct rcu_state *rsp, struct rcu_node *rnp, - bool wake) -{ -} - -#endif /* #ifdef CONFIG_HOTPLUG_CPU */ - -/* - * Because preemptible RCU does not exist, rcu_barrier() is just - * another name for rcu_barrier_sched(). - */ -void rcu_barrier(void) -{ - rcu_barrier_sched(); -} -EXPORT_SYMBOL_GPL(rcu_barrier); - -/* - * Because preemptible RCU does not exist, it need not be initialized. - */ -static void __init __rcu_init_preempt(void) -{ -} - -/* - * Because preemptible RCU does not exist, tasks cannot possibly exit - * while in preemptible RCU read-side critical sections. - */ -void exit_rcu(void) -{ -} - -#endif /* #else #ifdef CONFIG_TREE_PREEMPT_RCU */ - -#ifdef CONFIG_RCU_BOOST - -#include "rtmutex_common.h" - -#ifdef CONFIG_RCU_TRACE - -static void rcu_initiate_boost_trace(struct rcu_node *rnp) -{ - if (list_empty(&rnp->blkd_tasks)) - rnp->n_balk_blkd_tasks++; - else if (rnp->exp_tasks == NULL && rnp->gp_tasks == NULL) - rnp->n_balk_exp_gp_tasks++; - else if (rnp->gp_tasks != NULL && rnp->boost_tasks != NULL) - rnp->n_balk_boost_tasks++; - else if (rnp->gp_tasks != NULL && rnp->qsmask != 0) - rnp->n_balk_notblocked++; - else if (rnp->gp_tasks != NULL && - ULONG_CMP_LT(jiffies, rnp->boost_time)) - rnp->n_balk_notyet++; - else - rnp->n_balk_nos++; -} - -#else /* #ifdef CONFIG_RCU_TRACE */ - -static void rcu_initiate_boost_trace(struct rcu_node *rnp) -{ -} - -#endif /* #else #ifdef CONFIG_RCU_TRACE */ - -static void rcu_wake_cond(struct task_struct *t, int status) -{ - /* - * If the thread is yielding, only wake it when this - * is invoked from idle - */ - if (status != RCU_KTHREAD_YIELDING || is_idle_task(current)) - wake_up_process(t); -} - -/* - * Carry out RCU priority boosting on the task indicated by ->exp_tasks - * or ->boost_tasks, advancing the pointer to the next task in the - * ->blkd_tasks list. - * - * Note that irqs must be enabled: boosting the task can block. - * Returns 1 if there are more tasks needing to be boosted. - */ -static int rcu_boost(struct rcu_node *rnp) -{ - unsigned long flags; - struct rt_mutex mtx; - struct task_struct *t; - struct list_head *tb; - - if (rnp->exp_tasks == NULL && rnp->boost_tasks == NULL) - return 0; /* Nothing left to boost. */ - - raw_spin_lock_irqsave(&rnp->lock, flags); - - /* - * Recheck under the lock: all tasks in need of boosting - * might exit their RCU read-side critical sections on their own. - */ - if (rnp->exp_tasks == NULL && rnp->boost_tasks == NULL) { - raw_spin_unlock_irqrestore(&rnp->lock, flags); - return 0; - } - - /* - * Preferentially boost tasks blocking expedited grace periods. - * This cannot starve the normal grace periods because a second - * expedited grace period must boost all blocked tasks, including - * those blocking the pre-existing normal grace period. - */ - if (rnp->exp_tasks != NULL) { - tb = rnp->exp_tasks; - rnp->n_exp_boosts++; - } else { - tb = rnp->boost_tasks; - rnp->n_normal_boosts++; - } - rnp->n_tasks_boosted++; - - /* - * We boost task t by manufacturing an rt_mutex that appears to - * be held by task t. We leave a pointer to that rt_mutex where - * task t can find it, and task t will release the mutex when it - * exits its outermost RCU read-side critical section. Then - * simply acquiring this artificial rt_mutex will boost task - * t's priority. (Thanks to tglx for suggesting this approach!) - * - * Note that task t must acquire rnp->lock to remove itself from - * the ->blkd_tasks list, which it will do from exit() if from - * nowhere else. We therefore are guaranteed that task t will - * stay around at least until we drop rnp->lock. Note that - * rnp->lock also resolves races between our priority boosting - * and task t's exiting its outermost RCU read-side critical - * section. - */ - t = container_of(tb, struct task_struct, rcu_node_entry); - rt_mutex_init_proxy_locked(&mtx, t); - t->rcu_boost_mutex = &mtx; - raw_spin_unlock_irqrestore(&rnp->lock, flags); - rt_mutex_lock(&mtx); /* Side effect: boosts task t's priority. */ - rt_mutex_unlock(&mtx); /* Keep lockdep happy. */ - - return ACCESS_ONCE(rnp->exp_tasks) != NULL || - ACCESS_ONCE(rnp->boost_tasks) != NULL; -} - -/* - * Priority-boosting kthread. One per leaf rcu_node and one for the - * root rcu_node. - */ -static int rcu_boost_kthread(void *arg) -{ - struct rcu_node *rnp = (struct rcu_node *)arg; - int spincnt = 0; - int more2boost; - - trace_rcu_utilization(TPS("Start boost kthread@init")); - for (;;) { - rnp->boost_kthread_status = RCU_KTHREAD_WAITING; - trace_rcu_utilization(TPS("End boost kthread@rcu_wait")); - rcu_wait(rnp->boost_tasks || rnp->exp_tasks); - trace_rcu_utilization(TPS("Start boost kthread@rcu_wait")); - rnp->boost_kthread_status = RCU_KTHREAD_RUNNING; - more2boost = rcu_boost(rnp); - if (more2boost) - spincnt++; - else - spincnt = 0; - if (spincnt > 10) { - rnp->boost_kthread_status = RCU_KTHREAD_YIELDING; - trace_rcu_utilization(TPS("End boost kthread@rcu_yield")); - schedule_timeout_interruptible(2); - trace_rcu_utilization(TPS("Start boost kthread@rcu_yield")); - spincnt = 0; - } - } - /* NOTREACHED */ - trace_rcu_utilization(TPS("End boost kthread@notreached")); - return 0; -} - -/* - * Check to see if it is time to start boosting RCU readers that are - * blocking the current grace period, and, if so, tell the per-rcu_node - * kthread to start boosting them. If there is an expedited grace - * period in progress, it is always time to boost. - * - * The caller must hold rnp->lock, which this function releases. - * The ->boost_kthread_task is immortal, so we don't need to worry - * about it going away. - */ -static void rcu_initiate_boost(struct rcu_node *rnp, unsigned long flags) -{ - struct task_struct *t; - - if (!rcu_preempt_blocked_readers_cgp(rnp) && rnp->exp_tasks == NULL) { - rnp->n_balk_exp_gp_tasks++; - raw_spin_unlock_irqrestore(&rnp->lock, flags); - return; - } - if (rnp->exp_tasks != NULL || - (rnp->gp_tasks != NULL && - rnp->boost_tasks == NULL && - rnp->qsmask == 0 && - ULONG_CMP_GE(jiffies, rnp->boost_time))) { - if (rnp->exp_tasks == NULL) - rnp->boost_tasks = rnp->gp_tasks; - raw_spin_unlock_irqrestore(&rnp->lock, flags); - t = rnp->boost_kthread_task; - if (t) - rcu_wake_cond(t, rnp->boost_kthread_status); - } else { - rcu_initiate_boost_trace(rnp); - raw_spin_unlock_irqrestore(&rnp->lock, flags); - } -} - -/* - * Wake up the per-CPU kthread to invoke RCU callbacks. - */ -static void invoke_rcu_callbacks_kthread(void) -{ - unsigned long flags; - - local_irq_save(flags); - __this_cpu_write(rcu_cpu_has_work, 1); - if (__this_cpu_read(rcu_cpu_kthread_task) != NULL && - current != __this_cpu_read(rcu_cpu_kthread_task)) { - rcu_wake_cond(__this_cpu_read(rcu_cpu_kthread_task), - __this_cpu_read(rcu_cpu_kthread_status)); - } - local_irq_restore(flags); -} - -/* - * Is the current CPU running the RCU-callbacks kthread? - * Caller must have preemption disabled. - */ -static bool rcu_is_callbacks_kthread(void) -{ - return __this_cpu_read(rcu_cpu_kthread_task) == current; -} - -#define RCU_BOOST_DELAY_JIFFIES DIV_ROUND_UP(CONFIG_RCU_BOOST_DELAY * HZ, 1000) - -/* - * Do priority-boost accounting for the start of a new grace period. - */ -static void rcu_preempt_boost_start_gp(struct rcu_node *rnp) -{ - rnp->boost_time = jiffies + RCU_BOOST_DELAY_JIFFIES; -} - -/* - * Create an RCU-boost kthread for the specified node if one does not - * already exist. We only create this kthread for preemptible RCU. - * Returns zero if all is well, a negated errno otherwise. - */ -static int rcu_spawn_one_boost_kthread(struct rcu_state *rsp, - struct rcu_node *rnp) -{ - int rnp_index = rnp - &rsp->node[0]; - unsigned long flags; - struct sched_param sp; - struct task_struct *t; - - if (&rcu_preempt_state != rsp) - return 0; - - if (!rcu_scheduler_fully_active || rnp->qsmaskinit == 0) - return 0; - - rsp->boost = 1; - if (rnp->boost_kthread_task != NULL) - return 0; - t = kthread_create(rcu_boost_kthread, (void *)rnp, - "rcub/%d", rnp_index); - if (IS_ERR(t)) - return PTR_ERR(t); - raw_spin_lock_irqsave(&rnp->lock, flags); - rnp->boost_kthread_task = t; - raw_spin_unlock_irqrestore(&rnp->lock, flags); - sp.sched_priority = RCU_BOOST_PRIO; - sched_setscheduler_nocheck(t, SCHED_FIFO, &sp); - wake_up_process(t); /* get to TASK_INTERRUPTIBLE quickly. */ - return 0; -} - -static void rcu_kthread_do_work(void) -{ - rcu_do_batch(&rcu_sched_state, this_cpu_ptr(&rcu_sched_data)); - rcu_do_batch(&rcu_bh_state, this_cpu_ptr(&rcu_bh_data)); - rcu_preempt_do_callbacks(); -} - -static void rcu_cpu_kthread_setup(unsigned int cpu) -{ - struct sched_param sp; - - sp.sched_priority = RCU_KTHREAD_PRIO; - sched_setscheduler_nocheck(current, SCHED_FIFO, &sp); -} - -static void rcu_cpu_kthread_park(unsigned int cpu) -{ - per_cpu(rcu_cpu_kthread_status, cpu) = RCU_KTHREAD_OFFCPU; -} - -static int rcu_cpu_kthread_should_run(unsigned int cpu) -{ - return __this_cpu_read(rcu_cpu_has_work); -} - -/* - * Per-CPU kernel thread that invokes RCU callbacks. This replaces the - * RCU softirq used in flavors and configurations of RCU that do not - * support RCU priority boosting. - */ -static void rcu_cpu_kthread(unsigned int cpu) -{ - unsigned int *statusp = this_cpu_ptr(&rcu_cpu_kthread_status); - char work, *workp = this_cpu_ptr(&rcu_cpu_has_work); - int spincnt; - - for (spincnt = 0; spincnt < 10; spincnt++) { - trace_rcu_utilization(TPS("Start CPU kthread@rcu_wait")); - local_bh_disable(); - *statusp = RCU_KTHREAD_RUNNING; - this_cpu_inc(rcu_cpu_kthread_loops); - local_irq_disable(); - work = *workp; - *workp = 0; - local_irq_enable(); - if (work) - rcu_kthread_do_work(); - local_bh_enable(); - if (*workp == 0) { - trace_rcu_utilization(TPS("End CPU kthread@rcu_wait")); - *statusp = RCU_KTHREAD_WAITING; - return; - } - } - *statusp = RCU_KTHREAD_YIELDING; - trace_rcu_utilization(TPS("Start CPU kthread@rcu_yield")); - schedule_timeout_interruptible(2); - trace_rcu_utilization(TPS("End CPU kthread@rcu_yield")); - *statusp = RCU_KTHREAD_WAITING; -} - -/* - * Set the per-rcu_node kthread's affinity to cover all CPUs that are - * served by the rcu_node in question. The CPU hotplug lock is still - * held, so the value of rnp->qsmaskinit will be stable. - * - * We don't include outgoingcpu in the affinity set, use -1 if there is - * no outgoing CPU. If there are no CPUs left in the affinity set, - * this function allows the kthread to execute on any CPU. - */ -static void rcu_boost_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu) -{ - struct task_struct *t = rnp->boost_kthread_task; - unsigned long mask = rnp->qsmaskinit; - cpumask_var_t cm; - int cpu; - - if (!t) - return; - if (!zalloc_cpumask_var(&cm, GFP_KERNEL)) - return; - for (cpu = rnp->grplo; cpu <= rnp->grphi; cpu++, mask >>= 1) - if ((mask & 0x1) && cpu != outgoingcpu) - cpumask_set_cpu(cpu, cm); - if (cpumask_weight(cm) == 0) { - cpumask_setall(cm); - for (cpu = rnp->grplo; cpu <= rnp->grphi; cpu++) - cpumask_clear_cpu(cpu, cm); - WARN_ON_ONCE(cpumask_weight(cm) == 0); - } - set_cpus_allowed_ptr(t, cm); - free_cpumask_var(cm); -} - -static struct smp_hotplug_thread rcu_cpu_thread_spec = { - .store = &rcu_cpu_kthread_task, - .thread_should_run = rcu_cpu_kthread_should_run, - .thread_fn = rcu_cpu_kthread, - .thread_comm = "rcuc/%u", - .setup = rcu_cpu_kthread_setup, - .park = rcu_cpu_kthread_park, -}; - -/* - * Spawn all kthreads -- called as soon as the scheduler is running. - */ -static int __init rcu_spawn_kthreads(void) -{ - struct rcu_node *rnp; - int cpu; - - rcu_scheduler_fully_active = 1; - for_each_possible_cpu(cpu) - per_cpu(rcu_cpu_has_work, cpu) = 0; - BUG_ON(smpboot_register_percpu_thread(&rcu_cpu_thread_spec)); - rnp = rcu_get_root(rcu_state); - (void)rcu_spawn_one_boost_kthread(rcu_state, rnp); - if (NUM_RCU_NODES > 1) { - rcu_for_each_leaf_node(rcu_state, rnp) - (void)rcu_spawn_one_boost_kthread(rcu_state, rnp); - } - return 0; -} -early_initcall(rcu_spawn_kthreads); - -static void rcu_prepare_kthreads(int cpu) -{ - struct rcu_data *rdp = per_cpu_ptr(rcu_state->rda, cpu); - struct rcu_node *rnp = rdp->mynode; - - /* Fire up the incoming CPU's kthread and leaf rcu_node kthread. */ - if (rcu_scheduler_fully_active) - (void)rcu_spawn_one_boost_kthread(rcu_state, rnp); -} - -#else /* #ifdef CONFIG_RCU_BOOST */ - -static void rcu_initiate_boost(struct rcu_node *rnp, unsigned long flags) -{ - raw_spin_unlock_irqrestore(&rnp->lock, flags); -} - -static void invoke_rcu_callbacks_kthread(void) -{ - WARN_ON_ONCE(1); -} - -static bool rcu_is_callbacks_kthread(void) -{ - return false; -} - -static void rcu_preempt_boost_start_gp(struct rcu_node *rnp) -{ -} - -static void rcu_boost_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu) -{ -} - -static int __init rcu_scheduler_really_started(void) -{ - rcu_scheduler_fully_active = 1; - return 0; -} -early_initcall(rcu_scheduler_really_started); - -static void rcu_prepare_kthreads(int cpu) -{ -} - -#endif /* #else #ifdef CONFIG_RCU_BOOST */ - -#if !defined(CONFIG_RCU_FAST_NO_HZ) - -/* - * Check to see if any future RCU-related work will need to be done - * by the current CPU, even if none need be done immediately, returning - * 1 if so. This function is part of the RCU implementation; it is -not- - * an exported member of the RCU API. - * - * Because we not have RCU_FAST_NO_HZ, just check whether this CPU needs - * any flavor of RCU. - */ -int rcu_needs_cpu(int cpu, unsigned long *delta_jiffies) -{ - *delta_jiffies = ULONG_MAX; - return rcu_cpu_has_callbacks(cpu, NULL); -} - -/* - * Because we do not have RCU_FAST_NO_HZ, don't bother cleaning up - * after it. - */ -static void rcu_cleanup_after_idle(int cpu) -{ -} - -/* - * Do the idle-entry grace-period work, which, because CONFIG_RCU_FAST_NO_HZ=n, - * is nothing. - */ -static void rcu_prepare_for_idle(int cpu) -{ -} - -/* - * Don't bother keeping a running count of the number of RCU callbacks - * posted because CONFIG_RCU_FAST_NO_HZ=n. - */ -static void rcu_idle_count_callbacks_posted(void) -{ -} - -#else /* #if !defined(CONFIG_RCU_FAST_NO_HZ) */ - -/* - * This code is invoked when a CPU goes idle, at which point we want - * to have the CPU do everything required for RCU so that it can enter - * the energy-efficient dyntick-idle mode. This is handled by a - * state machine implemented by rcu_prepare_for_idle() below. - * - * The following three proprocessor symbols control this state machine: - * - * RCU_IDLE_GP_DELAY gives the number of jiffies that a CPU is permitted - * to sleep in dyntick-idle mode with RCU callbacks pending. This - * is sized to be roughly one RCU grace period. Those energy-efficiency - * benchmarkers who might otherwise be tempted to set this to a large - * number, be warned: Setting RCU_IDLE_GP_DELAY too high can hang your - * system. And if you are -that- concerned about energy efficiency, - * just power the system down and be done with it! - * RCU_IDLE_LAZY_GP_DELAY gives the number of jiffies that a CPU is - * permitted to sleep in dyntick-idle mode with only lazy RCU - * callbacks pending. Setting this too high can OOM your system. - * - * The values below work well in practice. If future workloads require - * adjustment, they can be converted into kernel config parameters, though - * making the state machine smarter might be a better option. - */ -#define RCU_IDLE_GP_DELAY 4 /* Roughly one grace period. */ -#define RCU_IDLE_LAZY_GP_DELAY (6 * HZ) /* Roughly six seconds. */ - -static int rcu_idle_gp_delay = RCU_IDLE_GP_DELAY; -module_param(rcu_idle_gp_delay, int, 0644); -static int rcu_idle_lazy_gp_delay = RCU_IDLE_LAZY_GP_DELAY; -module_param(rcu_idle_lazy_gp_delay, int, 0644); - -extern int tick_nohz_enabled; - -/* - * Try to advance callbacks for all flavors of RCU on the current CPU, but - * only if it has been awhile since the last time we did so. Afterwards, - * if there are any callbacks ready for immediate invocation, return true. - */ -static bool rcu_try_advance_all_cbs(void) -{ - bool cbs_ready = false; - struct rcu_data *rdp; - struct rcu_dynticks *rdtp = this_cpu_ptr(&rcu_dynticks); - struct rcu_node *rnp; - struct rcu_state *rsp; - - /* Exit early if we advanced recently. */ - if (jiffies == rdtp->last_advance_all) - return 0; - rdtp->last_advance_all = jiffies; - - for_each_rcu_flavor(rsp) { - rdp = this_cpu_ptr(rsp->rda); - rnp = rdp->mynode; - - /* - * Don't bother checking unless a grace period has - * completed since we last checked and there are - * callbacks not yet ready to invoke. - */ - if (rdp->completed != rnp->completed && - rdp->nxttail[RCU_DONE_TAIL] != rdp->nxttail[RCU_NEXT_TAIL]) - note_gp_changes(rsp, rdp); - - if (cpu_has_callbacks_ready_to_invoke(rdp)) - cbs_ready = true; - } - return cbs_ready; -} - -/* - * Allow the CPU to enter dyntick-idle mode unless it has callbacks ready - * to invoke. If the CPU has callbacks, try to advance them. Tell the - * caller to set the timeout based on whether or not there are non-lazy - * callbacks. - * - * The caller must have disabled interrupts. - */ -int rcu_needs_cpu(int cpu, unsigned long *dj) -{ - struct rcu_dynticks *rdtp = &per_cpu(rcu_dynticks, cpu); - - /* Snapshot to detect later posting of non-lazy callback. */ - rdtp->nonlazy_posted_snap = rdtp->nonlazy_posted; - - /* If no callbacks, RCU doesn't need the CPU. */ - if (!rcu_cpu_has_callbacks(cpu, &rdtp->all_lazy)) { - *dj = ULONG_MAX; - return 0; - } - - /* Attempt to advance callbacks. */ - if (rcu_try_advance_all_cbs()) { - /* Some ready to invoke, so initiate later invocation. */ - invoke_rcu_core(); - return 1; - } - rdtp->last_accelerate = jiffies; - - /* Request timer delay depending on laziness, and round. */ - if (!rdtp->all_lazy) { - *dj = round_up(rcu_idle_gp_delay + jiffies, - rcu_idle_gp_delay) - jiffies; - } else { - *dj = round_jiffies(rcu_idle_lazy_gp_delay + jiffies) - jiffies; - } - return 0; -} - -/* - * Prepare a CPU for idle from an RCU perspective. The first major task - * is to sense whether nohz mode has been enabled or disabled via sysfs. - * The second major task is to check to see if a non-lazy callback has - * arrived at a CPU that previously had only lazy callbacks. The third - * major task is to accelerate (that is, assign grace-period numbers to) - * any recently arrived callbacks. - * - * The caller must have disabled interrupts. - */ -static void rcu_prepare_for_idle(int cpu) -{ - struct rcu_data *rdp; - struct rcu_dynticks *rdtp = &per_cpu(rcu_dynticks, cpu); - struct rcu_node *rnp; - struct rcu_state *rsp; - int tne; - - /* Handle nohz enablement switches conservatively. */ - tne = ACCESS_ONCE(tick_nohz_enabled); - if (tne != rdtp->tick_nohz_enabled_snap) { - if (rcu_cpu_has_callbacks(cpu, NULL)) - invoke_rcu_core(); /* force nohz to see update. */ - rdtp->tick_nohz_enabled_snap = tne; - return; - } - if (!tne) - return; - - /* If this is a no-CBs CPU, no callbacks, just return. */ - if (rcu_is_nocb_cpu(cpu)) - return; - - /* - * If a non-lazy callback arrived at a CPU having only lazy - * callbacks, invoke RCU core for the side-effect of recalculating - * idle duration on re-entry to idle. - */ - if (rdtp->all_lazy && - rdtp->nonlazy_posted != rdtp->nonlazy_posted_snap) { - rdtp->all_lazy = false; - rdtp->nonlazy_posted_snap = rdtp->nonlazy_posted; - invoke_rcu_core(); - return; - } - - /* - * If we have not yet accelerated this jiffy, accelerate all - * callbacks on this CPU. - */ - if (rdtp->last_accelerate == jiffies) - return; - rdtp->last_accelerate = jiffies; - for_each_rcu_flavor(rsp) { - rdp = per_cpu_ptr(rsp->rda, cpu); - if (!*rdp->nxttail[RCU_DONE_TAIL]) - continue; - rnp = rdp->mynode; - raw_spin_lock(&rnp->lock); /* irqs already disabled. */ - rcu_accelerate_cbs(rsp, rnp, rdp); - raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */ - } -} - -/* - * Clean up for exit from idle. Attempt to advance callbacks based on - * any grace periods that elapsed while the CPU was idle, and if any - * callbacks are now ready to invoke, initiate invocation. - */ -static void rcu_cleanup_after_idle(int cpu) -{ - - if (rcu_is_nocb_cpu(cpu)) - return; - if (rcu_try_advance_all_cbs()) - invoke_rcu_core(); -} - -/* - * Keep a running count of the number of non-lazy callbacks posted - * on this CPU. This running counter (which is never decremented) allows - * rcu_prepare_for_idle() to detect when something out of the idle loop - * posts a callback, even if an equal number of callbacks are invoked. - * Of course, callbacks should only be posted from within a trace event - * designed to be called from idle or from within RCU_NONIDLE(). - */ -static void rcu_idle_count_callbacks_posted(void) -{ - __this_cpu_add(rcu_dynticks.nonlazy_posted, 1); -} - -/* - * Data for flushing lazy RCU callbacks at OOM time. - */ -static atomic_t oom_callback_count; -static DECLARE_WAIT_QUEUE_HEAD(oom_callback_wq); - -/* - * RCU OOM callback -- decrement the outstanding count and deliver the - * wake-up if we are the last one. - */ -static void rcu_oom_callback(struct rcu_head *rhp) -{ - if (atomic_dec_and_test(&oom_callback_count)) - wake_up(&oom_callback_wq); -} - -/* - * Post an rcu_oom_notify callback on the current CPU if it has at - * least one lazy callback. This will unnecessarily post callbacks - * to CPUs that already have a non-lazy callback at the end of their - * callback list, but this is an infrequent operation, so accept some - * extra overhead to keep things simple. - */ -static void rcu_oom_notify_cpu(void *unused) -{ - struct rcu_state *rsp; - struct rcu_data *rdp; - - for_each_rcu_flavor(rsp) { - rdp = __this_cpu_ptr(rsp->rda); - if (rdp->qlen_lazy != 0) { - atomic_inc(&oom_callback_count); - rsp->call(&rdp->oom_head, rcu_oom_callback); - } - } -} - -/* - * If low on memory, ensure that each CPU has a non-lazy callback. - * This will wake up CPUs that have only lazy callbacks, in turn - * ensuring that they free up the corresponding memory in a timely manner. - * Because an uncertain amount of memory will be freed in some uncertain - * timeframe, we do not claim to have freed anything. - */ -static int rcu_oom_notify(struct notifier_block *self, - unsigned long notused, void *nfreed) -{ - int cpu; - - /* Wait for callbacks from earlier instance to complete. */ - wait_event(oom_callback_wq, atomic_read(&oom_callback_count) == 0); - - /* - * Prevent premature wakeup: ensure that all increments happen - * before there is a chance of the counter reaching zero. - */ - atomic_set(&oom_callback_count, 1); - - get_online_cpus(); - for_each_online_cpu(cpu) { - smp_call_function_single(cpu, rcu_oom_notify_cpu, NULL, 1); - cond_resched(); - } - put_online_cpus(); - - /* Unconditionally decrement: no need to wake ourselves up. */ - atomic_dec(&oom_callback_count); - - return NOTIFY_OK; -} - -static struct notifier_block rcu_oom_nb = { - .notifier_call = rcu_oom_notify -}; - -static int __init rcu_register_oom_notifier(void) -{ - register_oom_notifier(&rcu_oom_nb); - return 0; -} -early_initcall(rcu_register_oom_notifier); - -#endif /* #else #if !defined(CONFIG_RCU_FAST_NO_HZ) */ - -#ifdef CONFIG_RCU_CPU_STALL_INFO - -#ifdef CONFIG_RCU_FAST_NO_HZ - -static void print_cpu_stall_fast_no_hz(char *cp, int cpu) -{ - struct rcu_dynticks *rdtp = &per_cpu(rcu_dynticks, cpu); - unsigned long nlpd = rdtp->nonlazy_posted - rdtp->nonlazy_posted_snap; - - sprintf(cp, "last_accelerate: %04lx/%04lx, nonlazy_posted: %ld, %c%c", - rdtp->last_accelerate & 0xffff, jiffies & 0xffff, - ulong2long(nlpd), - rdtp->all_lazy ? 'L' : '.', - rdtp->tick_nohz_enabled_snap ? '.' : 'D'); -} - -#else /* #ifdef CONFIG_RCU_FAST_NO_HZ */ - -static void print_cpu_stall_fast_no_hz(char *cp, int cpu) -{ - *cp = '\0'; -} - -#endif /* #else #ifdef CONFIG_RCU_FAST_NO_HZ */ - -/* Initiate the stall-info list. */ -static void print_cpu_stall_info_begin(void) -{ - pr_cont("\n"); -} - -/* - * Print out diagnostic information for the specified stalled CPU. - * - * If the specified CPU is aware of the current RCU grace period - * (flavor specified by rsp), then print the number of scheduling - * clock interrupts the CPU has taken during the time that it has - * been aware. Otherwise, print the number of RCU grace periods - * that this CPU is ignorant of, for example, "1" if the CPU was - * aware of the previous grace period. - * - * Also print out idle and (if CONFIG_RCU_FAST_NO_HZ) idle-entry info. - */ -static void print_cpu_stall_info(struct rcu_state *rsp, int cpu) -{ - char fast_no_hz[72]; - struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu); - struct rcu_dynticks *rdtp = rdp->dynticks; - char *ticks_title; - unsigned long ticks_value; - - if (rsp->gpnum == rdp->gpnum) { - ticks_title = "ticks this GP"; - ticks_value = rdp->ticks_this_gp; - } else { - ticks_title = "GPs behind"; - ticks_value = rsp->gpnum - rdp->gpnum; - } - print_cpu_stall_fast_no_hz(fast_no_hz, cpu); - pr_err("\t%d: (%lu %s) idle=%03x/%llx/%d softirq=%u/%u %s\n", - cpu, ticks_value, ticks_title, - atomic_read(&rdtp->dynticks) & 0xfff, - rdtp->dynticks_nesting, rdtp->dynticks_nmi_nesting, - rdp->softirq_snap, kstat_softirqs_cpu(RCU_SOFTIRQ, cpu), - fast_no_hz); -} - -/* Terminate the stall-info list. */ -static void print_cpu_stall_info_end(void) -{ - pr_err("\t"); -} - -/* Zero ->ticks_this_gp for all flavors of RCU. */ -static void zero_cpu_stall_ticks(struct rcu_data *rdp) -{ - rdp->ticks_this_gp = 0; - rdp->softirq_snap = kstat_softirqs_cpu(RCU_SOFTIRQ, smp_processor_id()); -} - -/* Increment ->ticks_this_gp for all flavors of RCU. */ -static void increment_cpu_stall_ticks(void) -{ - struct rcu_state *rsp; - - for_each_rcu_flavor(rsp) - __this_cpu_ptr(rsp->rda)->ticks_this_gp++; -} - -#else /* #ifdef CONFIG_RCU_CPU_STALL_INFO */ - -static void print_cpu_stall_info_begin(void) -{ - pr_cont(" {"); -} - -static void print_cpu_stall_info(struct rcu_state *rsp, int cpu) -{ - pr_cont(" %d", cpu); -} - -static void print_cpu_stall_info_end(void) -{ - pr_cont("} "); -} - -static void zero_cpu_stall_ticks(struct rcu_data *rdp) -{ -} - -static void increment_cpu_stall_ticks(void) -{ -} - -#endif /* #else #ifdef CONFIG_RCU_CPU_STALL_INFO */ - -#ifdef CONFIG_RCU_NOCB_CPU - -/* - * Offload callback processing from the boot-time-specified set of CPUs - * specified by rcu_nocb_mask. For each CPU in the set, there is a - * kthread created that pulls the callbacks from the corresponding CPU, - * waits for a grace period to elapse, and invokes the callbacks. - * The no-CBs CPUs do a wake_up() on their kthread when they insert - * a callback into any empty list, unless the rcu_nocb_poll boot parameter - * has been specified, in which case each kthread actively polls its - * CPU. (Which isn't so great for energy efficiency, but which does - * reduce RCU's overhead on that CPU.) - * - * This is intended to be used in conjunction with Frederic Weisbecker's - * adaptive-idle work, which would seriously reduce OS jitter on CPUs - * running CPU-bound user-mode computations. - * - * Offloading of callback processing could also in theory be used as - * an energy-efficiency measure because CPUs with no RCU callbacks - * queued are more aggressive about entering dyntick-idle mode. - */ - - -/* Parse the boot-time rcu_nocb_mask CPU list from the kernel parameters. */ -static int __init rcu_nocb_setup(char *str) -{ - alloc_bootmem_cpumask_var(&rcu_nocb_mask); - have_rcu_nocb_mask = true; - cpulist_parse(str, rcu_nocb_mask); - return 1; -} -__setup("rcu_nocbs=", rcu_nocb_setup); - -static int __init parse_rcu_nocb_poll(char *arg) -{ - rcu_nocb_poll = 1; - return 0; -} -early_param("rcu_nocb_poll", parse_rcu_nocb_poll); - -/* - * Do any no-CBs CPUs need another grace period? - * - * Interrupts must be disabled. If the caller does not hold the root - * rnp_node structure's ->lock, the results are advisory only. - */ -static int rcu_nocb_needs_gp(struct rcu_state *rsp) -{ - struct rcu_node *rnp = rcu_get_root(rsp); - - return rnp->need_future_gp[(ACCESS_ONCE(rnp->completed) + 1) & 0x1]; -} - -/* - * Wake up any no-CBs CPUs' kthreads that were waiting on the just-ended - * grace period. - */ -static void rcu_nocb_gp_cleanup(struct rcu_state *rsp, struct rcu_node *rnp) -{ - wake_up_all(&rnp->nocb_gp_wq[rnp->completed & 0x1]); -} - -/* - * Set the root rcu_node structure's ->need_future_gp field - * based on the sum of those of all rcu_node structures. This does - * double-count the root rcu_node structure's requests, but this - * is necessary to handle the possibility of a rcu_nocb_kthread() - * having awakened during the time that the rcu_node structures - * were being updated for the end of the previous grace period. - */ -static void rcu_nocb_gp_set(struct rcu_node *rnp, int nrq) -{ - rnp->need_future_gp[(rnp->completed + 1) & 0x1] += nrq; -} - -static void rcu_init_one_nocb(struct rcu_node *rnp) -{ - init_waitqueue_head(&rnp->nocb_gp_wq[0]); - init_waitqueue_head(&rnp->nocb_gp_wq[1]); -} - -/* Is the specified CPU a no-CPUs CPU? */ -bool rcu_is_nocb_cpu(int cpu) -{ - if (have_rcu_nocb_mask) - return cpumask_test_cpu(cpu, rcu_nocb_mask); - return false; -} - -/* - * Enqueue the specified string of rcu_head structures onto the specified - * CPU's no-CBs lists. The CPU is specified by rdp, the head of the - * string by rhp, and the tail of the string by rhtp. The non-lazy/lazy - * counts are supplied by rhcount and rhcount_lazy. - * - * If warranted, also wake up the kthread servicing this CPUs queues. - */ -static void __call_rcu_nocb_enqueue(struct rcu_data *rdp, - struct rcu_head *rhp, - struct rcu_head **rhtp, - int rhcount, int rhcount_lazy) -{ - int len; - struct rcu_head **old_rhpp; - struct task_struct *t; - - /* Enqueue the callback on the nocb list and update counts. */ - old_rhpp = xchg(&rdp->nocb_tail, rhtp); - ACCESS_ONCE(*old_rhpp) = rhp; - atomic_long_add(rhcount, &rdp->nocb_q_count); - atomic_long_add(rhcount_lazy, &rdp->nocb_q_count_lazy); - - /* If we are not being polled and there is a kthread, awaken it ... */ - t = ACCESS_ONCE(rdp->nocb_kthread); - if (rcu_nocb_poll || !t) { - trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, - TPS("WakeNotPoll")); - return; - } - len = atomic_long_read(&rdp->nocb_q_count); - if (old_rhpp == &rdp->nocb_head) { - wake_up(&rdp->nocb_wq); /* ... only if queue was empty ... */ - rdp->qlen_last_fqs_check = 0; - trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, TPS("WakeEmpty")); - } else if (len > rdp->qlen_last_fqs_check + qhimark) { - wake_up_process(t); /* ... or if many callbacks queued. */ - rdp->qlen_last_fqs_check = LONG_MAX / 2; - trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, TPS("WakeOvf")); - } else { - trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, TPS("WakeNot")); - } - return; -} - -/* - * This is a helper for __call_rcu(), which invokes this when the normal - * callback queue is inoperable. If this is not a no-CBs CPU, this - * function returns failure back to __call_rcu(), which can complain - * appropriately. - * - * Otherwise, this function queues the callback where the corresponding - * "rcuo" kthread can find it. - */ -static bool __call_rcu_nocb(struct rcu_data *rdp, struct rcu_head *rhp, - bool lazy) -{ - - if (!rcu_is_nocb_cpu(rdp->cpu)) - return 0; - __call_rcu_nocb_enqueue(rdp, rhp, &rhp->next, 1, lazy); - if (__is_kfree_rcu_offset((unsigned long)rhp->func)) - trace_rcu_kfree_callback(rdp->rsp->name, rhp, - (unsigned long)rhp->func, - -atomic_long_read(&rdp->nocb_q_count_lazy), - -atomic_long_read(&rdp->nocb_q_count)); - else - trace_rcu_callback(rdp->rsp->name, rhp, - -atomic_long_read(&rdp->nocb_q_count_lazy), - -atomic_long_read(&rdp->nocb_q_count)); - return 1; -} - -/* - * Adopt orphaned callbacks on a no-CBs CPU, or return 0 if this is - * not a no-CBs CPU. - */ -static bool __maybe_unused rcu_nocb_adopt_orphan_cbs(struct rcu_state *rsp, - struct rcu_data *rdp) -{ - long ql = rsp->qlen; - long qll = rsp->qlen_lazy; - - /* If this is not a no-CBs CPU, tell the caller to do it the old way. */ - if (!rcu_is_nocb_cpu(smp_processor_id())) - return 0; - rsp->qlen = 0; - rsp->qlen_lazy = 0; - - /* First, enqueue the donelist, if any. This preserves CB ordering. */ - if (rsp->orphan_donelist != NULL) { - __call_rcu_nocb_enqueue(rdp, rsp->orphan_donelist, - rsp->orphan_donetail, ql, qll); - ql = qll = 0; - rsp->orphan_donelist = NULL; - rsp->orphan_donetail = &rsp->orphan_donelist; - } - if (rsp->orphan_nxtlist != NULL) { - __call_rcu_nocb_enqueue(rdp, rsp->orphan_nxtlist, - rsp->orphan_nxttail, ql, qll); - ql = qll = 0; - rsp->orphan_nxtlist = NULL; - rsp->orphan_nxttail = &rsp->orphan_nxtlist; - } - return 1; -} - -/* - * If necessary, kick off a new grace period, and either way wait - * for a subsequent grace period to complete. - */ -static void rcu_nocb_wait_gp(struct rcu_data *rdp) -{ - unsigned long c; - bool d; - unsigned long flags; - struct rcu_node *rnp = rdp->mynode; - - raw_spin_lock_irqsave(&rnp->lock, flags); - c = rcu_start_future_gp(rnp, rdp); - raw_spin_unlock_irqrestore(&rnp->lock, flags); - - /* - * Wait for the grace period. Do so interruptibly to avoid messing - * up the load average. - */ - trace_rcu_future_gp(rnp, rdp, c, TPS("StartWait")); - for (;;) { - wait_event_interruptible( - rnp->nocb_gp_wq[c & 0x1], - (d = ULONG_CMP_GE(ACCESS_ONCE(rnp->completed), c))); - if (likely(d)) - break; - flush_signals(current); - trace_rcu_future_gp(rnp, rdp, c, TPS("ResumeWait")); - } - trace_rcu_future_gp(rnp, rdp, c, TPS("EndWait")); - smp_mb(); /* Ensure that CB invocation happens after GP end. */ -} - -/* - * Per-rcu_data kthread, but only for no-CBs CPUs. Each kthread invokes - * callbacks queued by the corresponding no-CBs CPU. - */ -static int rcu_nocb_kthread(void *arg) -{ - int c, cl; - bool firsttime = 1; - struct rcu_head *list; - struct rcu_head *next; - struct rcu_head **tail; - struct rcu_data *rdp = arg; - - /* Each pass through this loop invokes one batch of callbacks */ - for (;;) { - /* If not polling, wait for next batch of callbacks. */ - if (!rcu_nocb_poll) { - trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, - TPS("Sleep")); - wait_event_interruptible(rdp->nocb_wq, rdp->nocb_head); - } else if (firsttime) { - firsttime = 0; - trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, - TPS("Poll")); - } - list = ACCESS_ONCE(rdp->nocb_head); - if (!list) { - if (!rcu_nocb_poll) - trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, - TPS("WokeEmpty")); - schedule_timeout_interruptible(1); - flush_signals(current); - continue; - } - firsttime = 1; - trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, - TPS("WokeNonEmpty")); - - /* - * Extract queued callbacks, update counts, and wait - * for a grace period to elapse. - */ - ACCESS_ONCE(rdp->nocb_head) = NULL; - tail = xchg(&rdp->nocb_tail, &rdp->nocb_head); - c = atomic_long_xchg(&rdp->nocb_q_count, 0); - cl = atomic_long_xchg(&rdp->nocb_q_count_lazy, 0); - ACCESS_ONCE(rdp->nocb_p_count) += c; - ACCESS_ONCE(rdp->nocb_p_count_lazy) += cl; - rcu_nocb_wait_gp(rdp); - - /* Each pass through the following loop invokes a callback. */ - trace_rcu_batch_start(rdp->rsp->name, cl, c, -1); - c = cl = 0; - while (list) { - next = list->next; - /* Wait for enqueuing to complete, if needed. */ - while (next == NULL && &list->next != tail) { - trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, - TPS("WaitQueue")); - schedule_timeout_interruptible(1); - trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, - TPS("WokeQueue")); - next = list->next; - } - debug_rcu_head_unqueue(list); - local_bh_disable(); - if (__rcu_reclaim(rdp->rsp->name, list)) - cl++; - c++; - local_bh_enable(); - list = next; - } - trace_rcu_batch_end(rdp->rsp->name, c, !!list, 0, 0, 1); - ACCESS_ONCE(rdp->nocb_p_count) -= c; - ACCESS_ONCE(rdp->nocb_p_count_lazy) -= cl; - rdp->n_nocbs_invoked += c; - } - return 0; -} - -/* Initialize per-rcu_data variables for no-CBs CPUs. */ -static void __init rcu_boot_init_nocb_percpu_data(struct rcu_data *rdp) -{ - rdp->nocb_tail = &rdp->nocb_head; - init_waitqueue_head(&rdp->nocb_wq); -} - -/* Create a kthread for each RCU flavor for each no-CBs CPU. */ -static void __init rcu_spawn_nocb_kthreads(struct rcu_state *rsp) -{ - int cpu; - struct rcu_data *rdp; - struct task_struct *t; - - if (rcu_nocb_mask == NULL) - return; - for_each_cpu(cpu, rcu_nocb_mask) { - rdp = per_cpu_ptr(rsp->rda, cpu); - t = kthread_run(rcu_nocb_kthread, rdp, - "rcuo%c/%d", rsp->abbr, cpu); - BUG_ON(IS_ERR(t)); - ACCESS_ONCE(rdp->nocb_kthread) = t; - } -} - -/* Prevent __call_rcu() from enqueuing callbacks on no-CBs CPUs */ -static bool init_nocb_callback_list(struct rcu_data *rdp) -{ - if (rcu_nocb_mask == NULL || - !cpumask_test_cpu(rdp->cpu, rcu_nocb_mask)) - return false; - rdp->nxttail[RCU_NEXT_TAIL] = NULL; - return true; -} - -#else /* #ifdef CONFIG_RCU_NOCB_CPU */ - -static int rcu_nocb_needs_gp(struct rcu_state *rsp) -{ - return 0; -} - -static void rcu_nocb_gp_cleanup(struct rcu_state *rsp, struct rcu_node *rnp) -{ -} - -static void rcu_nocb_gp_set(struct rcu_node *rnp, int nrq) -{ -} - -static void rcu_init_one_nocb(struct rcu_node *rnp) -{ -} - -static bool __call_rcu_nocb(struct rcu_data *rdp, struct rcu_head *rhp, - bool lazy) -{ - return 0; -} - -static bool __maybe_unused rcu_nocb_adopt_orphan_cbs(struct rcu_state *rsp, - struct rcu_data *rdp) -{ - return 0; -} - -static void __init rcu_boot_init_nocb_percpu_data(struct rcu_data *rdp) -{ -} - -static void __init rcu_spawn_nocb_kthreads(struct rcu_state *rsp) -{ -} - -static bool init_nocb_callback_list(struct rcu_data *rdp) -{ - return false; -} - -#endif /* #else #ifdef CONFIG_RCU_NOCB_CPU */ - -/* - * An adaptive-ticks CPU can potentially execute in kernel mode for an - * arbitrarily long period of time with the scheduling-clock tick turned - * off. RCU will be paying attention to this CPU because it is in the - * kernel, but the CPU cannot be guaranteed to be executing the RCU state - * machine because the scheduling-clock tick has been disabled. Therefore, - * if an adaptive-ticks CPU is failing to respond to the current grace - * period and has not be idle from an RCU perspective, kick it. - */ -static void rcu_kick_nohz_cpu(int cpu) -{ -#ifdef CONFIG_NO_HZ_FULL - if (tick_nohz_full_cpu(cpu)) - smp_send_reschedule(cpu); -#endif /* #ifdef CONFIG_NO_HZ_FULL */ -} - - -#ifdef CONFIG_NO_HZ_FULL_SYSIDLE - -/* - * Define RCU flavor that holds sysidle state. This needs to be the - * most active flavor of RCU. - */ -#ifdef CONFIG_PREEMPT_RCU -static struct rcu_state *rcu_sysidle_state = &rcu_preempt_state; -#else /* #ifdef CONFIG_PREEMPT_RCU */ -static struct rcu_state *rcu_sysidle_state = &rcu_sched_state; -#endif /* #else #ifdef CONFIG_PREEMPT_RCU */ - -static int full_sysidle_state; /* Current system-idle state. */ -#define RCU_SYSIDLE_NOT 0 /* Some CPU is not idle. */ -#define RCU_SYSIDLE_SHORT 1 /* All CPUs idle for brief period. */ -#define RCU_SYSIDLE_LONG 2 /* All CPUs idle for long enough. */ -#define RCU_SYSIDLE_FULL 3 /* All CPUs idle, ready for sysidle. */ -#define RCU_SYSIDLE_FULL_NOTED 4 /* Actually entered sysidle state. */ - -/* - * Invoked to note exit from irq or task transition to idle. Note that - * usermode execution does -not- count as idle here! After all, we want - * to detect full-system idle states, not RCU quiescent states and grace - * periods. The caller must have disabled interrupts. - */ -static void rcu_sysidle_enter(struct rcu_dynticks *rdtp, int irq) -{ - unsigned long j; - - /* Adjust nesting, check for fully idle. */ - if (irq) { - rdtp->dynticks_idle_nesting--; - WARN_ON_ONCE(rdtp->dynticks_idle_nesting < 0); - if (rdtp->dynticks_idle_nesting != 0) - return; /* Still not fully idle. */ - } else { - if ((rdtp->dynticks_idle_nesting & DYNTICK_TASK_NEST_MASK) == - DYNTICK_TASK_NEST_VALUE) { - rdtp->dynticks_idle_nesting = 0; - } else { - rdtp->dynticks_idle_nesting -= DYNTICK_TASK_NEST_VALUE; - WARN_ON_ONCE(rdtp->dynticks_idle_nesting < 0); - return; /* Still not fully idle. */ - } - } - - /* Record start of fully idle period. */ - j = jiffies; - ACCESS_ONCE(rdtp->dynticks_idle_jiffies) = j; - smp_mb__before_atomic_inc(); - atomic_inc(&rdtp->dynticks_idle); - smp_mb__after_atomic_inc(); - WARN_ON_ONCE(atomic_read(&rdtp->dynticks_idle) & 0x1); -} - -/* - * Unconditionally force exit from full system-idle state. This is - * invoked when a normal CPU exits idle, but must be called separately - * for the timekeeping CPU (tick_do_timer_cpu). The reason for this - * is that the timekeeping CPU is permitted to take scheduling-clock - * interrupts while the system is in system-idle state, and of course - * rcu_sysidle_exit() has no way of distinguishing a scheduling-clock - * interrupt from any other type of interrupt. - */ -void rcu_sysidle_force_exit(void) -{ - int oldstate = ACCESS_ONCE(full_sysidle_state); - int newoldstate; - - /* - * Each pass through the following loop attempts to exit full - * system-idle state. If contention proves to be a problem, - * a trylock-based contention tree could be used here. - */ - while (oldstate > RCU_SYSIDLE_SHORT) { - newoldstate = cmpxchg(&full_sysidle_state, - oldstate, RCU_SYSIDLE_NOT); - if (oldstate == newoldstate && - oldstate == RCU_SYSIDLE_FULL_NOTED) { - rcu_kick_nohz_cpu(tick_do_timer_cpu); - return; /* We cleared it, done! */ - } - oldstate = newoldstate; - } - smp_mb(); /* Order initial oldstate fetch vs. later non-idle work. */ -} - -/* - * Invoked to note entry to irq or task transition from idle. Note that - * usermode execution does -not- count as idle here! The caller must - * have disabled interrupts. - */ -static void rcu_sysidle_exit(struct rcu_dynticks *rdtp, int irq) -{ - /* Adjust nesting, check for already non-idle. */ - if (irq) { - rdtp->dynticks_idle_nesting++; - WARN_ON_ONCE(rdtp->dynticks_idle_nesting <= 0); - if (rdtp->dynticks_idle_nesting != 1) - return; /* Already non-idle. */ - } else { - /* - * Allow for irq misnesting. Yes, it really is possible - * to enter an irq handler then never leave it, and maybe - * also vice versa. Handle both possibilities. - */ - if (rdtp->dynticks_idle_nesting & DYNTICK_TASK_NEST_MASK) { - rdtp->dynticks_idle_nesting += DYNTICK_TASK_NEST_VALUE; - WARN_ON_ONCE(rdtp->dynticks_idle_nesting <= 0); - return; /* Already non-idle. */ - } else { - rdtp->dynticks_idle_nesting = DYNTICK_TASK_EXIT_IDLE; - } - } - - /* Record end of idle period. */ - smp_mb__before_atomic_inc(); - atomic_inc(&rdtp->dynticks_idle); - smp_mb__after_atomic_inc(); - WARN_ON_ONCE(!(atomic_read(&rdtp->dynticks_idle) & 0x1)); - - /* - * If we are the timekeeping CPU, we are permitted to be non-idle - * during a system-idle state. This must be the case, because - * the timekeeping CPU has to take scheduling-clock interrupts - * during the time that the system is transitioning to full - * system-idle state. This means that the timekeeping CPU must - * invoke rcu_sysidle_force_exit() directly if it does anything - * more than take a scheduling-clock interrupt. - */ - if (smp_processor_id() == tick_do_timer_cpu) - return; - - /* Update system-idle state: We are clearly no longer fully idle! */ - rcu_sysidle_force_exit(); -} - -/* - * Check to see if the current CPU is idle. Note that usermode execution - * does not count as idle. The caller must have disabled interrupts. - */ -static void rcu_sysidle_check_cpu(struct rcu_data *rdp, bool *isidle, - unsigned long *maxj) -{ - int cur; - unsigned long j; - struct rcu_dynticks *rdtp = rdp->dynticks; - - /* - * If some other CPU has already reported non-idle, if this is - * not the flavor of RCU that tracks sysidle state, or if this - * is an offline or the timekeeping CPU, nothing to do. - */ - if (!*isidle || rdp->rsp != rcu_sysidle_state || - cpu_is_offline(rdp->cpu) || rdp->cpu == tick_do_timer_cpu) - return; - if (rcu_gp_in_progress(rdp->rsp)) - WARN_ON_ONCE(smp_processor_id() != tick_do_timer_cpu); - - /* Pick up current idle and NMI-nesting counter and check. */ - cur = atomic_read(&rdtp->dynticks_idle); - if (cur & 0x1) { - *isidle = false; /* We are not idle! */ - return; - } - smp_mb(); /* Read counters before timestamps. */ - - /* Pick up timestamps. */ - j = ACCESS_ONCE(rdtp->dynticks_idle_jiffies); - /* If this CPU entered idle more recently, update maxj timestamp. */ - if (ULONG_CMP_LT(*maxj, j)) - *maxj = j; -} - -/* - * Is this the flavor of RCU that is handling full-system idle? - */ -static bool is_sysidle_rcu_state(struct rcu_state *rsp) -{ - return rsp == rcu_sysidle_state; -} - -/* - * Bind the grace-period kthread for the sysidle flavor of RCU to the - * timekeeping CPU. - */ -static void rcu_bind_gp_kthread(void) -{ - int cpu = ACCESS_ONCE(tick_do_timer_cpu); - - if (cpu < 0 || cpu >= nr_cpu_ids) - return; - if (raw_smp_processor_id() != cpu) - set_cpus_allowed_ptr(current, cpumask_of(cpu)); -} - -/* - * Return a delay in jiffies based on the number of CPUs, rcu_node - * leaf fanout, and jiffies tick rate. The idea is to allow larger - * systems more time to transition to full-idle state in order to - * avoid the cache thrashing that otherwise occur on the state variable. - * Really small systems (less than a couple of tens of CPUs) should - * instead use a single global atomically incremented counter, and later - * versions of this will automatically reconfigure themselves accordingly. - */ -static unsigned long rcu_sysidle_delay(void) -{ - if (nr_cpu_ids <= CONFIG_NO_HZ_FULL_SYSIDLE_SMALL) - return 0; - return DIV_ROUND_UP(nr_cpu_ids * HZ, rcu_fanout_leaf * 1000); -} - -/* - * Advance the full-system-idle state. This is invoked when all of - * the non-timekeeping CPUs are idle. - */ -static void rcu_sysidle(unsigned long j) -{ - /* Check the current state. */ - switch (ACCESS_ONCE(full_sysidle_state)) { - case RCU_SYSIDLE_NOT: - - /* First time all are idle, so note a short idle period. */ - ACCESS_ONCE(full_sysidle_state) = RCU_SYSIDLE_SHORT; - break; - - case RCU_SYSIDLE_SHORT: - - /* - * Idle for a bit, time to advance to next state? - * cmpxchg failure means race with non-idle, let them win. - */ - if (ULONG_CMP_GE(jiffies, j + rcu_sysidle_delay())) - (void)cmpxchg(&full_sysidle_state, - RCU_SYSIDLE_SHORT, RCU_SYSIDLE_LONG); - break; - - case RCU_SYSIDLE_LONG: - - /* - * Do an additional check pass before advancing to full. - * cmpxchg failure means race with non-idle, let them win. - */ - if (ULONG_CMP_GE(jiffies, j + rcu_sysidle_delay())) - (void)cmpxchg(&full_sysidle_state, - RCU_SYSIDLE_LONG, RCU_SYSIDLE_FULL); - break; - - default: - break; - } -} - -/* - * Found a non-idle non-timekeeping CPU, so kick the system-idle state - * back to the beginning. - */ -static void rcu_sysidle_cancel(void) -{ - smp_mb(); - ACCESS_ONCE(full_sysidle_state) = RCU_SYSIDLE_NOT; -} - -/* - * Update the sysidle state based on the results of a force-quiescent-state - * scan of the CPUs' dyntick-idle state. - */ -static void rcu_sysidle_report(struct rcu_state *rsp, int isidle, - unsigned long maxj, bool gpkt) -{ - if (rsp != rcu_sysidle_state) - return; /* Wrong flavor, ignore. */ - if (gpkt && nr_cpu_ids <= CONFIG_NO_HZ_FULL_SYSIDLE_SMALL) - return; /* Running state machine from timekeeping CPU. */ - if (isidle) - rcu_sysidle(maxj); /* More idle! */ - else - rcu_sysidle_cancel(); /* Idle is over. */ -} - -/* - * Wrapper for rcu_sysidle_report() when called from the grace-period - * kthread's context. - */ -static void rcu_sysidle_report_gp(struct rcu_state *rsp, int isidle, - unsigned long maxj) -{ - rcu_sysidle_report(rsp, isidle, maxj, true); -} - -/* Callback and function for forcing an RCU grace period. */ -struct rcu_sysidle_head { - struct rcu_head rh; - int inuse; -}; - -static void rcu_sysidle_cb(struct rcu_head *rhp) -{ - struct rcu_sysidle_head *rshp; - - /* - * The following memory barrier is needed to replace the - * memory barriers that would normally be in the memory - * allocator. - */ - smp_mb(); /* grace period precedes setting inuse. */ - - rshp = container_of(rhp, struct rcu_sysidle_head, rh); - ACCESS_ONCE(rshp->inuse) = 0; -} - -/* - * Check to see if the system is fully idle, other than the timekeeping CPU. - * The caller must have disabled interrupts. - */ -bool rcu_sys_is_idle(void) -{ - static struct rcu_sysidle_head rsh; - int rss = ACCESS_ONCE(full_sysidle_state); - - if (WARN_ON_ONCE(smp_processor_id() != tick_do_timer_cpu)) - return false; - - /* Handle small-system case by doing a full scan of CPUs. */ - if (nr_cpu_ids <= CONFIG_NO_HZ_FULL_SYSIDLE_SMALL) { - int oldrss = rss - 1; - - /* - * One pass to advance to each state up to _FULL. - * Give up if any pass fails to advance the state. - */ - while (rss < RCU_SYSIDLE_FULL && oldrss < rss) { - int cpu; - bool isidle = true; - unsigned long maxj = jiffies - ULONG_MAX / 4; - struct rcu_data *rdp; - - /* Scan all the CPUs looking for nonidle CPUs. */ - for_each_possible_cpu(cpu) { - rdp = per_cpu_ptr(rcu_sysidle_state->rda, cpu); - rcu_sysidle_check_cpu(rdp, &isidle, &maxj); - if (!isidle) - break; - } - rcu_sysidle_report(rcu_sysidle_state, - isidle, maxj, false); - oldrss = rss; - rss = ACCESS_ONCE(full_sysidle_state); - } - } - - /* If this is the first observation of an idle period, record it. */ - if (rss == RCU_SYSIDLE_FULL) { - rss = cmpxchg(&full_sysidle_state, - RCU_SYSIDLE_FULL, RCU_SYSIDLE_FULL_NOTED); - return rss == RCU_SYSIDLE_FULL; - } - - smp_mb(); /* ensure rss load happens before later caller actions. */ - - /* If already fully idle, tell the caller (in case of races). */ - if (rss == RCU_SYSIDLE_FULL_NOTED) - return true; - - /* - * If we aren't there yet, and a grace period is not in flight, - * initiate a grace period. Either way, tell the caller that - * we are not there yet. We use an xchg() rather than an assignment - * to make up for the memory barriers that would otherwise be - * provided by the memory allocator. - */ - if (nr_cpu_ids > CONFIG_NO_HZ_FULL_SYSIDLE_SMALL && - !rcu_gp_in_progress(rcu_sysidle_state) && - !rsh.inuse && xchg(&rsh.inuse, 1) == 0) - call_rcu(&rsh.rh, rcu_sysidle_cb); - return false; -} - -/* - * Initialize dynticks sysidle state for CPUs coming online. - */ -static void rcu_sysidle_init_percpu_data(struct rcu_dynticks *rdtp) -{ - rdtp->dynticks_idle_nesting = DYNTICK_TASK_NEST_VALUE; -} - -#else /* #ifdef CONFIG_NO_HZ_FULL_SYSIDLE */ - -static void rcu_sysidle_enter(struct rcu_dynticks *rdtp, int irq) -{ -} - -static void rcu_sysidle_exit(struct rcu_dynticks *rdtp, int irq) -{ -} - -static void rcu_sysidle_check_cpu(struct rcu_data *rdp, bool *isidle, - unsigned long *maxj) -{ -} - -static bool is_sysidle_rcu_state(struct rcu_state *rsp) -{ - return false; -} - -static void rcu_bind_gp_kthread(void) -{ -} - -static void rcu_sysidle_report_gp(struct rcu_state *rsp, int isidle, - unsigned long maxj) -{ -} - -static void rcu_sysidle_init_percpu_data(struct rcu_dynticks *rdtp) -{ -} - -#endif /* #else #ifdef CONFIG_NO_HZ_FULL_SYSIDLE */ diff --git a/kernel/rcutree_trace.c b/kernel/rcutree_trace.c deleted file mode 100644 index cf6c174..0000000 --- a/kernel/rcutree_trace.c +++ /dev/null @@ -1,500 +0,0 @@ -/* - * Read-Copy Update tracing for classic implementation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Copyright IBM Corporation, 2008 - * - * Papers: http://www.rdrop.com/users/paulmck/RCU - * - * For detailed explanation of Read-Copy Update mechanism see - - * Documentation/RCU - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define RCU_TREE_NONCORE -#include "rcutree.h" - -static int r_open(struct inode *inode, struct file *file, - const struct seq_operations *op) -{ - int ret = seq_open(file, op); - if (!ret) { - struct seq_file *m = (struct seq_file *)file->private_data; - m->private = inode->i_private; - } - return ret; -} - -static void *r_start(struct seq_file *m, loff_t *pos) -{ - struct rcu_state *rsp = (struct rcu_state *)m->private; - *pos = cpumask_next(*pos - 1, cpu_possible_mask); - if ((*pos) < nr_cpu_ids) - return per_cpu_ptr(rsp->rda, *pos); - return NULL; -} - -static void *r_next(struct seq_file *m, void *v, loff_t *pos) -{ - (*pos)++; - return r_start(m, pos); -} - -static void r_stop(struct seq_file *m, void *v) -{ -} - -static int show_rcubarrier(struct seq_file *m, void *v) -{ - struct rcu_state *rsp = (struct rcu_state *)m->private; - seq_printf(m, "bcc: %d nbd: %lu\n", - atomic_read(&rsp->barrier_cpu_count), - rsp->n_barrier_done); - return 0; -} - -static int rcubarrier_open(struct inode *inode, struct file *file) -{ - return single_open(file, show_rcubarrier, inode->i_private); -} - -static const struct file_operations rcubarrier_fops = { - .owner = THIS_MODULE, - .open = rcubarrier_open, - .read = seq_read, - .llseek = no_llseek, - .release = single_release, -}; - -#ifdef CONFIG_RCU_BOOST - -static char convert_kthread_status(unsigned int kthread_status) -{ - if (kthread_status > RCU_KTHREAD_MAX) - return '?'; - return "SRWOY"[kthread_status]; -} - -#endif /* #ifdef CONFIG_RCU_BOOST */ - -static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp) -{ - long ql, qll; - - if (!rdp->beenonline) - return; - seq_printf(m, "%3d%cc=%ld g=%ld pq=%d qp=%d", - rdp->cpu, - cpu_is_offline(rdp->cpu) ? '!' : ' ', - ulong2long(rdp->completed), ulong2long(rdp->gpnum), - rdp->passed_quiesce, rdp->qs_pending); - seq_printf(m, " dt=%d/%llx/%d df=%lu", - atomic_read(&rdp->dynticks->dynticks), - rdp->dynticks->dynticks_nesting, - rdp->dynticks->dynticks_nmi_nesting, - rdp->dynticks_fqs); - seq_printf(m, " of=%lu", rdp->offline_fqs); - rcu_nocb_q_lengths(rdp, &ql, &qll); - qll += rdp->qlen_lazy; - ql += rdp->qlen; - seq_printf(m, " ql=%ld/%ld qs=%c%c%c%c", - qll, ql, - ".N"[rdp->nxttail[RCU_NEXT_READY_TAIL] != - rdp->nxttail[RCU_NEXT_TAIL]], - ".R"[rdp->nxttail[RCU_WAIT_TAIL] != - rdp->nxttail[RCU_NEXT_READY_TAIL]], - ".W"[rdp->nxttail[RCU_DONE_TAIL] != - rdp->nxttail[RCU_WAIT_TAIL]], - ".D"[&rdp->nxtlist != rdp->nxttail[RCU_DONE_TAIL]]); -#ifdef CONFIG_RCU_BOOST - seq_printf(m, " kt=%d/%c ktl=%x", - per_cpu(rcu_cpu_has_work, rdp->cpu), - convert_kthread_status(per_cpu(rcu_cpu_kthread_status, - rdp->cpu)), - per_cpu(rcu_cpu_kthread_loops, rdp->cpu) & 0xffff); -#endif /* #ifdef CONFIG_RCU_BOOST */ - seq_printf(m, " b=%ld", rdp->blimit); - seq_printf(m, " ci=%lu nci=%lu co=%lu ca=%lu\n", - rdp->n_cbs_invoked, rdp->n_nocbs_invoked, - rdp->n_cbs_orphaned, rdp->n_cbs_adopted); -} - -static int show_rcudata(struct seq_file *m, void *v) -{ - print_one_rcu_data(m, (struct rcu_data *)v); - return 0; -} - -static const struct seq_operations rcudate_op = { - .start = r_start, - .next = r_next, - .stop = r_stop, - .show = show_rcudata, -}; - -static int rcudata_open(struct inode *inode, struct file *file) -{ - return r_open(inode, file, &rcudate_op); -} - -static const struct file_operations rcudata_fops = { - .owner = THIS_MODULE, - .open = rcudata_open, - .read = seq_read, - .llseek = no_llseek, - .release = seq_release, -}; - -static int show_rcuexp(struct seq_file *m, void *v) -{ - struct rcu_state *rsp = (struct rcu_state *)m->private; - - seq_printf(m, "s=%lu d=%lu w=%lu tf=%lu wd1=%lu wd2=%lu n=%lu sc=%lu dt=%lu dl=%lu dx=%lu\n", - atomic_long_read(&rsp->expedited_start), - atomic_long_read(&rsp->expedited_done), - atomic_long_read(&rsp->expedited_wrap), - atomic_long_read(&rsp->expedited_tryfail), - atomic_long_read(&rsp->expedited_workdone1), - atomic_long_read(&rsp->expedited_workdone2), - atomic_long_read(&rsp->expedited_normal), - atomic_long_read(&rsp->expedited_stoppedcpus), - atomic_long_read(&rsp->expedited_done_tries), - atomic_long_read(&rsp->expedited_done_lost), - atomic_long_read(&rsp->expedited_done_exit)); - return 0; -} - -static int rcuexp_open(struct inode *inode, struct file *file) -{ - return single_open(file, show_rcuexp, inode->i_private); -} - -static const struct file_operations rcuexp_fops = { - .owner = THIS_MODULE, - .open = rcuexp_open, - .read = seq_read, - .llseek = no_llseek, - .release = single_release, -}; - -#ifdef CONFIG_RCU_BOOST - -static void print_one_rcu_node_boost(struct seq_file *m, struct rcu_node *rnp) -{ - seq_printf(m, "%d:%d tasks=%c%c%c%c kt=%c ntb=%lu neb=%lu nnb=%lu ", - rnp->grplo, rnp->grphi, - "T."[list_empty(&rnp->blkd_tasks)], - "N."[!rnp->gp_tasks], - "E."[!rnp->exp_tasks], - "B."[!rnp->boost_tasks], - convert_kthread_status(rnp->boost_kthread_status), - rnp->n_tasks_boosted, rnp->n_exp_boosts, - rnp->n_normal_boosts); - seq_printf(m, "j=%04x bt=%04x\n", - (int)(jiffies & 0xffff), - (int)(rnp->boost_time & 0xffff)); - seq_printf(m, " balk: nt=%lu egt=%lu bt=%lu nb=%lu ny=%lu nos=%lu\n", - rnp->n_balk_blkd_tasks, - rnp->n_balk_exp_gp_tasks, - rnp->n_balk_boost_tasks, - rnp->n_balk_notblocked, - rnp->n_balk_notyet, - rnp->n_balk_nos); -} - -static int show_rcu_node_boost(struct seq_file *m, void *unused) -{ - struct rcu_node *rnp; - - rcu_for_each_leaf_node(&rcu_preempt_state, rnp) - print_one_rcu_node_boost(m, rnp); - return 0; -} - -static int rcu_node_boost_open(struct inode *inode, struct file *file) -{ - return single_open(file, show_rcu_node_boost, NULL); -} - -static const struct file_operations rcu_node_boost_fops = { - .owner = THIS_MODULE, - .open = rcu_node_boost_open, - .read = seq_read, - .llseek = no_llseek, - .release = single_release, -}; - -#endif /* #ifdef CONFIG_RCU_BOOST */ - -static void print_one_rcu_state(struct seq_file *m, struct rcu_state *rsp) -{ - unsigned long gpnum; - int level = 0; - struct rcu_node *rnp; - - gpnum = rsp->gpnum; - seq_printf(m, "c=%ld g=%ld s=%d jfq=%ld j=%x ", - ulong2long(rsp->completed), ulong2long(gpnum), - rsp->fqs_state, - (long)(rsp->jiffies_force_qs - jiffies), - (int)(jiffies & 0xffff)); - seq_printf(m, "nfqs=%lu/nfqsng=%lu(%lu) fqlh=%lu oqlen=%ld/%ld\n", - rsp->n_force_qs, rsp->n_force_qs_ngp, - rsp->n_force_qs - rsp->n_force_qs_ngp, - rsp->n_force_qs_lh, rsp->qlen_lazy, rsp->qlen); - for (rnp = &rsp->node[0]; rnp - &rsp->node[0] < rcu_num_nodes; rnp++) { - if (rnp->level != level) { - seq_puts(m, "\n"); - level = rnp->level; - } - seq_printf(m, "%lx/%lx %c%c>%c %d:%d ^%d ", - rnp->qsmask, rnp->qsmaskinit, - ".G"[rnp->gp_tasks != NULL], - ".E"[rnp->exp_tasks != NULL], - ".T"[!list_empty(&rnp->blkd_tasks)], - rnp->grplo, rnp->grphi, rnp->grpnum); - } - seq_puts(m, "\n"); -} - -static int show_rcuhier(struct seq_file *m, void *v) -{ - struct rcu_state *rsp = (struct rcu_state *)m->private; - print_one_rcu_state(m, rsp); - return 0; -} - -static int rcuhier_open(struct inode *inode, struct file *file) -{ - return single_open(file, show_rcuhier, inode->i_private); -} - -static const struct file_operations rcuhier_fops = { - .owner = THIS_MODULE, - .open = rcuhier_open, - .read = seq_read, - .llseek = no_llseek, - .release = single_release, -}; - -static void show_one_rcugp(struct seq_file *m, struct rcu_state *rsp) -{ - unsigned long flags; - unsigned long completed; - unsigned long gpnum; - unsigned long gpage; - unsigned long gpmax; - struct rcu_node *rnp = &rsp->node[0]; - - raw_spin_lock_irqsave(&rnp->lock, flags); - completed = ACCESS_ONCE(rsp->completed); - gpnum = ACCESS_ONCE(rsp->gpnum); - if (completed == gpnum) - gpage = 0; - else - gpage = jiffies - rsp->gp_start; - gpmax = rsp->gp_max; - raw_spin_unlock_irqrestore(&rnp->lock, flags); - seq_printf(m, "completed=%ld gpnum=%ld age=%ld max=%ld\n", - ulong2long(completed), ulong2long(gpnum), gpage, gpmax); -} - -static int show_rcugp(struct seq_file *m, void *v) -{ - struct rcu_state *rsp = (struct rcu_state *)m->private; - show_one_rcugp(m, rsp); - return 0; -} - -static int rcugp_open(struct inode *inode, struct file *file) -{ - return single_open(file, show_rcugp, inode->i_private); -} - -static const struct file_operations rcugp_fops = { - .owner = THIS_MODULE, - .open = rcugp_open, - .read = seq_read, - .llseek = no_llseek, - .release = single_release, -}; - -static void print_one_rcu_pending(struct seq_file *m, struct rcu_data *rdp) -{ - if (!rdp->beenonline) - return; - seq_printf(m, "%3d%cnp=%ld ", - rdp->cpu, - cpu_is_offline(rdp->cpu) ? '!' : ' ', - rdp->n_rcu_pending); - seq_printf(m, "qsp=%ld rpq=%ld cbr=%ld cng=%ld ", - rdp->n_rp_qs_pending, - rdp->n_rp_report_qs, - rdp->n_rp_cb_ready, - rdp->n_rp_cpu_needs_gp); - seq_printf(m, "gpc=%ld gps=%ld nn=%ld\n", - rdp->n_rp_gp_completed, - rdp->n_rp_gp_started, - rdp->n_rp_need_nothing); -} - -static int show_rcu_pending(struct seq_file *m, void *v) -{ - print_one_rcu_pending(m, (struct rcu_data *)v); - return 0; -} - -static const struct seq_operations rcu_pending_op = { - .start = r_start, - .next = r_next, - .stop = r_stop, - .show = show_rcu_pending, -}; - -static int rcu_pending_open(struct inode *inode, struct file *file) -{ - return r_open(inode, file, &rcu_pending_op); -} - -static const struct file_operations rcu_pending_fops = { - .owner = THIS_MODULE, - .open = rcu_pending_open, - .read = seq_read, - .llseek = no_llseek, - .release = seq_release, -}; - -static int show_rcutorture(struct seq_file *m, void *unused) -{ - seq_printf(m, "rcutorture test sequence: %lu %s\n", - rcutorture_testseq >> 1, - (rcutorture_testseq & 0x1) ? "(test in progress)" : ""); - seq_printf(m, "rcutorture update version number: %lu\n", - rcutorture_vernum); - return 0; -} - -static int rcutorture_open(struct inode *inode, struct file *file) -{ - return single_open(file, show_rcutorture, NULL); -} - -static const struct file_operations rcutorture_fops = { - .owner = THIS_MODULE, - .open = rcutorture_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static struct dentry *rcudir; - -static int __init rcutree_trace_init(void) -{ - struct rcu_state *rsp; - struct dentry *retval; - struct dentry *rspdir; - - rcudir = debugfs_create_dir("rcu", NULL); - if (!rcudir) - goto free_out; - - for_each_rcu_flavor(rsp) { - rspdir = debugfs_create_dir(rsp->name, rcudir); - if (!rspdir) - goto free_out; - - retval = debugfs_create_file("rcudata", 0444, - rspdir, rsp, &rcudata_fops); - if (!retval) - goto free_out; - - retval = debugfs_create_file("rcuexp", 0444, - rspdir, rsp, &rcuexp_fops); - if (!retval) - goto free_out; - - retval = debugfs_create_file("rcu_pending", 0444, - rspdir, rsp, &rcu_pending_fops); - if (!retval) - goto free_out; - - retval = debugfs_create_file("rcubarrier", 0444, - rspdir, rsp, &rcubarrier_fops); - if (!retval) - goto free_out; - -#ifdef CONFIG_RCU_BOOST - if (rsp == &rcu_preempt_state) { - retval = debugfs_create_file("rcuboost", 0444, - rspdir, NULL, &rcu_node_boost_fops); - if (!retval) - goto free_out; - } -#endif - - retval = debugfs_create_file("rcugp", 0444, - rspdir, rsp, &rcugp_fops); - if (!retval) - goto free_out; - - retval = debugfs_create_file("rcuhier", 0444, - rspdir, rsp, &rcuhier_fops); - if (!retval) - goto free_out; - } - - retval = debugfs_create_file("rcutorture", 0444, rcudir, - NULL, &rcutorture_fops); - if (!retval) - goto free_out; - return 0; -free_out: - debugfs_remove_recursive(rcudir); - return 1; -} - -static void __exit rcutree_trace_cleanup(void) -{ - debugfs_remove_recursive(rcudir); -} - - -module_init(rcutree_trace_init); -module_exit(rcutree_trace_cleanup); - -MODULE_AUTHOR("Paul E. McKenney"); -MODULE_DESCRIPTION("Read-Copy Update tracing for hierarchical implementation"); -MODULE_LICENSE("GPL"); diff --git a/kernel/srcu.c b/kernel/srcu.c deleted file mode 100644 index 01d5ccb..0000000 --- a/kernel/srcu.c +++ /dev/null @@ -1,651 +0,0 @@ -/* - * Sleepable Read-Copy Update mechanism for mutual exclusion. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Copyright (C) IBM Corporation, 2006 - * Copyright (C) Fujitsu, 2012 - * - * Author: Paul McKenney - * Lai Jiangshan - * - * For detailed explanation of Read-Copy Update mechanism see - - * Documentation/RCU/ *.txt - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "rcu.h" - -/* - * Initialize an rcu_batch structure to empty. - */ -static inline void rcu_batch_init(struct rcu_batch *b) -{ - b->head = NULL; - b->tail = &b->head; -} - -/* - * Enqueue a callback onto the tail of the specified rcu_batch structure. - */ -static inline void rcu_batch_queue(struct rcu_batch *b, struct rcu_head *head) -{ - *b->tail = head; - b->tail = &head->next; -} - -/* - * Is the specified rcu_batch structure empty? - */ -static inline bool rcu_batch_empty(struct rcu_batch *b) -{ - return b->tail == &b->head; -} - -/* - * Remove the callback at the head of the specified rcu_batch structure - * and return a pointer to it, or return NULL if the structure is empty. - */ -static inline struct rcu_head *rcu_batch_dequeue(struct rcu_batch *b) -{ - struct rcu_head *head; - - if (rcu_batch_empty(b)) - return NULL; - - head = b->head; - b->head = head->next; - if (b->tail == &head->next) - rcu_batch_init(b); - - return head; -} - -/* - * Move all callbacks from the rcu_batch structure specified by "from" to - * the structure specified by "to". - */ -static inline void rcu_batch_move(struct rcu_batch *to, struct rcu_batch *from) -{ - if (!rcu_batch_empty(from)) { - *to->tail = from->head; - to->tail = from->tail; - rcu_batch_init(from); - } -} - -static int init_srcu_struct_fields(struct srcu_struct *sp) -{ - sp->completed = 0; - spin_lock_init(&sp->queue_lock); - sp->running = false; - rcu_batch_init(&sp->batch_queue); - rcu_batch_init(&sp->batch_check0); - rcu_batch_init(&sp->batch_check1); - rcu_batch_init(&sp->batch_done); - INIT_DELAYED_WORK(&sp->work, process_srcu); - sp->per_cpu_ref = alloc_percpu(struct srcu_struct_array); - return sp->per_cpu_ref ? 0 : -ENOMEM; -} - -#ifdef CONFIG_DEBUG_LOCK_ALLOC - -int __init_srcu_struct(struct srcu_struct *sp, const char *name, - struct lock_class_key *key) -{ - /* Don't re-initialize a lock while it is held. */ - debug_check_no_locks_freed((void *)sp, sizeof(*sp)); - lockdep_init_map(&sp->dep_map, name, key, 0); - return init_srcu_struct_fields(sp); -} -EXPORT_SYMBOL_GPL(__init_srcu_struct); - -#else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ - -/** - * init_srcu_struct - initialize a sleep-RCU structure - * @sp: structure to initialize. - * - * Must invoke this on a given srcu_struct before passing that srcu_struct - * to any other function. Each srcu_struct represents a separate domain - * of SRCU protection. - */ -int init_srcu_struct(struct srcu_struct *sp) -{ - return init_srcu_struct_fields(sp); -} -EXPORT_SYMBOL_GPL(init_srcu_struct); - -#endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */ - -/* - * Returns approximate total of the readers' ->seq[] values for the - * rank of per-CPU counters specified by idx. - */ -static unsigned long srcu_readers_seq_idx(struct srcu_struct *sp, int idx) -{ - int cpu; - unsigned long sum = 0; - unsigned long t; - - for_each_possible_cpu(cpu) { - t = ACCESS_ONCE(per_cpu_ptr(sp->per_cpu_ref, cpu)->seq[idx]); - sum += t; - } - return sum; -} - -/* - * Returns approximate number of readers active on the specified rank - * of the per-CPU ->c[] counters. - */ -static unsigned long srcu_readers_active_idx(struct srcu_struct *sp, int idx) -{ - int cpu; - unsigned long sum = 0; - unsigned long t; - - for_each_possible_cpu(cpu) { - t = ACCESS_ONCE(per_cpu_ptr(sp->per_cpu_ref, cpu)->c[idx]); - sum += t; - } - return sum; -} - -/* - * Return true if the number of pre-existing readers is determined to - * be stably zero. An example unstable zero can occur if the call - * to srcu_readers_active_idx() misses an __srcu_read_lock() increment, - * but due to task migration, sees the corresponding __srcu_read_unlock() - * decrement. This can happen because srcu_readers_active_idx() takes - * time to sum the array, and might in fact be interrupted or preempted - * partway through the summation. - */ -static bool srcu_readers_active_idx_check(struct srcu_struct *sp, int idx) -{ - unsigned long seq; - - seq = srcu_readers_seq_idx(sp, idx); - - /* - * The following smp_mb() A pairs with the smp_mb() B located in - * __srcu_read_lock(). This pairing ensures that if an - * __srcu_read_lock() increments its counter after the summation - * in srcu_readers_active_idx(), then the corresponding SRCU read-side - * critical section will see any changes made prior to the start - * of the current SRCU grace period. - * - * Also, if the above call to srcu_readers_seq_idx() saw the - * increment of ->seq[], then the call to srcu_readers_active_idx() - * must see the increment of ->c[]. - */ - smp_mb(); /* A */ - - /* - * Note that srcu_readers_active_idx() can incorrectly return - * zero even though there is a pre-existing reader throughout. - * To see this, suppose that task A is in a very long SRCU - * read-side critical section that started on CPU 0, and that - * no other reader exists, so that the sum of the counters - * is equal to one. Then suppose that task B starts executing - * srcu_readers_active_idx(), summing up to CPU 1, and then that - * task C starts reading on CPU 0, so that its increment is not - * summed, but finishes reading on CPU 2, so that its decrement - * -is- summed. Then when task B completes its sum, it will - * incorrectly get zero, despite the fact that task A has been - * in its SRCU read-side critical section the whole time. - * - * We therefore do a validation step should srcu_readers_active_idx() - * return zero. - */ - if (srcu_readers_active_idx(sp, idx) != 0) - return false; - - /* - * The remainder of this function is the validation step. - * The following smp_mb() D pairs with the smp_mb() C in - * __srcu_read_unlock(). If the __srcu_read_unlock() was seen - * by srcu_readers_active_idx() above, then any destructive - * operation performed after the grace period will happen after - * the corresponding SRCU read-side critical section. - * - * Note that there can be at most NR_CPUS worth of readers using - * the old index, which is not enough to overflow even a 32-bit - * integer. (Yes, this does mean that systems having more than - * a billion or so CPUs need to be 64-bit systems.) Therefore, - * the sum of the ->seq[] counters cannot possibly overflow. - * Therefore, the only way that the return values of the two - * calls to srcu_readers_seq_idx() can be equal is if there were - * no increments of the corresponding rank of ->seq[] counts - * in the interim. But the missed-increment scenario laid out - * above includes an increment of the ->seq[] counter by - * the corresponding __srcu_read_lock(). Therefore, if this - * scenario occurs, the return values from the two calls to - * srcu_readers_seq_idx() will differ, and thus the validation - * step below suffices. - */ - smp_mb(); /* D */ - - return srcu_readers_seq_idx(sp, idx) == seq; -} - -/** - * srcu_readers_active - returns approximate number of readers. - * @sp: which srcu_struct to count active readers (holding srcu_read_lock). - * - * Note that this is not an atomic primitive, and can therefore suffer - * severe errors when invoked on an active srcu_struct. That said, it - * can be useful as an error check at cleanup time. - */ -static int srcu_readers_active(struct srcu_struct *sp) -{ - int cpu; - unsigned long sum = 0; - - for_each_possible_cpu(cpu) { - sum += ACCESS_ONCE(per_cpu_ptr(sp->per_cpu_ref, cpu)->c[0]); - sum += ACCESS_ONCE(per_cpu_ptr(sp->per_cpu_ref, cpu)->c[1]); - } - return sum; -} - -/** - * cleanup_srcu_struct - deconstruct a sleep-RCU structure - * @sp: structure to clean up. - * - * Must invoke this after you are finished using a given srcu_struct that - * was initialized via init_srcu_struct(), else you leak memory. - */ -void cleanup_srcu_struct(struct srcu_struct *sp) -{ - if (WARN_ON(srcu_readers_active(sp))) - return; /* Leakage unless caller handles error. */ - free_percpu(sp->per_cpu_ref); - sp->per_cpu_ref = NULL; -} -EXPORT_SYMBOL_GPL(cleanup_srcu_struct); - -/* - * Counts the new reader in the appropriate per-CPU element of the - * srcu_struct. Must be called from process context. - * Returns an index that must be passed to the matching srcu_read_unlock(). - */ -int __srcu_read_lock(struct srcu_struct *sp) -{ - int idx; - - idx = ACCESS_ONCE(sp->completed) & 0x1; - preempt_disable(); - ACCESS_ONCE(this_cpu_ptr(sp->per_cpu_ref)->c[idx]) += 1; - smp_mb(); /* B */ /* Avoid leaking the critical section. */ - ACCESS_ONCE(this_cpu_ptr(sp->per_cpu_ref)->seq[idx]) += 1; - preempt_enable(); - return idx; -} -EXPORT_SYMBOL_GPL(__srcu_read_lock); - -/* - * Removes the count for the old reader from the appropriate per-CPU - * element of the srcu_struct. Note that this may well be a different - * CPU than that which was incremented by the corresponding srcu_read_lock(). - * Must be called from process context. - */ -void __srcu_read_unlock(struct srcu_struct *sp, int idx) -{ - smp_mb(); /* C */ /* Avoid leaking the critical section. */ - this_cpu_dec(sp->per_cpu_ref->c[idx]); -} -EXPORT_SYMBOL_GPL(__srcu_read_unlock); - -/* - * We use an adaptive strategy for synchronize_srcu() and especially for - * synchronize_srcu_expedited(). We spin for a fixed time period - * (defined below) to allow SRCU readers to exit their read-side critical - * sections. If there are still some readers after 10 microseconds, - * we repeatedly block for 1-millisecond time periods. This approach - * has done well in testing, so there is no need for a config parameter. - */ -#define SRCU_RETRY_CHECK_DELAY 5 -#define SYNCHRONIZE_SRCU_TRYCOUNT 2 -#define SYNCHRONIZE_SRCU_EXP_TRYCOUNT 12 - -/* - * @@@ Wait until all pre-existing readers complete. Such readers - * will have used the index specified by "idx". - * the caller should ensures the ->completed is not changed while checking - * and idx = (->completed & 1) ^ 1 - */ -static bool try_check_zero(struct srcu_struct *sp, int idx, int trycount) -{ - for (;;) { - if (srcu_readers_active_idx_check(sp, idx)) - return true; - if (--trycount <= 0) - return false; - udelay(SRCU_RETRY_CHECK_DELAY); - } -} - -/* - * Increment the ->completed counter so that future SRCU readers will - * use the other rank of the ->c[] and ->seq[] arrays. This allows - * us to wait for pre-existing readers in a starvation-free manner. - */ -static void srcu_flip(struct srcu_struct *sp) -{ - sp->completed++; -} - -/* - * Enqueue an SRCU callback on the specified srcu_struct structure, - * initiating grace-period processing if it is not already running. - */ -void call_srcu(struct srcu_struct *sp, struct rcu_head *head, - void (*func)(struct rcu_head *head)) -{ - unsigned long flags; - - head->next = NULL; - head->func = func; - spin_lock_irqsave(&sp->queue_lock, flags); - rcu_batch_queue(&sp->batch_queue, head); - if (!sp->running) { - sp->running = true; - schedule_delayed_work(&sp->work, 0); - } - spin_unlock_irqrestore(&sp->queue_lock, flags); -} -EXPORT_SYMBOL_GPL(call_srcu); - -struct rcu_synchronize { - struct rcu_head head; - struct completion completion; -}; - -/* - * Awaken the corresponding synchronize_srcu() instance now that a - * grace period has elapsed. - */ -static void wakeme_after_rcu(struct rcu_head *head) -{ - struct rcu_synchronize *rcu; - - rcu = container_of(head, struct rcu_synchronize, head); - complete(&rcu->completion); -} - -static void srcu_advance_batches(struct srcu_struct *sp, int trycount); -static void srcu_reschedule(struct srcu_struct *sp); - -/* - * Helper function for synchronize_srcu() and synchronize_srcu_expedited(). - */ -static void __synchronize_srcu(struct srcu_struct *sp, int trycount) -{ - struct rcu_synchronize rcu; - struct rcu_head *head = &rcu.head; - bool done = false; - - rcu_lockdep_assert(!lock_is_held(&sp->dep_map) && - !lock_is_held(&rcu_bh_lock_map) && - !lock_is_held(&rcu_lock_map) && - !lock_is_held(&rcu_sched_lock_map), - "Illegal synchronize_srcu() in same-type SRCU (or RCU) read-side critical section"); - - might_sleep(); - init_completion(&rcu.completion); - - head->next = NULL; - head->func = wakeme_after_rcu; - spin_lock_irq(&sp->queue_lock); - if (!sp->running) { - /* steal the processing owner */ - sp->running = true; - rcu_batch_queue(&sp->batch_check0, head); - spin_unlock_irq(&sp->queue_lock); - - srcu_advance_batches(sp, trycount); - if (!rcu_batch_empty(&sp->batch_done)) { - BUG_ON(sp->batch_done.head != head); - rcu_batch_dequeue(&sp->batch_done); - done = true; - } - /* give the processing owner to work_struct */ - srcu_reschedule(sp); - } else { - rcu_batch_queue(&sp->batch_queue, head); - spin_unlock_irq(&sp->queue_lock); - } - - if (!done) - wait_for_completion(&rcu.completion); -} - -/** - * synchronize_srcu - wait for prior SRCU read-side critical-section completion - * @sp: srcu_struct with which to synchronize. - * - * Wait for the count to drain to zero of both indexes. To avoid the - * possible starvation of synchronize_srcu(), it waits for the count of - * the index=((->completed & 1) ^ 1) to drain to zero at first, - * and then flip the completed and wait for the count of the other index. - * - * Can block; must be called from process context. - * - * Note that it is illegal to call synchronize_srcu() from the corresponding - * SRCU read-side critical section; doing so will result in deadlock. - * However, it is perfectly legal to call synchronize_srcu() on one - * srcu_struct from some other srcu_struct's read-side critical section. - */ -void synchronize_srcu(struct srcu_struct *sp) -{ - __synchronize_srcu(sp, rcu_expedited - ? SYNCHRONIZE_SRCU_EXP_TRYCOUNT - : SYNCHRONIZE_SRCU_TRYCOUNT); -} -EXPORT_SYMBOL_GPL(synchronize_srcu); - -/** - * synchronize_srcu_expedited - Brute-force SRCU grace period - * @sp: srcu_struct with which to synchronize. - * - * Wait for an SRCU grace period to elapse, but be more aggressive about - * spinning rather than blocking when waiting. - * - * Note that it is also illegal to call synchronize_srcu_expedited() - * from the corresponding SRCU read-side critical section; - * doing so will result in deadlock. However, it is perfectly legal - * to call synchronize_srcu_expedited() on one srcu_struct from some - * other srcu_struct's read-side critical section, as long as - * the resulting graph of srcu_structs is acyclic. - */ -void synchronize_srcu_expedited(struct srcu_struct *sp) -{ - __synchronize_srcu(sp, SYNCHRONIZE_SRCU_EXP_TRYCOUNT); -} -EXPORT_SYMBOL_GPL(synchronize_srcu_expedited); - -/** - * srcu_barrier - Wait until all in-flight call_srcu() callbacks complete. - */ -void srcu_barrier(struct srcu_struct *sp) -{ - synchronize_srcu(sp); -} -EXPORT_SYMBOL_GPL(srcu_barrier); - -/** - * srcu_batches_completed - return batches completed. - * @sp: srcu_struct on which to report batch completion. - * - * Report the number of batches, correlated with, but not necessarily - * precisely the same as, the number of grace periods that have elapsed. - */ -long srcu_batches_completed(struct srcu_struct *sp) -{ - return sp->completed; -} -EXPORT_SYMBOL_GPL(srcu_batches_completed); - -#define SRCU_CALLBACK_BATCH 10 -#define SRCU_INTERVAL 1 - -/* - * Move any new SRCU callbacks to the first stage of the SRCU grace - * period pipeline. - */ -static void srcu_collect_new(struct srcu_struct *sp) -{ - if (!rcu_batch_empty(&sp->batch_queue)) { - spin_lock_irq(&sp->queue_lock); - rcu_batch_move(&sp->batch_check0, &sp->batch_queue); - spin_unlock_irq(&sp->queue_lock); - } -} - -/* - * Core SRCU state machine. Advance callbacks from ->batch_check0 to - * ->batch_check1 and then to ->batch_done as readers drain. - */ -static void srcu_advance_batches(struct srcu_struct *sp, int trycount) -{ - int idx = 1 ^ (sp->completed & 1); - - /* - * Because readers might be delayed for an extended period after - * fetching ->completed for their index, at any point in time there - * might well be readers using both idx=0 and idx=1. We therefore - * need to wait for readers to clear from both index values before - * invoking a callback. - */ - - if (rcu_batch_empty(&sp->batch_check0) && - rcu_batch_empty(&sp->batch_check1)) - return; /* no callbacks need to be advanced */ - - if (!try_check_zero(sp, idx, trycount)) - return; /* failed to advance, will try after SRCU_INTERVAL */ - - /* - * The callbacks in ->batch_check1 have already done with their - * first zero check and flip back when they were enqueued on - * ->batch_check0 in a previous invocation of srcu_advance_batches(). - * (Presumably try_check_zero() returned false during that - * invocation, leaving the callbacks stranded on ->batch_check1.) - * They are therefore ready to invoke, so move them to ->batch_done. - */ - rcu_batch_move(&sp->batch_done, &sp->batch_check1); - - if (rcu_batch_empty(&sp->batch_check0)) - return; /* no callbacks need to be advanced */ - srcu_flip(sp); - - /* - * The callbacks in ->batch_check0 just finished their - * first check zero and flip, so move them to ->batch_check1 - * for future checking on the other idx. - */ - rcu_batch_move(&sp->batch_check1, &sp->batch_check0); - - /* - * SRCU read-side critical sections are normally short, so check - * at least twice in quick succession after a flip. - */ - trycount = trycount < 2 ? 2 : trycount; - if (!try_check_zero(sp, idx^1, trycount)) - return; /* failed to advance, will try after SRCU_INTERVAL */ - - /* - * The callbacks in ->batch_check1 have now waited for all - * pre-existing readers using both idx values. They are therefore - * ready to invoke, so move them to ->batch_done. - */ - rcu_batch_move(&sp->batch_done, &sp->batch_check1); -} - -/* - * Invoke a limited number of SRCU callbacks that have passed through - * their grace period. If there are more to do, SRCU will reschedule - * the workqueue. - */ -static void srcu_invoke_callbacks(struct srcu_struct *sp) -{ - int i; - struct rcu_head *head; - - for (i = 0; i < SRCU_CALLBACK_BATCH; i++) { - head = rcu_batch_dequeue(&sp->batch_done); - if (!head) - break; - local_bh_disable(); - head->func(head); - local_bh_enable(); - } -} - -/* - * Finished one round of SRCU grace period. Start another if there are - * more SRCU callbacks queued, otherwise put SRCU into not-running state. - */ -static void srcu_reschedule(struct srcu_struct *sp) -{ - bool pending = true; - - if (rcu_batch_empty(&sp->batch_done) && - rcu_batch_empty(&sp->batch_check1) && - rcu_batch_empty(&sp->batch_check0) && - rcu_batch_empty(&sp->batch_queue)) { - spin_lock_irq(&sp->queue_lock); - if (rcu_batch_empty(&sp->batch_done) && - rcu_batch_empty(&sp->batch_check1) && - rcu_batch_empty(&sp->batch_check0) && - rcu_batch_empty(&sp->batch_queue)) { - sp->running = false; - pending = false; - } - spin_unlock_irq(&sp->queue_lock); - } - - if (pending) - schedule_delayed_work(&sp->work, SRCU_INTERVAL); -} - -/* - * This is the work-queue function that handles SRCU grace periods. - */ -void process_srcu(struct work_struct *work) -{ - struct srcu_struct *sp; - - sp = container_of(work, struct srcu_struct, work.work); - - srcu_collect_new(sp); - srcu_advance_batches(sp, 1); - srcu_invoke_callbacks(sp); - srcu_reschedule(sp); -} -EXPORT_SYMBOL_GPL(process_srcu); -- cgit v0.10.2 From 4a7d3e8a9939cf8073c247286d81cbe0ae48eba2 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 15 Oct 2013 15:31:51 +0200 Subject: clocksource: arch_timer: Do not register arch_sys_counter twice Commit: 65cd4f6 ("arch_timer: Move to generic sched_clock framework") added code to register the arch_sys_counter in arch_timer_register(), but it is already registered in arch_counter_register(). This results in the timer being added to the clocksource list twice, therefore causing an infinite loop in the list. Remove the duplicate registration and register the scheduler clock after the original registration instead. This fixes a hang during boot on Tegra114 (Cortex-A15). [ While I've only tested this on Tegra114, I suspect the same hang during boot happens for all processors that use this clock source. ] Signed-off-by: Thierry Reding Acked-by: John Stultz Cc: Stephen Boyd Cc: Will Deacon Cc: Stephen Warren Cc: linux-arm-kernel@lists.infradead.org Cc: Daniel Lezcano Link: http://lkml.kernel.org/r/1381843911-31962-1-git-send-email-treding@nvidia.com Signed-off-by: Ingo Molnar diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index f655036..95fb944 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -436,6 +436,9 @@ static void __init arch_counter_register(unsigned type) cyclecounter.mult = clocksource_counter.mult; cyclecounter.shift = clocksource_counter.shift; timecounter_init(&timecounter, &cyclecounter, start_count); + + /* 56 bits minimum, so we assume worst case rollover */ + sched_clock_register(arch_timer_read_counter, 56, arch_timer_rate); } static void arch_timer_stop(struct clock_event_device *clk) @@ -515,15 +518,6 @@ static int __init arch_timer_register(void) goto out; } - clocksource_register_hz(&clocksource_counter, arch_timer_rate); - cyclecounter.mult = clocksource_counter.mult; - cyclecounter.shift = clocksource_counter.shift; - timecounter_init(&timecounter, &cyclecounter, - arch_counter_get_cntvct()); - - /* 56 bits minimum, so we assume worst case rollover */ - sched_clock_register(arch_timer_read_counter, 56, arch_timer_rate); - if (arch_timer_use_virtual) { ppi = arch_timer_ppi[VIRT_PPI]; err = request_percpu_irq(ppi, arch_timer_handler_virt, -- cgit v0.10.2 From 472f95b93860ef8ba562c6231ab8f6ff9d352a0e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 11 Oct 2013 19:10:00 +0200 Subject: gpio: adnp: rename "virq" to "child_irq" This variable is confusingly named, the different Linux IRQs aren't any more virtual than any other Linux IRQ. Give it a non-misleading name. Acked-by: Lars Poeschel Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c index 6439e0e..b204033 100644 --- a/drivers/gpio/gpio-adnp.c +++ b/drivers/gpio/gpio-adnp.c @@ -325,9 +325,9 @@ static irqreturn_t adnp_irq(int irq, void *data) pending &= isr & ier; for_each_set_bit(bit, &pending, 8) { - unsigned int virq; - virq = irq_find_mapping(adnp->domain, base + bit); - handle_nested_irq(virq); + unsigned int child_irq; + child_irq = irq_find_mapping(adnp->domain, base + bit); + handle_nested_irq(child_irq); } } -- cgit v0.10.2 From d933cc619e36c2288730fd8a8bdbb16a35dade07 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 11 Oct 2013 19:14:50 +0200 Subject: gpio: bcm_kona: rename confusing variables Rename the argument "virq" to just "irq", this IRQ isn't any more "virtual" than any other Linux IRQ number, we use "hwirq" for the actual hw-numbers, "virq" is just bogus. Rename the "gpio" variable to "hwirq" to reflect what it is. Rename one instance of "virq" to "child_irq" that better describes what it is. Cc: Markus Mayer Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index ff5c98e..58188ba 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -428,9 +428,10 @@ static void bcm_kona_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) while ((sta = readl(reg_base + GPIO_INT_STATUS(bank_id)) & (~(readl(reg_base + GPIO_INT_MASK(bank_id)))))) { for_each_set_bit(bit, &sta, 32) { - int gpio = GPIO_PER_BANK * bank_id + bit; - int virq = irq_find_mapping(bank->kona_gpio->irq_domain, - gpio); + int hwirq = GPIO_PER_BANK * bank_id + bit; + int child_irq = + irq_find_mapping(bank->kona_gpio->irq_domain, + hwirq); /* * Clear interrupt before handler is called so we don't * miss any interrupt occurred during executing them. @@ -438,7 +439,7 @@ static void bcm_kona_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) writel(readl(reg_base + GPIO_INT_STATUS(bank_id)) | BIT(bit), reg_base + GPIO_INT_STATUS(bank_id)); /* Invoke interrupt handler */ - generic_handle_irq(virq); + generic_handle_irq(child_irq); } } @@ -487,10 +488,10 @@ static int bcm_kona_gpio_irq_map(struct irq_domain *d, unsigned int irq, return 0; } -static void bcm_kona_gpio_irq_unmap(struct irq_domain *d, unsigned int virq) +static void bcm_kona_gpio_irq_unmap(struct irq_domain *d, unsigned int irq) { - irq_set_chip_and_handler(virq, NULL, NULL); - irq_set_chip_data(virq, NULL); + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_chip_data(irq, NULL); } static struct irq_domain_ops bcm_kona_irq_ops = { -- cgit v0.10.2 From 2d61e3e90798fdedb0a33714a30b241a5d5f2744 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 11 Oct 2013 19:21:34 +0200 Subject: gpio: em: drop references to "virtual" IRQ Rename the argument "virq" to just "irq", this IRQ isn't any more "virtual" than any other Linux IRQ number, we use "hwirq" for the actual hw-numbers, "virq" is just bogus. Cc: Magnus Damm Reviewed-by: Ian Molton Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c index 160d759..ec19036 100644 --- a/drivers/gpio/gpio-em.c +++ b/drivers/gpio/gpio-em.c @@ -232,16 +232,16 @@ static void em_gio_free(struct gpio_chip *chip, unsigned offset) em_gio_direction_input(chip, offset); } -static int em_gio_irq_domain_map(struct irq_domain *h, unsigned int virq, - irq_hw_number_t hw) +static int em_gio_irq_domain_map(struct irq_domain *h, unsigned int irq, + irq_hw_number_t hwirq) { struct em_gio_priv *p = h->host_data; - pr_debug("gio: map hw irq = %d, virq = %d\n", (int)hw, virq); + pr_debug("gio: map hw irq = %d, irq = %d\n", (int)hwirq, irq); - irq_set_chip_data(virq, h->host_data); - irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq); - set_irq_flags(virq, IRQF_VALID); /* kill me now */ + irq_set_chip_data(irq, h->host_data); + irq_set_chip_and_handler(irq, &p->irq_chip, handle_level_irq); + set_irq_flags(irq, IRQF_VALID); /* kill me now */ return 0; } -- cgit v0.10.2 From ba519dd46c46fb923b531ef833aac074cb0114b3 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 11 Oct 2013 19:27:02 +0200 Subject: gpio: intel-mid: drop references to "virtual" IRQ Rename the argument "virq" to just "irq", this IRQ isn't any more "virtual" than any other Linux IRQ number, we use "hwirq" for the actual hw-numbers, "virq" is just bogus. Acked-by: David Cohen Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-intel-mid.c b/drivers/gpio/gpio-intel-mid.c index 612b54d..be803af 100644 --- a/drivers/gpio/gpio-intel-mid.c +++ b/drivers/gpio/gpio-intel-mid.c @@ -353,15 +353,15 @@ static void intel_mid_irq_init_hw(struct intel_mid_gpio *priv) } } -static int intel_gpio_irq_map(struct irq_domain *d, unsigned int virq, - irq_hw_number_t hw) +static int intel_gpio_irq_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) { struct intel_mid_gpio *priv = d->host_data; - irq_set_chip_and_handler_name(virq, &intel_mid_irqchip, + irq_set_chip_and_handler_name(irq, &intel_mid_irqchip, handle_simple_irq, "demux"); - irq_set_chip_data(virq, priv); - irq_set_irq_type(virq, IRQ_TYPE_NONE); + irq_set_chip_data(irq, priv); + irq_set_irq_type(irq, IRQ_TYPE_NONE); return 0; } -- cgit v0.10.2 From b551b023bdeb28fe81ab522b25e89f85c6418198 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 11 Oct 2013 19:32:16 +0200 Subject: gpio: lynxpoint: drop references to "virtual" IRQ Rename the argument "virq" to just "irq", this IRQ isn't any more "virtual" than any other Linux IRQ number, we use "hwirq" for the actual hw-numbers, "virq" is just bogus. Take this opportunity to sink a variable into a loop. Acked-by: Mathias Nyman Acked-by: Mika Westerberg Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-lynxpoint.c b/drivers/gpio/gpio-lynxpoint.c index 2d9ca60..21603e6 100644 --- a/drivers/gpio/gpio-lynxpoint.c +++ b/drivers/gpio/gpio-lynxpoint.c @@ -242,26 +242,27 @@ static int lp_gpio_to_irq(struct gpio_chip *chip, unsigned offset) return irq_create_mapping(lg->domain, offset); } -static void lp_gpio_irq_handler(unsigned irq, struct irq_desc *desc) +static void lp_gpio_irq_handler(unsigned hwirq, struct irq_desc *desc) { struct irq_data *data = irq_desc_get_irq_data(desc); struct lp_gpio *lg = irq_data_get_irq_handler_data(data); struct irq_chip *chip = irq_data_get_irq_chip(data); u32 base, pin, mask; unsigned long reg, pending; - unsigned virq; /* check from GPIO controller which pin triggered the interrupt */ for (base = 0; base < lg->chip.ngpio; base += 32) { reg = lp_gpio_reg(&lg->chip, base, LP_INT_STAT); while ((pending = inl(reg))) { + unsigned irq; + pin = __ffs(pending); mask = BIT(pin); /* Clear before handling so we don't lose an edge */ outl(mask, reg); - virq = irq_find_mapping(lg->domain, base + pin); - generic_handle_irq(virq); + irq = irq_find_mapping(lg->domain, base + pin); + generic_handle_irq(irq); } } chip->irq_eoi(data); @@ -324,15 +325,15 @@ static void lp_gpio_irq_init_hw(struct lp_gpio *lg) } } -static int lp_gpio_irq_map(struct irq_domain *d, unsigned int virq, - irq_hw_number_t hw) +static int lp_gpio_irq_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) { struct lp_gpio *lg = d->host_data; - irq_set_chip_and_handler_name(virq, &lp_irqchip, handle_simple_irq, + irq_set_chip_and_handler_name(irq, &lp_irqchip, handle_simple_irq, "demux"); - irq_set_chip_data(virq, lg); - irq_set_irq_type(virq, IRQ_TYPE_NONE); + irq_set_chip_data(irq, lg); + irq_set_irq_type(irq, IRQ_TYPE_NONE); return 0; } -- cgit v0.10.2 From 5ba17ae9b84a04ee9aaa213a1cbb060f8e13c20c Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 11 Oct 2013 19:37:30 +0200 Subject: gpio: mpc8xxx: drop references to "virtual" IRQ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the argument "virq" to just "irq", this IRQ isn't any more "virtual" than any other Linux IRQ number, we use "hwirq" for the actual hw-numbers, "virq" is just bogus. Cc: Anatolij Gustschin Cc: Uwe Kleine-König Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index a0b33a2..b350649 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -282,16 +282,16 @@ static struct irq_chip mpc8xxx_irq_chip = { .irq_set_type = mpc8xxx_irq_set_type, }; -static int mpc8xxx_gpio_irq_map(struct irq_domain *h, unsigned int virq, - irq_hw_number_t hw) +static int mpc8xxx_gpio_irq_map(struct irq_domain *h, unsigned int irq, + irq_hw_number_t hwirq) { struct mpc8xxx_gpio_chip *mpc8xxx_gc = h->host_data; if (mpc8xxx_gc->of_dev_id_data) mpc8xxx_irq_chip.irq_set_type = mpc8xxx_gc->of_dev_id_data; - irq_set_chip_data(virq, h->host_data); - irq_set_chip_and_handler(virq, &mpc8xxx_irq_chip, handle_level_irq); + irq_set_chip_data(irq, h->host_data); + irq_set_chip_and_handler(irq, &mpc8xxx_irq_chip, handle_level_irq); return 0; } -- cgit v0.10.2 From f8f669f706664ef506b233157a1af67eaebf380c Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 11 Oct 2013 19:40:16 +0200 Subject: gpio: pl061: drop references to "virtual" IRQ Rename the argument "virq" to just "irq", this IRQ isn't any more "virtual" than any other Linux IRQ number, we use "hwirq" for the actual hw-numbers, "virq" is just bogus. Cc: Haojian Zhuang Cc: Rob Herring Acked-by: Baruch Siach Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index 4274e2e..f22f7f3 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -238,15 +238,15 @@ static struct irq_chip pl061_irqchip = { .irq_set_type = pl061_irq_type, }; -static int pl061_irq_map(struct irq_domain *d, unsigned int virq, - irq_hw_number_t hw) +static int pl061_irq_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) { struct pl061_gpio *chip = d->host_data; - irq_set_chip_and_handler_name(virq, &pl061_irqchip, handle_simple_irq, + irq_set_chip_and_handler_name(irq, &pl061_irqchip, handle_simple_irq, "pl061"); - irq_set_chip_data(virq, chip); - irq_set_irq_type(virq, IRQ_TYPE_NONE); + irq_set_chip_data(irq, chip); + irq_set_irq_type(irq, IRQ_TYPE_NONE); return 0; } -- cgit v0.10.2 From c0d6c1ad0ad8fa1b7c2148ba918fd5d64a51166a Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 11 Oct 2013 19:43:39 +0200 Subject: gpio: rcar: drop references to "virtual" IRQ Rename the argument "virq" to just "irq", this IRQ isn't any more "virtual" than any other Linux IRQ number, we use "hwirq" for the actual hw-numbers, "virq" is just bogus. Cc: Magnus Damm Acked-by: Laurent Pinchart Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index e3745eb..65dbf87 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -266,16 +266,16 @@ static int gpio_rcar_to_irq(struct gpio_chip *chip, unsigned offset) return irq_create_mapping(gpio_to_priv(chip)->irq_domain, offset); } -static int gpio_rcar_irq_domain_map(struct irq_domain *h, unsigned int virq, - irq_hw_number_t hw) +static int gpio_rcar_irq_domain_map(struct irq_domain *h, unsigned int irq, + irq_hw_number_t hwirq) { struct gpio_rcar_priv *p = h->host_data; - dev_dbg(&p->pdev->dev, "map hw irq = %d, virq = %d\n", (int)hw, virq); + dev_dbg(&p->pdev->dev, "map hw irq = %d, irq = %d\n", (int)hwirq, irq); - irq_set_chip_data(virq, h->host_data); - irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq); - set_irq_flags(virq, IRQF_VALID); /* kill me now */ + irq_set_chip_data(irq, h->host_data); + irq_set_chip_and_handler(irq, &p->irq_chip, handle_level_irq); + set_irq_flags(irq, IRQF_VALID); /* kill me now */ return 0; } -- cgit v0.10.2 From ed05e204af5a2a7814858407bfd698d51e31f938 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 11 Oct 2013 19:51:38 +0200 Subject: gpio: stmpe: drop references to "virtual" IRQ, fix bug Rename the argument "virq" to just "irq", this IRQ isn't any more "virtual" than any other Linux IRQ number, we use "hwirq" for the actual hw-numbers, "virq" is just bogus. When doing this I see that the hwirq argument is used for mapping rather than the Linux IRQ in the map function. This doesn't look right. Use the Linux IRQ instead. I cannot test this patch so I don't know if the mapping change is correct, however since absolutely every other driver does it the other way around this doesn't look sound at all. Please help out with review. Cc: Vipul Kumar Samar Cc: Lee Jones Cc: Gabriel Fernandez Cc: Jean-Nicolas Graux Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index b33bad1..2647e24 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -254,9 +254,10 @@ static irqreturn_t stmpe_gpio_irq(int irq, void *dev) while (stat) { int bit = __ffs(stat); int line = bank * 8 + bit; - int virq = irq_find_mapping(stmpe_gpio->domain, line); + int child_irq = irq_find_mapping(stmpe_gpio->domain, + line); - handle_nested_irq(virq); + handle_nested_irq(child_irq); stat &= ~(1 << bit); } @@ -271,7 +272,7 @@ static irqreturn_t stmpe_gpio_irq(int irq, void *dev) return IRQ_HANDLED; } -static int stmpe_gpio_irq_map(struct irq_domain *d, unsigned int virq, +static int stmpe_gpio_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hwirq) { struct stmpe_gpio *stmpe_gpio = d->host_data; @@ -279,26 +280,26 @@ static int stmpe_gpio_irq_map(struct irq_domain *d, unsigned int virq, if (!stmpe_gpio) return -EINVAL; - irq_set_chip_data(hwirq, stmpe_gpio); - irq_set_chip_and_handler(hwirq, &stmpe_gpio_irq_chip, + irq_set_chip_data(irq, stmpe_gpio); + irq_set_chip_and_handler(irq, &stmpe_gpio_irq_chip, handle_simple_irq); - irq_set_nested_thread(hwirq, 1); + irq_set_nested_thread(irq, 1); #ifdef CONFIG_ARM - set_irq_flags(hwirq, IRQF_VALID); + set_irq_flags(irq, IRQF_VALID); #else - irq_set_noprobe(hwirq); + irq_set_noprobe(irq); #endif return 0; } -static void stmpe_gpio_irq_unmap(struct irq_domain *d, unsigned int virq) +static void stmpe_gpio_irq_unmap(struct irq_domain *d, unsigned int irq) { #ifdef CONFIG_ARM - set_irq_flags(virq, 0); + set_irq_flags(irq, 0); #endif - irq_set_chip_and_handler(virq, NULL, NULL); - irq_set_chip_data(virq, NULL); + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_chip_data(irq, NULL); } static const struct irq_domain_ops stmpe_gpio_irq_simple_ops = { -- cgit v0.10.2 From d27e06ac5dcf60d5502269e1875bcb0f05f1b1c1 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 14 Oct 2013 10:07:08 +0200 Subject: gpio: ep93xx: get rid of bogus __raw* accessors I have no idea why this driver is using __raw* accessors for reading and writing registers, I suspect it is just force of habit or copy/paste. Change all to readb()/writeb() except one chain where I used writeb_relaxed() up until the last writeb(). Cc: Ryan Mallon Cc: H Hartley Sweeten Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index 56b98ee..80829f3 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -51,15 +51,15 @@ static void ep93xx_gpio_update_int_params(unsigned port) { BUG_ON(port > 2); - __raw_writeb(0, EP93XX_GPIO_REG(int_en_register_offset[port])); + writeb_relaxed(0, EP93XX_GPIO_REG(int_en_register_offset[port])); - __raw_writeb(gpio_int_type2[port], + writeb_relaxed(gpio_int_type2[port], EP93XX_GPIO_REG(int_type2_register_offset[port])); - __raw_writeb(gpio_int_type1[port], + writeb_relaxed(gpio_int_type1[port], EP93XX_GPIO_REG(int_type1_register_offset[port])); - __raw_writeb(gpio_int_unmasked[port] & gpio_int_enabled[port], + writeb(gpio_int_unmasked[port] & gpio_int_enabled[port], EP93XX_GPIO_REG(int_en_register_offset[port])); } @@ -74,7 +74,7 @@ static void ep93xx_gpio_int_debounce(unsigned int irq, bool enable) else gpio_int_debounce[port] &= ~port_mask; - __raw_writeb(gpio_int_debounce[port], + writeb(gpio_int_debounce[port], EP93XX_GPIO_REG(int_debounce_register_offset[port])); } @@ -83,7 +83,7 @@ static void ep93xx_gpio_ab_irq_handler(unsigned int irq, struct irq_desc *desc) unsigned char status; int i; - status = __raw_readb(EP93XX_GPIO_A_INT_STATUS); + status = readb(EP93XX_GPIO_A_INT_STATUS); for (i = 0; i < 8; i++) { if (status & (1 << i)) { int gpio_irq = gpio_to_irq(EP93XX_GPIO_LINE_A(0)) + i; @@ -91,7 +91,7 @@ static void ep93xx_gpio_ab_irq_handler(unsigned int irq, struct irq_desc *desc) } } - status = __raw_readb(EP93XX_GPIO_B_INT_STATUS); + status = readb(EP93XX_GPIO_B_INT_STATUS); for (i = 0; i < 8; i++) { if (status & (1 << i)) { int gpio_irq = gpio_to_irq(EP93XX_GPIO_LINE_B(0)) + i; @@ -124,7 +124,7 @@ static void ep93xx_gpio_irq_ack(struct irq_data *d) ep93xx_gpio_update_int_params(port); } - __raw_writeb(port_mask, EP93XX_GPIO_REG(eoi_register_offset[port])); + writeb(port_mask, EP93XX_GPIO_REG(eoi_register_offset[port])); } static void ep93xx_gpio_irq_mask_ack(struct irq_data *d) @@ -139,7 +139,7 @@ static void ep93xx_gpio_irq_mask_ack(struct irq_data *d) gpio_int_unmasked[port] &= ~port_mask; ep93xx_gpio_update_int_params(port); - __raw_writeb(port_mask, EP93XX_GPIO_REG(eoi_register_offset[port])); + writeb(port_mask, EP93XX_GPIO_REG(eoi_register_offset[port])); } static void ep93xx_gpio_irq_mask(struct irq_data *d) -- cgit v0.10.2 From d468bf9ecaabd3bf3a6134e5a369ced82b1d1ca1 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 24 Sep 2013 11:54:38 +0200 Subject: gpio: add API to be strict about GPIO IRQ usage It is currently often possible in many GPIO drivers to request a GPIO line to be used as IRQ after calling gpio_to_irq() and, as the gpiolib is not aware of this, set the same line to output and start driving it, with undesired side effects. As it is a bogus usage scenario to request a line flagged as output to used as IRQ, we introduce APIs to let gpiolib track the use of a line as IRQ, and also set this flag from the userspace ABI. The API is symmetric so that lines can also be flagged from .irq_enable() and unflagged from IRQ by .irq_disable(). The debugfs file is altered so that we see if a line is reserved for IRQ. Cc: Enric Balletbo i Serra Cc: Grant Likely Cc: Jean-Christophe PLAGNIOL-VILLARD Cc: Santosh Shilimkar Acked-by: Alexandre Courbot Reviewed-by: Stephen Warren Reviewed-by: Javier Martinez Canillas Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 4fc2860..1014cb5 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -60,6 +60,7 @@ struct gpio_desc { #define FLAG_ACTIVE_LOW 6 /* sysfs value has active low */ #define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */ #define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */ +#define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */ #define ID_SHIFT 16 /* add new flags before this one */ @@ -96,6 +97,8 @@ static int gpiod_get_value(const struct gpio_desc *desc); static void gpiod_set_value(struct gpio_desc *desc, int value); static int gpiod_cansleep(const struct gpio_desc *desc); static int gpiod_to_irq(const struct gpio_desc *desc); +static int gpiod_lock_as_irq(struct gpio_desc *desc); +static void gpiod_unlock_as_irq(struct gpio_desc *desc); static int gpiod_export(struct gpio_desc *desc, bool direction_may_change); static int gpiod_export_link(struct device *dev, const char *name, struct gpio_desc *desc); @@ -163,6 +166,17 @@ static struct gpio_desc *gpio_to_desc(unsigned gpio) } /** + * Convert an offset on a certain chip to a corresponding descriptor + */ +static struct gpio_desc *gpiochip_offset_to_desc(struct gpio_chip *chip, + unsigned int offset) +{ + unsigned int gpio = chip->base + offset; + + return gpio_to_desc(gpio); +} + +/** * Convert a GPIO descriptor to the integer namespace. * This should disappear in the future but is needed since we still * use GPIO numbers for error messages and sysfs nodes @@ -428,6 +442,7 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev, desc->flags &= ~GPIO_TRIGGER_MASK; if (!gpio_flags) { + gpiod_unlock_as_irq(desc); ret = 0; goto free_id; } @@ -466,6 +481,12 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev, if (ret < 0) goto free_id; + ret = gpiod_lock_as_irq(desc); + if (ret < 0) { + gpiod_warn(desc, "failed to flag the GPIO for IRQ\n"); + goto free_id; + } + desc->flags |= gpio_flags; return 0; @@ -1730,6 +1751,14 @@ static int gpiod_direction_output(struct gpio_desc *desc, int value) return -EINVAL; } + /* GPIOs used for IRQs shall not be set as output */ + if (test_bit(FLAG_USED_AS_IRQ, &desc->flags)) { + gpiod_err(desc, + "%s: tried to set a GPIO tied to an IRQ as output\n", + __func__); + return -EIO; + } + /* Open drain pin should not be driven to 1 */ if (value && test_bit(FLAG_OPEN_DRAIN, &desc->flags)) return gpiod_direction_input(desc); @@ -2050,6 +2079,58 @@ int __gpio_to_irq(unsigned gpio) } EXPORT_SYMBOL_GPL(__gpio_to_irq); +/** + * gpiod_lock_as_irq() - lock a GPIO to be used as IRQ + * @gpio: the GPIO line to lock as used for IRQ + * + * This is used directly by GPIO drivers that want to lock down + * a certain GPIO line to be used as IRQs, for example in the + * .to_irq() callback of their gpio_chip, or in the .irq_enable() + * of its irq_chip implementation if the GPIO is known from that + * code. + */ +static int gpiod_lock_as_irq(struct gpio_desc *desc) +{ + if (!desc) + return -EINVAL; + + if (test_bit(FLAG_IS_OUT, &desc->flags)) { + gpiod_err(desc, + "%s: tried to flag a GPIO set as output for IRQ\n", + __func__); + return -EIO; + } + + set_bit(FLAG_USED_AS_IRQ, &desc->flags); + return 0; +} + +int gpio_lock_as_irq(struct gpio_chip *chip, unsigned int offset) +{ + return gpiod_lock_as_irq(gpiochip_offset_to_desc(chip, offset)); +} +EXPORT_SYMBOL_GPL(gpio_lock_as_irq); + +/** + * gpiod_unlock_as_irq() - unlock a GPIO used as IRQ + * @gpio: the GPIO line to unlock from IRQ usage + * + * This is used directly by GPIO drivers that want to indicate + * that a certain GPIO is no longer used exclusively for IRQ. + */ +static void gpiod_unlock_as_irq(struct gpio_desc *desc) +{ + if (!desc) + return; + + clear_bit(FLAG_USED_AS_IRQ, &desc->flags); +} + +void gpio_unlock_as_irq(struct gpio_chip *chip, unsigned int offset) +{ + return gpiod_unlock_as_irq(gpiochip_offset_to_desc(chip, offset)); +} +EXPORT_SYMBOL_GPL(gpio_unlock_as_irq); /* There's no value in making it easy to inline GPIO calls that may sleep. * Common examples include ones connected to I2C or SPI chips. @@ -2091,6 +2172,7 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) unsigned gpio = chip->base; struct gpio_desc *gdesc = &chip->desc[0]; int is_out; + int is_irq; for (i = 0; i < chip->ngpio; i++, gpio++, gdesc++) { if (!test_bit(FLAG_REQUESTED, &gdesc->flags)) @@ -2098,12 +2180,14 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) gpiod_get_direction(gdesc); is_out = test_bit(FLAG_IS_OUT, &gdesc->flags); - seq_printf(s, " gpio-%-3d (%-20.20s) %s %s", + is_irq = test_bit(FLAG_USED_AS_IRQ, &gdesc->flags); + seq_printf(s, " gpio-%-3d (%-20.20s) %s %s %s", gpio, gdesc->label, is_out ? "out" : "in ", chip->get ? (chip->get(chip, i) ? "hi" : "lo") - : "? "); + : "? ", + is_irq ? "IRQ" : " "); seq_printf(s, "\n"); } } diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index bde6469..b309a5c 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -192,6 +192,9 @@ extern int __gpio_cansleep(unsigned gpio); extern int __gpio_to_irq(unsigned gpio); +extern int gpio_lock_as_irq(struct gpio_chip *chip, unsigned int offset); +extern void gpio_unlock_as_irq(struct gpio_chip *chip, unsigned int offset); + extern int gpio_request_one(unsigned gpio, unsigned long flags, const char *label); extern int gpio_request_array(const struct gpio *array, size_t num); extern void gpio_free_array(const struct gpio *array, size_t num); diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 552e3f4..a06ec3e 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -204,6 +204,18 @@ static inline int gpio_to_irq(unsigned gpio) return -EINVAL; } +static inline int gpio_lock_as_irq(struct gpio_chip *chip, unsigned int offset) +{ + WARN_ON(1); + return -EINVAL; +} + +static inline void gpio_unlock_as_irq(struct gpio_chip *chip, + unsigned int offset) +{ + WARN_ON(1); +} + static inline int irq_to_gpio(unsigned irq) { /* irq can never have been returned from gpio_to_irq() */ -- cgit v0.10.2 From 494336f3a6eabeede8ce07b73f5023ff4f7c745d Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 11 Oct 2013 11:30:25 +0200 Subject: pinctrl: nomadik: mark GPIO lines used for IRQ When an IRQ is started on a GPIO line, mark this GPIO as IRQ in the gpiolib so we can keep track of the usage centrally. Cc: Enric Balletbo i Serra Cc: Grant Likely Cc: Jean-Christophe PLAGNIOL-VILLARD Cc: Santosh Shilimkar Cc: Stephen Warren Cc: Javier Martinez Canillas Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/pinctrl-nomadik.c b/drivers/pinctrl/pinctrl-nomadik.c index d7c3ae3..7111c3b 100644 --- a/drivers/pinctrl/pinctrl-nomadik.c +++ b/drivers/pinctrl/pinctrl-nomadik.c @@ -634,6 +634,10 @@ static unsigned int nmk_gpio_irq_startup(struct irq_data *d) { struct nmk_gpio_chip *nmk_chip = irq_data_get_irq_chip_data(d); + if (gpio_lock_as_irq(&nmk_chip->chip, d->hwirq)) + dev_err(nmk_chip->chip.dev, + "unable to lock HW IRQ %lu for IRQ\n", + d->hwirq); clk_enable(nmk_chip->clk); nmk_gpio_irq_unmask(d); return 0; @@ -645,6 +649,7 @@ static void nmk_gpio_irq_shutdown(struct irq_data *d) nmk_gpio_irq_mask(d); clk_disable(nmk_chip->clk); + gpio_unlock_as_irq(&nmk_chip->chip, d->hwirq); } static struct irq_chip nmk_gpio_irq_chip = { -- cgit v0.10.2 From 873ee9ed2c020b59e22a5082f73fc2960ec959bf Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 14 Oct 2013 10:25:59 +0200 Subject: pinctrl: coh901: mark GPIO lines used for IRQ When an IRQ is started on a GPIO line, mark this GPIO as IRQ in the gpiolib so we can keep track of the usage centrally. Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/pinctrl-coh901.c b/drivers/pinctrl/pinctrl-coh901.c index f22a219..162ac0d 100644 --- a/drivers/pinctrl/pinctrl-coh901.c +++ b/drivers/pinctrl/pinctrl-coh901.c @@ -529,6 +529,10 @@ static void u300_gpio_irq_enable(struct irq_data *d) dev_dbg(gpio->dev, "enable IRQ for hwirq %lu on port %s, offset %d\n", d->hwirq, port->name, offset); + if (gpio_lock_as_irq(&gpio->chip, d->hwirq)) + dev_err(gpio->dev, + "unable to lock HW IRQ %lu for IRQ\n", + d->hwirq); local_irq_save(flags); val = readl(U300_PIN_REG(offset, ien)); writel(val | U300_PIN_BIT(offset), U300_PIN_REG(offset, ien)); @@ -547,6 +551,7 @@ static void u300_gpio_irq_disable(struct irq_data *d) val = readl(U300_PIN_REG(offset, ien)); writel(val & ~U300_PIN_BIT(offset), U300_PIN_REG(offset, ien)); local_irq_restore(flags); + gpio_unlock_as_irq(&gpio->chip, d->hwirq); } static struct irq_chip u300_gpio_irqchip = { -- cgit v0.10.2 From 2f56e0a57ff1bbf973bec86e527f222de8c4b4f9 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Wed, 16 Oct 2013 02:47:30 +0200 Subject: gpio/omap: use gpiolib API to mark a GPIO used as an IRQ The OMAP GPIO driver keeps track about GPIO pins that are used as IRQ lines for two reasons: 1) To prevent GPIO banks to be disabled while one of their GPIO pins are only used as an interrupt line. 2) To not allow another caller to set the GPIO pin as output. Now gpiolib has an API to mark GPIO pins as used as IRQ lines so the GPIO core only allows to set as output GPIO pins not tied to an IRQ. So there is no need to have custom code for 2). The IRQ usage still has to be maintained locally for 1) though. Signed-off-by: Javier Martinez Canillas Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 89675f8..f319c9f 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -514,6 +514,14 @@ static int gpio_irq_type(struct irq_data *d, unsigned type) return -EINVAL; } + retval = gpio_lock_as_irq(&bank->chip, offset); + if (retval) { + dev_err(bank->dev, "unable to lock offset %d for IRQ\n", + offset); + spin_unlock_irqrestore(&bank->lock, flags); + return retval; + } + bank->irq_usage |= 1 << GPIO_INDEX(bank, gpio); spin_unlock_irqrestore(&bank->lock, flags); @@ -797,6 +805,7 @@ static void gpio_irq_shutdown(struct irq_data *d) unsigned offset = GPIO_INDEX(bank, gpio); spin_lock_irqsave(&bank->lock, flags); + gpio_unlock_as_irq(&bank->chip, offset); bank->irq_usage &= ~(1 << offset); _disable_gpio_module(bank, offset); _reset_gpio(bank, gpio); @@ -957,22 +966,13 @@ static int gpio_output(struct gpio_chip *chip, unsigned offset, int value) { struct gpio_bank *bank; unsigned long flags; - int retval = 0; bank = container_of(chip, struct gpio_bank, chip); spin_lock_irqsave(&bank->lock, flags); - - if (LINE_USED(bank->irq_usage, offset)) { - retval = -EINVAL; - goto exit; - } - bank->set_dataout(bank, offset, value); _set_gpio_direction(bank, offset, 0); - -exit: spin_unlock_irqrestore(&bank->lock, flags); - return retval; + return 0; } static int gpio_debounce(struct gpio_chip *chip, unsigned offset, -- cgit v0.10.2 From 831cbd7a807b3f62f58fe98e0283af510b567d9b Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 16 Oct 2013 15:35:01 +0530 Subject: gpio: lpc32xx: Include linux/of.h header 'of_match_ptr' is defined in linux/of.h. Include it explicitly to avoid build breakage in the future. Signed-off-by: Sachin Kamat Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c index 90a80eb..2d5555d 100644 --- a/drivers/gpio/gpio-lpc32xx.c +++ b/drivers/gpio/gpio-lpc32xx.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include -- cgit v0.10.2 From bd0bf46844ad79c1360eebc73bf6f1e4c31b39fb Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 16 Oct 2013 15:35:02 +0530 Subject: gpio: rcar: Include linux/of.h header 'of_match_ptr' is defined in linux/of.h. Include it explicitly to avoid build breakage in the future. Signed-off-by: Sachin Kamat Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index be3e9c2..d3f15ae 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include -- cgit v0.10.2 From de0022d4bf3a5acaef75cf1c7c2f8d71b020e8c9 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 16 Oct 2013 15:58:48 +0530 Subject: ASoC: smdk_wm8994: Add .pm to struct smdk_audio_driver Register PM ops for this driver. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c index 831972d..b072bd1 100644 --- a/sound/soc/samsung/smdk_wm8994.c +++ b/sound/soc/samsung/smdk_wm8994.c @@ -9,6 +9,7 @@ #include "../codecs/wm8994.h" #include +#include #include #include #include @@ -206,6 +207,7 @@ static struct platform_driver smdk_audio_driver = { .name = "smdk-audio-wm8894", .owner = THIS_MODULE, .of_match_table = of_match_ptr(samsung_wm8994_of_match), + .pm = &snd_soc_pm_ops, }, .probe = smdk_audio_probe, }; -- cgit v0.10.2 From 7c3f2ab7b844f1a859afbc3d41925e8a0faba5fa Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 15 Oct 2013 12:35:07 +0200 Subject: sched/rt: Add missing rmb() While discussing the proposed SCHED_DEADLINE patches which in parts mimic the existing FIFO code it was noticed that the wmb in rt_set_overloaded() didn't have a matching barrier. The only site using rt_overloaded() to test the rto_count is pull_rt_task() and we should issue a matching rmb before then assuming there's an rto_mask bit set. Without that smp_rmb() in there we could actually miss seeing the rto_mask bit. Also, change to using smp_[wr]mb(), even though this is SMP only code; memory barriers without smp_ always make me think they're against hardware of some sort. Signed-off-by: Peter Zijlstra Cc: vincent.guittot@linaro.org Cc: luca.abeni@unitn.it Cc: bruce.ashfield@windriver.com Cc: dhaval.giani@gmail.com Cc: rostedt@goodmis.org Cc: hgu1972@gmail.com Cc: oleg@redhat.com Cc: fweisbec@gmail.com Cc: darren@dvhart.com Cc: johan.eker@ericsson.com Cc: p.faure@akatech.ch Cc: paulmck@linux.vnet.ibm.com Cc: raistlin@linux.it Cc: claudio@evidence.eu.com Cc: insop.song@gmail.com Cc: michael@amarulasolutions.com Cc: liming.wang@windriver.com Cc: fchecconi@gmail.com Cc: jkacur@redhat.com Cc: tommaso.cucinotta@sssup.it Cc: Juri Lelli Cc: harald.gustafsson@ericsson.com Cc: nicola.manica@disi.unitn.it Cc: tglx@linutronix.de Link: http://lkml.kernel.org/r/20131015103507.GF10651@twins.programming.kicks-ass.net Signed-off-by: Ingo Molnar diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index e9304cd..a848f52 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -246,8 +246,10 @@ static inline void rt_set_overload(struct rq *rq) * if we should look at the mask. It would be a shame * if we looked at the mask, but the mask was not * updated yet. + * + * Matched by the barrier in pull_rt_task(). */ - wmb(); + smp_wmb(); atomic_inc(&rq->rd->rto_count); } @@ -1626,6 +1628,12 @@ static int pull_rt_task(struct rq *this_rq) if (likely(!rt_overloaded(this_rq))) return 0; + /* + * Match the barrier from rt_set_overloaded; this guarantees that if we + * see overloaded we must also see the rto_mask bit. + */ + smp_rmb(); + for_each_cpu(cpu, this_rq->rd->rto_mask) { if (this_cpu == cpu) continue; -- cgit v0.10.2 From 746023159c40c523b08a3bc3d213dac212385895 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 10 Oct 2013 20:17:22 +0200 Subject: sched: Fix race in migrate_swap_stop() There is a subtle race in migrate_swap, when task P, on CPU A, decides to swap places with task T, on CPU B. Task P: - call migrate_swap Task T: - go to sleep, removing itself from the runqueue Task P: - double lock the runqueues on CPU A & B Task T: - get woken up, place itself on the runqueue of CPU C Task P: - see that task T is on a runqueue, and pretend to remove it from the runqueue on CPU B Now CPUs B & C both have corrupted scheduler data structures. This patch fixes it, by holding the pi_lock for both of the tasks involved in the migrate swap. This prevents task T from waking up, and placing itself onto another runqueue, until after migrate_swap has released all locks. This means that, when migrate_swap checks, task T will be either on the runqueue where it was originally seen, or not on any runqueue at all. Migrate_swap deals correctly with of those cases. Tested-by: Joe Mario Acked-by: Mel Gorman Reviewed-by: Rik van Riel Signed-off-by: Peter Zijlstra Cc: hannes@cmpxchg.org Cc: aarcange@redhat.com Cc: srikar@linux.vnet.ibm.com Cc: tglx@linutronix.de Cc: hpa@zytor.com Link: http://lkml.kernel.org/r/20131010181722.GO13848@laptop.programming.kicks-ass.net Signed-off-by: Ingo Molnar diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 0c3feeb..a972acd 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -1049,6 +1049,8 @@ static int migrate_swap_stop(void *data) src_rq = cpu_rq(arg->src_cpu); dst_rq = cpu_rq(arg->dst_cpu); + double_raw_lock(&arg->src_task->pi_lock, + &arg->dst_task->pi_lock); double_rq_lock(src_rq, dst_rq); if (task_cpu(arg->dst_task) != arg->dst_cpu) goto unlock; @@ -1069,6 +1071,8 @@ static int migrate_swap_stop(void *data) unlock: double_rq_unlock(src_rq, dst_rq); + raw_spin_unlock(&arg->dst_task->pi_lock); + raw_spin_unlock(&arg->src_task->pi_lock); return ret; } diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 4aa0b10..813dd61 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1448,15 +1448,6 @@ static inline void put_numa_group(struct numa_group *grp) kfree_rcu(grp, rcu); } -static void double_lock(spinlock_t *l1, spinlock_t *l2) -{ - if (l1 > l2) - swap(l1, l2); - - spin_lock(l1); - spin_lock_nested(l2, SINGLE_DEPTH_NESTING); -} - static void task_numa_group(struct task_struct *p, int cpupid, int flags, int *priv) { diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index d69cb32..ffc7087 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1249,6 +1249,24 @@ static inline void double_unlock_balance(struct rq *this_rq, struct rq *busiest) lock_set_subclass(&this_rq->lock.dep_map, 0, _RET_IP_); } +static inline void double_lock(spinlock_t *l1, spinlock_t *l2) +{ + if (l1 > l2) + swap(l1, l2); + + spin_lock(l1); + spin_lock_nested(l2, SINGLE_DEPTH_NESTING); +} + +static inline void double_raw_lock(raw_spinlock_t *l1, raw_spinlock_t *l2) +{ + if (l1 > l2) + swap(l1, l2); + + raw_spin_lock(l1); + raw_spin_lock_nested(l2, SINGLE_DEPTH_NESTING); +} + /* * double_rq_lock - safely lock two runqueues * -- cgit v0.10.2 From 6acce3ef84520537f8a09a12c9ddbe814a584dd2 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 11 Oct 2013 14:38:20 +0200 Subject: sched: Remove get_online_cpus() usage Remove get_online_cpus() usage from the scheduler; there's 4 sites that use it: - sched_init_smp(); where its completely superfluous since we're in 'early' boot and there simply cannot be any hotplugging. - sched_getaffinity(); we already take a raw spinlock to protect the task cpus_allowed mask, this disables preemption and therefore also stabilizes cpu_online_mask as that's modified using stop_machine. However switch to active mask for symmetry with sched_setaffinity()/set_cpus_allowed_ptr(). We guarantee active mask stability by inserting sync_rcu/sched() into _cpu_down. - sched_setaffinity(); we don't appear to need get_online_cpus() either, there's two sites where hotplug appears relevant: * cpuset_cpus_allowed(); for the !cpuset case we use possible_mask, for the cpuset case we hold task_lock, which is a spinlock and thus for mainline disables preemption (might cause pain on RT). * set_cpus_allowed_ptr(); Holds all scheduler locks and thus has preemption properly disabled; also it already deals with hotplug races explicitly where it releases them. - migrate_swap(); we can make stop_two_cpus() do the heavy lifting for us with a little trickery. By adding a sync_sched/rcu() after the CPU_DOWN_PREPARE notifier we can provide preempt/rcu guarantees for cpu_active_mask. Use these to validate that both our cpus are active when queueing the stop work before we queue the stop_machine works for take_cpu_down(). Signed-off-by: Peter Zijlstra Cc: "Srivatsa S. Bhat" Cc: Paul McKenney Cc: Mel Gorman Cc: Rik van Riel Cc: Srikar Dronamraju Cc: Andrea Arcangeli Cc: Johannes Weiner Cc: Linus Torvalds Cc: Andrew Morton Cc: Steven Rostedt Cc: Oleg Nesterov Link: http://lkml.kernel.org/r/20131011123820.GV3081@twins.programming.kicks-ass.net Signed-off-by: Ingo Molnar diff --git a/kernel/cpu.c b/kernel/cpu.c index d7f07a2..63aa50d 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -308,6 +308,23 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen) } smpboot_park_threads(cpu); + /* + * By now we've cleared cpu_active_mask, wait for all preempt-disabled + * and RCU users of this state to go away such that all new such users + * will observe it. + * + * For CONFIG_PREEMPT we have preemptible RCU and its sync_rcu() might + * not imply sync_sched(), so explicitly call both. + */ +#ifdef CONFIG_PREEMPT + synchronize_sched(); +#endif + synchronize_rcu(); + + /* + * So now all preempt/rcu users must observe !cpu_active(). + */ + err = __stop_machine(take_cpu_down, &tcd_param, cpumask_of(cpu)); if (err) { /* CPU didn't die: tell everyone. Can't complain. */ diff --git a/kernel/sched/core.c b/kernel/sched/core.c index a972acd..c06b8d3 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -1085,8 +1085,6 @@ int migrate_swap(struct task_struct *cur, struct task_struct *p) struct migration_swap_arg arg; int ret = -EINVAL; - get_online_cpus(); - arg = (struct migration_swap_arg){ .src_task = cur, .src_cpu = task_cpu(cur), @@ -1097,6 +1095,10 @@ int migrate_swap(struct task_struct *cur, struct task_struct *p) if (arg.src_cpu == arg.dst_cpu) goto out; + /* + * These three tests are all lockless; this is OK since all of them + * will be re-checked with proper locks held further down the line. + */ if (!cpu_active(arg.src_cpu) || !cpu_active(arg.dst_cpu)) goto out; @@ -1109,7 +1111,6 @@ int migrate_swap(struct task_struct *cur, struct task_struct *p) ret = stop_two_cpus(arg.dst_cpu, arg.src_cpu, migrate_swap_stop, &arg); out: - put_online_cpus(); return ret; } @@ -3710,7 +3711,6 @@ long sched_setaffinity(pid_t pid, const struct cpumask *in_mask) struct task_struct *p; int retval; - get_online_cpus(); rcu_read_lock(); p = find_process_by_pid(pid); @@ -3773,7 +3773,6 @@ out_free_cpus_allowed: free_cpumask_var(cpus_allowed); out_put_task: put_task_struct(p); - put_online_cpus(); return retval; } @@ -3818,7 +3817,6 @@ long sched_getaffinity(pid_t pid, struct cpumask *mask) unsigned long flags; int retval; - get_online_cpus(); rcu_read_lock(); retval = -ESRCH; @@ -3831,12 +3829,11 @@ long sched_getaffinity(pid_t pid, struct cpumask *mask) goto out_unlock; raw_spin_lock_irqsave(&p->pi_lock, flags); - cpumask_and(mask, &p->cpus_allowed, cpu_online_mask); + cpumask_and(mask, &p->cpus_allowed, cpu_active_mask); raw_spin_unlock_irqrestore(&p->pi_lock, flags); out_unlock: rcu_read_unlock(); - put_online_cpus(); return retval; } @@ -6494,14 +6491,17 @@ void __init sched_init_smp(void) sched_init_numa(); - get_online_cpus(); + /* + * There's no userspace yet to cause hotplug operations; hence all the + * cpu masks are stable and all blatant races in the below code cannot + * happen. + */ mutex_lock(&sched_domains_mutex); init_sched_domains(cpu_active_mask); cpumask_andnot(non_isolated_cpus, cpu_possible_mask, cpu_isolated_map); if (cpumask_empty(non_isolated_cpus)) cpumask_set_cpu(smp_processor_id(), non_isolated_cpus); mutex_unlock(&sched_domains_mutex); - put_online_cpus(); hotcpu_notifier(sched_domains_numa_masks_update, CPU_PRI_SCHED_ACTIVE); hotcpu_notifier(cpuset_cpu_active, CPU_PRI_CPUSET_ACTIVE); diff --git a/kernel/stop_machine.c b/kernel/stop_machine.c index 32a6c44..c530bc5 100644 --- a/kernel/stop_machine.c +++ b/kernel/stop_machine.c @@ -234,11 +234,13 @@ static void irq_cpu_stop_queue_work(void *arg) */ int stop_two_cpus(unsigned int cpu1, unsigned int cpu2, cpu_stop_fn_t fn, void *arg) { - int call_cpu; struct cpu_stop_done done; struct cpu_stop_work work1, work2; struct irq_cpu_stop_queue_work_info call_args; - struct multi_stop_data msdata = { + struct multi_stop_data msdata; + + preempt_disable(); + msdata = (struct multi_stop_data){ .fn = fn, .data = arg, .num_threads = 2, @@ -262,16 +264,30 @@ int stop_two_cpus(unsigned int cpu1, unsigned int cpu2, cpu_stop_fn_t fn, void * set_state(&msdata, MULTI_STOP_PREPARE); /* + * If we observe both CPUs active we know _cpu_down() cannot yet have + * queued its stop_machine works and therefore ours will get executed + * first. Or its not either one of our CPUs that's getting unplugged, + * in which case we don't care. + * + * This relies on the stopper workqueues to be FIFO. + */ + if (!cpu_active(cpu1) || !cpu_active(cpu2)) { + preempt_enable(); + return -ENOENT; + } + + /* * Queuing needs to be done by the lowest numbered CPU, to ensure * that works are always queued in the same order on every CPU. * This prevents deadlocks. */ - call_cpu = min(cpu1, cpu2); - - smp_call_function_single(call_cpu, &irq_cpu_stop_queue_work, + smp_call_function_single(min(cpu1, cpu2), + &irq_cpu_stop_queue_work, &call_args, 0); + preempt_enable(); wait_for_completion(&done.completion); + return done.executed ? done.ret : -ENOENT; } -- cgit v0.10.2 From 8922915b38cd8b72f8e5af614b95be71d1d299d4 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Mon, 7 Oct 2013 20:31:06 +0200 Subject: sched/wait: Add ___wait_cond_timeout() to wait_event*_timeout() too Commit 4c663cfc ("wait: fix false timeouts when using wait_event_timeout()") introduced the additional condition checks after a timeout but only in the "slow" __wait*() paths. wait_event_timeout(wq, CONDITION, 0) still returns 0 if CONDITION is already true and we do not call __wait*(). Now that we have ___wait_cond_timeout() we can use it instead to ensure that __ret will be properly updated. Signed-off-by: Oleg Nesterov Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20131007183106.GA10973@redhat.com Signed-off-by: Ingo Molnar diff --git a/include/linux/wait.h b/include/linux/wait.h index a2726c7..04c0260 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -270,7 +270,7 @@ do { \ #define wait_event_timeout(wq, condition, timeout) \ ({ \ long __ret = timeout; \ - if (!(condition)) \ + if (!___wait_cond_timeout(condition)) \ __ret = __wait_event_timeout(wq, condition, timeout); \ __ret; \ }) @@ -328,7 +328,7 @@ do { \ #define wait_event_interruptible_timeout(wq, condition, timeout) \ ({ \ long __ret = timeout; \ - if (!(condition)) \ + if (!___wait_cond_timeout(condition)) \ __ret = __wait_event_interruptible_timeout(wq, \ condition, timeout); \ __ret; \ @@ -769,7 +769,7 @@ do { \ timeout) \ ({ \ long __ret = timeout; \ - if (!(condition)) \ + if (!___wait_cond_timeout(condition)) \ __ret = __wait_event_interruptible_lock_irq_timeout( \ wq, condition, lock, timeout); \ __ret; \ -- cgit v0.10.2 From c2d816443ef305aba8eaf0bf368f4d3d87494f06 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Mon, 7 Oct 2013 18:18:24 +0200 Subject: sched/wait: Introduce prepare_to_wait_event() Add the new helper, prepare_to_wait_event() which should only be used by ___wait_event(). prepare_to_wait_event() returns -ERESTARTSYS if signal_pending_state() is true, otherwise it does prepare_to_wait/exclusive. This allows to uninline the signal-pending checks in wait_event*() macros. Also, it can initialize wait->private/func. We do not care if they were already initialized, the values are the same. This also shaves a couple of insns from the inlined code. This obviously makes prepare_*() path a little bit slower, but we are likely going to sleep anyway, so I think it makes sense to shrink .text: text data bss dec hex filename =================================================== before: 5126092 2959248 10117120 18202460 115bf5c vmlinux after: 5124618 2955152 10117120 18196890 115a99a vmlinux on my build. Signed-off-by: Oleg Nesterov Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20131007161824.GA29757@redhat.com Signed-off-by: Ingo Molnar diff --git a/include/linux/wait.h b/include/linux/wait.h index 04c0260..ec099b0 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -187,27 +187,30 @@ wait_queue_head_t *bit_waitqueue(void *, int); __cond || !__ret; \ }) -#define ___wait_signal_pending(state) \ - ((state == TASK_INTERRUPTIBLE && signal_pending(current)) || \ - (state == TASK_KILLABLE && fatal_signal_pending(current))) +#define ___wait_is_interruptible(state) \ + (!__builtin_constant_p(state) || \ + state == TASK_INTERRUPTIBLE || state == TASK_KILLABLE) \ #define ___wait_event(wq, condition, state, exclusive, ret, cmd) \ ({ \ __label__ __out; \ - DEFINE_WAIT(__wait); \ + wait_queue_t __wait; \ long __ret = ret; \ \ + INIT_LIST_HEAD(&__wait.task_list); \ + if (exclusive) \ + __wait.flags = WQ_FLAG_EXCLUSIVE; \ + else \ + __wait.flags = 0; \ + \ for (;;) { \ - if (exclusive) \ - prepare_to_wait_exclusive(&wq, &__wait, state); \ - else \ - prepare_to_wait(&wq, &__wait, state); \ + long __int = prepare_to_wait_event(&wq, &__wait, state);\ \ if (condition) \ break; \ \ - if (___wait_signal_pending(state)) { \ - __ret = -ERESTARTSYS; \ + if (___wait_is_interruptible(state) && __int) { \ + __ret = __int; \ if (exclusive) { \ abort_exclusive_wait(&wq, &__wait, \ state, NULL); \ @@ -791,6 +794,7 @@ extern long interruptible_sleep_on_timeout(wait_queue_head_t *q, signed long tim */ void prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state); void prepare_to_wait_exclusive(wait_queue_head_t *q, wait_queue_t *wait, int state); +long prepare_to_wait_event(wait_queue_head_t *q, wait_queue_t *wait, int state); void finish_wait(wait_queue_head_t *q, wait_queue_t *wait); void abort_exclusive_wait(wait_queue_head_t *q, wait_queue_t *wait, unsigned int mode, void *key); int autoremove_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key); diff --git a/kernel/wait.c b/kernel/wait.c index d550920..de21c63 100644 --- a/kernel/wait.c +++ b/kernel/wait.c @@ -92,6 +92,30 @@ prepare_to_wait_exclusive(wait_queue_head_t *q, wait_queue_t *wait, int state) } EXPORT_SYMBOL(prepare_to_wait_exclusive); +long prepare_to_wait_event(wait_queue_head_t *q, wait_queue_t *wait, int state) +{ + unsigned long flags; + + if (signal_pending_state(state, current)) + return -ERESTARTSYS; + + wait->private = current; + wait->func = autoremove_wake_function; + + spin_lock_irqsave(&q->lock, flags); + if (list_empty(&wait->task_list)) { + if (wait->flags & WQ_FLAG_EXCLUSIVE) + __add_wait_queue_tail(q, wait); + else + __add_wait_queue(q, wait); + } + set_current_state(state); + spin_unlock_irqrestore(&q->lock, flags); + + return 0; +} +EXPORT_SYMBOL(prepare_to_wait_event); + /** * finish_wait - clean up after waiting in a queue * @q: waitqueue waited on -- cgit v0.10.2 From 586a87e6edc936d6d3c3585af504b33b9c3f0a06 Mon Sep 17 00:00:00 2001 From: Christian Ruppert Date: Tue, 15 Oct 2013 15:37:54 +0200 Subject: pinctrl/gpio: non-linear GPIO ranges accesible from gpiolib This patch adds the infrastructure required to register non-linear gpio ranges through gpiolib and the standard GPIO device tree bindings. Signed-off-by: Christian Ruppert Signed-off-by: Linus Walleij diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt index 6cec6ff..0c85bb6 100644 --- a/Documentation/devicetree/bindings/gpio/gpio.txt +++ b/Documentation/devicetree/bindings/gpio/gpio.txt @@ -87,8 +87,10 @@ controllers. The gpio-ranges property described below represents this, and contains information structures as follows: gpio-range-list ::= [gpio-range-list] - single-gpio-range ::= + single-gpio-range ::= | + numeric-gpio-range ::= + named-gpio-range ::= '<0 0>' gpio-phandle : phandle to pin controller node. gpio-base : Base GPIO ID in the GPIO controller pinctrl-base : Base pinctrl pin ID in the pin controller @@ -97,6 +99,19 @@ contains information structures as follows: The "pin controller node" mentioned above must conform to the bindings described in ../pinctrl/pinctrl-bindings.txt. +In case named gpio ranges are used (ranges with both and + set to 0), the property gpio-ranges-group-names contains one string +for every single-gpio-range in gpio-ranges: + gpiorange-names-list ::= [gpiorange-names-list] + gpiorange-name : Name of the pingroup associated to the GPIO range in + the respective pin controller. + +Elements of gpiorange-names-list corresponding to numeric ranges contain +the empty string. Elements of gpiorange-names-list corresponding to named +ranges contain the name of a pin group defined in the respective pin +controller. The number of pins/GPIOs in the range is the number of pins in +that pin group. + Previous versions of this binding required all pin controller nodes that were referenced by any gpio-ranges property to contain a property named #gpio-range-cells with value <3>. This requirement is now deprecated. @@ -104,7 +119,7 @@ However, that property may still exist in older device trees for compatibility reasons, and would still be required even in new device trees that need to be compatible with older software. -Example: +Example 1: qe_pio_e: gpio-controller@1460 { #gpio-cells = <2>; @@ -117,3 +132,24 @@ Example: Here, a single GPIO controller has GPIOs 0..9 routed to pin controller pinctrl1's pins 20..29, and GPIOs 10..19 routed to pin controller pinctrl2's pins 50..59. + +Example 2: + + gpio_pio_i: gpio-controller@14B0 { + #gpio-cells = <2>; + compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank"; + reg = <0x1480 0x18>; + gpio-controller; + gpio-ranges = <&pinctrl1 0 20 10>, + <&pinctrl2 10 0 0>, + <&pinctrl1 15 0 10>, + <&pinctrl2 25 0 0>; + gpio-ranges-group-names = "", + "foo", + "", + "bar"; + }; + +Here, three GPIO ranges are defined wrt. two pin controllers. pinctrl1 GPIO +ranges are defined using pin numbers whereas the GPIO ranges wrt. pinctrl2 +are named "foo" and "bar". diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 0dfaf20..e787609 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -190,10 +190,15 @@ static void of_gpiochip_add_pin_range(struct gpio_chip *chip) struct of_phandle_args pinspec; struct pinctrl_dev *pctldev; int index = 0, ret; + const char *name; + static const char group_names_propname[] = "gpio-ranges-group-names"; + struct property *group_names; if (!np) return; + group_names = of_find_property(np, group_names_propname, NULL); + for (;; index++) { ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, index, &pinspec); @@ -204,14 +209,56 @@ static void of_gpiochip_add_pin_range(struct gpio_chip *chip) if (!pctldev) break; - ret = gpiochip_add_pin_range(chip, - pinctrl_dev_get_devname(pctldev), - pinspec.args[0], - pinspec.args[1], - pinspec.args[2]); - - if (ret) - break; + if (pinspec.args[2]) { + if (group_names) { + ret = of_property_read_string_index(np, + group_names_propname, + index, &name); + if (strlen(name)) { + pr_err("%s: Group name of numeric GPIO ranges must be the empty string.\n", + np->full_name); + break; + } + } + /* npins != 0: linear range */ + ret = gpiochip_add_pin_range(chip, + pinctrl_dev_get_devname(pctldev), + pinspec.args[0], + pinspec.args[1], + pinspec.args[2]); + if (ret) + break; + } else { + /* npins == 0: special range */ + if (pinspec.args[1]) { + pr_err("%s: Illegal gpio-range format.\n", + np->full_name); + break; + } + + if (!group_names) { + pr_err("%s: GPIO group range requested but no %s property.\n", + np->full_name, group_names_propname); + break; + } + + ret = of_property_read_string_index(np, + group_names_propname, + index, &name); + if (ret) + break; + + if (!strlen(name)) { + pr_err("%s: Group name of GPIO group range cannot be the empty string.\n", + np->full_name); + break; + } + + ret = gpiochip_add_pingroup_range(chip, pctldev, + pinspec.args[0], name); + if (ret) + break; + } } } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 86ef346..b83b7e4 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1320,6 +1320,53 @@ EXPORT_SYMBOL_GPL(gpiochip_find); #ifdef CONFIG_PINCTRL /** + * gpiochip_add_pingroup_range() - add a range for GPIO <-> pin mapping + * @chip: the gpiochip to add the range for + * @pinctrl: the dev_name() of the pin controller to map to + * @gpio_offset: the start offset in the current gpio_chip number space + * @pin_group: name of the pin group inside the pin controller + */ +int gpiochip_add_pingroup_range(struct gpio_chip *chip, + struct pinctrl_dev *pctldev, + unsigned int gpio_offset, const char *pin_group) +{ + struct gpio_pin_range *pin_range; + int ret; + + pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL); + if (!pin_range) { + pr_err("%s: GPIO chip: failed to allocate pin ranges\n", + chip->label); + return -ENOMEM; + } + + /* Use local offset as range ID */ + pin_range->range.id = gpio_offset; + pin_range->range.gc = chip; + pin_range->range.name = chip->label; + pin_range->range.base = chip->base + gpio_offset; + pin_range->pctldev = pctldev; + + ret = pinctrl_get_group_pins(pctldev, pin_group, + &pin_range->range.pins, + &pin_range->range.npins); + if (ret < 0) + return ret; + + pinctrl_add_gpio_range(pctldev, &pin_range->range); + + pr_debug("GPIO chip %s: created GPIO range %d->%d ==> %s PINGRP %s\n", + chip->label, gpio_offset, + gpio_offset + pin_range->range.npins - 1, + pinctrl_dev_get_devname(pctldev), pin_group); + + list_add_tail(&pin_range->node, &chip->pin_ranges); + + return 0; +} +EXPORT_SYMBOL_GPL(gpiochip_add_pingroup_range); + +/** * gpiochip_add_pin_range() - add a range for GPIO <-> pin mapping * @chip: the gpiochip to add the range for * @pinctrl_name: the dev_name() of the pin controller to map to diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 92f86ab..5ee61a4 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -462,6 +462,20 @@ struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname, } EXPORT_SYMBOL_GPL(pinctrl_find_and_add_gpio_range); +int pinctrl_get_group_pins(struct pinctrl_dev *pctldev, const char *pin_group, + const unsigned **pins, unsigned *num_pins) +{ + const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; + int gs; + + gs = pinctrl_get_group_selector(pctldev, pin_group); + if (gs < 0) + return gs; + + return pctlops->get_group_pins(pctldev, gs, pins, num_pins); +} +EXPORT_SYMBOL_GPL(pinctrl_get_group_pins); + /** * pinctrl_find_gpio_range_from_pin() - locate the GPIO range for a pin * @pctldev: the pin controller device to look in diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index bde6469..523f405 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -228,6 +228,9 @@ struct gpio_pin_range { int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, unsigned int gpio_offset, unsigned int pin_offset, unsigned int npins); +int gpiochip_add_pingroup_range(struct gpio_chip *chip, + struct pinctrl_dev *pctldev, + unsigned int gpio_offset, const char *pin_group); void gpiochip_remove_pin_ranges(struct gpio_chip *chip); #else @@ -239,6 +242,13 @@ gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, { return 0; } +static inline int +gpiochip_add_pingroup_range(struct gpio_chip *chip, + struct pinctrl_dev *pctldev, + unsigned int gpio_offset, const char *pin_group) +{ + return 0; +} static inline void gpiochip_remove_pin_ranges(struct gpio_chip *chip) diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 552e3f4..b8d0e53 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -80,6 +80,7 @@ static inline int irq_to_gpio(unsigned int irq) #include #include #include +#include struct device; struct gpio_chip; @@ -220,6 +221,15 @@ gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, return -EINVAL; } +static inline int +gpiochip_add_pingroup_range(struct gpio_chip *chip, + struct pinctrl_dev *pctldev, + unsigned int gpio_offset, const char *pin_group) +{ + WARN_ON(1); + return -EINVAL; +} + static inline void gpiochip_remove_pin_ranges(struct gpio_chip *chip) { diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h index 5979147..fefb886 100644 --- a/include/linux/pinctrl/pinctrl.h +++ b/include/linux/pinctrl/pinctrl.h @@ -144,6 +144,9 @@ extern struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname, extern struct pinctrl_gpio_range * pinctrl_find_gpio_range_from_pin(struct pinctrl_dev *pctldev, unsigned int pin); +extern int pinctrl_get_group_pins(struct pinctrl_dev *pctldev, + const char *pin_group, const unsigned **pins, + unsigned *num_pins); #ifdef CONFIG_OF extern struct pinctrl_dev *of_pinctrl_get(struct device_node *np); -- cgit v0.10.2 From 5aad0db1c1ebb0f5be79f0adbecc16a2f0259b21 Mon Sep 17 00:00:00 2001 From: Christian Ruppert Date: Tue, 15 Oct 2013 15:39:38 +0200 Subject: pinctrl: add TB10x pin control driver The pinmux driver of the Abilis Systems TB10x platform based on ARC700 CPUs. Used to control the pinmux and is a prerequisite for the GPIO driver. Signed-off-by: Christian Ruppert Signed-off-by: Pierrick Hascoet Signed-off-by: Linus Walleij diff --git a/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt b/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt new file mode 100644 index 0000000..2c11866 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt @@ -0,0 +1,80 @@ +Abilis Systems TB10x pin controller +=================================== + +Required properties +------------------- + +- compatible: should be "abilis,tb10x-iomux"; +- reg: should contain the physical address and size of the pin controller's + register range. + + +Function definitions +-------------------- + +Functions are defined (and referenced) by sub-nodes of the pin controller. +Every sub-node defines exactly one function (implying a set of pins). +Every function is associated to one named pin group inside the pin controller +driver and these names are used to associate pin group predefinitions to pin +controller sub-nodes. + +Required function definition subnode properties: + - abilis,function: should be set to the name of the function's pin group. + +The following pin groups are available: + - GPIO ports: gpioa, gpiob, gpioc, gpiod, gpioe, gpiof, gpiog, + gpioh, gpioi, gpioj, gpiok, gpiol, gpiom, gpion + - Serial TS input ports: mis0, mis1, mis2, mis3, mis4, mis5, mis6, mis7 + - Parallel TS input ports: mip1, mip3, mip5, mip7 + - Serial TS output ports: mos0, mos1, mos2, mos3 + - Parallel TS output port: mop + - CI+ port: ciplus + - CableCard (Mcard) port: mcard + - Smart card ports: stc0, stc1 + - UART ports: uart0, uart1 + - SPI ports: spi1, spi3 + - JTAG: jtag + +All other ports of the chip are not multiplexed and thus not managed by this +driver. + + +GPIO ranges definition +---------------------- + +The named pin groups of GPIO ports can be used to define GPIO ranges as +explained in Documentation/devicetree/bindings/gpio/gpio.txt. + + +Example +------- + +iomux: iomux@FF10601c { + compatible = "abilis,tb10x-iomux"; + reg = <0xFF10601c 0x4>; + pctl_gpio_a: pctl-gpio-a { + abilis,function = "gpioa"; + }; + pctl_uart0: pctl-uart0 { + abilis,function = "uart0"; + }; +}; +uart@FF100000 { + compatible = "snps,dw-apb-uart"; + reg = <0xFF100000 0x100>; + clock-frequency = <166666666>; + interrupts = <25 1>; + reg-shift = <2>; + reg-io-width = <4>; + pinctrl-names = "default"; + pinctrl-0 = <&pctl_uart0>; +}; +gpioa: gpio@FF140000 { + compatible = "abilis,tb10x-gpio"; + reg = <0xFF140000 0x1000>; + gpio-controller; + #gpio-cells = <2>; + ngpio = <3>; + gpio-ranges = <&iomux 0 0>; + gpio-ranges-group-names = "gpioa"; +}; diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 85f5462..e283c49 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -321,6 +321,10 @@ config PINCTRL_XWAY depends on SOC_TYPE_XWAY depends on PINCTRL_LANTIQ +config PINCTRL_TB10X + bool + depends on ARC_PLAT_TB10X + endmenu endif diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 3d14cb8..8476cd9 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_PINCTRL_S3C24XX) += pinctrl-s3c24xx.o obj-$(CONFIG_PINCTRL_S3C64XX) += pinctrl-s3c64xx.o obj-$(CONFIG_PINCTRL_XWAY) += pinctrl-xway.o obj-$(CONFIG_PINCTRL_LANTIQ) += pinctrl-lantiq.o +obj-$(CONFIG_PINCTRL_TB10X) += pinctrl-tb10x.o obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o obj-$(CONFIG_PINCTRL_VF610) += pinctrl-vf610.o diff --git a/drivers/pinctrl/pinctrl-tb10x.c b/drivers/pinctrl/pinctrl-tb10x.c new file mode 100644 index 0000000..2e1ea56 --- /dev/null +++ b/drivers/pinctrl/pinctrl-tb10x.c @@ -0,0 +1,886 @@ +/* + * Abilis Systems TB10x pin control driver + * + * Copyright (C) Abilis Systems 2012 + * + * Author: Christian Ruppert + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pinctrl-utils.h" + +#define TB10X_PORT1 (0) +#define TB10X_PORT2 (16) +#define TB10X_PORT3 (32) +#define TB10X_PORT4 (48) +#define TB10X_PORT5 (128) +#define TB10X_PORT6 (64) +#define TB10X_PORT7 (80) +#define TB10X_PORT8 (96) +#define TB10X_PORT9 (112) +#define TB10X_GPIOS (256) + +#define PCFG_PORT_BITWIDTH (2) +#define PCFG_PORT_MASK(PORT) \ + (((1 << PCFG_PORT_BITWIDTH) - 1) << (PCFG_PORT_BITWIDTH * (PORT))) + +static const struct pinctrl_pin_desc tb10x_pins[] = { + /* Port 1 */ + PINCTRL_PIN(TB10X_PORT1 + 0, "MICLK_S0"), + PINCTRL_PIN(TB10X_PORT1 + 1, "MISTRT_S0"), + PINCTRL_PIN(TB10X_PORT1 + 2, "MIVAL_S0"), + PINCTRL_PIN(TB10X_PORT1 + 3, "MDI_S0"), + PINCTRL_PIN(TB10X_PORT1 + 4, "GPIOA0"), + PINCTRL_PIN(TB10X_PORT1 + 5, "GPIOA1"), + PINCTRL_PIN(TB10X_PORT1 + 6, "GPIOA2"), + PINCTRL_PIN(TB10X_PORT1 + 7, "MDI_S1"), + PINCTRL_PIN(TB10X_PORT1 + 8, "MIVAL_S1"), + PINCTRL_PIN(TB10X_PORT1 + 9, "MISTRT_S1"), + PINCTRL_PIN(TB10X_PORT1 + 10, "MICLK_S1"), + /* Port 2 */ + PINCTRL_PIN(TB10X_PORT2 + 0, "MICLK_S2"), + PINCTRL_PIN(TB10X_PORT2 + 1, "MISTRT_S2"), + PINCTRL_PIN(TB10X_PORT2 + 2, "MIVAL_S2"), + PINCTRL_PIN(TB10X_PORT2 + 3, "MDI_S2"), + PINCTRL_PIN(TB10X_PORT2 + 4, "GPIOC0"), + PINCTRL_PIN(TB10X_PORT2 + 5, "GPIOC1"), + PINCTRL_PIN(TB10X_PORT2 + 6, "GPIOC2"), + PINCTRL_PIN(TB10X_PORT2 + 7, "MDI_S3"), + PINCTRL_PIN(TB10X_PORT2 + 8, "MIVAL_S3"), + PINCTRL_PIN(TB10X_PORT2 + 9, "MISTRT_S3"), + PINCTRL_PIN(TB10X_PORT2 + 10, "MICLK_S3"), + /* Port 3 */ + PINCTRL_PIN(TB10X_PORT3 + 0, "MICLK_S4"), + PINCTRL_PIN(TB10X_PORT3 + 1, "MISTRT_S4"), + PINCTRL_PIN(TB10X_PORT3 + 2, "MIVAL_S4"), + PINCTRL_PIN(TB10X_PORT3 + 3, "MDI_S4"), + PINCTRL_PIN(TB10X_PORT3 + 4, "GPIOE0"), + PINCTRL_PIN(TB10X_PORT3 + 5, "GPIOE1"), + PINCTRL_PIN(TB10X_PORT3 + 6, "GPIOE2"), + PINCTRL_PIN(TB10X_PORT3 + 7, "MDI_S5"), + PINCTRL_PIN(TB10X_PORT3 + 8, "MIVAL_S5"), + PINCTRL_PIN(TB10X_PORT3 + 9, "MISTRT_S5"), + PINCTRL_PIN(TB10X_PORT3 + 10, "MICLK_S5"), + /* Port 4 */ + PINCTRL_PIN(TB10X_PORT4 + 0, "MICLK_S6"), + PINCTRL_PIN(TB10X_PORT4 + 1, "MISTRT_S6"), + PINCTRL_PIN(TB10X_PORT4 + 2, "MIVAL_S6"), + PINCTRL_PIN(TB10X_PORT4 + 3, "MDI_S6"), + PINCTRL_PIN(TB10X_PORT4 + 4, "GPIOG0"), + PINCTRL_PIN(TB10X_PORT4 + 5, "GPIOG1"), + PINCTRL_PIN(TB10X_PORT4 + 6, "GPIOG2"), + PINCTRL_PIN(TB10X_PORT4 + 7, "MDI_S7"), + PINCTRL_PIN(TB10X_PORT4 + 8, "MIVAL_S7"), + PINCTRL_PIN(TB10X_PORT4 + 9, "MISTRT_S7"), + PINCTRL_PIN(TB10X_PORT4 + 10, "MICLK_S7"), + /* Port 5 */ + PINCTRL_PIN(TB10X_PORT5 + 0, "PC_CE1N"), + PINCTRL_PIN(TB10X_PORT5 + 1, "PC_CE2N"), + PINCTRL_PIN(TB10X_PORT5 + 2, "PC_REGN"), + PINCTRL_PIN(TB10X_PORT5 + 3, "PC_INPACKN"), + PINCTRL_PIN(TB10X_PORT5 + 4, "PC_OEN"), + PINCTRL_PIN(TB10X_PORT5 + 5, "PC_WEN"), + PINCTRL_PIN(TB10X_PORT5 + 6, "PC_IORDN"), + PINCTRL_PIN(TB10X_PORT5 + 7, "PC_IOWRN"), + PINCTRL_PIN(TB10X_PORT5 + 8, "PC_RDYIRQN"), + PINCTRL_PIN(TB10X_PORT5 + 9, "PC_WAITN"), + PINCTRL_PIN(TB10X_PORT5 + 10, "PC_A0"), + PINCTRL_PIN(TB10X_PORT5 + 11, "PC_A1"), + PINCTRL_PIN(TB10X_PORT5 + 12, "PC_A2"), + PINCTRL_PIN(TB10X_PORT5 + 13, "PC_A3"), + PINCTRL_PIN(TB10X_PORT5 + 14, "PC_A4"), + PINCTRL_PIN(TB10X_PORT5 + 15, "PC_A5"), + PINCTRL_PIN(TB10X_PORT5 + 16, "PC_A6"), + PINCTRL_PIN(TB10X_PORT5 + 17, "PC_A7"), + PINCTRL_PIN(TB10X_PORT5 + 18, "PC_A8"), + PINCTRL_PIN(TB10X_PORT5 + 19, "PC_A9"), + PINCTRL_PIN(TB10X_PORT5 + 20, "PC_A10"), + PINCTRL_PIN(TB10X_PORT5 + 21, "PC_A11"), + PINCTRL_PIN(TB10X_PORT5 + 22, "PC_A12"), + PINCTRL_PIN(TB10X_PORT5 + 23, "PC_A13"), + PINCTRL_PIN(TB10X_PORT5 + 24, "PC_A14"), + PINCTRL_PIN(TB10X_PORT5 + 25, "PC_D0"), + PINCTRL_PIN(TB10X_PORT5 + 26, "PC_D1"), + PINCTRL_PIN(TB10X_PORT5 + 27, "PC_D2"), + PINCTRL_PIN(TB10X_PORT5 + 28, "PC_D3"), + PINCTRL_PIN(TB10X_PORT5 + 29, "PC_D4"), + PINCTRL_PIN(TB10X_PORT5 + 30, "PC_D5"), + PINCTRL_PIN(TB10X_PORT5 + 31, "PC_D6"), + PINCTRL_PIN(TB10X_PORT5 + 32, "PC_D7"), + PINCTRL_PIN(TB10X_PORT5 + 33, "PC_MOSTRT"), + PINCTRL_PIN(TB10X_PORT5 + 34, "PC_MOVAL"), + PINCTRL_PIN(TB10X_PORT5 + 35, "PC_MDO0"), + PINCTRL_PIN(TB10X_PORT5 + 36, "PC_MDO1"), + PINCTRL_PIN(TB10X_PORT5 + 37, "PC_MDO2"), + PINCTRL_PIN(TB10X_PORT5 + 38, "PC_MDO3"), + PINCTRL_PIN(TB10X_PORT5 + 39, "PC_MDO4"), + PINCTRL_PIN(TB10X_PORT5 + 40, "PC_MDO5"), + PINCTRL_PIN(TB10X_PORT5 + 41, "PC_MDO6"), + PINCTRL_PIN(TB10X_PORT5 + 42, "PC_MDO7"), + PINCTRL_PIN(TB10X_PORT5 + 43, "PC_MISTRT"), + PINCTRL_PIN(TB10X_PORT5 + 44, "PC_MIVAL"), + PINCTRL_PIN(TB10X_PORT5 + 45, "PC_MDI0"), + PINCTRL_PIN(TB10X_PORT5 + 46, "PC_MDI1"), + PINCTRL_PIN(TB10X_PORT5 + 47, "PC_MDI2"), + PINCTRL_PIN(TB10X_PORT5 + 48, "PC_MDI3"), + PINCTRL_PIN(TB10X_PORT5 + 49, "PC_MDI4"), + PINCTRL_PIN(TB10X_PORT5 + 50, "PC_MDI5"), + PINCTRL_PIN(TB10X_PORT5 + 51, "PC_MDI6"), + PINCTRL_PIN(TB10X_PORT5 + 52, "PC_MDI7"), + PINCTRL_PIN(TB10X_PORT5 + 53, "PC_MICLK"), + /* Port 6 */ + PINCTRL_PIN(TB10X_PORT6 + 0, "T_MOSTRT_S0"), + PINCTRL_PIN(TB10X_PORT6 + 1, "T_MOVAL_S0"), + PINCTRL_PIN(TB10X_PORT6 + 2, "T_MDO_S0"), + PINCTRL_PIN(TB10X_PORT6 + 3, "T_MOSTRT_S1"), + PINCTRL_PIN(TB10X_PORT6 + 4, "T_MOVAL_S1"), + PINCTRL_PIN(TB10X_PORT6 + 5, "T_MDO_S1"), + PINCTRL_PIN(TB10X_PORT6 + 6, "T_MOSTRT_S2"), + PINCTRL_PIN(TB10X_PORT6 + 7, "T_MOVAL_S2"), + PINCTRL_PIN(TB10X_PORT6 + 8, "T_MDO_S2"), + PINCTRL_PIN(TB10X_PORT6 + 9, "T_MOSTRT_S3"), + /* Port 7 */ + PINCTRL_PIN(TB10X_PORT7 + 0, "UART0_TXD"), + PINCTRL_PIN(TB10X_PORT7 + 1, "UART0_RXD"), + PINCTRL_PIN(TB10X_PORT7 + 2, "UART0_CTS"), + PINCTRL_PIN(TB10X_PORT7 + 3, "UART0_RTS"), + PINCTRL_PIN(TB10X_PORT7 + 4, "UART1_TXD"), + PINCTRL_PIN(TB10X_PORT7 + 5, "UART1_RXD"), + PINCTRL_PIN(TB10X_PORT7 + 6, "UART1_CTS"), + PINCTRL_PIN(TB10X_PORT7 + 7, "UART1_RTS"), + /* Port 8 */ + PINCTRL_PIN(TB10X_PORT8 + 0, "SPI3_CLK"), + PINCTRL_PIN(TB10X_PORT8 + 1, "SPI3_MISO"), + PINCTRL_PIN(TB10X_PORT8 + 2, "SPI3_MOSI"), + PINCTRL_PIN(TB10X_PORT8 + 3, "SPI3_SSN"), + /* Port 9 */ + PINCTRL_PIN(TB10X_PORT9 + 0, "SPI1_CLK"), + PINCTRL_PIN(TB10X_PORT9 + 1, "SPI1_MISO"), + PINCTRL_PIN(TB10X_PORT9 + 2, "SPI1_MOSI"), + PINCTRL_PIN(TB10X_PORT9 + 3, "SPI1_SSN0"), + PINCTRL_PIN(TB10X_PORT9 + 4, "SPI1_SSN1"), + /* Unmuxed GPIOs */ + PINCTRL_PIN(TB10X_GPIOS + 0, "GPIOB0"), + PINCTRL_PIN(TB10X_GPIOS + 1, "GPIOB1"), + + PINCTRL_PIN(TB10X_GPIOS + 2, "GPIOD0"), + PINCTRL_PIN(TB10X_GPIOS + 3, "GPIOD1"), + + PINCTRL_PIN(TB10X_GPIOS + 4, "GPIOF0"), + PINCTRL_PIN(TB10X_GPIOS + 5, "GPIOF1"), + + PINCTRL_PIN(TB10X_GPIOS + 6, "GPIOH0"), + PINCTRL_PIN(TB10X_GPIOS + 7, "GPIOH1"), + + PINCTRL_PIN(TB10X_GPIOS + 8, "GPIOI0"), + PINCTRL_PIN(TB10X_GPIOS + 9, "GPIOI1"), + PINCTRL_PIN(TB10X_GPIOS + 10, "GPIOI2"), + PINCTRL_PIN(TB10X_GPIOS + 11, "GPIOI3"), + PINCTRL_PIN(TB10X_GPIOS + 12, "GPIOI4"), + PINCTRL_PIN(TB10X_GPIOS + 13, "GPIOI5"), + PINCTRL_PIN(TB10X_GPIOS + 14, "GPIOI6"), + PINCTRL_PIN(TB10X_GPIOS + 15, "GPIOI7"), + PINCTRL_PIN(TB10X_GPIOS + 16, "GPIOI8"), + PINCTRL_PIN(TB10X_GPIOS + 17, "GPIOI9"), + PINCTRL_PIN(TB10X_GPIOS + 18, "GPIOI10"), + PINCTRL_PIN(TB10X_GPIOS + 19, "GPIOI11"), + + PINCTRL_PIN(TB10X_GPIOS + 20, "GPION0"), + PINCTRL_PIN(TB10X_GPIOS + 21, "GPION1"), + PINCTRL_PIN(TB10X_GPIOS + 22, "GPION2"), + PINCTRL_PIN(TB10X_GPIOS + 23, "GPION3"), +#define MAX_PIN (TB10X_GPIOS + 24) + PINCTRL_PIN(MAX_PIN, "GPION4"), +}; + + +/* Port 1 */ +static const unsigned mis0_pins[] = { TB10X_PORT1 + 0, TB10X_PORT1 + 1, + TB10X_PORT1 + 2, TB10X_PORT1 + 3}; +static const unsigned gpioa_pins[] = { TB10X_PORT1 + 4, TB10X_PORT1 + 5, + TB10X_PORT1 + 6}; +static const unsigned mis1_pins[] = { TB10X_PORT1 + 7, TB10X_PORT1 + 8, + TB10X_PORT1 + 9, TB10X_PORT1 + 10}; +static const unsigned mip1_pins[] = { TB10X_PORT1 + 0, TB10X_PORT1 + 1, + TB10X_PORT1 + 2, TB10X_PORT1 + 3, + TB10X_PORT1 + 4, TB10X_PORT1 + 5, + TB10X_PORT1 + 6, TB10X_PORT1 + 7, + TB10X_PORT1 + 8, TB10X_PORT1 + 9, + TB10X_PORT1 + 10}; + +/* Port 2 */ +static const unsigned mis2_pins[] = { TB10X_PORT2 + 0, TB10X_PORT2 + 1, + TB10X_PORT2 + 2, TB10X_PORT2 + 3}; +static const unsigned gpioc_pins[] = { TB10X_PORT2 + 4, TB10X_PORT2 + 5, + TB10X_PORT2 + 6}; +static const unsigned mis3_pins[] = { TB10X_PORT2 + 7, TB10X_PORT2 + 8, + TB10X_PORT2 + 9, TB10X_PORT2 + 10}; +static const unsigned mip3_pins[] = { TB10X_PORT2 + 0, TB10X_PORT2 + 1, + TB10X_PORT2 + 2, TB10X_PORT2 + 3, + TB10X_PORT2 + 4, TB10X_PORT2 + 5, + TB10X_PORT2 + 6, TB10X_PORT2 + 7, + TB10X_PORT2 + 8, TB10X_PORT2 + 9, + TB10X_PORT2 + 10}; + +/* Port 3 */ +static const unsigned mis4_pins[] = { TB10X_PORT3 + 0, TB10X_PORT3 + 1, + TB10X_PORT3 + 2, TB10X_PORT3 + 3}; +static const unsigned gpioe_pins[] = { TB10X_PORT3 + 4, TB10X_PORT3 + 5, + TB10X_PORT3 + 6}; +static const unsigned mis5_pins[] = { TB10X_PORT3 + 7, TB10X_PORT3 + 8, + TB10X_PORT3 + 9, TB10X_PORT3 + 10}; +static const unsigned mip5_pins[] = { TB10X_PORT3 + 0, TB10X_PORT3 + 1, + TB10X_PORT3 + 2, TB10X_PORT3 + 3, + TB10X_PORT3 + 4, TB10X_PORT3 + 5, + TB10X_PORT3 + 6, TB10X_PORT3 + 7, + TB10X_PORT3 + 8, TB10X_PORT3 + 9, + TB10X_PORT3 + 10}; + +/* Port 4 */ +static const unsigned mis6_pins[] = { TB10X_PORT4 + 0, TB10X_PORT4 + 1, + TB10X_PORT4 + 2, TB10X_PORT4 + 3}; +static const unsigned gpiog_pins[] = { TB10X_PORT4 + 4, TB10X_PORT4 + 5, + TB10X_PORT4 + 6}; +static const unsigned mis7_pins[] = { TB10X_PORT4 + 7, TB10X_PORT4 + 8, + TB10X_PORT4 + 9, TB10X_PORT4 + 10}; +static const unsigned mip7_pins[] = { TB10X_PORT4 + 0, TB10X_PORT4 + 1, + TB10X_PORT4 + 2, TB10X_PORT4 + 3, + TB10X_PORT4 + 4, TB10X_PORT4 + 5, + TB10X_PORT4 + 6, TB10X_PORT4 + 7, + TB10X_PORT4 + 8, TB10X_PORT4 + 9, + TB10X_PORT4 + 10}; + +/* Port 6 */ +static const unsigned mop_pins[] = { TB10X_PORT6 + 0, TB10X_PORT6 + 1, + TB10X_PORT6 + 2, TB10X_PORT6 + 3, + TB10X_PORT6 + 4, TB10X_PORT6 + 5, + TB10X_PORT6 + 6, TB10X_PORT6 + 7, + TB10X_PORT6 + 8, TB10X_PORT6 + 9}; +static const unsigned mos0_pins[] = { TB10X_PORT6 + 0, TB10X_PORT6 + 1, + TB10X_PORT6 + 2}; +static const unsigned mos1_pins[] = { TB10X_PORT6 + 3, TB10X_PORT6 + 4, + TB10X_PORT6 + 5}; +static const unsigned mos2_pins[] = { TB10X_PORT6 + 6, TB10X_PORT6 + 7, + TB10X_PORT6 + 8}; +static const unsigned mos3_pins[] = { TB10X_PORT6 + 9}; + +/* Port 7 */ +static const unsigned uart0_pins[] = { TB10X_PORT7 + 0, TB10X_PORT7 + 1, + TB10X_PORT7 + 2, TB10X_PORT7 + 3}; +static const unsigned uart1_pins[] = { TB10X_PORT7 + 4, TB10X_PORT7 + 5, + TB10X_PORT7 + 6, TB10X_PORT7 + 7}; +static const unsigned gpiol_pins[] = { TB10X_PORT7 + 0, TB10X_PORT7 + 1, + TB10X_PORT7 + 2, TB10X_PORT7 + 3}; +static const unsigned gpiom_pins[] = { TB10X_PORT7 + 4, TB10X_PORT7 + 5, + TB10X_PORT7 + 6, TB10X_PORT7 + 7}; + +/* Port 8 */ +static const unsigned spi3_pins[] = { TB10X_PORT8 + 0, TB10X_PORT8 + 1, + TB10X_PORT8 + 2, TB10X_PORT8 + 3}; +static const unsigned jtag_pins[] = { TB10X_PORT8 + 0, TB10X_PORT8 + 1, + TB10X_PORT8 + 2, TB10X_PORT8 + 3}; + +/* Port 9 */ +static const unsigned spi1_pins[] = { TB10X_PORT9 + 0, TB10X_PORT9 + 1, + TB10X_PORT9 + 2, TB10X_PORT9 + 3, + TB10X_PORT9 + 4}; +static const unsigned gpion_pins[] = { TB10X_PORT9 + 0, TB10X_PORT9 + 1, + TB10X_PORT9 + 2, TB10X_PORT9 + 3, + TB10X_PORT9 + 4}; + +/* Port 5 */ +static const unsigned gpioj_pins[] = { TB10X_PORT5 + 0, TB10X_PORT5 + 1, + TB10X_PORT5 + 2, TB10X_PORT5 + 3, + TB10X_PORT5 + 4, TB10X_PORT5 + 5, + TB10X_PORT5 + 6, TB10X_PORT5 + 7, + TB10X_PORT5 + 8, TB10X_PORT5 + 9, + TB10X_PORT5 + 10, TB10X_PORT5 + 11, + TB10X_PORT5 + 12, TB10X_PORT5 + 13, + TB10X_PORT5 + 14, TB10X_PORT5 + 15, + TB10X_PORT5 + 16, TB10X_PORT5 + 17, + TB10X_PORT5 + 18, TB10X_PORT5 + 19, + TB10X_PORT5 + 20, TB10X_PORT5 + 21, + TB10X_PORT5 + 22, TB10X_PORT5 + 23, + TB10X_PORT5 + 24, TB10X_PORT5 + 25, + TB10X_PORT5 + 26, TB10X_PORT5 + 27, + TB10X_PORT5 + 28, TB10X_PORT5 + 29, + TB10X_PORT5 + 30, TB10X_PORT5 + 31}; +static const unsigned gpiok_pins[] = { TB10X_PORT5 + 32, TB10X_PORT5 + 33, + TB10X_PORT5 + 34, TB10X_PORT5 + 35, + TB10X_PORT5 + 36, TB10X_PORT5 + 37, + TB10X_PORT5 + 38, TB10X_PORT5 + 39, + TB10X_PORT5 + 40, TB10X_PORT5 + 41, + TB10X_PORT5 + 42, TB10X_PORT5 + 43, + TB10X_PORT5 + 44, TB10X_PORT5 + 45, + TB10X_PORT5 + 46, TB10X_PORT5 + 47, + TB10X_PORT5 + 48, TB10X_PORT5 + 49, + TB10X_PORT5 + 50, TB10X_PORT5 + 51, + TB10X_PORT5 + 52, TB10X_PORT5 + 53}; +static const unsigned ciplus_pins[] = { TB10X_PORT5 + 0, TB10X_PORT5 + 1, + TB10X_PORT5 + 2, TB10X_PORT5 + 3, + TB10X_PORT5 + 4, TB10X_PORT5 + 5, + TB10X_PORT5 + 6, TB10X_PORT5 + 7, + TB10X_PORT5 + 8, TB10X_PORT5 + 9, + TB10X_PORT5 + 10, TB10X_PORT5 + 11, + TB10X_PORT5 + 12, TB10X_PORT5 + 13, + TB10X_PORT5 + 14, TB10X_PORT5 + 15, + TB10X_PORT5 + 16, TB10X_PORT5 + 17, + TB10X_PORT5 + 18, TB10X_PORT5 + 19, + TB10X_PORT5 + 20, TB10X_PORT5 + 21, + TB10X_PORT5 + 22, TB10X_PORT5 + 23, + TB10X_PORT5 + 24, TB10X_PORT5 + 25, + TB10X_PORT5 + 26, TB10X_PORT5 + 27, + TB10X_PORT5 + 28, TB10X_PORT5 + 29, + TB10X_PORT5 + 30, TB10X_PORT5 + 31, + TB10X_PORT5 + 32, TB10X_PORT5 + 33, + TB10X_PORT5 + 34, TB10X_PORT5 + 35, + TB10X_PORT5 + 36, TB10X_PORT5 + 37, + TB10X_PORT5 + 38, TB10X_PORT5 + 39, + TB10X_PORT5 + 40, TB10X_PORT5 + 41, + TB10X_PORT5 + 42, TB10X_PORT5 + 43, + TB10X_PORT5 + 44, TB10X_PORT5 + 45, + TB10X_PORT5 + 46, TB10X_PORT5 + 47, + TB10X_PORT5 + 48, TB10X_PORT5 + 49, + TB10X_PORT5 + 50, TB10X_PORT5 + 51, + TB10X_PORT5 + 52, TB10X_PORT5 + 53}; +static const unsigned mcard_pins[] = { TB10X_PORT5 + 3, TB10X_PORT5 + 10, + TB10X_PORT5 + 11, TB10X_PORT5 + 12, + TB10X_PORT5 + 22, TB10X_PORT5 + 23, + TB10X_PORT5 + 33, TB10X_PORT5 + 35, + TB10X_PORT5 + 36, TB10X_PORT5 + 37, + TB10X_PORT5 + 38, TB10X_PORT5 + 39, + TB10X_PORT5 + 40, TB10X_PORT5 + 41, + TB10X_PORT5 + 42, TB10X_PORT5 + 43, + TB10X_PORT5 + 45, TB10X_PORT5 + 46, + TB10X_PORT5 + 47, TB10X_PORT5 + 48, + TB10X_PORT5 + 49, TB10X_PORT5 + 50, + TB10X_PORT5 + 51, TB10X_PORT5 + 52, + TB10X_PORT5 + 53}; +static const unsigned stc0_pins[] = { TB10X_PORT5 + 34, TB10X_PORT5 + 35, + TB10X_PORT5 + 36, TB10X_PORT5 + 37, + TB10X_PORT5 + 38, TB10X_PORT5 + 39, + TB10X_PORT5 + 40}; +static const unsigned stc1_pins[] = { TB10X_PORT5 + 25, TB10X_PORT5 + 26, + TB10X_PORT5 + 27, TB10X_PORT5 + 28, + TB10X_PORT5 + 29, TB10X_PORT5 + 30, + TB10X_PORT5 + 44}; + +/* Unmuxed GPIOs */ +static const unsigned gpiob_pins[] = { TB10X_GPIOS + 0, TB10X_GPIOS + 1}; +static const unsigned gpiod_pins[] = { TB10X_GPIOS + 2, TB10X_GPIOS + 3}; +static const unsigned gpiof_pins[] = { TB10X_GPIOS + 4, TB10X_GPIOS + 5}; +static const unsigned gpioh_pins[] = { TB10X_GPIOS + 6, TB10X_GPIOS + 7}; +static const unsigned gpioi_pins[] = { TB10X_GPIOS + 8, TB10X_GPIOS + 9, + TB10X_GPIOS + 10, TB10X_GPIOS + 11, + TB10X_GPIOS + 12, TB10X_GPIOS + 13, + TB10X_GPIOS + 14, TB10X_GPIOS + 15, + TB10X_GPIOS + 16, TB10X_GPIOS + 17, + TB10X_GPIOS + 18, TB10X_GPIOS + 19}; + +struct tb10x_pinfuncgrp { + const char *name; + const unsigned int *pins; + const unsigned int pincnt; + const int port; + const unsigned int mode; + const int isgpio; +}; +#define DEFPINFUNCGRP(NAME, PORT, MODE, ISGPIO) { \ + .name = __stringify(NAME), \ + .pins = NAME##_pins, .pincnt = ARRAY_SIZE(NAME##_pins), \ + .port = (PORT), .mode = (MODE), \ + .isgpio = (ISGPIO), \ + } +static const struct tb10x_pinfuncgrp tb10x_pingroups[] = { + DEFPINFUNCGRP(mis0, 0, 0, 0), + DEFPINFUNCGRP(gpioa, 0, 0, 1), + DEFPINFUNCGRP(mis1, 0, 0, 0), + DEFPINFUNCGRP(mip1, 0, 1, 0), + DEFPINFUNCGRP(mis2, 1, 0, 0), + DEFPINFUNCGRP(gpioc, 1, 0, 1), + DEFPINFUNCGRP(mis3, 1, 0, 0), + DEFPINFUNCGRP(mip3, 1, 1, 0), + DEFPINFUNCGRP(mis4, 2, 0, 0), + DEFPINFUNCGRP(gpioe, 2, 0, 1), + DEFPINFUNCGRP(mis5, 2, 0, 0), + DEFPINFUNCGRP(mip5, 2, 1, 0), + DEFPINFUNCGRP(mis6, 3, 0, 0), + DEFPINFUNCGRP(gpiog, 3, 0, 1), + DEFPINFUNCGRP(mis7, 3, 0, 0), + DEFPINFUNCGRP(mip7, 3, 1, 0), + DEFPINFUNCGRP(gpioj, 4, 0, 1), + DEFPINFUNCGRP(gpiok, 4, 0, 1), + DEFPINFUNCGRP(ciplus, 4, 1, 0), + DEFPINFUNCGRP(mcard, 4, 2, 0), + DEFPINFUNCGRP(stc0, 4, 3, 0), + DEFPINFUNCGRP(stc1, 4, 3, 0), + DEFPINFUNCGRP(mop, 5, 0, 0), + DEFPINFUNCGRP(mos0, 5, 1, 0), + DEFPINFUNCGRP(mos1, 5, 1, 0), + DEFPINFUNCGRP(mos2, 5, 1, 0), + DEFPINFUNCGRP(mos3, 5, 1, 0), + DEFPINFUNCGRP(uart0, 6, 0, 0), + DEFPINFUNCGRP(uart1, 6, 0, 0), + DEFPINFUNCGRP(gpiol, 6, 1, 1), + DEFPINFUNCGRP(gpiom, 6, 1, 1), + DEFPINFUNCGRP(spi3, 7, 0, 0), + DEFPINFUNCGRP(jtag, 7, 1, 0), + DEFPINFUNCGRP(spi1, 8, 0, 0), + DEFPINFUNCGRP(gpion, 8, 1, 1), + DEFPINFUNCGRP(gpiob, -1, 0, 1), + DEFPINFUNCGRP(gpiod, -1, 0, 1), + DEFPINFUNCGRP(gpiof, -1, 0, 1), + DEFPINFUNCGRP(gpioh, -1, 0, 1), + DEFPINFUNCGRP(gpioi, -1, 0, 1), +}; +#undef DEFPINFUNCGRP + +struct tb10x_of_pinfunc { + const char *name; + const char *group; +}; + +#define TB10X_PORTS (9) + +/** + * struct tb10x_port - state of an I/O port + * @mode: Node this port is currently in. + * @count: Number of enabled functions which require this port to be + * configured in @mode. + */ +struct tb10x_port { + unsigned int mode; + unsigned int count; +}; + +/** + * struct tb10x_pinctrl - TB10x pin controller internal state + * @pctl: pointer to the pinctrl_dev structure of this pin controller. + * @base: register set base address. + * @pingroups: pointer to an array of the pin groups this driver manages. + * @pinfuncgrpcnt: number of pingroups in @pingroups. + * @pinfuncs: pointer to an array of pin functions this driver manages. + * @pinfuncnt: number of pin functions in @pinfuncs. + * @mutex: mutex for exclusive access to a pin controller's state. + * @ports: current state of each port. + * @gpios: Indicates if a given pin is currently used as GPIO (1) or not (0). + */ +struct tb10x_pinctrl { + struct pinctrl_dev *pctl; + void *base; + const struct tb10x_pinfuncgrp *pingroups; + unsigned int pinfuncgrpcnt; + struct tb10x_of_pinfunc *pinfuncs; + unsigned int pinfuncnt; + struct mutex mutex; + struct tb10x_port ports[TB10X_PORTS]; + DECLARE_BITMAP(gpios, MAX_PIN + 1); +}; + +static inline void tb10x_pinctrl_set_config(struct tb10x_pinctrl *state, + unsigned int port, unsigned int mode) +{ + u32 pcfg; + + if (state->ports[port].count) + return; + + state->ports[port].mode = mode; + + pcfg = ioread32(state->base) & ~(PCFG_PORT_MASK(port)); + pcfg |= (mode << (PCFG_PORT_BITWIDTH * port)) & PCFG_PORT_MASK(port); + iowrite32(pcfg, state->base); +} + +static inline unsigned int tb10x_pinctrl_get_config( + struct tb10x_pinctrl *state, + unsigned int port) +{ + return (ioread32(state->base) & PCFG_PORT_MASK(port)) + >> (PCFG_PORT_BITWIDTH * port); +} + +static int tb10x_get_groups_count(struct pinctrl_dev *pctl) +{ + struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl); + return state->pinfuncgrpcnt; +} + +static const char *tb10x_get_group_name(struct pinctrl_dev *pctl, unsigned n) +{ + struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl); + return state->pingroups[n].name; +} + +static int tb10x_get_group_pins(struct pinctrl_dev *pctl, unsigned n, + unsigned const **pins, + unsigned * const num_pins) +{ + struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl); + + *pins = state->pingroups[n].pins; + *num_pins = state->pingroups[n].pincnt; + + return 0; +} + +static int tb10x_dt_node_to_map(struct pinctrl_dev *pctl, + struct device_node *np_config, + struct pinctrl_map **map, unsigned *num_maps) +{ + const char *string; + unsigned reserved_maps = 0; + int ret = 0; + + if (of_property_read_string(np_config, "abilis,function", &string)) { + pr_err("%s: No abilis,function property in device tree.\n", + np_config->full_name); + return -EINVAL; + } + + *map = NULL; + *num_maps = 0; + + ret = pinctrl_utils_reserve_map(pctl, map, &reserved_maps, + num_maps, 1); + if (ret) + goto out; + + ret = pinctrl_utils_add_map_mux(pctl, map, &reserved_maps, + num_maps, string, np_config->name); + +out: + return ret; +} + +static struct pinctrl_ops tb10x_pinctrl_ops = { + .get_groups_count = tb10x_get_groups_count, + .get_group_name = tb10x_get_group_name, + .get_group_pins = tb10x_get_group_pins, + .dt_node_to_map = tb10x_dt_node_to_map, + .dt_free_map = pinctrl_utils_dt_free_map, +}; + +static int tb10x_get_functions_count(struct pinctrl_dev *pctl) +{ + struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl); + return state->pinfuncnt; +} + +static const char *tb10x_get_function_name(struct pinctrl_dev *pctl, + unsigned n) +{ + struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl); + return state->pinfuncs[n].name; +} + +static int tb10x_get_function_groups(struct pinctrl_dev *pctl, + unsigned n, const char * const **groups, + unsigned * const num_groups) +{ + struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl); + + *groups = &state->pinfuncs[n].group; + *num_groups = 1; + + return 0; +} + +static int tb10x_gpio_request_enable(struct pinctrl_dev *pctl, + struct pinctrl_gpio_range *range, + unsigned pin) +{ + struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl); + int muxport = -1; + int muxmode = -1; + int i; + + mutex_lock(&state->mutex); + + /* + * Figure out to which port the requested GPIO belongs and how to + * configure that port. + * This loop also checks for pin conflicts between GPIOs and other + * functions. + */ + for (i = 0; i < state->pinfuncgrpcnt; i++) { + const struct tb10x_pinfuncgrp *pfg = &state->pingroups[i]; + unsigned int port = pfg->port; + unsigned int mode = pfg->mode; + int j; + + /* + * Skip pin groups which are always mapped and don't need + * to be configured. + */ + if (port < 0) + continue; + + for (j = 0; j < pfg->pincnt; j++) { + if (pin == pfg->pins[j]) { + if (pfg->isgpio) { + /* + * Remember the GPIO-only setting of + * the port this pin belongs to. + */ + muxport = port; + muxmode = mode; + } else if (state->ports[port].count + && (state->ports[port].mode == mode)) { + /* + * Error: The requested pin is already + * used for something else. + */ + mutex_unlock(&state->mutex); + return -EBUSY; + } + break; + } + } + } + + /* + * If we haven't returned an error at this point, the GPIO pin is not + * used by another function and the GPIO request can be granted: + * Register pin as being used as GPIO so we don't allocate it to + * another function later. + */ + set_bit(pin, state->gpios); + + /* + * Potential conflicts between GPIOs and pin functions were caught + * earlier in this function and tb10x_pinctrl_set_config will do the + * Right Thing, either configure the port in GPIO only mode or leave + * another mode compatible with this GPIO request untouched. + */ + if (muxport >= 0) + tb10x_pinctrl_set_config(state, muxport, muxmode); + + mutex_unlock(&state->mutex); + + return 0; +} + +static void tb10x_gpio_disable_free(struct pinctrl_dev *pctl, + struct pinctrl_gpio_range *range, + unsigned pin) +{ + struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl); + + mutex_lock(&state->mutex); + + clear_bit(pin, state->gpios); + + mutex_unlock(&state->mutex); +} + +static int tb10x_pctl_enable(struct pinctrl_dev *pctl, + unsigned func_selector, unsigned group_selector) +{ + struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl); + const struct tb10x_pinfuncgrp *grp = &state->pingroups[group_selector]; + int i; + + if (grp->port < 0) + return 0; + + mutex_lock(&state->mutex); + + /* + * Check if the requested function is compatible with previously + * requested functions. + */ + if (state->ports[grp->port].count + && (state->ports[grp->port].mode != grp->mode)) { + mutex_unlock(&state->mutex); + return -EBUSY; + } + + /* + * Check if the requested function is compatible with previously + * requested GPIOs. + */ + for (i = 0; i < grp->pincnt; i++) + if (test_bit(grp->pins[i], state->gpios)) { + mutex_unlock(&state->mutex); + return -EBUSY; + } + + tb10x_pinctrl_set_config(state, grp->port, grp->mode); + + state->ports[grp->port].count++; + + mutex_unlock(&state->mutex); + + return 0; +} + +static void tb10x_pctl_disable(struct pinctrl_dev *pctl, + unsigned func_selector, unsigned group_selector) +{ + struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl); + const struct tb10x_pinfuncgrp *grp = &state->pingroups[group_selector]; + + if (grp->port < 0) + return; + + mutex_lock(&state->mutex); + + state->ports[grp->port].count--; + + mutex_unlock(&state->mutex); +} + +static struct pinmux_ops tb10x_pinmux_ops = { + .get_functions_count = tb10x_get_functions_count, + .get_function_name = tb10x_get_function_name, + .get_function_groups = tb10x_get_function_groups, + .gpio_request_enable = tb10x_gpio_request_enable, + .gpio_disable_free = tb10x_gpio_disable_free, + .enable = tb10x_pctl_enable, + .disable = tb10x_pctl_disable, +}; + +static struct pinctrl_desc tb10x_pindesc = { + .name = "TB10x", + .pins = tb10x_pins, + .npins = ARRAY_SIZE(tb10x_pins), + .owner = THIS_MODULE, + .pctlops = &tb10x_pinctrl_ops, + .pmxops = &tb10x_pinmux_ops, +}; + +static int tb10x_pinctrl_probe(struct platform_device *pdev) +{ + int ret = -EINVAL; + struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct device *dev = &pdev->dev; + struct device_node *of_node = dev->of_node; + struct device_node *child; + struct tb10x_pinctrl *state; + int i; + + if (!of_node) { + dev_err(dev, "No device tree node found.\n"); + return -EINVAL; + } + + if (!mem) { + dev_err(dev, "No memory resource defined.\n"); + return -EINVAL; + } + + state = devm_kzalloc(dev, sizeof(struct tb10x_pinctrl) + + of_get_child_count(of_node) + * sizeof(struct tb10x_of_pinfunc), + GFP_KERNEL); + if (!state) + return -ENOMEM; + + platform_set_drvdata(pdev, state); + state->pinfuncs = (struct tb10x_of_pinfunc *)(state + 1); + mutex_init(&state->mutex); + + state->base = devm_ioremap_resource(dev, mem); + if (!state->base) { + dev_err(dev, "Request register region failed.\n"); + ret = -EBUSY; + goto fail; + } + + state->pingroups = tb10x_pingroups; + state->pinfuncgrpcnt = ARRAY_SIZE(tb10x_pingroups); + + for (i = 0; i < TB10X_PORTS; i++) + state->ports[i].mode = tb10x_pinctrl_get_config(state, i); + + for_each_child_of_node(of_node, child) { + const char *name; + + if (!of_property_read_string(child, "abilis,function", + &name)) { + state->pinfuncs[state->pinfuncnt].name = child->name; + state->pinfuncs[state->pinfuncnt].group = name; + state->pinfuncnt++; + } + } + + state->pctl = pinctrl_register(&tb10x_pindesc, dev, state); + if (IS_ERR(state->pctl)) { + dev_err(dev, "could not register TB10x pin driver\n"); + ret = PTR_ERR(state->pctl); + goto fail; + } + + return 0; + +fail: + mutex_destroy(&state->mutex); + return ret; +} + +static int tb10x_pinctrl_remove(struct platform_device *pdev) +{ + struct tb10x_pinctrl *state = platform_get_drvdata(pdev); + + pinctrl_unregister(state->pctl); + mutex_destroy(&state->mutex); + + return 0; +} + + +static const struct of_device_id tb10x_pinctrl_dt_ids[] = { + { .compatible = "abilis,tb10x-iomux" }, + { } +}; +MODULE_DEVICE_TABLE(of, tb10x_pinctrl_dt_ids); + +static struct platform_driver tb10x_pinctrl_pdrv = { + .probe = tb10x_pinctrl_probe, + .remove = tb10x_pinctrl_remove, + .driver = { + .name = "tb10x_pinctrl", + .of_match_table = of_match_ptr(tb10x_pinctrl_dt_ids), + .owner = THIS_MODULE + } +}; + +static int __init tb10x_iopinctrl_init(void) +{ + return platform_driver_register(&tb10x_pinctrl_pdrv); +} + +static void __exit tb10x_iopinctrl_exit(void) +{ + platform_driver_unregister(&tb10x_pinctrl_pdrv); +} + +MODULE_AUTHOR("Christian Ruppert "); +MODULE_LICENSE("GPL"); +module_init(tb10x_iopinctrl_init); +module_exit(tb10x_iopinctrl_exit); -- cgit v0.10.2 From 6d0a4ed2b90a12e1403d3e7d9d8c2cc7fdc301b5 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Mon, 14 Oct 2013 01:27:27 +0200 Subject: pinctrl: dove: unset twsi option3 for gconfig as well This fixes a typo which left twsi config3 option enabled. Cc: stable@vger.kernel.org Signed-off-by: Roel Kluin Acked-by: Sebastian Hesselbarth Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/mvebu/pinctrl-dove.c b/drivers/pinctrl/mvebu/pinctrl-dove.c index 29f7e4f..360b9b2 100644 --- a/drivers/pinctrl/mvebu/pinctrl-dove.c +++ b/drivers/pinctrl/mvebu/pinctrl-dove.c @@ -335,7 +335,7 @@ static int dove_twsi_ctrl_set(struct mvebu_mpp_ctrl *ctrl, unsigned long gcfg2 = readl(DOVE_GLOBAL_CONFIG_2); gcfg1 &= ~DOVE_TWSI_ENABLE_OPTION1; - gcfg2 &= ~(DOVE_TWSI_ENABLE_OPTION2 | DOVE_TWSI_ENABLE_OPTION2); + gcfg2 &= ~(DOVE_TWSI_ENABLE_OPTION2 | DOVE_TWSI_ENABLE_OPTION3); switch (config) { case 1: -- cgit v0.10.2 From a282926d658209ed1acee51a1ccc897ac169fb41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Heiko=20St=C3=BCbner?= Date: Wed, 16 Oct 2013 01:07:20 +0200 Subject: pinctrl: rockchip: separate different sub-types more Further investigation of the different Rockchip SoCs showed that the differences especially in the pull settings are quite deep. As further patches will show, the register layout for the pulls of the rk3188 is quite strange. Also it is to assume, that later Rockchip SoCs may introduce even more quirks in this regard, making it hard to support all of those using the current generic pull_* variables. Therefore move the driver to hold the type of controller in an enum and do the handling according to it in the necessary places. Also instead of calculating the register in the get and set pull functions move it to a type-specific callback. Signed-off-by: Heiko Stuebner Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c index e0718b7..df155f9 100644 --- a/drivers/pinctrl/pinctrl-rockchip.c +++ b/drivers/pinctrl/pinctrl-rockchip.c @@ -56,6 +56,12 @@ #define GPIO_EXT_PORT 0x50 #define GPIO_LS_SYNC 0x60 +enum rockchip_pinctrl_type { + RK2928, + RK3066B, + RK3188, +}; + /** * @reg_base: register base of the gpio bank * @clk: clock of the gpio bank @@ -98,18 +104,16 @@ struct rockchip_pin_bank { } /** - * @pull_auto: some SoCs don't allow pulls to be specified as up or down, but - * instead decide this automatically based on the pad-type. */ struct rockchip_pin_ctrl { struct rockchip_pin_bank *pin_banks; u32 nr_banks; u32 nr_pins; char *label; + enum rockchip_pinctrl_type type; int mux_offset; - int pull_offset; - bool pull_auto; - int pull_bank_stride; + void (*pull_calc_reg)(struct rockchip_pin_bank *bank, int pin_num, + void __iomem **reg, u8 *bit); }; struct rockchip_pin_config { @@ -354,6 +358,22 @@ static void rockchip_set_mux(struct rockchip_pin_bank *bank, int pin, int mux) spin_unlock_irqrestore(&bank->slock, flags); } +#define RK2928_PULL_OFFSET 0x118 +#define RK2928_PULL_PINS_PER_REG 16 +#define RK2928_PULL_BANK_STRIDE 8 + +static void rk2928_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, void __iomem **reg, u8 *bit) +{ + struct rockchip_pinctrl *info = bank->drvdata; + + *reg = info->reg_base + RK2928_PULL_OFFSET; + *reg += bank->bank_num * RK2928_PULL_BANK_STRIDE; + *reg += (pin_num / RK2928_PULL_PINS_PER_REG) * 4; + + *bit = pin_num % RK2928_PULL_PINS_PER_REG; +}; + static int rockchip_get_pull(struct rockchip_pin_bank *bank, int pin_num) { struct rockchip_pinctrl *info = bank->drvdata; @@ -362,23 +382,22 @@ static int rockchip_get_pull(struct rockchip_pin_bank *bank, int pin_num) u8 bit; /* rk3066b does support any pulls */ - if (!ctrl->pull_offset) + if (ctrl->type == RK3066B) return PIN_CONFIG_BIAS_DISABLE; - reg = info->reg_base + ctrl->pull_offset; - - if (ctrl->pull_auto) { - reg += bank->bank_num * ctrl->pull_bank_stride; - reg += (pin_num / 16) * 4; - bit = pin_num % 16; - + switch (ctrl->type) { + case RK2928: + ctrl->pull_calc_reg(bank, pin_num, ®, &bit); return !(readl_relaxed(reg) & BIT(bit)) ? PIN_CONFIG_BIAS_PULL_PIN_DEFAULT : PIN_CONFIG_BIAS_DISABLE; - } else { + case RK3188: dev_err(info->dev, "pull support for rk31xx not implemented\n"); return -EIO; - } + default: + dev_err(info->dev, "unsupported pinctrl type\n"); + return -EINVAL; + }; } static int rockchip_set_pull(struct rockchip_pin_bank *bank, @@ -395,21 +414,18 @@ static int rockchip_set_pull(struct rockchip_pin_bank *bank, bank->bank_num, pin_num, pull); /* rk3066b does support any pulls */ - if (!ctrl->pull_offset) + if (ctrl->type == RK3066B) return pull ? -EINVAL : 0; - reg = info->reg_base + ctrl->pull_offset; - - if (ctrl->pull_auto) { + switch (ctrl->type) { + case RK2928: if (pull != PIN_CONFIG_BIAS_PULL_PIN_DEFAULT && pull != PIN_CONFIG_BIAS_DISABLE) { dev_err(info->dev, "only PIN_DEFAULT and DISABLE allowed\n"); return -EINVAL; } - reg += bank->bank_num * ctrl->pull_bank_stride; - reg += (pin_num / 16) * 4; - bit = pin_num % 16; + ctrl->pull_calc_reg(bank, pin_num, ®, &bit); spin_lock_irqsave(&bank->slock, flags); @@ -419,14 +435,13 @@ static int rockchip_set_pull(struct rockchip_pin_bank *bank, writel(data, reg); spin_unlock_irqrestore(&bank->slock, flags); - } else { - if (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT) { - dev_err(info->dev, "pull direction (up/down) needs to be specified\n"); - return -EINVAL; - } - + break; + case RK3188: dev_err(info->dev, "pull support for rk31xx not implemented\n"); return -EIO; + default: + dev_err(info->dev, "unsupported pinctrl type\n"); + return -EINVAL; } return 0; @@ -556,20 +571,17 @@ static const struct pinmux_ops rockchip_pmx_ops = { static bool rockchip_pinconf_pull_valid(struct rockchip_pin_ctrl *ctrl, enum pin_config_param pull) { - /* rk3066b does support any pulls */ - if (!ctrl->pull_offset) + switch (ctrl->type) { + case RK2928: + return (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT || + pull == PIN_CONFIG_BIAS_DISABLE); + case RK3066B: return pull ? false : true; - - if (ctrl->pull_auto) { - if (pull != PIN_CONFIG_BIAS_PULL_PIN_DEFAULT && - pull != PIN_CONFIG_BIAS_DISABLE) - return false; - } else { - if (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT) - return false; + case RK3188: + return (pull != PIN_CONFIG_BIAS_PULL_PIN_DEFAULT); } - return true; + return false; } /* set the pin config settings for a specified pin */ @@ -1315,10 +1327,9 @@ static struct rockchip_pin_ctrl rk2928_pin_ctrl = { .pin_banks = rk2928_pin_banks, .nr_banks = ARRAY_SIZE(rk2928_pin_banks), .label = "RK2928-GPIO", + .type = RK2928, .mux_offset = 0xa8, - .pull_offset = 0x118, - .pull_auto = 1, - .pull_bank_stride = 8, + .pull_calc_reg = rk2928_calc_pull_reg_and_bit, }; static struct rockchip_pin_bank rk3066a_pin_banks[] = { @@ -1334,10 +1345,9 @@ static struct rockchip_pin_ctrl rk3066a_pin_ctrl = { .pin_banks = rk3066a_pin_banks, .nr_banks = ARRAY_SIZE(rk3066a_pin_banks), .label = "RK3066a-GPIO", + .type = RK2928, .mux_offset = 0xa8, - .pull_offset = 0x118, - .pull_auto = 1, - .pull_bank_stride = 8, + .pull_calc_reg = rk2928_calc_pull_reg_and_bit, }; static struct rockchip_pin_bank rk3066b_pin_banks[] = { @@ -1351,8 +1361,8 @@ static struct rockchip_pin_ctrl rk3066b_pin_ctrl = { .pin_banks = rk3066b_pin_banks, .nr_banks = ARRAY_SIZE(rk3066b_pin_banks), .label = "RK3066b-GPIO", + .type = RK3066B, .mux_offset = 0x60, - .pull_offset = -EINVAL, }; static struct rockchip_pin_bank rk3188_pin_banks[] = { @@ -1366,9 +1376,8 @@ static struct rockchip_pin_ctrl rk3188_pin_ctrl = { .pin_banks = rk3188_pin_banks, .nr_banks = ARRAY_SIZE(rk3188_pin_banks), .label = "RK3188-GPIO", + .type = RK3188, .mux_offset = 0x68, - .pull_offset = 0x164, - .pull_bank_stride = 16, }; static const struct of_device_id rockchip_pinctrl_dt_match[] = { -- cgit v0.10.2 From 65fca613b00aa2aaa008d40304e00b76f4693f60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Heiko=20St=C3=BCbner?= Date: Wed, 16 Oct 2013 01:07:49 +0200 Subject: pinctrl: rockchip: add support for multiple bank types There are Rockchip SoCs, namely the rk3188, that combine a set of regular banks with banks that need special handling for some settings. Therefore add the possibility for the driver to handle more than one bank type. Signed-off-by: Heiko Stuebner Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c index df155f9..efca116 100644 --- a/drivers/pinctrl/pinctrl-rockchip.c +++ b/drivers/pinctrl/pinctrl-rockchip.c @@ -62,6 +62,10 @@ enum rockchip_pinctrl_type { RK3188, }; +enum rockchip_pin_bank_type { + COMMON_BANK, +}; + /** * @reg_base: register base of the gpio bank * @clk: clock of the gpio bank @@ -86,6 +90,7 @@ struct rockchip_pin_bank { u8 nr_pins; char *name; u8 bank_num; + enum rockchip_pin_bank_type bank_type; bool valid; struct device_node *of_node; struct rockchip_pinctrl *drvdata; @@ -668,7 +673,10 @@ static const struct pinconf_ops rockchip_pinconf_ops = { .pin_config_set = rockchip_pinconf_set, }; -static const char *gpio_compat = "rockchip,gpio-bank"; +static const struct of_device_id rockchip_bank_match[] = { + { .compatible = "rockchip,gpio-bank" }, + {}, +}; static void rockchip_pinctrl_child_count(struct rockchip_pinctrl *info, struct device_node *np) @@ -676,7 +684,7 @@ static void rockchip_pinctrl_child_count(struct rockchip_pinctrl *info, struct device_node *child; for_each_child_of_node(np, child) { - if (of_device_is_compatible(child, gpio_compat)) + if (of_match_node(rockchip_bank_match, child)) continue; info->nfunctions++; @@ -819,8 +827,9 @@ static int rockchip_pinctrl_parse_dt(struct platform_device *pdev, i = 0; for_each_child_of_node(np, child) { - if (of_device_is_compatible(child, gpio_compat)) + if (of_match_node(rockchip_bank_match, child)) continue; + ret = rockchip_pinctrl_parse_functions(child, info, i++); if (ret) { dev_err(&pdev->dev, "failed to parse function\n"); @@ -1217,6 +1226,8 @@ static int rockchip_get_bank_data(struct rockchip_pin_bank *bank, if (IS_ERR(bank->reg_base)) return PTR_ERR(bank->reg_base); + bank->bank_type = COMMON_BANK; + bank->irq = irq_of_parse_and_map(bank->of_node, 0); bank->clk = of_clk_get(bank->of_node, 0); -- cgit v0.10.2 From d1358f90e3b63530a5e0cf4ffa7560b359e9d638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Heiko=20St=C3=BCbner?= Date: Wed, 16 Oct 2013 01:08:11 +0200 Subject: pinctrl: rockchip: remove redundant check The check limiting bias options to supported ones is already done thru rockchip_pinconf_pull_valid. Therefore this check is redundant and can be removed. Signed-off-by: Heiko Stuebner Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c index efca116..91027dd 100644 --- a/drivers/pinctrl/pinctrl-rockchip.c +++ b/drivers/pinctrl/pinctrl-rockchip.c @@ -424,12 +424,6 @@ static int rockchip_set_pull(struct rockchip_pin_bank *bank, switch (ctrl->type) { case RK2928: - if (pull != PIN_CONFIG_BIAS_PULL_PIN_DEFAULT && - pull != PIN_CONFIG_BIAS_DISABLE) { - dev_err(info->dev, "only PIN_DEFAULT and DISABLE allowed\n"); - return -EINVAL; - } - ctrl->pull_calc_reg(bank, pin_num, ®, &bit); spin_lock_irqsave(&bank->slock, flags); -- cgit v0.10.2 From 6ca5274d1d1258b0e84bc1a0d67ca160d8658e5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Heiko=20St=C3=BCbner?= Date: Wed, 16 Oct 2013 01:08:42 +0200 Subject: pinctrl: rockchip: add rk3188 specifics Besides the pull registers sitting in a separate place, the rk3188 also has the peculiarity that the pull registers of the first bank are split and the first half is sitting in the register space of the pmu. Therefore this adds a special bank-type for the first bank, to handle the two register sources. Signed-off-by: Heiko Stuebner Signed-off-by: Linus Walleij diff --git a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt index b0fb101..f378d34 100644 --- a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt @@ -21,10 +21,13 @@ defined as gpio sub-nodes of the pinmux controller. Required properties for iomux controller: - compatible: one of "rockchip,rk2928-pinctrl", "rockchip,rk3066a-pinctrl" "rockchip,rk3066b-pinctrl", "rockchip,rk3188-pinctrl" + - reg: first element is the general register space of the iomux controller + second element is the separate pull register space of the rk3188 Required properties for gpio sub nodes: - - compatible: "rockchip,gpio-bank" + - compatible: "rockchip,gpio-bank", "rockchip,rk3188-gpio-bank0" - reg: register of the gpio bank (different than the iomux registerset) + second element: separate pull register for rk3188 bank0 - interrupts: base interrupt of the gpio bank in the interrupt controller - clocks: clock that drives this bank - gpio-controller: identifies the node as a gpio controller and pin bank. @@ -95,3 +98,44 @@ uart2: serial@20064000 { pinctrl-names = "default"; pinctrl-0 = <&uart2_xfer>; }; + +Example for rk3188: + + pinctrl@20008000 { + compatible = "rockchip,rk3188-pinctrl"; + reg = <0x20008000 0xa0>, + <0x20008164 0x1a0>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + gpio0: gpio0@0x2000a000 { + compatible = "rockchip,rk3188-gpio-bank0"; + reg = <0x2000a000 0x100>, + <0x20004064 0x8>; + interrupts = ; + clocks = <&clk_gates8 9>; + + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpio1: gpio1@0x2003c000 { + compatible = "rockchip,gpio-bank"; + reg = <0x2003c000 0x100>; + interrupts = ; + clocks = <&clk_gates8 10>; + + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + ... + + }; diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c index 91027dd..f5e53a7 100644 --- a/drivers/pinctrl/pinctrl-rockchip.c +++ b/drivers/pinctrl/pinctrl-rockchip.c @@ -64,10 +64,12 @@ enum rockchip_pinctrl_type { enum rockchip_pin_bank_type { COMMON_BANK, + RK3188_BANK0, }; /** * @reg_base: register base of the gpio bank + * @reg_pull: optional separate register for additional pull settings * @clk: clock of the gpio bank * @irq: interrupt of the gpio bank * @pin_base: first pin number @@ -84,6 +86,7 @@ enum rockchip_pin_bank_type { */ struct rockchip_pin_bank { void __iomem *reg_base; + void __iomem *reg_pull; struct clk *clk; int irq; u32 pin_base; @@ -157,6 +160,7 @@ struct rockchip_pmx_func { struct rockchip_pinctrl { void __iomem *reg_base; + void __iomem *reg_pull; struct device *dev; struct rockchip_pin_ctrl *ctrl; struct pinctrl_desc pctl; @@ -379,25 +383,71 @@ static void rk2928_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, *bit = pin_num % RK2928_PULL_PINS_PER_REG; }; +#define RK3188_PULL_BITS_PER_PIN 2 +#define RK3188_PULL_PINS_PER_REG 8 +#define RK3188_PULL_BANK_STRIDE 16 + +static void rk3188_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, void __iomem **reg, u8 *bit) +{ + struct rockchip_pinctrl *info = bank->drvdata; + + /* The first 12 pins of the first bank are located elsewhere */ + if (bank->bank_type == RK3188_BANK0 && pin_num < 12) { + *reg = bank->reg_pull + + ((pin_num / RK3188_PULL_PINS_PER_REG) * 4); + *bit = pin_num % RK3188_PULL_PINS_PER_REG; + *bit *= RK3188_PULL_BITS_PER_PIN; + } else { + *reg = info->reg_pull - 4; + *reg += bank->bank_num * RK3188_PULL_BANK_STRIDE; + *reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4); + + /* + * The bits in these registers have an inverse ordering + * with the lowest pin being in bits 15:14 and the highest + * pin in bits 1:0 + */ + *bit = 7 - (pin_num % RK3188_PULL_PINS_PER_REG); + *bit *= RK3188_PULL_BITS_PER_PIN; + } +} + static int rockchip_get_pull(struct rockchip_pin_bank *bank, int pin_num) { struct rockchip_pinctrl *info = bank->drvdata; struct rockchip_pin_ctrl *ctrl = info->ctrl; void __iomem *reg; u8 bit; + u32 data; /* rk3066b does support any pulls */ if (ctrl->type == RK3066B) return PIN_CONFIG_BIAS_DISABLE; + ctrl->pull_calc_reg(bank, pin_num, ®, &bit); + switch (ctrl->type) { case RK2928: - ctrl->pull_calc_reg(bank, pin_num, ®, &bit); return !(readl_relaxed(reg) & BIT(bit)) ? PIN_CONFIG_BIAS_PULL_PIN_DEFAULT : PIN_CONFIG_BIAS_DISABLE; case RK3188: - dev_err(info->dev, "pull support for rk31xx not implemented\n"); + data = readl_relaxed(reg) >> bit; + data &= (1 << RK3188_PULL_BITS_PER_PIN) - 1; + + switch (data) { + case 0: + return PIN_CONFIG_BIAS_DISABLE; + case 1: + return PIN_CONFIG_BIAS_PULL_UP; + case 2: + return PIN_CONFIG_BIAS_PULL_DOWN; + case 3: + return PIN_CONFIG_BIAS_BUS_HOLD; + } + + dev_err(info->dev, "unknown pull setting\n"); return -EIO; default: dev_err(info->dev, "unsupported pinctrl type\n"); @@ -422,10 +472,10 @@ static int rockchip_set_pull(struct rockchip_pin_bank *bank, if (ctrl->type == RK3066B) return pull ? -EINVAL : 0; + ctrl->pull_calc_reg(bank, pin_num, ®, &bit); + switch (ctrl->type) { case RK2928: - ctrl->pull_calc_reg(bank, pin_num, ®, &bit); - spin_lock_irqsave(&bank->slock, flags); data = BIT(bit + 16); @@ -436,8 +486,33 @@ static int rockchip_set_pull(struct rockchip_pin_bank *bank, spin_unlock_irqrestore(&bank->slock, flags); break; case RK3188: - dev_err(info->dev, "pull support for rk31xx not implemented\n"); - return -EIO; + spin_lock_irqsave(&bank->slock, flags); + + /* enable the write to the equivalent lower bits */ + data = ((1 << RK3188_PULL_BITS_PER_PIN) - 1) << (bit + 16); + + switch (pull) { + case PIN_CONFIG_BIAS_DISABLE: + break; + case PIN_CONFIG_BIAS_PULL_UP: + data |= (1 << bit); + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + data |= (2 << bit); + break; + case PIN_CONFIG_BIAS_BUS_HOLD: + data |= (3 << bit); + break; + default: + dev_err(info->dev, "unsupported pull setting %d\n", + pull); + return -EINVAL; + } + + writel(data, reg); + + spin_unlock_irqrestore(&bank->slock, flags); + break; default: dev_err(info->dev, "unsupported pinctrl type\n"); return -EINVAL; @@ -608,6 +683,7 @@ static int rockchip_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, case PIN_CONFIG_BIAS_PULL_UP: case PIN_CONFIG_BIAS_PULL_DOWN: case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: + case PIN_CONFIG_BIAS_BUS_HOLD: if (!rockchip_pinconf_pull_valid(info->ctrl, param)) return -ENOTSUPP; @@ -646,6 +722,7 @@ static int rockchip_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, case PIN_CONFIG_BIAS_PULL_UP: case PIN_CONFIG_BIAS_PULL_DOWN: case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: + case PIN_CONFIG_BIAS_BUS_HOLD: if (!rockchip_pinconf_pull_valid(info->ctrl, param)) return -ENOTSUPP; @@ -669,6 +746,7 @@ static const struct pinconf_ops rockchip_pinconf_ops = { static const struct of_device_id rockchip_bank_match[] = { { .compatible = "rockchip,gpio-bank" }, + { .compatible = "rockchip,rk3188-gpio-bank0" }, {}, }; @@ -1220,7 +1298,25 @@ static int rockchip_get_bank_data(struct rockchip_pin_bank *bank, if (IS_ERR(bank->reg_base)) return PTR_ERR(bank->reg_base); - bank->bank_type = COMMON_BANK; + /* + * special case, where parts of the pull setting-registers are + * part of the PMU register space + */ + if (of_device_is_compatible(bank->of_node, + "rockchip,rk3188-gpio-bank0")) { + bank->bank_type = RK3188_BANK0; + + if (of_address_to_resource(bank->of_node, 1, &res)) { + dev_err(dev, "cannot find IO resource for bank\n"); + return -ENOENT; + } + + bank->reg_pull = devm_ioremap_resource(dev, &res); + if (IS_ERR(bank->reg_pull)) + return PTR_ERR(bank->reg_pull); + } else { + bank->bank_type = COMMON_BANK; + } bank->irq = irq_of_parse_and_map(bank->of_node, 0); @@ -1306,6 +1402,14 @@ static int rockchip_pinctrl_probe(struct platform_device *pdev) if (IS_ERR(info->reg_base)) return PTR_ERR(info->reg_base); + /* The RK3188 has its pull registers in a separate place */ + if (ctrl->type == RK3188) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + info->reg_pull = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(info->reg_base)) + return PTR_ERR(info->reg_base); + } + ret = rockchip_gpiolib_register(pdev, info); if (ret) return ret; @@ -1383,6 +1487,7 @@ static struct rockchip_pin_ctrl rk3188_pin_ctrl = { .label = "RK3188-GPIO", .type = RK3188, .mux_offset = 0x68, + .pull_calc_reg = rk3188_calc_pull_reg_and_bit, }; static const struct of_device_id rockchip_pinctrl_dt_match[] = { -- cgit v0.10.2 From 5a92750133ffd998e92e4f7ed874fae61d67aeed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Heiko=20St=C3=BCbner?= Date: Wed, 16 Oct 2013 01:09:08 +0200 Subject: pinctrl: rockchip: emulate both edge triggered interrupts The gpio interrupt controller on Rockchip socs can do edge triggers only for single edges but not both. Nevertheless a lot of gpio users rely on the availability of both-edge triggered interrupts - i.e. gpio-keys. Therefore implement a solution similar to pinctrl-coh901 re-setting the triggering edge depending on the gpio value in the interrupt demuxer. Signed-off-by: Heiko Stuebner Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c index f5e53a7..e939c28 100644 --- a/drivers/pinctrl/pinctrl-rockchip.c +++ b/drivers/pinctrl/pinctrl-rockchip.c @@ -101,7 +101,7 @@ struct rockchip_pin_bank { struct gpio_chip gpio_chip; struct pinctrl_gpio_range grange; spinlock_t slock; - + u32 toggle_edge_mode; }; #define PIN_BANK(id, pins, label) \ @@ -1078,7 +1078,9 @@ static void rockchip_irq_demux(unsigned int irq, struct irq_desc *desc) { struct irq_chip *chip = irq_get_chip(irq); struct rockchip_pin_bank *bank = irq_get_handler_data(irq); + u32 polarity = 0, data = 0; u32 pend; + bool edge_changed = false; dev_dbg(bank->drvdata->dev, "got irq for bank %s\n", bank->name); @@ -1086,6 +1088,12 @@ static void rockchip_irq_demux(unsigned int irq, struct irq_desc *desc) pend = readl_relaxed(bank->reg_base + GPIO_INT_STATUS); + if (bank->toggle_edge_mode) { + polarity = readl_relaxed(bank->reg_base + + GPIO_INT_POLARITY); + data = readl_relaxed(bank->reg_base + GPIO_EXT_PORT); + } + while (pend) { unsigned int virq; @@ -1100,9 +1108,30 @@ static void rockchip_irq_demux(unsigned int irq, struct irq_desc *desc) dev_dbg(bank->drvdata->dev, "handling irq %d\n", irq); + /* + * Triggering IRQ on both rising and falling edge + * needs manual intervention. + */ + if (bank->toggle_edge_mode & BIT(irq)) { + if (data & BIT(irq)) + polarity &= ~BIT(irq); + else + polarity |= BIT(irq); + + edge_changed = true; + } + generic_handle_irq(virq); } + if (bank->toggle_edge_mode && edge_changed) { + /* Interrupt params should only be set with ints disabled */ + data = readl_relaxed(bank->reg_base + GPIO_INTEN); + writel_relaxed(0, bank->reg_base + GPIO_INTEN); + writel(polarity, bank->reg_base + GPIO_INT_POLARITY); + writel(data, bank->reg_base + GPIO_INTEN); + } + chained_irq_exit(chip, desc); } @@ -1115,6 +1144,12 @@ static int rockchip_irq_set_type(struct irq_data *d, unsigned int type) u32 level; u32 data; + /* make sure the pin is configured as gpio input */ + rockchip_set_mux(bank, d->hwirq, RK_FUNC_GPIO); + data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR); + data &= ~mask; + writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR); + if (type & IRQ_TYPE_EDGE_BOTH) __irq_set_handler_locked(d->irq, handle_edge_irq); else @@ -1126,19 +1161,37 @@ static int rockchip_irq_set_type(struct irq_data *d, unsigned int type) polarity = readl_relaxed(gc->reg_base + GPIO_INT_POLARITY); switch (type) { + case IRQ_TYPE_EDGE_BOTH: + bank->toggle_edge_mode |= mask; + level |= mask; + + /* + * Determine gpio state. If 1 next interrupt should be falling + * otherwise rising. + */ + data = readl(bank->reg_base + GPIO_EXT_PORT); + if (data & mask) + polarity &= ~mask; + else + polarity |= mask; + break; case IRQ_TYPE_EDGE_RISING: + bank->toggle_edge_mode &= ~mask; level |= mask; polarity |= mask; break; case IRQ_TYPE_EDGE_FALLING: + bank->toggle_edge_mode &= ~mask; level |= mask; polarity &= ~mask; break; case IRQ_TYPE_LEVEL_HIGH: + bank->toggle_edge_mode &= ~mask; level &= ~mask; polarity |= mask; break; case IRQ_TYPE_LEVEL_LOW: + bank->toggle_edge_mode &= ~mask; level &= ~mask; polarity &= ~mask; break; @@ -1152,12 +1205,6 @@ static int rockchip_irq_set_type(struct irq_data *d, unsigned int type) irq_gc_unlock(gc); - /* make sure the pin is configured as gpio input */ - rockchip_set_mux(bank, d->hwirq, RK_FUNC_GPIO); - data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR); - data &= ~mask; - writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR); - return 0; } -- cgit v0.10.2 From fe61052ae97a33c229a3ae7ad29e0c9b9ccada02 Mon Sep 17 00:00:00 2001 From: Christian Ruppert Date: Wed, 16 Oct 2013 14:56:54 +0200 Subject: pinctrl: add documentation for pinctrl_get_group_pins() This patch adds a short description of how to use the newly added pinctrl_get_group_pins function to the pinctrl documentation. Signed-off-by: Christian Ruppert Signed-off-by: Linus Walleij diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt index c0ffd30..a7929cb 100644 --- a/Documentation/pinctrl.txt +++ b/Documentation/pinctrl.txt @@ -358,7 +358,12 @@ static struct pinctrl_gpio_range gpio_range = { .gc = &chip; }; -In this case the pin_base property will be ignored. +In this case the pin_base property will be ignored. If the name of a pin +group is known, the pins and npins elements of the above structure can be +initialised using the function pinctrl_get_group_pins(), e.g. for pin +group "foo": + +pinctrl_get_group_pins(pctl, "foo", &gpio_range.pins, &gpio_range.npins); When GPIO-specific functions in the pin control subsystem are called, these ranges will be used to look up the appropriate pin controller by inspecting -- cgit v0.10.2 From 9536c8d2da8059b00775bd9c5a84816b608cf6f4 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 15 Oct 2013 12:14:04 +0200 Subject: perf/x86: Optimize intel_pmu_pebs_fixup_ip() There's been reports of high NMI handler overhead, highlighted by such kernel messages: [ 3697.380195] perf samples too long (10009 > 10000), lowering kernel.perf_event_max_sample_rate to 13000 [ 3697.389509] INFO: NMI handler (perf_event_nmi_handler) took too long to run: 9.331 msecs Don Zickus analyzed the source of the overhead and reported: > While there are a few places that are causing latencies, for now I focused on > the longest one first. It seems to be 'copy_user_from_nmi' > > intel_pmu_handle_irq -> > intel_pmu_drain_pebs_nhm -> > __intel_pmu_drain_pebs_nhm -> > __intel_pmu_pebs_event -> > intel_pmu_pebs_fixup_ip -> > copy_from_user_nmi > > In intel_pmu_pebs_fixup_ip(), if the while-loop goes over 50, the sum of > all the copy_from_user_nmi latencies seems to go over 1,000,000 cycles > (there are some cases where only 10 iterations are needed to go that high > too, but in generall over 50 or so). At this point copy_user_from_nmi > seems to account for over 90% of the nmi latency. The solution to that is to avoid having to call copy_from_user_nmi() for every instruction. Since we already limit the max basic block size, we can easily pre-allocate a piece of memory to copy the entire thing into in one go. Don reported this test result: > Your patch made a huge difference in improvement. The > copy_from_user_nmi() no longer hits the million of cycles. I still > have a batch of 100,000-300,000 cycles. My longest NMI paths used > to be dominated by copy_from_user_nmi, now it is not (I have to dig > up the new hot path). Reported-and-tested-by: Don Zickus Cc: jmario@redhat.com Cc: acme@infradead.org Cc: dave.hansen@linux.intel.com Cc: eranian@google.com Cc: Linus Torvalds Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20131016105755.GX10651@twins.programming.kicks-ass.net Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index 32e9ed8..c1760ff 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -12,6 +12,7 @@ #define BTS_BUFFER_SIZE (PAGE_SIZE << 4) #define PEBS_BUFFER_SIZE PAGE_SIZE +#define PEBS_FIXUP_SIZE PAGE_SIZE /* * pebs_record_32 for p4 and core not supported @@ -228,12 +229,14 @@ void fini_debug_store_on_cpu(int cpu) wrmsr_on_cpu(cpu, MSR_IA32_DS_AREA, 0, 0); } +static DEFINE_PER_CPU(void *, insn_buffer); + static int alloc_pebs_buffer(int cpu) { struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds; int node = cpu_to_node(cpu); int max, thresh = 1; /* always use a single PEBS record */ - void *buffer; + void *buffer, *ibuffer; if (!x86_pmu.pebs) return 0; @@ -242,6 +245,19 @@ static int alloc_pebs_buffer(int cpu) if (unlikely(!buffer)) return -ENOMEM; + /* + * HSW+ already provides us the eventing ip; no need to allocate this + * buffer then. + */ + if (x86_pmu.intel_cap.pebs_format < 2) { + ibuffer = kzalloc_node(PEBS_FIXUP_SIZE, GFP_KERNEL, node); + if (!ibuffer) { + kfree(buffer); + return -ENOMEM; + } + per_cpu(insn_buffer, cpu) = ibuffer; + } + max = PEBS_BUFFER_SIZE / x86_pmu.pebs_record_size; ds->pebs_buffer_base = (u64)(unsigned long)buffer; @@ -262,6 +278,9 @@ static void release_pebs_buffer(int cpu) if (!ds || !x86_pmu.pebs) return; + kfree(per_cpu(insn_buffer, cpu)); + per_cpu(insn_buffer, cpu) = NULL; + kfree((void *)(unsigned long)ds->pebs_buffer_base); ds->pebs_buffer_base = 0; } @@ -729,6 +748,7 @@ static int intel_pmu_pebs_fixup_ip(struct pt_regs *regs) unsigned long old_to, to = cpuc->lbr_entries[0].to; unsigned long ip = regs->ip; int is_64bit = 0; + void *kaddr; /* * We don't need to fixup if the PEBS assist is fault like @@ -752,7 +772,7 @@ static int intel_pmu_pebs_fixup_ip(struct pt_regs *regs) * unsigned math, either ip is before the start (impossible) or * the basic block is larger than 1 page (sanity) */ - if ((ip - to) > PAGE_SIZE) + if ((ip - to) > PEBS_FIXUP_SIZE) return 0; /* @@ -763,29 +783,33 @@ static int intel_pmu_pebs_fixup_ip(struct pt_regs *regs) return 1; } + if (!kernel_ip(ip)) { + int size, bytes; + u8 *buf = this_cpu_read(insn_buffer); + + size = ip - to; /* Must fit our buffer, see above */ + bytes = copy_from_user_nmi(buf, (void __user *)to, size); + if (bytes != size) + return 0; + + kaddr = buf; + } else { + kaddr = (void *)to; + } + do { struct insn insn; - u8 buf[MAX_INSN_SIZE]; - void *kaddr; old_to = to; - if (!kernel_ip(ip)) { - int bytes, size = MAX_INSN_SIZE; - - bytes = copy_from_user_nmi(buf, (void __user *)to, size); - if (bytes != size) - return 0; - - kaddr = buf; - } else - kaddr = (void *)to; #ifdef CONFIG_X86_64 is_64bit = kernel_ip(to) || !test_thread_flag(TIF_IA32); #endif insn_init(&insn, kaddr, is_64bit); insn_get_length(&insn); + to += insn.length; + kaddr += insn.length; } while (to < ip); if (to == ip) { -- cgit v0.10.2 From 97119f37bbebbab852899bd37ed52b80396728f9 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 27 Sep 2013 17:34:10 -0300 Subject: perf trace: Split fd -> pathname array handling So that the part that grows the array as needed is untied from the code that reads the /proc/pid/fd symlink and can be used for the vfs_getname hook that will set the fd -> path translation too, when available. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-ydo5rumyv9hdc1vsfmqamugs@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index d0f91fe..763bef4 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -975,30 +975,9 @@ struct trace { double runtime_ms; }; -static int thread__read_fd_path(struct thread *thread, int fd) +static int trace__set_fd_pathname(struct thread *thread, int fd, const char *pathname) { struct thread_trace *ttrace = thread->priv; - char linkname[PATH_MAX], pathname[PATH_MAX]; - struct stat st; - int ret; - - if (thread->pid_ == thread->tid) { - scnprintf(linkname, sizeof(linkname), - "/proc/%d/fd/%d", thread->pid_, fd); - } else { - scnprintf(linkname, sizeof(linkname), - "/proc/%d/task/%d/fd/%d", thread->pid_, thread->tid, fd); - } - - if (lstat(linkname, &st) < 0 || st.st_size + 1 > (off_t)sizeof(pathname)) - return -1; - - ret = readlink(linkname, pathname, sizeof(pathname)); - - if (ret < 0 || ret > st.st_size) - return -1; - - pathname[ret] = '\0'; if (fd > ttrace->paths.max) { char **npath = realloc(ttrace->paths.table, (fd + 1) * sizeof(char *)); @@ -1022,6 +1001,32 @@ static int thread__read_fd_path(struct thread *thread, int fd) return ttrace->paths.table[fd] != NULL ? 0 : -1; } +static int thread__read_fd_path(struct thread *thread, int fd) +{ + char linkname[PATH_MAX], pathname[PATH_MAX]; + struct stat st; + int ret; + + if (thread->pid_ == thread->tid) { + scnprintf(linkname, sizeof(linkname), + "/proc/%d/fd/%d", thread->pid_, fd); + } else { + scnprintf(linkname, sizeof(linkname), + "/proc/%d/task/%d/fd/%d", thread->pid_, thread->tid, fd); + } + + if (lstat(linkname, &st) < 0 || st.st_size + 1 > (off_t)sizeof(pathname)) + return -1; + + ret = readlink(linkname, pathname, sizeof(pathname)); + + if (ret < 0 || ret > st.st_size) + return -1; + + pathname[ret] = '\0'; + return trace__set_fd_pathname(thread, fd, pathname); +} + static const char *thread__fd_path(struct thread *thread, int fd, bool live) { struct thread_trace *ttrace = thread->priv; -- cgit v0.10.2 From c522739d72a341a3e74a369ce6298b9412813d3f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 27 Sep 2013 18:06:19 -0300 Subject: perf trace: Use vfs_getname hook if available Initially it tries to find a probe:vfs_getname that should be setup with: perf probe 'vfs_getname=getname_flags:65 pathname=result->name:string' or with slight changes to cope with code flux in the getname_flags code. In the future, if a "vfs:getname" tracepoint becomes available, then it will be preferred. This is not strictly required and more expensive method of reading the /proc/pid/fd/ symlink will be used when the fd->path array entry is not populated by a previous vfs_getname + open syscall ret sequence. As with any other 'perf probe' probe the setup must be done just once and the probe will be left inactive, waiting for users, be it 'perf trace' of any other tool. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-ujg8se8glq5izmu8cdkq15po@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index 54139c6..7b0497f 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt @@ -97,6 +97,10 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs. Show a summary of syscalls by thread with min, max, and average times (in msec) and relative stddev. +--tool_stats:: + Show tool stats such as number of times fd->pathname was discovered thru + hooking the open syscall return + vfs_getname or via reading /proc/pid/fd, etc. + SEE ALSO -------- linkperf:perf-record[1], linkperf:perf-script[1] diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 763bef4..86d2b1f 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -951,7 +951,10 @@ fail: struct trace { struct perf_tool tool; - int audit_machine; + struct { + int machine; + int open_id; + } audit; struct { int max; struct syscall *table; @@ -965,14 +968,19 @@ struct trace { struct strlist *ev_qualifier; bool not_ev_qualifier; bool live; + const char *last_vfs_getname; struct intlist *tid_list; struct intlist *pid_list; bool sched; bool multiple_threads; bool summary; bool show_comm; + bool show_tool_stats; double duration_filter; double runtime_ms; + struct { + u64 vfs_getname, proc_getname; + } stats; }; static int trace__set_fd_pathname(struct thread *thread, int fd, const char *pathname) @@ -1027,7 +1035,8 @@ static int thread__read_fd_path(struct thread *thread, int fd) return trace__set_fd_pathname(thread, fd, pathname); } -static const char *thread__fd_path(struct thread *thread, int fd, bool live) +static const char *thread__fd_path(struct thread *thread, int fd, + struct trace *trace) { struct thread_trace *ttrace = thread->priv; @@ -1037,9 +1046,13 @@ static const char *thread__fd_path(struct thread *thread, int fd, bool live) if (fd < 0) return NULL; - if ((fd > ttrace->paths.max || ttrace->paths.table[fd] == NULL) && - (!live || thread__read_fd_path(thread, fd))) - return NULL; + if ((fd > ttrace->paths.max || ttrace->paths.table[fd] == NULL)) + if (!trace->live) + return NULL; + ++trace->stats.proc_getname; + if (thread__read_fd_path(thread, fd)) { + return NULL; + } return ttrace->paths.table[fd]; } @@ -1049,7 +1062,7 @@ static size_t syscall_arg__scnprintf_fd(char *bf, size_t size, { int fd = arg->val; size_t printed = scnprintf(bf, size, "%d", fd); - const char *path = thread__fd_path(arg->thread, fd, arg->trace->live); + const char *path = thread__fd_path(arg->thread, fd, arg->trace); if (path) printed += scnprintf(bf + printed, size - printed, "<%s>", path); @@ -1186,7 +1199,7 @@ static int trace__read_syscall_info(struct trace *trace, int id) { char tp_name[128]; struct syscall *sc; - const char *name = audit_syscall_to_name(id, trace->audit_machine); + const char *name = audit_syscall_to_name(id, trace->audit.machine); if (name == NULL) return -1; @@ -1450,6 +1463,12 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, ret = perf_evsel__intval(evsel, sample, "ret"); + if (id == trace->audit.open_id && ret >= 0 && trace->last_vfs_getname) { + trace__set_fd_pathname(thread, ret, trace->last_vfs_getname); + trace->last_vfs_getname = NULL; + ++trace->stats.vfs_getname; + } + ttrace = thread->priv; ttrace->exit_time = sample->time; @@ -1494,6 +1513,13 @@ out: return 0; } +static int trace__vfs_getname(struct trace *trace, struct perf_evsel *evsel, + struct perf_sample *sample) +{ + trace->last_vfs_getname = perf_evsel__rawptr(evsel, sample, "pathname"); + return 0; +} + static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evsel, struct perf_sample *sample) { @@ -1616,6 +1642,22 @@ static int trace__record(int argc, const char **argv) static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp); +static void perf_evlist__add_vfs_getname(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = perf_evsel__newtp("probe", "vfs_getname", + evlist->nr_entries); + if (evsel == NULL) + return; + + if (perf_evsel__field(evsel, "pathname") == NULL) { + perf_evsel__delete(evsel); + return; + } + + evsel->handler.func = trace__vfs_getname; + perf_evlist__add(evlist, evsel); +} + static int trace__run(struct trace *trace, int argc, const char **argv) { struct perf_evlist *evlist = perf_evlist__new(); @@ -1635,6 +1677,8 @@ static int trace__run(struct trace *trace, int argc, const char **argv) perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_exit", trace__sys_exit)) goto out_error_tp; + perf_evlist__add_vfs_getname(evlist); + if (trace->sched && perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime", trace__sched_stat_runtime)) @@ -1741,12 +1785,22 @@ again: if (done) perf_evlist__disable(evlist); - - goto again; + else + goto again; out_unmap_evlist: - if (!err && trace->summary) - trace__fprintf_thread_summary(trace, trace->output); + if (!err) { + if (trace->summary) + trace__fprintf_thread_summary(trace, trace->output); + + if (trace->show_tool_stats) { + fprintf(trace->output, "Stats:\n " + " vfs_getname : %" PRIu64 "\n" + " proc_getname: %" PRIu64 "\n", + trace->stats.vfs_getname, + trace->stats.proc_getname); + } + } perf_evlist__munmap(evlist); out_close_evlist: @@ -1788,6 +1842,7 @@ static int trace__replay(struct trace *trace) const struct perf_evsel_str_handler handlers[] = { { "raw_syscalls:sys_enter", trace__sys_enter, }, { "raw_syscalls:sys_exit", trace__sys_exit, }, + { "probe:vfs_getname", trace__vfs_getname, }, }; struct perf_session *session; @@ -1997,7 +2052,10 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) NULL }; struct trace trace = { - .audit_machine = audit_detect_machine(), + .audit = { + .machine = audit_detect_machine(), + .open_id = audit_name_to_syscall("open", trace.audit.machine), + }, .syscalls = { . max = -1, }, @@ -2019,6 +2077,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) const struct option trace_options[] = { OPT_BOOLEAN(0, "comm", &trace.show_comm, "show the thread COMM next to its id"), + OPT_BOOLEAN(0, "tool_stats", &trace.show_tool_stats, "show tool stats"), OPT_STRING('e', "expr", &ev_qualifier_str, "expr", "list of events to trace"), OPT_STRING('o', "output", &output_name, "file", "output file name"), -- cgit v0.10.2 From a7ea1b7249adc8c090a0b277ab5f3737ee4023c1 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 11 Oct 2013 17:23:56 +0530 Subject: ASoC: cs4271: Include linux/of.h header 'of_match_ptr' is defined in linux/of.h. Include it explicitly. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index a20f1bb..f6e9534 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include -- cgit v0.10.2 From 193a47162c93afa09fffd04a04443f14d402c606 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 11 Oct 2013 17:23:57 +0530 Subject: ASoC: pcm1681: Include linux/of.h header 'of_match_ptr' is defined in linux/of.h. Include it explicitly. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c index 651ce09..54ea15b 100644 --- a/sound/soc/codecs/pcm1681.c +++ b/sound/soc/codecs/pcm1681.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include -- cgit v0.10.2 From 4b2fa5121c758db6fe9ed4931b54e390661395de Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 11 Oct 2013 17:23:58 +0530 Subject: ASoC: pcm1792a: Include linux/of.h header 'of_match_ptr' is defined in linux/of.h. Include it explicitly. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/pcm1792a.c b/sound/soc/codecs/pcm1792a.c index 2a8eccf..6f14c50 100644 --- a/sound/soc/codecs/pcm1792a.c +++ b/sound/soc/codecs/pcm1792a.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "pcm1792a.h" -- cgit v0.10.2 From 285d00c11b0a8d0ef63c176f88caab5071c9e80d Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 11 Oct 2013 17:23:59 +0530 Subject: ASoC: tas5086: Include linux/of.h header 'of_match_ptr' is defined in linux/of.h. Include it explicitly. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c index 2996d2e..fe4d29d 100644 --- a/sound/soc/codecs/tas5086.c +++ b/sound/soc/codecs/tas5086.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include -- cgit v0.10.2 From b3b70786ec18ef3088b55b76258bbd48d75aee08 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 11 Oct 2013 17:24:00 +0530 Subject: ASoC: tlv320aic3x: Include linux/of.h header 'of_match_ptr' is defined in linux/of.h. Include it explicitly. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 892c108..f8b9fa6 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include -- cgit v0.10.2 From 8aa99652cd52af07f4fa49fc50d78ede48c9c9b3 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 11 Oct 2013 17:24:01 +0530 Subject: ASoC: atmel: Include linux/of.h header 'of_match_ptr' is defined in linux/of.h. Include it explicitly. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c index 802717e..f15bff1 100644 --- a/sound/soc/atmel/sam9g20_wm8731.c +++ b/sound/soc/atmel/sam9g20_wm8731.c @@ -37,6 +37,7 @@ #include #include #include +#include #include -- cgit v0.10.2 From ecc77773d3b5344394d0a126ab06fe51dac419b8 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 11 Oct 2013 17:21:53 +0530 Subject: spi: gpio: Include linux/of.h header 'of_match_ptr' is defined in linux/of.h. Include it explicitly. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index 68b69fe..22ef6e1 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include -- cgit v0.10.2 From a1829d2b76ae70e8f15fcf60aba5b703d46d66d9 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Fri, 11 Oct 2013 13:53:59 +0300 Subject: spi: Add missing newline to dev_ prints in drivers Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-bfin-sport.c b/drivers/spi/spi-bfin-sport.c index 91921b5..7b2cda0 100644 --- a/drivers/spi/spi-bfin-sport.c +++ b/drivers/spi/spi-bfin-sport.c @@ -592,7 +592,7 @@ bfin_sport_spi_setup(struct spi_device *spi) */ if (chip_info->ctl_reg || chip_info->enable_dma) { ret = -EINVAL; - dev_err(&spi->dev, "don't set ctl_reg/enable_dma fields"); + dev_err(&spi->dev, "don't set ctl_reg/enable_dma fields\n"); goto error; } chip->cs_chg_udelay = chip_info->cs_chg_udelay; diff --git a/drivers/spi/spi-bfin5xx.c b/drivers/spi/spi-bfin5xx.c index 45bdf73..18b892e 100644 --- a/drivers/spi/spi-bfin5xx.c +++ b/drivers/spi/spi-bfin5xx.c @@ -524,7 +524,7 @@ static irqreturn_t bfin_spi_dma_irq_handler(int irq, void *dev_id) timeout = jiffies + HZ; while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_SPIF)) if (!time_before(jiffies, timeout)) { - dev_warn(&drv_data->pdev->dev, "timeout waiting for SPIF"); + dev_warn(&drv_data->pdev->dev, "timeout waiting for SPIF\n"); break; } else cpu_relax(); @@ -1052,7 +1052,7 @@ static int bfin_spi_setup(struct spi_device *spi) if (!(spi->mode & SPI_CPHA)) dev_warn(&spi->dev, "Warning: SPI CPHA not set:" " Slave Select not under software control!\n" - " See Documentation/blackfin/bfin-spi-notes.txt"); + " See Documentation/blackfin/bfin-spi-notes.txt\n"); chip->flag = (1 << spi->chip_select) << 8; } else diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c index d22c00a..b57a341 100644 --- a/drivers/spi/spi-ep93xx.c +++ b/drivers/spi/spi-ep93xx.c @@ -330,7 +330,7 @@ static int ep93xx_spi_chip_setup(const struct ep93xx_spi *espi, dev_dbg(&espi->pdev->dev, "setup: mode %d, cpsr %d, scr %d, dss %d\n", chip->spi->mode, div_cpsr, div_scr, dss); - dev_dbg(&espi->pdev->dev, "setup: cr0 %#x", cr0); + dev_dbg(&espi->pdev->dev, "setup: cr0 %#x\n", cr0); ep93xx_spi_write_u8(espi, SSPCPSR, div_cpsr); ep93xx_spi_write_u16(espi, SSPCR0, cr0); @@ -509,7 +509,7 @@ ep93xx_spi_dma_prepare(struct ep93xx_spi *espi, enum dma_transfer_direction dir) } if (WARN_ON(len)) { - dev_warn(&espi->pdev->dev, "len = %zu expected 0!", len); + dev_warn(&espi->pdev->dev, "len = %zu expected 0!\n", len); return ERR_PTR(-EINVAL); } diff --git a/drivers/spi/spi-fsl-cpm.c b/drivers/spi/spi-fsl-cpm.c index 07971e3..58630ed 100644 --- a/drivers/spi/spi-fsl-cpm.c +++ b/drivers/spi/spi-fsl-cpm.c @@ -299,7 +299,7 @@ int fsl_spi_cpm_init(struct mpc8xxx_spi *mspi) switch (mspi->subblock) { default: - dev_warn(dev, "cell-index unspecified, assuming SPI1"); + dev_warn(dev, "cell-index unspecified, assuming SPI1\n"); /* fall through */ case 0: mspi->subblock = QE_CR_SUBBLOCK_SPI1; diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index ed4af47..15d3204 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -498,7 +498,7 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, ((u32 *)xfer->rx_buf)[elements++] = w; } else { int bytes_per_word = mcspi_bytes_per_word(word_len); - dev_err(&spi->dev, "DMA RX penultimate word empty"); + dev_err(&spi->dev, "DMA RX penultimate word empty\n"); count -= (bytes_per_word << 1); omap2_mcspi_set_enable(spi, 1); return count; @@ -516,7 +516,7 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, else /* word_len <= 32 */ ((u32 *)xfer->rx_buf)[elements] = w; } else { - dev_err(&spi->dev, "DMA RX last word empty"); + dev_err(&spi->dev, "DMA RX last word empty\n"); count -= mcspi_bytes_per_word(word_len); } omap2_mcspi_set_enable(spi, 1); -- cgit v0.10.2 From f6bd03a746271f298aa5bfb6e049b245757efaed Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Fri, 11 Oct 2013 13:54:00 +0300 Subject: spi: Don't break user-visible strings to multiple source lines in drivers User-visible strings are more difficult to grep from sources if they are separated to multiple source lines. This is worse than over 80 columns long line code style violation. Fix this by making those to single-line strings or by breaking them between variables. While at there, convert if (printk_ratelimit()) dev_warn() to use dev_warn_ratelimited in spi-pxa2xx.c. Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index d4ac60b..fb61f59 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -1401,8 +1401,8 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg) asd = spi->controller_state; bits = (asd->csr >> 4) & 0xf; if (bits != xfer->bits_per_word - 8) { - dev_dbg(&spi->dev, "you can't yet change " - "bits_per_word in transfers\n"); + dev_dbg(&spi->dev, + "you can't yet change bits_per_word in transfers\n"); return -ENOPROTOOPT; } } diff --git a/drivers/spi/spi-bfin5xx.c b/drivers/spi/spi-bfin5xx.c index 18b892e..b70a2bd 100644 --- a/drivers/spi/spi-bfin5xx.c +++ b/drivers/spi/spi-bfin5xx.c @@ -913,8 +913,9 @@ static void bfin_spi_pump_messages(struct work_struct *work) drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next, struct spi_transfer, transfer_list); - dev_dbg(&drv_data->pdev->dev, "got a message to pump, " - "state is set to: baud %d, flag 0x%x, ctl 0x%x\n", + dev_dbg(&drv_data->pdev->dev, + "got a message to pump, state is set to: baud " + "%d, flag 0x%x, ctl 0x%x\n", drv_data->cur_chip->baud, drv_data->cur_chip->flag, drv_data->cur_chip->ctl_reg); @@ -1013,8 +1014,8 @@ static int bfin_spi_setup(struct spi_device *spi) * but let's assume (for now) they do. */ if (chip_info->ctl_reg & ~bfin_ctl_reg) { - dev_err(&spi->dev, "do not set bits in ctl_reg " - "that the SPI framework manages\n"); + dev_err(&spi->dev, + "do not set bits in ctl_reg that the SPI framework manages\n"); goto error; } chip->enable_dma = chip_info->enable_dma != 0 @@ -1050,17 +1051,17 @@ static int bfin_spi_setup(struct spi_device *spi) chip->chip_select_num = spi->chip_select; if (chip->chip_select_num < MAX_CTRL_CS) { if (!(spi->mode & SPI_CPHA)) - dev_warn(&spi->dev, "Warning: SPI CPHA not set:" - " Slave Select not under software control!\n" - " See Documentation/blackfin/bfin-spi-notes.txt\n"); + dev_warn(&spi->dev, + "Warning: SPI CPHA not set: Slave Select not under software control!\n" + "See Documentation/blackfin/bfin-spi-notes.txt\n"); chip->flag = (1 << spi->chip_select) << 8; } else chip->cs_gpio = chip->chip_select_num - MAX_CTRL_CS; if (chip->enable_dma && chip->pio_interrupt) { - dev_err(&spi->dev, "enable_dma is set, " - "do not set pio_interrupt\n"); + dev_err(&spi->dev, + "enable_dma is set, do not set pio_interrupt\n"); goto error; } /* diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 79c958e..b897c4ad 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -870,8 +870,8 @@ void dw_spi_remove_host(struct dw_spi *dws) /* Remove the queue */ status = destroy_queue(dws); if (status != 0) - dev_err(&dws->master->dev, "dw_spi_remove: workqueue will not " - "complete, message memory not freed\n"); + dev_err(&dws->master->dev, + "dw_spi_remove: workqueue will not complete, message memory not freed\n"); if (dws->dma_ops && dws->dma_ops->dma_exit) dws->dma_ops->dma_exit(dws); diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index b8f1103..43222d7 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -289,8 +289,8 @@ static void fsl_espi_do_trans(struct spi_message *m, if ((first->bits_per_word != t->bits_per_word) || (first->speed_hz != t->speed_hz)) { espi_trans->status = -EINVAL; - dev_err(mspi->dev, "bits_per_word/speed_hz should be" - " same for the same SPI transfer\n"); + dev_err(mspi->dev, + "bits_per_word/speed_hz should be same for the same SPI transfer\n"); return; } diff --git a/drivers/spi/spi-mpc52xx-psc.c b/drivers/spi/spi-mpc52xx-psc.c index 6e925dc..00ba910 100644 --- a/drivers/spi/spi-mpc52xx-psc.c +++ b/drivers/spi/spi-mpc52xx-psc.c @@ -383,8 +383,8 @@ static int mpc52xx_psc_spi_do_probe(struct device *dev, u32 regaddr, mps->irq = irq; if (pdata == NULL) { - dev_warn(dev, "probe called without platform data, no " - "cs_control function will be called\n"); + dev_warn(dev, + "probe called without platform data, no cs_control function will be called\n"); mps->cs_control = NULL; mps->sysclk = 0; master->bus_num = bus_num; diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index c1a5067..64e1682 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -573,8 +573,8 @@ static irqreturn_t ssp_int(int irq, void *dev_id) write_SSTO(0, reg); write_SSSR_CS(drv_data, drv_data->clear_sr); - dev_err(&drv_data->pdev->dev, "bad message state " - "in interrupt handler\n"); + dev_err(&drv_data->pdev->dev, + "bad message state in interrupt handler\n"); /* Never fail */ return IRQ_HANDLED; @@ -651,8 +651,8 @@ static void pump_transfers(unsigned long data) if (message->is_dma_mapped || transfer->rx_dma || transfer->tx_dma) { dev_err(&drv_data->pdev->dev, - "pump_transfers: mapped transfer length " - "of %u is greater than %d\n", + "pump_transfers: mapped transfer length of " + "%u is greater than %d\n", transfer->len, MAX_DMA_LEN); message->status = -EINVAL; giveback(drv_data); @@ -660,11 +660,10 @@ static void pump_transfers(unsigned long data) } /* warn ... we force this to PIO mode */ - if (printk_ratelimit()) - dev_warn(&message->spi->dev, "pump_transfers: " - "DMA disabled for transfer length %ld " - "greater than %d\n", - (long)drv_data->len, MAX_DMA_LEN); + dev_warn_ratelimited(&message->spi->dev, + "pump_transfers: DMA disabled for transfer length %ld " + "greater than %d\n", + (long)drv_data->len, MAX_DMA_LEN); } /* Setup the transfer state based on the type of transfer */ @@ -726,11 +725,8 @@ static void pump_transfers(unsigned long data) message->spi, bits, &dma_burst, &dma_thresh)) - if (printk_ratelimit()) - dev_warn(&message->spi->dev, - "pump_transfers: " - "DMA burst size reduced to " - "match bits_per_word\n"); + dev_warn_ratelimited(&message->spi->dev, + "pump_transfers: DMA burst size reduced to match bits_per_word\n"); } cr0 = clk_div @@ -854,8 +850,8 @@ static int setup_cs(struct spi_device *spi, struct chip_data *chip, if (gpio_is_valid(chip_info->gpio_cs)) { err = gpio_request(chip_info->gpio_cs, "SPI_CS"); if (err) { - dev_err(&spi->dev, "failed to request chip select " - "GPIO%d\n", chip_info->gpio_cs); + dev_err(&spi->dev, "failed to request chip select GPIO%d\n", + chip_info->gpio_cs); return err; } @@ -899,8 +895,8 @@ static int setup(struct spi_device *spi) if (drv_data->ssp_type == CE4100_SSP) { if (spi->chip_select > 4) { - dev_err(&spi->dev, "failed setup: " - "cs number must not be > 4.\n"); + dev_err(&spi->dev, + "failed setup: cs number must not be > 4.\n"); kfree(chip); return -EINVAL; } @@ -956,8 +952,8 @@ static int setup(struct spi_device *spi) spi->bits_per_word, &chip->dma_burst_size, &chip->dma_threshold)) { - dev_warn(&spi->dev, "in setup: DMA burst size reduced " - "to match bits_per_word\n"); + dev_warn(&spi->dev, + "in setup: DMA burst size reduced to match bits_per_word\n"); } } diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index eaeeed5..14843ef 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -506,8 +506,8 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg) goto err_out; } - dev_dbg(&pspi->dev, "%s Transfer List not empty. " - "Transfer Speed is set.\n", __func__); + dev_dbg(&pspi->dev, + "%s Transfer List not empty. Transfer Speed is set.\n", __func__); spin_lock_irqsave(&data->lock, flags); /* validate Tx/Rx buffers and Transfer length */ @@ -526,8 +526,9 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg) goto err_return_spinlock; } - dev_dbg(&pspi->dev, "%s Tx/Rx buffer valid. Transfer length" - " valid\n", __func__); + dev_dbg(&pspi->dev, + "%s Tx/Rx buffer valid. Transfer length valid\n", + __func__); /* if baud rate has been specified validate the same */ if (transfer->speed_hz > PCH_MAX_BAUDRATE) @@ -1181,8 +1182,8 @@ static void pch_spi_process_messages(struct work_struct *pwork) spin_lock(&data->lock); /* check if suspend has been initiated;if yes flush queue */ if (data->board_dat->suspend_sts || (data->status == STATUS_EXITING)) { - dev_dbg(&data->master->dev, "%s suspend/remove initiated," - "flushing queue\n", __func__); + dev_dbg(&data->master->dev, + "%s suspend/remove initiated, flushing queue\n", __func__); list_for_each_entry_safe(pmsg, tmp, data->queue.next, queue) { pmsg->status = -EIO; -- cgit v0.10.2 From ba209f856380891a6ea6cdb07b2b068faccbff73 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 16 Oct 2013 11:57:33 -0300 Subject: perf trace: Improve event processing exit We need to differentiate SIGCHLD from SIGINT, the later should cause as immediate as possible exit, while the former should wait to process the events that may be perceived in the ring buffer after the SIGCHLD is handled. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-vf6n57ewm3mjy2sz6r491hus@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 86d2b1f..ec82895 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1098,10 +1098,12 @@ static size_t trace__fprintf_tstamp(struct trace *trace, u64 tstamp, FILE *fp) } static bool done = false; +static bool interrupted = false; -static void sig_handler(int sig __maybe_unused) +static void sig_handler(int sig) { done = true; + interrupted = sig == SIGINT; } static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thread, @@ -1771,24 +1773,23 @@ again: handler = evsel->handler.func; handler(trace, evsel, &sample); - if (done) - goto out_unmap_evlist; + if (interrupted) + goto out_disable; } } if (trace->nr_events == before) { - if (done) - goto out_unmap_evlist; + int timeout = done ? 100 : -1; - poll(evlist->pollfd, evlist->nr_fds, -1); + if (poll(evlist->pollfd, evlist->nr_fds, timeout) > 0) + goto again; + } else { + goto again; } - if (done) - perf_evlist__disable(evlist); - else - goto again; +out_disable: + perf_evlist__disable(evlist); -out_unmap_evlist: if (!err) { if (trace->summary) trace__fprintf_thread_summary(trace, trace->output); -- cgit v0.10.2 From 31407478a7b56187f9912eb6882a3c623365319f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 16 Oct 2013 13:22:35 +0100 Subject: spi/atmel: Convert to devm_ioremap_resource() This simplifies error handling. Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index ce4953f..118a938 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -1546,7 +1546,7 @@ static int atmel_spi_probe(struct platform_device *pdev) INIT_LIST_HEAD(&as->queue); as->pdev = pdev; - as->regs = ioremap(regs->start, resource_size(regs)); + as->regs = devm_ioremap_resource(&pdev->dev, regs); if (!as->regs) goto out_free_buffer; as->phybase = regs->start; @@ -1617,7 +1617,6 @@ out_free_dma: out_free_irq: free_irq(irq, master); out_unmap_regs: - iounmap(as->regs); out_free_buffer: if (!as->use_pdc) tasklet_kill(&as->tasklet); @@ -1669,7 +1668,6 @@ static int atmel_spi_remove(struct platform_device *pdev) clk_disable_unprepare(as->clk); clk_put(as->clk); free_irq(as->irq, master); - iounmap(as->regs); spi_unregister_master(master); -- cgit v0.10.2 From 6cf09b9d1762f40ba76a7c0271f00031abecc5dc Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Wed, 15 May 2013 11:21:01 +0200 Subject: powerpc: remove dependency on MV64360 The Kconfig entry that allows to "Distribute interrupts on all CPUs by default" has a (negative) dependency on MV64360. But that Kconfig symbol was removed in v2.6.27, which means that this dependency has evaluated to true ever since. It can be removed too. Signed-off-by: Paul Bolle Signed-off-by: Scott Wood diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 875d815..5f15468 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -420,7 +420,7 @@ config FA_DUMP config IRQ_ALL_CPUS bool "Distribute interrupts on all CPUs by default" - depends on SMP && !MV64360 + depends on SMP help This option gives the kernel permission to distribute IRQs across multiple CPUs. Saying N here will route all IRQs to the first -- cgit v0.10.2 From 6b310fc58db27318309a04621d3a4509f1c7845a Mon Sep 17 00:00:00 2001 From: Mihai Caraman Date: Mon, 1 Jul 2013 18:35:30 +0300 Subject: powerpc/booke64: Use common defines for AltiVec interrupts numbers On Book3E some SPE/FP/AltiVec interrupts share the same number. Use common defines to indentify these numbers. Signed-off-by: Mihai Caraman Signed-off-by: Scott Wood diff --git a/arch/powerpc/kernel/exceptions-64e.S b/arch/powerpc/kernel/exceptions-64e.S index 68d74b4..e775156 100644 --- a/arch/powerpc/kernel/exceptions-64e.S +++ b/arch/powerpc/kernel/exceptions-64e.S @@ -399,7 +399,7 @@ interrupt_end_book3e: /* Altivec Unavailable Interrupt */ START_EXCEPTION(altivec_unavailable); - NORMAL_EXCEPTION_PROLOG(0x200, BOOKE_INTERRUPT_ALTIVEC_UNAVAIL, + NORMAL_EXCEPTION_PROLOG(0x200, BOOKE_INTERRUPT_SPE_ALTIVEC_UNAVAIL, PROLOG_ADDITION_NONE) /* we can probably do a shorter exception entry for that one... */ EXCEPTION_COMMON(0x200, PACA_EXGEN, INTS_KEEP) @@ -421,7 +421,8 @@ END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) /* AltiVec Assist */ START_EXCEPTION(altivec_assist); - NORMAL_EXCEPTION_PROLOG(0x220, BOOKE_INTERRUPT_ALTIVEC_ASSIST, + NORMAL_EXCEPTION_PROLOG(0x220, + BOOKE_INTERRUPT_SPE_FP_DATA_ALTIVEC_ASSIST, PROLOG_ADDITION_NONE) EXCEPTION_COMMON(0x220, PACA_EXGEN, INTS_DISABLE) bl .save_nvgprs -- cgit v0.10.2 From c58ce397a62ec14b7b06c407a4173ed667e20d5f Mon Sep 17 00:00:00 2001 From: Mihai Caraman Date: Mon, 1 Jul 2013 18:35:31 +0300 Subject: powerpc/fsl-booke: Use common defines for SPE/FP interrupts numbers On Book3E some SPE/FP/AltiVec interrupts share the same number. Use common defines to indentify these numbers. Signed-off-by: Mihai Caraman [scottwood@freescale.com: fixed space-before-tab] Signed-off-by: Scott Wood diff --git a/arch/powerpc/kernel/head_fsl_booke.S b/arch/powerpc/kernel/head_fsl_booke.S index 289afaf..f45726a1 100644 --- a/arch/powerpc/kernel/head_fsl_booke.S +++ b/arch/powerpc/kernel/head_fsl_booke.S @@ -555,27 +555,27 @@ END_FTR_SECTION_IFSET(CPU_FTR_EMB_HV) #ifdef CONFIG_SPE /* SPE Unavailable */ START_EXCEPTION(SPEUnavailable) - NORMAL_EXCEPTION_PROLOG(SPE_UNAVAIL) + NORMAL_EXCEPTION_PROLOG(SPE_ALTIVEC_UNAVAIL) beq 1f bl load_up_spe b fast_exception_return 1: addi r3,r1,STACK_FRAME_OVERHEAD EXC_XFER_EE_LITE(0x2010, KernelSPE) #else - EXCEPTION(0x2020, SPE_UNAVAIL, SPEUnavailable, \ + EXCEPTION(0x2020, SPE_ALTIVEC_UNAVAIL, SPEUnavailable, \ unknown_exception, EXC_XFER_EE) #endif /* CONFIG_SPE */ /* SPE Floating Point Data */ #ifdef CONFIG_SPE - EXCEPTION(0x2030, SPE_FP_DATA, SPEFloatingPointData, \ - SPEFloatingPointException, EXC_XFER_EE); + EXCEPTION(0x2030, SPE_FP_DATA_ALTIVEC_ASSIST, SPEFloatingPointData, + SPEFloatingPointException, EXC_XFER_EE) /* SPE Floating Point Round */ EXCEPTION(0x2050, SPE_FP_ROUND, SPEFloatingPointRound, \ SPEFloatingPointRoundException, EXC_XFER_EE) #else - EXCEPTION(0x2040, SPE_FP_DATA, SPEFloatingPointData, \ + EXCEPTION(0x2040, SPE_FP_DATA_ALTIVEC_ASSIST, SPEFloatingPointData, unknown_exception, EXC_XFER_EE) EXCEPTION(0x2050, SPE_FP_ROUND, SPEFloatingPointRound, \ unknown_exception, EXC_XFER_EE) -- cgit v0.10.2 From 9863c28a2af90a56c088f5f6288d7f6d2c923c14 Mon Sep 17 00:00:00 2001 From: James Yang Date: Wed, 3 Jul 2013 16:26:47 -0500 Subject: powerpc: Emulate sync instruction variants Reserved fields of the sync instruction have been used for other instructions (e.g. lwsync). On processors that do not support variants of the sync instruction, emulate it by executing a sync to subsume the effect of the intended instruction. Signed-off-by: James Yang [scottwood@freescale.com: whitespace and subject line fix] Signed-off-by: Scott Wood diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h index ad5fcf5..442edee 100644 --- a/arch/powerpc/include/asm/ppc-opcode.h +++ b/arch/powerpc/include/asm/ppc-opcode.h @@ -143,6 +143,8 @@ #define PPC_INST_LSWX 0x7c00042a #define PPC_INST_LWARX 0x7c000028 #define PPC_INST_LWSYNC 0x7c2004ac +#define PPC_INST_SYNC 0x7c0004ac +#define PPC_INST_SYNC_MASK 0xfc0007fe #define PPC_INST_LXVD2X 0x7c000698 #define PPC_INST_MCRXR 0x7c000400 #define PPC_INST_MCRXR_MASK 0xfc0007fe diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index f0a6814..36a1f95 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -1018,6 +1018,13 @@ static int emulate_instruction(struct pt_regs *regs) return emulate_isel(regs, instword); } + /* Emulate sync instruction variants */ + if ((instword & PPC_INST_SYNC_MASK) == PPC_INST_SYNC) { + PPC_WARN_EMULATED(sync, regs); + asm volatile("sync"); + return 0; + } + #ifdef CONFIG_PPC64 /* Emulate the mfspr rD, DSCR. */ if ((((instword & PPC_INST_MFSPR_DSCR_USER_MASK) == -- cgit v0.10.2 From df231f280f0051e8de05a1f31c13b2ce2a761c3f Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 16 Oct 2013 13:25:33 -0600 Subject: gpio: tegra: use new gpio_lock_as_irq() API Whenever an IRQ is claimed or freed, call gpio_lock_as_irq() or gpio_unlock_as_irq() on the associated GPIO, to prevent that GPIO from being configured in a manner incompatible with an interrupt. Signed-off-by: Stephen Warren Reviewed-by: Javier Martinez Canillas Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 9a62672..cfd3b90 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -75,6 +75,7 @@ struct tegra_gpio_bank { #endif }; +static struct device *dev; static struct irq_domain *irq_domain; static void __iomem *regs; static u32 tegra_gpio_bank_count; @@ -205,6 +206,7 @@ static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type) int lvl_type; int val; unsigned long flags; + int ret; switch (type & IRQ_TYPE_SENSE_MASK) { case IRQ_TYPE_EDGE_RISING: @@ -231,6 +233,12 @@ static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type) return -EINVAL; } + ret = gpio_lock_as_irq(&tegra_gpio_chip, gpio); + if (ret) { + dev_err(dev, "unable to lock Tegra GPIO %d as IRQ\n", gpio); + return ret; + } + spin_lock_irqsave(&bank->lvl_lock[port], flags); val = tegra_gpio_readl(GPIO_INT_LVL(gpio)); @@ -251,6 +259,13 @@ static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type) return 0; } +static void tegra_gpio_irq_shutdown(struct irq_data *d) +{ + int gpio = d->hwirq; + + gpio_unlock_as_irq(&tegra_gpio_chip, gpio); +} + static void tegra_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) { struct tegra_gpio_bank *bank; @@ -368,6 +383,7 @@ static struct irq_chip tegra_gpio_irq_chip = { .irq_mask = tegra_gpio_irq_mask, .irq_unmask = tegra_gpio_irq_unmask, .irq_set_type = tegra_gpio_irq_set_type, + .irq_shutdown = tegra_gpio_irq_shutdown, #ifdef CONFIG_PM_SLEEP .irq_set_wake = tegra_gpio_irq_set_wake, #endif @@ -413,6 +429,8 @@ static int tegra_gpio_probe(struct platform_device *pdev) int i; int j; + dev = &pdev->dev; + match = of_match_device(tegra_gpio_of_match, &pdev->dev); if (!match) { dev_err(&pdev->dev, "Error: No device match found\n"); -- cgit v0.10.2 From 63fc184cde2d771affc2e7885c05a2792bae9f86 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 5 Oct 2013 12:23:38 +0100 Subject: spi/tegra20-slink: Crude refactoring to use core message parsing This is a half done conversion with minimal code reorganisation provided for bisection purposes. A further patch will move the first transfer preparation into tegra_slink_prepare_message(). The cs_change and udelay handling is removed, these should be implemented by the framework and in any case are buggy - the two fields should not be related and the cs_change handling appears to at best only work the first time it's used in a message. Signed-off-by: Mark Brown Tested-by: Stephen Warren diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index d1d2bff..3576fcb 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -196,6 +196,7 @@ struct tegra_slink_data { u32 rx_status; u32 status_reg; bool is_packed; + bool is_first_msg; unsigned long packed_size; u32 command_reg; @@ -823,55 +824,56 @@ static int tegra_slink_setup(struct spi_device *spi) return 0; } -static int tegra_slink_transfer_one_message(struct spi_master *master, - struct spi_message *msg) +static int tegra_slink_prepare_message(struct spi_master *master, + struct spi_message *msg) { - bool is_first_msg = true; struct tegra_slink_data *tspi = spi_master_get_devdata(master); - struct spi_transfer *xfer; - struct spi_device *spi = msg->spi; - int ret; - msg->status = 0; - msg->actual_length = 0; + tspi->is_first_msg = true; - list_for_each_entry(xfer, &msg->transfers, transfer_list) { - INIT_COMPLETION(tspi->xfer_completion); - ret = tegra_slink_start_transfer_one(spi, xfer, is_first_msg); - if (ret < 0) { - dev_err(tspi->dev, - "spi can not start transfer, err %d\n", ret); - goto exit; - } - is_first_msg = false; - ret = wait_for_completion_timeout(&tspi->xfer_completion, - SLINK_DMA_TIMEOUT); - if (WARN_ON(ret == 0)) { - dev_err(tspi->dev, - "spi trasfer timeout, err %d\n", ret); - ret = -EIO; - goto exit; - } + return 0; +} - if (tspi->tx_status || tspi->rx_status) { - dev_err(tspi->dev, "Error in Transfer\n"); - ret = -EIO; - goto exit; - } - msg->actual_length += xfer->len; - if (xfer->cs_change && xfer->delay_usecs) { - tegra_slink_writel(tspi, tspi->def_command_reg, - SLINK_COMMAND); - udelay(xfer->delay_usecs); - } +static int tegra_slink_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct tegra_slink_data *tspi = spi_master_get_devdata(master); + int ret; + + INIT_COMPLETION(tspi->xfer_completion); + ret = tegra_slink_start_transfer_one(spi, xfer, tspi->is_first_msg); + if (ret < 0) { + dev_err(tspi->dev, + "spi can not start transfer, err %d\n", ret); + return ret; } - ret = 0; -exit: + tspi->is_first_msg = false; + ret = wait_for_completion_timeout(&tspi->xfer_completion, + SLINK_DMA_TIMEOUT); + if (WARN_ON(ret == 0)) { + dev_err(tspi->dev, + "spi trasfer timeout, err %d\n", ret); + return -EIO; + } + + if (tspi->tx_status) + return tspi->tx_status; + if (tspi->rx_status) + return tspi->rx_status; + + return 0; +} + +static int tegra_slink_unprepare_message(struct spi_master *master, + struct spi_message *msg) +{ + struct tegra_slink_data *tspi = spi_master_get_devdata(master); + tegra_slink_writel(tspi, tspi->def_command_reg, SLINK_COMMAND); tegra_slink_writel(tspi, tspi->def_command2_reg, SLINK_COMMAND2); - msg->status = ret; - spi_finalize_current_message(master); - return ret; + + return 0; } static irqreturn_t handle_cpu_based_xfer(struct tegra_slink_data *tspi) @@ -1074,7 +1076,9 @@ static int tegra_slink_probe(struct platform_device *pdev) /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; master->setup = tegra_slink_setup; - master->transfer_one_message = tegra_slink_transfer_one_message; + master->prepare_message = tegra_slink_prepare_message; + master->transfer_one = tegra_slink_transfer_one; + master->unprepare_message = tegra_slink_unprepare_message; master->auto_runtime_pm = true; master->num_chipselect = MAX_CHIP_SELECT; master->bus_num = -1; -- cgit v0.10.2 From f178e3dec700d5f47cc9a282dd098a2a7422d6c7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 5 Oct 2013 12:30:42 +0100 Subject: spi/tegra20-slink: Move first transfer preparation to prepare_message This is more idiomatic for the factored out message processing and gives a small simplification of the code since we always set the per-transfer parameters in the same fashion. Signed-off-by: Mark Brown Tested-by: Stephen Warren diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index 3576fcb..14292c5 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -196,7 +196,6 @@ struct tegra_slink_data { u32 rx_status; u32 status_reg; bool is_packed; - bool is_first_msg; unsigned long packed_size; u32 command_reg; @@ -708,7 +707,7 @@ static void tegra_slink_deinit_dma_param(struct tegra_slink_data *tspi, } static int tegra_slink_start_transfer_one(struct spi_device *spi, - struct spi_transfer *t, bool is_first_of_msg) + struct spi_transfer *t) { struct tegra_slink_data *tspi = spi_master_get_devdata(spi->master); u32 speed; @@ -732,32 +731,12 @@ static int tegra_slink_start_transfer_one(struct spi_device *spi, tspi->curr_xfer = t; total_fifo_words = tegra_slink_calculate_curr_xfer_param(spi, tspi, t); - if (is_first_of_msg) { - tegra_slink_clear_status(tspi); + command = tspi->command_reg; + command &= ~SLINK_BIT_LENGTH(~0); + command |= SLINK_BIT_LENGTH(bits_per_word - 1); - command = tspi->def_command_reg; - command |= SLINK_BIT_LENGTH(bits_per_word - 1); - command |= SLINK_CS_SW | SLINK_CS_VALUE; - - command2 = tspi->def_command2_reg; - command2 |= SLINK_SS_EN_CS(spi->chip_select); - - command &= ~SLINK_MODES; - if (spi->mode & SPI_CPHA) - command |= SLINK_CK_SDA; - - if (spi->mode & SPI_CPOL) - command |= SLINK_IDLE_SCLK_DRIVE_HIGH; - else - command |= SLINK_IDLE_SCLK_DRIVE_LOW; - } else { - command = tspi->command_reg; - command &= ~SLINK_BIT_LENGTH(~0); - command |= SLINK_BIT_LENGTH(bits_per_word - 1); - - command2 = tspi->command2_reg; - command2 &= ~(SLINK_RXEN | SLINK_TXEN); - } + command2 = tspi->command2_reg; + command2 &= ~(SLINK_RXEN | SLINK_TXEN); tegra_slink_writel(tspi, command, SLINK_COMMAND); tspi->command_reg = command; @@ -828,8 +807,24 @@ static int tegra_slink_prepare_message(struct spi_master *master, struct spi_message *msg) { struct tegra_slink_data *tspi = spi_master_get_devdata(master); + struct spi_device *spi = msg->spi; - tspi->is_first_msg = true; + tegra_slink_clear_status(tspi); + + tspi->command_reg = tspi->def_command_reg; + tspi->command_reg |= SLINK_CS_SW | SLINK_CS_VALUE; + + tspi->command2_reg = tspi->def_command2_reg; + tspi->command2_reg |= SLINK_SS_EN_CS(spi->chip_select); + + tspi->command_reg &= ~SLINK_MODES; + if (spi->mode & SPI_CPHA) + tspi->command_reg |= SLINK_CK_SDA; + + if (spi->mode & SPI_CPOL) + tspi->command_reg |= SLINK_IDLE_SCLK_DRIVE_HIGH; + else + tspi->command_reg |= SLINK_IDLE_SCLK_DRIVE_LOW; return 0; } @@ -842,13 +837,13 @@ static int tegra_slink_transfer_one(struct spi_master *master, int ret; INIT_COMPLETION(tspi->xfer_completion); - ret = tegra_slink_start_transfer_one(spi, xfer, tspi->is_first_msg); + ret = tegra_slink_start_transfer_one(spi, xfer); if (ret < 0) { dev_err(tspi->dev, "spi can not start transfer, err %d\n", ret); return ret; } - tspi->is_first_msg = false; + ret = wait_for_completion_timeout(&tspi->xfer_completion, SLINK_DMA_TIMEOUT); if (WARN_ON(ret == 0)) { -- cgit v0.10.2 From 420f9739a62cdb027f5580d25c813501ff93aa6f Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Wed, 16 Oct 2013 23:10:31 +0200 Subject: thinkpad-acpi: Add mute and mic-mute LED functionality The LEDs are currently not visible to userspace, for security reasons. They are exported through thinkpad_acpi.h for use by the snd-hda-intel driver. Thanks to Alex Hung and Takashi Iwai for writing parts of this patch. Signed-off-by: David Henningsson Acked-by: Henrique de Moraes Holschuh Signed-off-by: Takashi Iwai diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index 86c5236..fc04c14 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt @@ -1,7 +1,7 @@ ThinkPad ACPI Extras Driver - Version 0.24 - December 11th, 2009 + Version 0.25 + October 16th, 2013 Borislav Deianov Henrique de Moraes Holschuh @@ -741,6 +741,9 @@ compiled with the CONFIG_THINKPAD_ACPI_UNSAFE_LEDS option enabled. Distributions must never enable this option. Individual users that are aware of the consequences are welcome to enabling it. +Audio mute and microphone mute LEDs are supported, but currently not +visible to userspace. They are used by the snd-hda-intel audio driver. + procfs notes: The available commands are: diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 03ca6c1..0b7efb2 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -23,7 +23,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define TPACPI_VERSION "0.24" +#define TPACPI_VERSION "0.25" #define TPACPI_SYSFS_VERSION 0x020700 /* @@ -88,6 +88,7 @@ #include +#include /* ThinkPad CMOS commands */ #define TP_CMOS_VOLUME_DOWN 0 @@ -8350,6 +8351,91 @@ static struct ibm_struct fan_driver_data = { .resume = fan_resume, }; +/************************************************************************* + * Mute LED subdriver + */ + + +struct tp_led_table { + acpi_string name; + int on_value; + int off_value; + int state; +}; + +static struct tp_led_table led_tables[] = { + [TPACPI_LED_MUTE] = { + .name = "SSMS", + .on_value = 1, + .off_value = 0, + }, + [TPACPI_LED_MICMUTE] = { + .name = "MMTS", + .on_value = 2, + .off_value = 0, + }, +}; + +static int mute_led_on_off(struct tp_led_table *t, bool state) +{ + acpi_handle temp; + int output; + + if (!ACPI_SUCCESS(acpi_get_handle(hkey_handle, t->name, &temp))) { + pr_warn("Thinkpad ACPI has no %s interface.\n", t->name); + return -EIO; + } + + if (!acpi_evalf(hkey_handle, &output, t->name, "dd", + state ? t->on_value : t->off_value)) + return -EIO; + + t->state = state; + return state; +} + +int tpacpi_led_set(int whichled, bool on) +{ + struct tp_led_table *t; + + if (whichled < 0 || whichled >= TPACPI_LED_MAX) + return -EINVAL; + + t = &led_tables[whichled]; + if (t->state < 0 || t->state == on) + return t->state; + return mute_led_on_off(t, on); +} +EXPORT_SYMBOL_GPL(tpacpi_led_set); + +static int mute_led_init(struct ibm_init_struct *iibm) +{ + acpi_handle temp; + int i; + + for (i = 0; i < TPACPI_LED_MAX; i++) { + struct tp_led_table *t = &led_tables[i]; + if (ACPI_SUCCESS(acpi_get_handle(hkey_handle, t->name, &temp))) + mute_led_on_off(t, false); + else + t->state = -ENODEV; + } + return 0; +} + +static void mute_led_exit(void) +{ + int i; + + for (i = 0; i < TPACPI_LED_MAX; i++) + tpacpi_led_set(i, false); +} + +static struct ibm_struct mute_led_driver_data = { + .name = "mute_led", + .exit = mute_led_exit, +}; + /**************************************************************************** **************************************************************************** * @@ -8768,6 +8854,10 @@ static struct ibm_init_struct ibms_init[] __initdata = { .init = fan_init, .data = &fan_driver_data, }, + { + .init = mute_led_init, + .data = &mute_led_driver_data, + }, }; static int __init set_ibm_param(const char *val, struct kernel_param *kp) diff --git a/include/linux/thinkpad_acpi.h b/include/linux/thinkpad_acpi.h new file mode 100644 index 0000000..361de59 --- /dev/null +++ b/include/linux/thinkpad_acpi.h @@ -0,0 +1,15 @@ +#ifndef __THINKPAD_ACPI_H__ +#define __THINKPAD_ACPI_H__ + +/* These two functions return 0 if success, or negative error code + (e g -ENODEV if no led present) */ + +enum { + TPACPI_LED_MUTE, + TPACPI_LED_MICMUTE, + TPACPI_LED_MAX, +}; + +int tpacpi_led_set(int whichled, bool on); + +#endif -- cgit v0.10.2 From 360fec281e2012b405b5fac48449f69e4a049949 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Wed, 16 Oct 2013 23:10:32 +0200 Subject: ALSA: hda - add HDA_FIXUP_ACT_FREE action A fixup which should be called before codec being freed will come to use in the next patch. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 2e7493e..a71bf34 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -428,6 +428,7 @@ enum { HDA_FIXUP_ACT_PROBE, HDA_FIXUP_ACT_INIT, HDA_FIXUP_ACT_BUILD, + HDA_FIXUP_ACT_FREE, }; int snd_hda_add_verbs(struct hda_codec *codec, const struct hda_verb *list); -- cgit v0.10.2 From 08cf680ccafd5df9885fbcd0cab85221df00b44b Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Wed, 16 Oct 2013 23:10:33 +0200 Subject: ALSA: hda - add connection to thinkpad_acpi to control mute/micmute LEDs Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index ec68eae..993b25c 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -3208,11 +3208,17 @@ static int cx_auto_init(struct hda_codec *codec) return 0; } +static void cx_auto_free(struct hda_codec *codec) +{ + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_FREE); + snd_hda_gen_free(codec); +} + static const struct hda_codec_ops cx_auto_patch_ops = { .build_controls = cx_auto_build_controls, .build_pcms = snd_hda_gen_build_pcms, .init = cx_auto_init, - .free = snd_hda_gen_free, + .free = cx_auto_free, .unsol_event = snd_hda_jack_unsol_event, #ifdef CONFIG_PM .check_power_status = snd_hda_gen_check_power_status, @@ -3232,8 +3238,84 @@ enum { CXT_FIXUP_HEADPHONE_MIC_PIN, CXT_FIXUP_HEADPHONE_MIC, CXT_FIXUP_GPIO1, + CXT_FIXUP_THINKPAD_ACPI, }; +#if IS_ENABLED(CONFIG_THINKPAD_ACPI) + +#include + +static int (*led_set_func)(int, bool); + +static void update_tpacpi_mute_led(void *private_data, int enabled) +{ + struct hda_codec *codec = private_data; + struct conexant_spec *spec = codec->spec; + + if (spec->dynamic_eapd) + cx_auto_vmaster_hook(private_data, enabled); + + if (led_set_func) + led_set_func(TPACPI_LED_MUTE, !enabled); +} + +static void update_tpacpi_micmute_led(struct hda_codec *codec, + struct snd_ctl_elem_value *ucontrol) +{ + if (!ucontrol || !led_set_func) + return; + if (strcmp("Capture Switch", ucontrol->id.name) == 0 && ucontrol->id.index == 0) { + /* TODO: How do I verify if it's a mono or stereo here? */ + bool val = ucontrol->value.integer.value[0] || ucontrol->value.integer.value[1]; + led_set_func(TPACPI_LED_MICMUTE, !val); + } +} + +static void cxt_fixup_thinkpad_acpi(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct conexant_spec *spec = codec->spec; + + bool removefunc = false; + + if (action == HDA_FIXUP_ACT_PROBE) { + if (!led_set_func) + led_set_func = symbol_request(tpacpi_led_set); + if (!led_set_func) { + snd_printk(KERN_WARNING "Failed to find thinkpad-acpi symbol tpacpi_led_set\n"); + return; + } + + removefunc = true; + if (led_set_func(TPACPI_LED_MUTE, false) >= 0) { + spec->gen.vmaster_mute.hook = update_tpacpi_mute_led; + removefunc = false; + } + if (led_set_func(TPACPI_LED_MICMUTE, false) >= 0) { + if (spec->gen.num_adc_nids > 1) + snd_printdd("Skipping micmute LED control due to several ADCs"); + else { + spec->gen.cap_sync_hook = update_tpacpi_micmute_led; + removefunc = false; + } + } + } + + if (led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) { + symbol_put(tpacpi_led_set); + led_set_func = NULL; + } +} + +#else + +static void cxt_fixup_thinkpad_acpi(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ +} + +#endif + static void cxt_fixup_stereo_dmic(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -3344,6 +3426,8 @@ static const struct hda_fixup cxt_fixups[] = { [CXT_PINCFG_LENOVO_TP410] = { .type = HDA_FIXUP_PINS, .v.pins = cxt_pincfg_lenovo_tp410, + .chained = true, + .chain_id = CXT_FIXUP_THINKPAD_ACPI, }, [CXT_PINCFG_LEMOTE_A1004] = { .type = HDA_FIXUP_PINS, @@ -3385,6 +3469,10 @@ static const struct hda_fixup cxt_fixups[] = { { } }, }, + [CXT_FIXUP_THINKPAD_ACPI] = { + .type = HDA_FIXUP_FUNC, + .v.func = cxt_fixup_thinkpad_acpi, + }, }; static const struct snd_pci_quirk cxt5051_fixups[] = { @@ -3507,7 +3595,7 @@ static int patch_conexant_auto(struct hda_codec *codec) return 0; error: - snd_hda_gen_free(codec); + cx_auto_free(codec); return err; } -- cgit v0.10.2 From 4758fed912d7cd0ba53d2694e89b884114de6580 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 17 Oct 2013 17:56:25 +0200 Subject: ALSA: hda - Treat zero connection as non-error The zero-length connection list happens so often on Haswell HDMI, and it results in warning messages like ALSA: hda_codec: invalid CONNECT_LIST verb 5[1]:0 at each time the codec resumes from the power-save, which is fairly annoying. Since this is no real error, make it shown only in the verbose debug mode. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 68801ba..a1632f4 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -565,7 +565,7 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, range_val = !!(parm & (1 << (shift-1))); /* ranges */ val = parm & mask; if (val == 0 && null_count++) { /* no second chance */ - snd_printk(KERN_WARNING "hda_codec: " + snd_printdd("hda_codec: " "invalid CONNECT_LIST verb %x[%i]:%x\n", nid, i, parm); return 0; -- cgit v0.10.2 From cbbaa603a03cc46681e24d6b2804b62fde95a2af Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 17 Oct 2013 18:03:24 +0200 Subject: ALSA: hda - Fix possible races in HDMI driver Some per_pin fields and ELD contents might be changed dynamically in multiple ways where the concurrent accesses are still opened in the current code. This patch fixes such possible races by using eld->lock in appropriate places. Reported-by: Anssi Hannula Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index b899eba..c2bad95 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1342,6 +1342,7 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) bool update_eld = false; bool eld_changed = false; + mutex_lock(&pin_eld->lock); pin_eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE); if (pin_eld->monitor_present) eld->eld_valid = !!(present & AC_PINSENSE_ELDV); @@ -1371,11 +1372,10 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) queue_delayed_work(codec->bus->workq, &per_pin->work, msecs_to_jiffies(300)); - return; + goto unlock; } } - mutex_lock(&pin_eld->lock); if (pin_eld->eld_valid && !eld->eld_valid) { update_eld = true; eld_changed = true; @@ -1400,12 +1400,13 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); } - mutex_unlock(&pin_eld->lock); if (eld_changed) snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, &per_pin->eld_ctl->id); + unlock: + mutex_unlock(&pin_eld->lock); } static void hdmi_repoll_eld(struct work_struct *work) @@ -1576,10 +1577,12 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, bool non_pcm; non_pcm = check_non_pcm_per_cvt(codec, cvt_nid); + mutex_lock(&per_pin->sink_eld.lock); per_pin->channels = substream->runtime->channels; per_pin->setup = true; hdmi_setup_audio_infoframe(codec, per_pin, non_pcm); + mutex_unlock(&per_pin->sink_eld.lock); return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); } @@ -1617,11 +1620,14 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, per_pin = get_pin(spec, pin_idx); snd_hda_spdif_ctls_unassign(codec, pin_idx); + + mutex_lock(&per_pin->sink_eld.lock); per_pin->chmap_set = false; memset(per_pin->chmap, 0, sizeof(per_pin->chmap)); per_pin->setup = false; per_pin->channels = 0; + mutex_unlock(&per_pin->sink_eld.lock); } return 0; @@ -1750,10 +1756,12 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); if (ca < 0) return -EINVAL; + mutex_lock(&per_pin->sink_eld.lock); per_pin->chmap_set = true; memcpy(per_pin->chmap, chmap, sizeof(chmap)); if (prepared) hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); + mutex_unlock(&per_pin->sink_eld.lock); return 0; } -- cgit v0.10.2 From a4e9a38b40a0e2f7dad1a0b355896d23fbdd16e0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 17 Oct 2013 18:21:12 +0200 Subject: ALSA: hda - Move mutex from hda_eld to per_pin in HDMI codec driver Since the lock is used primarily in patch_hdmi.c, it's better to move it in the local struct instead of exporting in hda_eld. The only functions requiring the lock in hda_eld.c are proc accessors. So in this patch, the proc entry and its creation/deletion/accessors are moved into patch_hdmi.c, together with the mutex lock to pin_spec struct. The former proc info functions are exported so that they can be called from patch_hdmi.c. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index d0d7ac1..f62356c 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -478,10 +478,9 @@ static void hdmi_print_sad_info(int i, struct cea_sad *a, snd_iprintf(buffer, "sad%d_profile\t\t%d\n", i, a->profile); } -static void hdmi_print_eld_info(struct snd_info_entry *entry, - struct snd_info_buffer *buffer) +void snd_hdmi_print_eld_info(struct hdmi_eld *eld, + struct snd_info_buffer *buffer) { - struct hdmi_eld *eld = entry->private_data; struct parsed_hdmi_eld *e = &eld->info; char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; int i; @@ -500,13 +499,10 @@ static void hdmi_print_eld_info(struct snd_info_entry *entry, [4 ... 7] = "reserved" }; - mutex_lock(&eld->lock); snd_iprintf(buffer, "monitor_present\t\t%d\n", eld->monitor_present); snd_iprintf(buffer, "eld_valid\t\t%d\n", eld->eld_valid); - if (!eld->eld_valid) { - mutex_unlock(&eld->lock); + if (!eld->eld_valid) return; - } snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name); snd_iprintf(buffer, "connection_type\t\t%s\n", eld_connection_type_names[e->conn_type]); @@ -528,13 +524,11 @@ static void hdmi_print_eld_info(struct snd_info_entry *entry, for (i = 0; i < e->sad_count; i++) hdmi_print_sad_info(i, e->sad + i, buffer); - mutex_unlock(&eld->lock); } -static void hdmi_write_eld_info(struct snd_info_entry *entry, - struct snd_info_buffer *buffer) +void snd_hdmi_write_eld_info(struct hdmi_eld *eld, + struct snd_info_buffer *buffer) { - struct hdmi_eld *eld = entry->private_data; struct parsed_hdmi_eld *e = &eld->info; char line[64]; char name[64]; @@ -542,7 +536,6 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry, long long val; unsigned int n; - mutex_lock(&eld->lock); while (!snd_info_get_line(buffer, line, sizeof(line))) { if (sscanf(line, "%s %llx", name, &val) != 2) continue; @@ -594,38 +587,7 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry, e->sad_count = n + 1; } } - mutex_unlock(&eld->lock); -} - - -int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld, - int index) -{ - char name[32]; - struct snd_info_entry *entry; - int err; - - snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index); - err = snd_card_proc_new(codec->bus->card, name, &entry); - if (err < 0) - return err; - - snd_info_set_text_ops(entry, eld, hdmi_print_eld_info); - entry->c.text.write = hdmi_write_eld_info; - entry->mode |= S_IWUSR; - eld->proc_entry = entry; - - return 0; -} - -void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld) -{ - if (!codec->bus->shutdown && eld->proc_entry) { - snd_device_free(codec->bus->card, eld->proc_entry); - eld->proc_entry = NULL; - } } - #endif /* CONFIG_PROC_FS */ /* update PCM info based on ELD */ diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index a71bf34..46cddd4 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -752,10 +752,6 @@ struct hdmi_eld { int eld_size; char eld_buffer[ELD_MAX_SIZE]; struct parsed_hdmi_eld info; - struct mutex lock; -#ifdef CONFIG_PROC_FS - struct snd_info_entry *proc_entry; -#endif }; int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid); @@ -768,20 +764,10 @@ void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e, struct hda_pcm_stream *hinfo); #ifdef CONFIG_PROC_FS -int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld, - int index); -void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld); -#else -static inline int snd_hda_eld_proc_new(struct hda_codec *codec, - struct hdmi_eld *eld, - int index) -{ - return 0; -} -static inline void snd_hda_eld_proc_free(struct hda_codec *codec, - struct hdmi_eld *eld) -{ -} +void snd_hdmi_print_eld_info(struct hdmi_eld *eld, + struct snd_info_buffer *buffer); +void snd_hdmi_write_eld_info(struct hdmi_eld *eld, + struct snd_info_buffer *buffer); #endif #define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80 diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index c2bad95..804adb8 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -67,6 +67,7 @@ struct hdmi_spec_per_pin { struct hda_codec *codec; struct hdmi_eld sink_eld; + struct mutex lock; struct delayed_work work; struct snd_kcontrol *eld_ctl; int repoll_count; @@ -76,6 +77,9 @@ struct hdmi_spec_per_pin { bool chmap_set; /* channel-map override by ALSA API? */ unsigned char chmap[8]; /* ALSA API channel-map */ char pcm_name[8]; /* filled in build_pcm callbacks */ +#ifdef CONFIG_PROC_FS + struct snd_info_entry *proc_entry; +#endif }; struct hdmi_spec { @@ -349,17 +353,19 @@ static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin; struct hdmi_eld *eld; int pin_idx; uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; pin_idx = kcontrol->private_value; - eld = &get_pin(spec, pin_idx)->sink_eld; + per_pin = get_pin(spec, pin_idx); + eld = &per_pin->sink_eld; - mutex_lock(&eld->lock); + mutex_lock(&per_pin->lock); uinfo->count = eld->eld_valid ? eld->eld_size : 0; - mutex_unlock(&eld->lock); + mutex_unlock(&per_pin->lock); return 0; } @@ -369,15 +375,17 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin; struct hdmi_eld *eld; int pin_idx; pin_idx = kcontrol->private_value; - eld = &get_pin(spec, pin_idx)->sink_eld; + per_pin = get_pin(spec, pin_idx); + eld = &per_pin->sink_eld; - mutex_lock(&eld->lock); + mutex_lock(&per_pin->lock); if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data)) { - mutex_unlock(&eld->lock); + mutex_unlock(&per_pin->lock); snd_BUG(); return -EINVAL; } @@ -387,7 +395,7 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, if (eld->eld_valid) memcpy(ucontrol->value.bytes.data, eld->eld_buffer, eld->eld_size); - mutex_unlock(&eld->lock); + mutex_unlock(&per_pin->lock); return 0; } @@ -478,6 +486,68 @@ static void hdmi_set_channel_count(struct hda_codec *codec, AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); } +/* + * ELD proc files + */ + +#ifdef CONFIG_PROC_FS +static void print_eld_info(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct hdmi_spec_per_pin *per_pin = entry->private_data; + + mutex_lock(&per_pin->lock); + snd_hdmi_print_eld_info(&per_pin->sink_eld, buffer); + mutex_unlock(&per_pin->lock); +} + +static void write_eld_info(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct hdmi_spec_per_pin *per_pin = entry->private_data; + + mutex_lock(&per_pin->lock); + snd_hdmi_write_eld_info(&per_pin->sink_eld, buffer); + mutex_unlock(&per_pin->lock); +} + +static int eld_proc_new(struct hdmi_spec_per_pin *per_pin, int index) +{ + char name[32]; + struct hda_codec *codec = per_pin->codec; + struct snd_info_entry *entry; + int err; + + snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index); + err = snd_card_proc_new(codec->bus->card, name, &entry); + if (err < 0) + return err; + + snd_info_set_text_ops(entry, per_pin, print_eld_info); + entry->c.text.write = write_eld_info; + entry->mode |= S_IWUSR; + per_pin->proc_entry = entry; + + return 0; +} + +static void eld_proc_free(struct hdmi_spec_per_pin *per_pin) +{ + if (!per_pin->codec->bus->shutdown && per_pin->proc_entry) { + snd_device_free(per_pin->codec->bus->card, per_pin->proc_entry); + per_pin->proc_entry = NULL; + } +} +#else +static inline int snd_hda_eld_proc_new(struct hdmi_spec_per_pin *per_pin, + int index) +{ + return 0; +} +static inline void snd_hda_eld_proc_free(struct hdmi_spec_per_pin *per_pin) +{ +} +#endif /* * Channel mapping routines @@ -1342,7 +1412,7 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) bool update_eld = false; bool eld_changed = false; - mutex_lock(&pin_eld->lock); + mutex_lock(&per_pin->lock); pin_eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE); if (pin_eld->monitor_present) eld->eld_valid = !!(present & AC_PINSENSE_ELDV); @@ -1406,7 +1476,7 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, &per_pin->eld_ctl->id); unlock: - mutex_unlock(&pin_eld->lock); + mutex_unlock(&per_pin->lock); } static void hdmi_repoll_eld(struct work_struct *work) @@ -1577,12 +1647,12 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, bool non_pcm; non_pcm = check_non_pcm_per_cvt(codec, cvt_nid); - mutex_lock(&per_pin->sink_eld.lock); + mutex_lock(&per_pin->lock); per_pin->channels = substream->runtime->channels; per_pin->setup = true; hdmi_setup_audio_infoframe(codec, per_pin, non_pcm); - mutex_unlock(&per_pin->sink_eld.lock); + mutex_unlock(&per_pin->lock); return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); } @@ -1621,13 +1691,13 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, snd_hda_spdif_ctls_unassign(codec, pin_idx); - mutex_lock(&per_pin->sink_eld.lock); + mutex_lock(&per_pin->lock); per_pin->chmap_set = false; memset(per_pin->chmap, 0, sizeof(per_pin->chmap)); per_pin->setup = false; per_pin->channels = 0; - mutex_unlock(&per_pin->sink_eld.lock); + mutex_unlock(&per_pin->lock); } return 0; @@ -1756,12 +1826,12 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); if (ca < 0) return -EINVAL; - mutex_lock(&per_pin->sink_eld.lock); + mutex_lock(&per_pin->lock); per_pin->chmap_set = true; memcpy(per_pin->chmap, chmap, sizeof(chmap)); if (prepared) hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); - mutex_unlock(&per_pin->sink_eld.lock); + mutex_unlock(&per_pin->lock); return 0; } @@ -1878,12 +1948,11 @@ static int generic_hdmi_init_per_pins(struct hda_codec *codec) for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - struct hdmi_eld *eld = &per_pin->sink_eld; per_pin->codec = codec; - mutex_init(&eld->lock); + mutex_init(&per_pin->lock); INIT_DELAYED_WORK(&per_pin->work, hdmi_repoll_eld); - snd_hda_eld_proc_new(codec, eld, pin_idx); + eld_proc_new(per_pin, pin_idx); } return 0; } @@ -1924,10 +1993,9 @@ static void generic_hdmi_free(struct hda_codec *codec) for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - struct hdmi_eld *eld = &per_pin->sink_eld; cancel_delayed_work(&per_pin->work); - snd_hda_eld_proc_free(codec, eld); + eld_proc_free(per_pin); } flush_workqueue(codec->bus->workq); -- cgit v0.10.2 From 6ef068cb8e77784431b6c80adc49d0b0a6a5df66 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 17 Oct 2013 12:07:58 -0300 Subject: perf evlist: Introduce perf_evlist__strerror_tp method Out of 'perf trace', should be used by other tools that uses tracepoints. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Ramkumar Ramachandra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-lyvtxhchz4ga8fwht15x8wou@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index ec82895..78b0d6a 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1814,27 +1814,11 @@ out: trace->live = false; return err; out_error_tp: - switch(errno) { - case ENOENT: - fputs("Error:\tUnable to find debugfs\n" - "Hint:\tWas your kernel was compiled with debugfs support?\n" - "Hint:\tIs the debugfs filesystem mounted?\n" - "Hint:\tTry 'sudo mount -t debugfs nodev /sys/kernel/debug'\n", - trace->output); - break; - case EACCES: - fprintf(trace->output, - "Error:\tNo permissions to read %s/tracing/events/raw_syscalls\n" - "Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n", - debugfs_mountpoint, debugfs_mountpoint); - break; - default: { - char bf[256]; - fprintf(trace->output, "Can't trace: %s\n", - strerror_r(errno, bf, sizeof(bf))); - } - break; - } +{ + char errbuf[BUFSIZ]; + perf_evlist__strerror_tp(evlist, errno, errbuf, sizeof(errbuf)); + fprintf(trace->output, "%s\n", errbuf); +} goto out_delete_evlist; } diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index cb9523f..6737420 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -1126,3 +1126,30 @@ size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp) return printed + fprintf(fp, "\n");; } + +int perf_evlist__strerror_tp(struct perf_evlist *evlist __maybe_unused, + int err, char *buf, size_t size) +{ + char sbuf[128]; + + switch (err) { + case ENOENT: + scnprintf(buf, size, "%s", + "Error:\tUnable to find debugfs\n" + "Hint:\tWas your kernel was compiled with debugfs support?\n" + "Hint:\tIs the debugfs filesystem mounted?\n" + "Hint:\tTry 'sudo mount -t debugfs nodev /sys/kernel/debug'"); + break; + case EACCES: + scnprintf(buf, size, + "Error:\tNo permissions to read %s/tracing/events/raw_syscalls\n" + "Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n", + debugfs_mountpoint, debugfs_mountpoint); + break; + default: + scnprintf(buf, size, "%s", strerror_r(err, sbuf, sizeof(sbuf))); + break; + } + + return 0; +} diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 722618f..386de10 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -168,6 +168,8 @@ static inline struct perf_evsel *perf_evlist__last(struct perf_evlist *evlist) size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp); +int perf_evlist__strerror_tp(struct perf_evlist *evlist, int err, char *buf, size_t size); + static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm) { struct perf_event_mmap_page *pc = mm->base; -- cgit v0.10.2 From 82fbb4f7b47683077e0716474d4f1ce65a2146cb Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 4 Sep 2011 22:04:49 +0200 Subject: ALSA: add DICE driver As a start point for further development, this is an incomplete driver for DICE devices: - only playback (so no clock source except the bus clock) - only 44.1 kHz - no MIDI - recovery after bus reset is slow - hwdep device is created, but not actually implemented Contains compilation fixes by Stefan Richter. Signed-off-by: Clemens Ladisch diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 2a5f0e1..7cbfa3c 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -138,6 +138,7 @@ Code Seq#(hex) Include File Comments 'H' C0-DF net/bluetooth/cmtp/cmtp.h conflict! 'H' C0-DF net/bluetooth/bnep/bnep.h conflict! 'H' F1 linux/hid-roccat.h +'H' F8-FA sound/firewire.h 'I' all linux/isdn.h conflict! 'I' 00-0F drivers/isdn/divert/isdn_divert.h conflict! 'I' 40-4F linux/mISDNif.h conflict! diff --git a/include/uapi/sound/Kbuild b/include/uapi/sound/Kbuild index 0f7d279..a7f2770 100644 --- a/include/uapi/sound/Kbuild +++ b/include/uapi/sound/Kbuild @@ -5,6 +5,7 @@ header-y += asound_fm.h header-y += compress_offload.h header-y += compress_params.h header-y += emu10k1.h +header-y += firewire.h header-y += hdsp.h header-y += hdspm.h header-y += sb16_csp.h diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index 041203f..9fc6219 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -93,9 +93,10 @@ enum { SNDRV_HWDEP_IFACE_SB_RC, /* SB Extigy/Audigy2NX remote control */ SNDRV_HWDEP_IFACE_HDA, /* HD-audio */ SNDRV_HWDEP_IFACE_USB_STREAM, /* direct access to usb stream */ + SNDRV_HWDEP_IFACE_FW_DICE, /* TC DICE FireWire device */ /* Don't forget to change the following: */ - SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_USB_STREAM + SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_DICE }; struct snd_hwdep_info { diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h new file mode 100644 index 0000000..e86131c --- /dev/null +++ b/include/uapi/sound/firewire.h @@ -0,0 +1,51 @@ +#ifndef UAPI_SOUND_FIREWIRE_H_INCLUDED +#define UAPI_SOUND_FIREWIRE_H_INCLUDED + +#include + +/* events can be read() from the hwdep device */ + +#define SNDRV_FIREWIRE_EVENT_LOCK_STATUS 0x000010cc +#define SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION 0xd1ce004e + +struct snd_firewire_event_common { + unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */ +}; + +struct snd_firewire_event_lock_status { + unsigned int type; + unsigned int status; /* 0/1 = unlocked/locked */ +}; + +struct snd_firewire_event_dice_notification { + unsigned int type; + unsigned int notification; /* DICE-specific bits */ +}; + +union snd_firewire_event { + struct snd_firewire_event_common common; + struct snd_firewire_event_lock_status lock_status; + struct snd_firewire_event_dice_notification dice_notification; +}; + + +#define SNDRV_FIREWIRE_IOCTL_GET_INFO _IOR('H', 0xf8, struct snd_firewire_get_info) +#define SNDRV_FIREWIRE_IOCTL_LOCK _IO('H', 0xf9) +#define SNDRV_FIREWIRE_IOCTL_UNLOCK _IO('H', 0xfa) + +#define SNDRV_FIREWIRE_TYPE_DICE 1 +/* Fireworks, AV/C, RME, MOTU, ... */ + +struct snd_firewire_get_info { + unsigned int type; /* SNDRV_FIREWIRE_TYPE_xxx */ + unsigned int card; /* same as fw_cdev_get_info.card */ + unsigned char guid[8]; + char device_name[16]; /* device node in /dev */ +}; + +/* + * SNDRV_FIREWIRE_IOCTL_LOCK prevents the driver from streaming. + * Returns -EBUSY if the driver is already streaming. + */ + +#endif diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index ea063e1..9153309 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -11,6 +11,19 @@ config SND_FIREWIRE_LIB tristate depends on SND_PCM +config SND_DICE + tristate "DICE devices (EXPERIMENTAL)" + select SND_HWDEP + select SND_PCM + select SND_FIREWIRE_LIB + help + Say Y here to include support for many FireWire audio interfaces + based on the DICE chip family (DICE-II/Jr/Mini) from TC Applied + Technologies. + + To compile this driver as a module, choose M here: the module + will be called snd-dice. + config SND_FIREWIRE_SPEAKERS tristate "FireWire speakers" select SND_PCM diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile index 460179d..5099550 100644 --- a/sound/firewire/Makefile +++ b/sound/firewire/Makefile @@ -1,10 +1,12 @@ snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \ fcp.o cmp.o amdtp.o +snd-dice-objs := dice.o snd-firewire-speakers-objs := speakers.o snd-isight-objs := isight.o snd-scs1x-objs := scs1x.o obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o +obj-$(CONFIG_SND_DICE) += snd-dice.o obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o obj-$(CONFIG_SND_ISIGHT) += snd-isight.o obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c new file mode 100644 index 0000000..ac71b2b --- /dev/null +++ b/sound/firewire/dice.c @@ -0,0 +1,1008 @@ +/* + * TC Applied Technologies Digital Interface Communications Engine driver + * + * Copyright (c) Clemens Ladisch + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "amdtp.h" +#include "iso-resources.h" +#include "lib.h" + +#define DICE_PRIVATE_SPACE 0xffffe0000000uLL + +/* offset from DICE_PRIVATE_SPACE; offsets and sizes in quadlets */ +#define DICE_GLOBAL_OFFSET 0x00 +#define DICE_GLOBAL_SIZE 0x04 +#define DICE_TX_OFFSET 0x08 +#define DICE_TX_SIZE 0x0c +#define DICE_RX_OFFSET 0x10 +#define DICE_RX_SIZE 0x14 + +/* pointed to by DICE_GLOBAL_OFFSET */ +#define GLOBAL_OWNER 0x000 +#define OWNER_NO_OWNER 0xffff000000000000uLL +#define OWNER_NODE_SHIFT 48 +#define GLOBAL_NOTIFICATION 0x008 +#define NOTIFY_RX_CFG_CHG 0x00000001 +#define NOTIFY_TX_CFG_CHG 0x00000002 +#define NOTIFY_DUP_ISOC 0x00000004 +#define NOTIFY_BW_ERR 0x00000008 +#define NOTIFY_LOCK_CHG 0x00000010 +#define NOTIFY_CLOCK_ACCEPTED 0x00000020 +#define NOTIFY_INTERFACE_CHG 0x00000040 +#define NOTIFY_MESSAGE 0x00100000 +#define GLOBAL_NICK_NAME 0x00c +#define NICK_NAME_SIZE 64 +#define GLOBAL_CLOCK_SELECT 0x04c +#define CLOCK_SOURCE_MASK 0x000000ff +#define CLOCK_SOURCE_AES1 0x00000000 +#define CLOCK_SOURCE_AES2 0x00000001 +#define CLOCK_SOURCE_AES3 0x00000002 +#define CLOCK_SOURCE_AES4 0x00000003 +#define CLOCK_SOURCE_AES_ANY 0x00000004 +#define CLOCK_SOURCE_ADAT 0x00000005 +#define CLOCK_SOURCE_TDIF 0x00000006 +#define CLOCK_SOURCE_WC 0x00000007 +#define CLOCK_SOURCE_ARX1 0x00000008 +#define CLOCK_SOURCE_ARX2 0x00000009 +#define CLOCK_SOURCE_ARX3 0x0000000a +#define CLOCK_SOURCE_ARX4 0x0000000b +#define CLOCK_SOURCE_INTERNAL 0x0000000c +#define CLOCK_RATE_MASK 0x0000ff00 +#define CLOCK_RATE_32000 0x00000000 +#define CLOCK_RATE_44100 0x00000100 +#define CLOCK_RATE_48000 0x00000200 +#define CLOCK_RATE_88200 0x00000300 +#define CLOCK_RATE_96000 0x00000400 +#define CLOCK_RATE_176400 0x00000500 +#define CLOCK_RATE_192000 0x00000600 +#define CLOCK_RATE_ANY_LOW 0x00000700 +#define CLOCK_RATE_ANY_MID 0x00000800 +#define CLOCK_RATE_ANY_HIGH 0x00000900 +#define CLOCK_RATE_NONE 0x00000a00 +#define GLOBAL_ENABLE 0x050 +#define ENABLE 0x00000001 +#define GLOBAL_STATUS 0x054 +#define STATUS_SOURCE_LOCKED 0x00000001 +#define STATUS_RATE_CONFLICT 0x00000002 +#define STATUS_NOMINAL_RATE_MASK 0x0000ff00 +#define GLOBAL_EXTENDED_STATUS 0x058 +#define EXT_STATUS_AES1_LOCKED 0x00000001 +#define EXT_STATUS_AES2_LOCKED 0x00000002 +#define EXT_STATUS_AES3_LOCKED 0x00000004 +#define EXT_STATUS_AES4_LOCKED 0x00000008 +#define EXT_STATUS_ADAT_LOCKED 0x00000010 +#define EXT_STATUS_TDIF_LOCKED 0x00000020 +#define EXT_STATUS_ARX1_LOCKED 0x00000040 +#define EXT_STATUS_ARX2_LOCKED 0x00000080 +#define EXT_STATUS_ARX3_LOCKED 0x00000100 +#define EXT_STATUS_ARX4_LOCKED 0x00000200 +#define EXT_STATUS_WC_LOCKED 0x00000400 +#define EXT_STATUS_AES1_SLIP 0x00010000 +#define EXT_STATUS_AES2_SLIP 0x00020000 +#define EXT_STATUS_AES3_SLIP 0x00040000 +#define EXT_STATUS_AES4_SLIP 0x00080000 +#define EXT_STATUS_ADAT_SLIP 0x00100000 +#define EXT_STATUS_TDIF_SLIP 0x00200000 +#define EXT_STATUS_ARX1_SLIP 0x00400000 +#define EXT_STATUS_ARX2_SLIP 0x00800000 +#define EXT_STATUS_ARX3_SLIP 0x01000000 +#define EXT_STATUS_ARX4_SLIP 0x02000000 +#define EXT_STATUS_WC_SLIP 0x04000000 +#define GLOBAL_SAMPLE_RATE 0x05c +#define GLOBAL_VERSION 0x060 +#define GLOBAL_CLOCK_CAPABILITIES 0x064 +#define CLOCK_CAP_RATE_32000 0x00000001 +#define CLOCK_CAP_RATE_44100 0x00000002 +#define CLOCK_CAP_RATE_48000 0x00000004 +#define CLOCK_CAP_RATE_88200 0x00000008 +#define CLOCK_CAP_RATE_96000 0x00000010 +#define CLOCK_CAP_RATE_176400 0x00000020 +#define CLOCK_CAP_RATE_192000 0x00000040 +#define CLOCK_CAP_SOURCE_AES1 0x00010000 +#define CLOCK_CAP_SOURCE_AES2 0x00020000 +#define CLOCK_CAP_SOURCE_AES3 0x00040000 +#define CLOCK_CAP_SOURCE_AES4 0x00080000 +#define CLOCK_CAP_SOURCE_AES_ANY 0x00100000 +#define CLOCK_CAP_SOURCE_ADAT 0x00200000 +#define CLOCK_CAP_SOURCE_TDIF 0x00400000 +#define CLOCK_CAP_SOURCE_WC 0x00800000 +#define CLOCK_CAP_SOURCE_ARX1 0x01000000 +#define CLOCK_CAP_SOURCE_ARX2 0x02000000 +#define CLOCK_CAP_SOURCE_ARX3 0x04000000 +#define CLOCK_CAP_SOURCE_ARX4 0x08000000 +#define CLOCK_CAP_SOURCE_INTERNAL 0x10000000 +#define GLOBAL_CLOCK_SOURCE_NAMES 0x068 +#define CLOCK_SOURCE_NAMES_SIZE 256 + +/* pointed to by DICE_TX_OFFSET */ +#define TX_NUMBER 0x000 +#define TX_SIZE 0x004 +/* repeated TX_NUMBER times, offset by TX_SIZE quadlets */ +#define TX_ISOCHRONOUS 0x008 +#define TX_NUMBER_AUDIO 0x00c +#define TX_NUMBER_MIDI 0x010 +#define TX_SPEED 0x014 +#define TX_NAMES 0x018 +#define TX_NAMES_SIZE 256 +#define TX_AC3_CAPABILITIES 0x118 +#define TX_AC3_ENABLE 0x11c + +/* pointed to by DICE_RX_OFFSET */ +#define RX_NUMBER 0x000 +#define RX_SIZE 0x004 +/* repeated RX_NUMBER times, offset by RX_SIZE quadlets */ +#define RX_ISOCHRONOUS 0x008 +#define RX_SEQ_START 0x00c +#define RX_NUMBER_AUDIO 0x010 +#define RX_NUMBER_MIDI 0x014 +#define RX_NAMES 0x018 +#define RX_NAMES_SIZE 256 +#define RX_AC3_CAPABILITIES 0x118 +#define RX_AC3_ENABLE 0x11c + + +#define FIRMWARE_LOAD_SPACE 0xffffe0100000uLL + +/* offset from FIRMWARE_LOAD_SPACE */ +#define FIRMWARE_VERSION 0x000 +#define FIRMWARE_OPCODE 0x004 +#define OPCODE_MASK 0x00000fff +#define OPCODE_GET_IMAGE_DESC 0x00000000 +#define OPCODE_DELETE_IMAGE 0x00000001 +#define OPCODE_CREATE_IMAGE 0x00000002 +#define OPCODE_UPLOAD 0x00000003 +#define OPCODE_UPLOAD_STAT 0x00000004 +#define OPCODE_RESET_IMAGE 0x00000005 +#define OPCODE_TEST_ACTION 0x00000006 +#define OPCODE_GET_RUNNING_IMAGE_VINFO 0x0000000a +#define OPCODE_EXECUTE 0x80000000 +#define FIRMWARE_RETURN_STATUS 0x008 +#define FIRMWARE_PROGRESS 0x00c +#define PROGRESS_CURR_MASK 0x00000fff +#define PROGRESS_MAX_MASK 0x00fff000 +#define PROGRESS_TOUT_MASK 0x0f000000 +#define PROGRESS_FLAG 0x80000000 +#define FIRMWARE_CAPABILITIES 0x010 +#define FL_CAP_AUTOERASE 0x00000001 +#define FL_CAP_PROGRESS 0x00000002 +#define FIRMWARE_DATA 0x02c +#define TEST_CMD_POKE 0x00000001 +#define TEST_CMD_PEEK 0x00000002 +#define CMD_GET_AVS_CNT 0x00000003 +#define CMD_CLR_AVS_CNT 0x00000004 +#define CMD_SET_MODE 0x00000005 +#define CMD_SET_MIDIBP 0x00000006 +#define CMD_GET_AVSPHASE 0x00000007 +#define CMD_ENABLE_BNC_SYNC 0x00000008 +#define CMD_PULSE_BNC_SYNC 0x00000009 +#define CMD_EMUL_SLOW_CMD 0x0000000a +#define FIRMWARE_TEST_DELAY 0xfd8 +#define FIRMWARE_TEST_BUF 0xfdc + + +/* EAP */ +#define EAP_PRIVATE_SPACE 0xffffe0200000uLL + +#define EAP_CAPABILITY_OFFSET 0x000 +#define EAP_CAPABILITY_SIZE 0x004 +/* ... */ + +#define EAP_ROUTER_CAPS 0x000 +#define ROUTER_EXPOSED 0x00000001 +#define ROUTER_READ_ONLY 0x00000002 +#define ROUTER_FLASH 0x00000004 +#define MAX_ROUTES_MASK 0xffff0000 +#define EAP_MIXER_CAPS 0x004 +#define MIXER_EXPOSED 0x00000001 +#define MIXER_READ_ONLY 0x00000002 +#define MIXER_FLASH 0x00000004 +#define MIXER_IN_DEV_MASK 0x000000f0 +#define MIXER_OUT_DEV_MASK 0x00000f00 +#define MIXER_INPUTS_MASK 0x00ff0000 +#define MIXER_OUTPUTS_MASK 0xff000000 +#define EAP_GENERAL_CAPS 0x008 +#define GENERAL_STREAM_CONFIG 0x00000001 +#define GENERAL_FLASH 0x00000002 +#define GENERAL_PEAK 0x00000004 +#define GENERAL_MAX_TX_STREAMS_MASK 0x000000f0 +#define GENERAL_MAX_RX_STREAMS_MASK 0x00000f00 +#define GENERAL_STREAM_CONFIG_FLASH 0x00001000 +#define GENERAL_CHIP_MASK 0x00ff0000 +#define GENERAL_CHIP_DICE_II 0x00000000 +#define GENERAL_CHIP_DICE_MINI 0x00010000 +#define GENERAL_CHIP_DICE_JR 0x00020000 + + +struct dice { + struct snd_card *card; + struct fw_unit *unit; + struct mutex mutex; + unsigned int global_offset; + unsigned int rx_offset; + struct fw_address_handler notification_handler; + int owner_generation; + bool global_enabled; + bool stream_running; + struct snd_pcm_substream *pcm; + struct fw_iso_resources resources; + struct amdtp_out_stream stream; +}; + +MODULE_DESCRIPTION("DICE driver"); +MODULE_AUTHOR("Clemens Ladisch "); +MODULE_LICENSE("GPL v2"); + +static inline u64 global_address(struct dice *dice, unsigned int offset) +{ + return DICE_PRIVATE_SPACE + dice->global_offset + offset; +} + +// TODO: rx index +static inline u64 rx_address(struct dice *dice, unsigned int offset) +{ + return DICE_PRIVATE_SPACE + dice->rx_offset + offset; +} + +static int dice_owner_set(struct dice *dice) +{ + struct fw_device *device = fw_parent_device(dice->unit); + __be64 *buffer; + int rcode, err, errors = 0; + + buffer = kmalloc(2 * 8, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + for (;;) { + buffer[0] = cpu_to_be64(OWNER_NO_OWNER); + buffer[1] = cpu_to_be64( + ((u64)device->card->node_id << OWNER_NODE_SHIFT) | + dice->notification_handler.offset); + + dice->owner_generation = device->generation; + smp_rmb(); /* node_id vs. generation */ + rcode = fw_run_transaction(device->card, + TCODE_LOCK_COMPARE_SWAP, + device->node_id, + dice->owner_generation, + device->max_speed, + global_address(dice, GLOBAL_OWNER), + buffer, 2 * 8); + + if (rcode == RCODE_COMPLETE) { + if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER)) { + err = 0; + } else { + dev_err(&dice->unit->device, + "device is already in use\n"); + err = -EBUSY; + } + break; + } + if (rcode_is_permanent_error(rcode) || ++errors >= 3) { + dev_err(&dice->unit->device, + "setting device owner failed: %s\n", + fw_rcode_string(rcode)); + err = -EIO; + break; + } + msleep(20); + } + + kfree(buffer); + + return err; +} + +static int dice_owner_update(struct dice *dice) +{ + struct fw_device *device = fw_parent_device(dice->unit); + __be64 *buffer; + int rcode, err, errors = 0; + + if (dice->owner_generation == -1) + return 0; + + buffer = kmalloc(2 * 8, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + for (;;) { + buffer[0] = cpu_to_be64(OWNER_NO_OWNER); + buffer[1] = cpu_to_be64( + ((u64)device->card->node_id << OWNER_NODE_SHIFT) | + dice->notification_handler.offset); + + dice->owner_generation = device->generation; + smp_rmb(); /* node_id vs. generation */ + rcode = fw_run_transaction(device->card, + TCODE_LOCK_COMPARE_SWAP, + device->node_id, + dice->owner_generation, + device->max_speed, + global_address(dice, GLOBAL_OWNER), + buffer, 2 * 8); + + if (rcode == RCODE_COMPLETE) { + if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER)) { + err = 0; + } else { + dev_err(&dice->unit->device, + "device is already in use\n"); + err = -EBUSY; + } + break; + } + if (rcode == RCODE_GENERATION) { + err = 0; /* try again later */ + break; + } + if (rcode_is_permanent_error(rcode) || ++errors >= 3) { + dev_err(&dice->unit->device, + "setting device owner failed: %s\n", + fw_rcode_string(rcode)); + err = -EIO; + break; + } + msleep(20); + } + + kfree(buffer); + + if (err < 0) + dice->owner_generation = -1; + + return err; +} + +static void dice_owner_clear(struct dice *dice) +{ + struct fw_device *device = fw_parent_device(dice->unit); + __be64 *buffer; + int rcode, errors = 0; + + buffer = kmalloc(2 * 8, GFP_KERNEL); + if (!buffer) + return; + + for (;;) { + buffer[0] = cpu_to_be64( + ((u64)device->card->node_id << OWNER_NODE_SHIFT) | + dice->notification_handler.offset); + buffer[1] = cpu_to_be64(OWNER_NO_OWNER); + + rcode = fw_run_transaction(device->card, + TCODE_LOCK_COMPARE_SWAP, + device->node_id, + dice->owner_generation, + device->max_speed, + global_address(dice, GLOBAL_OWNER), + buffer, 2 * 8); + + if (rcode == RCODE_COMPLETE) + break; + if (rcode == RCODE_GENERATION) + break; + if (rcode_is_permanent_error(rcode) || ++errors >= 3) { + dev_err(&dice->unit->device, + "clearing device owner failed: %s\n", + fw_rcode_string(rcode)); + break; + } + msleep(20); + } + + kfree(buffer); + + dice->owner_generation = -1; +} + +static int dice_enable_set(struct dice *dice) +{ + struct fw_device *device = fw_parent_device(dice->unit); + __be32 value; + int rcode, err, errors = 0; + + value = cpu_to_be32(ENABLE); + for (;;) { + rcode = fw_run_transaction(device->card, + TCODE_WRITE_QUADLET_REQUEST, + device->node_id, + dice->owner_generation, + device->max_speed, + global_address(dice, GLOBAL_ENABLE), + &value, 4); + if (rcode == RCODE_COMPLETE) { + dice->global_enabled = true; + err = 0; + break; + } + if (rcode == RCODE_GENERATION) { + err = -EAGAIN; + break; + } + if (rcode_is_permanent_error(rcode) || ++errors >= 3) { + dev_err(&dice->unit->device, + "device enabling failed: %s\n", + fw_rcode_string(rcode)); + err = -EIO; + break; + } + msleep(20); + } + + return err; +} + +static void dice_enable_clear(struct dice *dice) +{ + struct fw_device *device = fw_parent_device(dice->unit); + __be32 value; + int rcode, errors = 0; + + value = 0; + for (;;) { + rcode = fw_run_transaction(device->card, + TCODE_WRITE_QUADLET_REQUEST, + device->node_id, + dice->owner_generation, + device->max_speed, + global_address(dice, GLOBAL_ENABLE), + &value, 4); + if (rcode == RCODE_COMPLETE || + rcode == RCODE_GENERATION) + break; + if (rcode_is_permanent_error(rcode) || ++errors >= 3) { + dev_err(&dice->unit->device, + "device disabling failed: %s\n", + fw_rcode_string(rcode)); + break; + } + msleep(20); + } + dice->global_enabled = false; +} + +static void dice_notification(struct fw_card *card, struct fw_request *request, + int tcode, int destination, int source, + int generation, unsigned long long offset, + void *data, size_t length, void *callback_data) +{ + struct dice *dice = callback_data; + + if (tcode != TCODE_WRITE_QUADLET_REQUEST) { + fw_send_response(card, request, RCODE_TYPE_ERROR); + return; + } + if ((offset & 3) != 0) { + fw_send_response(card, request, RCODE_ADDRESS_ERROR); + return; + } + dev_info(&dice->unit->device, + "notification: %08x\n", be32_to_cpup(data)); + fw_send_response(card, request, RCODE_COMPLETE); +} + +static int dice_open(struct snd_pcm_substream *substream) +{ + static const struct snd_pcm_hardware hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER, + .formats = AMDTP_OUT_PCM_FORMAT_BITS, + .rates = SNDRV_PCM_RATE_44100, + .rate_min = 44100, + .rate_max = 44100, + .buffer_bytes_max = 16 * 1024 * 1024, + .period_bytes_min = 1, + .period_bytes_max = UINT_MAX, + .periods_min = 1, + .periods_max = UINT_MAX, + }; + struct dice *dice = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + __be32 number_audio, number_midi; + int err; + + err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, + rx_address(dice, RX_NUMBER_AUDIO), + &number_audio, 4); + if (err < 0) + return err; + err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, + rx_address(dice, RX_NUMBER_MIDI), + &number_midi, 4); + if (err < 0) + return err; + + runtime->hw = hardware; + runtime->hw.channels_min = be32_to_cpu(number_audio); + runtime->hw.channels_max = be32_to_cpu(number_audio); + + amdtp_out_stream_set_rate(&dice->stream, 44100); + amdtp_out_stream_set_pcm(&dice->stream, be32_to_cpu(number_audio)); + amdtp_out_stream_set_midi(&dice->stream, be32_to_cpu(number_midi)); + + err = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_PERIOD_TIME, + 5000, 8192000); + if (err < 0) + return err; + + err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + if (err < 0) + return err; + + return 0; +} + +static int dice_close(struct snd_pcm_substream *substream) +{ + return 0; +} + +static void dice_stop_stream(struct dice *dice) +{ + __be32 channel; + + if (dice->stream_running) { + dice_enable_clear(dice); + + amdtp_out_stream_stop(&dice->stream); + + channel = cpu_to_be32((u32)-1); + snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, + rx_address(dice, RX_ISOCHRONOUS), + &channel, 4); + + fw_iso_resources_free(&dice->resources); + + dice->stream_running = false; + } +} + +static int dice_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct dice *dice = substream->private_data; + int err; + + mutex_lock(&dice->mutex); + dice_stop_stream(dice); + mutex_unlock(&dice->mutex); + + err = snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + goto error; + + amdtp_out_stream_set_pcm_format(&dice->stream, + params_format(hw_params)); + + return 0; + +error: + return err; +} + +static int dice_hw_free(struct snd_pcm_substream *substream) +{ + struct dice *dice = substream->private_data; + + mutex_lock(&dice->mutex); + dice_stop_stream(dice); + mutex_unlock(&dice->mutex); + + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static int dice_prepare(struct snd_pcm_substream *substream) +{ + struct dice *dice = substream->private_data; + struct fw_device *device = fw_parent_device(dice->unit); + __be32 channel; + int err; + + mutex_lock(&dice->mutex); + + if (amdtp_out_streaming_error(&dice->stream)) + dice_stop_stream(dice); + + if (!dice->stream_running) { + err = fw_iso_resources_allocate(&dice->resources, + amdtp_out_stream_get_max_payload(&dice->stream), + device->max_speed); + if (err < 0) + goto error; + + //TODO: RX_SEQ_START + channel = cpu_to_be32(dice->resources.channel); + err = snd_fw_transaction(dice->unit, + TCODE_WRITE_QUADLET_REQUEST, + rx_address(dice, RX_ISOCHRONOUS), + &channel, 4); + if (err < 0) + goto err_resources; + + err = amdtp_out_stream_start(&dice->stream, + dice->resources.channel, + device->max_speed); + if (err < 0) + goto err_resources; + + err = dice_enable_set(dice); + if (err < 0) + goto err_stream; + + dice->stream_running = true; + } + + mutex_unlock(&dice->mutex); + + amdtp_out_stream_pcm_prepare(&dice->stream); + + return 0; + +err_stream: + amdtp_out_stream_stop(&dice->stream); +err_resources: + fw_iso_resources_free(&dice->resources); +error: + mutex_unlock(&dice->mutex); + + return err; +} + +static int dice_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct dice *dice = substream->private_data; + struct snd_pcm_substream *pcm; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + pcm = substream; + break; + case SNDRV_PCM_TRIGGER_STOP: + pcm = NULL; + break; + default: + return -EINVAL; + } + amdtp_out_stream_pcm_trigger(&dice->stream, pcm); + + return 0; +} + +static snd_pcm_uframes_t dice_pointer(struct snd_pcm_substream *substream) +{ + struct dice *dice = substream->private_data; + + return amdtp_out_stream_pcm_pointer(&dice->stream); +} + +static int dice_create_pcm(struct dice *dice) +{ + static struct snd_pcm_ops ops = { + .open = dice_open, + .close = dice_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = dice_hw_params, + .hw_free = dice_hw_free, + .prepare = dice_prepare, + .trigger = dice_trigger, + .pointer = dice_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; + __be32 clock; + struct snd_pcm *pcm; + int err; + + clock = cpu_to_be32(CLOCK_SOURCE_ARX1 | CLOCK_RATE_44100); + err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, + global_address(dice, GLOBAL_CLOCK_SELECT), + &clock, 4); + if (err < 0) + return err; + + err = snd_pcm_new(dice->card, "DICE", 0, 1, 0, &pcm); + if (err < 0) + return err; + pcm->private_data = dice; + strcpy(pcm->name, dice->card->shortname); + dice->pcm = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + dice->pcm->ops = &ops; + + return 0; +} + +// TODO: implement these + +static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, + long count, loff_t *offset) +{ + return -EIO; +} + +static int dice_hwdep_open(struct snd_hwdep *hwdep, struct file *file) +{ + return -EIO; +} + +static int dice_hwdep_release(struct snd_hwdep *hwdep, struct file *file) +{ + return 0; +} + +static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file, + poll_table *wait) +{ + return POLLERR | POLLHUP; +} + +static int dice_hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return -EIO; +} + +static int dice_create_hwdep(struct dice *dice) +{ + static const struct snd_hwdep_ops ops = { + .read = dice_hwdep_read, + .open = dice_hwdep_open, + .release = dice_hwdep_release, + .poll = dice_hwdep_poll, + .ioctl = dice_hwdep_ioctl, + .ioctl_compat = dice_hwdep_ioctl, + }; + struct snd_hwdep *hwdep; + int err; + + err = snd_hwdep_new(dice->card, "DICE", 0, &hwdep); + if (err < 0) + return err; + strcpy(hwdep->name, "DICE"); + hwdep->iface = SNDRV_HWDEP_IFACE_FW_DICE; + hwdep->ops = ops; + hwdep->private_data = dice; + hwdep->exclusive = true; + + return 0; +} + +static void dice_card_free(struct snd_card *card) +{ + struct dice *dice = card->private_data; + + amdtp_out_stream_destroy(&dice->stream); + fw_core_remove_address_handler(&dice->notification_handler); + mutex_destroy(&dice->mutex); +} + +static int dice_init_offsets(struct dice *dice) +{ + __be32 pointers[6]; + unsigned int global_size, rx_size; + int err; + + err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, + DICE_PRIVATE_SPACE, &pointers, 6 * 4); + if (err < 0) + return err; + + dice->global_offset = be32_to_cpu(pointers[0]) * 4; + global_size = be32_to_cpu(pointers[1]); + dice->rx_offset = be32_to_cpu(pointers[4]) * 4; + rx_size = be32_to_cpu(pointers[5]); + + /* some sanity checks to ensure that we actually have a DICE */ + if (dice->global_offset < 10 * 4 || global_size < 0x168 / 4 || + dice->rx_offset < 10 * 4 || rx_size < 0x120 / 4) { + dev_err(&dice->unit->device, "invalid register pointers\n"); + return -ENXIO; + } + + return 0; +} + +static void dice_card_strings(struct dice *dice) +{ + struct snd_card *card = dice->card; + struct fw_device *dev = fw_parent_device(dice->unit); + char vendor[32], model[32]; + unsigned int i; + int err; + + strcpy(card->driver, "DICE"); + + strcpy(card->shortname, "DICE"); + BUILD_BUG_ON(NICK_NAME_SIZE < sizeof(card->shortname)); + err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, + global_address(dice, GLOBAL_NICK_NAME), + card->shortname, sizeof(card->shortname)); + if (err >= 0) { + /* DICE strings are returned in "always-wrong" endianness */ + BUILD_BUG_ON(sizeof(card->shortname) % 4 != 0); + for (i = 0; i < sizeof(card->shortname); i += 4) + swab32s((u32 *)&card->shortname[i]); + card->shortname[sizeof(card->shortname) - 1] = '\0'; + } + + strcpy(vendor, "?"); + fw_csr_string(dev->config_rom + 5, CSR_VENDOR, vendor, sizeof(vendor)); + strcpy(model, "?"); + fw_csr_string(dice->unit->directory, CSR_MODEL, model, sizeof(model)); + snprintf(card->longname, sizeof(card->longname), + "%s %s, GUID %08x%08x at %s, S%d", + vendor, model, dev->config_rom[3], dev->config_rom[4], + dev_name(&dice->unit->device), 100 << dev->max_speed); + + strcpy(card->mixername, "DICE"); +} + +static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) +{ + struct snd_card *card; + struct dice *dice; + int err; + + err = snd_card_create(-1, NULL, THIS_MODULE, sizeof(*dice), &card); + if (err < 0) + return err; + snd_card_set_dev(card, &unit->device); + + dice = card->private_data; + dice->card = card; + mutex_init(&dice->mutex); + dice->unit = unit; + + err = dice_init_offsets(dice); + if (err < 0) + goto err_mutex; + + dice->notification_handler.length = 4; + dice->notification_handler.address_callback = dice_notification; + dice->notification_handler.callback_data = dice; + err = fw_core_add_address_handler(&dice->notification_handler, + &fw_high_memory_region); + if (err < 0) + goto err_mutex; + + err = fw_iso_resources_init(&dice->resources, unit); + if (err < 0) + goto err_notification_handler; + dice->resources.channels_mask = 0x00000000ffffffffuLL; + + err = amdtp_out_stream_init(&dice->stream, unit, CIP_NONBLOCKING); + if (err < 0) + goto err_resources; + + err = dice_owner_set(dice); + if (err < 0) + goto err_stream; + + card->private_free = dice_card_free; + + dice_card_strings(dice); + + err = dice_create_pcm(dice); + if (err < 0) + goto error; + + err = dice_create_hwdep(dice); + if (err < 0) + goto error; + + err = snd_card_register(card); + if (err < 0) + goto error; + + dev_set_drvdata(&unit->device, dice); + + return 0; + +err_stream: + amdtp_out_stream_destroy(&dice->stream); +err_resources: + fw_iso_resources_destroy(&dice->resources); +err_notification_handler: + fw_core_remove_address_handler(&dice->notification_handler); +err_mutex: + mutex_destroy(&dice->mutex); +error: + snd_card_free(card); + return err; +} + +static void dice_remove(struct fw_unit *unit) +{ + struct dice *dice = dev_get_drvdata(&unit->device); + + snd_card_disconnect(dice->card); + + mutex_lock(&dice->mutex); + amdtp_out_stream_pcm_abort(&dice->stream); + dice_stop_stream(dice); + dice_owner_clear(dice); + mutex_unlock(&dice->mutex); + + snd_card_free_when_closed(dice->card); +} + +static void dice_bus_reset(struct fw_unit *unit) +{ + struct dice *dice = dev_get_drvdata(&unit->device); + + mutex_lock(&dice->mutex); + /* + * XXX is the following true? + * On a bus reset, the DICE firmware disables streaming and then goes + * off contemplating its own navel for hundreds of milliseconds before + * it can react to any of our attempts to reenable streaming. This + * means that we lose synchronization anyway, so we force our streams + * to stop so that the application can restart them in an orderly + * manner. + */ + dice_owner_update(dice); + amdtp_out_stream_pcm_abort(&dice->stream); + dice_stop_stream(dice); + mutex_unlock(&dice->mutex); +} + +#define TC_OUI 0x000166 +#define DICE_INTERFACE 0x000001 + +static const struct ieee1394_device_id dice_id_table[] = { + { + .match_flags = IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION, + .specifier_id = TC_OUI, + .version = DICE_INTERFACE, + }, + { } +}; +MODULE_DEVICE_TABLE(ieee1394, dice_id_table); + +static struct fw_driver dice_driver = { + .driver = { + .owner = THIS_MODULE, + .name = KBUILD_MODNAME, + .bus = &fw_bus_type, + }, + .probe = dice_probe, + .update = dice_bus_reset, + .remove = dice_remove, + .id_table = dice_id_table, +}; + +static int __init alsa_dice_init(void) +{ + return driver_register(&dice_driver.driver); +} + +static void __exit alsa_dice_exit(void) +{ + driver_unregister(&dice_driver.driver); +} + +module_init(alsa_dice_init); +module_exit(alsa_dice_exit); -- cgit v0.10.2 From 97a07f10c38064dea492794c99445f6260afcdc1 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 17 Oct 2013 16:33:43 -0300 Subject: perf tools: Introduce filename__read_int helper Just opens a file and calls atoi() in at most its first 64 bytes. To read things like /proc/sys/kernel/perf_event_paranoid. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-669q04c5tou5pnt8jtiz6y2r@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 8dc8cf3..c25e57b 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -394,3 +394,20 @@ unsigned long parse_tag_value(const char *str, struct parse_tag *tags) return (unsigned long) -1; } + +int filename__read_int(const char *filename, int *value) +{ + char line[64]; + int fd = open(filename, O_RDONLY), err = -1; + + if (fd < 0) + return -1; + + if (read(fd, line, sizeof(line)) > 0) { + *value = atoi(line); + err = 0; + } + + close(fd); + return err; +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 42dfba7..c8f362d 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -305,4 +305,6 @@ struct dso; char *get_srcline(struct dso *dso, unsigned long addr); void free_srcline(char *srcline); + +int filename__read_int(const char *filename, int *value); #endif /* GIT_COMPAT_UTIL_H */ -- cgit v0.10.2 From a8f23d8f8af43d49cd3331681913b76e4951e1a4 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 17 Oct 2013 17:38:29 -0300 Subject: perf trace: Improve messages related to /proc/sys/kernel/perf_event_paranoid kernel/events/core.c has: /* * perf event paranoia level: * -1 - not paranoid at all * 0 - disallow raw tracepoint access for unpriv * 1 - disallow cpu events for unpriv * 2 - disallow kernel profiling for unpriv */ int sysctl_perf_event_paranoid __read_mostly = 1; So, with the default being 1, a non-root user can trace his stuff: [acme@zoo ~]$ cat /proc/sys/kernel/perf_event_paranoid 1 [acme@zoo ~]$ yes > /dev/null & [1] 15338 [acme@zoo ~]$ trace -p 15338 | head -5 0.005 ( 0.005 ms): write(fd: 1, buf: 0x7fe6db765000, count: 4096 ) = 4096 0.045 ( 0.001 ms): write(fd: 1, buf: 0x7fe6db765000, count: 4096 ) = 4096 0.085 ( 0.001 ms): write(fd: 1, buf: 0x7fe6db765000, count: 4096 ) = 4096 0.125 ( 0.001 ms): write(fd: 1, buf: 0x7fe6db765000, count: 4096 ) = 4096 0.165 ( 0.001 ms): write(fd: 1, buf: 0x7fe6db765000, count: 4096 ) = 4096 [acme@zoo ~]$ [acme@zoo ~]$ trace --duration 1 sleep 1 1002.148 (1001.218 ms): nanosleep(rqtp: 0x7fff46c79250 ) = 0 [acme@zoo ~]$ [acme@zoo ~]$ trace -- usleep 1 | tail -5 0.905 ( 0.002 ms): brk( ) = 0x1c82000 0.910 ( 0.003 ms): brk(brk: 0x1ca3000 ) = 0x1ca3000 0.913 ( 0.001 ms): brk( ) = 0x1ca3000 0.990 ( 0.059 ms): nanosleep(rqtp: 0x7fffe31a3280 ) = 0 0.995 ( 0.000 ms): exit_group( [acme@zoo ~]$ But can't do system wide tracing: [acme@zoo ~]$ trace Error: Operation not permitted. Hint: Check /proc/sys/kernel/perf_event_paranoid setting. Hint: For system wide tracing it needs to be set to -1. Hint: The current value is 1. [acme@zoo ~]$ [acme@zoo ~]$ trace --cpu 0 Error: Operation not permitted. Hint: Check /proc/sys/kernel/perf_event_paranoid setting. Hint: For system wide tracing it needs to be set to -1. Hint: The current value is 1. [acme@zoo ~]$ If the paranoid level is >= 2, i.e. turn this perf stuff off for !root users: [acme@zoo ~]$ sudo sh -c 'echo 2 > /proc/sys/kernel/perf_event_paranoid' [acme@zoo ~]$ cat /proc/sys/kernel/perf_event_paranoid 2 [acme@zoo ~]$ [acme@zoo ~]$ trace usleep 1 Error: Permission denied. Hint: Check /proc/sys/kernel/perf_event_paranoid setting. Hint: For your workloads it needs to be <= 1 Hint: For system wide tracing it needs to be set to -1. Hint: The current value is 2. [acme@zoo ~]$ [acme@zoo ~]$ trace Error: Permission denied. Hint: Check /proc/sys/kernel/perf_event_paranoid setting. Hint: For your workloads it needs to be <= 1 Hint: For system wide tracing it needs to be set to -1. Hint: The current value is 2. [acme@zoo ~]$ [acme@zoo ~]$ trace --cpu 1 Error: Permission denied. Hint: Check /proc/sys/kernel/perf_event_paranoid setting. Hint: For your workloads it needs to be <= 1 Hint: For system wide tracing it needs to be set to -1. Hint: The current value is 2. [acme@zoo ~]$ If the user manages to get what he/she wants, convincing root not to be paranoid at all... [root@zoo ~]# echo -1 > /proc/sys/kernel/perf_event_paranoid [root@zoo ~]# cat /proc/sys/kernel/perf_event_paranoid -1 [root@zoo ~]# [acme@zoo ~]$ ps -eo user,pid,comm | grep Xorg root 729 Xorg [acme@zoo ~]$ [acme@zoo ~]$ trace -a --duration 0.001 -e \!select,ioctl,writev | grep Xorg | head -5 23.143 ( 0.003 ms): Xorg/729 setitimer(which: REAL, value: 0x7fffaadf16e0 ) = 0 23.152 ( 0.004 ms): Xorg/729 read(fd: 31, buf: 0x2544af0, count: 4096 ) = 8 23.161 ( 0.002 ms): Xorg/729 read(fd: 31, buf: 0x2544af0, count: 4096 ) = -1 EAGAIN Resource temporarily unavailable 23.175 ( 0.002 ms): Xorg/729 setitimer(which: REAL, value: 0x7fffaadf16e0 ) = 0 23.235 ( 0.002 ms): Xorg/729 setitimer(which: REAL, value: 0x7fffaadf16e0 ) = 0 [acme@zoo ~]$ Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-di28olfwd28rvkox7v3hqhu1@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 78b0d6a..db959ac 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1713,10 +1713,8 @@ static int trace__run(struct trace *trace, int argc, const char **argv) } err = perf_evlist__open(evlist); - if (err < 0) { - fprintf(trace->output, "Couldn't create the events: %s\n", strerror(errno)); - goto out_delete_maps; - } + if (err < 0) + goto out_error_open; err = perf_evlist__mmap(evlist, UINT_MAX, false); if (err < 0) { @@ -1813,14 +1811,21 @@ out_delete_evlist: out: trace->live = false; return err; -out_error_tp: { char errbuf[BUFSIZ]; + +out_error_tp: perf_evlist__strerror_tp(evlist, errno, errbuf, sizeof(errbuf)); + goto out_error; + +out_error_open: + perf_evlist__strerror_open(evlist, errno, errbuf, sizeof(errbuf)); + +out_error: fprintf(trace->output, "%s\n", errbuf); -} goto out_delete_evlist; } +} static int trace__replay(struct trace *trace) { diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 6737420..0b5425b 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -1153,3 +1153,39 @@ int perf_evlist__strerror_tp(struct perf_evlist *evlist __maybe_unused, return 0; } + +int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused, + int err, char *buf, size_t size) +{ + int printed, value; + char sbuf[128], *emsg = strerror_r(err, sbuf, sizeof(sbuf)); + + switch (err) { + case EACCES: + case EPERM: + printed = scnprintf(buf, size, + "Error:\t%s.\n" + "Hint:\tCheck /proc/sys/kernel/perf_event_paranoid setting.", emsg); + + if (filename__read_int("/proc/sys/kernel/perf_event_paranoid", &value)) + break; + + printed += scnprintf(buf + printed, size - printed, "\nHint:\t"); + + if (value >= 2) { + printed += scnprintf(buf + printed, size - printed, + "For your workloads it needs to be <= 1\nHint:\t"); + } + printed += scnprintf(buf + printed, size - printed, + "For system wide tracing it needs to be set to -1"); + + printed += scnprintf(buf + printed, size - printed, + ".\nHint:\tThe current value is %d.", value); + break; + default: + scnprintf(buf, size, "%s", emsg); + break; + } + + return 0; +} diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 386de10..7f8f1ae 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -169,6 +169,7 @@ static inline struct perf_evsel *perf_evlist__last(struct perf_evlist *evlist) size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp); int perf_evlist__strerror_tp(struct perf_evlist *evlist, int err, char *buf, size_t size); +int perf_evlist__strerror_open(struct perf_evlist *evlist, int err, char *buf, size_t size); static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm) { -- cgit v0.10.2 From 383382501193dc70fb92393eb585a39f9d39b378 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 17 Oct 2013 18:06:46 +0200 Subject: spi/s3c64xx: Add missing pm_runtime_set_active() call in probe() Mark device as PM runtime active during initialization to reflect actual device power/clocks state. This reduces the enable count for SPI bus controller gate clock so it can be disabled when the bus controller is not used. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Kyungmin Park Reviewed-by: Sylwester Nawrocki Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index a80376d..05fab71 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1428,6 +1428,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) S3C64XX_SPI_INT_TX_OVERRUN_EN | S3C64XX_SPI_INT_TX_UNDERRUN_EN, sdd->regs + S3C64XX_SPI_INT_EN); + pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); if (spi_register_master(master)) { -- cgit v0.10.2 From 7b8f7eef1afcb834d701f369dbd2e18b03fd7647 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 17 Oct 2013 14:45:41 +0200 Subject: spi/s3c64xx: Add missing pm_runtime_put on setup fail pm_runtime_put() wasn't called if clock rate could not be set up in s3c64xx_spi_setup() leading to invalid count of device pm_runtime usage. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Sylwester Nawrocki Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 05fab71..ae07c3a 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1121,6 +1121,7 @@ static int s3c64xx_spi_setup(struct spi_device *spi) return 0; setup_exit: + pm_runtime_put(&sdd->pdev->dev); /* setup() returns with device de-selected */ disable_cs(sdd, spi); -- cgit v0.10.2 From beb02cddd64b56081951de9048952f0fa1ff545f Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Thu, 17 Oct 2013 14:01:35 +0400 Subject: ALSA: pxa: slightly refactor reset handling PXA25x also shows some problems when using interrupts during reset handling. Thus do not use interrupts on all pxa kinds (to detect codec ready state). Instead use a common mdelay-loop on all platforms to detect codecs becoming ready. Signed-off-by: Dmitry Eremin-Solenikov Signed-off-by: Mark Brown diff --git a/sound/arm/pxa2xx-ac97-lib.c b/sound/arm/pxa2xx-ac97-lib.c index e6f4633..99a4668 100644 --- a/sound/arm/pxa2xx-ac97-lib.c +++ b/sound/arm/pxa2xx-ac97-lib.c @@ -117,8 +117,7 @@ static inline void pxa_ac97_warm_pxa25x(void) { gsr_bits = 0; - GCR |= GCR_WARM_RST | GCR_PRIRDY_IEN | GCR_SECRDY_IEN; - wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1); + GCR |= GCR_WARM_RST; } static inline void pxa_ac97_cold_pxa25x(void) @@ -129,8 +128,6 @@ static inline void pxa_ac97_cold_pxa25x(void) gsr_bits = 0; GCR = GCR_COLD_RST; - GCR |= GCR_CDONE_IE|GCR_SDONE_IE; - wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1); } #endif @@ -149,8 +146,6 @@ static inline void pxa_ac97_warm_pxa27x(void) static inline void pxa_ac97_cold_pxa27x(void) { - unsigned int timeout; - GCR &= GCR_COLD_RST; /* clear everything but nCRST */ GCR &= ~GCR_COLD_RST; /* then assert nCRST */ @@ -161,29 +156,20 @@ static inline void pxa_ac97_cold_pxa27x(void) udelay(5); clk_disable(ac97conf_clk); GCR = GCR_COLD_RST | GCR_WARM_RST; - timeout = 100; /* wait for the codec-ready bit to be set */ - while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--) - mdelay(1); } #endif #ifdef CONFIG_PXA3xx static inline void pxa_ac97_warm_pxa3xx(void) { - int timeout = 100; - gsr_bits = 0; /* Can't use interrupts */ GCR |= GCR_WARM_RST; - while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--) - mdelay(1); } static inline void pxa_ac97_cold_pxa3xx(void) { - int timeout = 1000; - /* Hold CLKBPB for 100us */ GCR = 0; GCR = GCR_CLKBPB; @@ -199,14 +185,13 @@ static inline void pxa_ac97_cold_pxa3xx(void) GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); GCR = GCR_WARM_RST | GCR_COLD_RST; - while (!(GSR & (GSR_PCR | GSR_SCR)) && timeout--) - mdelay(10); } #endif bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97) { unsigned long gsr; + unsigned int timeout = 100; #ifdef CONFIG_PXA25x if (cpu_is_pxa25x()) @@ -224,6 +209,10 @@ bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97) else #endif BUG(); + + while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--) + mdelay(1); + gsr = GSR | gsr_bits; if (!(gsr & (GSR_PCR | GSR_SCR))) { printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n", @@ -239,6 +228,7 @@ EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_warm_reset); bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97) { unsigned long gsr; + unsigned int timeout = 1000; #ifdef CONFIG_PXA25x if (cpu_is_pxa25x()) @@ -257,6 +247,9 @@ bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97) #endif BUG(); + while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--) + mdelay(1); + gsr = GSR | gsr_bits; if (!(gsr & (GSR_PCR | GSR_SCR))) { printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n", -- cgit v0.10.2 From f62aa9b6c900a9aaf302c1f9ac1883c143afd832 Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Thu, 17 Oct 2013 14:01:36 +0400 Subject: ALSA: ASoC: pxa: fix pxa2xx-ac97 DAI initialization order After recent changes to codec/DAI initialization order changes, codec driver (wm9712 in my case) tries to access codec prior to pxa2xx_ac97_hw_probe() being called (because DAIs are probed after all codecs are probed). Move hw-related probe/remove/suspend/resume functions to pxa2xx-ac97 driver level, instead of DAI level. Signed-off-by: Dmitry Eremin-Solenikov Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index f1059d9..ae956e3 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -89,33 +89,6 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = { .filter_data = &pxa2xx_ac97_pcm_aux_mic_mono_req, }; -#ifdef CONFIG_PM -static int pxa2xx_ac97_suspend(struct snd_soc_dai *dai) -{ - return pxa2xx_ac97_hw_suspend(); -} - -static int pxa2xx_ac97_resume(struct snd_soc_dai *dai) -{ - return pxa2xx_ac97_hw_resume(); -} - -#else -#define pxa2xx_ac97_suspend NULL -#define pxa2xx_ac97_resume NULL -#endif - -static int pxa2xx_ac97_probe(struct snd_soc_dai *dai) -{ - return pxa2xx_ac97_hw_probe(to_platform_device(dai->dev)); -} - -static int pxa2xx_ac97_remove(struct snd_soc_dai *dai) -{ - pxa2xx_ac97_hw_remove(to_platform_device(dai->dev)); - return 0; -} - static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) @@ -185,10 +158,6 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = { { .name = "pxa2xx-ac97", .ac97_control = 1, - .probe = pxa2xx_ac97_probe, - .remove = pxa2xx_ac97_remove, - .suspend = pxa2xx_ac97_suspend, - .resume = pxa2xx_ac97_resume, .playback = { .stream_name = "AC97 Playback", .channels_min = 2, @@ -246,6 +215,12 @@ static int pxa2xx_ac97_dev_probe(struct platform_device *pdev) return -ENXIO; } + ret = pxa2xx_ac97_hw_probe(pdev); + if (ret) { + dev_err(&pdev->dev, "PXA2xx AC97 hw probe error (%d)\n", ret); + return ret; + } + ret = snd_soc_set_ac97_ops(&pxa2xx_ac97_ops); if (ret != 0) return ret; @@ -262,15 +237,34 @@ static int pxa2xx_ac97_dev_remove(struct platform_device *pdev) { snd_soc_unregister_component(&pdev->dev); snd_soc_set_ac97_ops(NULL); + pxa2xx_ac97_hw_remove(pdev); return 0; } +#ifdef CONFIG_PM_SLEEP +static int pxa2xx_ac97_dev_suspend(struct device *dev) +{ + return pxa2xx_ac97_hw_suspend(); +} + +static int pxa2xx_ac97_dev_resume(struct device *dev) +{ + return pxa2xx_ac97_hw_resume(); +} + +static SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, + pxa2xx_ac97_dev_suspend, pxa2xx_ac97_dev_resume); +#endif + static struct platform_driver pxa2xx_ac97_driver = { .probe = pxa2xx_ac97_dev_probe, .remove = pxa2xx_ac97_dev_remove, .driver = { .name = "pxa2xx-ac97", .owner = THIS_MODULE, +#ifdef CONFIG_PM_SLEEP + .pm = &pxa2xx_ac97_pm_ops, +#endif }, }; -- cgit v0.10.2 From 7db1698f728e1176cc7869f22565e3faa8ec2b72 Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Thu, 17 Oct 2013 14:01:37 +0400 Subject: ALSA: ASoC: pxa: add asoc pm callbacks to pxa audio drivers After convertion to snd_soc_register_card, platform driver should reference snd_soc_pm_ops callbacks to properly suspend/resume sound hardware. This was missed during conversion of PXA sound devices. Signed-off-by: Dmitry Eremin-Solenikov Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/brownstone.c b/sound/soc/pxa/brownstone.c index 5b7d969..08acdc2 100644 --- a/sound/soc/pxa/brownstone.c +++ b/sound/soc/pxa/brownstone.c @@ -163,6 +163,7 @@ static struct platform_driver mmp_driver = { .driver = { .name = "brownstone-audio", .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, }, .probe = brownstone_probe, .remove = brownstone_remove, diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c index f4cce1e..1853d41 100644 --- a/sound/soc/pxa/corgi.c +++ b/sound/soc/pxa/corgi.c @@ -329,6 +329,7 @@ static struct platform_driver corgi_driver = { .driver = { .name = "corgi-audio", .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, }, .probe = corgi_probe, .remove = corgi_remove, diff --git a/sound/soc/pxa/e740_wm9705.c b/sound/soc/pxa/e740_wm9705.c index 70d799b..44b5c09 100644 --- a/sound/soc/pxa/e740_wm9705.c +++ b/sound/soc/pxa/e740_wm9705.c @@ -178,6 +178,7 @@ static struct platform_driver e740_driver = { .driver = { .name = "e740-audio", .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, }, .probe = e740_probe, .remove = e740_remove, diff --git a/sound/soc/pxa/e750_wm9705.c b/sound/soc/pxa/e750_wm9705.c index f94d2ab..c34e447 100644 --- a/sound/soc/pxa/e750_wm9705.c +++ b/sound/soc/pxa/e750_wm9705.c @@ -160,6 +160,7 @@ static struct platform_driver e750_driver = { .driver = { .name = "e750-audio", .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, }, .probe = e750_probe, .remove = e750_remove, diff --git a/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c index 8768a64..3137f80 100644 --- a/sound/soc/pxa/e800_wm9712.c +++ b/sound/soc/pxa/e800_wm9712.c @@ -150,6 +150,7 @@ static struct platform_driver e800_driver = { .driver = { .name = "e800-audio", .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, }, .probe = e800_probe, .remove = e800_remove, diff --git a/sound/soc/pxa/imote2.c b/sound/soc/pxa/imote2.c index eef1f7b..fd2f4ed 100644 --- a/sound/soc/pxa/imote2.c +++ b/sound/soc/pxa/imote2.c @@ -91,6 +91,7 @@ static struct platform_driver imote2_driver = { .driver = { .name = "imote2-audio", .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, }, .probe = imote2_probe, .remove = imote2_remove, diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c index bbea778..160c524 100644 --- a/sound/soc/pxa/mioa701_wm9713.c +++ b/sound/soc/pxa/mioa701_wm9713.c @@ -215,6 +215,7 @@ static struct platform_driver mioa701_wm9713_driver = { .driver = { .name = "mioa701-wm9713", .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, }, }; diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c index e1ffcdd..3284c4b 100644 --- a/sound/soc/pxa/palm27x.c +++ b/sound/soc/pxa/palm27x.c @@ -181,6 +181,7 @@ static struct platform_driver palm27x_wm9712_driver = { .driver = { .name = "palm27x-asoc", .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, }, }; diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c index fafe463..c93e138 100644 --- a/sound/soc/pxa/poodle.c +++ b/sound/soc/pxa/poodle.c @@ -303,6 +303,7 @@ static struct platform_driver poodle_driver = { .driver = { .name = "poodle-audio", .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, }, .probe = poodle_probe, .remove = poodle_remove, diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c index a3fe191..1d9c2ed 100644 --- a/sound/soc/pxa/tosa.c +++ b/sound/soc/pxa/tosa.c @@ -275,6 +275,7 @@ static struct platform_driver tosa_driver = { .driver = { .name = "tosa-audio", .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, }, .probe = tosa_probe, .remove = tosa_remove, diff --git a/sound/soc/pxa/ttc-dkb.c b/sound/soc/pxa/ttc-dkb.c index 13c9ee0..0b535b5 100644 --- a/sound/soc/pxa/ttc-dkb.c +++ b/sound/soc/pxa/ttc-dkb.c @@ -160,6 +160,7 @@ static struct platform_driver ttc_dkb_driver = { .driver = { .name = "ttc-dkb-audio", .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, }, .probe = ttc_dkb_probe, .remove = ttc_dkb_remove, -- cgit v0.10.2 From 3d8c8bc0250f7cb11f887691b7473b51adcd2bcb Mon Sep 17 00:00:00 2001 From: Brian Austin Date: Thu, 17 Oct 2013 11:03:33 -0500 Subject: ASoC: cs42l73: Add platform data support for cs42l73 codec Add support for RST GPIO and Charge Pump Freq in platform data Signed-off-by: Brian Austin Signed-off-by: Mark Brown diff --git a/include/sound/cs42l73.h b/include/sound/cs42l73.h new file mode 100644 index 0000000..f354be4 --- /dev/null +++ b/include/sound/cs42l73.h @@ -0,0 +1,22 @@ +/* + * linux/sound/cs42l73.h -- Platform data for CS42L73 + * + * Copyright (c) 2012 Cirrus Logic Inc. + * + * 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 __CS42L73_H +#define __CS42L73_H + +struct cs42l73_platform_data { + /* RST GPIO */ + unsigned int reset_gpio; + unsigned int chgfreq; + int jack_detection; + unsigned int mclk_freq; +}; + +#endif /* __CS42L73_H */ diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c index 3b20c86..db9d396 100644 --- a/sound/soc/codecs/cs42l73.c +++ b/sound/soc/codecs/cs42l73.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,7 @@ #include #include #include +#include #include "cs42l73.h" struct sp_config { @@ -35,6 +37,7 @@ struct sp_config { u32 srate; }; struct cs42l73_private { + struct cs42l73_platform_data pdata; struct sp_config config[3]; struct regmap *regmap; u32 sysclk; @@ -310,15 +313,6 @@ static const struct soc_enum ng_delay_enum = SOC_ENUM_SINGLE(CS42L73_NGCAB, 0, ARRAY_SIZE(cs42l73_ng_delay_text), cs42l73_ng_delay_text); -static const char * const charge_pump_freq_text[] = { - "0", "1", "2", "3", "4", - "5", "6", "7", "8", "9", - "10", "11", "12", "13", "14", "15" }; - -static const struct soc_enum charge_pump_enum = - SOC_ENUM_SINGLE(CS42L73_CPFCHC, 4, - ARRAY_SIZE(charge_pump_freq_text), charge_pump_freq_text); - static const char * const cs42l73_mono_mix_texts[] = { "Left", "Right", "Mono Mix"}; @@ -511,8 +505,6 @@ static const struct snd_kcontrol_new cs42l73_snd_controls[] = { SOC_SINGLE("NG Threshold", CS42L73_NGCAB, 2, 7, 0), SOC_ENUM("NG Delay", ng_delay_enum), - SOC_ENUM("Charge Pump Frequency", charge_pump_enum), - SOC_DOUBLE_R_TLV("XSP-IP Volume", CS42L73_XSPAIPAA, CS42L73_XSPBIPBA, 0, 0x3F, 1, attn_tlv), @@ -1367,11 +1359,16 @@ static int cs42l73_probe(struct snd_soc_codec *codec) return ret; } - regcache_cache_only(cs42l73->regmap, true); - cs42l73_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - cs42l73->mclksel = CS42L73_CLKID_MCLK1; /* MCLK1 as master clk */ + /* Set Charge Pump Frequency */ + if (cs42l73->pdata.chgfreq) + snd_soc_update_bits(codec, CS42L73_CPFCHC, + CS42L73_CHARGEPUMP_MASK, + cs42l73->pdata.chgfreq << 4); + + /* MCLK1 as master clk */ + cs42l73->mclksel = CS42L73_CLKID_MCLK1; cs42l73->mclk = 0; return ret; @@ -1415,6 +1412,7 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client, const struct i2c_device_id *id) { struct cs42l73_private *cs42l73; + struct cs42l73_platform_data *pdata = dev_get_platdata(&i2c_client->dev); int ret; unsigned int devid = 0; unsigned int reg; @@ -1426,14 +1424,32 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client, return -ENOMEM; } - i2c_set_clientdata(i2c_client, cs42l73); - cs42l73->regmap = devm_regmap_init_i2c(i2c_client, &cs42l73_regmap); if (IS_ERR(cs42l73->regmap)) { ret = PTR_ERR(cs42l73->regmap); dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret); return ret; } + + if (pdata) + cs42l73->pdata = *pdata; + + i2c_set_clientdata(i2c_client, cs42l73); + + if (cs42l73->pdata.reset_gpio) { + ret = gpio_request_one(cs42l73->pdata.reset_gpio, + GPIOF_OUT_INIT_HIGH, "CS42L73 /RST"); + if (ret < 0) { + dev_err(&i2c_client->dev, "Failed to request /RST %d: %d\n", + cs42l73->pdata.reset_gpio, ret); + return ret; + } + gpio_set_value_cansleep(cs42l73->pdata.reset_gpio, 0); + gpio_set_value_cansleep(cs42l73->pdata.reset_gpio, 1); + } + + regcache_cache_bypass(cs42l73->regmap, true); + /* initialize codec */ ret = regmap_read(cs42l73->regmap, CS42L73_DEVID_AB, ®); devid = (reg & 0xFF) << 12; @@ -1444,7 +1460,6 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client, ret = regmap_read(cs42l73->regmap, CS42L73_DEVID_E, ®); devid |= (reg & 0xF0) >> 4; - if (devid != CS42L73_DEVID) { ret = -ENODEV; dev_err(&i2c_client->dev, @@ -1462,7 +1477,7 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client, dev_info(&i2c_client->dev, "Cirrus Logic CS42L73, Revision: %02X\n", reg & 0xFF); - regcache_cache_only(cs42l73->regmap, true); + regcache_cache_bypass(cs42l73->regmap, false); ret = snd_soc_register_codec(&i2c_client->dev, &soc_codec_dev_cs42l73, cs42l73_dai, diff --git a/sound/soc/codecs/cs42l73.h b/sound/soc/codecs/cs42l73.h index f30a4c4..4f83d39 100644 --- a/sound/soc/codecs/cs42l73.h +++ b/sound/soc/codecs/cs42l73.h @@ -159,6 +159,7 @@ #define THMOVLD_115C 2 #define THMOVLD_098C 3 +#define CS42L73_CHARGEPUMP_MASK (0xF0) /* CS42L73_ASPC, CS42L73_XSPC, CS42L73_VSPC */ #define SP_3ST (1 << 7) -- cgit v0.10.2 From f9ca060680e7c26a88d990ad9370572274b0d54b Mon Sep 17 00:00:00 2001 From: Brian Austin Date: Thu, 17 Oct 2013 11:03:34 -0500 Subject: ASoC: cs42l73: Namespace defines for cs42l73 codec Cleanup to namespace the defines for the cs42l73 driver Signed-off-by: Brian Austin Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c index db9d396..89efc3c 100644 --- a/sound/soc/codecs/cs42l73.c +++ b/sound/soc/codecs/cs42l73.c @@ -1047,11 +1047,11 @@ static int cs42l73_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: - mmcc |= MS_MASTER; + mmcc |= CS42L73_MS_MASTER; break; case SND_SOC_DAIFMT_CBS_CFS: - mmcc &= ~MS_MASTER; + mmcc &= ~CS42L73_MS_MASTER; break; default: @@ -1063,11 +1063,11 @@ static int cs42l73_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) switch (format) { case SND_SOC_DAIFMT_I2S: - spc &= ~SPDIF_PCM; + spc &= ~CS42L73_SPDIF_PCM; break; case SND_SOC_DAIFMT_DSP_A: case SND_SOC_DAIFMT_DSP_B: - if (mmcc & MS_MASTER) { + if (mmcc & CS42L73_MS_MASTER) { dev_err(codec->dev, "PCM format in slave mode only\n"); return -EINVAL; @@ -1077,25 +1077,25 @@ static int cs42l73_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) "PCM format is not supported on ASP port\n"); return -EINVAL; } - spc |= SPDIF_PCM; + spc |= CS42L73_SPDIF_PCM; break; default: return -EINVAL; } - if (spc & SPDIF_PCM) { + if (spc & CS42L73_SPDIF_PCM) { /* Clear PCM mode, clear PCM_BIT_ORDER bit for MSB->LSB */ - spc &= ~(PCM_MODE_MASK | PCM_BIT_ORDER); + spc &= ~(CS42L73_PCM_MODE_MASK | CS42L73_PCM_BIT_ORDER); switch (format) { case SND_SOC_DAIFMT_DSP_B: if (inv == SND_SOC_DAIFMT_IB_IF) - spc |= PCM_MODE0; + spc |= CS42L73_PCM_MODE0; if (inv == SND_SOC_DAIFMT_IB_NF) - spc |= PCM_MODE1; + spc |= CS42L73_PCM_MODE1; break; case SND_SOC_DAIFMT_DSP_A: if (inv == SND_SOC_DAIFMT_IB_IF) - spc |= PCM_MODE1; + spc |= CS42L73_PCM_MODE1; break; default: return -EINVAL; @@ -1155,7 +1155,7 @@ static int cs42l73_pcm_hw_params(struct snd_pcm_substream *substream, int mclk_coeff; int srate = params_rate(params); - if (priv->config[id].mmcc & MS_MASTER) { + if (priv->config[id].mmcc & CS42L73_MS_MASTER) { /* CS42L73 Master */ /* MCLK -> srate */ mclk_coeff = @@ -1174,13 +1174,13 @@ static int cs42l73_pcm_hw_params(struct snd_pcm_substream *substream, priv->config[id].spc &= 0xFC; /* Use SCLK=64*Fs if internal MCLK >= 6.4MHz */ if (priv->mclk >= 6400000) - priv->config[id].spc |= MCK_SCLK_64FS; + priv->config[id].spc |= CS42L73_MCK_SCLK_64FS; else - priv->config[id].spc |= MCK_SCLK_MCLK; + priv->config[id].spc |= CS42L73_MCK_SCLK_MCLK; } else { /* CS42L73 Slave */ priv->config[id].spc &= 0xFC; - priv->config[id].spc |= MCK_SCLK_64FS; + priv->config[id].spc |= CS42L73_MCK_SCLK_64FS; } /* Update ASRCs */ priv->config[id].srate = srate; @@ -1200,8 +1200,8 @@ static int cs42l73_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_ON: - snd_soc_update_bits(codec, CS42L73_DMMCC, MCLKDIS, 0); - snd_soc_update_bits(codec, CS42L73_PWRCTL1, PDN, 0); + snd_soc_update_bits(codec, CS42L73_DMMCC, CS42L73_MCLKDIS, 0); + snd_soc_update_bits(codec, CS42L73_PWRCTL1, CS42L73_PDN, 0); break; case SND_SOC_BIAS_PREPARE: @@ -1212,11 +1212,11 @@ static int cs42l73_set_bias_level(struct snd_soc_codec *codec, regcache_cache_only(cs42l73->regmap, false); regcache_sync(cs42l73->regmap); } - snd_soc_update_bits(codec, CS42L73_PWRCTL1, PDN, 1); + snd_soc_update_bits(codec, CS42L73_PWRCTL1, CS42L73_PDN, 1); break; case SND_SOC_BIAS_OFF: - snd_soc_update_bits(codec, CS42L73_PWRCTL1, PDN, 1); + snd_soc_update_bits(codec, CS42L73_PWRCTL1, CS42L73_PDN, 1); if (cs42l73->shutdwn_delay > 0) { mdelay(cs42l73->shutdwn_delay); cs42l73->shutdwn_delay = 0; @@ -1225,7 +1225,7 @@ static int cs42l73_set_bias_level(struct snd_soc_codec *codec, * down. */ } - snd_soc_update_bits(codec, CS42L73_DMMCC, MCLKDIS, 1); + snd_soc_update_bits(codec, CS42L73_DMMCC, CS42L73_MCLKDIS, 1); break; } codec->dapm.bias_level = level; diff --git a/sound/soc/codecs/cs42l73.h b/sound/soc/codecs/cs42l73.h index 4f83d39..4574618 100644 --- a/sound/soc/codecs/cs42l73.h +++ b/sound/soc/codecs/cs42l73.h @@ -128,60 +128,60 @@ /* Bitfield Definitions */ /* CS42L73_PWRCTL1 */ -#define PDN_ADCB (1 << 7) -#define PDN_DMICB (1 << 6) -#define PDN_ADCA (1 << 5) -#define PDN_DMICA (1 << 4) -#define PDN_LDO (1 << 2) -#define DISCHG_FILT (1 << 1) -#define PDN (1 << 0) +#define CS42L73_PDN_ADCB (1 << 7) +#define CS42L73_PDN_DMICB (1 << 6) +#define CS42L73_PDN_ADCA (1 << 5) +#define CS42L73_PDN_DMICA (1 << 4) +#define CS42L73_PDN_LDO (1 << 2) +#define CS42L73_DISCHG_FILT (1 << 1) +#define CS42L73_PDN (1 << 0) /* CS42L73_PWRCTL2 */ -#define PDN_MIC2_BIAS (1 << 7) -#define PDN_MIC1_BIAS (1 << 6) -#define PDN_VSP (1 << 4) -#define PDN_ASP_SDOUT (1 << 3) -#define PDN_ASP_SDIN (1 << 2) -#define PDN_XSP_SDOUT (1 << 1) -#define PDN_XSP_SDIN (1 << 0) +#define CS42L73_PDN_MIC2_BIAS (1 << 7) +#define CS42L73_PDN_MIC1_BIAS (1 << 6) +#define CS42L73_PDN_VSP (1 << 4) +#define CS42L73_PDN_ASP_SDOUT (1 << 3) +#define CS42L73_PDN_ASP_SDIN (1 << 2) +#define CS42L73_PDN_XSP_SDOUT (1 << 1) +#define CS42L73_PDN_XSP_SDIN (1 << 0) /* CS42L73_PWRCTL3 */ -#define PDN_THMS (1 << 5) -#define PDN_SPKLO (1 << 4) -#define PDN_EAR (1 << 3) -#define PDN_SPK (1 << 2) -#define PDN_LO (1 << 1) -#define PDN_HP (1 << 0) +#define CS42L73_PDN_THMS (1 << 5) +#define CS42L73_PDN_SPKLO (1 << 4) +#define CS42L73_PDN_EAR (1 << 3) +#define CS42L73_PDN_SPK (1 << 2) +#define CS42L73_PDN_LO (1 << 1) +#define CS42L73_PDN_HP (1 << 0) /* Thermal Overload Detect. Requires interrupt ... */ -#define THMOVLD_150C 0 -#define THMOVLD_132C 1 -#define THMOVLD_115C 2 -#define THMOVLD_098C 3 +#define CS42L73_THMOVLD_150C 0 +#define CS42L73_THMOVLD_132C 1 +#define CS42L73_THMOVLD_115C 2 +#define CS42L73_THMOVLD_098C 3 #define CS42L73_CHARGEPUMP_MASK (0xF0) /* CS42L73_ASPC, CS42L73_XSPC, CS42L73_VSPC */ -#define SP_3ST (1 << 7) -#define SPDIF_I2S (0 << 6) -#define SPDIF_PCM (1 << 6) -#define PCM_MODE0 (0 << 4) -#define PCM_MODE1 (1 << 4) -#define PCM_MODE2 (2 << 4) -#define PCM_MODE_MASK (3 << 4) -#define PCM_BIT_ORDER (1 << 3) -#define MCK_SCLK_64FS (0 << 0) -#define MCK_SCLK_MCLK (2 << 0) -#define MCK_SCLK_PREMCLK (3 << 0) +#define CS42L73_SP_3ST (1 << 7) +#define CS42L73_SPDIF_I2S (0 << 6) +#define CS42L73_SPDIF_PCM (1 << 6) +#define CS42L73_PCM_MODE0 (0 << 4) +#define CS42L73_PCM_MODE1 (1 << 4) +#define CS42L73_PCM_MODE2 (2 << 4) +#define CS42L73_PCM_MODE_MASK (3 << 4) +#define CS42L73_PCM_BIT_ORDER (1 << 3) +#define CS42L73_MCK_SCLK_64FS (0 << 0) +#define CS42L73_MCK_SCLK_MCLK (2 << 0) +#define CS42L73_MCK_SCLK_PREMCLK (3 << 0) /* CS42L73_xSPMMCC */ -#define MS_MASTER (1 << 7) +#define CS42L73_MS_MASTER (1 << 7) /* CS42L73_DMMCC */ -#define MCLKDIS (1 << 0) -#define MCLKSEL_MCLK2 (1 << 4) -#define MCLKSEL_MCLK1 (0 << 4) +#define CS42L73_MCLKDIS (1 << 0) +#define CS42L73_MCLKSEL_MCLK2 (1 << 4) +#define CS42L73_MCLKSEL_MCLK1 (0 << 4) /* CS42L73 MCLK derived from MCLK1 or MCLK2 */ #define CS42L73_CLKID_MCLK1 0 @@ -195,28 +195,26 @@ #define CS42L73_VSP 2 /* IS1, IM1 */ -#define MIC2_SDET (1 << 6) -#define THMOVLD (1 << 4) -#define DIGMIXOVFL (1 << 3) -#define IPBOVFL (1 << 1) -#define IPAOVFL (1 << 0) +#define CS42L73_MIC2_SDET (1 << 6) +#define CS42L73_THMOVLD (1 << 4) +#define CS42L73_DIGMIXOVFL (1 << 3) +#define CS42L73_IPBOVFL (1 << 1) +#define CS42L73_IPAOVFL (1 << 0) /* Analog Softramp */ -#define ANLGOSFT (1 << 0) +#define CS42L73_ANLGOSFT (1 << 0) /* HP A/B Analog Mute */ -#define HPA_MUTE (1 << 7) +#define CS42L73_HPA_MUTE (1 << 7) /* LO A/B Analog Mute */ -#define LOA_MUTE (1 << 7) +#define CS42L73_LOA_MUTE (1 << 7) /* Digital Mute */ -#define HLAD_MUTE (1 << 0) -#define HLBD_MUTE (1 << 1) -#define SPKD_MUTE (1 << 2) -#define ESLD_MUTE (1 << 3) +#define CS42L73_HLAD_MUTE (1 << 0) +#define CS42L73_HLBD_MUTE (1 << 1) +#define CS42L73_SPKD_MUTE (1 << 2) +#define CS42L73_ESLD_MUTE (1 << 3) /* Misc defines for codec */ -#define CS42L73_RESET_GPIO 143 - #define CS42L73_DEVID 0x00042A73 #define CS42L73_MCLKX_MIN 5644800 #define CS42L73_MCLKX_MAX 38400000 -- cgit v0.10.2 From 001d4c7aea587ce0865c07ec45aa56ecbefd431a Mon Sep 17 00:00:00 2001 From: Kuppuswamy Sathyanarayanan Date: Thu, 17 Oct 2013 15:35:25 -0700 Subject: mrst: Fixed printk/pr_* related issues Fixed printk and pr_* related issues in mrst related files. Signed-off-by: Kuppuswamy Sathyanarayanan Link: http://lkml.kernel.org/r/1382049336-21316-2-git-send-email-david.a.cohen@linux.intel.com Signed-off-by: David Cohen Signed-off-by: H. Peter Anvin diff --git a/arch/x86/platform/mrst/early_printk_mrst.c b/arch/x86/platform/mrst/early_printk_mrst.c index 028454f..95880f7 100644 --- a/arch/x86/platform/mrst/early_printk_mrst.c +++ b/arch/x86/platform/mrst/early_printk_mrst.c @@ -213,7 +213,7 @@ static void early_mrst_spi_putc(char c) } if (!timeout) - pr_warning("MRST earlycon: timed out\n"); + pr_warn("MRST earlycon: timed out\n"); else max3110_write_data(c); } diff --git a/arch/x86/platform/mrst/mrst.c b/arch/x86/platform/mrst/mrst.c index 3ca5957..b9aeb54 100644 --- a/arch/x86/platform/mrst/mrst.c +++ b/arch/x86/platform/mrst/mrst.c @@ -328,7 +328,7 @@ static inline int __init setup_x86_mrst_timer(char *arg) else if (strcmp("lapic_and_apbt", arg) == 0) mrst_timer_options = MRST_TIMER_LAPIC_APBT; else { - pr_warning("X86 MRST timer option %s not recognised" + pr_warn("X86 MRST timer option %s not recognised" " use x86_mrst_timer=apbt_only or lapic_and_apbt\n", arg); return -EINVAL; diff --git a/arch/x86/platform/mrst/vrtc.c b/arch/x86/platform/mrst/vrtc.c index 5e355b1..ca4f7d9 100644 --- a/arch/x86/platform/mrst/vrtc.c +++ b/arch/x86/platform/mrst/vrtc.c @@ -79,7 +79,7 @@ void vrtc_get_time(struct timespec *now) /* vRTC YEAR reg contains the offset to 1972 */ year += 1972; - printk(KERN_INFO "vRTC: sec: %d min: %d hour: %d day: %d " + pr_info("vRTC: sec: %d min: %d hour: %d day: %d " "mon: %d year: %d\n", sec, min, hour, mday, mon, year); now->tv_sec = mktime(year, mon, mday, hour, min, sec); @@ -109,8 +109,7 @@ int vrtc_set_mmss(const struct timespec *now) vrtc_cmos_write(tm.tm_sec, RTC_SECONDS); spin_unlock_irqrestore(&rtc_lock, flags); } else { - printk(KERN_ERR - "%s: Invalid vRTC value: write of %lx to vRTC failed\n", + pr_err("%s: Invalid vRTC value: write of %lx to vRTC failed\n", __FUNCTION__, now->tv_sec); retval = -EINVAL; } -- cgit v0.10.2 From d8059302b374b351731ba503bb6f5bc88962d983 Mon Sep 17 00:00:00 2001 From: Kuppuswamy Sathyanarayanan Date: Thu, 17 Oct 2013 15:35:26 -0700 Subject: mrst: Fixed indentation issues Fixed indentation issues reported by checkpatch script in mrst related files. Signed-off-by: Kuppuswamy Sathyanarayanan Link: http://lkml.kernel.org/r/1382049336-21316-3-git-send-email-david.a.cohen@linux.intel.com Signed-off-by: David Cohen Signed-off-by: H. Peter Anvin diff --git a/arch/x86/platform/mrst/early_printk_mrst.c b/arch/x86/platform/mrst/early_printk_mrst.c index 95880f7..39ecc27 100644 --- a/arch/x86/platform/mrst/early_printk_mrst.c +++ b/arch/x86/platform/mrst/early_printk_mrst.c @@ -219,7 +219,8 @@ static void early_mrst_spi_putc(char c) } /* Early SPI only uses polling mode */ -static void early_mrst_spi_write(struct console *con, const char *str, unsigned n) +static void early_mrst_spi_write(struct console *con, const char *str, + unsigned n) { int i; diff --git a/arch/x86/platform/mrst/mrst.c b/arch/x86/platform/mrst/mrst.c index b9aeb54..235a742 100644 --- a/arch/x86/platform/mrst/mrst.c +++ b/arch/x86/platform/mrst/mrst.c @@ -131,7 +131,7 @@ struct sfi_timer_table_entry *sfi_get_mtmr(int hint) int i; if (hint < sfi_mtimer_num) { if (!sfi_mtimer_usage[hint]) { - pr_debug("hint taken for timer %d irq %d\n",\ + pr_debug("hint taken for timer %d irq %d\n", hint, sfi_mtimer_array[hint].irq); sfi_mtimer_usage[hint] = 1; return &sfi_mtimer_array[hint]; @@ -679,14 +679,14 @@ static void *msic_thermal_platform_data(void *info) /* tc35876x DSI-LVDS bridge chip and panel platform data */ static void *tc35876x_platform_data(void *data) { - static struct tc35876x_platform_data pdata; + static struct tc35876x_platform_data pdata; - /* gpio pins set to -1 will not be used by the driver */ - pdata.gpio_bridge_reset = get_gpio_by_name("LCMB_RXEN"); - pdata.gpio_panel_bl_en = get_gpio_by_name("6S6P_BL_EN"); - pdata.gpio_panel_vadd = get_gpio_by_name("EN_VREG_LCD_V3P3"); + /* gpio pins set to -1 will not be used by the driver */ + pdata.gpio_bridge_reset = get_gpio_by_name("LCMB_RXEN"); + pdata.gpio_panel_bl_en = get_gpio_by_name("6S6P_BL_EN"); + pdata.gpio_panel_vadd = get_gpio_by_name("EN_VREG_LCD_V3P3"); - return &pdata; + return &pdata; } static const struct devs_id __initconst device_ids[] = { @@ -729,7 +729,7 @@ static int i2c_next_dev; static void __init intel_scu_device_register(struct platform_device *pdev) { - if(ipc_next_dev == MAX_IPCDEVS) + if (ipc_next_dev == MAX_IPCDEVS) pr_err("too many SCU IPC devices"); else ipc_devs[ipc_next_dev++] = pdev; @@ -872,7 +872,8 @@ static void __init sfi_handle_spi_dev(struct spi_board_info *spi_info) while (dev->name[0]) { if (dev->type == SFI_DEV_TYPE_SPI && - !strncmp(dev->name, spi_info->modalias, SFI_NAME_LEN)) { + !strncmp(dev->name, spi_info->modalias, + SFI_NAME_LEN)) { pdata = dev->get_platform_data(spi_info); break; } @@ -904,7 +905,7 @@ static void __init sfi_handle_i2c_dev(int bus, struct i2c_board_info *i2c_info) intel_scu_i2c_device_register(bus, i2c_info); else i2c_register_board_info(bus, i2c_info, 1); - } +} static int __init sfi_parse_devs(struct sfi_table_header *table) @@ -1034,7 +1035,8 @@ static int __init pb_keys_init(void) num = sizeof(gpio_button) / sizeof(struct gpio_keys_button); for (i = 0; i < num; i++) { gb[i].gpio = get_gpio_by_name(gb[i].desc); - pr_debug("info[%2d]: name = %s, gpio = %d\n", i, gb[i].desc, gb[i].gpio); + pr_debug("info[%2d]: name = %s, gpio = %d\n", i, gb[i].desc, + gb[i].gpio); if (gb[i].gpio == -1) continue; -- cgit v0.10.2 From 05454c26eb3587b56abc5eb139797ac5afb6d77a Mon Sep 17 00:00:00 2001 From: Kuppuswamy Sathyanarayanan Date: Thu, 17 Oct 2013 15:35:27 -0700 Subject: intel_mid: Renamed *mrst* to *intel_mid* Following files contains code that is common to all intel mid soc's. So renamed them as below. mrst/mrst.c -> intel-mid/intel-mid.c mrst/vrtc.c -> intel-mid/intel_mid_vrtc.c mrst/early_printk_mrst.c -> intel-mid/intel_mid_vrtc.c pci/mrst.c -> pci/intel_mid_pci.c Also, renamed the corresponding header files and made changes to the driver files that included these header files. To ensure that there are no functional changes, I have compared the objdump of renamed files before and after rename and found that the only difference is file name change. Signed-off-by: Kuppuswamy Sathyanarayanan Link: http://lkml.kernel.org/r/1382049336-21316-4-git-send-email-david.a.cohen@linux.intel.com Signed-off-by: David Cohen Signed-off-by: H. Peter Anvin diff --git a/arch/x86/include/asm/intel-mid.h b/arch/x86/include/asm/intel-mid.h new file mode 100644 index 0000000..cc79a4f --- /dev/null +++ b/arch/x86/include/asm/intel-mid.h @@ -0,0 +1,81 @@ +/* + * intel-mid.h: Intel MID specific setup code + * + * (C) Copyright 2009 Intel Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ +#ifndef _ASM_X86_INTEL_MID_H +#define _ASM_X86_INTEL_MID_H + +#include + +extern int pci_mrst_init(void); +extern int __init sfi_parse_mrtc(struct sfi_table_header *table); +extern int sfi_mrtc_num; +extern struct sfi_rtc_table_entry sfi_mrtc_array[]; + +/* + * Medfield is the follow-up of Moorestown, it combines two chip solution into + * one. Other than that it also added always-on and constant tsc and lapic + * timers. Medfield is the platform name, and the chip name is called Penwell + * we treat Medfield/Penwell as a variant of Moorestown. Penwell can be + * identified via MSRs. + */ +enum mrst_cpu_type { + /* 1 was Moorestown */ + MRST_CPU_CHIP_PENWELL = 2, +}; + +extern enum mrst_cpu_type __mrst_cpu_chip; + +#ifdef CONFIG_X86_INTEL_MID + +static inline enum mrst_cpu_type mrst_identify_cpu(void) +{ + return __mrst_cpu_chip; +} + +#else /* !CONFIG_X86_INTEL_MID */ + +#define mrst_identify_cpu() (0) + +#endif /* !CONFIG_X86_INTEL_MID */ + +enum mrst_timer_options { + MRST_TIMER_DEFAULT, + MRST_TIMER_APBT_ONLY, + MRST_TIMER_LAPIC_APBT, +}; + +extern enum mrst_timer_options mrst_timer_options; + +/* + * Penwell uses spread spectrum clock, so the freq number is not exactly + * the same as reported by MSR based on SDM. + */ +#define PENWELL_FSB_FREQ_83SKU 83200 +#define PENWELL_FSB_FREQ_100SKU 99840 + +#define SFI_MTMR_MAX_NUM 8 +#define SFI_MRTC_MAX 8 + +extern struct console early_mrst_console; +extern void mrst_early_console_init(void); + +extern struct console early_hsu_console; +extern void hsu_early_console_init(const char *); + +extern void intel_scu_devices_create(void); +extern void intel_scu_devices_destroy(void); + +/* VRTC timer */ +#define MRST_VRTC_MAP_SZ (1024) +/*#define MRST_VRTC_PGOFFSET (0xc00) */ + +extern void mrst_rtc_init(void); + +#endif /* _ASM_X86_INTEL_MID_H */ diff --git a/arch/x86/include/asm/intel_mid_vrtc.h b/arch/x86/include/asm/intel_mid_vrtc.h new file mode 100644 index 0000000..86ff468 --- /dev/null +++ b/arch/x86/include/asm/intel_mid_vrtc.h @@ -0,0 +1,9 @@ +#ifndef _INTEL_MID_VRTC_H +#define _INTEL_MID_VRTC_H + +extern unsigned char vrtc_cmos_read(unsigned char reg); +extern void vrtc_cmos_write(unsigned char val, unsigned char reg); +extern void vrtc_get_time(struct timespec *now); +extern int vrtc_set_mmss(const struct timespec *now); + +#endif diff --git a/arch/x86/include/asm/mrst-vrtc.h b/arch/x86/include/asm/mrst-vrtc.h deleted file mode 100644 index 1e69a75..0000000 --- a/arch/x86/include/asm/mrst-vrtc.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _MRST_VRTC_H -#define _MRST_VRTC_H - -extern unsigned char vrtc_cmos_read(unsigned char reg); -extern void vrtc_cmos_write(unsigned char val, unsigned char reg); -extern void vrtc_get_time(struct timespec *now); -extern int vrtc_set_mmss(const struct timespec *now); - -#endif diff --git a/arch/x86/include/asm/mrst.h b/arch/x86/include/asm/mrst.h deleted file mode 100644 index fc18bf3..0000000 --- a/arch/x86/include/asm/mrst.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * mrst.h: Intel Moorestown platform specific setup code - * - * (C) Copyright 2009 Intel Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License. - */ -#ifndef _ASM_X86_MRST_H -#define _ASM_X86_MRST_H - -#include - -extern int pci_mrst_init(void); -extern int __init sfi_parse_mrtc(struct sfi_table_header *table); -extern int sfi_mrtc_num; -extern struct sfi_rtc_table_entry sfi_mrtc_array[]; - -/* - * Medfield is the follow-up of Moorestown, it combines two chip solution into - * one. Other than that it also added always-on and constant tsc and lapic - * timers. Medfield is the platform name, and the chip name is called Penwell - * we treat Medfield/Penwell as a variant of Moorestown. Penwell can be - * identified via MSRs. - */ -enum mrst_cpu_type { - /* 1 was Moorestown */ - MRST_CPU_CHIP_PENWELL = 2, -}; - -extern enum mrst_cpu_type __mrst_cpu_chip; - -#ifdef CONFIG_X86_INTEL_MID - -static inline enum mrst_cpu_type mrst_identify_cpu(void) -{ - return __mrst_cpu_chip; -} - -#else /* !CONFIG_X86_INTEL_MID */ - -#define mrst_identify_cpu() (0) - -#endif /* !CONFIG_X86_INTEL_MID */ - -enum mrst_timer_options { - MRST_TIMER_DEFAULT, - MRST_TIMER_APBT_ONLY, - MRST_TIMER_LAPIC_APBT, -}; - -extern enum mrst_timer_options mrst_timer_options; - -/* - * Penwell uses spread spectrum clock, so the freq number is not exactly - * the same as reported by MSR based on SDM. - */ -#define PENWELL_FSB_FREQ_83SKU 83200 -#define PENWELL_FSB_FREQ_100SKU 99840 - -#define SFI_MTMR_MAX_NUM 8 -#define SFI_MRTC_MAX 8 - -extern struct console early_mrst_console; -extern void mrst_early_console_init(void); - -extern struct console early_hsu_console; -extern void hsu_early_console_init(const char *); - -extern void intel_scu_devices_create(void); -extern void intel_scu_devices_destroy(void); - -/* VRTC timer */ -#define MRST_VRTC_MAP_SZ (1024) -/*#define MRST_VRTC_PGOFFSET (0xc00) */ - -extern void mrst_rtc_init(void); - -#endif /* _ASM_X86_MRST_H */ diff --git a/arch/x86/kernel/apb_timer.c b/arch/x86/kernel/apb_timer.c index c9876ef..9154836 100644 --- a/arch/x86/kernel/apb_timer.c +++ b/arch/x86/kernel/apb_timer.c @@ -40,7 +40,7 @@ #include #include -#include +#include #include #define APBT_CLOCKEVENT_RATING 110 diff --git a/arch/x86/kernel/early_printk.c b/arch/x86/kernel/early_printk.c index d15f575..38ca398 100644 --- a/arch/x86/kernel/early_printk.c +++ b/arch/x86/kernel/early_printk.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include diff --git a/arch/x86/kernel/rtc.c b/arch/x86/kernel/rtc.c index 0aa2939..a1b52fe 100644 --- a/arch/x86/kernel/rtc.c +++ b/arch/x86/kernel/rtc.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #ifdef CONFIG_X86_32 diff --git a/arch/x86/pci/Makefile b/arch/x86/pci/Makefile index ee0af58..e063eed 100644 --- a/arch/x86/pci/Makefile +++ b/arch/x86/pci/Makefile @@ -18,7 +18,7 @@ obj-$(CONFIG_X86_VISWS) += visws.o obj-$(CONFIG_X86_NUMAQ) += numaq_32.o obj-$(CONFIG_X86_NUMACHIP) += numachip.o -obj-$(CONFIG_X86_INTEL_MID) += mrst.o +obj-$(CONFIG_X86_INTEL_MID) += intel_mid_pci.o obj-y += common.o early.o obj-y += bus_numa.o diff --git a/arch/x86/pci/intel_mid_pci.c b/arch/x86/pci/intel_mid_pci.c new file mode 100644 index 0000000..f8715f7 --- /dev/null +++ b/arch/x86/pci/intel_mid_pci.c @@ -0,0 +1,310 @@ +/* + * Intel MID PCI support + * Copyright (c) 2008 Intel Corporation + * Jesse Barnes + * + * Moorestown has an interesting PCI implementation: + * - configuration space is memory mapped (as defined by MCFG) + * - Lincroft devices also have a real, type 1 configuration space + * - Early Lincroft silicon has a type 1 access bug that will cause + * a hang if non-existent devices are accessed + * - some devices have the "fixed BAR" capability, which means + * they can't be relocated or modified; check for that during + * BAR sizing + * + * So, we use the MCFG space for all reads and writes, but also send + * Lincroft writes to type 1 space. But only read/write if the device + * actually exists, otherwise return all 1s for reads and bit bucket + * the writes. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define PCIE_CAP_OFFSET 0x100 + +/* Fixed BAR fields */ +#define PCIE_VNDR_CAP_ID_FIXED_BAR 0x00 /* Fixed BAR (TBD) */ +#define PCI_FIXED_BAR_0_SIZE 0x04 +#define PCI_FIXED_BAR_1_SIZE 0x08 +#define PCI_FIXED_BAR_2_SIZE 0x0c +#define PCI_FIXED_BAR_3_SIZE 0x10 +#define PCI_FIXED_BAR_4_SIZE 0x14 +#define PCI_FIXED_BAR_5_SIZE 0x1c + +static int pci_soc_mode; + +/** + * fixed_bar_cap - return the offset of the fixed BAR cap if found + * @bus: PCI bus + * @devfn: device in question + * + * Look for the fixed BAR cap on @bus and @devfn, returning its offset + * if found or 0 otherwise. + */ +static int fixed_bar_cap(struct pci_bus *bus, unsigned int devfn) +{ + int pos; + u32 pcie_cap = 0, cap_data; + + pos = PCIE_CAP_OFFSET; + + if (!raw_pci_ext_ops) + return 0; + + while (pos) { + if (raw_pci_ext_ops->read(pci_domain_nr(bus), bus->number, + devfn, pos, 4, &pcie_cap)) + return 0; + + if (PCI_EXT_CAP_ID(pcie_cap) == 0x0000 || + PCI_EXT_CAP_ID(pcie_cap) == 0xffff) + break; + + if (PCI_EXT_CAP_ID(pcie_cap) == PCI_EXT_CAP_ID_VNDR) { + raw_pci_ext_ops->read(pci_domain_nr(bus), bus->number, + devfn, pos + 4, 4, &cap_data); + if ((cap_data & 0xffff) == PCIE_VNDR_CAP_ID_FIXED_BAR) + return pos; + } + + pos = PCI_EXT_CAP_NEXT(pcie_cap); + } + + return 0; +} + +static int pci_device_update_fixed(struct pci_bus *bus, unsigned int devfn, + int reg, int len, u32 val, int offset) +{ + u32 size; + unsigned int domain, busnum; + int bar = (reg - PCI_BASE_ADDRESS_0) >> 2; + + domain = pci_domain_nr(bus); + busnum = bus->number; + + if (val == ~0 && len == 4) { + unsigned long decode; + + raw_pci_ext_ops->read(domain, busnum, devfn, + offset + 8 + (bar * 4), 4, &size); + + /* Turn the size into a decode pattern for the sizing code */ + if (size) { + decode = size - 1; + decode |= decode >> 1; + decode |= decode >> 2; + decode |= decode >> 4; + decode |= decode >> 8; + decode |= decode >> 16; + decode++; + decode = ~(decode - 1); + } else { + decode = 0; + } + + /* + * If val is all ones, the core code is trying to size the reg, + * so update the mmconfig space with the real size. + * + * Note: this assumes the fixed size we got is a power of two. + */ + return raw_pci_ext_ops->write(domain, busnum, devfn, reg, 4, + decode); + } + + /* This is some other kind of BAR write, so just do it. */ + return raw_pci_ext_ops->write(domain, busnum, devfn, reg, len, val); +} + +/** + * type1_access_ok - check whether to use type 1 + * @bus: bus number + * @devfn: device & function in question + * + * If the bus is on a Lincroft chip and it exists, or is not on a Lincroft at + * all, the we can go ahead with any reads & writes. If it's on a Lincroft, + * but doesn't exist, avoid the access altogether to keep the chip from + * hanging. + */ +static bool type1_access_ok(unsigned int bus, unsigned int devfn, int reg) +{ + /* + * This is a workaround for A0 LNC bug where PCI status register does + * not have new CAP bit set. can not be written by SW either. + * + * PCI header type in real LNC indicates a single function device, this + * will prevent probing other devices under the same function in PCI + * shim. Therefore, use the header type in shim instead. + */ + if (reg >= 0x100 || reg == PCI_STATUS || reg == PCI_HEADER_TYPE) + return 0; + if (bus == 0 && (devfn == PCI_DEVFN(2, 0) + || devfn == PCI_DEVFN(0, 0) + || devfn == PCI_DEVFN(3, 0))) + return 1; + return 0; /* Langwell on others */ +} + +static int pci_read(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 *value) +{ + if (type1_access_ok(bus->number, devfn, where)) + return pci_direct_conf1.read(pci_domain_nr(bus), bus->number, + devfn, where, size, value); + return raw_pci_ext_ops->read(pci_domain_nr(bus), bus->number, + devfn, where, size, value); +} + +static int pci_write(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 value) +{ + int offset; + + /* + * On MRST, there is no PCI ROM BAR, this will cause a subsequent read + * to ROM BAR return 0 then being ignored. + */ + if (where == PCI_ROM_ADDRESS) + return 0; + + /* + * Devices with fixed BARs need special handling: + * - BAR sizing code will save, write ~0, read size, restore + * - so writes to fixed BARs need special handling + * - other writes to fixed BAR devices should go through mmconfig + */ + offset = fixed_bar_cap(bus, devfn); + if (offset && + (where >= PCI_BASE_ADDRESS_0 && where <= PCI_BASE_ADDRESS_5)) { + return pci_device_update_fixed(bus, devfn, where, size, value, + offset); + } + + /* + * On Moorestown update both real & mmconfig space + * Note: early Lincroft silicon can't handle type 1 accesses to + * non-existent devices, so just eat the write in that case. + */ + if (type1_access_ok(bus->number, devfn, where)) + return pci_direct_conf1.write(pci_domain_nr(bus), bus->number, + devfn, where, size, value); + return raw_pci_ext_ops->write(pci_domain_nr(bus), bus->number, devfn, + where, size, value); +} + +static int mrst_pci_irq_enable(struct pci_dev *dev) +{ + u8 pin; + struct io_apic_irq_attr irq_attr; + + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + + /* + * MRST only have IOAPIC, the PCI irq lines are 1:1 mapped to + * IOAPIC RTE entries, so we just enable RTE for the device. + */ + irq_attr.ioapic = mp_find_ioapic(dev->irq); + irq_attr.ioapic_pin = dev->irq; + irq_attr.trigger = 1; /* level */ + irq_attr.polarity = 1; /* active low */ + io_apic_set_pci_routing(&dev->dev, dev->irq, &irq_attr); + + return 0; +} + +struct pci_ops pci_mrst_ops = { + .read = pci_read, + .write = pci_write, +}; + +/** + * pci_mrst_init - installs pci_mrst_ops + * + * Moorestown has an interesting PCI implementation (see above). + * Called when the early platform detection installs it. + */ +int __init pci_mrst_init(void) +{ + pr_info("Intel MID platform detected, using MID PCI ops\n"); + pci_mmcfg_late_init(); + pcibios_enable_irq = mrst_pci_irq_enable; + pci_root_ops = pci_mrst_ops; + pci_soc_mode = 1; + /* Continue with standard init */ + return 1; +} + +/* + * Langwell devices are not true PCI devices; they are not subject to 10 ms + * d3 to d0 delay required by PCI spec. + */ +static void pci_d3delay_fixup(struct pci_dev *dev) +{ + /* + * PCI fixups are effectively decided compile time. If we have a dual + * SoC/non-SoC kernel we don't want to mangle d3 on non-SoC devices. + */ + if (!pci_soc_mode) + return; + /* + * True PCI devices in Lincroft should allow type 1 access, the rest + * are Langwell fake PCI devices. + */ + if (type1_access_ok(dev->bus->number, dev->devfn, PCI_DEVICE_ID)) + return; + dev->d3_delay = 0; +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_d3delay_fixup); + +static void mrst_power_off_unused_dev(struct pci_dev *dev) +{ + pci_set_power_state(dev, PCI_D3hot); +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0801, mrst_power_off_unused_dev); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0809, mrst_power_off_unused_dev); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x080C, mrst_power_off_unused_dev); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0812, mrst_power_off_unused_dev); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0815, mrst_power_off_unused_dev); + +/* + * Langwell devices reside at fixed offsets, don't try to move them. + */ +static void pci_fixed_bar_fixup(struct pci_dev *dev) +{ + unsigned long offset; + u32 size; + int i; + + if (!pci_soc_mode) + return; + + /* Must have extended configuration space */ + if (dev->cfg_size < PCIE_CAP_OFFSET + 4) + return; + + /* Fixup the BAR sizes for fixed BAR devices and make them unmoveable */ + offset = fixed_bar_cap(dev->bus, dev->devfn); + if (!offset || PCI_DEVFN(2, 0) == dev->devfn || + PCI_DEVFN(2, 2) == dev->devfn) + return; + + for (i = 0; i < PCI_ROM_RESOURCE; i++) { + pci_read_config_dword(dev, offset + 8 + (i * 4), &size); + dev->resource[i].end = dev->resource[i].start + size - 1; + dev->resource[i].flags |= IORESOURCE_PCI_FIXED; + } +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_fixed_bar_fixup); diff --git a/arch/x86/pci/mrst.c b/arch/x86/pci/mrst.c deleted file mode 100644 index 903fded..0000000 --- a/arch/x86/pci/mrst.c +++ /dev/null @@ -1,310 +0,0 @@ -/* - * Moorestown PCI support - * Copyright (c) 2008 Intel Corporation - * Jesse Barnes - * - * Moorestown has an interesting PCI implementation: - * - configuration space is memory mapped (as defined by MCFG) - * - Lincroft devices also have a real, type 1 configuration space - * - Early Lincroft silicon has a type 1 access bug that will cause - * a hang if non-existent devices are accessed - * - some devices have the "fixed BAR" capability, which means - * they can't be relocated or modified; check for that during - * BAR sizing - * - * So, we use the MCFG space for all reads and writes, but also send - * Lincroft writes to type 1 space. But only read/write if the device - * actually exists, otherwise return all 1s for reads and bit bucket - * the writes. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define PCIE_CAP_OFFSET 0x100 - -/* Fixed BAR fields */ -#define PCIE_VNDR_CAP_ID_FIXED_BAR 0x00 /* Fixed BAR (TBD) */ -#define PCI_FIXED_BAR_0_SIZE 0x04 -#define PCI_FIXED_BAR_1_SIZE 0x08 -#define PCI_FIXED_BAR_2_SIZE 0x0c -#define PCI_FIXED_BAR_3_SIZE 0x10 -#define PCI_FIXED_BAR_4_SIZE 0x14 -#define PCI_FIXED_BAR_5_SIZE 0x1c - -static int pci_soc_mode; - -/** - * fixed_bar_cap - return the offset of the fixed BAR cap if found - * @bus: PCI bus - * @devfn: device in question - * - * Look for the fixed BAR cap on @bus and @devfn, returning its offset - * if found or 0 otherwise. - */ -static int fixed_bar_cap(struct pci_bus *bus, unsigned int devfn) -{ - int pos; - u32 pcie_cap = 0, cap_data; - - pos = PCIE_CAP_OFFSET; - - if (!raw_pci_ext_ops) - return 0; - - while (pos) { - if (raw_pci_ext_ops->read(pci_domain_nr(bus), bus->number, - devfn, pos, 4, &pcie_cap)) - return 0; - - if (PCI_EXT_CAP_ID(pcie_cap) == 0x0000 || - PCI_EXT_CAP_ID(pcie_cap) == 0xffff) - break; - - if (PCI_EXT_CAP_ID(pcie_cap) == PCI_EXT_CAP_ID_VNDR) { - raw_pci_ext_ops->read(pci_domain_nr(bus), bus->number, - devfn, pos + 4, 4, &cap_data); - if ((cap_data & 0xffff) == PCIE_VNDR_CAP_ID_FIXED_BAR) - return pos; - } - - pos = PCI_EXT_CAP_NEXT(pcie_cap); - } - - return 0; -} - -static int pci_device_update_fixed(struct pci_bus *bus, unsigned int devfn, - int reg, int len, u32 val, int offset) -{ - u32 size; - unsigned int domain, busnum; - int bar = (reg - PCI_BASE_ADDRESS_0) >> 2; - - domain = pci_domain_nr(bus); - busnum = bus->number; - - if (val == ~0 && len == 4) { - unsigned long decode; - - raw_pci_ext_ops->read(domain, busnum, devfn, - offset + 8 + (bar * 4), 4, &size); - - /* Turn the size into a decode pattern for the sizing code */ - if (size) { - decode = size - 1; - decode |= decode >> 1; - decode |= decode >> 2; - decode |= decode >> 4; - decode |= decode >> 8; - decode |= decode >> 16; - decode++; - decode = ~(decode - 1); - } else { - decode = 0; - } - - /* - * If val is all ones, the core code is trying to size the reg, - * so update the mmconfig space with the real size. - * - * Note: this assumes the fixed size we got is a power of two. - */ - return raw_pci_ext_ops->write(domain, busnum, devfn, reg, 4, - decode); - } - - /* This is some other kind of BAR write, so just do it. */ - return raw_pci_ext_ops->write(domain, busnum, devfn, reg, len, val); -} - -/** - * type1_access_ok - check whether to use type 1 - * @bus: bus number - * @devfn: device & function in question - * - * If the bus is on a Lincroft chip and it exists, or is not on a Lincroft at - * all, the we can go ahead with any reads & writes. If it's on a Lincroft, - * but doesn't exist, avoid the access altogether to keep the chip from - * hanging. - */ -static bool type1_access_ok(unsigned int bus, unsigned int devfn, int reg) -{ - /* - * This is a workaround for A0 LNC bug where PCI status register does - * not have new CAP bit set. can not be written by SW either. - * - * PCI header type in real LNC indicates a single function device, this - * will prevent probing other devices under the same function in PCI - * shim. Therefore, use the header type in shim instead. - */ - if (reg >= 0x100 || reg == PCI_STATUS || reg == PCI_HEADER_TYPE) - return 0; - if (bus == 0 && (devfn == PCI_DEVFN(2, 0) - || devfn == PCI_DEVFN(0, 0) - || devfn == PCI_DEVFN(3, 0))) - return 1; - return 0; /* Langwell on others */ -} - -static int pci_read(struct pci_bus *bus, unsigned int devfn, int where, - int size, u32 *value) -{ - if (type1_access_ok(bus->number, devfn, where)) - return pci_direct_conf1.read(pci_domain_nr(bus), bus->number, - devfn, where, size, value); - return raw_pci_ext_ops->read(pci_domain_nr(bus), bus->number, - devfn, where, size, value); -} - -static int pci_write(struct pci_bus *bus, unsigned int devfn, int where, - int size, u32 value) -{ - int offset; - - /* - * On MRST, there is no PCI ROM BAR, this will cause a subsequent read - * to ROM BAR return 0 then being ignored. - */ - if (where == PCI_ROM_ADDRESS) - return 0; - - /* - * Devices with fixed BARs need special handling: - * - BAR sizing code will save, write ~0, read size, restore - * - so writes to fixed BARs need special handling - * - other writes to fixed BAR devices should go through mmconfig - */ - offset = fixed_bar_cap(bus, devfn); - if (offset && - (where >= PCI_BASE_ADDRESS_0 && where <= PCI_BASE_ADDRESS_5)) { - return pci_device_update_fixed(bus, devfn, where, size, value, - offset); - } - - /* - * On Moorestown update both real & mmconfig space - * Note: early Lincroft silicon can't handle type 1 accesses to - * non-existent devices, so just eat the write in that case. - */ - if (type1_access_ok(bus->number, devfn, where)) - return pci_direct_conf1.write(pci_domain_nr(bus), bus->number, - devfn, where, size, value); - return raw_pci_ext_ops->write(pci_domain_nr(bus), bus->number, devfn, - where, size, value); -} - -static int mrst_pci_irq_enable(struct pci_dev *dev) -{ - u8 pin; - struct io_apic_irq_attr irq_attr; - - pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); - - /* - * MRST only have IOAPIC, the PCI irq lines are 1:1 mapped to - * IOAPIC RTE entries, so we just enable RTE for the device. - */ - irq_attr.ioapic = mp_find_ioapic(dev->irq); - irq_attr.ioapic_pin = dev->irq; - irq_attr.trigger = 1; /* level */ - irq_attr.polarity = 1; /* active low */ - io_apic_set_pci_routing(&dev->dev, dev->irq, &irq_attr); - - return 0; -} - -struct pci_ops pci_mrst_ops = { - .read = pci_read, - .write = pci_write, -}; - -/** - * pci_mrst_init - installs pci_mrst_ops - * - * Moorestown has an interesting PCI implementation (see above). - * Called when the early platform detection installs it. - */ -int __init pci_mrst_init(void) -{ - pr_info("Intel MID platform detected, using MID PCI ops\n"); - pci_mmcfg_late_init(); - pcibios_enable_irq = mrst_pci_irq_enable; - pci_root_ops = pci_mrst_ops; - pci_soc_mode = 1; - /* Continue with standard init */ - return 1; -} - -/* - * Langwell devices are not true PCI devices; they are not subject to 10 ms - * d3 to d0 delay required by PCI spec. - */ -static void pci_d3delay_fixup(struct pci_dev *dev) -{ - /* - * PCI fixups are effectively decided compile time. If we have a dual - * SoC/non-SoC kernel we don't want to mangle d3 on non-SoC devices. - */ - if (!pci_soc_mode) - return; - /* - * True PCI devices in Lincroft should allow type 1 access, the rest - * are Langwell fake PCI devices. - */ - if (type1_access_ok(dev->bus->number, dev->devfn, PCI_DEVICE_ID)) - return; - dev->d3_delay = 0; -} -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_d3delay_fixup); - -static void mrst_power_off_unused_dev(struct pci_dev *dev) -{ - pci_set_power_state(dev, PCI_D3hot); -} -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0801, mrst_power_off_unused_dev); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0809, mrst_power_off_unused_dev); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x080C, mrst_power_off_unused_dev); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0812, mrst_power_off_unused_dev); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0815, mrst_power_off_unused_dev); - -/* - * Langwell devices reside at fixed offsets, don't try to move them. - */ -static void pci_fixed_bar_fixup(struct pci_dev *dev) -{ - unsigned long offset; - u32 size; - int i; - - if (!pci_soc_mode) - return; - - /* Must have extended configuration space */ - if (dev->cfg_size < PCIE_CAP_OFFSET + 4) - return; - - /* Fixup the BAR sizes for fixed BAR devices and make them unmoveable */ - offset = fixed_bar_cap(dev->bus, dev->devfn); - if (!offset || PCI_DEVFN(2, 0) == dev->devfn || - PCI_DEVFN(2, 2) == dev->devfn) - return; - - for (i = 0; i < PCI_ROM_RESOURCE; i++) { - pci_read_config_dword(dev, offset + 8 + (i * 4), &size); - dev->resource[i].end = dev->resource[i].start + size - 1; - dev->resource[i].flags |= IORESOURCE_PCI_FIXED; - } -} -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_fixed_bar_fixup); diff --git a/arch/x86/platform/Makefile b/arch/x86/platform/Makefile index 01e0231..20342d4 100644 --- a/arch/x86/platform/Makefile +++ b/arch/x86/platform/Makefile @@ -4,7 +4,7 @@ obj-y += efi/ obj-y += geode/ obj-y += goldfish/ obj-y += iris/ -obj-y += mrst/ +obj-y += intel-mid/ obj-y += olpc/ obj-y += scx200/ obj-y += sfi/ diff --git a/arch/x86/platform/intel-mid/Makefile b/arch/x86/platform/intel-mid/Makefile new file mode 100644 index 0000000..de29635 --- /dev/null +++ b/arch/x86/platform/intel-mid/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_X86_INTEL_MID) += intel-mid.o +obj-$(CONFIG_X86_INTEL_MID) += intel_mid_vrtc.o +obj-$(CONFIG_EARLY_PRINTK_INTEL_MID) += early_printk_intel_mid.o diff --git a/arch/x86/platform/intel-mid/early_printk_intel_mid.c b/arch/x86/platform/intel-mid/early_printk_intel_mid.c new file mode 100644 index 0000000..7c56e70 --- /dev/null +++ b/arch/x86/platform/intel-mid/early_printk_intel_mid.c @@ -0,0 +1,325 @@ +/* + * early_printk_intel_mid.c - early consoles for Intel MID platforms + * + * Copyright (c) 2008-2010, Intel Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +/* + * This file implements two early consoles named mrst and hsu. + * mrst is based on Maxim3110 spi-uart device, it exists in both + * Moorestown and Medfield platforms, while hsu is based on a High + * Speed UART device which only exists in the Medfield platform + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MRST_SPI_TIMEOUT 0x200000 +#define MRST_REGBASE_SPI0 0xff128000 +#define MRST_REGBASE_SPI1 0xff128400 +#define MRST_CLK_SPI0_REG 0xff11d86c + +/* Bit fields in CTRLR0 */ +#define SPI_DFS_OFFSET 0 + +#define SPI_FRF_OFFSET 4 +#define SPI_FRF_SPI 0x0 +#define SPI_FRF_SSP 0x1 +#define SPI_FRF_MICROWIRE 0x2 +#define SPI_FRF_RESV 0x3 + +#define SPI_MODE_OFFSET 6 +#define SPI_SCPH_OFFSET 6 +#define SPI_SCOL_OFFSET 7 +#define SPI_TMOD_OFFSET 8 +#define SPI_TMOD_TR 0x0 /* xmit & recv */ +#define SPI_TMOD_TO 0x1 /* xmit only */ +#define SPI_TMOD_RO 0x2 /* recv only */ +#define SPI_TMOD_EPROMREAD 0x3 /* eeprom read mode */ + +#define SPI_SLVOE_OFFSET 10 +#define SPI_SRL_OFFSET 11 +#define SPI_CFS_OFFSET 12 + +/* Bit fields in SR, 7 bits */ +#define SR_MASK 0x7f /* cover 7 bits */ +#define SR_BUSY (1 << 0) +#define SR_TF_NOT_FULL (1 << 1) +#define SR_TF_EMPT (1 << 2) +#define SR_RF_NOT_EMPT (1 << 3) +#define SR_RF_FULL (1 << 4) +#define SR_TX_ERR (1 << 5) +#define SR_DCOL (1 << 6) + +struct dw_spi_reg { + u32 ctrl0; + u32 ctrl1; + u32 ssienr; + u32 mwcr; + u32 ser; + u32 baudr; + u32 txfltr; + u32 rxfltr; + u32 txflr; + u32 rxflr; + u32 sr; + u32 imr; + u32 isr; + u32 risr; + u32 txoicr; + u32 rxoicr; + u32 rxuicr; + u32 msticr; + u32 icr; + u32 dmacr; + u32 dmatdlr; + u32 dmardlr; + u32 idr; + u32 version; + + /* Currently operates as 32 bits, though only the low 16 bits matter */ + u32 dr; +} __packed; + +#define dw_readl(dw, name) __raw_readl(&(dw)->name) +#define dw_writel(dw, name, val) __raw_writel((val), &(dw)->name) + +/* Default use SPI0 register for mrst, we will detect Penwell and use SPI1 */ +static unsigned long mrst_spi_paddr = MRST_REGBASE_SPI0; + +static u32 *pclk_spi0; +/* Always contains an accessible address, start with 0 */ +static struct dw_spi_reg *pspi; + +static struct kmsg_dumper dw_dumper; +static int dumper_registered; + +static void dw_kmsg_dump(struct kmsg_dumper *dumper, + enum kmsg_dump_reason reason) +{ + static char line[1024]; + size_t len; + + /* When run to this, we'd better re-init the HW */ + mrst_early_console_init(); + + while (kmsg_dump_get_line(dumper, true, line, sizeof(line), &len)) + early_mrst_console.write(&early_mrst_console, line, len); +} + +/* Set the ratio rate to 115200, 8n1, IRQ disabled */ +static void max3110_write_config(void) +{ + u16 config; + + config = 0xc001; + dw_writel(pspi, dr, config); +} + +/* Translate char to a eligible word and send to max3110 */ +static void max3110_write_data(char c) +{ + u16 data; + + data = 0x8000 | c; + dw_writel(pspi, dr, data); +} + +void mrst_early_console_init(void) +{ + u32 ctrlr0 = 0; + u32 spi0_cdiv; + u32 freq; /* Freqency info only need be searched once */ + + /* Base clk is 100 MHz, the actual clk = 100M / (clk_divider + 1) */ + pclk_spi0 = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, + MRST_CLK_SPI0_REG); + spi0_cdiv = ((*pclk_spi0) & 0xe00) >> 9; + freq = 100000000 / (spi0_cdiv + 1); + + if (mrst_identify_cpu() == MRST_CPU_CHIP_PENWELL) + mrst_spi_paddr = MRST_REGBASE_SPI1; + + pspi = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, + mrst_spi_paddr); + + /* Disable SPI controller */ + dw_writel(pspi, ssienr, 0); + + /* Set control param, 8 bits, transmit only mode */ + ctrlr0 = dw_readl(pspi, ctrl0); + + ctrlr0 &= 0xfcc0; + ctrlr0 |= 0xf | (SPI_FRF_SPI << SPI_FRF_OFFSET) + | (SPI_TMOD_TO << SPI_TMOD_OFFSET); + dw_writel(pspi, ctrl0, ctrlr0); + + /* + * Change the spi0 clk to comply with 115200 bps, use 100000 to + * calculate the clk dividor to make the clock a little slower + * than real baud rate. + */ + dw_writel(pspi, baudr, freq/100000); + + /* Disable all INT for early phase */ + dw_writel(pspi, imr, 0x0); + + /* Set the cs to spi-uart */ + dw_writel(pspi, ser, 0x2); + + /* Enable the HW, the last step for HW init */ + dw_writel(pspi, ssienr, 0x1); + + /* Set the default configuration */ + max3110_write_config(); + + /* Register the kmsg dumper */ + if (!dumper_registered) { + dw_dumper.dump = dw_kmsg_dump; + kmsg_dump_register(&dw_dumper); + dumper_registered = 1; + } +} + +/* Slave select should be called in the read/write function */ +static void early_mrst_spi_putc(char c) +{ + unsigned int timeout; + u32 sr; + + timeout = MRST_SPI_TIMEOUT; + /* Early putc needs to make sure the TX FIFO is not full */ + while (--timeout) { + sr = dw_readl(pspi, sr); + if (!(sr & SR_TF_NOT_FULL)) + cpu_relax(); + else + break; + } + + if (!timeout) + pr_warn("MRST earlycon: timed out\n"); + else + max3110_write_data(c); +} + +/* Early SPI only uses polling mode */ +static void early_mrst_spi_write(struct console *con, const char *str, + unsigned n) +{ + int i; + + for (i = 0; i < n && *str; i++) { + if (*str == '\n') + early_mrst_spi_putc('\r'); + early_mrst_spi_putc(*str); + str++; + } +} + +struct console early_mrst_console = { + .name = "earlymrst", + .write = early_mrst_spi_write, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +/* + * Following is the early console based on Medfield HSU (High + * Speed UART) device. + */ +#define HSU_PORT_BASE 0xffa28080 + +static void __iomem *phsu; + +void hsu_early_console_init(const char *s) +{ + unsigned long paddr, port = 0; + u8 lcr; + + /* + * Select the early HSU console port if specified by user in the + * kernel command line. + */ + if (*s && !kstrtoul(s, 10, &port)) + port = clamp_val(port, 0, 2); + + paddr = HSU_PORT_BASE + port * 0x80; + phsu = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, paddr); + + /* Disable FIFO */ + writeb(0x0, phsu + UART_FCR); + + /* Set to default 115200 bps, 8n1 */ + lcr = readb(phsu + UART_LCR); + writeb((0x80 | lcr), phsu + UART_LCR); + writeb(0x18, phsu + UART_DLL); + writeb(lcr, phsu + UART_LCR); + writel(0x3600, phsu + UART_MUL*4); + + writeb(0x8, phsu + UART_MCR); + writeb(0x7, phsu + UART_FCR); + writeb(0x3, phsu + UART_LCR); + + /* Clear IRQ status */ + readb(phsu + UART_LSR); + readb(phsu + UART_RX); + readb(phsu + UART_IIR); + readb(phsu + UART_MSR); + + /* Enable FIFO */ + writeb(0x7, phsu + UART_FCR); +} + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +static void early_hsu_putc(char ch) +{ + unsigned int timeout = 10000; /* 10ms */ + u8 status; + + while (--timeout) { + status = readb(phsu + UART_LSR); + if (status & BOTH_EMPTY) + break; + udelay(1); + } + + /* Only write the char when there was no timeout */ + if (timeout) + writeb(ch, phsu + UART_TX); +} + +static void early_hsu_write(struct console *con, const char *str, unsigned n) +{ + int i; + + for (i = 0; i < n && *str; i++) { + if (*str == '\n') + early_hsu_putc('\r'); + early_hsu_putc(*str); + str++; + } +} + +struct console early_hsu_console = { + .name = "earlyhsu", + .write = early_hsu_write, + .flags = CON_PRINTBUFFER, + .index = -1, +}; diff --git a/arch/x86/platform/intel-mid/intel-mid.c b/arch/x86/platform/intel-mid/intel-mid.c new file mode 100644 index 0000000..7e6d7b2 --- /dev/null +++ b/arch/x86/platform/intel-mid/intel-mid.c @@ -0,0 +1,1055 @@ +/* + * intel-mid.c: Intel MID platform setup code + * + * (C) Copyright 2008, 2012 Intel Corporation + * Author: Jacob Pan (jacob.jun.pan@intel.com) + * Author: Sathyanarayanan Kuppuswamy + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#define pr_fmt(fmt) "mrst: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * the clockevent devices on Moorestown/Medfield can be APBT or LAPIC clock, + * cmdline option x86_mrst_timer can be used to override the configuration + * to prefer one or the other. + * at runtime, there are basically three timer configurations: + * 1. per cpu apbt clock only + * 2. per cpu always-on lapic clocks only, this is Penwell/Medfield only + * 3. per cpu lapic clock (C3STOP) and one apbt clock, with broadcast. + * + * by default (without cmdline option), platform code first detects cpu type + * to see if we are on lincroft or penwell, then set up both lapic or apbt + * clocks accordingly. + * i.e. by default, medfield uses configuration #2, moorestown uses #1. + * config #3 is supported but not recommended on medfield. + * + * rating and feature summary: + * lapic (with C3STOP) --------- 100 + * apbt (always-on) ------------ 110 + * lapic (always-on,ARAT) ------ 150 + */ + +enum mrst_timer_options mrst_timer_options; + +static u32 sfi_mtimer_usage[SFI_MTMR_MAX_NUM]; +static struct sfi_timer_table_entry sfi_mtimer_array[SFI_MTMR_MAX_NUM]; +enum mrst_cpu_type __mrst_cpu_chip; +EXPORT_SYMBOL_GPL(__mrst_cpu_chip); + +int sfi_mtimer_num; + +struct sfi_rtc_table_entry sfi_mrtc_array[SFI_MRTC_MAX]; +EXPORT_SYMBOL_GPL(sfi_mrtc_array); +int sfi_mrtc_num; + +static void mrst_power_off(void) +{ +} + +static void mrst_reboot(void) +{ + intel_scu_ipc_simple_command(IPCMSG_COLD_BOOT, 0); +} + +/* parse all the mtimer info to a static mtimer array */ +static int __init sfi_parse_mtmr(struct sfi_table_header *table) +{ + struct sfi_table_simple *sb; + struct sfi_timer_table_entry *pentry; + struct mpc_intsrc mp_irq; + int totallen; + + sb = (struct sfi_table_simple *)table; + if (!sfi_mtimer_num) { + sfi_mtimer_num = SFI_GET_NUM_ENTRIES(sb, + struct sfi_timer_table_entry); + pentry = (struct sfi_timer_table_entry *) sb->pentry; + totallen = sfi_mtimer_num * sizeof(*pentry); + memcpy(sfi_mtimer_array, pentry, totallen); + } + + pr_debug("SFI MTIMER info (num = %d):\n", sfi_mtimer_num); + pentry = sfi_mtimer_array; + for (totallen = 0; totallen < sfi_mtimer_num; totallen++, pentry++) { + pr_debug("timer[%d]: paddr = 0x%08x, freq = %dHz," + " irq = %d\n", totallen, (u32)pentry->phys_addr, + pentry->freq_hz, pentry->irq); + if (!pentry->irq) + continue; + mp_irq.type = MP_INTSRC; + mp_irq.irqtype = mp_INT; +/* triggering mode edge bit 2-3, active high polarity bit 0-1 */ + mp_irq.irqflag = 5; + mp_irq.srcbus = MP_BUS_ISA; + mp_irq.srcbusirq = pentry->irq; /* IRQ */ + mp_irq.dstapic = MP_APIC_ALL; + mp_irq.dstirq = pentry->irq; + mp_save_irq(&mp_irq); + } + + return 0; +} + +struct sfi_timer_table_entry *sfi_get_mtmr(int hint) +{ + int i; + if (hint < sfi_mtimer_num) { + if (!sfi_mtimer_usage[hint]) { + pr_debug("hint taken for timer %d irq %d\n", + hint, sfi_mtimer_array[hint].irq); + sfi_mtimer_usage[hint] = 1; + return &sfi_mtimer_array[hint]; + } + } + /* take the first timer available */ + for (i = 0; i < sfi_mtimer_num;) { + if (!sfi_mtimer_usage[i]) { + sfi_mtimer_usage[i] = 1; + return &sfi_mtimer_array[i]; + } + i++; + } + return NULL; +} + +void sfi_free_mtmr(struct sfi_timer_table_entry *mtmr) +{ + int i; + for (i = 0; i < sfi_mtimer_num;) { + if (mtmr->irq == sfi_mtimer_array[i].irq) { + sfi_mtimer_usage[i] = 0; + return; + } + i++; + } +} + +/* parse all the mrtc info to a global mrtc array */ +int __init sfi_parse_mrtc(struct sfi_table_header *table) +{ + struct sfi_table_simple *sb; + struct sfi_rtc_table_entry *pentry; + struct mpc_intsrc mp_irq; + + int totallen; + + sb = (struct sfi_table_simple *)table; + if (!sfi_mrtc_num) { + sfi_mrtc_num = SFI_GET_NUM_ENTRIES(sb, + struct sfi_rtc_table_entry); + pentry = (struct sfi_rtc_table_entry *)sb->pentry; + totallen = sfi_mrtc_num * sizeof(*pentry); + memcpy(sfi_mrtc_array, pentry, totallen); + } + + pr_debug("SFI RTC info (num = %d):\n", sfi_mrtc_num); + pentry = sfi_mrtc_array; + for (totallen = 0; totallen < sfi_mrtc_num; totallen++, pentry++) { + pr_debug("RTC[%d]: paddr = 0x%08x, irq = %d\n", + totallen, (u32)pentry->phys_addr, pentry->irq); + mp_irq.type = MP_INTSRC; + mp_irq.irqtype = mp_INT; + mp_irq.irqflag = 0xf; /* level trigger and active low */ + mp_irq.srcbus = MP_BUS_ISA; + mp_irq.srcbusirq = pentry->irq; /* IRQ */ + mp_irq.dstapic = MP_APIC_ALL; + mp_irq.dstirq = pentry->irq; + mp_save_irq(&mp_irq); + } + return 0; +} + +static unsigned long __init mrst_calibrate_tsc(void) +{ + unsigned long fast_calibrate; + u32 lo, hi, ratio, fsb; + + rdmsr(MSR_IA32_PERF_STATUS, lo, hi); + pr_debug("IA32 perf status is 0x%x, 0x%0x\n", lo, hi); + ratio = (hi >> 8) & 0x1f; + pr_debug("ratio is %d\n", ratio); + if (!ratio) { + pr_err("read a zero ratio, should be incorrect!\n"); + pr_err("force tsc ratio to 16 ...\n"); + ratio = 16; + } + rdmsr(MSR_FSB_FREQ, lo, hi); + if ((lo & 0x7) == 0x7) + fsb = PENWELL_FSB_FREQ_83SKU; + else + fsb = PENWELL_FSB_FREQ_100SKU; + fast_calibrate = ratio * fsb; + pr_debug("read penwell tsc %lu khz\n", fast_calibrate); + lapic_timer_frequency = fsb * 1000 / HZ; + /* mark tsc clocksource as reliable */ + set_cpu_cap(&boot_cpu_data, X86_FEATURE_TSC_RELIABLE); + + if (fast_calibrate) + return fast_calibrate; + + return 0; +} + +static void __init mrst_time_init(void) +{ + sfi_table_parse(SFI_SIG_MTMR, NULL, NULL, sfi_parse_mtmr); + switch (mrst_timer_options) { + case MRST_TIMER_APBT_ONLY: + break; + case MRST_TIMER_LAPIC_APBT: + x86_init.timers.setup_percpu_clockev = setup_boot_APIC_clock; + x86_cpuinit.setup_percpu_clockev = setup_secondary_APIC_clock; + break; + default: + if (!boot_cpu_has(X86_FEATURE_ARAT)) + break; + x86_init.timers.setup_percpu_clockev = setup_boot_APIC_clock; + x86_cpuinit.setup_percpu_clockev = setup_secondary_APIC_clock; + return; + } + /* we need at least one APB timer */ + pre_init_apic_IRQ0(); + apbt_time_init(); +} + +static void mrst_arch_setup(void) +{ + if (boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model == 0x27) + __mrst_cpu_chip = MRST_CPU_CHIP_PENWELL; + else { + pr_err("Unknown Intel MID CPU (%d:%d), default to Penwell\n", + boot_cpu_data.x86, boot_cpu_data.x86_model); + __mrst_cpu_chip = MRST_CPU_CHIP_PENWELL; + } +} + +/* MID systems don't have i8042 controller */ +static int mrst_i8042_detect(void) +{ + return 0; +} + +/* + * Moorestown does not have external NMI source nor port 0x61 to report + * NMI status. The possible NMI sources are from pmu as a result of NMI + * watchdog or lock debug. Reading io port 0x61 results in 0xff which + * misled NMI handler. + */ +static unsigned char mrst_get_nmi_reason(void) +{ + return 0; +} + +/* + * Moorestown specific x86_init function overrides and early setup + * calls. + */ +void __init x86_mrst_early_setup(void) +{ + x86_init.resources.probe_roms = x86_init_noop; + x86_init.resources.reserve_resources = x86_init_noop; + + x86_init.timers.timer_init = mrst_time_init; + x86_init.timers.setup_percpu_clockev = x86_init_noop; + + x86_init.irqs.pre_vector_init = x86_init_noop; + + x86_init.oem.arch_setup = mrst_arch_setup; + + x86_cpuinit.setup_percpu_clockev = apbt_setup_secondary_clock; + + x86_platform.calibrate_tsc = mrst_calibrate_tsc; + x86_platform.i8042_detect = mrst_i8042_detect; + x86_init.timers.wallclock_init = mrst_rtc_init; + x86_platform.get_nmi_reason = mrst_get_nmi_reason; + + x86_init.pci.init = pci_mrst_init; + x86_init.pci.fixup_irqs = x86_init_noop; + + legacy_pic = &null_legacy_pic; + + /* Moorestown specific power_off/restart method */ + pm_power_off = mrst_power_off; + machine_ops.emergency_restart = mrst_reboot; + + /* Avoid searching for BIOS MP tables */ + x86_init.mpparse.find_smp_config = x86_init_noop; + x86_init.mpparse.get_smp_config = x86_init_uint_noop; + set_bit(MP_BUS_ISA, mp_bus_not_pci); +} + +/* + * if user does not want to use per CPU apb timer, just give it a lower rating + * than local apic timer and skip the late per cpu timer init. + */ +static inline int __init setup_x86_mrst_timer(char *arg) +{ + if (!arg) + return -EINVAL; + + if (strcmp("apbt_only", arg) == 0) + mrst_timer_options = MRST_TIMER_APBT_ONLY; + else if (strcmp("lapic_and_apbt", arg) == 0) + mrst_timer_options = MRST_TIMER_LAPIC_APBT; + else { + pr_warn("X86 MRST timer option %s not recognised" + " use x86_mrst_timer=apbt_only or lapic_and_apbt\n", + arg); + return -EINVAL; + } + return 0; +} +__setup("x86_mrst_timer=", setup_x86_mrst_timer); + +/* + * Parsing GPIO table first, since the DEVS table will need this table + * to map the pin name to the actual pin. + */ +static struct sfi_gpio_table_entry *gpio_table; +static int gpio_num_entry; + +static int __init sfi_parse_gpio(struct sfi_table_header *table) +{ + struct sfi_table_simple *sb; + struct sfi_gpio_table_entry *pentry; + int num, i; + + if (gpio_table) + return 0; + sb = (struct sfi_table_simple *)table; + num = SFI_GET_NUM_ENTRIES(sb, struct sfi_gpio_table_entry); + pentry = (struct sfi_gpio_table_entry *)sb->pentry; + + gpio_table = kmalloc(num * sizeof(*pentry), GFP_KERNEL); + if (!gpio_table) + return -1; + memcpy(gpio_table, pentry, num * sizeof(*pentry)); + gpio_num_entry = num; + + pr_debug("GPIO pin info:\n"); + for (i = 0; i < num; i++, pentry++) + pr_debug("info[%2d]: controller = %16.16s, pin_name = %16.16s," + " pin = %d\n", i, + pentry->controller_name, + pentry->pin_name, + pentry->pin_no); + return 0; +} + +static int get_gpio_by_name(const char *name) +{ + struct sfi_gpio_table_entry *pentry = gpio_table; + int i; + + if (!pentry) + return -1; + for (i = 0; i < gpio_num_entry; i++, pentry++) { + if (!strncmp(name, pentry->pin_name, SFI_NAME_LEN)) + return pentry->pin_no; + } + return -1; +} + +/* + * Here defines the array of devices platform data that IAFW would export + * through SFI "DEVS" table, we use name and type to match the device and + * its platform data. + */ +struct devs_id { + char name[SFI_NAME_LEN + 1]; + u8 type; + u8 delay; + void *(*get_platform_data)(void *info); +}; + +/* the offset for the mapping of global gpio pin to irq */ +#define MRST_IRQ_OFFSET 0x100 + +static void __init *pmic_gpio_platform_data(void *info) +{ + static struct intel_pmic_gpio_platform_data pmic_gpio_pdata; + int gpio_base = get_gpio_by_name("pmic_gpio_base"); + + if (gpio_base == -1) + gpio_base = 64; + pmic_gpio_pdata.gpio_base = gpio_base; + pmic_gpio_pdata.irq_base = gpio_base + MRST_IRQ_OFFSET; + pmic_gpio_pdata.gpiointr = 0xffffeff8; + + return &pmic_gpio_pdata; +} + +static void __init *max3111_platform_data(void *info) +{ + struct spi_board_info *spi_info = info; + int intr = get_gpio_by_name("max3111_int"); + + spi_info->mode = SPI_MODE_0; + if (intr == -1) + return NULL; + spi_info->irq = intr + MRST_IRQ_OFFSET; + return NULL; +} + +/* we have multiple max7315 on the board ... */ +#define MAX7315_NUM 2 +static void __init *max7315_platform_data(void *info) +{ + static struct pca953x_platform_data max7315_pdata[MAX7315_NUM]; + static int nr; + struct pca953x_platform_data *max7315 = &max7315_pdata[nr]; + struct i2c_board_info *i2c_info = info; + int gpio_base, intr; + char base_pin_name[SFI_NAME_LEN + 1]; + char intr_pin_name[SFI_NAME_LEN + 1]; + + if (nr == MAX7315_NUM) { + pr_err("too many max7315s, we only support %d\n", + MAX7315_NUM); + return NULL; + } + /* we have several max7315 on the board, we only need load several + * instances of the same pca953x driver to cover them + */ + strcpy(i2c_info->type, "max7315"); + if (nr++) { + sprintf(base_pin_name, "max7315_%d_base", nr); + sprintf(intr_pin_name, "max7315_%d_int", nr); + } else { + strcpy(base_pin_name, "max7315_base"); + strcpy(intr_pin_name, "max7315_int"); + } + + gpio_base = get_gpio_by_name(base_pin_name); + intr = get_gpio_by_name(intr_pin_name); + + if (gpio_base == -1) + return NULL; + max7315->gpio_base = gpio_base; + if (intr != -1) { + i2c_info->irq = intr + MRST_IRQ_OFFSET; + max7315->irq_base = gpio_base + MRST_IRQ_OFFSET; + } else { + i2c_info->irq = -1; + max7315->irq_base = -1; + } + return max7315; +} + +static void *tca6416_platform_data(void *info) +{ + static struct pca953x_platform_data tca6416; + struct i2c_board_info *i2c_info = info; + int gpio_base, intr; + char base_pin_name[SFI_NAME_LEN + 1]; + char intr_pin_name[SFI_NAME_LEN + 1]; + + strcpy(i2c_info->type, "tca6416"); + strcpy(base_pin_name, "tca6416_base"); + strcpy(intr_pin_name, "tca6416_int"); + + gpio_base = get_gpio_by_name(base_pin_name); + intr = get_gpio_by_name(intr_pin_name); + + if (gpio_base == -1) + return NULL; + tca6416.gpio_base = gpio_base; + if (intr != -1) { + i2c_info->irq = intr + MRST_IRQ_OFFSET; + tca6416.irq_base = gpio_base + MRST_IRQ_OFFSET; + } else { + i2c_info->irq = -1; + tca6416.irq_base = -1; + } + return &tca6416; +} + +static void *mpu3050_platform_data(void *info) +{ + struct i2c_board_info *i2c_info = info; + int intr = get_gpio_by_name("mpu3050_int"); + + if (intr == -1) + return NULL; + + i2c_info->irq = intr + MRST_IRQ_OFFSET; + return NULL; +} + +static void __init *emc1403_platform_data(void *info) +{ + static short intr2nd_pdata; + struct i2c_board_info *i2c_info = info; + int intr = get_gpio_by_name("thermal_int"); + int intr2nd = get_gpio_by_name("thermal_alert"); + + if (intr == -1 || intr2nd == -1) + return NULL; + + i2c_info->irq = intr + MRST_IRQ_OFFSET; + intr2nd_pdata = intr2nd + MRST_IRQ_OFFSET; + + return &intr2nd_pdata; +} + +static void __init *lis331dl_platform_data(void *info) +{ + static short intr2nd_pdata; + struct i2c_board_info *i2c_info = info; + int intr = get_gpio_by_name("accel_int"); + int intr2nd = get_gpio_by_name("accel_2"); + + if (intr == -1 || intr2nd == -1) + return NULL; + + i2c_info->irq = intr + MRST_IRQ_OFFSET; + intr2nd_pdata = intr2nd + MRST_IRQ_OFFSET; + + return &intr2nd_pdata; +} + +static void __init *no_platform_data(void *info) +{ + return NULL; +} + +static struct resource msic_resources[] = { + { + .start = INTEL_MSIC_IRQ_PHYS_BASE, + .end = INTEL_MSIC_IRQ_PHYS_BASE + 64 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct intel_msic_platform_data msic_pdata; + +static struct platform_device msic_device = { + .name = "intel_msic", + .id = -1, + .dev = { + .platform_data = &msic_pdata, + }, + .num_resources = ARRAY_SIZE(msic_resources), + .resource = msic_resources, +}; + +static inline bool mrst_has_msic(void) +{ + return mrst_identify_cpu() == MRST_CPU_CHIP_PENWELL; +} + +static int msic_scu_status_change(struct notifier_block *nb, + unsigned long code, void *data) +{ + if (code == SCU_DOWN) { + platform_device_unregister(&msic_device); + return 0; + } + + return platform_device_register(&msic_device); +} + +static int __init msic_init(void) +{ + static struct notifier_block msic_scu_notifier = { + .notifier_call = msic_scu_status_change, + }; + + /* + * We need to be sure that the SCU IPC is ready before MSIC device + * can be registered. + */ + if (mrst_has_msic()) + intel_scu_notifier_add(&msic_scu_notifier); + + return 0; +} +arch_initcall(msic_init); + +/* + * msic_generic_platform_data - sets generic platform data for the block + * @info: pointer to the SFI device table entry for this block + * @block: MSIC block + * + * Function sets IRQ number from the SFI table entry for given device to + * the MSIC platform data. + */ +static void *msic_generic_platform_data(void *info, enum intel_msic_block block) +{ + struct sfi_device_table_entry *entry = info; + + BUG_ON(block < 0 || block >= INTEL_MSIC_BLOCK_LAST); + msic_pdata.irq[block] = entry->irq; + + return no_platform_data(info); +} + +static void *msic_battery_platform_data(void *info) +{ + return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_BATTERY); +} + +static void *msic_gpio_platform_data(void *info) +{ + static struct intel_msic_gpio_pdata pdata; + int gpio = get_gpio_by_name("msic_gpio_base"); + + if (gpio < 0) + return NULL; + + pdata.gpio_base = gpio; + msic_pdata.gpio = &pdata; + + return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_GPIO); +} + +static void *msic_audio_platform_data(void *info) +{ + struct platform_device *pdev; + + pdev = platform_device_register_simple("sst-platform", -1, NULL, 0); + if (IS_ERR(pdev)) { + pr_err("failed to create audio platform device\n"); + return NULL; + } + + return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_AUDIO); +} + +static void *msic_power_btn_platform_data(void *info) +{ + return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_POWER_BTN); +} + +static void *msic_ocd_platform_data(void *info) +{ + static struct intel_msic_ocd_pdata pdata; + int gpio = get_gpio_by_name("ocd_gpio"); + + if (gpio < 0) + return NULL; + + pdata.gpio = gpio; + msic_pdata.ocd = &pdata; + + return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_OCD); +} + +static void *msic_thermal_platform_data(void *info) +{ + return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_THERMAL); +} + +/* tc35876x DSI-LVDS bridge chip and panel platform data */ +static void *tc35876x_platform_data(void *data) +{ + static struct tc35876x_platform_data pdata; + + /* gpio pins set to -1 will not be used by the driver */ + pdata.gpio_bridge_reset = get_gpio_by_name("LCMB_RXEN"); + pdata.gpio_panel_bl_en = get_gpio_by_name("6S6P_BL_EN"); + pdata.gpio_panel_vadd = get_gpio_by_name("EN_VREG_LCD_V3P3"); + + return &pdata; +} + +static const struct devs_id __initconst device_ids[] = { + {"bma023", SFI_DEV_TYPE_I2C, 1, &no_platform_data}, + {"pmic_gpio", SFI_DEV_TYPE_SPI, 1, &pmic_gpio_platform_data}, + {"pmic_gpio", SFI_DEV_TYPE_IPC, 1, &pmic_gpio_platform_data}, + {"spi_max3111", SFI_DEV_TYPE_SPI, 0, &max3111_platform_data}, + {"i2c_max7315", SFI_DEV_TYPE_I2C, 1, &max7315_platform_data}, + {"i2c_max7315_2", SFI_DEV_TYPE_I2C, 1, &max7315_platform_data}, + {"tca6416", SFI_DEV_TYPE_I2C, 1, &tca6416_platform_data}, + {"emc1403", SFI_DEV_TYPE_I2C, 1, &emc1403_platform_data}, + {"i2c_accel", SFI_DEV_TYPE_I2C, 0, &lis331dl_platform_data}, + {"pmic_audio", SFI_DEV_TYPE_IPC, 1, &no_platform_data}, + {"mpu3050", SFI_DEV_TYPE_I2C, 1, &mpu3050_platform_data}, + {"i2c_disp_brig", SFI_DEV_TYPE_I2C, 0, &tc35876x_platform_data}, + + /* MSIC subdevices */ + {"msic_battery", SFI_DEV_TYPE_IPC, 1, &msic_battery_platform_data}, + {"msic_gpio", SFI_DEV_TYPE_IPC, 1, &msic_gpio_platform_data}, + {"msic_audio", SFI_DEV_TYPE_IPC, 1, &msic_audio_platform_data}, + {"msic_power_btn", SFI_DEV_TYPE_IPC, 1, &msic_power_btn_platform_data}, + {"msic_ocd", SFI_DEV_TYPE_IPC, 1, &msic_ocd_platform_data}, + {"msic_thermal", SFI_DEV_TYPE_IPC, 1, &msic_thermal_platform_data}, + + {}, +}; + +#define MAX_IPCDEVS 24 +static struct platform_device *ipc_devs[MAX_IPCDEVS]; +static int ipc_next_dev; + +#define MAX_SCU_SPI 24 +static struct spi_board_info *spi_devs[MAX_SCU_SPI]; +static int spi_next_dev; + +#define MAX_SCU_I2C 24 +static struct i2c_board_info *i2c_devs[MAX_SCU_I2C]; +static int i2c_bus[MAX_SCU_I2C]; +static int i2c_next_dev; + +static void __init intel_scu_device_register(struct platform_device *pdev) +{ + if (ipc_next_dev == MAX_IPCDEVS) + pr_err("too many SCU IPC devices"); + else + ipc_devs[ipc_next_dev++] = pdev; +} + +static void __init intel_scu_spi_device_register(struct spi_board_info *sdev) +{ + struct spi_board_info *new_dev; + + if (spi_next_dev == MAX_SCU_SPI) { + pr_err("too many SCU SPI devices"); + return; + } + + new_dev = kzalloc(sizeof(*sdev), GFP_KERNEL); + if (!new_dev) { + pr_err("failed to alloc mem for delayed spi dev %s\n", + sdev->modalias); + return; + } + memcpy(new_dev, sdev, sizeof(*sdev)); + + spi_devs[spi_next_dev++] = new_dev; +} + +static void __init intel_scu_i2c_device_register(int bus, + struct i2c_board_info *idev) +{ + struct i2c_board_info *new_dev; + + if (i2c_next_dev == MAX_SCU_I2C) { + pr_err("too many SCU I2C devices"); + return; + } + + new_dev = kzalloc(sizeof(*idev), GFP_KERNEL); + if (!new_dev) { + pr_err("failed to alloc mem for delayed i2c dev %s\n", + idev->type); + return; + } + memcpy(new_dev, idev, sizeof(*idev)); + + i2c_bus[i2c_next_dev] = bus; + i2c_devs[i2c_next_dev++] = new_dev; +} + +BLOCKING_NOTIFIER_HEAD(intel_scu_notifier); +EXPORT_SYMBOL_GPL(intel_scu_notifier); + +/* Called by IPC driver */ +void intel_scu_devices_create(void) +{ + int i; + + for (i = 0; i < ipc_next_dev; i++) + platform_device_add(ipc_devs[i]); + + for (i = 0; i < spi_next_dev; i++) + spi_register_board_info(spi_devs[i], 1); + + for (i = 0; i < i2c_next_dev; i++) { + struct i2c_adapter *adapter; + struct i2c_client *client; + + adapter = i2c_get_adapter(i2c_bus[i]); + if (adapter) { + client = i2c_new_device(adapter, i2c_devs[i]); + if (!client) + pr_err("can't create i2c device %s\n", + i2c_devs[i]->type); + } else + i2c_register_board_info(i2c_bus[i], i2c_devs[i], 1); + } + intel_scu_notifier_post(SCU_AVAILABLE, NULL); +} +EXPORT_SYMBOL_GPL(intel_scu_devices_create); + +/* Called by IPC driver */ +void intel_scu_devices_destroy(void) +{ + int i; + + intel_scu_notifier_post(SCU_DOWN, NULL); + + for (i = 0; i < ipc_next_dev; i++) + platform_device_del(ipc_devs[i]); +} +EXPORT_SYMBOL_GPL(intel_scu_devices_destroy); + +static void __init install_irq_resource(struct platform_device *pdev, int irq) +{ + /* Single threaded */ + static struct resource __initdata res = { + .name = "IRQ", + .flags = IORESOURCE_IRQ, + }; + res.start = irq; + platform_device_add_resources(pdev, &res, 1); +} + +static void __init sfi_handle_ipc_dev(struct sfi_device_table_entry *entry) +{ + const struct devs_id *dev = device_ids; + struct platform_device *pdev; + void *pdata = NULL; + + while (dev->name[0]) { + if (dev->type == SFI_DEV_TYPE_IPC && + !strncmp(dev->name, entry->name, SFI_NAME_LEN)) { + pdata = dev->get_platform_data(entry); + break; + } + dev++; + } + + /* + * On Medfield the platform device creation is handled by the MSIC + * MFD driver so we don't need to do it here. + */ + if (mrst_has_msic()) + return; + + pdev = platform_device_alloc(entry->name, 0); + if (pdev == NULL) { + pr_err("out of memory for SFI platform device '%s'.\n", + entry->name); + return; + } + install_irq_resource(pdev, entry->irq); + + pdev->dev.platform_data = pdata; + intel_scu_device_register(pdev); +} + +static void __init sfi_handle_spi_dev(struct spi_board_info *spi_info) +{ + const struct devs_id *dev = device_ids; + void *pdata = NULL; + + while (dev->name[0]) { + if (dev->type == SFI_DEV_TYPE_SPI && + !strncmp(dev->name, spi_info->modalias, + SFI_NAME_LEN)) { + pdata = dev->get_platform_data(spi_info); + break; + } + dev++; + } + spi_info->platform_data = pdata; + if (dev->delay) + intel_scu_spi_device_register(spi_info); + else + spi_register_board_info(spi_info, 1); +} + +static void __init sfi_handle_i2c_dev(int bus, struct i2c_board_info *i2c_info) +{ + const struct devs_id *dev = device_ids; + void *pdata = NULL; + + while (dev->name[0]) { + if (dev->type == SFI_DEV_TYPE_I2C && + !strncmp(dev->name, i2c_info->type, SFI_NAME_LEN)) { + pdata = dev->get_platform_data(i2c_info); + break; + } + dev++; + } + i2c_info->platform_data = pdata; + + if (dev->delay) + intel_scu_i2c_device_register(bus, i2c_info); + else + i2c_register_board_info(bus, i2c_info, 1); +} + + +static int __init sfi_parse_devs(struct sfi_table_header *table) +{ + struct sfi_table_simple *sb; + struct sfi_device_table_entry *pentry; + struct spi_board_info spi_info; + struct i2c_board_info i2c_info; + int num, i, bus; + int ioapic; + struct io_apic_irq_attr irq_attr; + + sb = (struct sfi_table_simple *)table; + num = SFI_GET_NUM_ENTRIES(sb, struct sfi_device_table_entry); + pentry = (struct sfi_device_table_entry *)sb->pentry; + + for (i = 0; i < num; i++, pentry++) { + int irq = pentry->irq; + + if (irq != (u8)0xff) { /* native RTE case */ + /* these SPI2 devices are not exposed to system as PCI + * devices, but they have separate RTE entry in IOAPIC + * so we have to enable them one by one here + */ + ioapic = mp_find_ioapic(irq); + irq_attr.ioapic = ioapic; + irq_attr.ioapic_pin = irq; + irq_attr.trigger = 1; + irq_attr.polarity = 1; + io_apic_set_pci_routing(NULL, irq, &irq_attr); + } else + irq = 0; /* No irq */ + + switch (pentry->type) { + case SFI_DEV_TYPE_IPC: + pr_debug("info[%2d]: IPC bus, name = %16.16s, " + "irq = 0x%2x\n", i, pentry->name, pentry->irq); + sfi_handle_ipc_dev(pentry); + break; + case SFI_DEV_TYPE_SPI: + memset(&spi_info, 0, sizeof(spi_info)); + strncpy(spi_info.modalias, pentry->name, SFI_NAME_LEN); + spi_info.irq = irq; + spi_info.bus_num = pentry->host_num; + spi_info.chip_select = pentry->addr; + spi_info.max_speed_hz = pentry->max_freq; + pr_debug("info[%2d]: SPI bus = %d, name = %16.16s, " + "irq = 0x%2x, max_freq = %d, cs = %d\n", i, + spi_info.bus_num, + spi_info.modalias, + spi_info.irq, + spi_info.max_speed_hz, + spi_info.chip_select); + sfi_handle_spi_dev(&spi_info); + break; + case SFI_DEV_TYPE_I2C: + memset(&i2c_info, 0, sizeof(i2c_info)); + bus = pentry->host_num; + strncpy(i2c_info.type, pentry->name, SFI_NAME_LEN); + i2c_info.irq = irq; + i2c_info.addr = pentry->addr; + pr_debug("info[%2d]: I2C bus = %d, name = %16.16s, " + "irq = 0x%2x, addr = 0x%x\n", i, bus, + i2c_info.type, + i2c_info.irq, + i2c_info.addr); + sfi_handle_i2c_dev(bus, &i2c_info); + break; + case SFI_DEV_TYPE_UART: + case SFI_DEV_TYPE_HSI: + default: + ; + } + } + return 0; +} + +static int __init mrst_platform_init(void) +{ + sfi_table_parse(SFI_SIG_GPIO, NULL, NULL, sfi_parse_gpio); + sfi_table_parse(SFI_SIG_DEVS, NULL, NULL, sfi_parse_devs); + return 0; +} +arch_initcall(mrst_platform_init); + +/* + * we will search these buttons in SFI GPIO table (by name) + * and register them dynamically. Please add all possible + * buttons here, we will shrink them if no GPIO found. + */ +static struct gpio_keys_button gpio_button[] = { + {KEY_POWER, -1, 1, "power_btn", EV_KEY, 0, 3000}, + {KEY_PROG1, -1, 1, "prog_btn1", EV_KEY, 0, 20}, + {KEY_PROG2, -1, 1, "prog_btn2", EV_KEY, 0, 20}, + {SW_LID, -1, 1, "lid_switch", EV_SW, 0, 20}, + {KEY_VOLUMEUP, -1, 1, "vol_up", EV_KEY, 0, 20}, + {KEY_VOLUMEDOWN, -1, 1, "vol_down", EV_KEY, 0, 20}, + {KEY_CAMERA, -1, 1, "camera_full", EV_KEY, 0, 20}, + {KEY_CAMERA_FOCUS, -1, 1, "camera_half", EV_KEY, 0, 20}, + {SW_KEYPAD_SLIDE, -1, 1, "MagSw1", EV_SW, 0, 20}, + {SW_KEYPAD_SLIDE, -1, 1, "MagSw2", EV_SW, 0, 20}, +}; + +static struct gpio_keys_platform_data mrst_gpio_keys = { + .buttons = gpio_button, + .rep = 1, + .nbuttons = -1, /* will fill it after search */ +}; + +static struct platform_device pb_device = { + .name = "gpio-keys", + .id = -1, + .dev = { + .platform_data = &mrst_gpio_keys, + }, +}; + +/* + * Shrink the non-existent buttons, register the gpio button + * device if there is some + */ +static int __init pb_keys_init(void) +{ + struct gpio_keys_button *gb = gpio_button; + int i, num, good = 0; + + num = sizeof(gpio_button) / sizeof(struct gpio_keys_button); + for (i = 0; i < num; i++) { + gb[i].gpio = get_gpio_by_name(gb[i].desc); + pr_debug("info[%2d]: name = %s, gpio = %d\n", i, gb[i].desc, + gb[i].gpio); + if (gb[i].gpio == -1) + continue; + + if (i != good) + gb[good] = gb[i]; + good++; + } + + if (good) { + mrst_gpio_keys.nbuttons = good; + return platform_device_register(&pb_device); + } + return 0; +} +late_initcall(pb_keys_init); diff --git a/arch/x86/platform/intel-mid/intel_mid_vrtc.c b/arch/x86/platform/intel-mid/intel_mid_vrtc.c new file mode 100644 index 0000000..ded9fbd --- /dev/null +++ b/arch/x86/platform/intel-mid/intel_mid_vrtc.c @@ -0,0 +1,177 @@ +/* + * intel_mid_vrtc.c: Driver for virtual RTC device on Intel MID platform + * + * (C) Copyright 2009 Intel Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + * + * Note: + * VRTC is emulated by system controller firmware, the real HW + * RTC is located in the PMIC device. SCU FW shadows PMIC RTC + * in a memory mapped IO space that is visible to the host IA + * processor. + * + * This driver is based on RTC CMOS driver. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static unsigned char __iomem *vrtc_virt_base; + +unsigned char vrtc_cmos_read(unsigned char reg) +{ + unsigned char retval; + + /* vRTC's registers range from 0x0 to 0xD */ + if (reg > 0xd || !vrtc_virt_base) + return 0xff; + + lock_cmos_prefix(reg); + retval = __raw_readb(vrtc_virt_base + (reg << 2)); + lock_cmos_suffix(reg); + return retval; +} +EXPORT_SYMBOL_GPL(vrtc_cmos_read); + +void vrtc_cmos_write(unsigned char val, unsigned char reg) +{ + if (reg > 0xd || !vrtc_virt_base) + return; + + lock_cmos_prefix(reg); + __raw_writeb(val, vrtc_virt_base + (reg << 2)); + lock_cmos_suffix(reg); +} +EXPORT_SYMBOL_GPL(vrtc_cmos_write); + +void vrtc_get_time(struct timespec *now) +{ + u8 sec, min, hour, mday, mon; + unsigned long flags; + u32 year; + + spin_lock_irqsave(&rtc_lock, flags); + + while ((vrtc_cmos_read(RTC_FREQ_SELECT) & RTC_UIP)) + cpu_relax(); + + sec = vrtc_cmos_read(RTC_SECONDS); + min = vrtc_cmos_read(RTC_MINUTES); + hour = vrtc_cmos_read(RTC_HOURS); + mday = vrtc_cmos_read(RTC_DAY_OF_MONTH); + mon = vrtc_cmos_read(RTC_MONTH); + year = vrtc_cmos_read(RTC_YEAR); + + spin_unlock_irqrestore(&rtc_lock, flags); + + /* vRTC YEAR reg contains the offset to 1972 */ + year += 1972; + + pr_info("vRTC: sec: %d min: %d hour: %d day: %d " + "mon: %d year: %d\n", sec, min, hour, mday, mon, year); + + now->tv_sec = mktime(year, mon, mday, hour, min, sec); + now->tv_nsec = 0; +} + +int vrtc_set_mmss(const struct timespec *now) +{ + unsigned long flags; + struct rtc_time tm; + int year; + int retval = 0; + + rtc_time_to_tm(now->tv_sec, &tm); + if (!rtc_valid_tm(&tm) && tm.tm_year >= 72) { + /* + * tm.year is the number of years since 1900, and the + * vrtc need the years since 1972. + */ + year = tm.tm_year - 72; + spin_lock_irqsave(&rtc_lock, flags); + vrtc_cmos_write(year, RTC_YEAR); + vrtc_cmos_write(tm.tm_mon, RTC_MONTH); + vrtc_cmos_write(tm.tm_mday, RTC_DAY_OF_MONTH); + vrtc_cmos_write(tm.tm_hour, RTC_HOURS); + vrtc_cmos_write(tm.tm_min, RTC_MINUTES); + vrtc_cmos_write(tm.tm_sec, RTC_SECONDS); + spin_unlock_irqrestore(&rtc_lock, flags); + } else { + pr_err("%s: Invalid vRTC value: write of %lx to vRTC failed\n", + __FUNCTION__, now->tv_sec); + retval = -EINVAL; + } + return retval; +} + +void __init mrst_rtc_init(void) +{ + unsigned long vrtc_paddr; + + sfi_table_parse(SFI_SIG_MRTC, NULL, NULL, sfi_parse_mrtc); + + vrtc_paddr = sfi_mrtc_array[0].phys_addr; + if (!sfi_mrtc_num || !vrtc_paddr) + return; + + vrtc_virt_base = (void __iomem *)set_fixmap_offset_nocache(FIX_LNW_VRTC, + vrtc_paddr); + x86_platform.get_wallclock = vrtc_get_time; + x86_platform.set_wallclock = vrtc_set_mmss; +} + +/* + * The Moorestown platform has a memory mapped virtual RTC device that emulates + * the programming interface of the RTC. + */ + +static struct resource vrtc_resources[] = { + [0] = { + .flags = IORESOURCE_MEM, + }, + [1] = { + .flags = IORESOURCE_IRQ, + } +}; + +static struct platform_device vrtc_device = { + .name = "rtc_mrst", + .id = -1, + .resource = vrtc_resources, + .num_resources = ARRAY_SIZE(vrtc_resources), +}; + +/* Register the RTC device if appropriate */ +static int __init mrst_device_create(void) +{ + /* No Moorestown, no device */ + if (!mrst_identify_cpu()) + return -ENODEV; + /* No timer, no device */ + if (!sfi_mrtc_num) + return -ENODEV; + + /* iomem resource */ + vrtc_resources[0].start = sfi_mrtc_array[0].phys_addr; + vrtc_resources[0].end = sfi_mrtc_array[0].phys_addr + + MRST_VRTC_MAP_SZ; + /* irq resource */ + vrtc_resources[1].start = sfi_mrtc_array[0].irq; + vrtc_resources[1].end = sfi_mrtc_array[0].irq; + + return platform_device_register(&vrtc_device); +} + +module_init(mrst_device_create); diff --git a/arch/x86/platform/mrst/Makefile b/arch/x86/platform/mrst/Makefile deleted file mode 100644 index af1da7e..0000000 --- a/arch/x86/platform/mrst/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -obj-$(CONFIG_X86_INTEL_MID) += mrst.o -obj-$(CONFIG_X86_INTEL_MID) += vrtc.o -obj-$(CONFIG_EARLY_PRINTK_INTEL_MID) += early_printk_mrst.o diff --git a/arch/x86/platform/mrst/early_printk_mrst.c b/arch/x86/platform/mrst/early_printk_mrst.c deleted file mode 100644 index 39ecc27..0000000 --- a/arch/x86/platform/mrst/early_printk_mrst.c +++ /dev/null @@ -1,325 +0,0 @@ -/* - * early_printk_mrst.c - early consoles for Intel MID platforms - * - * Copyright (c) 2008-2010, Intel Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License. - */ - -/* - * This file implements two early consoles named mrst and hsu. - * mrst is based on Maxim3110 spi-uart device, it exists in both - * Moorestown and Medfield platforms, while hsu is based on a High - * Speed UART device which only exists in the Medfield platform - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define MRST_SPI_TIMEOUT 0x200000 -#define MRST_REGBASE_SPI0 0xff128000 -#define MRST_REGBASE_SPI1 0xff128400 -#define MRST_CLK_SPI0_REG 0xff11d86c - -/* Bit fields in CTRLR0 */ -#define SPI_DFS_OFFSET 0 - -#define SPI_FRF_OFFSET 4 -#define SPI_FRF_SPI 0x0 -#define SPI_FRF_SSP 0x1 -#define SPI_FRF_MICROWIRE 0x2 -#define SPI_FRF_RESV 0x3 - -#define SPI_MODE_OFFSET 6 -#define SPI_SCPH_OFFSET 6 -#define SPI_SCOL_OFFSET 7 -#define SPI_TMOD_OFFSET 8 -#define SPI_TMOD_TR 0x0 /* xmit & recv */ -#define SPI_TMOD_TO 0x1 /* xmit only */ -#define SPI_TMOD_RO 0x2 /* recv only */ -#define SPI_TMOD_EPROMREAD 0x3 /* eeprom read mode */ - -#define SPI_SLVOE_OFFSET 10 -#define SPI_SRL_OFFSET 11 -#define SPI_CFS_OFFSET 12 - -/* Bit fields in SR, 7 bits */ -#define SR_MASK 0x7f /* cover 7 bits */ -#define SR_BUSY (1 << 0) -#define SR_TF_NOT_FULL (1 << 1) -#define SR_TF_EMPT (1 << 2) -#define SR_RF_NOT_EMPT (1 << 3) -#define SR_RF_FULL (1 << 4) -#define SR_TX_ERR (1 << 5) -#define SR_DCOL (1 << 6) - -struct dw_spi_reg { - u32 ctrl0; - u32 ctrl1; - u32 ssienr; - u32 mwcr; - u32 ser; - u32 baudr; - u32 txfltr; - u32 rxfltr; - u32 txflr; - u32 rxflr; - u32 sr; - u32 imr; - u32 isr; - u32 risr; - u32 txoicr; - u32 rxoicr; - u32 rxuicr; - u32 msticr; - u32 icr; - u32 dmacr; - u32 dmatdlr; - u32 dmardlr; - u32 idr; - u32 version; - - /* Currently operates as 32 bits, though only the low 16 bits matter */ - u32 dr; -} __packed; - -#define dw_readl(dw, name) __raw_readl(&(dw)->name) -#define dw_writel(dw, name, val) __raw_writel((val), &(dw)->name) - -/* Default use SPI0 register for mrst, we will detect Penwell and use SPI1 */ -static unsigned long mrst_spi_paddr = MRST_REGBASE_SPI0; - -static u32 *pclk_spi0; -/* Always contains an accessible address, start with 0 */ -static struct dw_spi_reg *pspi; - -static struct kmsg_dumper dw_dumper; -static int dumper_registered; - -static void dw_kmsg_dump(struct kmsg_dumper *dumper, - enum kmsg_dump_reason reason) -{ - static char line[1024]; - size_t len; - - /* When run to this, we'd better re-init the HW */ - mrst_early_console_init(); - - while (kmsg_dump_get_line(dumper, true, line, sizeof(line), &len)) - early_mrst_console.write(&early_mrst_console, line, len); -} - -/* Set the ratio rate to 115200, 8n1, IRQ disabled */ -static void max3110_write_config(void) -{ - u16 config; - - config = 0xc001; - dw_writel(pspi, dr, config); -} - -/* Translate char to a eligible word and send to max3110 */ -static void max3110_write_data(char c) -{ - u16 data; - - data = 0x8000 | c; - dw_writel(pspi, dr, data); -} - -void mrst_early_console_init(void) -{ - u32 ctrlr0 = 0; - u32 spi0_cdiv; - u32 freq; /* Freqency info only need be searched once */ - - /* Base clk is 100 MHz, the actual clk = 100M / (clk_divider + 1) */ - pclk_spi0 = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, - MRST_CLK_SPI0_REG); - spi0_cdiv = ((*pclk_spi0) & 0xe00) >> 9; - freq = 100000000 / (spi0_cdiv + 1); - - if (mrst_identify_cpu() == MRST_CPU_CHIP_PENWELL) - mrst_spi_paddr = MRST_REGBASE_SPI1; - - pspi = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, - mrst_spi_paddr); - - /* Disable SPI controller */ - dw_writel(pspi, ssienr, 0); - - /* Set control param, 8 bits, transmit only mode */ - ctrlr0 = dw_readl(pspi, ctrl0); - - ctrlr0 &= 0xfcc0; - ctrlr0 |= 0xf | (SPI_FRF_SPI << SPI_FRF_OFFSET) - | (SPI_TMOD_TO << SPI_TMOD_OFFSET); - dw_writel(pspi, ctrl0, ctrlr0); - - /* - * Change the spi0 clk to comply with 115200 bps, use 100000 to - * calculate the clk dividor to make the clock a little slower - * than real baud rate. - */ - dw_writel(pspi, baudr, freq/100000); - - /* Disable all INT for early phase */ - dw_writel(pspi, imr, 0x0); - - /* Set the cs to spi-uart */ - dw_writel(pspi, ser, 0x2); - - /* Enable the HW, the last step for HW init */ - dw_writel(pspi, ssienr, 0x1); - - /* Set the default configuration */ - max3110_write_config(); - - /* Register the kmsg dumper */ - if (!dumper_registered) { - dw_dumper.dump = dw_kmsg_dump; - kmsg_dump_register(&dw_dumper); - dumper_registered = 1; - } -} - -/* Slave select should be called in the read/write function */ -static void early_mrst_spi_putc(char c) -{ - unsigned int timeout; - u32 sr; - - timeout = MRST_SPI_TIMEOUT; - /* Early putc needs to make sure the TX FIFO is not full */ - while (--timeout) { - sr = dw_readl(pspi, sr); - if (!(sr & SR_TF_NOT_FULL)) - cpu_relax(); - else - break; - } - - if (!timeout) - pr_warn("MRST earlycon: timed out\n"); - else - max3110_write_data(c); -} - -/* Early SPI only uses polling mode */ -static void early_mrst_spi_write(struct console *con, const char *str, - unsigned n) -{ - int i; - - for (i = 0; i < n && *str; i++) { - if (*str == '\n') - early_mrst_spi_putc('\r'); - early_mrst_spi_putc(*str); - str++; - } -} - -struct console early_mrst_console = { - .name = "earlymrst", - .write = early_mrst_spi_write, - .flags = CON_PRINTBUFFER, - .index = -1, -}; - -/* - * Following is the early console based on Medfield HSU (High - * Speed UART) device. - */ -#define HSU_PORT_BASE 0xffa28080 - -static void __iomem *phsu; - -void hsu_early_console_init(const char *s) -{ - unsigned long paddr, port = 0; - u8 lcr; - - /* - * Select the early HSU console port if specified by user in the - * kernel command line. - */ - if (*s && !kstrtoul(s, 10, &port)) - port = clamp_val(port, 0, 2); - - paddr = HSU_PORT_BASE + port * 0x80; - phsu = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, paddr); - - /* Disable FIFO */ - writeb(0x0, phsu + UART_FCR); - - /* Set to default 115200 bps, 8n1 */ - lcr = readb(phsu + UART_LCR); - writeb((0x80 | lcr), phsu + UART_LCR); - writeb(0x18, phsu + UART_DLL); - writeb(lcr, phsu + UART_LCR); - writel(0x3600, phsu + UART_MUL*4); - - writeb(0x8, phsu + UART_MCR); - writeb(0x7, phsu + UART_FCR); - writeb(0x3, phsu + UART_LCR); - - /* Clear IRQ status */ - readb(phsu + UART_LSR); - readb(phsu + UART_RX); - readb(phsu + UART_IIR); - readb(phsu + UART_MSR); - - /* Enable FIFO */ - writeb(0x7, phsu + UART_FCR); -} - -#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) - -static void early_hsu_putc(char ch) -{ - unsigned int timeout = 10000; /* 10ms */ - u8 status; - - while (--timeout) { - status = readb(phsu + UART_LSR); - if (status & BOTH_EMPTY) - break; - udelay(1); - } - - /* Only write the char when there was no timeout */ - if (timeout) - writeb(ch, phsu + UART_TX); -} - -static void early_hsu_write(struct console *con, const char *str, unsigned n) -{ - int i; - - for (i = 0; i < n && *str; i++) { - if (*str == '\n') - early_hsu_putc('\r'); - early_hsu_putc(*str); - str++; - } -} - -struct console early_hsu_console = { - .name = "earlyhsu", - .write = early_hsu_write, - .flags = CON_PRINTBUFFER, - .index = -1, -}; diff --git a/arch/x86/platform/mrst/mrst.c b/arch/x86/platform/mrst/mrst.c deleted file mode 100644 index 235a742..0000000 --- a/arch/x86/platform/mrst/mrst.c +++ /dev/null @@ -1,1054 +0,0 @@ -/* - * mrst.c: Intel Moorestown platform specific setup code - * - * (C) Copyright 2008 Intel Corporation - * Author: Jacob Pan (jacob.jun.pan@intel.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License. - */ - -#define pr_fmt(fmt) "mrst: " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * the clockevent devices on Moorestown/Medfield can be APBT or LAPIC clock, - * cmdline option x86_mrst_timer can be used to override the configuration - * to prefer one or the other. - * at runtime, there are basically three timer configurations: - * 1. per cpu apbt clock only - * 2. per cpu always-on lapic clocks only, this is Penwell/Medfield only - * 3. per cpu lapic clock (C3STOP) and one apbt clock, with broadcast. - * - * by default (without cmdline option), platform code first detects cpu type - * to see if we are on lincroft or penwell, then set up both lapic or apbt - * clocks accordingly. - * i.e. by default, medfield uses configuration #2, moorestown uses #1. - * config #3 is supported but not recommended on medfield. - * - * rating and feature summary: - * lapic (with C3STOP) --------- 100 - * apbt (always-on) ------------ 110 - * lapic (always-on,ARAT) ------ 150 - */ - -enum mrst_timer_options mrst_timer_options; - -static u32 sfi_mtimer_usage[SFI_MTMR_MAX_NUM]; -static struct sfi_timer_table_entry sfi_mtimer_array[SFI_MTMR_MAX_NUM]; -enum mrst_cpu_type __mrst_cpu_chip; -EXPORT_SYMBOL_GPL(__mrst_cpu_chip); - -int sfi_mtimer_num; - -struct sfi_rtc_table_entry sfi_mrtc_array[SFI_MRTC_MAX]; -EXPORT_SYMBOL_GPL(sfi_mrtc_array); -int sfi_mrtc_num; - -static void mrst_power_off(void) -{ -} - -static void mrst_reboot(void) -{ - intel_scu_ipc_simple_command(IPCMSG_COLD_BOOT, 0); -} - -/* parse all the mtimer info to a static mtimer array */ -static int __init sfi_parse_mtmr(struct sfi_table_header *table) -{ - struct sfi_table_simple *sb; - struct sfi_timer_table_entry *pentry; - struct mpc_intsrc mp_irq; - int totallen; - - sb = (struct sfi_table_simple *)table; - if (!sfi_mtimer_num) { - sfi_mtimer_num = SFI_GET_NUM_ENTRIES(sb, - struct sfi_timer_table_entry); - pentry = (struct sfi_timer_table_entry *) sb->pentry; - totallen = sfi_mtimer_num * sizeof(*pentry); - memcpy(sfi_mtimer_array, pentry, totallen); - } - - pr_debug("SFI MTIMER info (num = %d):\n", sfi_mtimer_num); - pentry = sfi_mtimer_array; - for (totallen = 0; totallen < sfi_mtimer_num; totallen++, pentry++) { - pr_debug("timer[%d]: paddr = 0x%08x, freq = %dHz," - " irq = %d\n", totallen, (u32)pentry->phys_addr, - pentry->freq_hz, pentry->irq); - if (!pentry->irq) - continue; - mp_irq.type = MP_INTSRC; - mp_irq.irqtype = mp_INT; -/* triggering mode edge bit 2-3, active high polarity bit 0-1 */ - mp_irq.irqflag = 5; - mp_irq.srcbus = MP_BUS_ISA; - mp_irq.srcbusirq = pentry->irq; /* IRQ */ - mp_irq.dstapic = MP_APIC_ALL; - mp_irq.dstirq = pentry->irq; - mp_save_irq(&mp_irq); - } - - return 0; -} - -struct sfi_timer_table_entry *sfi_get_mtmr(int hint) -{ - int i; - if (hint < sfi_mtimer_num) { - if (!sfi_mtimer_usage[hint]) { - pr_debug("hint taken for timer %d irq %d\n", - hint, sfi_mtimer_array[hint].irq); - sfi_mtimer_usage[hint] = 1; - return &sfi_mtimer_array[hint]; - } - } - /* take the first timer available */ - for (i = 0; i < sfi_mtimer_num;) { - if (!sfi_mtimer_usage[i]) { - sfi_mtimer_usage[i] = 1; - return &sfi_mtimer_array[i]; - } - i++; - } - return NULL; -} - -void sfi_free_mtmr(struct sfi_timer_table_entry *mtmr) -{ - int i; - for (i = 0; i < sfi_mtimer_num;) { - if (mtmr->irq == sfi_mtimer_array[i].irq) { - sfi_mtimer_usage[i] = 0; - return; - } - i++; - } -} - -/* parse all the mrtc info to a global mrtc array */ -int __init sfi_parse_mrtc(struct sfi_table_header *table) -{ - struct sfi_table_simple *sb; - struct sfi_rtc_table_entry *pentry; - struct mpc_intsrc mp_irq; - - int totallen; - - sb = (struct sfi_table_simple *)table; - if (!sfi_mrtc_num) { - sfi_mrtc_num = SFI_GET_NUM_ENTRIES(sb, - struct sfi_rtc_table_entry); - pentry = (struct sfi_rtc_table_entry *)sb->pentry; - totallen = sfi_mrtc_num * sizeof(*pentry); - memcpy(sfi_mrtc_array, pentry, totallen); - } - - pr_debug("SFI RTC info (num = %d):\n", sfi_mrtc_num); - pentry = sfi_mrtc_array; - for (totallen = 0; totallen < sfi_mrtc_num; totallen++, pentry++) { - pr_debug("RTC[%d]: paddr = 0x%08x, irq = %d\n", - totallen, (u32)pentry->phys_addr, pentry->irq); - mp_irq.type = MP_INTSRC; - mp_irq.irqtype = mp_INT; - mp_irq.irqflag = 0xf; /* level trigger and active low */ - mp_irq.srcbus = MP_BUS_ISA; - mp_irq.srcbusirq = pentry->irq; /* IRQ */ - mp_irq.dstapic = MP_APIC_ALL; - mp_irq.dstirq = pentry->irq; - mp_save_irq(&mp_irq); - } - return 0; -} - -static unsigned long __init mrst_calibrate_tsc(void) -{ - unsigned long fast_calibrate; - u32 lo, hi, ratio, fsb; - - rdmsr(MSR_IA32_PERF_STATUS, lo, hi); - pr_debug("IA32 perf status is 0x%x, 0x%0x\n", lo, hi); - ratio = (hi >> 8) & 0x1f; - pr_debug("ratio is %d\n", ratio); - if (!ratio) { - pr_err("read a zero ratio, should be incorrect!\n"); - pr_err("force tsc ratio to 16 ...\n"); - ratio = 16; - } - rdmsr(MSR_FSB_FREQ, lo, hi); - if ((lo & 0x7) == 0x7) - fsb = PENWELL_FSB_FREQ_83SKU; - else - fsb = PENWELL_FSB_FREQ_100SKU; - fast_calibrate = ratio * fsb; - pr_debug("read penwell tsc %lu khz\n", fast_calibrate); - lapic_timer_frequency = fsb * 1000 / HZ; - /* mark tsc clocksource as reliable */ - set_cpu_cap(&boot_cpu_data, X86_FEATURE_TSC_RELIABLE); - - if (fast_calibrate) - return fast_calibrate; - - return 0; -} - -static void __init mrst_time_init(void) -{ - sfi_table_parse(SFI_SIG_MTMR, NULL, NULL, sfi_parse_mtmr); - switch (mrst_timer_options) { - case MRST_TIMER_APBT_ONLY: - break; - case MRST_TIMER_LAPIC_APBT: - x86_init.timers.setup_percpu_clockev = setup_boot_APIC_clock; - x86_cpuinit.setup_percpu_clockev = setup_secondary_APIC_clock; - break; - default: - if (!boot_cpu_has(X86_FEATURE_ARAT)) - break; - x86_init.timers.setup_percpu_clockev = setup_boot_APIC_clock; - x86_cpuinit.setup_percpu_clockev = setup_secondary_APIC_clock; - return; - } - /* we need at least one APB timer */ - pre_init_apic_IRQ0(); - apbt_time_init(); -} - -static void mrst_arch_setup(void) -{ - if (boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model == 0x27) - __mrst_cpu_chip = MRST_CPU_CHIP_PENWELL; - else { - pr_err("Unknown Intel MID CPU (%d:%d), default to Penwell\n", - boot_cpu_data.x86, boot_cpu_data.x86_model); - __mrst_cpu_chip = MRST_CPU_CHIP_PENWELL; - } -} - -/* MID systems don't have i8042 controller */ -static int mrst_i8042_detect(void) -{ - return 0; -} - -/* - * Moorestown does not have external NMI source nor port 0x61 to report - * NMI status. The possible NMI sources are from pmu as a result of NMI - * watchdog or lock debug. Reading io port 0x61 results in 0xff which - * misled NMI handler. - */ -static unsigned char mrst_get_nmi_reason(void) -{ - return 0; -} - -/* - * Moorestown specific x86_init function overrides and early setup - * calls. - */ -void __init x86_mrst_early_setup(void) -{ - x86_init.resources.probe_roms = x86_init_noop; - x86_init.resources.reserve_resources = x86_init_noop; - - x86_init.timers.timer_init = mrst_time_init; - x86_init.timers.setup_percpu_clockev = x86_init_noop; - - x86_init.irqs.pre_vector_init = x86_init_noop; - - x86_init.oem.arch_setup = mrst_arch_setup; - - x86_cpuinit.setup_percpu_clockev = apbt_setup_secondary_clock; - - x86_platform.calibrate_tsc = mrst_calibrate_tsc; - x86_platform.i8042_detect = mrst_i8042_detect; - x86_init.timers.wallclock_init = mrst_rtc_init; - x86_platform.get_nmi_reason = mrst_get_nmi_reason; - - x86_init.pci.init = pci_mrst_init; - x86_init.pci.fixup_irqs = x86_init_noop; - - legacy_pic = &null_legacy_pic; - - /* Moorestown specific power_off/restart method */ - pm_power_off = mrst_power_off; - machine_ops.emergency_restart = mrst_reboot; - - /* Avoid searching for BIOS MP tables */ - x86_init.mpparse.find_smp_config = x86_init_noop; - x86_init.mpparse.get_smp_config = x86_init_uint_noop; - set_bit(MP_BUS_ISA, mp_bus_not_pci); -} - -/* - * if user does not want to use per CPU apb timer, just give it a lower rating - * than local apic timer and skip the late per cpu timer init. - */ -static inline int __init setup_x86_mrst_timer(char *arg) -{ - if (!arg) - return -EINVAL; - - if (strcmp("apbt_only", arg) == 0) - mrst_timer_options = MRST_TIMER_APBT_ONLY; - else if (strcmp("lapic_and_apbt", arg) == 0) - mrst_timer_options = MRST_TIMER_LAPIC_APBT; - else { - pr_warn("X86 MRST timer option %s not recognised" - " use x86_mrst_timer=apbt_only or lapic_and_apbt\n", - arg); - return -EINVAL; - } - return 0; -} -__setup("x86_mrst_timer=", setup_x86_mrst_timer); - -/* - * Parsing GPIO table first, since the DEVS table will need this table - * to map the pin name to the actual pin. - */ -static struct sfi_gpio_table_entry *gpio_table; -static int gpio_num_entry; - -static int __init sfi_parse_gpio(struct sfi_table_header *table) -{ - struct sfi_table_simple *sb; - struct sfi_gpio_table_entry *pentry; - int num, i; - - if (gpio_table) - return 0; - sb = (struct sfi_table_simple *)table; - num = SFI_GET_NUM_ENTRIES(sb, struct sfi_gpio_table_entry); - pentry = (struct sfi_gpio_table_entry *)sb->pentry; - - gpio_table = kmalloc(num * sizeof(*pentry), GFP_KERNEL); - if (!gpio_table) - return -1; - memcpy(gpio_table, pentry, num * sizeof(*pentry)); - gpio_num_entry = num; - - pr_debug("GPIO pin info:\n"); - for (i = 0; i < num; i++, pentry++) - pr_debug("info[%2d]: controller = %16.16s, pin_name = %16.16s," - " pin = %d\n", i, - pentry->controller_name, - pentry->pin_name, - pentry->pin_no); - return 0; -} - -static int get_gpio_by_name(const char *name) -{ - struct sfi_gpio_table_entry *pentry = gpio_table; - int i; - - if (!pentry) - return -1; - for (i = 0; i < gpio_num_entry; i++, pentry++) { - if (!strncmp(name, pentry->pin_name, SFI_NAME_LEN)) - return pentry->pin_no; - } - return -1; -} - -/* - * Here defines the array of devices platform data that IAFW would export - * through SFI "DEVS" table, we use name and type to match the device and - * its platform data. - */ -struct devs_id { - char name[SFI_NAME_LEN + 1]; - u8 type; - u8 delay; - void *(*get_platform_data)(void *info); -}; - -/* the offset for the mapping of global gpio pin to irq */ -#define MRST_IRQ_OFFSET 0x100 - -static void __init *pmic_gpio_platform_data(void *info) -{ - static struct intel_pmic_gpio_platform_data pmic_gpio_pdata; - int gpio_base = get_gpio_by_name("pmic_gpio_base"); - - if (gpio_base == -1) - gpio_base = 64; - pmic_gpio_pdata.gpio_base = gpio_base; - pmic_gpio_pdata.irq_base = gpio_base + MRST_IRQ_OFFSET; - pmic_gpio_pdata.gpiointr = 0xffffeff8; - - return &pmic_gpio_pdata; -} - -static void __init *max3111_platform_data(void *info) -{ - struct spi_board_info *spi_info = info; - int intr = get_gpio_by_name("max3111_int"); - - spi_info->mode = SPI_MODE_0; - if (intr == -1) - return NULL; - spi_info->irq = intr + MRST_IRQ_OFFSET; - return NULL; -} - -/* we have multiple max7315 on the board ... */ -#define MAX7315_NUM 2 -static void __init *max7315_platform_data(void *info) -{ - static struct pca953x_platform_data max7315_pdata[MAX7315_NUM]; - static int nr; - struct pca953x_platform_data *max7315 = &max7315_pdata[nr]; - struct i2c_board_info *i2c_info = info; - int gpio_base, intr; - char base_pin_name[SFI_NAME_LEN + 1]; - char intr_pin_name[SFI_NAME_LEN + 1]; - - if (nr == MAX7315_NUM) { - pr_err("too many max7315s, we only support %d\n", - MAX7315_NUM); - return NULL; - } - /* we have several max7315 on the board, we only need load several - * instances of the same pca953x driver to cover them - */ - strcpy(i2c_info->type, "max7315"); - if (nr++) { - sprintf(base_pin_name, "max7315_%d_base", nr); - sprintf(intr_pin_name, "max7315_%d_int", nr); - } else { - strcpy(base_pin_name, "max7315_base"); - strcpy(intr_pin_name, "max7315_int"); - } - - gpio_base = get_gpio_by_name(base_pin_name); - intr = get_gpio_by_name(intr_pin_name); - - if (gpio_base == -1) - return NULL; - max7315->gpio_base = gpio_base; - if (intr != -1) { - i2c_info->irq = intr + MRST_IRQ_OFFSET; - max7315->irq_base = gpio_base + MRST_IRQ_OFFSET; - } else { - i2c_info->irq = -1; - max7315->irq_base = -1; - } - return max7315; -} - -static void *tca6416_platform_data(void *info) -{ - static struct pca953x_platform_data tca6416; - struct i2c_board_info *i2c_info = info; - int gpio_base, intr; - char base_pin_name[SFI_NAME_LEN + 1]; - char intr_pin_name[SFI_NAME_LEN + 1]; - - strcpy(i2c_info->type, "tca6416"); - strcpy(base_pin_name, "tca6416_base"); - strcpy(intr_pin_name, "tca6416_int"); - - gpio_base = get_gpio_by_name(base_pin_name); - intr = get_gpio_by_name(intr_pin_name); - - if (gpio_base == -1) - return NULL; - tca6416.gpio_base = gpio_base; - if (intr != -1) { - i2c_info->irq = intr + MRST_IRQ_OFFSET; - tca6416.irq_base = gpio_base + MRST_IRQ_OFFSET; - } else { - i2c_info->irq = -1; - tca6416.irq_base = -1; - } - return &tca6416; -} - -static void *mpu3050_platform_data(void *info) -{ - struct i2c_board_info *i2c_info = info; - int intr = get_gpio_by_name("mpu3050_int"); - - if (intr == -1) - return NULL; - - i2c_info->irq = intr + MRST_IRQ_OFFSET; - return NULL; -} - -static void __init *emc1403_platform_data(void *info) -{ - static short intr2nd_pdata; - struct i2c_board_info *i2c_info = info; - int intr = get_gpio_by_name("thermal_int"); - int intr2nd = get_gpio_by_name("thermal_alert"); - - if (intr == -1 || intr2nd == -1) - return NULL; - - i2c_info->irq = intr + MRST_IRQ_OFFSET; - intr2nd_pdata = intr2nd + MRST_IRQ_OFFSET; - - return &intr2nd_pdata; -} - -static void __init *lis331dl_platform_data(void *info) -{ - static short intr2nd_pdata; - struct i2c_board_info *i2c_info = info; - int intr = get_gpio_by_name("accel_int"); - int intr2nd = get_gpio_by_name("accel_2"); - - if (intr == -1 || intr2nd == -1) - return NULL; - - i2c_info->irq = intr + MRST_IRQ_OFFSET; - intr2nd_pdata = intr2nd + MRST_IRQ_OFFSET; - - return &intr2nd_pdata; -} - -static void __init *no_platform_data(void *info) -{ - return NULL; -} - -static struct resource msic_resources[] = { - { - .start = INTEL_MSIC_IRQ_PHYS_BASE, - .end = INTEL_MSIC_IRQ_PHYS_BASE + 64 - 1, - .flags = IORESOURCE_MEM, - }, -}; - -static struct intel_msic_platform_data msic_pdata; - -static struct platform_device msic_device = { - .name = "intel_msic", - .id = -1, - .dev = { - .platform_data = &msic_pdata, - }, - .num_resources = ARRAY_SIZE(msic_resources), - .resource = msic_resources, -}; - -static inline bool mrst_has_msic(void) -{ - return mrst_identify_cpu() == MRST_CPU_CHIP_PENWELL; -} - -static int msic_scu_status_change(struct notifier_block *nb, - unsigned long code, void *data) -{ - if (code == SCU_DOWN) { - platform_device_unregister(&msic_device); - return 0; - } - - return platform_device_register(&msic_device); -} - -static int __init msic_init(void) -{ - static struct notifier_block msic_scu_notifier = { - .notifier_call = msic_scu_status_change, - }; - - /* - * We need to be sure that the SCU IPC is ready before MSIC device - * can be registered. - */ - if (mrst_has_msic()) - intel_scu_notifier_add(&msic_scu_notifier); - - return 0; -} -arch_initcall(msic_init); - -/* - * msic_generic_platform_data - sets generic platform data for the block - * @info: pointer to the SFI device table entry for this block - * @block: MSIC block - * - * Function sets IRQ number from the SFI table entry for given device to - * the MSIC platform data. - */ -static void *msic_generic_platform_data(void *info, enum intel_msic_block block) -{ - struct sfi_device_table_entry *entry = info; - - BUG_ON(block < 0 || block >= INTEL_MSIC_BLOCK_LAST); - msic_pdata.irq[block] = entry->irq; - - return no_platform_data(info); -} - -static void *msic_battery_platform_data(void *info) -{ - return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_BATTERY); -} - -static void *msic_gpio_platform_data(void *info) -{ - static struct intel_msic_gpio_pdata pdata; - int gpio = get_gpio_by_name("msic_gpio_base"); - - if (gpio < 0) - return NULL; - - pdata.gpio_base = gpio; - msic_pdata.gpio = &pdata; - - return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_GPIO); -} - -static void *msic_audio_platform_data(void *info) -{ - struct platform_device *pdev; - - pdev = platform_device_register_simple("sst-platform", -1, NULL, 0); - if (IS_ERR(pdev)) { - pr_err("failed to create audio platform device\n"); - return NULL; - } - - return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_AUDIO); -} - -static void *msic_power_btn_platform_data(void *info) -{ - return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_POWER_BTN); -} - -static void *msic_ocd_platform_data(void *info) -{ - static struct intel_msic_ocd_pdata pdata; - int gpio = get_gpio_by_name("ocd_gpio"); - - if (gpio < 0) - return NULL; - - pdata.gpio = gpio; - msic_pdata.ocd = &pdata; - - return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_OCD); -} - -static void *msic_thermal_platform_data(void *info) -{ - return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_THERMAL); -} - -/* tc35876x DSI-LVDS bridge chip and panel platform data */ -static void *tc35876x_platform_data(void *data) -{ - static struct tc35876x_platform_data pdata; - - /* gpio pins set to -1 will not be used by the driver */ - pdata.gpio_bridge_reset = get_gpio_by_name("LCMB_RXEN"); - pdata.gpio_panel_bl_en = get_gpio_by_name("6S6P_BL_EN"); - pdata.gpio_panel_vadd = get_gpio_by_name("EN_VREG_LCD_V3P3"); - - return &pdata; -} - -static const struct devs_id __initconst device_ids[] = { - {"bma023", SFI_DEV_TYPE_I2C, 1, &no_platform_data}, - {"pmic_gpio", SFI_DEV_TYPE_SPI, 1, &pmic_gpio_platform_data}, - {"pmic_gpio", SFI_DEV_TYPE_IPC, 1, &pmic_gpio_platform_data}, - {"spi_max3111", SFI_DEV_TYPE_SPI, 0, &max3111_platform_data}, - {"i2c_max7315", SFI_DEV_TYPE_I2C, 1, &max7315_platform_data}, - {"i2c_max7315_2", SFI_DEV_TYPE_I2C, 1, &max7315_platform_data}, - {"tca6416", SFI_DEV_TYPE_I2C, 1, &tca6416_platform_data}, - {"emc1403", SFI_DEV_TYPE_I2C, 1, &emc1403_platform_data}, - {"i2c_accel", SFI_DEV_TYPE_I2C, 0, &lis331dl_platform_data}, - {"pmic_audio", SFI_DEV_TYPE_IPC, 1, &no_platform_data}, - {"mpu3050", SFI_DEV_TYPE_I2C, 1, &mpu3050_platform_data}, - {"i2c_disp_brig", SFI_DEV_TYPE_I2C, 0, &tc35876x_platform_data}, - - /* MSIC subdevices */ - {"msic_battery", SFI_DEV_TYPE_IPC, 1, &msic_battery_platform_data}, - {"msic_gpio", SFI_DEV_TYPE_IPC, 1, &msic_gpio_platform_data}, - {"msic_audio", SFI_DEV_TYPE_IPC, 1, &msic_audio_platform_data}, - {"msic_power_btn", SFI_DEV_TYPE_IPC, 1, &msic_power_btn_platform_data}, - {"msic_ocd", SFI_DEV_TYPE_IPC, 1, &msic_ocd_platform_data}, - {"msic_thermal", SFI_DEV_TYPE_IPC, 1, &msic_thermal_platform_data}, - - {}, -}; - -#define MAX_IPCDEVS 24 -static struct platform_device *ipc_devs[MAX_IPCDEVS]; -static int ipc_next_dev; - -#define MAX_SCU_SPI 24 -static struct spi_board_info *spi_devs[MAX_SCU_SPI]; -static int spi_next_dev; - -#define MAX_SCU_I2C 24 -static struct i2c_board_info *i2c_devs[MAX_SCU_I2C]; -static int i2c_bus[MAX_SCU_I2C]; -static int i2c_next_dev; - -static void __init intel_scu_device_register(struct platform_device *pdev) -{ - if (ipc_next_dev == MAX_IPCDEVS) - pr_err("too many SCU IPC devices"); - else - ipc_devs[ipc_next_dev++] = pdev; -} - -static void __init intel_scu_spi_device_register(struct spi_board_info *sdev) -{ - struct spi_board_info *new_dev; - - if (spi_next_dev == MAX_SCU_SPI) { - pr_err("too many SCU SPI devices"); - return; - } - - new_dev = kzalloc(sizeof(*sdev), GFP_KERNEL); - if (!new_dev) { - pr_err("failed to alloc mem for delayed spi dev %s\n", - sdev->modalias); - return; - } - memcpy(new_dev, sdev, sizeof(*sdev)); - - spi_devs[spi_next_dev++] = new_dev; -} - -static void __init intel_scu_i2c_device_register(int bus, - struct i2c_board_info *idev) -{ - struct i2c_board_info *new_dev; - - if (i2c_next_dev == MAX_SCU_I2C) { - pr_err("too many SCU I2C devices"); - return; - } - - new_dev = kzalloc(sizeof(*idev), GFP_KERNEL); - if (!new_dev) { - pr_err("failed to alloc mem for delayed i2c dev %s\n", - idev->type); - return; - } - memcpy(new_dev, idev, sizeof(*idev)); - - i2c_bus[i2c_next_dev] = bus; - i2c_devs[i2c_next_dev++] = new_dev; -} - -BLOCKING_NOTIFIER_HEAD(intel_scu_notifier); -EXPORT_SYMBOL_GPL(intel_scu_notifier); - -/* Called by IPC driver */ -void intel_scu_devices_create(void) -{ - int i; - - for (i = 0; i < ipc_next_dev; i++) - platform_device_add(ipc_devs[i]); - - for (i = 0; i < spi_next_dev; i++) - spi_register_board_info(spi_devs[i], 1); - - for (i = 0; i < i2c_next_dev; i++) { - struct i2c_adapter *adapter; - struct i2c_client *client; - - adapter = i2c_get_adapter(i2c_bus[i]); - if (adapter) { - client = i2c_new_device(adapter, i2c_devs[i]); - if (!client) - pr_err("can't create i2c device %s\n", - i2c_devs[i]->type); - } else - i2c_register_board_info(i2c_bus[i], i2c_devs[i], 1); - } - intel_scu_notifier_post(SCU_AVAILABLE, NULL); -} -EXPORT_SYMBOL_GPL(intel_scu_devices_create); - -/* Called by IPC driver */ -void intel_scu_devices_destroy(void) -{ - int i; - - intel_scu_notifier_post(SCU_DOWN, NULL); - - for (i = 0; i < ipc_next_dev; i++) - platform_device_del(ipc_devs[i]); -} -EXPORT_SYMBOL_GPL(intel_scu_devices_destroy); - -static void __init install_irq_resource(struct platform_device *pdev, int irq) -{ - /* Single threaded */ - static struct resource __initdata res = { - .name = "IRQ", - .flags = IORESOURCE_IRQ, - }; - res.start = irq; - platform_device_add_resources(pdev, &res, 1); -} - -static void __init sfi_handle_ipc_dev(struct sfi_device_table_entry *entry) -{ - const struct devs_id *dev = device_ids; - struct platform_device *pdev; - void *pdata = NULL; - - while (dev->name[0]) { - if (dev->type == SFI_DEV_TYPE_IPC && - !strncmp(dev->name, entry->name, SFI_NAME_LEN)) { - pdata = dev->get_platform_data(entry); - break; - } - dev++; - } - - /* - * On Medfield the platform device creation is handled by the MSIC - * MFD driver so we don't need to do it here. - */ - if (mrst_has_msic()) - return; - - pdev = platform_device_alloc(entry->name, 0); - if (pdev == NULL) { - pr_err("out of memory for SFI platform device '%s'.\n", - entry->name); - return; - } - install_irq_resource(pdev, entry->irq); - - pdev->dev.platform_data = pdata; - intel_scu_device_register(pdev); -} - -static void __init sfi_handle_spi_dev(struct spi_board_info *spi_info) -{ - const struct devs_id *dev = device_ids; - void *pdata = NULL; - - while (dev->name[0]) { - if (dev->type == SFI_DEV_TYPE_SPI && - !strncmp(dev->name, spi_info->modalias, - SFI_NAME_LEN)) { - pdata = dev->get_platform_data(spi_info); - break; - } - dev++; - } - spi_info->platform_data = pdata; - if (dev->delay) - intel_scu_spi_device_register(spi_info); - else - spi_register_board_info(spi_info, 1); -} - -static void __init sfi_handle_i2c_dev(int bus, struct i2c_board_info *i2c_info) -{ - const struct devs_id *dev = device_ids; - void *pdata = NULL; - - while (dev->name[0]) { - if (dev->type == SFI_DEV_TYPE_I2C && - !strncmp(dev->name, i2c_info->type, SFI_NAME_LEN)) { - pdata = dev->get_platform_data(i2c_info); - break; - } - dev++; - } - i2c_info->platform_data = pdata; - - if (dev->delay) - intel_scu_i2c_device_register(bus, i2c_info); - else - i2c_register_board_info(bus, i2c_info, 1); -} - - -static int __init sfi_parse_devs(struct sfi_table_header *table) -{ - struct sfi_table_simple *sb; - struct sfi_device_table_entry *pentry; - struct spi_board_info spi_info; - struct i2c_board_info i2c_info; - int num, i, bus; - int ioapic; - struct io_apic_irq_attr irq_attr; - - sb = (struct sfi_table_simple *)table; - num = SFI_GET_NUM_ENTRIES(sb, struct sfi_device_table_entry); - pentry = (struct sfi_device_table_entry *)sb->pentry; - - for (i = 0; i < num; i++, pentry++) { - int irq = pentry->irq; - - if (irq != (u8)0xff) { /* native RTE case */ - /* these SPI2 devices are not exposed to system as PCI - * devices, but they have separate RTE entry in IOAPIC - * so we have to enable them one by one here - */ - ioapic = mp_find_ioapic(irq); - irq_attr.ioapic = ioapic; - irq_attr.ioapic_pin = irq; - irq_attr.trigger = 1; - irq_attr.polarity = 1; - io_apic_set_pci_routing(NULL, irq, &irq_attr); - } else - irq = 0; /* No irq */ - - switch (pentry->type) { - case SFI_DEV_TYPE_IPC: - pr_debug("info[%2d]: IPC bus, name = %16.16s, " - "irq = 0x%2x\n", i, pentry->name, pentry->irq); - sfi_handle_ipc_dev(pentry); - break; - case SFI_DEV_TYPE_SPI: - memset(&spi_info, 0, sizeof(spi_info)); - strncpy(spi_info.modalias, pentry->name, SFI_NAME_LEN); - spi_info.irq = irq; - spi_info.bus_num = pentry->host_num; - spi_info.chip_select = pentry->addr; - spi_info.max_speed_hz = pentry->max_freq; - pr_debug("info[%2d]: SPI bus = %d, name = %16.16s, " - "irq = 0x%2x, max_freq = %d, cs = %d\n", i, - spi_info.bus_num, - spi_info.modalias, - spi_info.irq, - spi_info.max_speed_hz, - spi_info.chip_select); - sfi_handle_spi_dev(&spi_info); - break; - case SFI_DEV_TYPE_I2C: - memset(&i2c_info, 0, sizeof(i2c_info)); - bus = pentry->host_num; - strncpy(i2c_info.type, pentry->name, SFI_NAME_LEN); - i2c_info.irq = irq; - i2c_info.addr = pentry->addr; - pr_debug("info[%2d]: I2C bus = %d, name = %16.16s, " - "irq = 0x%2x, addr = 0x%x\n", i, bus, - i2c_info.type, - i2c_info.irq, - i2c_info.addr); - sfi_handle_i2c_dev(bus, &i2c_info); - break; - case SFI_DEV_TYPE_UART: - case SFI_DEV_TYPE_HSI: - default: - ; - } - } - return 0; -} - -static int __init mrst_platform_init(void) -{ - sfi_table_parse(SFI_SIG_GPIO, NULL, NULL, sfi_parse_gpio); - sfi_table_parse(SFI_SIG_DEVS, NULL, NULL, sfi_parse_devs); - return 0; -} -arch_initcall(mrst_platform_init); - -/* - * we will search these buttons in SFI GPIO table (by name) - * and register them dynamically. Please add all possible - * buttons here, we will shrink them if no GPIO found. - */ -static struct gpio_keys_button gpio_button[] = { - {KEY_POWER, -1, 1, "power_btn", EV_KEY, 0, 3000}, - {KEY_PROG1, -1, 1, "prog_btn1", EV_KEY, 0, 20}, - {KEY_PROG2, -1, 1, "prog_btn2", EV_KEY, 0, 20}, - {SW_LID, -1, 1, "lid_switch", EV_SW, 0, 20}, - {KEY_VOLUMEUP, -1, 1, "vol_up", EV_KEY, 0, 20}, - {KEY_VOLUMEDOWN, -1, 1, "vol_down", EV_KEY, 0, 20}, - {KEY_CAMERA, -1, 1, "camera_full", EV_KEY, 0, 20}, - {KEY_CAMERA_FOCUS, -1, 1, "camera_half", EV_KEY, 0, 20}, - {SW_KEYPAD_SLIDE, -1, 1, "MagSw1", EV_SW, 0, 20}, - {SW_KEYPAD_SLIDE, -1, 1, "MagSw2", EV_SW, 0, 20}, -}; - -static struct gpio_keys_platform_data mrst_gpio_keys = { - .buttons = gpio_button, - .rep = 1, - .nbuttons = -1, /* will fill it after search */ -}; - -static struct platform_device pb_device = { - .name = "gpio-keys", - .id = -1, - .dev = { - .platform_data = &mrst_gpio_keys, - }, -}; - -/* - * Shrink the non-existent buttons, register the gpio button - * device if there is some - */ -static int __init pb_keys_init(void) -{ - struct gpio_keys_button *gb = gpio_button; - int i, num, good = 0; - - num = sizeof(gpio_button) / sizeof(struct gpio_keys_button); - for (i = 0; i < num; i++) { - gb[i].gpio = get_gpio_by_name(gb[i].desc); - pr_debug("info[%2d]: name = %s, gpio = %d\n", i, gb[i].desc, - gb[i].gpio); - if (gb[i].gpio == -1) - continue; - - if (i != good) - gb[good] = gb[i]; - good++; - } - - if (good) { - mrst_gpio_keys.nbuttons = good; - return platform_device_register(&pb_device); - } - return 0; -} -late_initcall(pb_keys_init); diff --git a/arch/x86/platform/mrst/vrtc.c b/arch/x86/platform/mrst/vrtc.c deleted file mode 100644 index ca4f7d9..0000000 --- a/arch/x86/platform/mrst/vrtc.c +++ /dev/null @@ -1,177 +0,0 @@ -/* - * vrtc.c: Driver for virtual RTC device on Intel MID platform - * - * (C) Copyright 2009 Intel Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License. - * - * Note: - * VRTC is emulated by system controller firmware, the real HW - * RTC is located in the PMIC device. SCU FW shadows PMIC RTC - * in a memory mapped IO space that is visible to the host IA - * processor. - * - * This driver is based on RTC CMOS driver. - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -static unsigned char __iomem *vrtc_virt_base; - -unsigned char vrtc_cmos_read(unsigned char reg) -{ - unsigned char retval; - - /* vRTC's registers range from 0x0 to 0xD */ - if (reg > 0xd || !vrtc_virt_base) - return 0xff; - - lock_cmos_prefix(reg); - retval = __raw_readb(vrtc_virt_base + (reg << 2)); - lock_cmos_suffix(reg); - return retval; -} -EXPORT_SYMBOL_GPL(vrtc_cmos_read); - -void vrtc_cmos_write(unsigned char val, unsigned char reg) -{ - if (reg > 0xd || !vrtc_virt_base) - return; - - lock_cmos_prefix(reg); - __raw_writeb(val, vrtc_virt_base + (reg << 2)); - lock_cmos_suffix(reg); -} -EXPORT_SYMBOL_GPL(vrtc_cmos_write); - -void vrtc_get_time(struct timespec *now) -{ - u8 sec, min, hour, mday, mon; - unsigned long flags; - u32 year; - - spin_lock_irqsave(&rtc_lock, flags); - - while ((vrtc_cmos_read(RTC_FREQ_SELECT) & RTC_UIP)) - cpu_relax(); - - sec = vrtc_cmos_read(RTC_SECONDS); - min = vrtc_cmos_read(RTC_MINUTES); - hour = vrtc_cmos_read(RTC_HOURS); - mday = vrtc_cmos_read(RTC_DAY_OF_MONTH); - mon = vrtc_cmos_read(RTC_MONTH); - year = vrtc_cmos_read(RTC_YEAR); - - spin_unlock_irqrestore(&rtc_lock, flags); - - /* vRTC YEAR reg contains the offset to 1972 */ - year += 1972; - - pr_info("vRTC: sec: %d min: %d hour: %d day: %d " - "mon: %d year: %d\n", sec, min, hour, mday, mon, year); - - now->tv_sec = mktime(year, mon, mday, hour, min, sec); - now->tv_nsec = 0; -} - -int vrtc_set_mmss(const struct timespec *now) -{ - unsigned long flags; - struct rtc_time tm; - int year; - int retval = 0; - - rtc_time_to_tm(now->tv_sec, &tm); - if (!rtc_valid_tm(&tm) && tm.tm_year >= 72) { - /* - * tm.year is the number of years since 1900, and the - * vrtc need the years since 1972. - */ - year = tm.tm_year - 72; - spin_lock_irqsave(&rtc_lock, flags); - vrtc_cmos_write(year, RTC_YEAR); - vrtc_cmos_write(tm.tm_mon, RTC_MONTH); - vrtc_cmos_write(tm.tm_mday, RTC_DAY_OF_MONTH); - vrtc_cmos_write(tm.tm_hour, RTC_HOURS); - vrtc_cmos_write(tm.tm_min, RTC_MINUTES); - vrtc_cmos_write(tm.tm_sec, RTC_SECONDS); - spin_unlock_irqrestore(&rtc_lock, flags); - } else { - pr_err("%s: Invalid vRTC value: write of %lx to vRTC failed\n", - __FUNCTION__, now->tv_sec); - retval = -EINVAL; - } - return retval; -} - -void __init mrst_rtc_init(void) -{ - unsigned long vrtc_paddr; - - sfi_table_parse(SFI_SIG_MRTC, NULL, NULL, sfi_parse_mrtc); - - vrtc_paddr = sfi_mrtc_array[0].phys_addr; - if (!sfi_mrtc_num || !vrtc_paddr) - return; - - vrtc_virt_base = (void __iomem *)set_fixmap_offset_nocache(FIX_LNW_VRTC, - vrtc_paddr); - x86_platform.get_wallclock = vrtc_get_time; - x86_platform.set_wallclock = vrtc_set_mmss; -} - -/* - * The Moorestown platform has a memory mapped virtual RTC device that emulates - * the programming interface of the RTC. - */ - -static struct resource vrtc_resources[] = { - [0] = { - .flags = IORESOURCE_MEM, - }, - [1] = { - .flags = IORESOURCE_IRQ, - } -}; - -static struct platform_device vrtc_device = { - .name = "rtc_mrst", - .id = -1, - .resource = vrtc_resources, - .num_resources = ARRAY_SIZE(vrtc_resources), -}; - -/* Register the RTC device if appropriate */ -static int __init mrst_device_create(void) -{ - /* No Moorestown, no device */ - if (!mrst_identify_cpu()) - return -ENODEV; - /* No timer, no device */ - if (!sfi_mrtc_num) - return -ENODEV; - - /* iomem resource */ - vrtc_resources[0].start = sfi_mrtc_array[0].phys_addr; - vrtc_resources[0].end = sfi_mrtc_array[0].phys_addr + - MRST_VRTC_MAP_SZ; - /* irq resource */ - vrtc_resources[1].start = sfi_mrtc_array[0].irq; - vrtc_resources[1].end = sfi_mrtc_array[0].irq; - - return platform_device_register(&vrtc_device); -} - -module_init(mrst_device_create); diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_output.h b/drivers/gpu/drm/gma500/mdfld_dsi_output.h index 45d5af0..5b646c1 100644 --- a/drivers/gpu/drm/gma500/mdfld_dsi_output.h +++ b/drivers/gpu/drm/gma500/mdfld_dsi_output.h @@ -39,7 +39,7 @@ #include "psb_intel_reg.h" #include "mdfld_output.h" -#include +#include #define FLD_MASK(start, end) (((1 << ((start) - (end) + 1)) - 1) << (end)) #define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end)) diff --git a/drivers/gpu/drm/gma500/oaktrail_device.c b/drivers/gpu/drm/gma500/oaktrail_device.c index 08747fd..7a9ce00 100644 --- a/drivers/gpu/drm/gma500/oaktrail_device.c +++ b/drivers/gpu/drm/gma500/oaktrail_device.c @@ -26,7 +26,7 @@ #include "psb_drv.h" #include "psb_reg.h" #include "psb_intel_reg.h" -#include +#include #include #include "mid_bios.h" #include "intel_bios.h" diff --git a/drivers/gpu/drm/gma500/oaktrail_lvds.c b/drivers/gpu/drm/gma500/oaktrail_lvds.c index e77d721..3ece553 100644 --- a/drivers/gpu/drm/gma500/oaktrail_lvds.c +++ b/drivers/gpu/drm/gma500/oaktrail_lvds.c @@ -22,7 +22,7 @@ #include #include -#include +#include #include "intel_bios.h" #include "psb_drv.h" diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index 9215ed7..5f8f6c9 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include /* IPC defines the following message types */ diff --git a/drivers/rtc/rtc-mrst.c b/drivers/rtc/rtc-mrst.c index 578baf9..315209d9 100644 --- a/drivers/rtc/rtc-mrst.c +++ b/drivers/rtc/rtc-mrst.c @@ -38,8 +38,8 @@ #include #include -#include -#include +#include +#include struct mrst_rtc { struct rtc_device *rtc; diff --git a/drivers/watchdog/intel_scu_watchdog.c b/drivers/watchdog/intel_scu_watchdog.c index 9dda2d0..07964d8 100644 --- a/drivers/watchdog/intel_scu_watchdog.c +++ b/drivers/watchdog/intel_scu_watchdog.c @@ -48,7 +48,7 @@ #include #include #include -#include +#include #include "intel_scu_watchdog.h" -- cgit v0.10.2 From 6c21b176a93ffaa8023555107167379ccdc6b71f Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Thu, 17 Oct 2013 15:35:28 -0700 Subject: pci: intel_mid: Return true/false in function returning bool Function 'type1_access_ok' should return bool value, not 0/1. This patch changes 'return 0/1' to 'return false/true'. Cc: Kuppuswamy Sathyanarayanan Cc: H. Peter Anvin Cc: David Cohen Signed-off-by: Fengguang Wu Link: http://lkml.kernel.org/r/1382049336-21316-5-git-send-email-david.a.cohen@linux.intel.com Signed-off-by: H. Peter Anvin diff --git a/arch/x86/pci/intel_mid_pci.c b/arch/x86/pci/intel_mid_pci.c index f8715f7..c5ca5b9 100644 --- a/arch/x86/pci/intel_mid_pci.c +++ b/arch/x86/pci/intel_mid_pci.c @@ -150,12 +150,12 @@ static bool type1_access_ok(unsigned int bus, unsigned int devfn, int reg) * shim. Therefore, use the header type in shim instead. */ if (reg >= 0x100 || reg == PCI_STATUS || reg == PCI_HEADER_TYPE) - return 0; + return false; if (bus == 0 && (devfn == PCI_DEVFN(2, 0) || devfn == PCI_DEVFN(0, 0) || devfn == PCI_DEVFN(3, 0))) - return 1; - return 0; /* Langwell on others */ + return true; + return false; /* Langwell on others */ } static int pci_read(struct pci_bus *bus, unsigned int devfn, int where, -- cgit v0.10.2 From 712b6aa8731a7e148298c58cea66a5209c659e3c Mon Sep 17 00:00:00 2001 From: Kuppuswamy Sathyanarayanan Date: Thu, 17 Oct 2013 15:35:29 -0700 Subject: intel_mid: Renamed *mrst* to *intel_mid* mrst is used as common name to represent all intel_mid type soc's. But moorsetwon is just one of the intel_mid soc. So renamed them to use intel_mid. This patch mainly renames the variables and related functions that uses *mrst* prefix with *intel_mid*. To ensure that there are no functional changes, I have compared the objdump of related files before and after rename and found the only difference is symbol and name changes. Signed-off-by: Kuppuswamy Sathyanarayanan Link: http://lkml.kernel.org/r/1382049336-21316-6-git-send-email-david.a.cohen@linux.intel.com Signed-off-by: David Cohen Signed-off-by: H. Peter Anvin diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index fcbb736..dfaeb0c 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -3471,11 +3471,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted. default x2apic cluster mode on platforms supporting x2apic. - x86_mrst_timer= [X86-32,APBT] - Choose timer option for x86 Moorestown MID platform. + x86_intel_mid_timer= [X86-32,APBT] + Choose timer option for x86 Intel MID platform. Two valid options are apbt timer only and lapic timer plus one apbt timer for broadcast timer. - x86_mrst_timer=apbt_only | lapic_and_apbt + x86_intel_mid_timer=apbt_only | lapic_and_apbt xen_emul_unplug= [HW,X86,XEN] Unplug Xen emulated devices diff --git a/arch/x86/include/asm/intel-mid.h b/arch/x86/include/asm/intel-mid.h index cc79a4f..beb7a5f 100644 --- a/arch/x86/include/asm/intel-mid.h +++ b/arch/x86/include/asm/intel-mid.h @@ -13,7 +13,7 @@ #include -extern int pci_mrst_init(void); +extern int intel_mid_pci_init(void); extern int __init sfi_parse_mrtc(struct sfi_table_header *table); extern int sfi_mrtc_num; extern struct sfi_rtc_table_entry sfi_mrtc_array[]; @@ -25,33 +25,33 @@ extern struct sfi_rtc_table_entry sfi_mrtc_array[]; * we treat Medfield/Penwell as a variant of Moorestown. Penwell can be * identified via MSRs. */ -enum mrst_cpu_type { +enum intel_mid_cpu_type { /* 1 was Moorestown */ - MRST_CPU_CHIP_PENWELL = 2, + INTEL_MID_CPU_CHIP_PENWELL = 2, }; -extern enum mrst_cpu_type __mrst_cpu_chip; +extern enum intel_mid_cpu_type __intel_mid_cpu_chip; #ifdef CONFIG_X86_INTEL_MID -static inline enum mrst_cpu_type mrst_identify_cpu(void) +static inline enum intel_mid_cpu_type intel_mid_identify_cpu(void) { - return __mrst_cpu_chip; + return __intel_mid_cpu_chip; } #else /* !CONFIG_X86_INTEL_MID */ -#define mrst_identify_cpu() (0) +#define intel_mid_identify_cpu() (0) #endif /* !CONFIG_X86_INTEL_MID */ -enum mrst_timer_options { - MRST_TIMER_DEFAULT, - MRST_TIMER_APBT_ONLY, - MRST_TIMER_LAPIC_APBT, +enum intel_mid_timer_options { + INTEL_MID_TIMER_DEFAULT, + INTEL_MID_TIMER_APBT_ONLY, + INTEL_MID_TIMER_LAPIC_APBT, }; -extern enum mrst_timer_options mrst_timer_options; +extern enum intel_mid_timer_options intel_mid_timer_options; /* * Penwell uses spread spectrum clock, so the freq number is not exactly @@ -76,6 +76,6 @@ extern void intel_scu_devices_destroy(void); #define MRST_VRTC_MAP_SZ (1024) /*#define MRST_VRTC_PGOFFSET (0xc00) */ -extern void mrst_rtc_init(void); +extern void intel_mid_rtc_init(void); #endif /* _ASM_X86_INTEL_MID_H */ diff --git a/arch/x86/include/asm/setup.h b/arch/x86/include/asm/setup.h index 3475554..59bcf4e 100644 --- a/arch/x86/include/asm/setup.h +++ b/arch/x86/include/asm/setup.h @@ -51,9 +51,9 @@ extern void i386_reserve_resources(void); extern void setup_default_timer_irq(void); #ifdef CONFIG_X86_INTEL_MID -extern void x86_mrst_early_setup(void); +extern void x86_intel_mid_early_setup(void); #else -static inline void x86_mrst_early_setup(void) { } +static inline void x86_intel_mid_early_setup(void) { } #endif #ifdef CONFIG_X86_INTEL_CE diff --git a/arch/x86/include/uapi/asm/bootparam.h b/arch/x86/include/uapi/asm/bootparam.h index c15ddaf..9c3733c 100644 --- a/arch/x86/include/uapi/asm/bootparam.h +++ b/arch/x86/include/uapi/asm/bootparam.h @@ -158,7 +158,7 @@ enum { X86_SUBARCH_PC = 0, X86_SUBARCH_LGUEST, X86_SUBARCH_XEN, - X86_SUBARCH_MRST, + X86_SUBARCH_INTEL_MID, X86_SUBARCH_CE4100, X86_NR_SUBARCHS, }; diff --git a/arch/x86/kernel/apb_timer.c b/arch/x86/kernel/apb_timer.c index 9154836..af5b08a 100644 --- a/arch/x86/kernel/apb_timer.c +++ b/arch/x86/kernel/apb_timer.c @@ -157,13 +157,13 @@ static int __init apbt_clockevent_register(void) adev->num = smp_processor_id(); adev->timer = dw_apb_clockevent_init(smp_processor_id(), "apbt0", - mrst_timer_options == MRST_TIMER_LAPIC_APBT ? + intel_mid_timer_options == INTEL_MID_TIMER_LAPIC_APBT ? APBT_CLOCKEVENT_RATING - 100 : APBT_CLOCKEVENT_RATING, adev_virt_addr(adev), 0, apbt_freq); /* Firmware does EOI handling for us. */ adev->timer->eoi = NULL; - if (mrst_timer_options == MRST_TIMER_LAPIC_APBT) { + if (intel_mid_timer_options == INTEL_MID_TIMER_LAPIC_APBT) { global_clock_event = &adev->timer->ced; printk(KERN_DEBUG "%s clockevent registered as global\n", global_clock_event->name); @@ -253,7 +253,7 @@ static int apbt_cpuhp_notify(struct notifier_block *n, static __init int apbt_late_init(void) { - if (mrst_timer_options == MRST_TIMER_LAPIC_APBT || + if (intel_mid_timer_options == INTEL_MID_TIMER_LAPIC_APBT || !apb_timer_block_enabled) return 0; /* This notifier should be called after workqueue is ready */ @@ -340,7 +340,7 @@ void __init apbt_time_init(void) } #ifdef CONFIG_SMP /* kernel cmdline disable apb timer, so we will use lapic timers */ - if (mrst_timer_options == MRST_TIMER_LAPIC_APBT) { + if (intel_mid_timer_options == INTEL_MID_TIMER_LAPIC_APBT) { printk(KERN_INFO "apbt: disabled per cpu timer\n"); return; } diff --git a/arch/x86/kernel/head32.c b/arch/x86/kernel/head32.c index 06f87be..c61a14a 100644 --- a/arch/x86/kernel/head32.c +++ b/arch/x86/kernel/head32.c @@ -35,8 +35,8 @@ asmlinkage void __init i386_start_kernel(void) /* Call the subarch specific early setup function */ switch (boot_params.hdr.hardware_subarch) { - case X86_SUBARCH_MRST: - x86_mrst_early_setup(); + case X86_SUBARCH_INTEL_MID: + x86_intel_mid_early_setup(); break; case X86_SUBARCH_CE4100: x86_ce4100_early_setup(); diff --git a/arch/x86/kernel/rtc.c b/arch/x86/kernel/rtc.c index a1b52fe..e35cb18 100644 --- a/arch/x86/kernel/rtc.c +++ b/arch/x86/kernel/rtc.c @@ -189,7 +189,7 @@ static __init int add_rtc_cmos(void) return 0; /* Intel MID platforms don't have ioport rtc */ - if (mrst_identify_cpu()) + if (intel_mid_identify_cpu()) return -ENODEV; platform_device_register(&rtc_device); diff --git a/arch/x86/pci/intel_mid_pci.c b/arch/x86/pci/intel_mid_pci.c index c5ca5b9..51384ca 100644 --- a/arch/x86/pci/intel_mid_pci.c +++ b/arch/x86/pci/intel_mid_pci.c @@ -205,7 +205,7 @@ static int pci_write(struct pci_bus *bus, unsigned int devfn, int where, where, size, value); } -static int mrst_pci_irq_enable(struct pci_dev *dev) +static int intel_mid_pci_irq_enable(struct pci_dev *dev) { u8 pin; struct io_apic_irq_attr irq_attr; @@ -225,23 +225,23 @@ static int mrst_pci_irq_enable(struct pci_dev *dev) return 0; } -struct pci_ops pci_mrst_ops = { +struct pci_ops intel_mid_pci_ops = { .read = pci_read, .write = pci_write, }; /** - * pci_mrst_init - installs pci_mrst_ops + * intel_mid_pci_init - installs intel_mid_pci_ops * * Moorestown has an interesting PCI implementation (see above). * Called when the early platform detection installs it. */ -int __init pci_mrst_init(void) +int __init intel_mid_pci_init(void) { pr_info("Intel MID platform detected, using MID PCI ops\n"); pci_mmcfg_late_init(); - pcibios_enable_irq = mrst_pci_irq_enable; - pci_root_ops = pci_mrst_ops; + pcibios_enable_irq = intel_mid_pci_irq_enable; + pci_root_ops = intel_mid_pci_ops; pci_soc_mode = 1; /* Continue with standard init */ return 1; diff --git a/arch/x86/platform/intel-mid/early_printk_intel_mid.c b/arch/x86/platform/intel-mid/early_printk_intel_mid.c index 7c56e70..4f702f5 100644 --- a/arch/x86/platform/intel-mid/early_printk_intel_mid.c +++ b/arch/x86/platform/intel-mid/early_printk_intel_mid.c @@ -152,7 +152,7 @@ void mrst_early_console_init(void) spi0_cdiv = ((*pclk_spi0) & 0xe00) >> 9; freq = 100000000 / (spi0_cdiv + 1); - if (mrst_identify_cpu() == MRST_CPU_CHIP_PENWELL) + if (intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_PENWELL) mrst_spi_paddr = MRST_REGBASE_SPI1; pspi = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, diff --git a/arch/x86/platform/intel-mid/intel-mid.c b/arch/x86/platform/intel-mid/intel-mid.c index 7e6d7b2..94689ac 100644 --- a/arch/x86/platform/intel-mid/intel-mid.c +++ b/arch/x86/platform/intel-mid/intel-mid.c @@ -11,7 +11,7 @@ * of the License. */ -#define pr_fmt(fmt) "mrst: " fmt +#define pr_fmt(fmt) "intel_mid: " fmt #include #include @@ -47,7 +47,7 @@ /* * the clockevent devices on Moorestown/Medfield can be APBT or LAPIC clock, - * cmdline option x86_mrst_timer can be used to override the configuration + * cmdline option x86_intel_mid_timer can be used to override the configuration * to prefer one or the other. * at runtime, there are basically three timer configurations: * 1. per cpu apbt clock only @@ -66,12 +66,12 @@ * lapic (always-on,ARAT) ------ 150 */ -enum mrst_timer_options mrst_timer_options; +enum intel_mid_timer_options intel_mid_timer_options; static u32 sfi_mtimer_usage[SFI_MTMR_MAX_NUM]; static struct sfi_timer_table_entry sfi_mtimer_array[SFI_MTMR_MAX_NUM]; -enum mrst_cpu_type __mrst_cpu_chip; -EXPORT_SYMBOL_GPL(__mrst_cpu_chip); +enum intel_mid_cpu_type __intel_mid_cpu_chip; +EXPORT_SYMBOL_GPL(__intel_mid_cpu_chip); int sfi_mtimer_num; @@ -79,11 +79,11 @@ struct sfi_rtc_table_entry sfi_mrtc_array[SFI_MRTC_MAX]; EXPORT_SYMBOL_GPL(sfi_mrtc_array); int sfi_mrtc_num; -static void mrst_power_off(void) +static void intel_mid_power_off(void) { } -static void mrst_reboot(void) +static void intel_mid_reboot(void) { intel_scu_ipc_simple_command(IPCMSG_COLD_BOOT, 0); } @@ -196,7 +196,7 @@ int __init sfi_parse_mrtc(struct sfi_table_header *table) return 0; } -static unsigned long __init mrst_calibrate_tsc(void) +static unsigned long __init intel_mid_calibrate_tsc(void) { unsigned long fast_calibrate; u32 lo, hi, ratio, fsb; @@ -227,13 +227,13 @@ static unsigned long __init mrst_calibrate_tsc(void) return 0; } -static void __init mrst_time_init(void) +static void __init intel_mid_time_init(void) { sfi_table_parse(SFI_SIG_MTMR, NULL, NULL, sfi_parse_mtmr); - switch (mrst_timer_options) { - case MRST_TIMER_APBT_ONLY: + switch (intel_mid_timer_options) { + case INTEL_MID_TIMER_APBT_ONLY: break; - case MRST_TIMER_LAPIC_APBT: + case INTEL_MID_TIMER_LAPIC_APBT: x86_init.timers.setup_percpu_clockev = setup_boot_APIC_clock; x86_cpuinit.setup_percpu_clockev = setup_secondary_APIC_clock; break; @@ -249,19 +249,19 @@ static void __init mrst_time_init(void) apbt_time_init(); } -static void mrst_arch_setup(void) +static void __cpuinit intel_mid_arch_setup(void) { if (boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model == 0x27) - __mrst_cpu_chip = MRST_CPU_CHIP_PENWELL; + __intel_mid_cpu_chip = INTEL_MID_CPU_CHIP_PENWELL; else { pr_err("Unknown Intel MID CPU (%d:%d), default to Penwell\n", boot_cpu_data.x86, boot_cpu_data.x86_model); - __mrst_cpu_chip = MRST_CPU_CHIP_PENWELL; + __intel_mid_cpu_chip = INTEL_MID_CPU_CHIP_PENWELL; } } /* MID systems don't have i8042 controller */ -static int mrst_i8042_detect(void) +static int intel_mid_i8042_detect(void) { return 0; } @@ -272,7 +272,7 @@ static int mrst_i8042_detect(void) * watchdog or lock debug. Reading io port 0x61 results in 0xff which * misled NMI handler. */ -static unsigned char mrst_get_nmi_reason(void) +static unsigned char intel_mid_get_nmi_reason(void) { return 0; } @@ -281,33 +281,32 @@ static unsigned char mrst_get_nmi_reason(void) * Moorestown specific x86_init function overrides and early setup * calls. */ -void __init x86_mrst_early_setup(void) +void __init x86_intel_mid_early_setup(void) { x86_init.resources.probe_roms = x86_init_noop; x86_init.resources.reserve_resources = x86_init_noop; - x86_init.timers.timer_init = mrst_time_init; + x86_init.timers.timer_init = intel_mid_time_init; x86_init.timers.setup_percpu_clockev = x86_init_noop; x86_init.irqs.pre_vector_init = x86_init_noop; - x86_init.oem.arch_setup = mrst_arch_setup; + x86_init.oem.arch_setup = intel_mid_arch_setup; x86_cpuinit.setup_percpu_clockev = apbt_setup_secondary_clock; - x86_platform.calibrate_tsc = mrst_calibrate_tsc; - x86_platform.i8042_detect = mrst_i8042_detect; - x86_init.timers.wallclock_init = mrst_rtc_init; - x86_platform.get_nmi_reason = mrst_get_nmi_reason; + x86_platform.calibrate_tsc = intel_mid_calibrate_tsc; + x86_platform.i8042_detect = intel_mid_i8042_detect; + x86_init.timers.wallclock_init = intel_mid_rtc_init; + x86_platform.get_nmi_reason = intel_mid_get_nmi_reason; - x86_init.pci.init = pci_mrst_init; + x86_init.pci.init = intel_mid_pci_init; x86_init.pci.fixup_irqs = x86_init_noop; legacy_pic = &null_legacy_pic; - /* Moorestown specific power_off/restart method */ - pm_power_off = mrst_power_off; - machine_ops.emergency_restart = mrst_reboot; + pm_power_off = intel_mid_power_off; + machine_ops.emergency_restart = intel_mid_reboot; /* Avoid searching for BIOS MP tables */ x86_init.mpparse.find_smp_config = x86_init_noop; @@ -319,24 +318,24 @@ void __init x86_mrst_early_setup(void) * if user does not want to use per CPU apb timer, just give it a lower rating * than local apic timer and skip the late per cpu timer init. */ -static inline int __init setup_x86_mrst_timer(char *arg) +static inline int __init setup_x86_intel_mid_timer(char *arg) { if (!arg) return -EINVAL; if (strcmp("apbt_only", arg) == 0) - mrst_timer_options = MRST_TIMER_APBT_ONLY; + intel_mid_timer_options = INTEL_MID_TIMER_APBT_ONLY; else if (strcmp("lapic_and_apbt", arg) == 0) - mrst_timer_options = MRST_TIMER_LAPIC_APBT; + intel_mid_timer_options = INTEL_MID_TIMER_LAPIC_APBT; else { - pr_warn("X86 MRST timer option %s not recognised" - " use x86_mrst_timer=apbt_only or lapic_and_apbt\n", + pr_warn("X86 INTEL_MID timer option %s not recognised" + " use x86_intel_mid_timer=apbt_only or lapic_and_apbt\n", arg); return -EINVAL; } return 0; } -__setup("x86_mrst_timer=", setup_x86_mrst_timer); +__setup("x86_intel_mid_timer=", setup_x86_intel_mid_timer); /* * Parsing GPIO table first, since the DEVS table will need this table @@ -400,7 +399,7 @@ struct devs_id { }; /* the offset for the mapping of global gpio pin to irq */ -#define MRST_IRQ_OFFSET 0x100 +#define INTEL_MID_IRQ_OFFSET 0x100 static void __init *pmic_gpio_platform_data(void *info) { @@ -410,7 +409,7 @@ static void __init *pmic_gpio_platform_data(void *info) if (gpio_base == -1) gpio_base = 64; pmic_gpio_pdata.gpio_base = gpio_base; - pmic_gpio_pdata.irq_base = gpio_base + MRST_IRQ_OFFSET; + pmic_gpio_pdata.irq_base = gpio_base + INTEL_MID_IRQ_OFFSET; pmic_gpio_pdata.gpiointr = 0xffffeff8; return &pmic_gpio_pdata; @@ -424,7 +423,7 @@ static void __init *max3111_platform_data(void *info) spi_info->mode = SPI_MODE_0; if (intr == -1) return NULL; - spi_info->irq = intr + MRST_IRQ_OFFSET; + spi_info->irq = intr + INTEL_MID_IRQ_OFFSET; return NULL; } @@ -464,8 +463,8 @@ static void __init *max7315_platform_data(void *info) return NULL; max7315->gpio_base = gpio_base; if (intr != -1) { - i2c_info->irq = intr + MRST_IRQ_OFFSET; - max7315->irq_base = gpio_base + MRST_IRQ_OFFSET; + i2c_info->irq = intr + INTEL_MID_IRQ_OFFSET; + max7315->irq_base = gpio_base + INTEL_MID_IRQ_OFFSET; } else { i2c_info->irq = -1; max7315->irq_base = -1; @@ -492,8 +491,8 @@ static void *tca6416_platform_data(void *info) return NULL; tca6416.gpio_base = gpio_base; if (intr != -1) { - i2c_info->irq = intr + MRST_IRQ_OFFSET; - tca6416.irq_base = gpio_base + MRST_IRQ_OFFSET; + i2c_info->irq = intr + INTEL_MID_IRQ_OFFSET; + tca6416.irq_base = gpio_base + INTEL_MID_IRQ_OFFSET; } else { i2c_info->irq = -1; tca6416.irq_base = -1; @@ -509,7 +508,7 @@ static void *mpu3050_platform_data(void *info) if (intr == -1) return NULL; - i2c_info->irq = intr + MRST_IRQ_OFFSET; + i2c_info->irq = intr + INTEL_MID_IRQ_OFFSET; return NULL; } @@ -523,8 +522,8 @@ static void __init *emc1403_platform_data(void *info) if (intr == -1 || intr2nd == -1) return NULL; - i2c_info->irq = intr + MRST_IRQ_OFFSET; - intr2nd_pdata = intr2nd + MRST_IRQ_OFFSET; + i2c_info->irq = intr + INTEL_MID_IRQ_OFFSET; + intr2nd_pdata = intr2nd + INTEL_MID_IRQ_OFFSET; return &intr2nd_pdata; } @@ -539,8 +538,8 @@ static void __init *lis331dl_platform_data(void *info) if (intr == -1 || intr2nd == -1) return NULL; - i2c_info->irq = intr + MRST_IRQ_OFFSET; - intr2nd_pdata = intr2nd + MRST_IRQ_OFFSET; + i2c_info->irq = intr + INTEL_MID_IRQ_OFFSET; + intr2nd_pdata = intr2nd + INTEL_MID_IRQ_OFFSET; return &intr2nd_pdata; } @@ -570,9 +569,9 @@ static struct platform_device msic_device = { .resource = msic_resources, }; -static inline bool mrst_has_msic(void) +static inline bool intel_mid_has_msic(void) { - return mrst_identify_cpu() == MRST_CPU_CHIP_PENWELL; + return intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_PENWELL; } static int msic_scu_status_change(struct notifier_block *nb, @@ -596,7 +595,7 @@ static int __init msic_init(void) * We need to be sure that the SCU IPC is ready before MSIC device * can be registered. */ - if (mrst_has_msic()) + if (intel_mid_has_msic()) intel_scu_notifier_add(&msic_scu_notifier); return 0; @@ -851,7 +850,7 @@ static void __init sfi_handle_ipc_dev(struct sfi_device_table_entry *entry) * On Medfield the platform device creation is handled by the MSIC * MFD driver so we don't need to do it here. */ - if (mrst_has_msic()) + if (intel_mid_has_msic()) return; pdev = platform_device_alloc(entry->name, 0); @@ -984,13 +983,13 @@ static int __init sfi_parse_devs(struct sfi_table_header *table) return 0; } -static int __init mrst_platform_init(void) +static int __init intel_mid_platform_init(void) { sfi_table_parse(SFI_SIG_GPIO, NULL, NULL, sfi_parse_gpio); sfi_table_parse(SFI_SIG_DEVS, NULL, NULL, sfi_parse_devs); return 0; } -arch_initcall(mrst_platform_init); +arch_initcall(intel_mid_platform_init); /* * we will search these buttons in SFI GPIO table (by name) @@ -1010,7 +1009,7 @@ static struct gpio_keys_button gpio_button[] = { {SW_KEYPAD_SLIDE, -1, 1, "MagSw2", EV_SW, 0, 20}, }; -static struct gpio_keys_platform_data mrst_gpio_keys = { +static struct gpio_keys_platform_data intel_mid_gpio_keys = { .buttons = gpio_button, .rep = 1, .nbuttons = -1, /* will fill it after search */ @@ -1020,7 +1019,7 @@ static struct platform_device pb_device = { .name = "gpio-keys", .id = -1, .dev = { - .platform_data = &mrst_gpio_keys, + .platform_data = &intel_mid_gpio_keys, }, }; @@ -1047,7 +1046,7 @@ static int __init pb_keys_init(void) } if (good) { - mrst_gpio_keys.nbuttons = good; + intel_mid_gpio_keys.nbuttons = good; return platform_device_register(&pb_device); } return 0; diff --git a/arch/x86/platform/intel-mid/intel_mid_vrtc.c b/arch/x86/platform/intel-mid/intel_mid_vrtc.c index ded9fbd..4762cff 100644 --- a/arch/x86/platform/intel-mid/intel_mid_vrtc.c +++ b/arch/x86/platform/intel-mid/intel_mid_vrtc.c @@ -116,7 +116,7 @@ int vrtc_set_mmss(const struct timespec *now) return retval; } -void __init mrst_rtc_init(void) +void __init intel_mid_rtc_init(void) { unsigned long vrtc_paddr; @@ -154,10 +154,10 @@ static struct platform_device vrtc_device = { }; /* Register the RTC device if appropriate */ -static int __init mrst_device_create(void) +static int __init intel_mid_device_create(void) { /* No Moorestown, no device */ - if (!mrst_identify_cpu()) + if (!intel_mid_identify_cpu()) return -ENODEV; /* No timer, no device */ if (!sfi_mrtc_num) @@ -174,4 +174,4 @@ static int __init mrst_device_create(void) return platform_device_register(&vrtc_device); } -module_init(mrst_device_create); +module_init(intel_mid_device_create); diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index 5f8f6c9..d654f83 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -579,7 +579,7 @@ static struct pci_driver ipc_driver = { static int __init intel_scu_ipc_init(void) { - platform = mrst_identify_cpu(); + platform = intel_mid_identify_cpu(); if (platform == 0) return -ENODEV; return pci_register_driver(&ipc_driver); diff --git a/drivers/watchdog/intel_scu_watchdog.c b/drivers/watchdog/intel_scu_watchdog.c index 07964d8..8ced256 100644 --- a/drivers/watchdog/intel_scu_watchdog.c +++ b/drivers/watchdog/intel_scu_watchdog.c @@ -445,7 +445,7 @@ static int __init intel_scu_watchdog_init(void) * * If it isn't an intel MID device then it doesn't have this watchdog */ - if (!mrst_identify_cpu()) + if (!intel_mid_identify_cpu()) return -ENODEV; /* Check boot parameters to verify that their initial values */ -- cgit v0.10.2 From 661b01076500e364c68dd9fdf0ef4216a75e8375 Mon Sep 17 00:00:00 2001 From: Kuppuswamy Sathyanarayanan Date: Thu, 17 Oct 2013 15:35:30 -0700 Subject: intel_mid: Refactored sfi_parse_devs() function SFI device_id[] table parsing code is duplicated in every SFI device handler. This patch removes this code duplication, by adding a seperate function get_device_id() to parse through the device table. Also this patch moves the SPI, I2C, IPC info code from sfi_parse_devs() to respective device handlers. Signed-off-by: Kuppuswamy Sathyanarayanan Link: http://lkml.kernel.org/r/1382049336-21316-7-git-send-email-david.a.cohen@linux.intel.com Signed-off-by: David Cohen Signed-off-by: H. Peter Anvin diff --git a/arch/x86/platform/intel-mid/intel-mid.c b/arch/x86/platform/intel-mid/intel-mid.c index 94689ac..d24c729 100644 --- a/arch/x86/platform/intel-mid/intel-mid.c +++ b/arch/x86/platform/intel-mid/intel-mid.c @@ -831,20 +831,15 @@ static void __init install_irq_resource(struct platform_device *pdev, int irq) platform_device_add_resources(pdev, &res, 1); } -static void __init sfi_handle_ipc_dev(struct sfi_device_table_entry *entry) +static void __init sfi_handle_ipc_dev(struct sfi_device_table_entry *pentry, + struct devs_id *dev) { - const struct devs_id *dev = device_ids; struct platform_device *pdev; void *pdata = NULL; - while (dev->name[0]) { - if (dev->type == SFI_DEV_TYPE_IPC && - !strncmp(dev->name, entry->name, SFI_NAME_LEN)) { - pdata = dev->get_platform_data(entry); - break; - } - dev++; - } + pr_debug("IPC bus, name = %16.16s, irq = 0x%2x\n", + pentry->name, pentry->irq); + pdata = dev->get_platform_data(pentry); /* * On Medfield the platform device creation is handled by the MSIC @@ -853,68 +848,94 @@ static void __init sfi_handle_ipc_dev(struct sfi_device_table_entry *entry) if (intel_mid_has_msic()) return; - pdev = platform_device_alloc(entry->name, 0); + pdev = platform_device_alloc(pentry->name, 0); if (pdev == NULL) { pr_err("out of memory for SFI platform device '%s'.\n", - entry->name); + pentry->name); return; } - install_irq_resource(pdev, entry->irq); + install_irq_resource(pdev, pentry->irq); pdev->dev.platform_data = pdata; intel_scu_device_register(pdev); } -static void __init sfi_handle_spi_dev(struct spi_board_info *spi_info) +static void __init sfi_handle_spi_dev(struct sfi_device_table_entry *pentry, + struct devs_id *dev) { - const struct devs_id *dev = device_ids; + struct spi_board_info spi_info; void *pdata = NULL; - while (dev->name[0]) { - if (dev->type == SFI_DEV_TYPE_SPI && - !strncmp(dev->name, spi_info->modalias, - SFI_NAME_LEN)) { - pdata = dev->get_platform_data(spi_info); - break; - } - dev++; - } - spi_info->platform_data = pdata; + memset(&spi_info, 0, sizeof(spi_info)); + strncpy(spi_info.modalias, pentry->name, SFI_NAME_LEN); + spi_info.irq = ((pentry->irq == (u8)0xff) ? 0 : pentry->irq); + spi_info.bus_num = pentry->host_num; + spi_info.chip_select = pentry->addr; + spi_info.max_speed_hz = pentry->max_freq; + pr_debug("SPI bus=%d, name=%16.16s, irq=0x%2x, max_freq=%d, cs=%d\n", + spi_info.bus_num, + spi_info.modalias, + spi_info.irq, + spi_info.max_speed_hz, + spi_info.chip_select); + + pdata = dev->get_platform_data(&spi_info); + + spi_info.platform_data = pdata; if (dev->delay) - intel_scu_spi_device_register(spi_info); + intel_scu_spi_device_register(&spi_info); else - spi_register_board_info(spi_info, 1); + spi_register_board_info(&spi_info, 1); } -static void __init sfi_handle_i2c_dev(int bus, struct i2c_board_info *i2c_info) +static void __init sfi_handle_i2c_dev(struct sfi_device_table_entry *pentry, + struct devs_id *dev) { - const struct devs_id *dev = device_ids; + struct i2c_board_info i2c_info; void *pdata = NULL; + memset(&i2c_info, 0, sizeof(i2c_info)); + strncpy(i2c_info.type, pentry->name, SFI_NAME_LEN); + i2c_info.irq = ((pentry->irq == (u8)0xff) ? 0 : pentry->irq); + i2c_info.addr = pentry->addr; + pr_debug("I2C bus = %d, name = %16.16s, irq = 0x%2x, addr = 0x%x\n", + pentry->host_num, + i2c_info.type, + i2c_info.irq, + i2c_info.addr); + pdata = dev->get_platform_data(&i2c_info); + i2c_info.platform_data = pdata; + + if (dev->delay) + intel_scu_i2c_device_register(pentry->host_num, &i2c_info); + else + i2c_register_board_info(pentry->host_num, &i2c_info, 1); +} + +static struct devs_id __init *get_device_id(u8 type, char *name) +{ + struct devs_id *dev = device_ids; + + if (device_ids == NULL) + return NULL; + while (dev->name[0]) { - if (dev->type == SFI_DEV_TYPE_I2C && - !strncmp(dev->name, i2c_info->type, SFI_NAME_LEN)) { - pdata = dev->get_platform_data(i2c_info); - break; + if (dev->type == type && + !strncmp(dev->name, name, SFI_NAME_LEN)) { + return dev; } dev++; } - i2c_info->platform_data = pdata; - if (dev->delay) - intel_scu_i2c_device_register(bus, i2c_info); - else - i2c_register_board_info(bus, i2c_info, 1); + return NULL; } - static int __init sfi_parse_devs(struct sfi_table_header *table) { struct sfi_table_simple *sb; struct sfi_device_table_entry *pentry; - struct spi_board_info spi_info; - struct i2c_board_info i2c_info; - int num, i, bus; + struct devs_id *dev = NULL; + int num, i; int ioapic; struct io_apic_irq_attr irq_attr; @@ -939,40 +960,20 @@ static int __init sfi_parse_devs(struct sfi_table_header *table) } else irq = 0; /* No irq */ + dev = get_device_id(pentry->type, pentry->name); + + if ((dev == NULL) || (dev->get_platform_data == NULL)) + continue; + switch (pentry->type) { case SFI_DEV_TYPE_IPC: - pr_debug("info[%2d]: IPC bus, name = %16.16s, " - "irq = 0x%2x\n", i, pentry->name, pentry->irq); - sfi_handle_ipc_dev(pentry); + sfi_handle_ipc_dev(pentry, dev); break; case SFI_DEV_TYPE_SPI: - memset(&spi_info, 0, sizeof(spi_info)); - strncpy(spi_info.modalias, pentry->name, SFI_NAME_LEN); - spi_info.irq = irq; - spi_info.bus_num = pentry->host_num; - spi_info.chip_select = pentry->addr; - spi_info.max_speed_hz = pentry->max_freq; - pr_debug("info[%2d]: SPI bus = %d, name = %16.16s, " - "irq = 0x%2x, max_freq = %d, cs = %d\n", i, - spi_info.bus_num, - spi_info.modalias, - spi_info.irq, - spi_info.max_speed_hz, - spi_info.chip_select); - sfi_handle_spi_dev(&spi_info); + sfi_handle_spi_dev(pentry, dev); break; case SFI_DEV_TYPE_I2C: - memset(&i2c_info, 0, sizeof(i2c_info)); - bus = pentry->host_num; - strncpy(i2c_info.type, pentry->name, SFI_NAME_LEN); - i2c_info.irq = irq; - i2c_info.addr = pentry->addr; - pr_debug("info[%2d]: I2C bus = %d, name = %16.16s, " - "irq = 0x%2x, addr = 0x%x\n", i, bus, - i2c_info.type, - i2c_info.irq, - i2c_info.addr); - sfi_handle_i2c_dev(bus, &i2c_info); + sfi_handle_i2c_dev(pentry, dev); break; case SFI_DEV_TYPE_UART: case SFI_DEV_TYPE_HSI: -- cgit v0.10.2 From 3fd79ae4275001f293dbd170479e89df6c433226 Mon Sep 17 00:00:00 2001 From: Kuppuswamy Sathyanarayanan Date: Thu, 17 Oct 2013 15:35:31 -0700 Subject: intel_mid: Added custom device_handler support This patch provides a means to add custom handler for SFI devices. If you set device_handler as NULL in device_id table standard SFI device handler will be used. If its not NULL custom handler will be called. Signed-off-by: Kuppuswamy Sathyanarayanan Link: http://lkml.kernel.org/r/1382049336-21316-8-git-send-email-david.a.cohen@linux.intel.com Signed-off-by: David Cohen Signed-off-by: H. Peter Anvin diff --git a/arch/x86/platform/intel-mid/intel-mid.c b/arch/x86/platform/intel-mid/intel-mid.c index d24c729..7bfd784 100644 --- a/arch/x86/platform/intel-mid/intel-mid.c +++ b/arch/x86/platform/intel-mid/intel-mid.c @@ -396,6 +396,9 @@ struct devs_id { u8 type; u8 delay; void *(*get_platform_data)(void *info); + /* Custom handler for devices */ + void (*device_handler)(struct sfi_device_table_entry *pentry, + struct devs_id *dev); }; /* the offset for the mapping of global gpio pin to irq */ @@ -690,28 +693,27 @@ static void *tc35876x_platform_data(void *data) } static const struct devs_id __initconst device_ids[] = { - {"bma023", SFI_DEV_TYPE_I2C, 1, &no_platform_data}, - {"pmic_gpio", SFI_DEV_TYPE_SPI, 1, &pmic_gpio_platform_data}, - {"pmic_gpio", SFI_DEV_TYPE_IPC, 1, &pmic_gpio_platform_data}, - {"spi_max3111", SFI_DEV_TYPE_SPI, 0, &max3111_platform_data}, - {"i2c_max7315", SFI_DEV_TYPE_I2C, 1, &max7315_platform_data}, - {"i2c_max7315_2", SFI_DEV_TYPE_I2C, 1, &max7315_platform_data}, - {"tca6416", SFI_DEV_TYPE_I2C, 1, &tca6416_platform_data}, - {"emc1403", SFI_DEV_TYPE_I2C, 1, &emc1403_platform_data}, - {"i2c_accel", SFI_DEV_TYPE_I2C, 0, &lis331dl_platform_data}, - {"pmic_audio", SFI_DEV_TYPE_IPC, 1, &no_platform_data}, - {"mpu3050", SFI_DEV_TYPE_I2C, 1, &mpu3050_platform_data}, - {"i2c_disp_brig", SFI_DEV_TYPE_I2C, 0, &tc35876x_platform_data}, + {"bma023", SFI_DEV_TYPE_I2C, 1, &no_platform_data, NULL}, + {"pmic_gpio", SFI_DEV_TYPE_SPI, 1, &pmic_gpio_platform_data, NULL}, + {"pmic_gpio", SFI_DEV_TYPE_IPC, 1, &pmic_gpio_platform_data, NULL}, + {"spi_max3111", SFI_DEV_TYPE_SPI, 0, &max3111_platform_data, NULL}, + {"i2c_max7315", SFI_DEV_TYPE_I2C, 1, &max7315_platform_data, NULL}, + {"i2c_max7315_2", SFI_DEV_TYPE_I2C, 1, &max7315_platform_data, NULL}, + {"tca6416", SFI_DEV_TYPE_I2C, 1, &tca6416_platform_data, NULL}, + {"emc1403", SFI_DEV_TYPE_I2C, 1, &emc1403_platform_data, NULL}, + {"i2c_accel", SFI_DEV_TYPE_I2C, 0, &lis331dl_platform_data, NULL}, + {"pmic_audio", SFI_DEV_TYPE_IPC, 1, &no_platform_data, NULL}, + {"mpu3050", SFI_DEV_TYPE_I2C, 1, &mpu3050_platform_data, NULL}, + {"i2c_disp_brig", SFI_DEV_TYPE_I2C, 0, &tc35876x_platform_data, NULL}, /* MSIC subdevices */ - {"msic_battery", SFI_DEV_TYPE_IPC, 1, &msic_battery_platform_data}, - {"msic_gpio", SFI_DEV_TYPE_IPC, 1, &msic_gpio_platform_data}, - {"msic_audio", SFI_DEV_TYPE_IPC, 1, &msic_audio_platform_data}, - {"msic_power_btn", SFI_DEV_TYPE_IPC, 1, &msic_power_btn_platform_data}, - {"msic_ocd", SFI_DEV_TYPE_IPC, 1, &msic_ocd_platform_data}, - {"msic_thermal", SFI_DEV_TYPE_IPC, 1, &msic_thermal_platform_data}, - - {}, + {"msic_battery", SFI_DEV_TYPE_IPC, 1, &msic_battery_platform_data, NULL}, + {"msic_gpio", SFI_DEV_TYPE_IPC, 1, &msic_gpio_platform_data, NULL}, + {"msic_audio", SFI_DEV_TYPE_IPC, 1, &msic_audio_platform_data, NULL}, + {"msic_power_btn", SFI_DEV_TYPE_IPC, 1, &msic_power_btn_platform_data, NULL}, + {"msic_ocd", SFI_DEV_TYPE_IPC, 1, &msic_ocd_platform_data, NULL}, + {"msic_thermal", SFI_DEV_TYPE_IPC, 1, &msic_thermal_platform_data, NULL}, + { 0 } }; #define MAX_IPCDEVS 24 @@ -965,20 +967,24 @@ static int __init sfi_parse_devs(struct sfi_table_header *table) if ((dev == NULL) || (dev->get_platform_data == NULL)) continue; - switch (pentry->type) { - case SFI_DEV_TYPE_IPC: - sfi_handle_ipc_dev(pentry, dev); - break; - case SFI_DEV_TYPE_SPI: - sfi_handle_spi_dev(pentry, dev); - break; - case SFI_DEV_TYPE_I2C: - sfi_handle_i2c_dev(pentry, dev); - break; - case SFI_DEV_TYPE_UART: - case SFI_DEV_TYPE_HSI: - default: - ; + if (dev->device_handler) { + dev->device_handler(pentry, dev); + } else { + switch (pentry->type) { + case SFI_DEV_TYPE_IPC: + sfi_handle_ipc_dev(pentry, dev); + break; + case SFI_DEV_TYPE_SPI: + sfi_handle_spi_dev(pentry, dev); + break; + case SFI_DEV_TYPE_I2C: + sfi_handle_i2c_dev(pentry, dev); + break; + case SFI_DEV_TYPE_UART: + case SFI_DEV_TYPE_HSI: + default: + break; + } } } return 0; -- cgit v0.10.2 From 49c72a0a8ad640fa6026962056eeaf85a4ce79fd Mon Sep 17 00:00:00 2001 From: Kuppuswamy Sathyanarayanan Date: Thu, 17 Oct 2013 15:35:32 -0700 Subject: intel_mid: Added custom handler for ipc devices Added a custom handler for medfield based ipc devices and moved devs_id structure defintion to header file. Signed-off-by: Kuppuswamy Sathyanarayanan Link: http://lkml.kernel.org/r/1382049336-21316-9-git-send-email-david.a.cohen@linux.intel.com Signed-off-by: David Cohen Signed-off-by: H. Peter Anvin diff --git a/arch/x86/include/asm/intel-mid.h b/arch/x86/include/asm/intel-mid.h index beb7a5f..ad236ae 100644 --- a/arch/x86/include/asm/intel-mid.h +++ b/arch/x86/include/asm/intel-mid.h @@ -19,6 +19,21 @@ extern int sfi_mrtc_num; extern struct sfi_rtc_table_entry sfi_mrtc_array[]; /* + * Here defines the array of devices platform data that IAFW would export + * through SFI "DEVS" table, we use name and type to match the device and + * its platform data. + */ +struct devs_id { + char name[SFI_NAME_LEN + 1]; + u8 type; + u8 delay; + void *(*get_platform_data)(void *info); + /* Custom handler for devices */ + void (*device_handler)(struct sfi_device_table_entry *pentry, + struct devs_id *dev); +}; + +/* * Medfield is the follow-up of Moorestown, it combines two chip solution into * one. Other than that it also added always-on and constant tsc and lapic * timers. Medfield is the platform name, and the chip name is called Penwell diff --git a/arch/x86/platform/intel-mid/intel-mid.c b/arch/x86/platform/intel-mid/intel-mid.c index 7bfd784..40a3ff8 100644 --- a/arch/x86/platform/intel-mid/intel-mid.c +++ b/arch/x86/platform/intel-mid/intel-mid.c @@ -78,6 +78,8 @@ int sfi_mtimer_num; struct sfi_rtc_table_entry sfi_mrtc_array[SFI_MRTC_MAX]; EXPORT_SYMBOL_GPL(sfi_mrtc_array); int sfi_mrtc_num; +static void __init ipc_device_handler(struct sfi_device_table_entry *pentry, + struct devs_id *dev); static void intel_mid_power_off(void) { @@ -386,21 +388,6 @@ static int get_gpio_by_name(const char *name) return -1; } -/* - * Here defines the array of devices platform data that IAFW would export - * through SFI "DEVS" table, we use name and type to match the device and - * its platform data. - */ -struct devs_id { - char name[SFI_NAME_LEN + 1]; - u8 type; - u8 delay; - void *(*get_platform_data)(void *info); - /* Custom handler for devices */ - void (*device_handler)(struct sfi_device_table_entry *pentry, - struct devs_id *dev); -}; - /* the offset for the mapping of global gpio pin to irq */ #define INTEL_MID_IRQ_OFFSET 0x100 @@ -695,24 +682,24 @@ static void *tc35876x_platform_data(void *data) static const struct devs_id __initconst device_ids[] = { {"bma023", SFI_DEV_TYPE_I2C, 1, &no_platform_data, NULL}, {"pmic_gpio", SFI_DEV_TYPE_SPI, 1, &pmic_gpio_platform_data, NULL}, - {"pmic_gpio", SFI_DEV_TYPE_IPC, 1, &pmic_gpio_platform_data, NULL}, + {"pmic_gpio", SFI_DEV_TYPE_IPC, 1, &pmic_gpio_platform_data, &ipc_device_handler}, {"spi_max3111", SFI_DEV_TYPE_SPI, 0, &max3111_platform_data, NULL}, {"i2c_max7315", SFI_DEV_TYPE_I2C, 1, &max7315_platform_data, NULL}, {"i2c_max7315_2", SFI_DEV_TYPE_I2C, 1, &max7315_platform_data, NULL}, {"tca6416", SFI_DEV_TYPE_I2C, 1, &tca6416_platform_data, NULL}, {"emc1403", SFI_DEV_TYPE_I2C, 1, &emc1403_platform_data, NULL}, {"i2c_accel", SFI_DEV_TYPE_I2C, 0, &lis331dl_platform_data, NULL}, - {"pmic_audio", SFI_DEV_TYPE_IPC, 1, &no_platform_data, NULL}, + {"pmic_audio", SFI_DEV_TYPE_IPC, 1, &no_platform_data, &ipc_device_handler}, {"mpu3050", SFI_DEV_TYPE_I2C, 1, &mpu3050_platform_data, NULL}, {"i2c_disp_brig", SFI_DEV_TYPE_I2C, 0, &tc35876x_platform_data, NULL}, /* MSIC subdevices */ - {"msic_battery", SFI_DEV_TYPE_IPC, 1, &msic_battery_platform_data, NULL}, - {"msic_gpio", SFI_DEV_TYPE_IPC, 1, &msic_gpio_platform_data, NULL}, - {"msic_audio", SFI_DEV_TYPE_IPC, 1, &msic_audio_platform_data, NULL}, - {"msic_power_btn", SFI_DEV_TYPE_IPC, 1, &msic_power_btn_platform_data, NULL}, - {"msic_ocd", SFI_DEV_TYPE_IPC, 1, &msic_ocd_platform_data, NULL}, - {"msic_thermal", SFI_DEV_TYPE_IPC, 1, &msic_thermal_platform_data, NULL}, + {"msic_battery", SFI_DEV_TYPE_IPC, 1, &msic_battery_platform_data, &ipc_device_handler}, + {"msic_gpio", SFI_DEV_TYPE_IPC, 1, &msic_gpio_platform_data, &ipc_device_handler}, + {"msic_audio", SFI_DEV_TYPE_IPC, 1, &msic_audio_platform_data, &ipc_device_handler}, + {"msic_power_btn", SFI_DEV_TYPE_IPC, 1, &msic_power_btn_platform_data, &ipc_device_handler}, + {"msic_ocd", SFI_DEV_TYPE_IPC, 1, &msic_ocd_platform_data, &ipc_device_handler}, + {"msic_thermal", SFI_DEV_TYPE_IPC, 1, &msic_thermal_platform_data, &ipc_device_handler}, { 0 } }; @@ -843,13 +830,6 @@ static void __init sfi_handle_ipc_dev(struct sfi_device_table_entry *pentry, pentry->name, pentry->irq); pdata = dev->get_platform_data(pentry); - /* - * On Medfield the platform device creation is handled by the MSIC - * MFD driver so we don't need to do it here. - */ - if (intel_mid_has_msic()) - return; - pdev = platform_device_alloc(pentry->name, 0); if (pdev == NULL) { pr_err("out of memory for SFI platform device '%s'.\n", @@ -859,7 +839,7 @@ static void __init sfi_handle_ipc_dev(struct sfi_device_table_entry *pentry, install_irq_resource(pdev, pentry->irq); pdev->dev.platform_data = pdata; - intel_scu_device_register(pdev); + platform_device_add(pdev); } static void __init sfi_handle_spi_dev(struct sfi_device_table_entry *pentry, @@ -914,6 +894,46 @@ static void __init sfi_handle_i2c_dev(struct sfi_device_table_entry *pentry, i2c_register_board_info(pentry->host_num, &i2c_info, 1); } +static void __init ipc_device_handler(struct sfi_device_table_entry *pentry, + struct devs_id *dev) +{ + struct platform_device *pdev; + void *pdata = NULL; + static struct resource res __initdata = { + .name = "IRQ", + .flags = IORESOURCE_IRQ, + }; + + pr_debug("IPC bus, name = %16.16s, irq = 0x%2x\n", + pentry->name, pentry->irq); + + /* + * We need to call platform init of IPC devices to fill misc_pdata + * structure. It will be used in msic_init for initialization. + */ + if (dev != NULL) + pdata = dev->get_platform_data(pentry); + + /* + * On Medfield the platform device creation is handled by the MSIC + * MFD driver so we don't need to do it here. + */ + if (intel_mid_has_msic()) + return; + + pdev = platform_device_alloc(pentry->name, 0); + if (pdev == NULL) { + pr_err("out of memory for SFI platform device '%s'.\n", + pentry->name); + return; + } + res.start = pentry->irq; + platform_device_add_resources(pdev, &res, 1); + + pdev->dev.platform_data = pdata; + intel_scu_device_register(pdev); +} + static struct devs_id __init *get_device_id(u8 type, char *name) { struct devs_id *dev = device_ids; -- cgit v0.10.2 From aeedb370e7398fb5b39185b295d36f2da0653215 Mon Sep 17 00:00:00 2001 From: Kuppuswamy Sathyanarayanan Date: Thu, 17 Oct 2013 15:35:33 -0700 Subject: intel_mid: Moved SFI related code to sfi.c Moved SFI specific parsing/handling code to sfi.c. This will enable us to reuse our intel-mid code for platforms that supports firmware interfaces other than SFI (like ACPI). Signed-off-by: Kuppuswamy Sathyanarayanan Link: http://lkml.kernel.org/r/1382049336-21316-10-git-send-email-david.a.cohen@linux.intel.com Signed-off-by: David Cohen Signed-off-by: H. Peter Anvin diff --git a/arch/x86/include/asm/intel-mid.h b/arch/x86/include/asm/intel-mid.h index ad236ae..3b0e7a7 100644 --- a/arch/x86/include/asm/intel-mid.h +++ b/arch/x86/include/asm/intel-mid.h @@ -15,6 +15,7 @@ extern int intel_mid_pci_init(void); extern int __init sfi_parse_mrtc(struct sfi_table_header *table); +extern int __init sfi_parse_mtmr(struct sfi_table_header *table); extern int sfi_mrtc_num; extern struct sfi_rtc_table_entry sfi_mrtc_array[]; diff --git a/arch/x86/platform/intel-mid/Makefile b/arch/x86/platform/intel-mid/Makefile index de29635..b11e5b2 100644 --- a/arch/x86/platform/intel-mid/Makefile +++ b/arch/x86/platform/intel-mid/Makefile @@ -1,3 +1,5 @@ obj-$(CONFIG_X86_INTEL_MID) += intel-mid.o obj-$(CONFIG_X86_INTEL_MID) += intel_mid_vrtc.o obj-$(CONFIG_EARLY_PRINTK_INTEL_MID) += early_printk_intel_mid.o +# SFI specific code +obj-$(CONFIG_SFI) += sfi.o diff --git a/arch/x86/platform/intel-mid/intel-mid.c b/arch/x86/platform/intel-mid/intel-mid.c index 40a3ff8..4091569 100644 --- a/arch/x86/platform/intel-mid/intel-mid.c +++ b/arch/x86/platform/intel-mid/intel-mid.c @@ -18,19 +18,9 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include #include #include #include -#include -#include -#include #include #include @@ -68,19 +58,11 @@ enum intel_mid_timer_options intel_mid_timer_options; -static u32 sfi_mtimer_usage[SFI_MTMR_MAX_NUM]; -static struct sfi_timer_table_entry sfi_mtimer_array[SFI_MTMR_MAX_NUM]; enum intel_mid_cpu_type __intel_mid_cpu_chip; EXPORT_SYMBOL_GPL(__intel_mid_cpu_chip); -int sfi_mtimer_num; - -struct sfi_rtc_table_entry sfi_mrtc_array[SFI_MRTC_MAX]; -EXPORT_SYMBOL_GPL(sfi_mrtc_array); -int sfi_mrtc_num; static void __init ipc_device_handler(struct sfi_device_table_entry *pentry, struct devs_id *dev); - static void intel_mid_power_off(void) { } @@ -90,114 +72,6 @@ static void intel_mid_reboot(void) intel_scu_ipc_simple_command(IPCMSG_COLD_BOOT, 0); } -/* parse all the mtimer info to a static mtimer array */ -static int __init sfi_parse_mtmr(struct sfi_table_header *table) -{ - struct sfi_table_simple *sb; - struct sfi_timer_table_entry *pentry; - struct mpc_intsrc mp_irq; - int totallen; - - sb = (struct sfi_table_simple *)table; - if (!sfi_mtimer_num) { - sfi_mtimer_num = SFI_GET_NUM_ENTRIES(sb, - struct sfi_timer_table_entry); - pentry = (struct sfi_timer_table_entry *) sb->pentry; - totallen = sfi_mtimer_num * sizeof(*pentry); - memcpy(sfi_mtimer_array, pentry, totallen); - } - - pr_debug("SFI MTIMER info (num = %d):\n", sfi_mtimer_num); - pentry = sfi_mtimer_array; - for (totallen = 0; totallen < sfi_mtimer_num; totallen++, pentry++) { - pr_debug("timer[%d]: paddr = 0x%08x, freq = %dHz," - " irq = %d\n", totallen, (u32)pentry->phys_addr, - pentry->freq_hz, pentry->irq); - if (!pentry->irq) - continue; - mp_irq.type = MP_INTSRC; - mp_irq.irqtype = mp_INT; -/* triggering mode edge bit 2-3, active high polarity bit 0-1 */ - mp_irq.irqflag = 5; - mp_irq.srcbus = MP_BUS_ISA; - mp_irq.srcbusirq = pentry->irq; /* IRQ */ - mp_irq.dstapic = MP_APIC_ALL; - mp_irq.dstirq = pentry->irq; - mp_save_irq(&mp_irq); - } - - return 0; -} - -struct sfi_timer_table_entry *sfi_get_mtmr(int hint) -{ - int i; - if (hint < sfi_mtimer_num) { - if (!sfi_mtimer_usage[hint]) { - pr_debug("hint taken for timer %d irq %d\n", - hint, sfi_mtimer_array[hint].irq); - sfi_mtimer_usage[hint] = 1; - return &sfi_mtimer_array[hint]; - } - } - /* take the first timer available */ - for (i = 0; i < sfi_mtimer_num;) { - if (!sfi_mtimer_usage[i]) { - sfi_mtimer_usage[i] = 1; - return &sfi_mtimer_array[i]; - } - i++; - } - return NULL; -} - -void sfi_free_mtmr(struct sfi_timer_table_entry *mtmr) -{ - int i; - for (i = 0; i < sfi_mtimer_num;) { - if (mtmr->irq == sfi_mtimer_array[i].irq) { - sfi_mtimer_usage[i] = 0; - return; - } - i++; - } -} - -/* parse all the mrtc info to a global mrtc array */ -int __init sfi_parse_mrtc(struct sfi_table_header *table) -{ - struct sfi_table_simple *sb; - struct sfi_rtc_table_entry *pentry; - struct mpc_intsrc mp_irq; - - int totallen; - - sb = (struct sfi_table_simple *)table; - if (!sfi_mrtc_num) { - sfi_mrtc_num = SFI_GET_NUM_ENTRIES(sb, - struct sfi_rtc_table_entry); - pentry = (struct sfi_rtc_table_entry *)sb->pentry; - totallen = sfi_mrtc_num * sizeof(*pentry); - memcpy(sfi_mrtc_array, pentry, totallen); - } - - pr_debug("SFI RTC info (num = %d):\n", sfi_mrtc_num); - pentry = sfi_mrtc_array; - for (totallen = 0; totallen < sfi_mrtc_num; totallen++, pentry++) { - pr_debug("RTC[%d]: paddr = 0x%08x, irq = %d\n", - totallen, (u32)pentry->phys_addr, pentry->irq); - mp_irq.type = MP_INTSRC; - mp_irq.irqtype = mp_INT; - mp_irq.irqflag = 0xf; /* level trigger and active low */ - mp_irq.srcbus = MP_BUS_ISA; - mp_irq.srcbusirq = pentry->irq; /* IRQ */ - mp_irq.dstapic = MP_APIC_ALL; - mp_irq.dstirq = pentry->irq; - mp_save_irq(&mp_irq); - } - return 0; -} - static unsigned long __init intel_mid_calibrate_tsc(void) { unsigned long fast_calibrate; @@ -339,55 +213,6 @@ static inline int __init setup_x86_intel_mid_timer(char *arg) } __setup("x86_intel_mid_timer=", setup_x86_intel_mid_timer); -/* - * Parsing GPIO table first, since the DEVS table will need this table - * to map the pin name to the actual pin. - */ -static struct sfi_gpio_table_entry *gpio_table; -static int gpio_num_entry; - -static int __init sfi_parse_gpio(struct sfi_table_header *table) -{ - struct sfi_table_simple *sb; - struct sfi_gpio_table_entry *pentry; - int num, i; - - if (gpio_table) - return 0; - sb = (struct sfi_table_simple *)table; - num = SFI_GET_NUM_ENTRIES(sb, struct sfi_gpio_table_entry); - pentry = (struct sfi_gpio_table_entry *)sb->pentry; - - gpio_table = kmalloc(num * sizeof(*pentry), GFP_KERNEL); - if (!gpio_table) - return -1; - memcpy(gpio_table, pentry, num * sizeof(*pentry)); - gpio_num_entry = num; - - pr_debug("GPIO pin info:\n"); - for (i = 0; i < num; i++, pentry++) - pr_debug("info[%2d]: controller = %16.16s, pin_name = %16.16s," - " pin = %d\n", i, - pentry->controller_name, - pentry->pin_name, - pentry->pin_no); - return 0; -} - -static int get_gpio_by_name(const char *name) -{ - struct sfi_gpio_table_entry *pentry = gpio_table; - int i; - - if (!pentry) - return -1; - for (i = 0; i < gpio_num_entry; i++, pentry++) { - if (!strncmp(name, pentry->pin_name, SFI_NAME_LEN)) - return pentry->pin_no; - } - return -1; -} - /* the offset for the mapping of global gpio pin to irq */ #define INTEL_MID_IRQ_OFFSET 0x100 @@ -703,197 +528,6 @@ static const struct devs_id __initconst device_ids[] = { { 0 } }; -#define MAX_IPCDEVS 24 -static struct platform_device *ipc_devs[MAX_IPCDEVS]; -static int ipc_next_dev; - -#define MAX_SCU_SPI 24 -static struct spi_board_info *spi_devs[MAX_SCU_SPI]; -static int spi_next_dev; - -#define MAX_SCU_I2C 24 -static struct i2c_board_info *i2c_devs[MAX_SCU_I2C]; -static int i2c_bus[MAX_SCU_I2C]; -static int i2c_next_dev; - -static void __init intel_scu_device_register(struct platform_device *pdev) -{ - if (ipc_next_dev == MAX_IPCDEVS) - pr_err("too many SCU IPC devices"); - else - ipc_devs[ipc_next_dev++] = pdev; -} - -static void __init intel_scu_spi_device_register(struct spi_board_info *sdev) -{ - struct spi_board_info *new_dev; - - if (spi_next_dev == MAX_SCU_SPI) { - pr_err("too many SCU SPI devices"); - return; - } - - new_dev = kzalloc(sizeof(*sdev), GFP_KERNEL); - if (!new_dev) { - pr_err("failed to alloc mem for delayed spi dev %s\n", - sdev->modalias); - return; - } - memcpy(new_dev, sdev, sizeof(*sdev)); - - spi_devs[spi_next_dev++] = new_dev; -} - -static void __init intel_scu_i2c_device_register(int bus, - struct i2c_board_info *idev) -{ - struct i2c_board_info *new_dev; - - if (i2c_next_dev == MAX_SCU_I2C) { - pr_err("too many SCU I2C devices"); - return; - } - - new_dev = kzalloc(sizeof(*idev), GFP_KERNEL); - if (!new_dev) { - pr_err("failed to alloc mem for delayed i2c dev %s\n", - idev->type); - return; - } - memcpy(new_dev, idev, sizeof(*idev)); - - i2c_bus[i2c_next_dev] = bus; - i2c_devs[i2c_next_dev++] = new_dev; -} - -BLOCKING_NOTIFIER_HEAD(intel_scu_notifier); -EXPORT_SYMBOL_GPL(intel_scu_notifier); - -/* Called by IPC driver */ -void intel_scu_devices_create(void) -{ - int i; - - for (i = 0; i < ipc_next_dev; i++) - platform_device_add(ipc_devs[i]); - - for (i = 0; i < spi_next_dev; i++) - spi_register_board_info(spi_devs[i], 1); - - for (i = 0; i < i2c_next_dev; i++) { - struct i2c_adapter *adapter; - struct i2c_client *client; - - adapter = i2c_get_adapter(i2c_bus[i]); - if (adapter) { - client = i2c_new_device(adapter, i2c_devs[i]); - if (!client) - pr_err("can't create i2c device %s\n", - i2c_devs[i]->type); - } else - i2c_register_board_info(i2c_bus[i], i2c_devs[i], 1); - } - intel_scu_notifier_post(SCU_AVAILABLE, NULL); -} -EXPORT_SYMBOL_GPL(intel_scu_devices_create); - -/* Called by IPC driver */ -void intel_scu_devices_destroy(void) -{ - int i; - - intel_scu_notifier_post(SCU_DOWN, NULL); - - for (i = 0; i < ipc_next_dev; i++) - platform_device_del(ipc_devs[i]); -} -EXPORT_SYMBOL_GPL(intel_scu_devices_destroy); - -static void __init install_irq_resource(struct platform_device *pdev, int irq) -{ - /* Single threaded */ - static struct resource __initdata res = { - .name = "IRQ", - .flags = IORESOURCE_IRQ, - }; - res.start = irq; - platform_device_add_resources(pdev, &res, 1); -} - -static void __init sfi_handle_ipc_dev(struct sfi_device_table_entry *pentry, - struct devs_id *dev) -{ - struct platform_device *pdev; - void *pdata = NULL; - - pr_debug("IPC bus, name = %16.16s, irq = 0x%2x\n", - pentry->name, pentry->irq); - pdata = dev->get_platform_data(pentry); - - pdev = platform_device_alloc(pentry->name, 0); - if (pdev == NULL) { - pr_err("out of memory for SFI platform device '%s'.\n", - pentry->name); - return; - } - install_irq_resource(pdev, pentry->irq); - - pdev->dev.platform_data = pdata; - platform_device_add(pdev); -} - -static void __init sfi_handle_spi_dev(struct sfi_device_table_entry *pentry, - struct devs_id *dev) -{ - struct spi_board_info spi_info; - void *pdata = NULL; - - memset(&spi_info, 0, sizeof(spi_info)); - strncpy(spi_info.modalias, pentry->name, SFI_NAME_LEN); - spi_info.irq = ((pentry->irq == (u8)0xff) ? 0 : pentry->irq); - spi_info.bus_num = pentry->host_num; - spi_info.chip_select = pentry->addr; - spi_info.max_speed_hz = pentry->max_freq; - pr_debug("SPI bus=%d, name=%16.16s, irq=0x%2x, max_freq=%d, cs=%d\n", - spi_info.bus_num, - spi_info.modalias, - spi_info.irq, - spi_info.max_speed_hz, - spi_info.chip_select); - - pdata = dev->get_platform_data(&spi_info); - - spi_info.platform_data = pdata; - if (dev->delay) - intel_scu_spi_device_register(&spi_info); - else - spi_register_board_info(&spi_info, 1); -} - -static void __init sfi_handle_i2c_dev(struct sfi_device_table_entry *pentry, - struct devs_id *dev) -{ - struct i2c_board_info i2c_info; - void *pdata = NULL; - - memset(&i2c_info, 0, sizeof(i2c_info)); - strncpy(i2c_info.type, pentry->name, SFI_NAME_LEN); - i2c_info.irq = ((pentry->irq == (u8)0xff) ? 0 : pentry->irq); - i2c_info.addr = pentry->addr; - pr_debug("I2C bus = %d, name = %16.16s, irq = 0x%2x, addr = 0x%x\n", - pentry->host_num, - i2c_info.type, - i2c_info.irq, - i2c_info.addr); - pdata = dev->get_platform_data(&i2c_info); - i2c_info.platform_data = pdata; - - if (dev->delay) - intel_scu_i2c_device_register(pentry->host_num, &i2c_info); - else - i2c_register_board_info(pentry->host_num, &i2c_info, 1); -} - static void __init ipc_device_handler(struct sfi_device_table_entry *pentry, struct devs_id *dev) { @@ -934,89 +568,6 @@ static void __init ipc_device_handler(struct sfi_device_table_entry *pentry, intel_scu_device_register(pdev); } -static struct devs_id __init *get_device_id(u8 type, char *name) -{ - struct devs_id *dev = device_ids; - - if (device_ids == NULL) - return NULL; - - while (dev->name[0]) { - if (dev->type == type && - !strncmp(dev->name, name, SFI_NAME_LEN)) { - return dev; - } - dev++; - } - - return NULL; -} - -static int __init sfi_parse_devs(struct sfi_table_header *table) -{ - struct sfi_table_simple *sb; - struct sfi_device_table_entry *pentry; - struct devs_id *dev = NULL; - int num, i; - int ioapic; - struct io_apic_irq_attr irq_attr; - - sb = (struct sfi_table_simple *)table; - num = SFI_GET_NUM_ENTRIES(sb, struct sfi_device_table_entry); - pentry = (struct sfi_device_table_entry *)sb->pentry; - - for (i = 0; i < num; i++, pentry++) { - int irq = pentry->irq; - - if (irq != (u8)0xff) { /* native RTE case */ - /* these SPI2 devices are not exposed to system as PCI - * devices, but they have separate RTE entry in IOAPIC - * so we have to enable them one by one here - */ - ioapic = mp_find_ioapic(irq); - irq_attr.ioapic = ioapic; - irq_attr.ioapic_pin = irq; - irq_attr.trigger = 1; - irq_attr.polarity = 1; - io_apic_set_pci_routing(NULL, irq, &irq_attr); - } else - irq = 0; /* No irq */ - - dev = get_device_id(pentry->type, pentry->name); - - if ((dev == NULL) || (dev->get_platform_data == NULL)) - continue; - - if (dev->device_handler) { - dev->device_handler(pentry, dev); - } else { - switch (pentry->type) { - case SFI_DEV_TYPE_IPC: - sfi_handle_ipc_dev(pentry, dev); - break; - case SFI_DEV_TYPE_SPI: - sfi_handle_spi_dev(pentry, dev); - break; - case SFI_DEV_TYPE_I2C: - sfi_handle_i2c_dev(pentry, dev); - break; - case SFI_DEV_TYPE_UART: - case SFI_DEV_TYPE_HSI: - default: - break; - } - } - } - return 0; -} - -static int __init intel_mid_platform_init(void) -{ - sfi_table_parse(SFI_SIG_GPIO, NULL, NULL, sfi_parse_gpio); - sfi_table_parse(SFI_SIG_DEVS, NULL, NULL, sfi_parse_devs); - return 0; -} -arch_initcall(intel_mid_platform_init); /* * we will search these buttons in SFI GPIO table (by name) @@ -1078,4 +629,4 @@ static int __init pb_keys_init(void) } return 0; } -late_initcall(pb_keys_init); +late_initcall(pb_keys_init); \ No newline at end of file diff --git a/arch/x86/platform/intel-mid/sfi.c b/arch/x86/platform/intel-mid/sfi.c new file mode 100644 index 0000000..2f8196d --- /dev/null +++ b/arch/x86/platform/intel-mid/sfi.c @@ -0,0 +1,485 @@ +/* + * intel_mid_sfi.c: Intel MID SFI initialization code + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "intel_mid_weak_decls.h" + +#define SFI_SIG_OEM0 "OEM0" +#define MAX_IPCDEVS 24 +#define MAX_SCU_SPI 24 +#define MAX_SCU_I2C 24 + +static struct platform_device *ipc_devs[MAX_IPCDEVS]; +static struct spi_board_info *spi_devs[MAX_SCU_SPI]; +static struct i2c_board_info *i2c_devs[MAX_SCU_I2C]; +static struct sfi_gpio_table_entry *gpio_table; +static struct sfi_timer_table_entry sfi_mtimer_array[SFI_MTMR_MAX_NUM]; +static int ipc_next_dev; +static int spi_next_dev; +static int i2c_next_dev; +static int i2c_bus[MAX_SCU_I2C]; +static int gpio_num_entry; +static u32 sfi_mtimer_usage[SFI_MTMR_MAX_NUM]; +int sfi_mrtc_num; +int sfi_mtimer_num; + +struct sfi_rtc_table_entry sfi_mrtc_array[SFI_MRTC_MAX]; +EXPORT_SYMBOL_GPL(sfi_mrtc_array); + +struct blocking_notifier_head intel_scu_notifier = + BLOCKING_NOTIFIER_INIT(intel_scu_notifier); +EXPORT_SYMBOL_GPL(intel_scu_notifier); + +/* parse all the mtimer info to a static mtimer array */ +int __init sfi_parse_mtmr(struct sfi_table_header *table) +{ + struct sfi_table_simple *sb; + struct sfi_timer_table_entry *pentry; + struct mpc_intsrc mp_irq; + int totallen; + + sb = (struct sfi_table_simple *)table; + if (!sfi_mtimer_num) { + sfi_mtimer_num = SFI_GET_NUM_ENTRIES(sb, + struct sfi_timer_table_entry); + pentry = (struct sfi_timer_table_entry *) sb->pentry; + totallen = sfi_mtimer_num * sizeof(*pentry); + memcpy(sfi_mtimer_array, pentry, totallen); + } + + pr_debug("SFI MTIMER info (num = %d):\n", sfi_mtimer_num); + pentry = sfi_mtimer_array; + for (totallen = 0; totallen < sfi_mtimer_num; totallen++, pentry++) { + pr_debug("timer[%d]: paddr = 0x%08x, freq = %dHz, irq = %d\n", + totallen, (u32)pentry->phys_addr, + pentry->freq_hz, pentry->irq); + if (!pentry->irq) + continue; + mp_irq.type = MP_INTSRC; + mp_irq.irqtype = mp_INT; +/* triggering mode edge bit 2-3, active high polarity bit 0-1 */ + mp_irq.irqflag = 5; + mp_irq.srcbus = MP_BUS_ISA; + mp_irq.srcbusirq = pentry->irq; /* IRQ */ + mp_irq.dstapic = MP_APIC_ALL; + mp_irq.dstirq = pentry->irq; + mp_save_irq(&mp_irq); + } + + return 0; +} + +struct sfi_timer_table_entry *sfi_get_mtmr(int hint) +{ + int i; + if (hint < sfi_mtimer_num) { + if (!sfi_mtimer_usage[hint]) { + pr_debug("hint taken for timer %d irq %d\n", + hint, sfi_mtimer_array[hint].irq); + sfi_mtimer_usage[hint] = 1; + return &sfi_mtimer_array[hint]; + } + } + /* take the first timer available */ + for (i = 0; i < sfi_mtimer_num;) { + if (!sfi_mtimer_usage[i]) { + sfi_mtimer_usage[i] = 1; + return &sfi_mtimer_array[i]; + } + i++; + } + return NULL; +} + +void sfi_free_mtmr(struct sfi_timer_table_entry *mtmr) +{ + int i; + for (i = 0; i < sfi_mtimer_num;) { + if (mtmr->irq == sfi_mtimer_array[i].irq) { + sfi_mtimer_usage[i] = 0; + return; + } + i++; + } +} + +/* parse all the mrtc info to a global mrtc array */ +int __init sfi_parse_mrtc(struct sfi_table_header *table) +{ + struct sfi_table_simple *sb; + struct sfi_rtc_table_entry *pentry; + struct mpc_intsrc mp_irq; + + int totallen; + + sb = (struct sfi_table_simple *)table; + if (!sfi_mrtc_num) { + sfi_mrtc_num = SFI_GET_NUM_ENTRIES(sb, + struct sfi_rtc_table_entry); + pentry = (struct sfi_rtc_table_entry *)sb->pentry; + totallen = sfi_mrtc_num * sizeof(*pentry); + memcpy(sfi_mrtc_array, pentry, totallen); + } + + pr_debug("SFI RTC info (num = %d):\n", sfi_mrtc_num); + pentry = sfi_mrtc_array; + for (totallen = 0; totallen < sfi_mrtc_num; totallen++, pentry++) { + pr_debug("RTC[%d]: paddr = 0x%08x, irq = %d\n", + totallen, (u32)pentry->phys_addr, pentry->irq); + mp_irq.type = MP_INTSRC; + mp_irq.irqtype = mp_INT; + mp_irq.irqflag = 0xf; /* level trigger and active low */ + mp_irq.srcbus = MP_BUS_ISA; + mp_irq.srcbusirq = pentry->irq; /* IRQ */ + mp_irq.dstapic = MP_APIC_ALL; + mp_irq.dstirq = pentry->irq; + mp_save_irq(&mp_irq); + } + return 0; +} + + +/* + * Parsing GPIO table first, since the DEVS table will need this table + * to map the pin name to the actual pin. + */ +static int __init sfi_parse_gpio(struct sfi_table_header *table) +{ + struct sfi_table_simple *sb; + struct sfi_gpio_table_entry *pentry; + int num, i; + + if (gpio_table) + return 0; + sb = (struct sfi_table_simple *)table; + num = SFI_GET_NUM_ENTRIES(sb, struct sfi_gpio_table_entry); + pentry = (struct sfi_gpio_table_entry *)sb->pentry; + + gpio_table = kmalloc(num * sizeof(*pentry), GFP_KERNEL); + if (!gpio_table) + return -1; + memcpy(gpio_table, pentry, num * sizeof(*pentry)); + gpio_num_entry = num; + + pr_debug("GPIO pin info:\n"); + for (i = 0; i < num; i++, pentry++) + pr_debug("info[%2d]: controller = %16.16s, pin_name = %16.16s," + " pin = %d\n", i, + pentry->controller_name, + pentry->pin_name, + pentry->pin_no); + return 0; +} + +int get_gpio_by_name(const char *name) +{ + struct sfi_gpio_table_entry *pentry = gpio_table; + int i; + + if (!pentry) + return -1; + for (i = 0; i < gpio_num_entry; i++, pentry++) { + if (!strncmp(name, pentry->pin_name, SFI_NAME_LEN)) + return pentry->pin_no; + } + return -1; +} + +void __init intel_scu_device_register(struct platform_device *pdev) +{ + if (ipc_next_dev == MAX_IPCDEVS) + pr_err("too many SCU IPC devices"); + else + ipc_devs[ipc_next_dev++] = pdev; +} + +static void __init intel_scu_spi_device_register(struct spi_board_info *sdev) +{ + struct spi_board_info *new_dev; + + if (spi_next_dev == MAX_SCU_SPI) { + pr_err("too many SCU SPI devices"); + return; + } + + new_dev = kzalloc(sizeof(*sdev), GFP_KERNEL); + if (!new_dev) { + pr_err("failed to alloc mem for delayed spi dev %s\n", + sdev->modalias); + return; + } + memcpy(new_dev, sdev, sizeof(*sdev)); + + spi_devs[spi_next_dev++] = new_dev; +} + +static void __init intel_scu_i2c_device_register(int bus, + struct i2c_board_info *idev) +{ + struct i2c_board_info *new_dev; + + if (i2c_next_dev == MAX_SCU_I2C) { + pr_err("too many SCU I2C devices"); + return; + } + + new_dev = kzalloc(sizeof(*idev), GFP_KERNEL); + if (!new_dev) { + pr_err("failed to alloc mem for delayed i2c dev %s\n", + idev->type); + return; + } + memcpy(new_dev, idev, sizeof(*idev)); + + i2c_bus[i2c_next_dev] = bus; + i2c_devs[i2c_next_dev++] = new_dev; +} + +/* Called by IPC driver */ +void intel_scu_devices_create(void) +{ + int i; + + for (i = 0; i < ipc_next_dev; i++) + platform_device_add(ipc_devs[i]); + + for (i = 0; i < spi_next_dev; i++) + spi_register_board_info(spi_devs[i], 1); + + for (i = 0; i < i2c_next_dev; i++) { + struct i2c_adapter *adapter; + struct i2c_client *client; + + adapter = i2c_get_adapter(i2c_bus[i]); + if (adapter) { + client = i2c_new_device(adapter, i2c_devs[i]); + if (!client) + pr_err("can't create i2c device %s\n", + i2c_devs[i]->type); + } else + i2c_register_board_info(i2c_bus[i], i2c_devs[i], 1); + } + intel_scu_notifier_post(SCU_AVAILABLE, NULL); +} +EXPORT_SYMBOL_GPL(intel_scu_devices_create); + +/* Called by IPC driver */ +void intel_scu_devices_destroy(void) +{ + int i; + + intel_scu_notifier_post(SCU_DOWN, NULL); + + for (i = 0; i < ipc_next_dev; i++) + platform_device_del(ipc_devs[i]); +} +EXPORT_SYMBOL_GPL(intel_scu_devices_destroy); + +static void __init install_irq_resource(struct platform_device *pdev, int irq) +{ + /* Single threaded */ + static struct resource res __initdata = { + .name = "IRQ", + .flags = IORESOURCE_IRQ, + }; + res.start = irq; + platform_device_add_resources(pdev, &res, 1); +} + +static void __init sfi_handle_ipc_dev(struct sfi_device_table_entry *pentry, + struct devs_id *dev) +{ + struct platform_device *pdev; + void *pdata = NULL; + + pr_debug("IPC bus, name = %16.16s, irq = 0x%2x\n", + pentry->name, pentry->irq); + pdata = dev->get_platform_data(pentry); + + pdev = platform_device_alloc(pentry->name, 0); + if (pdev == NULL) { + pr_err("out of memory for SFI platform device '%s'.\n", + pentry->name); + return; + } + install_irq_resource(pdev, pentry->irq); + + pdev->dev.platform_data = pdata; + platform_device_add(pdev); +} + +static void __init sfi_handle_spi_dev(struct sfi_device_table_entry *pentry, + struct devs_id *dev) +{ + struct spi_board_info spi_info; + void *pdata = NULL; + + memset(&spi_info, 0, sizeof(spi_info)); + strncpy(spi_info.modalias, pentry->name, SFI_NAME_LEN); + spi_info.irq = ((pentry->irq == (u8)0xff) ? 0 : pentry->irq); + spi_info.bus_num = pentry->host_num; + spi_info.chip_select = pentry->addr; + spi_info.max_speed_hz = pentry->max_freq; + pr_debug("SPI bus=%d, name=%16.16s, irq=0x%2x, max_freq=%d, cs=%d\n", + spi_info.bus_num, + spi_info.modalias, + spi_info.irq, + spi_info.max_speed_hz, + spi_info.chip_select); + + pdata = dev->get_platform_data(&spi_info); + + spi_info.platform_data = pdata; + if (dev->delay) + intel_scu_spi_device_register(&spi_info); + else + spi_register_board_info(&spi_info, 1); +} + +static void __init sfi_handle_i2c_dev(struct sfi_device_table_entry *pentry, + struct devs_id *dev) +{ + struct i2c_board_info i2c_info; + void *pdata = NULL; + + memset(&i2c_info, 0, sizeof(i2c_info)); + strncpy(i2c_info.type, pentry->name, SFI_NAME_LEN); + i2c_info.irq = ((pentry->irq == (u8)0xff) ? 0 : pentry->irq); + i2c_info.addr = pentry->addr; + pr_debug("I2C bus = %d, name = %16.16s, irq = 0x%2x, addr = 0x%x\n", + pentry->host_num, + i2c_info.type, + i2c_info.irq, + i2c_info.addr); + pdata = dev->get_platform_data(&i2c_info); + i2c_info.platform_data = pdata; + + if (dev->delay) + intel_scu_i2c_device_register(pentry->host_num, &i2c_info); + else + i2c_register_board_info(pentry->host_num, &i2c_info, 1); +} + +static struct devs_id __init *get_device_id(u8 type, char *name) +{ + struct devs_id *dev = device_ids; + + if (device_ids == NULL) + return NULL; + + while (dev->name[0]) { + if (dev->type == type && + !strncmp(dev->name, name, SFI_NAME_LEN)) { + return dev; + } + dev++; + } + + return NULL; +} + +static int __init sfi_parse_devs(struct sfi_table_header *table) +{ + struct sfi_table_simple *sb; + struct sfi_device_table_entry *pentry; + struct devs_id *dev = NULL; + int num, i; + int ioapic; + struct io_apic_irq_attr irq_attr; + + sb = (struct sfi_table_simple *)table; + num = SFI_GET_NUM_ENTRIES(sb, struct sfi_device_table_entry); + pentry = (struct sfi_device_table_entry *)sb->pentry; + + for (i = 0; i < num; i++, pentry++) { + int irq = pentry->irq; + + if (irq != (u8)0xff) { /* native RTE case */ + /* these SPI2 devices are not exposed to system as PCI + * devices, but they have separate RTE entry in IOAPIC + * so we have to enable them one by one here + */ + ioapic = mp_find_ioapic(irq); + irq_attr.ioapic = ioapic; + irq_attr.ioapic_pin = irq; + irq_attr.trigger = 1; + irq_attr.polarity = 1; + io_apic_set_pci_routing(NULL, irq, &irq_attr); + } else + irq = 0; /* No irq */ + + dev = get_device_id(pentry->type, pentry->name); + + if ((dev == NULL) || (dev->get_platform_data == NULL)) + continue; + + if (dev->device_handler) { + dev->device_handler(pentry, dev); + } else { + switch (pentry->type) { + case SFI_DEV_TYPE_IPC: + sfi_handle_ipc_dev(pentry, dev); + break; + case SFI_DEV_TYPE_SPI: + sfi_handle_spi_dev(pentry, dev); + break; + case SFI_DEV_TYPE_I2C: + sfi_handle_i2c_dev(pentry, dev); + break; + case SFI_DEV_TYPE_UART: + case SFI_DEV_TYPE_HSI: + default: + break; + } + } + } + return 0; +} + +static int __init intel_mid_platform_init(void) +{ + sfi_table_parse(SFI_SIG_GPIO, NULL, NULL, sfi_parse_gpio); + sfi_table_parse(SFI_SIG_DEVS, NULL, NULL, sfi_parse_devs); + return 0; +} +arch_initcall(intel_mid_platform_init); -- cgit v0.10.2 From 0e6fdb5f036338bc38bf660c65c931b3e92a31d7 Mon Sep 17 00:00:00 2001 From: David Cohen Date: Thu, 17 Oct 2013 15:35:34 -0700 Subject: intel-mid: sfi: Allow struct devs_id.get_platform_data to be NULL Intel mid sfi code doesn't need struct devs_id.get_platform_data != NULL. If the callback is not set, just assume there is no platform_data. Signed-off-by: David Cohen Link: http://lkml.kernel.org/r/1382049336-21316-11-git-send-email-david.a.cohen@linux.intel.com Cc: Kuppuswamy Sathyanarayanan Signed-off-by: H. Peter Anvin diff --git a/arch/x86/platform/intel-mid/sfi.c b/arch/x86/platform/intel-mid/sfi.c index 2f8196d..3f1c171 100644 --- a/arch/x86/platform/intel-mid/sfi.c +++ b/arch/x86/platform/intel-mid/sfi.c @@ -70,6 +70,9 @@ struct blocking_notifier_head intel_scu_notifier = BLOCKING_NOTIFIER_INIT(intel_scu_notifier); EXPORT_SYMBOL_GPL(intel_scu_notifier); +#define intel_mid_sfi_get_pdata(dev, priv) \ + ((dev)->get_platform_data ? (dev)->get_platform_data(priv) : NULL) + /* parse all the mtimer info to a static mtimer array */ int __init sfi_parse_mtmr(struct sfi_table_header *table) { @@ -334,7 +337,7 @@ static void __init sfi_handle_ipc_dev(struct sfi_device_table_entry *pentry, pr_debug("IPC bus, name = %16.16s, irq = 0x%2x\n", pentry->name, pentry->irq); - pdata = dev->get_platform_data(pentry); + pdata = intel_mid_sfi_get_pdata(dev, pentry); pdev = platform_device_alloc(pentry->name, 0); if (pdev == NULL) { @@ -367,7 +370,7 @@ static void __init sfi_handle_spi_dev(struct sfi_device_table_entry *pentry, spi_info.max_speed_hz, spi_info.chip_select); - pdata = dev->get_platform_data(&spi_info); + pdata = intel_mid_sfi_get_pdata(dev, &spi_info); spi_info.platform_data = pdata; if (dev->delay) @@ -391,7 +394,7 @@ static void __init sfi_handle_i2c_dev(struct sfi_device_table_entry *pentry, i2c_info.type, i2c_info.irq, i2c_info.addr); - pdata = dev->get_platform_data(&i2c_info); + pdata = intel_mid_sfi_get_pdata(dev, &i2c_info); i2c_info.platform_data = pdata; if (dev->delay) @@ -450,7 +453,7 @@ static int __init sfi_parse_devs(struct sfi_table_header *table) dev = get_device_id(pentry->type, pentry->name); - if ((dev == NULL) || (dev->get_platform_data == NULL)) + if (!dev) continue; if (dev->device_handler) { -- cgit v0.10.2 From 66ac50137049b3d3fab39e5ae245e1562aee5acd Mon Sep 17 00:00:00 2001 From: David Cohen Date: Thu, 17 Oct 2013 15:35:35 -0700 Subject: x86: intel-mid: Add section for sfi device table When Intel mid uses SFI table to enumerate devices, it requires an extra device table with further information about how to probe such devices. This patch creates a section where the device table will stay if CONFIG_X86_INTEL_MID is selected. Signed-off-by: David Cohen Link: http://lkml.kernel.org/r/1382049336-21316-12-git-send-email-david.a.cohen@linux.intel.com Signed-off-by: H. Peter Anvin diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S index 10c4f30..da6b35a 100644 --- a/arch/x86/kernel/vmlinux.lds.S +++ b/arch/x86/kernel/vmlinux.lds.S @@ -199,6 +199,15 @@ SECTIONS __x86_cpu_dev_end = .; } +#ifdef CONFIG_X86_INTEL_MID + .x86_intel_mid_dev.init : AT(ADDR(.x86_intel_mid_dev.init) - \ + LOAD_OFFSET) { + __x86_intel_mid_dev_start = .; + *(.x86_intel_mid_dev.init) + __x86_intel_mid_dev_end = .; + } +#endif + /* * start address and size of operations which during runtime * can be patched with virtualization friendly instructions or -- cgit v0.10.2 From 40a96d54ee2232045783e657eb9224cd723dcb40 Mon Sep 17 00:00:00 2001 From: David Cohen Date: Thu, 17 Oct 2013 15:35:36 -0700 Subject: intel_mid: Move platform device setups to their own platform_.* files As Intel rolling out more SoC's after Moorestown, we need to re-structure the code in a way that is backward compatible and easy to expand. This patch implements a flexible way to support multiple boards and devices. This patch does not add any new functional support. It just refactors the existing code to increase the modularity and decrease the code duplication for supporting multiple soc's and boards. Currently intel-mid.c has both board and soc related code in one file. This patch moves the board related code to new files and let linker script to create SFI devite table following this: 1. Move the SFI device specific code to arch/x86/platform/intel-mid/device-libs/platform_.* A new device file is added for every supported device. This code will get conditionally compiled by using corresponding device driver CONFIG option. 2. Move the device_ids location to .x86_intel_mid_dev.init section by using new sfi_device() macro. This patch was based on previous code from Sathyanarayanan Kuppuswamy. Signed-off-by: Kuppuswamy Sathyanarayanan Link: http://lkml.kernel.org/r/1382049336-21316-13-git-send-email-david.a.cohen@linux.intel.com Signed-off-by: David Cohen Signed-off-by: H. Peter Anvin diff --git a/arch/x86/include/asm/intel-mid.h b/arch/x86/include/asm/intel-mid.h index 3b0e7a7..459769d 100644 --- a/arch/x86/include/asm/intel-mid.h +++ b/arch/x86/include/asm/intel-mid.h @@ -12,8 +12,11 @@ #define _ASM_X86_INTEL_MID_H #include +#include extern int intel_mid_pci_init(void); +extern int get_gpio_by_name(const char *name); +extern void intel_scu_device_register(struct platform_device *pdev); extern int __init sfi_parse_mrtc(struct sfi_table_header *table); extern int __init sfi_parse_mtmr(struct sfi_table_header *table); extern int sfi_mrtc_num; @@ -34,6 +37,10 @@ struct devs_id { struct devs_id *dev); }; +#define sfi_device(i) \ + static const struct devs_id *const __intel_mid_sfi_##i##_dev __used \ + __attribute__((__section__(".x86_intel_mid_dev.init"))) = &i + /* * Medfield is the follow-up of Moorestown, it combines two chip solution into * one. Other than that it also added always-on and constant tsc and lapic @@ -55,9 +62,15 @@ static inline enum intel_mid_cpu_type intel_mid_identify_cpu(void) return __intel_mid_cpu_chip; } +static inline bool intel_mid_has_msic(void) +{ + return (intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_PENWELL); +} + #else /* !CONFIG_X86_INTEL_MID */ #define intel_mid_identify_cpu() (0) +#define intel_mid_has_msic() (0) #endif /* !CONFIG_X86_INTEL_MID */ @@ -94,4 +107,7 @@ extern void intel_scu_devices_destroy(void); extern void intel_mid_rtc_init(void); +/* the offset for the mapping of global gpio pin to irq */ +#define INTEL_MID_IRQ_OFFSET 0x100 + #endif /* _ASM_X86_INTEL_MID_H */ diff --git a/arch/x86/platform/intel-mid/Makefile b/arch/x86/platform/intel-mid/Makefile index b11e5b2..01cc29e 100644 --- a/arch/x86/platform/intel-mid/Makefile +++ b/arch/x86/platform/intel-mid/Makefile @@ -1,5 +1,7 @@ obj-$(CONFIG_X86_INTEL_MID) += intel-mid.o -obj-$(CONFIG_X86_INTEL_MID) += intel_mid_vrtc.o -obj-$(CONFIG_EARLY_PRINTK_INTEL_MID) += early_printk_intel_mid.o +obj-$(CONFIG_X86_INTEL_MID) += intel_mid_vrtc.o +obj-$(CONFIG_EARLY_PRINTK_INTEL_MID) += early_printk_intel_mid.o # SFI specific code -obj-$(CONFIG_SFI) += sfi.o +ifdef CONFIG_X86_INTEL_MID +obj-$(CONFIG_SFI) += sfi.o device_libs/ +endif diff --git a/arch/x86/platform/intel-mid/device_libs/Makefile b/arch/x86/platform/intel-mid/device_libs/Makefile new file mode 100644 index 0000000..097e7a7 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/Makefile @@ -0,0 +1,22 @@ +# IPC Devices +obj-y += platform_ipc.o +obj-$(subst m,y,$(CONFIG_MFD_INTEL_MSIC)) += platform_msic.o +obj-$(subst m,y,$(CONFIG_SND_MFLD_MACHINE)) += platform_msic_audio.o +obj-$(subst m,y,$(CONFIG_GPIO_MSIC)) += platform_msic_gpio.o +obj-$(subst m,y,$(CONFIG_MFD_INTEL_MSIC)) += platform_msic_ocd.o +obj-$(subst m,y,$(CONFIG_MFD_INTEL_MSIC)) += platform_msic_battery.o +obj-$(subst m,y,$(CONFIG_INTEL_MID_POWER_BUTTON)) += platform_msic_power_btn.o +obj-$(subst m,y,$(CONFIG_GPIO_INTEL_PMIC)) += platform_pmic_gpio.o +obj-$(subst m,y,$(CONFIG_INTEL_MFLD_THERMAL)) += platform_msic_thermal.o +# I2C Devices +obj-$(subst m,y,$(CONFIG_SENSORS_EMC1403)) += platform_emc1403.o +obj-$(subst m,y,$(CONFIG_SENSORS_LIS3LV02D)) += platform_lis331.o +obj-$(subst m,y,$(CONFIG_GPIO_PCA953X)) += platform_max7315.o +obj-$(subst m,y,$(CONFIG_INPUT_MPU3050)) += platform_mpu3050.o +obj-$(subst m,y,$(CONFIG_INPUT_BMA150)) += platform_bma023.o +obj-$(subst m,y,$(CONFIG_GPIO_PCA953X)) += platform_tca6416.o +obj-$(subst m,y,$(CONFIG_DRM_MEDFIELD)) += platform_tc35876x.o +# SPI Devices +obj-$(subst m,y,$(CONFIG_SERIAL_MRST_MAX3110)) += platform_max3111.o +# MISC Devices +obj-$(subst m,y,$(CONFIG_KEYBOARD_GPIO)) += platform_gpio_keys.o diff --git a/arch/x86/platform/intel-mid/device_libs/platform_bma023.c b/arch/x86/platform/intel-mid/device_libs/platform_bma023.c new file mode 100644 index 0000000..0ae7f2a --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_bma023.c @@ -0,0 +1,20 @@ +/* + * platform_bma023.c: bma023 platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include + +static const struct devs_id bma023_dev_id __initconst = { + .name = "bma023", + .type = SFI_DEV_TYPE_I2C, + .delay = 1, +}; + +sfi_device(bma023_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_emc1403.c b/arch/x86/platform/intel-mid/device_libs/platform_emc1403.c new file mode 100644 index 0000000..0d942c1 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_emc1403.c @@ -0,0 +1,41 @@ +/* + * platform_emc1403.c: emc1403 platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include +#include +#include +#include + +static void __init *emc1403_platform_data(void *info) +{ + static short intr2nd_pdata; + struct i2c_board_info *i2c_info = info; + int intr = get_gpio_by_name("thermal_int"); + int intr2nd = get_gpio_by_name("thermal_alert"); + + if (intr == -1 || intr2nd == -1) + return NULL; + + i2c_info->irq = intr + INTEL_MID_IRQ_OFFSET; + intr2nd_pdata = intr2nd + INTEL_MID_IRQ_OFFSET; + + return &intr2nd_pdata; +} + +static const struct devs_id emc1403_dev_id __initconst = { + .name = "emc1403", + .type = SFI_DEV_TYPE_I2C, + .delay = 1, + .get_platform_data = &emc1403_platform_data, +}; + +sfi_device(emc1403_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_gpio_keys.c b/arch/x86/platform/intel-mid/device_libs/platform_gpio_keys.c new file mode 100644 index 0000000..a013a48 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_gpio_keys.c @@ -0,0 +1,83 @@ +/* + * platform_gpio_keys.c: gpio_keys platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DEVICE_NAME "gpio-keys" + +/* + * we will search these buttons in SFI GPIO table (by name) + * and register them dynamically. Please add all possible + * buttons here, we will shrink them if no GPIO found. + */ +static struct gpio_keys_button gpio_button[] = { + {KEY_POWER, -1, 1, "power_btn", EV_KEY, 0, 3000}, + {KEY_PROG1, -1, 1, "prog_btn1", EV_KEY, 0, 20}, + {KEY_PROG2, -1, 1, "prog_btn2", EV_KEY, 0, 20}, + {SW_LID, -1, 1, "lid_switch", EV_SW, 0, 20}, + {KEY_VOLUMEUP, -1, 1, "vol_up", EV_KEY, 0, 20}, + {KEY_VOLUMEDOWN, -1, 1, "vol_down", EV_KEY, 0, 20}, + {KEY_CAMERA, -1, 1, "camera_full", EV_KEY, 0, 20}, + {KEY_CAMERA_FOCUS, -1, 1, "camera_half", EV_KEY, 0, 20}, + {SW_KEYPAD_SLIDE, -1, 1, "MagSw1", EV_SW, 0, 20}, + {SW_KEYPAD_SLIDE, -1, 1, "MagSw2", EV_SW, 0, 20}, +}; + +static struct gpio_keys_platform_data gpio_keys = { + .buttons = gpio_button, + .rep = 1, + .nbuttons = -1, /* will fill it after search */ +}; + +static struct platform_device pb_device = { + .name = DEVICE_NAME, + .id = -1, + .dev = { + .platform_data = &gpio_keys, + }, +}; + +/* + * Shrink the non-existent buttons, register the gpio button + * device if there is some + */ +static int __init pb_keys_init(void) +{ + struct gpio_keys_button *gb = gpio_button; + int i, num, good = 0; + + num = sizeof(gpio_button) / sizeof(struct gpio_keys_button); + for (i = 0; i < num; i++) { + gb[i].gpio = get_gpio_by_name(gb[i].desc); + pr_debug("info[%2d]: name = %s, gpio = %d\n", i, gb[i].desc, + gb[i].gpio); + if (gb[i].gpio == -1) + continue; + + if (i != good) + gb[good] = gb[i]; + good++; + } + + if (good) { + gpio_keys.nbuttons = good; + return platform_device_register(&pb_device); + } + return 0; +} +late_initcall(pb_keys_init); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_ipc.c b/arch/x86/platform/intel-mid/device_libs/platform_ipc.c new file mode 100644 index 0000000..a84b73d --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_ipc.c @@ -0,0 +1,68 @@ +/* + * platform_ipc.c: IPC platform library file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include +#include +#include +#include +#include +#include +#include "platform_ipc.h" + +void __init ipc_device_handler(struct sfi_device_table_entry *pentry, + struct devs_id *dev) +{ + struct platform_device *pdev; + void *pdata = NULL; + static struct resource res __initdata = { + .name = "IRQ", + .flags = IORESOURCE_IRQ, + }; + + pr_debug("IPC bus, name = %16.16s, irq = 0x%2x\n", + pentry->name, pentry->irq); + + /* + * We need to call platform init of IPC devices to fill misc_pdata + * structure. It will be used in msic_init for initialization. + */ + if (dev != NULL) + pdata = dev->get_platform_data(pentry); + + /* + * On Medfield the platform device creation is handled by the MSIC + * MFD driver so we don't need to do it here. + */ + if (intel_mid_has_msic()) + return; + + pdev = platform_device_alloc(pentry->name, 0); + if (pdev == NULL) { + pr_err("out of memory for SFI platform device '%s'.\n", + pentry->name); + return; + } + res.start = pentry->irq; + platform_device_add_resources(pdev, &res, 1); + + pdev->dev.platform_data = pdata; + intel_scu_device_register(pdev); +} + +static const struct devs_id pmic_audio_dev_id __initconst = { + .name = "pmic_audio", + .type = SFI_DEV_TYPE_IPC, + .delay = 1, + .device_handler = &ipc_device_handler, +}; + +sfi_device(pmic_audio_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_ipc.h b/arch/x86/platform/intel-mid/device_libs/platform_ipc.h new file mode 100644 index 0000000..8f568dd --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_ipc.h @@ -0,0 +1,17 @@ +/* + * platform_ipc.h: IPC platform library header file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ +#ifndef _PLATFORM_IPC_H_ +#define _PLATFORM_IPC_H_ + +extern void __init ipc_device_handler(struct sfi_device_table_entry *pentry, + struct devs_id *dev) __attribute__((weak)); +#endif diff --git a/arch/x86/platform/intel-mid/device_libs/platform_lis331.c b/arch/x86/platform/intel-mid/device_libs/platform_lis331.c new file mode 100644 index 0000000..15278c1 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_lis331.c @@ -0,0 +1,39 @@ +/* + * platform_lis331.c: lis331 platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include +#include +#include + +static void __init *lis331dl_platform_data(void *info) +{ + static short intr2nd_pdata; + struct i2c_board_info *i2c_info = info; + int intr = get_gpio_by_name("accel_int"); + int intr2nd = get_gpio_by_name("accel_2"); + + if (intr == -1 || intr2nd == -1) + return NULL; + + i2c_info->irq = intr + INTEL_MID_IRQ_OFFSET; + intr2nd_pdata = intr2nd + INTEL_MID_IRQ_OFFSET; + + return &intr2nd_pdata; +} + +static const struct devs_id lis331dl_dev_id __initconst = { + .name = "i2c_accel", + .type = SFI_DEV_TYPE_I2C, + .get_platform_data = &lis331dl_platform_data, +}; + +sfi_device(lis331dl_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_max3111.c b/arch/x86/platform/intel-mid/device_libs/platform_max3111.c new file mode 100644 index 0000000..afd1df9 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_max3111.c @@ -0,0 +1,35 @@ +/* + * platform_max3111.c: max3111 platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include +#include +#include + +static void __init *max3111_platform_data(void *info) +{ + struct spi_board_info *spi_info = info; + int intr = get_gpio_by_name("max3111_int"); + + spi_info->mode = SPI_MODE_0; + if (intr == -1) + return NULL; + spi_info->irq = intr + INTEL_MID_IRQ_OFFSET; + return NULL; +} + +static const struct devs_id max3111_dev_id __initconst = { + .name = "spi_max3111", + .type = SFI_DEV_TYPE_SPI, + .get_platform_data = &max3111_platform_data, +}; + +sfi_device(max3111_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_max7315.c b/arch/x86/platform/intel-mid/device_libs/platform_max7315.c new file mode 100644 index 0000000..94ade10 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_max7315.c @@ -0,0 +1,79 @@ +/* + * platform_max7315.c: max7315 platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include +#include +#include +#include +#include + +#define MAX7315_NUM 2 + +static void __init *max7315_platform_data(void *info) +{ + static struct pca953x_platform_data max7315_pdata[MAX7315_NUM]; + static int nr; + struct pca953x_platform_data *max7315 = &max7315_pdata[nr]; + struct i2c_board_info *i2c_info = info; + int gpio_base, intr; + char base_pin_name[SFI_NAME_LEN + 1]; + char intr_pin_name[SFI_NAME_LEN + 1]; + + if (nr == MAX7315_NUM) { + pr_err("too many max7315s, we only support %d\n", + MAX7315_NUM); + return NULL; + } + /* we have several max7315 on the board, we only need load several + * instances of the same pca953x driver to cover them + */ + strcpy(i2c_info->type, "max7315"); + if (nr++) { + sprintf(base_pin_name, "max7315_%d_base", nr); + sprintf(intr_pin_name, "max7315_%d_int", nr); + } else { + strcpy(base_pin_name, "max7315_base"); + strcpy(intr_pin_name, "max7315_int"); + } + + gpio_base = get_gpio_by_name(base_pin_name); + intr = get_gpio_by_name(intr_pin_name); + + if (gpio_base == -1) + return NULL; + max7315->gpio_base = gpio_base; + if (intr != -1) { + i2c_info->irq = intr + INTEL_MID_IRQ_OFFSET; + max7315->irq_base = gpio_base + INTEL_MID_IRQ_OFFSET; + } else { + i2c_info->irq = -1; + max7315->irq_base = -1; + } + return max7315; +} + +static const struct devs_id max7315_dev_id __initconst = { + .name = "i2c_max7315", + .type = SFI_DEV_TYPE_I2C, + .delay = 1, + .get_platform_data = &max7315_platform_data, +}; + +static const struct devs_id max7315_2_dev_id __initconst = { + .name = "i2c_max7315_2", + .type = SFI_DEV_TYPE_I2C, + .delay = 1, + .get_platform_data = &max7315_platform_data, +}; + +sfi_device(max7315_dev_id); +sfi_device(max7315_2_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_mpu3050.c b/arch/x86/platform/intel-mid/device_libs/platform_mpu3050.c new file mode 100644 index 0000000..dd28d63 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_mpu3050.c @@ -0,0 +1,36 @@ +/* + * platform_mpu3050.c: mpu3050 platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include +#include +#include + +static void *mpu3050_platform_data(void *info) +{ + struct i2c_board_info *i2c_info = info; + int intr = get_gpio_by_name("mpu3050_int"); + + if (intr == -1) + return NULL; + + i2c_info->irq = intr + INTEL_MID_IRQ_OFFSET; + return NULL; +} + +static const struct devs_id mpu3050_dev_id __initconst = { + .name = "mpu3050", + .type = SFI_DEV_TYPE_I2C, + .delay = 1, + .get_platform_data = &mpu3050_platform_data, +}; + +sfi_device(mpu3050_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_msic.c b/arch/x86/platform/intel-mid/device_libs/platform_msic.c new file mode 100644 index 0000000..9f4a775 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_msic.c @@ -0,0 +1,87 @@ +/* + * platform_msic.c: MSIC platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "platform_msic.h" + +struct intel_msic_platform_data msic_pdata; + +static struct resource msic_resources[] = { + { + .start = INTEL_MSIC_IRQ_PHYS_BASE, + .end = INTEL_MSIC_IRQ_PHYS_BASE + 64 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device msic_device = { + .name = "intel_msic", + .id = -1, + .dev = { + .platform_data = &msic_pdata, + }, + .num_resources = ARRAY_SIZE(msic_resources), + .resource = msic_resources, +}; + +static int msic_scu_status_change(struct notifier_block *nb, + unsigned long code, void *data) +{ + if (code == SCU_DOWN) { + platform_device_unregister(&msic_device); + return 0; + } + + return platform_device_register(&msic_device); +} + +static int __init msic_init(void) +{ + static struct notifier_block msic_scu_notifier = { + .notifier_call = msic_scu_status_change, + }; + + /* + * We need to be sure that the SCU IPC is ready before MSIC device + * can be registered. + */ + if (intel_mid_has_msic()) + intel_scu_notifier_add(&msic_scu_notifier); + + return 0; +} +arch_initcall(msic_init); + +/* + * msic_generic_platform_data - sets generic platform data for the block + * @info: pointer to the SFI device table entry for this block + * @block: MSIC block + * + * Function sets IRQ number from the SFI table entry for given device to + * the MSIC platform data. + */ +void *msic_generic_platform_data(void *info, enum intel_msic_block block) +{ + struct sfi_device_table_entry *entry = info; + + BUG_ON(block < 0 || block >= INTEL_MSIC_BLOCK_LAST); + msic_pdata.irq[block] = entry->irq; + + return NULL; +} diff --git a/arch/x86/platform/intel-mid/device_libs/platform_msic.h b/arch/x86/platform/intel-mid/device_libs/platform_msic.h new file mode 100644 index 0000000..917eb56 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_msic.h @@ -0,0 +1,19 @@ +/* + * platform_msic.h: MSIC platform data header file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ +#ifndef _PLATFORM_MSIC_H_ +#define _PLATFORM_MSIC_H_ + +extern struct intel_msic_platform_data msic_pdata; + +extern void *msic_generic_platform_data(void *info, + enum intel_msic_block block) __attribute__((weak)); +#endif diff --git a/arch/x86/platform/intel-mid/device_libs/platform_msic_audio.c b/arch/x86/platform/intel-mid/device_libs/platform_msic_audio.c new file mode 100644 index 0000000..2962939 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_msic_audio.c @@ -0,0 +1,47 @@ +/* + * platform_msic_audio.c: MSIC audio platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "platform_msic.h" +#include "platform_ipc.h" + +static void *msic_audio_platform_data(void *info) +{ + struct platform_device *pdev; + + pdev = platform_device_register_simple("sst-platform", -1, NULL, 0); + + if (IS_ERR(pdev)) { + pr_err("failed to create audio platform device\n"); + return NULL; + } + + return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_AUDIO); +} + +static const struct devs_id msic_audio_dev_id __initconst = { + .name = "msic_audio", + .type = SFI_DEV_TYPE_IPC, + .delay = 1, + .get_platform_data = &msic_audio_platform_data, + .device_handler = &ipc_device_handler, +}; + +sfi_device(msic_audio_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_msic_battery.c b/arch/x86/platform/intel-mid/device_libs/platform_msic_battery.c new file mode 100644 index 0000000..f446c33 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_msic_battery.c @@ -0,0 +1,37 @@ +/* + * platform_msic_battery.c: MSIC battery platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "platform_msic.h" +#include "platform_ipc.h" + +static void __init *msic_battery_platform_data(void *info) +{ + return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_BATTERY); +} + +static const struct devs_id msic_battery_dev_id __initconst = { + .name = "msic_battery", + .type = SFI_DEV_TYPE_IPC, + .delay = 1, + .get_platform_data = &msic_battery_platform_data, + .device_handler = &ipc_device_handler, +}; + +sfi_device(msic_battery_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_msic_gpio.c b/arch/x86/platform/intel-mid/device_libs/platform_msic_gpio.c new file mode 100644 index 0000000..2a4f7b1 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_msic_gpio.c @@ -0,0 +1,48 @@ +/* + * platform_msic_gpio.c: MSIC GPIO platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "platform_msic.h" +#include "platform_ipc.h" + +static void __init *msic_gpio_platform_data(void *info) +{ + static struct intel_msic_gpio_pdata msic_gpio_pdata; + + int gpio = get_gpio_by_name("msic_gpio_base"); + + if (gpio < 0) + return NULL; + + msic_gpio_pdata.gpio_base = gpio; + msic_pdata.gpio = &msic_gpio_pdata; + + return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_GPIO); +} + +static const struct devs_id msic_gpio_dev_id __initconst = { + .name = "msic_gpio", + .type = SFI_DEV_TYPE_IPC, + .delay = 1, + .get_platform_data = &msic_gpio_platform_data, + .device_handler = &ipc_device_handler, +}; + +sfi_device(msic_gpio_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_msic_ocd.c b/arch/x86/platform/intel-mid/device_libs/platform_msic_ocd.c new file mode 100644 index 0000000..6497111 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_msic_ocd.c @@ -0,0 +1,49 @@ +/* + * platform_msic_ocd.c: MSIC OCD platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "platform_msic.h" +#include "platform_ipc.h" + +static void __init *msic_ocd_platform_data(void *info) +{ + static struct intel_msic_ocd_pdata msic_ocd_pdata; + int gpio; + + gpio = get_gpio_by_name("ocd_gpio"); + + if (gpio < 0) + return NULL; + + msic_ocd_pdata.gpio = gpio; + msic_pdata.ocd = &msic_ocd_pdata; + + return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_OCD); +} + +static const struct devs_id msic_ocd_dev_id __initconst = { + .name = "msic_ocd", + .type = SFI_DEV_TYPE_IPC, + .delay = 1, + .get_platform_data = &msic_ocd_platform_data, + .device_handler = &ipc_device_handler, +}; + +sfi_device(msic_ocd_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_msic_power_btn.c b/arch/x86/platform/intel-mid/device_libs/platform_msic_power_btn.c new file mode 100644 index 0000000..83a3459 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_msic_power_btn.c @@ -0,0 +1,36 @@ +/* + * platform_msic_power_btn.c: MSIC power btn platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "platform_msic.h" +#include "platform_ipc.h" + +static void __init *msic_power_btn_platform_data(void *info) +{ + return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_POWER_BTN); +} + +static const struct devs_id msic_power_btn_dev_id __initconst = { + .name = "msic_power_btn", + .type = SFI_DEV_TYPE_IPC, + .delay = 1, + .get_platform_data = &msic_power_btn_platform_data, + .device_handler = &ipc_device_handler, +}; + +sfi_device(msic_power_btn_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_msic_thermal.c b/arch/x86/platform/intel-mid/device_libs/platform_msic_thermal.c new file mode 100644 index 0000000..a351878 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_msic_thermal.c @@ -0,0 +1,37 @@ +/* + * platform_msic_thermal.c: msic_thermal platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "platform_msic.h" +#include "platform_ipc.h" + +static void __init *msic_thermal_platform_data(void *info) +{ + return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_THERMAL); +} + +static const struct devs_id msic_thermal_dev_id __initconst = { + .name = "msic_thermal", + .type = SFI_DEV_TYPE_IPC, + .delay = 1, + .get_platform_data = &msic_thermal_platform_data, + .device_handler = &ipc_device_handler, +}; + +sfi_device(msic_thermal_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_pmic_gpio.c b/arch/x86/platform/intel-mid/device_libs/platform_pmic_gpio.c new file mode 100644 index 0000000..d87182a --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_pmic_gpio.c @@ -0,0 +1,54 @@ +/* + * platform_pmic_gpio.c: PMIC GPIO platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "platform_ipc.h" + +static void __init *pmic_gpio_platform_data(void *info) +{ + static struct intel_pmic_gpio_platform_data pmic_gpio_pdata; + int gpio_base = get_gpio_by_name("pmic_gpio_base"); + + if (gpio_base == -1) + gpio_base = 64; + pmic_gpio_pdata.gpio_base = gpio_base; + pmic_gpio_pdata.irq_base = gpio_base + INTEL_MID_IRQ_OFFSET; + pmic_gpio_pdata.gpiointr = 0xffffeff8; + + return &pmic_gpio_pdata; +} + +static const struct devs_id pmic_gpio_spi_dev_id __initconst = { + .name = "pmic_gpio", + .type = SFI_DEV_TYPE_SPI, + .delay = 1, + .get_platform_data = &pmic_gpio_platform_data, +}; + +static const struct devs_id pmic_gpio_ipc_dev_id __initconst = { + .name = "pmic_gpio", + .type = SFI_DEV_TYPE_IPC, + .delay = 1, + .get_platform_data = &pmic_gpio_platform_data, + .device_handler = &ipc_device_handler +}; + +sfi_device(pmic_gpio_spi_dev_id); +sfi_device(pmic_gpio_ipc_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_tc35876x.c b/arch/x86/platform/intel-mid/device_libs/platform_tc35876x.c new file mode 100644 index 0000000..740fc75 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_tc35876x.c @@ -0,0 +1,36 @@ +/* + * platform_tc35876x.c: tc35876x platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include +#include +#include + +/*tc35876x DSI_LVDS bridge chip and panel platform data*/ +static void *tc35876x_platform_data(void *data) +{ + static struct tc35876x_platform_data pdata; + + /* gpio pins set to -1 will not be used by the driver */ + pdata.gpio_bridge_reset = get_gpio_by_name("LCMB_RXEN"); + pdata.gpio_panel_bl_en = get_gpio_by_name("6S6P_BL_EN"); + pdata.gpio_panel_vadd = get_gpio_by_name("EN_VREG_LCD_V3P3"); + + return &pdata; +} + +static const struct devs_id tc35876x_dev_id __initconst = { + .name = "i2c_disp_brig", + .type = SFI_DEV_TYPE_I2C, + .get_platform_data = &tc35876x_platform_data, +}; + +sfi_device(tc35876x_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_tca6416.c b/arch/x86/platform/intel-mid/device_libs/platform_tca6416.c new file mode 100644 index 0000000..22881c9 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_tca6416.c @@ -0,0 +1,57 @@ +/* + * platform_tca6416.c: tca6416 platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include +#include +#include +#include + +#define TCA6416_NAME "tca6416" +#define TCA6416_BASE "tca6416_base" +#define TCA6416_INTR "tca6416_int" + +static void *tca6416_platform_data(void *info) +{ + static struct pca953x_platform_data tca6416; + struct i2c_board_info *i2c_info = info; + int gpio_base, intr; + char base_pin_name[SFI_NAME_LEN + 1]; + char intr_pin_name[SFI_NAME_LEN + 1]; + + strcpy(i2c_info->type, TCA6416_NAME); + strcpy(base_pin_name, TCA6416_BASE); + strcpy(intr_pin_name, TCA6416_INTR); + + gpio_base = get_gpio_by_name(base_pin_name); + intr = get_gpio_by_name(intr_pin_name); + + if (gpio_base == -1) + return NULL; + tca6416.gpio_base = gpio_base; + if (intr != -1) { + i2c_info->irq = intr + INTEL_MID_IRQ_OFFSET; + tca6416.irq_base = gpio_base + INTEL_MID_IRQ_OFFSET; + } else { + i2c_info->irq = -1; + tca6416.irq_base = -1; + } + return &tca6416; +} + +static const struct devs_id tca6416_dev_id __initconst = { + .name = "tca6416", + .type = SFI_DEV_TYPE_I2C, + .delay = 1, + .get_platform_data = &tca6416_platform_data, +}; + +sfi_device(tca6416_dev_id); diff --git a/arch/x86/platform/intel-mid/intel-mid.c b/arch/x86/platform/intel-mid/intel-mid.c index 4091569..523a1c8 100644 --- a/arch/x86/platform/intel-mid/intel-mid.c +++ b/arch/x86/platform/intel-mid/intel-mid.c @@ -61,8 +61,6 @@ enum intel_mid_timer_options intel_mid_timer_options; enum intel_mid_cpu_type __intel_mid_cpu_chip; EXPORT_SYMBOL_GPL(__intel_mid_cpu_chip); -static void __init ipc_device_handler(struct sfi_device_table_entry *pentry, - struct devs_id *dev); static void intel_mid_power_off(void) { } @@ -213,420 +211,3 @@ static inline int __init setup_x86_intel_mid_timer(char *arg) } __setup("x86_intel_mid_timer=", setup_x86_intel_mid_timer); -/* the offset for the mapping of global gpio pin to irq */ -#define INTEL_MID_IRQ_OFFSET 0x100 - -static void __init *pmic_gpio_platform_data(void *info) -{ - static struct intel_pmic_gpio_platform_data pmic_gpio_pdata; - int gpio_base = get_gpio_by_name("pmic_gpio_base"); - - if (gpio_base == -1) - gpio_base = 64; - pmic_gpio_pdata.gpio_base = gpio_base; - pmic_gpio_pdata.irq_base = gpio_base + INTEL_MID_IRQ_OFFSET; - pmic_gpio_pdata.gpiointr = 0xffffeff8; - - return &pmic_gpio_pdata; -} - -static void __init *max3111_platform_data(void *info) -{ - struct spi_board_info *spi_info = info; - int intr = get_gpio_by_name("max3111_int"); - - spi_info->mode = SPI_MODE_0; - if (intr == -1) - return NULL; - spi_info->irq = intr + INTEL_MID_IRQ_OFFSET; - return NULL; -} - -/* we have multiple max7315 on the board ... */ -#define MAX7315_NUM 2 -static void __init *max7315_platform_data(void *info) -{ - static struct pca953x_platform_data max7315_pdata[MAX7315_NUM]; - static int nr; - struct pca953x_platform_data *max7315 = &max7315_pdata[nr]; - struct i2c_board_info *i2c_info = info; - int gpio_base, intr; - char base_pin_name[SFI_NAME_LEN + 1]; - char intr_pin_name[SFI_NAME_LEN + 1]; - - if (nr == MAX7315_NUM) { - pr_err("too many max7315s, we only support %d\n", - MAX7315_NUM); - return NULL; - } - /* we have several max7315 on the board, we only need load several - * instances of the same pca953x driver to cover them - */ - strcpy(i2c_info->type, "max7315"); - if (nr++) { - sprintf(base_pin_name, "max7315_%d_base", nr); - sprintf(intr_pin_name, "max7315_%d_int", nr); - } else { - strcpy(base_pin_name, "max7315_base"); - strcpy(intr_pin_name, "max7315_int"); - } - - gpio_base = get_gpio_by_name(base_pin_name); - intr = get_gpio_by_name(intr_pin_name); - - if (gpio_base == -1) - return NULL; - max7315->gpio_base = gpio_base; - if (intr != -1) { - i2c_info->irq = intr + INTEL_MID_IRQ_OFFSET; - max7315->irq_base = gpio_base + INTEL_MID_IRQ_OFFSET; - } else { - i2c_info->irq = -1; - max7315->irq_base = -1; - } - return max7315; -} - -static void *tca6416_platform_data(void *info) -{ - static struct pca953x_platform_data tca6416; - struct i2c_board_info *i2c_info = info; - int gpio_base, intr; - char base_pin_name[SFI_NAME_LEN + 1]; - char intr_pin_name[SFI_NAME_LEN + 1]; - - strcpy(i2c_info->type, "tca6416"); - strcpy(base_pin_name, "tca6416_base"); - strcpy(intr_pin_name, "tca6416_int"); - - gpio_base = get_gpio_by_name(base_pin_name); - intr = get_gpio_by_name(intr_pin_name); - - if (gpio_base == -1) - return NULL; - tca6416.gpio_base = gpio_base; - if (intr != -1) { - i2c_info->irq = intr + INTEL_MID_IRQ_OFFSET; - tca6416.irq_base = gpio_base + INTEL_MID_IRQ_OFFSET; - } else { - i2c_info->irq = -1; - tca6416.irq_base = -1; - } - return &tca6416; -} - -static void *mpu3050_platform_data(void *info) -{ - struct i2c_board_info *i2c_info = info; - int intr = get_gpio_by_name("mpu3050_int"); - - if (intr == -1) - return NULL; - - i2c_info->irq = intr + INTEL_MID_IRQ_OFFSET; - return NULL; -} - -static void __init *emc1403_platform_data(void *info) -{ - static short intr2nd_pdata; - struct i2c_board_info *i2c_info = info; - int intr = get_gpio_by_name("thermal_int"); - int intr2nd = get_gpio_by_name("thermal_alert"); - - if (intr == -1 || intr2nd == -1) - return NULL; - - i2c_info->irq = intr + INTEL_MID_IRQ_OFFSET; - intr2nd_pdata = intr2nd + INTEL_MID_IRQ_OFFSET; - - return &intr2nd_pdata; -} - -static void __init *lis331dl_platform_data(void *info) -{ - static short intr2nd_pdata; - struct i2c_board_info *i2c_info = info; - int intr = get_gpio_by_name("accel_int"); - int intr2nd = get_gpio_by_name("accel_2"); - - if (intr == -1 || intr2nd == -1) - return NULL; - - i2c_info->irq = intr + INTEL_MID_IRQ_OFFSET; - intr2nd_pdata = intr2nd + INTEL_MID_IRQ_OFFSET; - - return &intr2nd_pdata; -} - -static void __init *no_platform_data(void *info) -{ - return NULL; -} - -static struct resource msic_resources[] = { - { - .start = INTEL_MSIC_IRQ_PHYS_BASE, - .end = INTEL_MSIC_IRQ_PHYS_BASE + 64 - 1, - .flags = IORESOURCE_MEM, - }, -}; - -static struct intel_msic_platform_data msic_pdata; - -static struct platform_device msic_device = { - .name = "intel_msic", - .id = -1, - .dev = { - .platform_data = &msic_pdata, - }, - .num_resources = ARRAY_SIZE(msic_resources), - .resource = msic_resources, -}; - -static inline bool intel_mid_has_msic(void) -{ - return intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_PENWELL; -} - -static int msic_scu_status_change(struct notifier_block *nb, - unsigned long code, void *data) -{ - if (code == SCU_DOWN) { - platform_device_unregister(&msic_device); - return 0; - } - - return platform_device_register(&msic_device); -} - -static int __init msic_init(void) -{ - static struct notifier_block msic_scu_notifier = { - .notifier_call = msic_scu_status_change, - }; - - /* - * We need to be sure that the SCU IPC is ready before MSIC device - * can be registered. - */ - if (intel_mid_has_msic()) - intel_scu_notifier_add(&msic_scu_notifier); - - return 0; -} -arch_initcall(msic_init); - -/* - * msic_generic_platform_data - sets generic platform data for the block - * @info: pointer to the SFI device table entry for this block - * @block: MSIC block - * - * Function sets IRQ number from the SFI table entry for given device to - * the MSIC platform data. - */ -static void *msic_generic_platform_data(void *info, enum intel_msic_block block) -{ - struct sfi_device_table_entry *entry = info; - - BUG_ON(block < 0 || block >= INTEL_MSIC_BLOCK_LAST); - msic_pdata.irq[block] = entry->irq; - - return no_platform_data(info); -} - -static void *msic_battery_platform_data(void *info) -{ - return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_BATTERY); -} - -static void *msic_gpio_platform_data(void *info) -{ - static struct intel_msic_gpio_pdata pdata; - int gpio = get_gpio_by_name("msic_gpio_base"); - - if (gpio < 0) - return NULL; - - pdata.gpio_base = gpio; - msic_pdata.gpio = &pdata; - - return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_GPIO); -} - -static void *msic_audio_platform_data(void *info) -{ - struct platform_device *pdev; - - pdev = platform_device_register_simple("sst-platform", -1, NULL, 0); - if (IS_ERR(pdev)) { - pr_err("failed to create audio platform device\n"); - return NULL; - } - - return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_AUDIO); -} - -static void *msic_power_btn_platform_data(void *info) -{ - return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_POWER_BTN); -} - -static void *msic_ocd_platform_data(void *info) -{ - static struct intel_msic_ocd_pdata pdata; - int gpio = get_gpio_by_name("ocd_gpio"); - - if (gpio < 0) - return NULL; - - pdata.gpio = gpio; - msic_pdata.ocd = &pdata; - - return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_OCD); -} - -static void *msic_thermal_platform_data(void *info) -{ - return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_THERMAL); -} - -/* tc35876x DSI-LVDS bridge chip and panel platform data */ -static void *tc35876x_platform_data(void *data) -{ - static struct tc35876x_platform_data pdata; - - /* gpio pins set to -1 will not be used by the driver */ - pdata.gpio_bridge_reset = get_gpio_by_name("LCMB_RXEN"); - pdata.gpio_panel_bl_en = get_gpio_by_name("6S6P_BL_EN"); - pdata.gpio_panel_vadd = get_gpio_by_name("EN_VREG_LCD_V3P3"); - - return &pdata; -} - -static const struct devs_id __initconst device_ids[] = { - {"bma023", SFI_DEV_TYPE_I2C, 1, &no_platform_data, NULL}, - {"pmic_gpio", SFI_DEV_TYPE_SPI, 1, &pmic_gpio_platform_data, NULL}, - {"pmic_gpio", SFI_DEV_TYPE_IPC, 1, &pmic_gpio_platform_data, &ipc_device_handler}, - {"spi_max3111", SFI_DEV_TYPE_SPI, 0, &max3111_platform_data, NULL}, - {"i2c_max7315", SFI_DEV_TYPE_I2C, 1, &max7315_platform_data, NULL}, - {"i2c_max7315_2", SFI_DEV_TYPE_I2C, 1, &max7315_platform_data, NULL}, - {"tca6416", SFI_DEV_TYPE_I2C, 1, &tca6416_platform_data, NULL}, - {"emc1403", SFI_DEV_TYPE_I2C, 1, &emc1403_platform_data, NULL}, - {"i2c_accel", SFI_DEV_TYPE_I2C, 0, &lis331dl_platform_data, NULL}, - {"pmic_audio", SFI_DEV_TYPE_IPC, 1, &no_platform_data, &ipc_device_handler}, - {"mpu3050", SFI_DEV_TYPE_I2C, 1, &mpu3050_platform_data, NULL}, - {"i2c_disp_brig", SFI_DEV_TYPE_I2C, 0, &tc35876x_platform_data, NULL}, - - /* MSIC subdevices */ - {"msic_battery", SFI_DEV_TYPE_IPC, 1, &msic_battery_platform_data, &ipc_device_handler}, - {"msic_gpio", SFI_DEV_TYPE_IPC, 1, &msic_gpio_platform_data, &ipc_device_handler}, - {"msic_audio", SFI_DEV_TYPE_IPC, 1, &msic_audio_platform_data, &ipc_device_handler}, - {"msic_power_btn", SFI_DEV_TYPE_IPC, 1, &msic_power_btn_platform_data, &ipc_device_handler}, - {"msic_ocd", SFI_DEV_TYPE_IPC, 1, &msic_ocd_platform_data, &ipc_device_handler}, - {"msic_thermal", SFI_DEV_TYPE_IPC, 1, &msic_thermal_platform_data, &ipc_device_handler}, - { 0 } -}; - -static void __init ipc_device_handler(struct sfi_device_table_entry *pentry, - struct devs_id *dev) -{ - struct platform_device *pdev; - void *pdata = NULL; - static struct resource res __initdata = { - .name = "IRQ", - .flags = IORESOURCE_IRQ, - }; - - pr_debug("IPC bus, name = %16.16s, irq = 0x%2x\n", - pentry->name, pentry->irq); - - /* - * We need to call platform init of IPC devices to fill misc_pdata - * structure. It will be used in msic_init for initialization. - */ - if (dev != NULL) - pdata = dev->get_platform_data(pentry); - - /* - * On Medfield the platform device creation is handled by the MSIC - * MFD driver so we don't need to do it here. - */ - if (intel_mid_has_msic()) - return; - - pdev = platform_device_alloc(pentry->name, 0); - if (pdev == NULL) { - pr_err("out of memory for SFI platform device '%s'.\n", - pentry->name); - return; - } - res.start = pentry->irq; - platform_device_add_resources(pdev, &res, 1); - - pdev->dev.platform_data = pdata; - intel_scu_device_register(pdev); -} - - -/* - * we will search these buttons in SFI GPIO table (by name) - * and register them dynamically. Please add all possible - * buttons here, we will shrink them if no GPIO found. - */ -static struct gpio_keys_button gpio_button[] = { - {KEY_POWER, -1, 1, "power_btn", EV_KEY, 0, 3000}, - {KEY_PROG1, -1, 1, "prog_btn1", EV_KEY, 0, 20}, - {KEY_PROG2, -1, 1, "prog_btn2", EV_KEY, 0, 20}, - {SW_LID, -1, 1, "lid_switch", EV_SW, 0, 20}, - {KEY_VOLUMEUP, -1, 1, "vol_up", EV_KEY, 0, 20}, - {KEY_VOLUMEDOWN, -1, 1, "vol_down", EV_KEY, 0, 20}, - {KEY_CAMERA, -1, 1, "camera_full", EV_KEY, 0, 20}, - {KEY_CAMERA_FOCUS, -1, 1, "camera_half", EV_KEY, 0, 20}, - {SW_KEYPAD_SLIDE, -1, 1, "MagSw1", EV_SW, 0, 20}, - {SW_KEYPAD_SLIDE, -1, 1, "MagSw2", EV_SW, 0, 20}, -}; - -static struct gpio_keys_platform_data intel_mid_gpio_keys = { - .buttons = gpio_button, - .rep = 1, - .nbuttons = -1, /* will fill it after search */ -}; - -static struct platform_device pb_device = { - .name = "gpio-keys", - .id = -1, - .dev = { - .platform_data = &intel_mid_gpio_keys, - }, -}; - -/* - * Shrink the non-existent buttons, register the gpio button - * device if there is some - */ -static int __init pb_keys_init(void) -{ - struct gpio_keys_button *gb = gpio_button; - int i, num, good = 0; - - num = sizeof(gpio_button) / sizeof(struct gpio_keys_button); - for (i = 0; i < num; i++) { - gb[i].gpio = get_gpio_by_name(gb[i].desc); - pr_debug("info[%2d]: name = %s, gpio = %d\n", i, gb[i].desc, - gb[i].gpio); - if (gb[i].gpio == -1) - continue; - - if (i != good) - gb[good] = gb[i]; - good++; - } - - if (good) { - intel_mid_gpio_keys.nbuttons = good; - return platform_device_register(&pb_device); - } - return 0; -} -late_initcall(pb_keys_init); \ No newline at end of file diff --git a/arch/x86/platform/intel-mid/sfi.c b/arch/x86/platform/intel-mid/sfi.c index 3f1c171..c84c1ca 100644 --- a/arch/x86/platform/intel-mid/sfi.c +++ b/arch/x86/platform/intel-mid/sfi.c @@ -42,7 +42,6 @@ #include #include #include -#include "intel_mid_weak_decls.h" #define SFI_SIG_OEM0 "OEM0" #define MAX_IPCDEVS 24 @@ -403,19 +402,20 @@ static void __init sfi_handle_i2c_dev(struct sfi_device_table_entry *pentry, i2c_register_board_info(pentry->host_num, &i2c_info, 1); } +extern struct devs_id *const __x86_intel_mid_dev_start[], + *const __x86_intel_mid_dev_end[]; + static struct devs_id __init *get_device_id(u8 type, char *name) { - struct devs_id *dev = device_ids; - - if (device_ids == NULL) - return NULL; + struct devs_id *const *dev_table; - while (dev->name[0]) { + for (dev_table = __x86_intel_mid_dev_start; + dev_table < __x86_intel_mid_dev_end; dev_table++) { + struct devs_id *dev = *dev_table; if (dev->type == type && !strncmp(dev->name, name, SFI_NAME_LEN)) { return dev; } - dev++; } return NULL; diff --git a/include/linux/sfi.h b/include/linux/sfi.h index fe81791..d9b436f 100644 --- a/include/linux/sfi.h +++ b/include/linux/sfi.h @@ -59,6 +59,9 @@ #ifndef _LINUX_SFI_H #define _LINUX_SFI_H +#include +#include + /* Table signatures reserved by the SFI specification */ #define SFI_SIG_SYST "SYST" #define SFI_SIG_FREQ "FREQ" -- cgit v0.10.2 From 6833c452c2fb47353566aa705d68541c6045c796 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 16 Oct 2013 22:05:26 -0700 Subject: ASoC: add snd_soc_of_get_dai_name() default of_xlate Current snd_soc_of_get_dai_name() needs .of_xlate_dai_name() callback on each component drivers. But required behavior on almost all these drivers is just returns its indexed driver's name. This patch adds this feature as default behavior. .of_xlate_dai_name() can overwrite it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index b13eecb..6ed3dc0 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -644,10 +644,12 @@ struct snd_soc_component_driver { struct snd_soc_component { const char *name; int id; - int num_dai; struct device *dev; struct list_head list; + struct snd_soc_dai_driver *dai_drv; + int num_dai; + const struct snd_soc_component_driver *driver; }; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 67cfb5f..0860a7f 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4023,6 +4023,7 @@ __snd_soc_register_component(struct device *dev, cmpnt->dev = dev; cmpnt->driver = cmpnt_drv; + cmpnt->dai_drv = dai_drv; cmpnt->num_dai = num_dai; /* @@ -4548,12 +4549,31 @@ int snd_soc_of_get_dai_name(struct device_node *of_node, if (pos->dev->of_node != args.np) continue; - if (!pos->driver->of_xlate_dai_name) { - ret = -ENOSYS; - break; + if (pos->driver->of_xlate_dai_name) { + ret = pos->driver->of_xlate_dai_name(pos, &args, dai_name); + } else { + int id = -1; + + switch (args.args_count) { + case 0: + id = 0; /* same as dai_drv[0] */ + break; + case 1: + id = args.args[0]; + break; + default: + /* not supported */ + break; + } + + if (id < 0 || id >= pos->num_dai) { + ret = -EINVAL; + } else { + *dai_name = pos->dai_drv[id].name; + ret = 0; + } } - ret = pos->driver->of_xlate_dai_name(pos, &args, dai_name); break; } mutex_unlock(&client_mutex); -- cgit v0.10.2 From a06ccd9c3785fa5550917ae036944f4e080b5749 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 15 Oct 2013 20:14:20 +0100 Subject: regulator: core: Add ability to create a lookup alias for supply These patches add the ability to create an alternative device on which a lookup for a certain supply should be conducted. A common use-case for this would be devices that are logically represented as a collection of drivers within Linux but are are presented as a single device from device tree. It this case it is necessary for each sub device to locate their supply data on the main device. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 906deb7..16427de 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -53,6 +53,7 @@ static DEFINE_MUTEX(regulator_list_mutex); static LIST_HEAD(regulator_list); static LIST_HEAD(regulator_map_list); static LIST_HEAD(regulator_ena_gpio_list); +static LIST_HEAD(regulator_supply_alias_list); static bool has_full_constraints; static bool board_wants_dummy_regulator; @@ -83,6 +84,19 @@ struct regulator_enable_gpio { unsigned int ena_gpio_invert:1; }; +/* + * struct regulator_supply_alias + * + * Used to map lookups for a supply onto an alternative device. + */ +struct regulator_supply_alias { + struct list_head list; + struct device *src_dev; + const char *src_supply; + struct device *alias_dev; + const char *alias_supply; +}; + static int _regulator_is_enabled(struct regulator_dev *rdev); static int _regulator_disable(struct regulator_dev *rdev); static int _regulator_get_voltage(struct regulator_dev *rdev); @@ -1173,6 +1187,32 @@ static int _regulator_get_enable_time(struct regulator_dev *rdev) return rdev->desc->ops->enable_time(rdev); } +static struct regulator_supply_alias *regulator_find_supply_alias( + struct device *dev, const char *supply) +{ + struct regulator_supply_alias *map; + + list_for_each_entry(map, ®ulator_supply_alias_list, list) + if (map->src_dev == dev && strcmp(map->src_supply, supply) == 0) + return map; + + return NULL; +} + +static void regulator_supply_alias(struct device **dev, const char **supply) +{ + struct regulator_supply_alias *map; + + map = regulator_find_supply_alias(*dev, *supply); + if (map) { + dev_dbg(*dev, "Mapping supply %s to %s,%s\n", + *supply, map->alias_supply, + dev_name(map->alias_dev)); + *dev = map->alias_dev; + *supply = map->alias_supply; + } +} + static struct regulator_dev *regulator_dev_lookup(struct device *dev, const char *supply, int *ret) @@ -1182,6 +1222,8 @@ static struct regulator_dev *regulator_dev_lookup(struct device *dev, struct regulator_map *map; const char *devname = NULL; + regulator_supply_alias(&dev, &supply); + /* first do a dt based lookup */ if (dev && dev->of_node) { node = of_get_regulator(dev, supply); @@ -1432,6 +1474,134 @@ void regulator_put(struct regulator *regulator) } EXPORT_SYMBOL_GPL(regulator_put); +/** + * regulator_register_supply_alias - Provide device alias for supply lookup + * + * @dev: device that will be given as the regulator "consumer" + * @id: Supply name or regulator ID + * @alias_dev: device that should be used to lookup the supply + * @alias_id: Supply name or regulator ID that should be used to lookup the + * supply + * + * All lookups for id on dev will instead be conducted for alias_id on + * alias_dev. + */ +int regulator_register_supply_alias(struct device *dev, const char *id, + struct device *alias_dev, + const char *alias_id) +{ + struct regulator_supply_alias *map; + + map = regulator_find_supply_alias(dev, id); + if (map) + return -EEXIST; + + map = kzalloc(sizeof(struct regulator_supply_alias), GFP_KERNEL); + if (!map) + return -ENOMEM; + + map->src_dev = dev; + map->src_supply = id; + map->alias_dev = alias_dev; + map->alias_supply = alias_id; + + list_add(&map->list, ®ulator_supply_alias_list); + + pr_info("Adding alias for supply %s,%s -> %s,%s\n", + id, dev_name(dev), alias_id, dev_name(alias_dev)); + + return 0; +} +EXPORT_SYMBOL_GPL(regulator_register_supply_alias); + +/** + * regulator_unregister_supply_alias - Remove device alias + * + * @dev: device that will be given as the regulator "consumer" + * @id: Supply name or regulator ID + * + * Remove a lookup alias if one exists for id on dev. + */ +void regulator_unregister_supply_alias(struct device *dev, const char *id) +{ + struct regulator_supply_alias *map; + + map = regulator_find_supply_alias(dev, id); + if (map) { + list_del(&map->list); + kfree(map); + } +} +EXPORT_SYMBOL_GPL(regulator_unregister_supply_alias); + +/** + * regulator_bulk_register_supply_alias - register multiple aliases + * + * @dev: device that will be given as the regulator "consumer" + * @id: List of supply names or regulator IDs + * @alias_dev: device that should be used to lookup the supply + * @alias_id: List of supply names or regulator IDs that should be used to + * lookup the supply + * @num_id: Number of aliases to register + * + * @return 0 on success, an errno on failure. + * + * This helper function allows drivers to register several supply + * aliases in one operation. If any of the aliases cannot be + * registered any aliases that were registered will be removed + * before returning to the caller. + */ +int regulator_bulk_register_supply_alias(struct device *dev, const char **id, + struct device *alias_dev, + const char **alias_id, + int num_id) +{ + int i; + int ret; + + for (i = 0; i < num_id; ++i) { + ret = regulator_register_supply_alias(dev, id[i], alias_dev, + alias_id[i]); + if (ret < 0) + goto err; + } + + return 0; + +err: + dev_err(dev, + "Failed to create supply alias %s,%s -> %s,%s\n", + id[i], dev_name(dev), alias_id[i], dev_name(alias_dev)); + + while (--i >= 0) + regulator_unregister_supply_alias(dev, id[i]); + + return ret; +} +EXPORT_SYMBOL_GPL(regulator_bulk_register_supply_alias); + +/** + * regulator_bulk_unregister_supply_alias - unregister multiple aliases + * + * @dev: device that will be given as the regulator "consumer" + * @id: List of supply names or regulator IDs + * @num_id: Number of aliases to unregister + * + * This helper function allows drivers to unregister several supply + * aliases in one operation. + */ +void regulator_bulk_unregister_supply_alias(struct device *dev, + const char **id, + int num_id) +{ + int i; + + for (i = 0; i < num_id; ++i) + regulator_unregister_supply_alias(dev, id[i]); +} +EXPORT_SYMBOL_GPL(regulator_bulk_unregister_supply_alias); + + /* Manage enable GPIO list. Same GPIO pin can be shared among regulators */ static int regulator_ena_gpio_request(struct regulator_dev *rdev, const struct regulator_config *config) diff --git a/drivers/regulator/devres.c b/drivers/regulator/devres.c index 2672a02..f44818b 100644 --- a/drivers/regulator/devres.c +++ b/drivers/regulator/devres.c @@ -250,3 +250,166 @@ void devm_regulator_unregister(struct device *dev, struct regulator_dev *rdev) WARN_ON(rc); } EXPORT_SYMBOL_GPL(devm_regulator_unregister); + +struct regulator_supply_alias_match { + struct device *dev; + const char *id; +}; + +static int devm_regulator_match_supply_alias(struct device *dev, void *res, + void *data) +{ + struct regulator_supply_alias_match *match = res; + struct regulator_supply_alias_match *target = data; + + return match->dev == target->dev && strcmp(match->id, target->id) == 0; +} + +static void devm_regulator_destroy_supply_alias(struct device *dev, void *res) +{ + struct regulator_supply_alias_match *match = res; + + regulator_unregister_supply_alias(match->dev, match->id); +} + +/** + * devm_regulator_register_supply_alias - Resource managed + * regulator_register_supply_alias() + * + * @dev: device that will be given as the regulator "consumer" + * @id: Supply name or regulator ID + * @alias_dev: device that should be used to lookup the supply + * @alias_id: Supply name or regulator ID that should be used to lookup the + * supply + * + * The supply alias will automatically be unregistered when the source + * device is unbound. + */ +int devm_regulator_register_supply_alias(struct device *dev, const char *id, + struct device *alias_dev, + const char *alias_id) +{ + struct regulator_supply_alias_match *match; + int ret; + + match = devres_alloc(devm_regulator_destroy_supply_alias, + sizeof(struct regulator_supply_alias_match), + GFP_KERNEL); + if (!match) + return -ENOMEM; + + match->dev = dev; + match->id = id; + + ret = regulator_register_supply_alias(dev, id, alias_dev, alias_id); + if (ret < 0) { + devres_free(match); + return ret; + } + + devres_add(dev, match); + + return 0; +} +EXPORT_SYMBOL_GPL(devm_regulator_register_supply_alias); + +/** + * devm_regulator_unregister_supply_alias - Resource managed + * regulator_unregister_supply_alias() + * + * @dev: device that will be given as the regulator "consumer" + * @id: Supply name or regulator ID + * + * Unregister an alias registered with + * devm_regulator_register_supply_alias(). Normally this function + * will not need to be called and the resource management code + * will ensure that the resource is freed. + */ +void devm_regulator_unregister_supply_alias(struct device *dev, const char *id) +{ + struct regulator_supply_alias_match match; + int rc; + + match.dev = dev; + match.id = id; + + rc = devres_release(dev, devm_regulator_destroy_supply_alias, + devm_regulator_match_supply_alias, &match); + if (rc != 0) + WARN_ON(rc); +} +EXPORT_SYMBOL_GPL(devm_regulator_unregister_supply_alias); + +/** + * devm_regulator_bulk_register_supply_alias - Managed register + * multiple aliases + * + * @dev: device that will be given as the regulator "consumer" + * @id: List of supply names or regulator IDs + * @alias_dev: device that should be used to lookup the supply + * @alias_id: List of supply names or regulator IDs that should be used to + * lookup the supply + * @num_id: Number of aliases to register + * + * @return 0 on success, an errno on failure. + * + * This helper function allows drivers to register several supply + * aliases in one operation, the aliases will be automatically + * unregisters when the source device is unbound. If any of the + * aliases cannot be registered any aliases that were registered + * will be removed before returning to the caller. + */ +int devm_regulator_bulk_register_supply_alias(struct device *dev, + const char **id, + struct device *alias_dev, + const char **alias_id, + int num_id) +{ + int i; + int ret; + + for (i = 0; i < num_id; ++i) { + ret = devm_regulator_register_supply_alias(dev, id[i], + alias_dev, + alias_id[i]); + if (ret < 0) + goto err; + } + + return 0; + +err: + dev_err(dev, + "Failed to create supply alias %s,%s -> %s,%s\n", + id[i], dev_name(dev), alias_id[i], dev_name(alias_dev)); + + while (--i >= 0) + devm_regulator_unregister_supply_alias(dev, id[i]); + + return ret; +} +EXPORT_SYMBOL_GPL(devm_regulator_bulk_register_supply_alias); + +/** + * devm_regulator_bulk_unregister_supply_alias - Managed unregister + * multiple aliases + * + * @dev: device that will be given as the regulator "consumer" + * @id: List of supply names or regulator IDs + * @num_id: Number of aliases to unregister + * + * Unregister aliases registered with + * devm_regulator_bulk_register_supply_alias(). Normally this function + * will not need to be called and the resource management code + * will ensure that the resource is freed. + */ +void devm_regulator_bulk_unregister_supply_alias(struct device *dev, + const char **id, + int num_id) +{ + int i; + + for (i = 0; i < num_id; ++i) + devm_regulator_unregister_supply_alias(dev, id[i]); +} +EXPORT_SYMBOL_GPL(devm_regulator_bulk_unregister_supply_alias); diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index 27be915..e530681 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -146,6 +146,32 @@ struct regulator *__must_check devm_regulator_get_optional(struct device *dev, void regulator_put(struct regulator *regulator); void devm_regulator_put(struct regulator *regulator); +int regulator_register_supply_alias(struct device *dev, const char *id, + struct device *alias_dev, + const char *alias_id); +void regulator_unregister_supply_alias(struct device *dev, const char *id); + +int regulator_bulk_register_supply_alias(struct device *dev, const char **id, + struct device *alias_dev, + const char **alias_id, int num_id); +void regulator_bulk_unregister_supply_alias(struct device *dev, + const char **id, int num_id); + +int devm_regulator_register_supply_alias(struct device *dev, const char *id, + struct device *alias_dev, + const char *alias_id); +void devm_regulator_unregister_supply_alias(struct device *dev, + const char *id); + +int devm_regulator_bulk_register_supply_alias(struct device *dev, + const char **id, + struct device *alias_dev, + const char **alias_id, + int num_id); +void devm_regulator_bulk_unregister_supply_alias(struct device *dev, + const char **id, + int num_id); + /* regulator output control and status */ int __must_check regulator_enable(struct regulator *regulator); int regulator_disable(struct regulator *regulator); @@ -250,6 +276,59 @@ static inline void devm_regulator_put(struct regulator *regulator) { } +static inline int regulator_register_supply_alias(struct device *dev, + const char *id, + struct device *alias_dev, + const char *alias_id) +{ + return 0; +} + +static inline void regulator_unregister_supply_alias(struct device *dev, + const char *id) +{ +} + +static inline int regulator_bulk_register_supply_alias(struct device *dev, + const char **id, + struct device *alias_dev, + const char **alias_id, + int num_id) +{ + return 0; +} + +static inline void regulator_bulk_unregister_supply_alias(struct device *dev, + const char **id, + int num_id) +{ +} + +static inline int devm_regulator_register_supply_alias(struct device *dev, + const char *id, + struct device *alias_dev, + const char *alias_id) +{ + return 0; +} + +static inline void devm_regulator_unregister_supply_alias(struct device *dev, + const char *id) +{ +} + +static inline int devm_regulator_bulk_register_supply_alias( + struct device *dev, const char **id, struct device *alias_dev, + const char **alias_id, int num_id) +{ + return 0; +} + +static inline void devm_regulator_bulk_unregister_supply_alias( + struct device *dev, const char **id, int num_id) +{ +} + static inline int regulator_enable(struct regulator *regulator) { return 0; -- cgit v0.10.2 From 58f46e41c1925236a1c34873caa5d1247f846005 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 1 Oct 2013 13:14:25 -0700 Subject: spi: spi-mxs: Always set LOCK_CS There are two bits which control the CS line in the CTRL0 register: LOCK_CS and IGNORE_CRC. The latter would be better named DEASSERT_CS in SPI mode. LOCK_CS keeps CS asserted though the entire transfer. This should always be set. The DMA code will always set it, explicitly on the first segment of the first transfer, and then implicitly on all the rest by never clearing the bit from the value read from the ctrl0 register. The PIO code will explicitly set it for the first transfer, leave it set for intermediate transfers, and then clear it for the final transfer. It should not clear it. The only reason to not set LOCK_CS would be to attempt an altered protocol where CS pulses between each word. Though don't get your hopes up if you want to do this, as the hardware doesn't appear to do this in any sane manner. It appears to be related to the hardware FIFO fill level. The code can be simplified by just setting LOCK_CS once and then not needing to deal with it at all in the PIO and DMA transfer functions. Signed-off-by: Trent Piepho Cc: Marek Vasut Cc: Fabio Estevam Cc: Shawn Guo Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index de7b114..e6172ae 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -79,6 +79,8 @@ static int mxs_spi_setup_transfer(struct spi_device *dev, mxs_ssp_set_clk_rate(ssp, hz); + writel(BM_SSP_CTRL0_LOCK_CS, + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); writel(BF_SSP_CTRL1_SSP_MODE(BV_SSP_CTRL1_SSP_MODE__SPI) | BF_SSP_CTRL1_WORD_LENGTH (BV_SSP_CTRL1_WORD_LENGTH__EIGHT_BITS) | @@ -147,8 +149,6 @@ static inline void mxs_spi_enable(struct mxs_spi *spi) { struct mxs_ssp *ssp = &spi->ssp; - writel(BM_SSP_CTRL0_LOCK_CS, - ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); writel(BM_SSP_CTRL0_IGNORE_CRC, ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); } @@ -157,8 +157,6 @@ static inline void mxs_spi_disable(struct mxs_spi *spi) { struct mxs_ssp *ssp = &spi->ssp; - writel(BM_SSP_CTRL0_LOCK_CS, - ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); writel(BM_SSP_CTRL0_IGNORE_CRC, ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); } @@ -232,8 +230,6 @@ static int mxs_spi_txrx_dma(struct mxs_spi *spi, int cs, ctrl0 &= ~BM_SSP_CTRL0_XFER_COUNT; ctrl0 |= BM_SSP_CTRL0_DATA_XFER | mxs_spi_cs_to_reg(cs); - if (*first) - ctrl0 |= BM_SSP_CTRL0_LOCK_CS; if (!write) ctrl0 |= BM_SSP_CTRL0_READ; -- cgit v0.10.2 From f5bc7384dc4e9bf3bcf976ef0c1ac9704fa1ad43 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 1 Oct 2013 13:14:32 -0700 Subject: spi: spi-mxs: Remove mxs_spi_enable and mxs_spi_disable These functions consist of nothing but one single writel call and are only called once. And the names really aren't accurate or clear, since they don't enable or disble SPI. Rather they set the bit that controls the state of CS at the end of transfer. It easier to follow the code to just set this bit with a writel() along with all the other bits being set in the same function. Signed-off-by: Trent Piepho Cc: Marek Vasut Cc: Fabio Estevam Cc: Shawn Guo Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index e6172ae..991ee01 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -145,22 +145,6 @@ static void mxs_spi_set_cs(struct mxs_spi *spi, unsigned cs) writel(select, ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); } -static inline void mxs_spi_enable(struct mxs_spi *spi) -{ - struct mxs_ssp *ssp = &spi->ssp; - - writel(BM_SSP_CTRL0_IGNORE_CRC, - ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); -} - -static inline void mxs_spi_disable(struct mxs_spi *spi) -{ - struct mxs_ssp *ssp = &spi->ssp; - - writel(BM_SSP_CTRL0_IGNORE_CRC, - ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); -} - static int mxs_ssp_wait(struct mxs_spi *spi, int offset, int mask, bool set) { const unsigned long timeout = jiffies + msecs_to_jiffies(SSP_TIMEOUT); @@ -335,13 +319,15 @@ static int mxs_spi_txrx_pio(struct mxs_spi *spi, int cs, struct mxs_ssp *ssp = &spi->ssp; if (*first) - mxs_spi_enable(spi); + writel(BM_SSP_CTRL0_IGNORE_CRC, + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); mxs_spi_set_cs(spi, cs); while (len--) { if (*last && len == 0) - mxs_spi_disable(spi); + writel(BM_SSP_CTRL0_IGNORE_CRC, + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); if (ssp->devid == IMX23_SSP) { writel(BM_SSP_CTRL0_XFER_COUNT, -- cgit v0.10.2 From 75e73fa24882fb76e8ef89226893728ed0f78870 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 1 Oct 2013 13:14:39 -0700 Subject: spi: spi-mxs: Always clear INGORE_CRC, to keep CS asserted INGORE_CRC, better named DEASSERT_CS, should be cleared on all tranfers except the last. So instead of only clearing it on the first transfer, we can just always clear it. It will set on the last transfer. This removes the only use of the "first" flag in the transfer functions, so that flag can be then be removed. Signed-off-by: Trent Piepho Cc: Marek Vasut Cc: Fabio Estevam Cc: Shawn Guo Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 991ee01..e2a9cc2 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -318,9 +318,8 @@ static int mxs_spi_txrx_pio(struct mxs_spi *spi, int cs, { struct mxs_ssp *ssp = &spi->ssp; - if (*first) - writel(BM_SSP_CTRL0_IGNORE_CRC, - ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); + writel(BM_SSP_CTRL0_IGNORE_CRC, + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); mxs_spi_set_cs(spi, cs); -- cgit v0.10.2 From 28cad125881cb10172895774d4c3a04e748bf6bf Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 1 Oct 2013 13:14:50 -0700 Subject: spi: spi-mxs: Change flag arguments in txrx functions to bit flags There are three flag arguments to the PIO and DMA txrx functions. Two are passed as pointers to integers, even though they are input only and not modified, which makes no sense to do. The third is passed as an integer. The compiler must use an argument register or stack variable for each flag this way. Using bitflags in a single flag argument is more efficient and produces smaller code, since all the flags can fit in a single register. And all the flag arguments get cumbersome, especially when more are added for things like GPIO chipselects. The "first" flag is never used, so can just be deleted. The "last" flag is renamed to DEASSERT_CS, since that's really what it does. The spi_transfer cs_change flag means that CS might be de-asserted on a transfer which is not last and not de-assert on the last transfer, so it is not which transfer is the last we need to know but rather the transfers after which CS should be de-asserted. This also extends the driver to not ignore cs_change when setting the DEASSERT_CS nee "last" flag. Signed-off-by: Trent Piepho Cc: Marek Vasut Cc: Fabio Estevam Cc: Shawn Guo Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index e2a9cc2..090930a 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -57,6 +57,13 @@ #define SG_MAXLEN 0xff00 +/* + * Flags for txrx functions. More efficient that using an argument register for + * each one. + */ +#define TXRX_WRITE (1<<0) /* This is a write */ +#define TXRX_DEASSERT_CS (1<<1) /* De-assert CS at end of txrx */ + struct mxs_spi { struct mxs_ssp ssp; struct completion c; @@ -184,7 +191,7 @@ static irqreturn_t mxs_ssp_irq_handler(int irq, void *dev_id) static int mxs_spi_txrx_dma(struct mxs_spi *spi, int cs, unsigned char *buf, int len, - int *first, int *last, int write) + unsigned int flags) { struct mxs_ssp *ssp = &spi->ssp; struct dma_async_tx_descriptor *desc = NULL; @@ -214,15 +221,19 @@ static int mxs_spi_txrx_dma(struct mxs_spi *spi, int cs, ctrl0 &= ~BM_SSP_CTRL0_XFER_COUNT; ctrl0 |= BM_SSP_CTRL0_DATA_XFER | mxs_spi_cs_to_reg(cs); - if (!write) + if (!(flags & TXRX_WRITE)) ctrl0 |= BM_SSP_CTRL0_READ; /* Queue the DMA data transfer. */ for (sg_count = 0; sg_count < sgs; sg_count++) { + /* Prepare the transfer descriptor. */ min = min(len, desc_len); - /* Prepare the transfer descriptor. */ - if ((sg_count + 1 == sgs) && *last) + /* + * De-assert CS on last segment if flag is set (i.e., no more + * transfers will follow) + */ + if ((sg_count + 1 == sgs) && (flags & TXRX_DEASSERT_CS)) ctrl0 |= BM_SSP_CTRL0_IGNORE_CRC; if (ssp->devid == IMX23_SSP) { @@ -247,7 +258,7 @@ static int mxs_spi_txrx_dma(struct mxs_spi *spi, int cs, sg_init_one(&dma_xfer[sg_count].sg, sg_buf, min); ret = dma_map_sg(ssp->dev, &dma_xfer[sg_count].sg, 1, - write ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + (flags & TXRX_WRITE) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); len -= min; buf += min; @@ -267,7 +278,7 @@ static int mxs_spi_txrx_dma(struct mxs_spi *spi, int cs, desc = dmaengine_prep_slave_sg(ssp->dmach, &dma_xfer[sg_count].sg, 1, - write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, + (flags & TXRX_WRITE) ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { @@ -304,7 +315,7 @@ err_vmalloc: while (--sg_count >= 0) { err_mapped: dma_unmap_sg(ssp->dev, &dma_xfer[sg_count].sg, 1, - write ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + (flags & TXRX_WRITE) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); } kfree(dma_xfer); @@ -314,7 +325,7 @@ err_mapped: static int mxs_spi_txrx_pio(struct mxs_spi *spi, int cs, unsigned char *buf, int len, - int *first, int *last, int write) + unsigned int flags) { struct mxs_ssp *ssp = &spi->ssp; @@ -324,7 +335,7 @@ static int mxs_spi_txrx_pio(struct mxs_spi *spi, int cs, mxs_spi_set_cs(spi, cs); while (len--) { - if (*last && len == 0) + if (len == 0 && (flags & TXRX_DEASSERT_CS)) writel(BM_SSP_CTRL0_IGNORE_CRC, ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); @@ -337,7 +348,7 @@ static int mxs_spi_txrx_pio(struct mxs_spi *spi, int cs, writel(1, ssp->base + HW_SSP_XFER_SIZE); } - if (write) + if (flags & TXRX_WRITE) writel(BM_SSP_CTRL0_READ, ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); else @@ -350,13 +361,13 @@ static int mxs_spi_txrx_pio(struct mxs_spi *spi, int cs, if (mxs_ssp_wait(spi, HW_SSP_CTRL0, BM_SSP_CTRL0_RUN, 1)) return -ETIMEDOUT; - if (write) + if (flags & TXRX_WRITE) writel(*buf, ssp->base + HW_SSP_DATA(ssp)); writel(BM_SSP_CTRL0_DATA_XFER, ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); - if (!write) { + if (!(flags & TXRX_WRITE)) { if (mxs_ssp_wait(spi, HW_SSP_STATUS(ssp), BM_SSP_STATUS_FIFO_EMPTY, 0)) return -ETIMEDOUT; @@ -381,13 +392,11 @@ static int mxs_spi_transfer_one(struct spi_master *master, { struct mxs_spi *spi = spi_master_get_devdata(master); struct mxs_ssp *ssp = &spi->ssp; - int first, last; struct spi_transfer *t, *tmp_t; + unsigned int flag; int status = 0; int cs; - first = last = 0; - cs = m->spi->chip_select; list_for_each_entry_safe(t, tmp_t, &m->transfers, transfer_list) { @@ -396,10 +405,9 @@ static int mxs_spi_transfer_one(struct spi_master *master, if (status) break; - if (&t->transfer_list == m->transfers.next) - first = 1; - if (&t->transfer_list == m->transfers.prev) - last = 1; + /* De-assert on last transfer, inverted by cs_change flag */ + flag = (&t->transfer_list == m->transfers.prev) ^ t->cs_change ? + TXRX_DEASSERT_CS : 0; if ((t->rx_buf && t->tx_buf) || (t->rx_dma && t->tx_dma)) { dev_err(ssp->dev, "Cannot send and receive simultaneously\n"); @@ -424,11 +432,11 @@ static int mxs_spi_transfer_one(struct spi_master *master, if (t->tx_buf) status = mxs_spi_txrx_pio(spi, cs, (void *)t->tx_buf, - t->len, &first, &last, 1); + t->len, flag | TXRX_WRITE); if (t->rx_buf) status = mxs_spi_txrx_pio(spi, cs, t->rx_buf, t->len, - &first, &last, 0); + flag); } else { writel(BM_SSP_CTRL1_DMA_ENABLE, ssp->base + HW_SSP_CTRL1(ssp) + @@ -437,11 +445,11 @@ static int mxs_spi_transfer_one(struct spi_master *master, if (t->tx_buf) status = mxs_spi_txrx_dma(spi, cs, (void *)t->tx_buf, t->len, - &first, &last, 1); + flag | TXRX_WRITE); if (t->rx_buf) status = mxs_spi_txrx_dma(spi, cs, t->rx_buf, t->len, - &first, &last, 0); + flag); } if (status) { @@ -450,7 +458,6 @@ static int mxs_spi_transfer_one(struct spi_master *master, } m->actual_length += t->len; - first = last = 0; } m->status = status; -- cgit v0.10.2 From df23286e57ceefe427d7ff925193283a8fafe9f3 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 1 Oct 2013 13:14:57 -0700 Subject: spi: spi-mxs: Fix extra CS pulses and read mode in multi-transfer messages There are two bits which control the CS line in the CTRL0 register: LOCK_CS and IGNORE_CRC. The latter would be better named DEASSERT_CS in SPI mode. Setting DEASSERT_CS causes CS to be de-asserted at the end of the transfer. It should normally be set only for the final segment of the final transfer. The DMA code explicitly sets it in this case, but because it never clears the bit from the ctrl0 register, it will remain set for all transfers in subsequent messages. This results in a CS pulse between transfers. There is a similar problem with the read mode bit never being cleared in DMA mode. This patch fixes DEASSERT_CS and READ being left on in DMA mode. Signed-off-by: Trent Piepho Cc: Marek Vasut Cc: Fabio Estevam Cc: Shawn Guo Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 090930a..68ea507 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -218,7 +218,8 @@ static int mxs_spi_txrx_dma(struct mxs_spi *spi, int cs, INIT_COMPLETION(spi->c); ctrl0 = readl(ssp->base + HW_SSP_CTRL0); - ctrl0 &= ~BM_SSP_CTRL0_XFER_COUNT; + ctrl0 &= ~(BM_SSP_CTRL0_XFER_COUNT | BM_SSP_CTRL0_IGNORE_CRC | + BM_SSP_CTRL0_READ); ctrl0 |= BM_SSP_CTRL0_DATA_XFER | mxs_spi_cs_to_reg(cs); if (!(flags & TXRX_WRITE)) -- cgit v0.10.2 From 0b782f70b51b9e611a69b9d4533b44d66b2e3e75 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 1 Oct 2013 13:15:04 -0700 Subject: spi: spi-mxs: Fix chip select control bits in DMA mode In DMA mode the chip select control bits would be ORed into the CTRL0 register without first clearing the bits. This means that after addressing slave 1, the CTRL0 bit to address slave 1 would be still be set when addressing slave 0, resulting in slave 1 continuing to be addressed. The message handling function would pass the CS value to the txrx function, which would re-program the bits on each transfer in the message. The selected CS does not change during a message so this is inefficient. It also means there are two different sets of code for selecting the CS, one for PIO that worked and one for DMA that didn't. Change the code to set the CS bits in the message handling function once. Now the DMA and PIO txrx functions don't need to care about CS at all. Signed-off-by: Trent Piepho Cc: Marek Vasut Cc: Fabio Estevam Cc: Shawn Guo Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 68ea507..a9a273e 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -140,18 +140,6 @@ static uint32_t mxs_spi_cs_to_reg(unsigned cs) return select; } -static void mxs_spi_set_cs(struct mxs_spi *spi, unsigned cs) -{ - const uint32_t mask = - BM_SSP_CTRL0_WAIT_FOR_CMD | BM_SSP_CTRL0_WAIT_FOR_IRQ; - uint32_t select; - struct mxs_ssp *ssp = &spi->ssp; - - writel(mask, ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); - select = mxs_spi_cs_to_reg(cs); - writel(select, ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); -} - static int mxs_ssp_wait(struct mxs_spi *spi, int offset, int mask, bool set) { const unsigned long timeout = jiffies + msecs_to_jiffies(SSP_TIMEOUT); @@ -189,7 +177,7 @@ static irqreturn_t mxs_ssp_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } -static int mxs_spi_txrx_dma(struct mxs_spi *spi, int cs, +static int mxs_spi_txrx_dma(struct mxs_spi *spi, unsigned char *buf, int len, unsigned int flags) { @@ -217,10 +205,11 @@ static int mxs_spi_txrx_dma(struct mxs_spi *spi, int cs, INIT_COMPLETION(spi->c); + /* Chip select was already programmed into CTRL0 */ ctrl0 = readl(ssp->base + HW_SSP_CTRL0); ctrl0 &= ~(BM_SSP_CTRL0_XFER_COUNT | BM_SSP_CTRL0_IGNORE_CRC | BM_SSP_CTRL0_READ); - ctrl0 |= BM_SSP_CTRL0_DATA_XFER | mxs_spi_cs_to_reg(cs); + ctrl0 |= BM_SSP_CTRL0_DATA_XFER; if (!(flags & TXRX_WRITE)) ctrl0 |= BM_SSP_CTRL0_READ; @@ -324,7 +313,7 @@ err_mapped: return ret; } -static int mxs_spi_txrx_pio(struct mxs_spi *spi, int cs, +static int mxs_spi_txrx_pio(struct mxs_spi *spi, unsigned char *buf, int len, unsigned int flags) { @@ -333,8 +322,6 @@ static int mxs_spi_txrx_pio(struct mxs_spi *spi, int cs, writel(BM_SSP_CTRL0_IGNORE_CRC, ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); - mxs_spi_set_cs(spi, cs); - while (len--) { if (len == 0 && (flags & TXRX_DEASSERT_CS)) writel(BM_SSP_CTRL0_IGNORE_CRC, @@ -396,9 +383,12 @@ static int mxs_spi_transfer_one(struct spi_master *master, struct spi_transfer *t, *tmp_t; unsigned int flag; int status = 0; - int cs; - cs = m->spi->chip_select; + /* Program CS register bits here, it will be used for all transfers. */ + writel(BM_SSP_CTRL0_WAIT_FOR_CMD | BM_SSP_CTRL0_WAIT_FOR_IRQ, + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); + writel(mxs_spi_cs_to_reg(m->spi->chip_select), + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); list_for_each_entry_safe(t, tmp_t, &m->transfers, transfer_list) { @@ -431,11 +421,11 @@ static int mxs_spi_transfer_one(struct spi_master *master, STMP_OFFSET_REG_CLR); if (t->tx_buf) - status = mxs_spi_txrx_pio(spi, cs, + status = mxs_spi_txrx_pio(spi, (void *)t->tx_buf, t->len, flag | TXRX_WRITE); if (t->rx_buf) - status = mxs_spi_txrx_pio(spi, cs, + status = mxs_spi_txrx_pio(spi, t->rx_buf, t->len, flag); } else { @@ -444,11 +434,11 @@ static int mxs_spi_transfer_one(struct spi_master *master, STMP_OFFSET_REG_SET); if (t->tx_buf) - status = mxs_spi_txrx_dma(spi, cs, + status = mxs_spi_txrx_dma(spi, (void *)t->tx_buf, t->len, flag | TXRX_WRITE); if (t->rx_buf) - status = mxs_spi_txrx_dma(spi, cs, + status = mxs_spi_txrx_dma(spi, t->rx_buf, t->len, flag); } -- cgit v0.10.2 From 210f65fedf1d26d0a1d604fa82425018f7ad6090 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 1 Oct 2013 13:15:11 -0700 Subject: spi: spi-mxs: Remove full duplex check, spi core already does it Because the driver sets the SPI_MASTER_HALF_DUPLEX flag, the spi core will check transfers to insure they are not full duplex. It's not necessary to check that in the spi-mxs driver as well. Signed-off-by: Trent Piepho Cc: Marek Vasut Cc: Fabio Estevam Cc: Shawn Guo Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index a9a273e..de7387e 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -399,12 +399,6 @@ static int mxs_spi_transfer_one(struct spi_master *master, /* De-assert on last transfer, inverted by cs_change flag */ flag = (&t->transfer_list == m->transfers.prev) ^ t->cs_change ? TXRX_DEASSERT_CS : 0; - if ((t->rx_buf && t->tx_buf) || (t->rx_dma && t->tx_dma)) { - dev_err(ssp->dev, - "Cannot send and receive simultaneously\n"); - status = -EINVAL; - break; - } /* * Small blocks can be transfered via PIO. -- cgit v0.10.2 From 1a33073fcf11e70447b704c0228e28099e6ab39d Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 1 Oct 2013 13:15:18 -0700 Subject: spi: spi-mxs: Remove bogus setting of ssp clk rate field The ssp struct has a clock rate field, to provide the actual value, in Hz, of the SSP output clock (the rate of SSP_SCK) after mxs_ssp_set_clk_rate() is called. It is set by mxs_ssp_set_clk_rate(), for SSP using drivers (like SPI and MMC) to *read* if they want to know the actual clock rate. The SPI driver isn't supposed to *write* to it. For some reason the spi-mxs driver decides to write to this field on init, and sets it to the value of the SSP input clock (clk_sspN, from the MXS clocking block) in kHz. It shouldn't be setting the value, and certainly shouldn't be setting it with the wrong clock in the wrong units. Signed-off-by: Trent Piepho Cc: Marek Vasut Cc: Fabio Estevam Cc: Shawn Guo Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index de7387e..8cb5d8b 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -536,7 +536,6 @@ static int mxs_spi_probe(struct platform_device *pdev) goto out_dma_release; clk_set_rate(ssp->clk, clk_freq); - ssp->clk_rate = clk_get_rate(ssp->clk) / 1000; ret = stmp_reset_block(ssp->base); if (ret) -- cgit v0.10.2 From 9c97e3421fa0aeec131689008181f8ee1dac5e99 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 1 Oct 2013 13:15:25 -0700 Subject: spi: spi-mxs: Fix race in setup method Despite many warnings in the SPI documentation and code, the spi-mxs driver sets shared chip registers in the ->setup method. This method can be called when transfers are in progress on other slaves controlled by the master. Setting registers or any other shared state will corrupt those transfers. So fix mxs_spi_setup() to not call mxs_spi_setup_transfer(). mxs_spi_setup_transfer() is already called for each transfer when they are actually performed in mxs_spi_transfer_one(), so the call in mxs_spi_setup() isn't necessary to setup anything. Signed-off-by: Trent Piepho Cc: Marek Vasut Cc: Fabio Estevam Cc: Shawn Guo Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 8cb5d8b..e9cbdb6 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -103,21 +103,13 @@ static int mxs_spi_setup_transfer(struct spi_device *dev, static int mxs_spi_setup(struct spi_device *dev) { - int err = 0; - if (!dev->bits_per_word) dev->bits_per_word = 8; if (dev->mode & ~(SPI_CPOL | SPI_CPHA)) return -EINVAL; - err = mxs_spi_setup_transfer(dev, NULL); - if (err) { - dev_err(&dev->dev, - "Failed to setup transfer, error = %d\n", err); - } - - return err; + return 0; } static uint32_t mxs_spi_cs_to_reg(unsigned cs) -- cgit v0.10.2 From d426eadb1ef166b472b09a223c30d3104fde2586 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 1 Oct 2013 13:15:33 -0700 Subject: spi: spi-mxs: Remove check of spi mode bits The spi core already checks for a slave setting mode bits that we didn't list as supported when the master was registered. There is no need to do it again in the master driver. Signed-off-by: Trent Piepho Cc: Marek Vasut Cc: Fabio Estevam Cc: Shawn Guo Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index e9cbdb6..23cc412 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -106,9 +106,6 @@ static int mxs_spi_setup(struct spi_device *dev) if (!dev->bits_per_word) dev->bits_per_word = 8; - if (dev->mode & ~(SPI_CPOL | SPI_CPHA)) - return -EINVAL; - return 0; } -- cgit v0.10.2 From aa9e0c6feb3e06463b45b0d9734a2cdbf95c4431 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 1 Oct 2013 13:15:40 -0700 Subject: spi: spi-mxs: Clean up setup_transfer function It can't be called with a NULL transfer anymore so it can be simplified to not check for that. Fix indention of line-wrapped code to Linux standard. The transfer pointer can be const. It's not necessary to check if the spi_transfer's speed_hz is zero, as the spi core also fills it in from the spi_device. However, the spi core does not check if spi_device's speed is zero so we have to do that still. Signed-off-by: Trent Piepho Cc: Marek Vasut Cc: Fabio Estevam Cc: Shawn Guo Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 23cc412..9e6101a 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -70,17 +70,14 @@ struct mxs_spi { }; static int mxs_spi_setup_transfer(struct spi_device *dev, - struct spi_transfer *t) + const struct spi_transfer *t) { struct mxs_spi *spi = spi_master_get_devdata(dev->master); struct mxs_ssp *ssp = &spi->ssp; - uint32_t hz = 0; + const unsigned int hz = min(dev->max_speed_hz, t->speed_hz); - hz = dev->max_speed_hz; - if (t && t->speed_hz) - hz = min(hz, t->speed_hz); if (hz == 0) { - dev_err(&dev->dev, "Cannot continue with zero clock\n"); + dev_err(&dev->dev, "SPI clock rate of zero not allowed\n"); return -EINVAL; } @@ -88,12 +85,12 @@ static int mxs_spi_setup_transfer(struct spi_device *dev, writel(BM_SSP_CTRL0_LOCK_CS, ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); + writel(BF_SSP_CTRL1_SSP_MODE(BV_SSP_CTRL1_SSP_MODE__SPI) | - BF_SSP_CTRL1_WORD_LENGTH - (BV_SSP_CTRL1_WORD_LENGTH__EIGHT_BITS) | - ((dev->mode & SPI_CPOL) ? BM_SSP_CTRL1_POLARITY : 0) | - ((dev->mode & SPI_CPHA) ? BM_SSP_CTRL1_PHASE : 0), - ssp->base + HW_SSP_CTRL1(ssp)); + BF_SSP_CTRL1_WORD_LENGTH(BV_SSP_CTRL1_WORD_LENGTH__EIGHT_BITS) | + ((dev->mode & SPI_CPOL) ? BM_SSP_CTRL1_POLARITY : 0) | + ((dev->mode & SPI_CPHA) ? BM_SSP_CTRL1_PHASE : 0), + ssp->base + HW_SSP_CTRL1(ssp)); writel(0x0, ssp->base + HW_SSP_CMD0); writel(0x0, ssp->base + HW_SSP_CMD1); -- cgit v0.10.2 From a560943ead2cbded93c30c9b04886f3c743d7f00 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 1 Oct 2013 13:15:47 -0700 Subject: spi: spi-mxs: Don't set clock for each xfer mxs_spi_setup_transfer() would set the SSP SCK rate every time it was called, which is before every transfer. It is uncommon for the SCK rate to change between transfers (or at all of that matter) and this causes many unnecessary reprogrammings of the clock registers. Code changed to only set the rate when it changes. This significantly speeds up short SPI messages, especially messages made up of many transfers, as the calculation of the clock divisors is rather costly. On an iMX287, using spidev with messages that consist of 511 transfers of 4 bytes each at an SCK of 48 MHz, the effective transfer rate more than doubles from about 290 KB/sec to 600 KB/sec! Signed-off-by: Trent Piepho Cc: Marek Vasut Cc: Fabio Estevam Cc: Shawn Guo Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 9e6101a..759de70 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -67,6 +67,7 @@ struct mxs_spi { struct mxs_ssp ssp; struct completion c; + unsigned int sck; /* Rate requested (vs actual) */ }; static int mxs_spi_setup_transfer(struct spi_device *dev, @@ -81,7 +82,19 @@ static int mxs_spi_setup_transfer(struct spi_device *dev, return -EINVAL; } - mxs_ssp_set_clk_rate(ssp, hz); + if (hz != spi->sck) { + mxs_ssp_set_clk_rate(ssp, hz); + /* + * Save requested rate, hz, rather than the actual rate, + * ssp->clk_rate. Otherwise we would set the rate every trasfer + * when the actual rate is not quite the same as requested rate. + */ + spi->sck = hz; + /* + * Perhaps we should return an error if the actual clock is + * nowhere close to what was requested? + */ + } writel(BM_SSP_CTRL0_LOCK_CS, ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); -- cgit v0.10.2 From 42e182f86222dee2679c19fb1ab5006354a3be62 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 1 Oct 2013 13:15:54 -0700 Subject: spi: spi-mxs: Use u32 instead of uint32_t It's consistent with all the other spi drivers that way. Signed-off-by: Trent Piepho Cc: Marek Vasut Cc: Fabio Estevam Cc: Shawn Guo Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 759de70..2a819f7 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -119,9 +119,9 @@ static int mxs_spi_setup(struct spi_device *dev) return 0; } -static uint32_t mxs_spi_cs_to_reg(unsigned cs) +static u32 mxs_spi_cs_to_reg(unsigned cs) { - uint32_t select = 0; + u32 select = 0; /* * i.MX28 Datasheet: 17.10.1: HW_SSP_CTRL0 @@ -143,7 +143,7 @@ static int mxs_ssp_wait(struct mxs_spi *spi, int offset, int mask, bool set) { const unsigned long timeout = jiffies + msecs_to_jiffies(SSP_TIMEOUT); struct mxs_ssp *ssp = &spi->ssp; - uint32_t reg; + u32 reg; do { reg = readl_relaxed(ssp->base + offset); @@ -187,11 +187,11 @@ static int mxs_spi_txrx_dma(struct mxs_spi *spi, const int sgs = DIV_ROUND_UP(len, desc_len); int sg_count; int min, ret; - uint32_t ctrl0; + u32 ctrl0; struct page *vm_page; void *sg_buf; struct { - uint32_t pio[4]; + u32 pio[4]; struct scatterlist sg; } *dma_xfer; -- cgit v0.10.2 From f1b4863a871d1ca2984bdaa8c88598690530a65c Mon Sep 17 00:00:00 2001 From: "Geyslan G. Bem" Date: Thu, 17 Oct 2013 19:57:12 -0300 Subject: ALSA: emu10k1: code refactoring Partially restructures _snd_emu10k1_audigy_init_efx() and _snd_emu10k1_init_efx() functions. Be noted that the cast is demanded to use '__user'. So, in these cases, avoid patches based on the coccinelle 'drop_kmalloc_cast' semantic patch. Signed-off-by: Geyslan G. Bem Signed-off-by: Takashi Iwai diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index 0275209..1f9c7c4 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -1182,15 +1182,20 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu) u32 *gpr_map; mm_segment_t seg; - if ((icode = kzalloc(sizeof(*icode), GFP_KERNEL)) == NULL || - (icode->gpr_map = (u_int32_t __user *) - kcalloc(512 + 256 + 256 + 2 * 1024, sizeof(u_int32_t), - GFP_KERNEL)) == NULL || - (controls = kcalloc(SND_EMU10K1_GPR_CONTROLS, - sizeof(*controls), GFP_KERNEL)) == NULL) { - err = -ENOMEM; - goto __err; - } + err = -ENOMEM; + icode = kzalloc(sizeof(*icode), GFP_KERNEL); + if (!icode) + return err; + + icode->gpr_map = (u_int32_t __user *) kcalloc(512 + 256 + 256 + 2 * 1024, + sizeof(u_int32_t), GFP_KERNEL); + if (!icode->gpr_map) + goto __err_gpr; + controls = kcalloc(SND_EMU10K1_GPR_CONTROLS, + sizeof(*controls), GFP_KERNEL); + if (!controls) + goto __err_ctrls; + gpr_map = (u32 __force *)icode->gpr_map; icode->tram_data_map = icode->gpr_map + 512; @@ -1741,12 +1746,12 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) emu->support_tlv = 0; /* clear again */ snd_leave_user(seg); - __err: +__err: kfree(controls); - if (icode != NULL) { - kfree((void __force *)icode->gpr_map); - kfree(icode); - } +__err_ctrls: + kfree((void __force *)icode->gpr_map); +__err_gpr: + kfree(icode); return err; } @@ -1813,18 +1818,26 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu) u32 *gpr_map; mm_segment_t seg; - if ((icode = kzalloc(sizeof(*icode), GFP_KERNEL)) == NULL) - return -ENOMEM; - if ((icode->gpr_map = (u_int32_t __user *) - kcalloc(256 + 160 + 160 + 2 * 512, sizeof(u_int32_t), - GFP_KERNEL)) == NULL || - (controls = kcalloc(SND_EMU10K1_GPR_CONTROLS, - sizeof(struct snd_emu10k1_fx8010_control_gpr), - GFP_KERNEL)) == NULL || - (ipcm = kzalloc(sizeof(*ipcm), GFP_KERNEL)) == NULL) { - err = -ENOMEM; - goto __err; - } + err = -ENOMEM; + icode = kzalloc(sizeof(*icode), GFP_KERNEL); + if (!icode) + return err; + + icode->gpr_map = (u_int32_t __user *) kcalloc(256 + 160 + 160 + 2 * 512, + sizeof(u_int32_t), GFP_KERNEL); + if (!icode->gpr_map) + goto __err_gpr; + + controls = kcalloc(SND_EMU10K1_GPR_CONTROLS, + sizeof(struct snd_emu10k1_fx8010_control_gpr), + GFP_KERNEL); + if (!controls) + goto __err_ctrls; + + ipcm = kzalloc(sizeof(*ipcm), GFP_KERNEL); + if (!ipcm) + goto __err_ipcm; + gpr_map = (u32 __force *)icode->gpr_map; icode->tram_data_map = icode->gpr_map + 256; @@ -2363,13 +2376,14 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu) snd_leave_user(seg); if (err >= 0) err = snd_emu10k1_ipcm_poke(emu, ipcm); - __err: +__err: kfree(ipcm); +__err_ipcm: kfree(controls); - if (icode != NULL) { - kfree((void __force *)icode->gpr_map); - kfree(icode); - } +__err_ctrls: + kfree((void __force *)icode->gpr_map); +__err_gpr: + kfree(icode); return err; } -- cgit v0.10.2 From ca50410b731c636b9750c02d5ae45be215056634 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 17 Oct 2013 14:56:13 +0100 Subject: ASoC: wm8962: Move interrupt initalisation to probe() This is more idiomatic and fixes bugs in the error handling paths. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 11d80f3..54379ee 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3377,7 +3377,7 @@ static int wm8962_probe(struct snd_soc_codec *codec) int ret; struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); struct wm8962_pdata *pdata = &wm8962->pdata; - int i, trigger, irq_pol; + int i; bool dmicclk, dmicdat; wm8962->codec = codec; @@ -3506,36 +3506,6 @@ static int wm8962_probe(struct snd_soc_codec *codec) wm8962_init_beep(codec); wm8962_init_gpio(codec); - if (wm8962->irq) { - if (pdata->irq_active_low) { - trigger = IRQF_TRIGGER_LOW; - irq_pol = WM8962_IRQ_POL; - } else { - trigger = IRQF_TRIGGER_HIGH; - irq_pol = 0; - } - - snd_soc_update_bits(codec, WM8962_INTERRUPT_CONTROL, - WM8962_IRQ_POL, irq_pol); - - ret = request_threaded_irq(wm8962->irq, NULL, wm8962_irq, - trigger | IRQF_ONESHOT, - "wm8962", codec->dev); - if (ret != 0) { - dev_err(codec->dev, "Failed to request IRQ %d: %d\n", - wm8962->irq, ret); - wm8962->irq = 0; - /* Non-fatal */ - } else { - /* Enable some IRQs by default */ - snd_soc_update_bits(codec, - WM8962_INTERRUPT_STATUS_2_MASK, - WM8962_FLL_LOCK_EINT | - WM8962_TEMP_SHUT_EINT | - WM8962_FIFOS_ERR_EINT, 0); - } - } - return 0; } @@ -3544,9 +3514,6 @@ static int wm8962_remove(struct snd_soc_codec *codec) struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); int i; - if (wm8962->irq) - free_irq(wm8962->irq, codec); - cancel_delayed_work_sync(&wm8962->mic_work); wm8962_free_gpio(codec); @@ -3619,7 +3586,7 @@ static int wm8962_i2c_probe(struct i2c_client *i2c, struct wm8962_pdata *pdata = dev_get_platdata(&i2c->dev); struct wm8962_priv *wm8962; unsigned int reg; - int ret, i; + int ret, i, irq_pol, trigger; wm8962 = devm_kzalloc(&i2c->dev, sizeof(struct wm8962_priv), GFP_KERNEL); @@ -3714,6 +3681,37 @@ static int wm8962_i2c_probe(struct i2c_client *i2c, ret); } + if (wm8962->irq) { + if (pdata->irq_active_low) { + trigger = IRQF_TRIGGER_LOW; + irq_pol = WM8962_IRQ_POL; + } else { + trigger = IRQF_TRIGGER_HIGH; + irq_pol = 0; + } + + regmap_update_bits(wm8962->regmap, WM8962_INTERRUPT_CONTROL, + WM8962_IRQ_POL, irq_pol); + + ret = devm_request_threaded_irq(&i2c->dev, wm8962->irq, NULL, + wm8962_irq, + trigger | IRQF_ONESHOT, + "wm8962", &i2c->dev); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request IRQ %d: %d\n", + wm8962->irq, ret); + wm8962->irq = 0; + /* Non-fatal */ + } else { + /* Enable some IRQs by default */ + regmap_update_bits(wm8962->regmap, + WM8962_INTERRUPT_STATUS_2_MASK, + WM8962_FLL_LOCK_EINT | + WM8962_TEMP_SHUT_EINT | + WM8962_FIFOS_ERR_EINT, 0); + } + } + pm_runtime_enable(&i2c->dev); pm_request_idle(&i2c->dev); -- cgit v0.10.2 From 78b78f5c019e5c68c88afad4b0d3070becde939e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 17 Oct 2013 15:04:21 +0100 Subject: ASoC: wm8962: Move register initialisation to I2C probe() This is more idiomatic and is required for robust operation since we must ensure that the clocking configuration is valid as rapidly as possible. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 54379ee..2bf9ee7 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3242,7 +3242,7 @@ static void wm8962_free_beep(struct snd_soc_codec *codec) } #endif -static void wm8962_set_gpio_mode(struct snd_soc_codec *codec, int gpio) +static void wm8962_set_gpio_mode(struct wm8962_priv *wm8962, int gpio) { int mask = 0; int val = 0; @@ -3263,8 +3263,8 @@ static void wm8962_set_gpio_mode(struct snd_soc_codec *codec, int gpio) } if (mask) - snd_soc_update_bits(codec, WM8962_ANALOGUE_CLOCKING1, - mask, val); + regmap_update_bits(wm8962->regmap, WM8962_ANALOGUE_CLOCKING1, + mask, val); } #ifdef CONFIG_GPIOLIB @@ -3276,7 +3276,6 @@ static inline struct wm8962_priv *gpio_to_wm8962(struct gpio_chip *chip) static int wm8962_gpio_request(struct gpio_chip *chip, unsigned offset) { struct wm8962_priv *wm8962 = gpio_to_wm8962(chip); - struct snd_soc_codec *codec = wm8962->codec; /* The WM8962 GPIOs aren't linearly numbered. For simplicity * we export linear numbers and error out if the unsupported @@ -3292,7 +3291,7 @@ static int wm8962_gpio_request(struct gpio_chip *chip, unsigned offset) return -EINVAL; } - wm8962_set_gpio_mode(codec, offset + 1); + wm8962_set_gpio_mode(wm8962, offset + 1); return 0; } @@ -3376,7 +3375,6 @@ static int wm8962_probe(struct snd_soc_codec *codec) { int ret; struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); - struct wm8962_pdata *pdata = &wm8962->pdata; int i; bool dmicclk, dmicdat; @@ -3409,75 +3407,6 @@ static int wm8962_probe(struct snd_soc_codec *codec) } } - /* SYSCLK defaults to on; make sure it is off so we can safely - * write to registers if the device is declocked. - */ - snd_soc_update_bits(codec, WM8962_CLOCKING2, WM8962_SYSCLK_ENA, 0); - - /* Ensure we have soft control over all registers */ - snd_soc_update_bits(codec, WM8962_CLOCKING2, - WM8962_CLKREG_OVD, WM8962_CLKREG_OVD); - - /* Ensure that the oscillator and PLLs are disabled */ - snd_soc_update_bits(codec, WM8962_PLL2, - WM8962_OSC_ENA | WM8962_PLL2_ENA | WM8962_PLL3_ENA, - 0); - - /* Apply static configuration for GPIOs */ - for (i = 0; i < ARRAY_SIZE(pdata->gpio_init); i++) - if (pdata->gpio_init[i]) { - wm8962_set_gpio_mode(codec, i + 1); - snd_soc_write(codec, 0x200 + i, - pdata->gpio_init[i] & 0xffff); - } - - - /* Put the speakers into mono mode? */ - if (pdata->spk_mono) - snd_soc_update_bits(codec, WM8962_CLASS_D_CONTROL_2, - WM8962_SPK_MONO_MASK, WM8962_SPK_MONO); - - /* Micbias setup, detection enable and detection - * threasholds. */ - if (pdata->mic_cfg) - snd_soc_update_bits(codec, WM8962_ADDITIONAL_CONTROL_4, - WM8962_MICDET_ENA | - WM8962_MICDET_THR_MASK | - WM8962_MICSHORT_THR_MASK | - WM8962_MICBIAS_LVL, - pdata->mic_cfg); - - /* Latch volume update bits */ - snd_soc_update_bits(codec, WM8962_LEFT_INPUT_VOLUME, - WM8962_IN_VU, WM8962_IN_VU); - snd_soc_update_bits(codec, WM8962_RIGHT_INPUT_VOLUME, - WM8962_IN_VU, WM8962_IN_VU); - snd_soc_update_bits(codec, WM8962_LEFT_ADC_VOLUME, - WM8962_ADC_VU, WM8962_ADC_VU); - snd_soc_update_bits(codec, WM8962_RIGHT_ADC_VOLUME, - WM8962_ADC_VU, WM8962_ADC_VU); - snd_soc_update_bits(codec, WM8962_LEFT_DAC_VOLUME, - WM8962_DAC_VU, WM8962_DAC_VU); - snd_soc_update_bits(codec, WM8962_RIGHT_DAC_VOLUME, - WM8962_DAC_VU, WM8962_DAC_VU); - snd_soc_update_bits(codec, WM8962_SPKOUTL_VOLUME, - WM8962_SPKOUT_VU, WM8962_SPKOUT_VU); - snd_soc_update_bits(codec, WM8962_SPKOUTR_VOLUME, - WM8962_SPKOUT_VU, WM8962_SPKOUT_VU); - snd_soc_update_bits(codec, WM8962_HPOUTL_VOLUME, - WM8962_HPOUT_VU, WM8962_HPOUT_VU); - snd_soc_update_bits(codec, WM8962_HPOUTR_VOLUME, - WM8962_HPOUT_VU, WM8962_HPOUT_VU); - - /* Stereo control for EQ */ - snd_soc_update_bits(codec, WM8962_EQ1, WM8962_EQ_SHARED_COEFF, 0); - - /* Don't debouce interrupts so we don't need SYSCLK */ - snd_soc_update_bits(codec, WM8962_IRQ_DEBOUNCE, - WM8962_FLL_LOCK_DB | WM8962_PLL3_LOCK_DB | - WM8962_PLL2_LOCK_DB | WM8962_TEMP_SHUT_DB, - 0); - wm8962_add_widgets(codec); /* Save boards having to disable DMIC when not in use */ @@ -3671,6 +3600,77 @@ static int wm8962_i2c_probe(struct i2c_client *i2c, goto err_enable; } + /* SYSCLK defaults to on; make sure it is off so we can safely + * write to registers if the device is declocked. + */ + regmap_update_bits(wm8962->regmap, WM8962_CLOCKING2, + WM8962_SYSCLK_ENA, 0); + + /* Ensure we have soft control over all registers */ + regmap_update_bits(wm8962->regmap, WM8962_CLOCKING2, + WM8962_CLKREG_OVD, WM8962_CLKREG_OVD); + + /* Ensure that the oscillator and PLLs are disabled */ + regmap_update_bits(wm8962->regmap, WM8962_PLL2, + WM8962_OSC_ENA | WM8962_PLL2_ENA | WM8962_PLL3_ENA, + 0); + + /* Apply static configuration for GPIOs */ + for (i = 0; i < ARRAY_SIZE(pdata->gpio_init); i++) + if (pdata->gpio_init[i]) { + wm8962_set_gpio_mode(wm8962, i + 1); + regmap_write(wm8962->regmap, 0x200 + i, + pdata->gpio_init[i] & 0xffff); + } + + + /* Put the speakers into mono mode? */ + if (pdata->spk_mono) + regmap_update_bits(wm8962->regmap, WM8962_CLASS_D_CONTROL_2, + WM8962_SPK_MONO_MASK, WM8962_SPK_MONO); + + /* Micbias setup, detection enable and detection + * threasholds. */ + if (pdata->mic_cfg) + regmap_update_bits(wm8962->regmap, WM8962_ADDITIONAL_CONTROL_4, + WM8962_MICDET_ENA | + WM8962_MICDET_THR_MASK | + WM8962_MICSHORT_THR_MASK | + WM8962_MICBIAS_LVL, + pdata->mic_cfg); + + /* Latch volume update bits */ + regmap_update_bits(wm8962->regmap, WM8962_LEFT_INPUT_VOLUME, + WM8962_IN_VU, WM8962_IN_VU); + regmap_update_bits(wm8962->regmap, WM8962_RIGHT_INPUT_VOLUME, + WM8962_IN_VU, WM8962_IN_VU); + regmap_update_bits(wm8962->regmap, WM8962_LEFT_ADC_VOLUME, + WM8962_ADC_VU, WM8962_ADC_VU); + regmap_update_bits(wm8962->regmap, WM8962_RIGHT_ADC_VOLUME, + WM8962_ADC_VU, WM8962_ADC_VU); + regmap_update_bits(wm8962->regmap, WM8962_LEFT_DAC_VOLUME, + WM8962_DAC_VU, WM8962_DAC_VU); + regmap_update_bits(wm8962->regmap, WM8962_RIGHT_DAC_VOLUME, + WM8962_DAC_VU, WM8962_DAC_VU); + regmap_update_bits(wm8962->regmap, WM8962_SPKOUTL_VOLUME, + WM8962_SPKOUT_VU, WM8962_SPKOUT_VU); + regmap_update_bits(wm8962->regmap, WM8962_SPKOUTR_VOLUME, + WM8962_SPKOUT_VU, WM8962_SPKOUT_VU); + regmap_update_bits(wm8962->regmap, WM8962_HPOUTL_VOLUME, + WM8962_HPOUT_VU, WM8962_HPOUT_VU); + regmap_update_bits(wm8962->regmap, WM8962_HPOUTR_VOLUME, + WM8962_HPOUT_VU, WM8962_HPOUT_VU); + + /* Stereo control for EQ */ + regmap_update_bits(wm8962->regmap, WM8962_EQ1, + WM8962_EQ_SHARED_COEFF, 0); + + /* Don't debouce interrupts so we don't need SYSCLK */ + regmap_update_bits(wm8962->regmap, WM8962_IRQ_DEBOUNCE, + WM8962_FLL_LOCK_DB | WM8962_PLL3_LOCK_DB | + WM8962_PLL2_LOCK_DB | WM8962_TEMP_SHUT_DB, + 0); + if (wm8962->pdata.in4_dc_measure) { ret = regmap_register_patch(wm8962->regmap, wm8962_dc_measure, -- cgit v0.10.2 From 40d54ec2f77edc52340dcae236aaabe8c3cc3a07 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 18 Oct 2013 15:28:58 +0300 Subject: perf evsel: Add missing 'mmap2' from debug print The struct perf_event_attr now has a 'mmap2' member. Add it to perf_event_attr__fprintf(). Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382099356-4918-2-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index bfebc1e..291b18a 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -986,6 +986,7 @@ static size_t perf_event_attr__fprintf(struct perf_event_attr *attr, FILE *fp) ret += PRINT_ATTR2(exclude_host, exclude_guest); ret += PRINT_ATTR2N("excl.callchain_kern", exclude_callchain_kernel, "excl.callchain_user", exclude_callchain_user); + ret += PRINT_ATTR_U32(mmap2); ret += PRINT_ATTR_U32(wakeup_events); ret += PRINT_ATTR_U32(wakeup_watermark); -- cgit v0.10.2 From f3643ac7ffd8bf9a3b0bac8e207529d01bebf269 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 4 Sep 2013 16:24:09 -0700 Subject: hwmon: (tmp401) Convert to use devm_hwmon_device_register_with_groups Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c index dfe6d95..7fa6e7d 100644 --- a/drivers/hwmon/tmp401.c +++ b/drivers/hwmon/tmp401.c @@ -155,7 +155,8 @@ MODULE_DEVICE_TABLE(i2c, tmp401_id); */ struct tmp401_data { - struct device *hwmon_dev; + struct i2c_client *client; + const struct attribute_group *groups[3]; struct mutex update_lock; char valid; /* zero until following fields are valid */ unsigned long last_updated; /* in jiffies */ @@ -231,8 +232,8 @@ static int tmp401_update_device_reg16(struct i2c_client *client, static struct tmp401_data *tmp401_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct tmp401_data *data = i2c_get_clientdata(client); + struct tmp401_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; struct tmp401_data *ret = data; int i, val; unsigned long next_update; @@ -350,15 +351,12 @@ static ssize_t store_temp(struct device *dev, struct device_attribute *devattr, { int nr = to_sensor_dev_attr_2(devattr)->nr; int index = to_sensor_dev_attr_2(devattr)->index; - struct i2c_client *client = to_i2c_client(dev); - struct tmp401_data *data = tmp401_update_device(dev); + struct tmp401_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; long val; u16 reg; u8 regaddr; - if (IS_ERR(data)) - return PTR_ERR(data); - if (kstrtol(buf, 10, &val)) return -EINVAL; @@ -405,7 +403,7 @@ static ssize_t store_temp_crit_hyst(struct device *dev, struct device_attribute val = clamp_val(val, temp - 255000, temp); reg = ((temp - val) + 500) / 1000; - i2c_smbus_write_byte_data(to_i2c_client(dev), TMP401_TEMP_CRIT_HYST, + i2c_smbus_write_byte_data(data->client, TMP401_TEMP_CRIT_HYST, reg); data->temp_crit_hyst = reg; @@ -423,8 +421,8 @@ static ssize_t store_temp_crit_hyst(struct device *dev, struct device_attribute static ssize_t reset_temp_history(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct tmp401_data *data = i2c_get_clientdata(client); + struct tmp401_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; long val; if (kstrtol(buf, 10, &val)) @@ -447,8 +445,7 @@ static ssize_t reset_temp_history(struct device *dev, static ssize_t show_update_interval(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct tmp401_data *data = i2c_get_clientdata(client); + struct tmp401_data *data = dev_get_drvdata(dev); return sprintf(buf, "%u\n", data->update_interval); } @@ -457,8 +454,8 @@ static ssize_t set_update_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct tmp401_data *data = i2c_get_clientdata(client); + struct tmp401_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned long val; int err, rate; @@ -616,10 +613,10 @@ static const struct attribute_group tmp432_group = { * Begin non sysfs callback code (aka Real code) */ -static void tmp401_init_client(struct i2c_client *client) +static void tmp401_init_client(struct tmp401_data *data, + struct i2c_client *client) { int config, config_orig; - struct tmp401_data *data = i2c_get_clientdata(client); /* Set the conversion rate to 2 Hz */ i2c_smbus_write_byte_data(client, TMP401_CONVERSION_RATE_WRITE, 5); @@ -705,77 +702,45 @@ static int tmp401_detect(struct i2c_client *client, return 0; } -static int tmp401_remove(struct i2c_client *client) -{ - struct device *dev = &client->dev; - struct tmp401_data *data = i2c_get_clientdata(client); - - if (data->hwmon_dev) - hwmon_device_unregister(data->hwmon_dev); - - sysfs_remove_group(&dev->kobj, &tmp401_group); - - if (data->kind == tmp411) - sysfs_remove_group(&dev->kobj, &tmp411_group); - - if (data->kind == tmp432) - sysfs_remove_group(&dev->kobj, &tmp432_group); - - return 0; -} - static int tmp401_probe(struct i2c_client *client, const struct i2c_device_id *id) { + const char *names[] = { "TMP401", "TMP411", "TMP431", "TMP432" }; struct device *dev = &client->dev; - int err; + struct device *hwmon_dev; struct tmp401_data *data; - const char *names[] = { "TMP401", "TMP411", "TMP431", "TMP432" }; + int groups = 0; data = devm_kzalloc(dev, sizeof(struct tmp401_data), GFP_KERNEL); if (!data) return -ENOMEM; - i2c_set_clientdata(client, data); + data->client = client; mutex_init(&data->update_lock); data->kind = id->driver_data; /* Initialize the TMP401 chip */ - tmp401_init_client(client); + tmp401_init_client(data, client); /* Register sysfs hooks */ - err = sysfs_create_group(&dev->kobj, &tmp401_group); - if (err) - return err; + data->groups[groups++] = &tmp401_group; /* Register additional tmp411 sysfs hooks */ - if (data->kind == tmp411) { - err = sysfs_create_group(&dev->kobj, &tmp411_group); - if (err) - goto exit_remove; - } + if (data->kind == tmp411) + data->groups[groups++] = &tmp411_group; /* Register additional tmp432 sysfs hooks */ - if (data->kind == tmp432) { - err = sysfs_create_group(&dev->kobj, &tmp432_group); - if (err) - goto exit_remove; - } + if (data->kind == tmp432) + data->groups[groups++] = &tmp432_group; - data->hwmon_dev = hwmon_device_register(dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - data->hwmon_dev = NULL; - goto exit_remove; - } + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, data->groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); dev_info(dev, "Detected TI %s chip\n", names[data->kind]); return 0; - -exit_remove: - tmp401_remove(client); - return err; } static struct i2c_driver tmp401_driver = { @@ -784,7 +749,6 @@ static struct i2c_driver tmp401_driver = { .name = "tmp401", }, .probe = tmp401_probe, - .remove = tmp401_remove, .id_table = tmp401_id, .detect = tmp401_detect, .address_list = normal_i2c, -- cgit v0.10.2 From 9d86bd6ba3ba2f254720e2916dfda64737974963 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 4 Sep 2013 16:39:12 -0700 Subject: hwmon: (lm95234) Convert to use devm_hwmon_device_register_with_groups Also use new macro ATTRIBUTE_GROUPS to declare attribute groups. Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c index 307c9ea..cf87507 100644 --- a/drivers/hwmon/lm95234.c +++ b/drivers/hwmon/lm95234.c @@ -57,7 +57,7 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4d, 0x4e, I2C_CLIENT_END }; /* Client data (each client gets its own) */ struct lm95234_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex update_lock; unsigned long last_updated, interval; /* in jiffies */ bool valid; /* false until following fields are valid */ @@ -114,9 +114,9 @@ static u16 update_intervals[] = { 143, 364, 1000, 2500 }; /* Fill value cache. Must be called with update lock held. */ -static int lm95234_fill_cache(struct i2c_client *client) +static int lm95234_fill_cache(struct lm95234_data *data, + struct i2c_client *client) { - struct lm95234_data *data = i2c_get_clientdata(client); int i, ret; ret = i2c_smbus_read_byte_data(client, LM95234_REG_CONVRATE); @@ -157,9 +157,9 @@ static int lm95234_fill_cache(struct i2c_client *client) return 0; } -static int lm95234_update_device(struct i2c_client *client, - struct lm95234_data *data) +static int lm95234_update_device(struct lm95234_data *data) { + struct i2c_client *client = data->client; int ret; mutex_lock(&data->update_lock); @@ -169,7 +169,7 @@ static int lm95234_update_device(struct i2c_client *client, int i; if (!data->valid) { - ret = lm95234_fill_cache(client); + ret = lm95234_fill_cache(data, client); if (ret < 0) goto abort; } @@ -209,10 +209,9 @@ abort: static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95234_data *data = i2c_get_clientdata(client); + struct lm95234_data *data = dev_get_drvdata(dev); int index = to_sensor_dev_attr(attr)->index; - int ret = lm95234_update_device(client, data); + int ret = lm95234_update_device(data); if (ret) return ret; @@ -224,10 +223,9 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *attr, static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95234_data *data = i2c_get_clientdata(client); + struct lm95234_data *data = dev_get_drvdata(dev); u32 mask = to_sensor_dev_attr(attr)->index; - int ret = lm95234_update_device(client, data); + int ret = lm95234_update_device(data); if (ret) return ret; @@ -238,10 +236,9 @@ static ssize_t show_alarm(struct device *dev, static ssize_t show_type(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95234_data *data = i2c_get_clientdata(client); + struct lm95234_data *data = dev_get_drvdata(dev); u8 mask = to_sensor_dev_attr(attr)->index; - int ret = lm95234_update_device(client, data); + int ret = lm95234_update_device(data); if (ret) return ret; @@ -252,11 +249,10 @@ static ssize_t show_type(struct device *dev, struct device_attribute *attr, static ssize_t set_type(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95234_data *data = i2c_get_clientdata(client); + struct lm95234_data *data = dev_get_drvdata(dev); unsigned long val; u8 mask = to_sensor_dev_attr(attr)->index; - int ret = lm95234_update_device(client, data); + int ret = lm95234_update_device(data); if (ret) return ret; @@ -274,7 +270,7 @@ static ssize_t set_type(struct device *dev, struct device_attribute *attr, else data->sensor_type &= ~mask; data->valid = false; - i2c_smbus_write_byte_data(client, LM95234_REG_REM_MODEL, + i2c_smbus_write_byte_data(data->client, LM95234_REG_REM_MODEL, data->sensor_type); mutex_unlock(&data->update_lock); @@ -284,10 +280,9 @@ static ssize_t set_type(struct device *dev, struct device_attribute *attr, static ssize_t show_tcrit2(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95234_data *data = i2c_get_clientdata(client); + struct lm95234_data *data = dev_get_drvdata(dev); int index = to_sensor_dev_attr(attr)->index; - int ret = lm95234_update_device(client, data); + int ret = lm95234_update_device(data); if (ret) return ret; @@ -298,11 +293,10 @@ static ssize_t show_tcrit2(struct device *dev, struct device_attribute *attr, static ssize_t set_tcrit2(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95234_data *data = i2c_get_clientdata(client); + struct lm95234_data *data = dev_get_drvdata(dev); int index = to_sensor_dev_attr(attr)->index; long val; - int ret = lm95234_update_device(client, data); + int ret = lm95234_update_device(data); if (ret) return ret; @@ -315,7 +309,7 @@ static ssize_t set_tcrit2(struct device *dev, struct device_attribute *attr, mutex_lock(&data->update_lock); data->tcrit2[index] = val; - i2c_smbus_write_byte_data(client, LM95234_REG_TCRIT2(index), val); + i2c_smbus_write_byte_data(data->client, LM95234_REG_TCRIT2(index), val); mutex_unlock(&data->update_lock); return count; @@ -324,10 +318,9 @@ static ssize_t set_tcrit2(struct device *dev, struct device_attribute *attr, static ssize_t show_tcrit2_hyst(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95234_data *data = i2c_get_clientdata(client); + struct lm95234_data *data = dev_get_drvdata(dev); int index = to_sensor_dev_attr(attr)->index; - int ret = lm95234_update_device(client, data); + int ret = lm95234_update_device(data); if (ret) return ret; @@ -340,8 +333,7 @@ static ssize_t show_tcrit2_hyst(struct device *dev, static ssize_t show_tcrit1(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95234_data *data = i2c_get_clientdata(client); + struct lm95234_data *data = dev_get_drvdata(dev); int index = to_sensor_dev_attr(attr)->index; return sprintf(buf, "%u", data->tcrit1[index] * 1000); @@ -350,11 +342,10 @@ static ssize_t show_tcrit1(struct device *dev, struct device_attribute *attr, static ssize_t set_tcrit1(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95234_data *data = i2c_get_clientdata(client); + struct lm95234_data *data = dev_get_drvdata(dev); int index = to_sensor_dev_attr(attr)->index; + int ret = lm95234_update_device(data); long val; - int ret = lm95234_update_device(client, data); if (ret) return ret; @@ -367,7 +358,7 @@ static ssize_t set_tcrit1(struct device *dev, struct device_attribute *attr, mutex_lock(&data->update_lock); data->tcrit1[index] = val; - i2c_smbus_write_byte_data(client, LM95234_REG_TCRIT1(index), val); + i2c_smbus_write_byte_data(data->client, LM95234_REG_TCRIT1(index), val); mutex_unlock(&data->update_lock); return count; @@ -376,10 +367,9 @@ static ssize_t set_tcrit1(struct device *dev, struct device_attribute *attr, static ssize_t show_tcrit1_hyst(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95234_data *data = i2c_get_clientdata(client); + struct lm95234_data *data = dev_get_drvdata(dev); int index = to_sensor_dev_attr(attr)->index; - int ret = lm95234_update_device(client, data); + int ret = lm95234_update_device(data); if (ret) return ret; @@ -393,11 +383,10 @@ static ssize_t set_tcrit1_hyst(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95234_data *data = i2c_get_clientdata(client); + struct lm95234_data *data = dev_get_drvdata(dev); int index = to_sensor_dev_attr(attr)->index; + int ret = lm95234_update_device(data); long val; - int ret = lm95234_update_device(client, data); if (ret) return ret; @@ -411,7 +400,7 @@ static ssize_t set_tcrit1_hyst(struct device *dev, mutex_lock(&data->update_lock); data->thyst = val; - i2c_smbus_write_byte_data(client, LM95234_REG_TCRIT_HYST, val); + i2c_smbus_write_byte_data(data->client, LM95234_REG_TCRIT_HYST, val); mutex_unlock(&data->update_lock); return count; @@ -420,10 +409,9 @@ static ssize_t set_tcrit1_hyst(struct device *dev, static ssize_t show_offset(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95234_data *data = i2c_get_clientdata(client); + struct lm95234_data *data = dev_get_drvdata(dev); int index = to_sensor_dev_attr(attr)->index; - int ret = lm95234_update_device(client, data); + int ret = lm95234_update_device(data); if (ret) return ret; @@ -434,11 +422,10 @@ static ssize_t show_offset(struct device *dev, struct device_attribute *attr, static ssize_t set_offset(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95234_data *data = i2c_get_clientdata(client); + struct lm95234_data *data = dev_get_drvdata(dev); int index = to_sensor_dev_attr(attr)->index; + int ret = lm95234_update_device(data); long val; - int ret = lm95234_update_device(client, data); if (ret) return ret; @@ -452,7 +439,7 @@ static ssize_t set_offset(struct device *dev, struct device_attribute *attr, mutex_lock(&data->update_lock); data->toffset[index] = val; - i2c_smbus_write_byte_data(client, LM95234_REG_OFFSET(index), val); + i2c_smbus_write_byte_data(data->client, LM95234_REG_OFFSET(index), val); mutex_unlock(&data->update_lock); return count; @@ -461,9 +448,8 @@ static ssize_t set_offset(struct device *dev, struct device_attribute *attr, static ssize_t show_interval(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95234_data *data = i2c_get_clientdata(client); - int ret = lm95234_update_device(client, data); + struct lm95234_data *data = dev_get_drvdata(dev); + int ret = lm95234_update_device(data); if (ret) return ret; @@ -475,11 +461,10 @@ static ssize_t show_interval(struct device *dev, struct device_attribute *attr, static ssize_t set_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95234_data *data = i2c_get_clientdata(client); + struct lm95234_data *data = dev_get_drvdata(dev); + int ret = lm95234_update_device(data); unsigned long val; u8 regval; - int ret = lm95234_update_device(client, data); if (ret) return ret; @@ -495,7 +480,7 @@ static ssize_t set_interval(struct device *dev, struct device_attribute *attr, mutex_lock(&data->update_lock); data->interval = msecs_to_jiffies(update_intervals[regval]); - i2c_smbus_write_byte_data(client, LM95234_REG_CONVRATE, regval); + i2c_smbus_write_byte_data(data->client, LM95234_REG_CONVRATE, regval); mutex_unlock(&data->update_lock); return count; @@ -579,7 +564,7 @@ static SENSOR_DEVICE_ATTR(temp5_offset, S_IWUSR | S_IRUGO, show_offset, static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval, set_interval); -static struct attribute *lm95234_attributes[] = { +static struct attribute *lm95234_attrs[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp2_input.dev_attr.attr, &sensor_dev_attr_temp3_input.dev_attr.attr, @@ -621,10 +606,7 @@ static struct attribute *lm95234_attributes[] = { &dev_attr_update_interval.attr, NULL }; - -static const struct attribute_group lm95234_group = { - .attrs = lm95234_attributes, -}; +ATTRIBUTE_GROUPS(lm95234); static int lm95234_detect(struct i2c_client *client, struct i2c_board_info *info) @@ -701,13 +683,14 @@ static int lm95234_probe(struct i2c_client *client, { struct device *dev = &client->dev; struct lm95234_data *data; + struct device *hwmon_dev; int err; data = devm_kzalloc(dev, sizeof(struct lm95234_data), GFP_KERNEL); if (!data) return -ENOMEM; - i2c_set_clientdata(client, data); + data->client = client; mutex_init(&data->update_lock); /* Initialize the LM95234 chip */ @@ -715,30 +698,11 @@ static int lm95234_probe(struct i2c_client *client, if (err < 0) return err; - /* Register sysfs hooks */ - err = sysfs_create_group(&dev->kobj, &lm95234_group); - if (err) - return err; - - data->hwmon_dev = hwmon_device_register(dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove_files; - } - - return 0; - -exit_remove_files: - sysfs_remove_group(&dev->kobj, &lm95234_group); - return err; -} - -static int lm95234_remove(struct i2c_client *client) -{ - struct lm95234_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &lm95234_group); + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, + lm95234_groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); return 0; } @@ -756,7 +720,6 @@ static struct i2c_driver lm95234_driver = { .name = DRVNAME, }, .probe = lm95234_probe, - .remove = lm95234_remove, .id_table = lm95234_id, .detect = lm95234_detect, .address_list = normal_i2c, -- cgit v0.10.2 From 89de734471c5381558b1e749e0b1b88b5b497708 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 4 Sep 2013 16:55:59 -0700 Subject: hwmon: (ina209) Convert to use devm_hwmon_device_register_with_groups Also use new macro ATTRIBUTE_GROUPS to declare attribute groups. Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/ina209.c b/drivers/hwmon/ina209.c index c6fdd5b..5378fde 100644 --- a/drivers/hwmon/ina209.c +++ b/drivers/hwmon/ina209.c @@ -63,7 +63,7 @@ #define INA209_SHUNT_DEFAULT 10000 /* uOhm */ struct ina209_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex update_lock; bool valid; @@ -78,8 +78,8 @@ struct ina209_data { static struct ina209_data *ina209_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct ina209_data *data = i2c_get_clientdata(client); + struct ina209_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; struct ina209_data *ret = data; s32 val; int i; @@ -234,7 +234,6 @@ static ssize_t ina209_set_interval(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); struct ina209_data *data = ina209_update_device(dev); long val; u16 regval; @@ -250,7 +249,8 @@ static ssize_t ina209_set_interval(struct device *dev, mutex_lock(&data->update_lock); regval = ina209_reg_from_interval(data->regs[INA209_CONFIGURATION], val); - i2c_smbus_write_word_swapped(client, INA209_CONFIGURATION, regval); + i2c_smbus_write_word_swapped(data->client, INA209_CONFIGURATION, + regval); data->regs[INA209_CONFIGURATION] = regval; data->update_interval = ina209_interval_from_reg(regval); mutex_unlock(&data->update_lock); @@ -260,8 +260,7 @@ static ssize_t ina209_set_interval(struct device *dev, static ssize_t ina209_show_interval(struct device *dev, struct device_attribute *da, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct ina209_data *data = i2c_get_clientdata(client); + struct ina209_data *data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%d\n", data->update_interval); } @@ -285,9 +284,9 @@ static ssize_t ina209_reset_history(struct device *dev, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct ina209_data *data = i2c_get_clientdata(client); struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ina209_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; u32 mask = attr->index; long val; int i, ret; @@ -312,7 +311,6 @@ static ssize_t ina209_set_value(struct device *dev, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); struct ina209_data *data = ina209_update_device(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(da); int reg = attr->index; @@ -332,7 +330,7 @@ static ssize_t ina209_set_value(struct device *dev, count = ret; goto abort; } - i2c_smbus_write_word_swapped(client, reg, ret); + i2c_smbus_write_word_swapped(data->client, reg, ret); data->regs[reg] = ret; abort: mutex_unlock(&data->update_lock); @@ -457,7 +455,7 @@ static SENSOR_DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR, * Finally, construct an array of pointers to members of the above objects, * as required for sysfs_create_group() */ -static struct attribute *ina209_attributes[] = { +static struct attribute *ina209_attrs[] = { &sensor_dev_attr_in0_input.dev_attr.attr, &sensor_dev_attr_in0_input_highest.dev_attr.attr, &sensor_dev_attr_in0_input_lowest.dev_attr.attr, @@ -498,10 +496,7 @@ static struct attribute *ina209_attributes[] = { NULL, }; - -static const struct attribute_group ina209_group = { - .attrs = ina209_attributes, -}; +ATTRIBUTE_GROUPS(ina209); static void ina209_restore_conf(struct i2c_client *client, struct ina209_data *data) @@ -565,6 +560,7 @@ static int ina209_probe(struct i2c_client *client, { struct i2c_adapter *adapter = client->adapter; struct ina209_data *data; + struct device *hwmon_dev; int ret; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) @@ -575,27 +571,23 @@ static int ina209_probe(struct i2c_client *client, return -ENOMEM; i2c_set_clientdata(client, data); + data->client = client; mutex_init(&data->update_lock); ret = ina209_init_client(client, data); if (ret) return ret; - /* Register sysfs hooks */ - ret = sysfs_create_group(&client->dev.kobj, &ina209_group); - if (ret) + hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev, + client->name, + data, ina209_groups); + if (IS_ERR(hwmon_dev)) { + ret = PTR_ERR(hwmon_dev); goto out_restore_conf; - - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - ret = PTR_ERR(data->hwmon_dev); - goto out_hwmon_device_register; } return 0; -out_hwmon_device_register: - sysfs_remove_group(&client->dev.kobj, &ina209_group); out_restore_conf: ina209_restore_conf(client, data); return ret; @@ -605,8 +597,6 @@ static int ina209_remove(struct i2c_client *client) { struct ina209_data *data = i2c_get_clientdata(client); - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &ina209_group); ina209_restore_conf(client, data); return 0; -- cgit v0.10.2 From 38f150fe382a42abeb333cc814becd57f5c01add Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 5 Sep 2013 08:52:41 -0700 Subject: hwmon: (ltc4261) Convert to use devm_hwmon_device_register_with_groups Also use new macro ATTRIBUTE_GROUPS to declare attribute groups. Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/ltc4261.c b/drivers/hwmon/ltc4261.c index 487da58..6c50db9 100644 --- a/drivers/hwmon/ltc4261.c +++ b/drivers/hwmon/ltc4261.c @@ -55,7 +55,7 @@ #define FAULT_OC (1<<2) struct ltc4261_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex update_lock; bool valid; @@ -67,8 +67,8 @@ struct ltc4261_data { static struct ltc4261_data *ltc4261_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct ltc4261_data *data = i2c_get_clientdata(client); + struct ltc4261_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; struct ltc4261_data *ret = data; mutex_lock(&data->update_lock); @@ -150,7 +150,6 @@ static ssize_t ltc4261_show_bool(struct device *dev, struct device_attribute *da, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct i2c_client *client = to_i2c_client(dev); struct ltc4261_data *data = ltc4261_update_device(dev); u8 fault; @@ -159,7 +158,7 @@ static ssize_t ltc4261_show_bool(struct device *dev, fault = data->regs[LTC4261_FAULT] & attr->index; if (fault) /* Clear reported faults in chip register */ - i2c_smbus_write_byte_data(client, LTC4261_FAULT, ~fault); + i2c_smbus_write_byte_data(data->client, LTC4261_FAULT, ~fault); return snprintf(buf, PAGE_SIZE, "%d\n", fault ? 1 : 0); } @@ -197,7 +196,7 @@ static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4261_show_value, NULL, static SENSOR_DEVICE_ATTR(curr1_max_alarm, S_IRUGO, ltc4261_show_bool, NULL, FAULT_OC); -static struct attribute *ltc4261_attributes[] = { +static struct attribute *ltc4261_attrs[] = { &sensor_dev_attr_in1_input.dev_attr.attr, &sensor_dev_attr_in1_min_alarm.dev_attr.attr, &sensor_dev_attr_in1_max_alarm.dev_attr.attr, @@ -210,60 +209,39 @@ static struct attribute *ltc4261_attributes[] = { NULL, }; - -static const struct attribute_group ltc4261_group = { - .attrs = ltc4261_attributes, -}; +ATTRIBUTE_GROUPS(ltc4261); static int ltc4261_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_adapter *adapter = client->adapter; + struct device *dev = &client->dev; struct ltc4261_data *data; - int ret; + struct device *hwmon_dev; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; if (i2c_smbus_read_byte_data(client, LTC4261_STATUS) < 0) { - dev_err(&client->dev, "Failed to read status register\n"); + dev_err(dev, "Failed to read status register\n"); return -ENODEV; } - data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - i2c_set_clientdata(client, data); + data->client = client; mutex_init(&data->update_lock); /* Clear faults */ i2c_smbus_write_byte_data(client, LTC4261_FAULT, 0x00); - /* Register sysfs hooks */ - ret = sysfs_create_group(&client->dev.kobj, <c4261_group); - if (ret) - return ret; - - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - ret = PTR_ERR(data->hwmon_dev); - goto out_hwmon_device_register; - } - - return 0; - -out_hwmon_device_register: - sysfs_remove_group(&client->dev.kobj, <c4261_group); - return ret; -} - -static int ltc4261_remove(struct i2c_client *client) -{ - struct ltc4261_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, <c4261_group); + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, + ltc4261_groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); return 0; } @@ -281,7 +259,6 @@ static struct i2c_driver ltc4261_driver = { .name = "ltc4261", }, .probe = ltc4261_probe, - .remove = ltc4261_remove, .id_table = ltc4261_id, }; -- cgit v0.10.2 From 62f9a57c0b48b6fd6c13e3b8d0c424737b1103c9 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 5 Sep 2013 09:02:34 -0700 Subject: hwmon: (jc42) Convert to use devm_hwmon_device_register_with_groups Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c index 4a58f13..f362cea 100644 --- a/drivers/hwmon/jc42.c +++ b/drivers/hwmon/jc42.c @@ -163,7 +163,7 @@ static struct jc42_chips jc42_chips[] = { /* Each client has this additional data */ struct jc42_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex update_lock; /* protect register access */ bool extended; /* true if extended range supported */ bool valid; @@ -193,21 +193,21 @@ MODULE_DEVICE_TABLE(i2c, jc42_id); static int jc42_suspend(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct jc42_data *data = i2c_get_clientdata(client); + struct jc42_data *data = dev_get_drvdata(dev); data->config |= JC42_CFG_SHUTDOWN; - i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG, data->config); + i2c_smbus_write_word_swapped(data->client, JC42_REG_CONFIG, + data->config); return 0; } static int jc42_resume(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct jc42_data *data = i2c_get_clientdata(client); + struct jc42_data *data = dev_get_drvdata(dev); data->config &= ~JC42_CFG_SHUTDOWN; - i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG, data->config); + i2c_smbus_write_word_swapped(data->client, JC42_REG_CONFIG, + data->config); return 0; } @@ -317,15 +317,14 @@ static ssize_t set_##value(struct device *dev, \ struct device_attribute *attr, \ const char *buf, size_t count) \ { \ - struct i2c_client *client = to_i2c_client(dev); \ - struct jc42_data *data = i2c_get_clientdata(client); \ + struct jc42_data *data = dev_get_drvdata(dev); \ int err, ret = count; \ long val; \ - if (kstrtol(buf, 10, &val) < 0) \ + if (kstrtol(buf, 10, &val) < 0) \ return -EINVAL; \ mutex_lock(&data->update_lock); \ data->value = jc42_temp_to_reg(val, data->extended); \ - err = i2c_smbus_write_word_swapped(client, reg, data->value); \ + err = i2c_smbus_write_word_swapped(data->client, reg, data->value); \ if (err < 0) \ ret = err; \ mutex_unlock(&data->update_lock); \ @@ -344,8 +343,7 @@ static ssize_t set_temp_crit_hyst(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct jc42_data *data = i2c_get_clientdata(client); + struct jc42_data *data = dev_get_drvdata(dev); unsigned long val; int diff, hyst; int err; @@ -368,7 +366,7 @@ static ssize_t set_temp_crit_hyst(struct device *dev, mutex_lock(&data->update_lock); data->config = (data->config & ~JC42_CFG_HYST_MASK) | (hyst << JC42_CFG_HYST_SHIFT); - err = i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG, + err = i2c_smbus_write_word_swapped(data->client, JC42_REG_CONFIG, data->config); if (err < 0) ret = err; @@ -430,8 +428,7 @@ static umode_t jc42_attribute_mode(struct kobject *kobj, struct attribute *attr, int index) { struct device *dev = container_of(kobj, struct device, kobj); - struct i2c_client *client = to_i2c_client(dev); - struct jc42_data *data = i2c_get_clientdata(client); + struct jc42_data *data = dev_get_drvdata(dev); unsigned int config = data->config; bool readonly; @@ -452,6 +449,7 @@ static const struct attribute_group jc42_group = { .attrs = jc42_attributes, .is_visible = jc42_attribute_mode, }; +__ATTRIBUTE_GROUPS(jc42); /* Return 0 if detection is successful, -ENODEV otherwise */ static int jc42_detect(struct i2c_client *client, struct i2c_board_info *info) @@ -487,14 +485,16 @@ static int jc42_detect(struct i2c_client *client, struct i2c_board_info *info) static int jc42_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct jc42_data *data; - int config, cap, err; struct device *dev = &client->dev; + struct device *hwmon_dev; + struct jc42_data *data; + int config, cap; data = devm_kzalloc(dev, sizeof(struct jc42_data), GFP_KERNEL); if (!data) return -ENOMEM; + data->client = client; i2c_set_clientdata(client, data); mutex_init(&data->update_lock); @@ -515,29 +515,18 @@ static int jc42_probe(struct i2c_client *client, const struct i2c_device_id *id) } data->config = config; - /* Register sysfs hooks */ - err = sysfs_create_group(&dev->kobj, &jc42_group); - if (err) - return err; - - data->hwmon_dev = hwmon_device_register(dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove; - } + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, + jc42_groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); return 0; - -exit_remove: - sysfs_remove_group(&dev->kobj, &jc42_group); - return err; } static int jc42_remove(struct i2c_client *client) { struct jc42_data *data = i2c_get_clientdata(client); - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &jc42_group); /* Restore original configuration except hysteresis */ if ((data->config & ~JC42_CFG_HYST_MASK) != @@ -553,8 +542,8 @@ static int jc42_remove(struct i2c_client *client) static struct jc42_data *jc42_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct jc42_data *data = i2c_get_clientdata(client); + struct jc42_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; struct jc42_data *ret = data; int val; -- cgit v0.10.2 From 9c09bd8d896f8dcce14d0f032cb449a00a3e3141 Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Tue, 17 Sep 2013 06:43:42 -0700 Subject: hwmon: (nct6775) fix coccinelle warnings drivers/hwmon/nct6775.c:3866:1-3: WARNING: PTR_RET can be used Use PTR_ERR_OR_ZERO rather than if(IS_ERR(...)) + PTR_ERR Generated by: coccinelle/api/ptr_ret.cocci CC: Guenter Roeck Signed-off-by: Fengguang Wu Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index fdbd632..7a1b6a7 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -3862,10 +3862,7 @@ static int nct6775_probe(struct platform_device *pdev) hwmon_dev = devm_hwmon_device_register_with_groups(dev, data->name, data, data->groups); - if (IS_ERR(hwmon_dev)) - return PTR_ERR(hwmon_dev); - - return 0; + return PTR_ERR_OR_ZERO(hwmon_dev); } #ifdef CONFIG_PM -- cgit v0.10.2 From 31ab1ad70b25f4e19f6442e8f6d68025ba43b942 Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Tue, 17 Sep 2013 20:54:09 -0700 Subject: hwmon: (ds1621) fix coccinelle warnings drivers/hwmon/ds1621.c:381:1-3: WARNING: PTR_RET can be used Use PTR_ERR_OR_ZERO rather than if(IS_ERR(...)) + PTR_ERR Generated by: coccinelle/api/ptr_ret.cocci CC: Guenter Roeck Signed-off-by: Fengguang Wu Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c index 5e398c9..872d767 100644 --- a/drivers/hwmon/ds1621.c +++ b/drivers/hwmon/ds1621.c @@ -378,10 +378,7 @@ static int ds1621_probe(struct i2c_client *client, hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev, client->name, data, ds1621_groups); - if (IS_ERR(hwmon_dev)) - return PTR_ERR(hwmon_dev); - - return 0; + return PTR_ERR_OR_ZERO(hwmon_dev); } static const struct i2c_device_id ds1621_id[] = { -- cgit v0.10.2 From e5840c78f3c9e3d950363e4305b65183f2998a73 Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Tue, 17 Sep 2013 20:54:11 -0700 Subject: hwmon: (max6642 fix coccinelle warnings drivers/hwmon/max6642.c:299:1-3: WARNING: PTR_RET can be used Use PTR_ERR_OR_ZERO rather than if(IS_ERR(...)) + PTR_ERR Generated by: coccinelle/api/ptr_ret.cocci CC: Guenter Roeck Signed-off-by: Fengguang Wu Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/max6642.c b/drivers/hwmon/max6642.c index 3d61f8d..8326fbd 100644 --- a/drivers/hwmon/max6642.c +++ b/drivers/hwmon/max6642.c @@ -296,10 +296,7 @@ static int max6642_probe(struct i2c_client *client, hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev, client->name, data, max6642_groups); - if (IS_ERR(hwmon_dev)) - return PTR_ERR(hwmon_dev); - - return 0; + return PTR_ERR_OR_ZERO(hwmon_dev); } /* -- cgit v0.10.2 From 4109a7160849604395eda57d6f011c8db3866482 Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Tue, 17 Sep 2013 20:54:12 -0700 Subject: hwmon: (max6697) fix coccinelle warnings drivers/hwmon/max6697.c:649:1-3: WARNING: PTR_RET can be used Use PTR_ERR_OR_ZERO rather than if(IS_ERR(...)) + PTR_ERR Generated by: coccinelle/api/ptr_ret.cocci CC: Guenter Roeck Signed-off-by: Fengguang Wu Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/max6697.c b/drivers/hwmon/max6697.c index 0af910a..7fd3eaf 100644 --- a/drivers/hwmon/max6697.c +++ b/drivers/hwmon/max6697.c @@ -646,10 +646,7 @@ static int max6697_probe(struct i2c_client *client, hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, data, max6697_groups); - if (IS_ERR(hwmon_dev)) - return PTR_ERR(hwmon_dev); - - return 0; + return PTR_ERR_OR_ZERO(hwmon_dev); } static const struct i2c_device_id max6697_id[] = { -- cgit v0.10.2 From 3ea85ba7af58878889f438b1dfbe08e87befcb6d Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Tue, 17 Sep 2013 20:54:14 -0700 Subject: hwmon: (lm95234) fix coccinelle warnings drivers/hwmon/lm95234.c:704:1-3: WARNING: PTR_RET can be used Use PTR_ERR_OR_ZERO rather than if(IS_ERR(...)) + PTR_ERR Generated by: coccinelle/api/ptr_ret.cocci CC: Guenter Roeck Signed-off-by: Fengguang Wu Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c index cf87507..411202b 100644 --- a/drivers/hwmon/lm95234.c +++ b/drivers/hwmon/lm95234.c @@ -701,10 +701,7 @@ static int lm95234_probe(struct i2c_client *client, hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, data, lm95234_groups); - if (IS_ERR(hwmon_dev)) - return PTR_ERR(hwmon_dev); - - return 0; + return PTR_ERR_OR_ZERO(hwmon_dev); } /* Driver data (common to all clients) */ -- cgit v0.10.2 From 2a253c4789db7ff46dbdba74d0f89ac08b077fda Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Tue, 17 Sep 2013 20:54:16 -0700 Subject: hwmon: (ltc4261) fix coccinelle warnings drivers/hwmon/ltc4261.c:243:1-3: WARNING: PTR_RET can be used Use PTR_ERR_OR_ZERO rather than if(IS_ERR(...)) + PTR_ERR Generated by: coccinelle/api/ptr_ret.cocci CC: Guenter Roeck Signed-off-by: Fengguang Wu Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/ltc4261.c b/drivers/hwmon/ltc4261.c index 6c50db9..0becd69 100644 --- a/drivers/hwmon/ltc4261.c +++ b/drivers/hwmon/ltc4261.c @@ -240,10 +240,7 @@ static int ltc4261_probe(struct i2c_client *client, hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, data, ltc4261_groups); - if (IS_ERR(hwmon_dev)) - return PTR_ERR(hwmon_dev); - - return 0; + return PTR_ERR_OR_ZERO(hwmon_dev); } static const struct i2c_device_id ltc4261_id[] = { -- cgit v0.10.2 From 650a2c02b5a0eaf46209bf505f95739366119bc8 Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Tue, 17 Sep 2013 20:54:24 -0700 Subject: hwmon: (jc42) fix coccinelle warnings drivers/hwmon/jc42.c:521:1-3: WARNING: PTR_RET can be used Use PTR_ERR_OR_ZERO rather than if(IS_ERR(...)) + PTR_ERR Generated by: coccinelle/api/ptr_ret.cocci CC: Guenter Roeck Signed-off-by: Fengguang Wu Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c index f362cea..6013611 100644 --- a/drivers/hwmon/jc42.c +++ b/drivers/hwmon/jc42.c @@ -518,10 +518,7 @@ static int jc42_probe(struct i2c_client *client, const struct i2c_device_id *id) hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, data, jc42_groups); - if (IS_ERR(hwmon_dev)) - return PTR_ERR(hwmon_dev); - - return 0; + return PTR_ERR_OR_ZERO(hwmon_dev); } static int jc42_remove(struct i2c_client *client) -- cgit v0.10.2 From 20652ab8ca3854a1967baf8452c8bedceff7b92b Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 20 Sep 2013 09:36:41 +0530 Subject: hwmon: (adcxx) Remove redundant spi_set_drvdata Driver core sets driver data to NULL upon failure or remove. Signed-off-by: Sachin Kamat Cc: Marc Pignat Acked-by: Marc Pignat Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/adcxx.c b/drivers/hwmon/adcxx.c index 751b1f0..04c08c2 100644 --- a/drivers/hwmon/adcxx.c +++ b/drivers/hwmon/adcxx.c @@ -203,7 +203,6 @@ out_err: for (i--; i >= 0; i--) device_remove_file(&spi->dev, &ad_input[i].dev_attr); - spi_set_drvdata(spi, NULL); mutex_unlock(&adc->lock); return status; } @@ -218,7 +217,6 @@ static int adcxx_remove(struct spi_device *spi) for (i = 0; i < 3 + adc->channels; i++) device_remove_file(&spi->dev, &ad_input[i].dev_attr); - spi_set_drvdata(spi, NULL); mutex_unlock(&adc->lock); return 0; -- cgit v0.10.2 From 2825f033da13bc932aa470533da0f8cfb9a6e40d Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 20 Sep 2013 09:36:42 +0530 Subject: hwmon: (lm70) Remove redundant spi_set_drvdata Driver core sets driver data to NULL upon failure or remove. Signed-off-by: Sachin Kamat Cc: Kaiwan N Billimoria Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/lm70.c b/drivers/hwmon/lm70.c index 016efa2..505a59e 100644 --- a/drivers/hwmon/lm70.c +++ b/drivers/hwmon/lm70.c @@ -174,7 +174,6 @@ out_dev_reg_failed: out_dev_create_file_failed: device_remove_file(&spi->dev, &dev_attr_temp1_input); out_dev_create_temp_file_failed: - spi_set_drvdata(spi, NULL); return status; } @@ -185,7 +184,6 @@ static int lm70_remove(struct spi_device *spi) hwmon_device_unregister(p_lm70->hwmon_dev); device_remove_file(&spi->dev, &dev_attr_temp1_input); device_remove_file(&spi->dev, &dev_attr_name); - spi_set_drvdata(spi, NULL); return 0; } -- cgit v0.10.2 From c50588ababd85e164a97f114d79af267a6aef42a Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 27 Sep 2013 16:56:00 +0530 Subject: hwmon: (gpio-fan) Include linux/of.h header 'of_match_ptr' is defined in linux/of.h. Include it explicitly. Signed-off-by: Sachin Kamat Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index d5148c8..73181be 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include -- cgit v0.10.2 From 84fb029faa05e1de229a68829cca5dcf85c79894 Mon Sep 17 00:00:00 2001 From: LABBE Corentin Date: Fri, 27 Sep 2013 14:36:04 +0200 Subject: hwmon: Correct some typos Signed-off-by: LABBE Corentin Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/abituguru.c b/drivers/hwmon/abituguru.c index 2ebd6ce..9c8a6ba 100644 --- a/drivers/hwmon/abituguru.c +++ b/drivers/hwmon/abituguru.c @@ -164,7 +164,7 @@ static const u8 abituguru_bank2_max_threshold = 50; static const int abituguru_pwm_settings_multiplier[5] = { 0, 1, 1, 1000, 1000 }; /* * Min / Max allowed values for pwm_settings. Note: pwm1 (CPU fan) is a - * special case the minium allowed pwm% setting for this is 30% (77) on + * special case the minimum allowed pwm% setting for this is 30% (77) on * some MB's this special case is handled in the code! */ static const u8 abituguru_pwm_min[5] = { 0, 170, 170, 25, 25 }; @@ -517,7 +517,7 @@ abituguru_detect_bank1_sensor_type(struct abituguru_data *data, ABIT_UGURU_DEBUG(2, "testing bank1 sensor %d\n", (int)sensor_addr); /* - * Volt sensor test, enable volt low alarm, set min value ridicously + * Volt sensor test, enable volt low alarm, set min value ridiculously * high, or vica versa if the reading is very high. If its a volt * sensor this should always give us an alarm. */ @@ -564,7 +564,7 @@ abituguru_detect_bank1_sensor_type(struct abituguru_data *data, /* * Temp sensor test, enable sensor as a temp sensor, set beep value - * ridicously low (but not too low, otherwise uguru ignores it). + * ridiculously low (but not too low, otherwise uguru ignores it). * If its a temp sensor this should always give us an alarm. */ buf[0] = ABIT_UGURU_TEMP_HIGH_ALARM_ENABLE; diff --git a/drivers/hwmon/abituguru3.c b/drivers/hwmon/abituguru3.c index 0cac8c0..4ae74aa8 100644 --- a/drivers/hwmon/abituguru3.c +++ b/drivers/hwmon/abituguru3.c @@ -176,7 +176,7 @@ struct abituguru3_data { /* * The abituguru3 supports up to 48 sensors, and thus has registers - * sets for 48 sensors, for convienence reasons / simplicity of the + * sets for 48 sensors, for convenience reasons / simplicity of the * code we always read and store all registers for all 48 sensors */ diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c index b25c643..1d7ff46 100644 --- a/drivers/hwmon/asus_atk0110.c +++ b/drivers/hwmon/asus_atk0110.c @@ -119,7 +119,7 @@ struct atk_data { acpi_handle rtmp_handle; acpi_handle rvlt_handle; acpi_handle rfan_handle; - /* new inteface */ + /* new interface */ acpi_handle enumerate_handle; acpi_handle read_handle; acpi_handle write_handle; diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c index 3c16cbd..0cafc39 100644 --- a/drivers/hwmon/max6650.c +++ b/drivers/hwmon/max6650.c @@ -660,7 +660,7 @@ static int max6650_init_client(struct i2c_client *client) /* * If mode is set to "full off", we change it to "open loop" and * set DAC to 255, which has the same effect. We do this because - * there's no "full off" mode defined in hwmon specifcations. + * there's no "full off" mode defined in hwmon specifications. */ if ((config & MAX6650_CFG_MODE_MASK) == MAX6650_CFG_MODE_OFF) { diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 7f9ce32..3cbf66e 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -157,7 +157,7 @@ EXPORT_SYMBOL_GPL(pmbus_write_byte); /* * _pmbus_write_byte() is similar to pmbus_write_byte(), but checks if - * a device specific mapping funcion exists and calls it if necessary. + * a device specific mapping function exists and calls it if necessary. */ static int _pmbus_write_byte(struct i2c_client *client, int page, u8 value) { diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index a3feee3..bdcf2dc 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -1043,7 +1043,7 @@ static struct sensor_device_attribute sda_temp_alarm[] = { SENSOR_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13), }; -/* get reatime status of all sensors items: voltage, temp, fan */ +/* get realtime status of all sensors items: voltage, temp, fan */ static ssize_t show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf) { diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c index 5febb43..df58580 100644 --- a/drivers/hwmon/w83792d.c +++ b/drivers/hwmon/w83792d.c @@ -579,7 +579,7 @@ static ssize_t store_temp23(struct device *dev, struct device_attribute *attr, return count; } -/* get reatime status of all sensors items: voltage, temp, fan */ +/* get realtime status of all sensors items: voltage, temp, fan */ static ssize_t show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf) { diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c index b0c30a5..b6470ec 100644 --- a/drivers/hwmon/w83793.c +++ b/drivers/hwmon/w83793.c @@ -808,7 +808,7 @@ show_sf_ctrl(struct device *dev, struct device_attribute *attr, char *buf) if (nr == TEMP_FAN_MAP) { val = data->temp_fan_map[index]; } else if (nr == TEMP_PWM_ENABLE) { - /* +2 to transfrom into 2 and 3 to conform with sysfs intf */ + /* +2 to transform into 2 and 3 to conform with sysfs intf */ val = ((data->pwm_enable >> index) & 0x01) + 2; } else if (nr == TEMP_CRUISE) { val = TEMP_FROM_REG(data->temp_cruise[index] & 0x7f); -- cgit v0.10.2 From c8ccab7ab5c71b4fab274bfd18425503a4dcc288 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 16 Mar 2013 10:25:04 -0700 Subject: hwmon: (pmbus/lm25066) Add support for LM25063 Signed-off-by: Guenter Roeck diff --git a/Documentation/hwmon/lm25066 b/Documentation/hwmon/lm25066 index c1b57d7..b34c3de 100644 --- a/Documentation/hwmon/lm25066 +++ b/Documentation/hwmon/lm25066 @@ -8,6 +8,11 @@ Supported chips: Datasheets: http://www.ti.com/lit/gpn/lm25056 http://www.ti.com/lit/gpn/lm25056a + * TI LM25063 + Prefix: 'lm25063' + Addresses scanned: - + Datasheet: + To be announced * National Semiconductor LM25066 Prefix: 'lm25066' Addresses scanned: - @@ -32,7 +37,7 @@ Description ----------- This driver supports hardware montoring for National Semiconductor / TI LM25056, -LM25066, LM5064, and LM5064 Power Management, Monitoring, Control, and +LM25063, LM25066, LM5064, and LM5066 Power Management, Monitoring, Control, and Protection ICs. The driver is a client driver to the core PMBus driver. Please see @@ -64,8 +69,12 @@ in1_input Measured input voltage. in1_average Average measured input voltage. in1_min Minimum input voltage. in1_max Maximum input voltage. +in1_crit Critical high input voltage (LM25063 only). +in1_lcrit Critical low input voltage (LM25063 only). in1_min_alarm Input voltage low alarm. in1_max_alarm Input voltage high alarm. +in1_lcrit_alarm Input voltage critical low alarm (LM25063 only). +in1_crit_alarm Input voltage critical high alarm. (LM25063 only). in2_label "vmon" in2_input Measured voltage on VAUX pin @@ -80,12 +89,16 @@ in3_input Measured output voltage. in3_average Average measured output voltage. in3_min Minimum output voltage. in3_min_alarm Output voltage low alarm. +in3_highest Historical minimum output voltage (LM25063 only). +in3_lowest Historical maximum output voltage (LM25063 only). curr1_label "iin" curr1_input Measured input current. curr1_average Average measured input current. curr1_max Maximum input current. +curr1_crit Critical input current (LM25063 only). curr1_max_alarm Input current high alarm. +curr1_crit_alarm Input current critical high alarm (LM25063 only). power1_label "pin" power1_input Measured input power. @@ -95,6 +108,11 @@ power1_alarm Input power alarm power1_input_highest Historical maximum power. power1_reset_history Write any value to reset maximum power history. +power2_label "pout". LM25063 only. +power2_input Measured output power. +power2_max Maximum output power limit. +power2_crit Critical output power limit. + temp1_input Measured temperature. temp1_max Maximum temperature. temp1_crit Critical high temperature. diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c index 6a9d6ed..a26b1d1 100644 --- a/drivers/hwmon/pmbus/lm25066.c +++ b/drivers/hwmon/pmbus/lm25066.c @@ -1,5 +1,5 @@ /* - * Hardware monitoring driver for LM25056 / LM25066 / LM5064 / LM5066 + * Hardware monitoring driver for LM25056 / LM25063 / LM25066 / LM5064 / LM5066 * * Copyright (c) 2011 Ericsson AB. * Copyright (c) 2013 Guenter Roeck @@ -27,7 +27,7 @@ #include #include "pmbus.h" -enum chips { lm25056, lm25066, lm5064, lm5066 }; +enum chips { lm25056, lm25063, lm25066, lm5064, lm5066 }; #define LM25066_READ_VAUX 0xd0 #define LM25066_MFR_READ_IIN 0xd1 @@ -52,6 +52,11 @@ enum chips { lm25056, lm25066, lm5064, lm5066 }; #define LM25056_MFR_STS_VAUX_OV_WARN (1 << 1) #define LM25056_MFR_STS_VAUX_UV_WARN (1 << 0) +/* LM25063 only */ + +#define LM25063_READ_VOUT_MAX 0xe5 +#define LM25063_READ_VOUT_MIN 0xe6 + struct __coeff { short m, b, R; }; @@ -59,7 +64,7 @@ struct __coeff { #define PSC_CURRENT_IN_L (PSC_NUM_CLASSES) #define PSC_POWER_L (PSC_NUM_CLASSES + 1) -static struct __coeff lm25066_coeff[4][PSC_NUM_CLASSES + 2] = { +static struct __coeff lm25066_coeff[5][PSC_NUM_CLASSES + 2] = { [lm25056] = { [PSC_VOLTAGE_IN] = { .m = 16296, @@ -116,6 +121,36 @@ static struct __coeff lm25066_coeff[4][PSC_NUM_CLASSES + 2] = { .m = 16, }, }, + [lm25063] = { + [PSC_VOLTAGE_IN] = { + .m = 16000, + .R = -2, + }, + [PSC_VOLTAGE_OUT] = { + .m = 16000, + .R = -2, + }, + [PSC_CURRENT_IN] = { + .m = 10000, + .R = -2, + }, + [PSC_CURRENT_IN_L] = { + .m = 10000, + .R = -2, + }, + [PSC_POWER] = { + .m = 5000, + .R = -3, + }, + [PSC_POWER_L] = { + .m = 5000, + .R = -3, + }, + [PSC_TEMPERATURE] = { + .m = 15596, + .R = -3, + }, + }, [lm5064] = { [PSC_VOLTAGE_IN] = { .m = 4611, @@ -178,6 +213,7 @@ static struct __coeff lm25066_coeff[4][PSC_NUM_CLASSES + 2] = { struct lm25066_data { int id; + u16 rlimit; /* Maximum register value */ struct pmbus_driver_info info; }; @@ -200,6 +236,10 @@ static int lm25066_read_word_data(struct i2c_client *client, int page, int reg) /* VIN: 6.14 mV VAUX: 293 uV LSB */ ret = DIV_ROUND_CLOSEST(ret * 293, 6140); break; + case lm25063: + /* VIN: 6.25 mV VAUX: 200.0 uV LSB */ + ret = DIV_ROUND_CLOSEST(ret * 20, 625); + break; case lm25066: /* VIN: 4.54 mV VAUX: 283.2 uV LSB */ ret = DIV_ROUND_CLOSEST(ret * 2832, 45400); @@ -253,6 +293,24 @@ static int lm25066_read_word_data(struct i2c_client *client, int page, int reg) return ret; } +static int lm25063_read_word_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_VOUT_MAX: + ret = pmbus_read_word_data(client, 0, LM25063_READ_VOUT_MAX); + break; + case PMBUS_VIRT_READ_VOUT_MIN: + ret = pmbus_read_word_data(client, 0, LM25063_READ_VOUT_MIN); + break; + default: + ret = lm25066_read_word_data(client, page, reg); + break; + } + return ret; +} + static int lm25056_read_word_data(struct i2c_client *client, int page, int reg) { int ret; @@ -308,27 +366,34 @@ static int lm25056_read_byte_data(struct i2c_client *client, int page, int reg) static int lm25066_write_word_data(struct i2c_client *client, int page, int reg, u16 word) { + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct lm25066_data *data = to_lm25066_data(info); int ret; switch (reg) { + case PMBUS_POUT_OP_FAULT_LIMIT: + case PMBUS_POUT_OP_WARN_LIMIT: case PMBUS_VOUT_UV_WARN_LIMIT: case PMBUS_OT_FAULT_LIMIT: case PMBUS_OT_WARN_LIMIT: + case PMBUS_IIN_OC_FAULT_LIMIT: case PMBUS_VIN_UV_WARN_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + case PMBUS_VIN_OV_FAULT_LIMIT: case PMBUS_VIN_OV_WARN_LIMIT: - word = ((s16)word < 0) ? 0 : clamp_val(word, 0, 0x0fff); + word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit); ret = pmbus_write_word_data(client, 0, reg, word); pmbus_clear_cache(client); break; case PMBUS_IIN_OC_WARN_LIMIT: - word = ((s16)word < 0) ? 0 : clamp_val(word, 0, 0x0fff); + word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit); ret = pmbus_write_word_data(client, 0, LM25066_MFR_IIN_OC_WARN_LIMIT, word); pmbus_clear_cache(client); break; case PMBUS_PIN_OP_WARN_LIMIT: - word = ((s16)word < 0) ? 0 : clamp_val(word, 0, 0x0fff); + word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit); ret = pmbus_write_word_data(client, 0, LM25066_MFR_PIN_OP_WARN_LIMIT, word); @@ -337,7 +402,7 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg, case PMBUS_VIRT_VMON_UV_WARN_LIMIT: /* Adjust from VIN coefficients (for LM25056) */ word = DIV_ROUND_CLOSEST((int)word * 6140, 293); - word = ((s16)word < 0) ? 0 : clamp_val(word, 0, 0x0fff); + word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit); ret = pmbus_write_word_data(client, 0, LM25056_VAUX_UV_WARN_LIMIT, word); pmbus_clear_cache(client); @@ -345,7 +410,7 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg, case PMBUS_VIRT_VMON_OV_WARN_LIMIT: /* Adjust from VIN coefficients (for LM25056) */ word = DIV_ROUND_CLOSEST((int)word * 6140, 293); - word = ((s16)word < 0) ? 0 : clamp_val(word, 0, 0x0fff); + word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit); ret = pmbus_write_word_data(client, 0, LM25056_VAUX_OV_WARN_LIMIT, word); pmbus_clear_cache(client); @@ -399,9 +464,16 @@ static int lm25066_probe(struct i2c_client *client, info->func[0] |= PMBUS_HAVE_STATUS_VMON; info->read_word_data = lm25056_read_word_data; info->read_byte_data = lm25056_read_byte_data; + data->rlimit = 0x0fff; + } else if (data->id == lm25063) { + info->func[0] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_POUT; + info->read_word_data = lm25063_read_word_data; + data->rlimit = 0xffff; } else { info->func[0] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; info->read_word_data = lm25066_read_word_data; + data->rlimit = 0x0fff; } info->write_word_data = lm25066_write_word_data; @@ -432,6 +504,7 @@ static int lm25066_probe(struct i2c_client *client, static const struct i2c_device_id lm25066_id[] = { {"lm25056", lm25056}, + {"lm25063", lm25063}, {"lm25066", lm25066}, {"lm5064", lm5064}, {"lm5066", lm5066}, @@ -453,5 +526,5 @@ static struct i2c_driver lm25066_driver = { module_i2c_driver(lm25066_driver); MODULE_AUTHOR("Guenter Roeck"); -MODULE_DESCRIPTION("PMBus driver for LM25056/LM25066/LM5064/LM5066"); +MODULE_DESCRIPTION("PMBus driver for LM25066 and compatible chips"); MODULE_LICENSE("GPL"); -- cgit v0.10.2 From c24c407e963e73d3ad18b9bc0af32cf23f37a7b0 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 23 Sep 2013 10:56:48 -0700 Subject: hwmon: (pmbus/ltc2978): Add support for LTC2977 LTC2977 is a pin compatible replacement for LTC2978. Signed-off-by: Guenter Roeck diff --git a/Documentation/hwmon/ltc2978 b/Documentation/hwmon/ltc2978 index dc0d08c..6934936 100644 --- a/Documentation/hwmon/ltc2978 +++ b/Documentation/hwmon/ltc2978 @@ -6,6 +6,10 @@ Supported chips: Prefix: 'ltc2974' Addresses scanned: - Datasheet: http://www.linear.com/product/ltc2974 + * Linear Technology LTC2977 + Prefix: 'ltc2977' + Addresses scanned: - + Datasheet: http://www.linear.com/product/ltc2977 * Linear Technology LTC2978 Prefix: 'ltc2978' Addresses scanned: - @@ -26,8 +30,9 @@ Description ----------- LTC2974 is a quad digital power supply manager. LTC2978 is an octal power supply -monitor. LTC3880 is a dual output poly-phase step-down DC/DC controller. LTC3883 -is a single phase step-down DC/DC controller. +monitor. LTC2977 is a pin compatible replacement for LTC2978. LTC3880 is a dual +output poly-phase step-down DC/DC controller. LTC3883 is a single phase +step-down DC/DC controller. Usage Notes @@ -49,21 +54,25 @@ Sysfs attributes in1_label "vin" in1_input Measured input voltage. in1_min Minimum input voltage. -in1_max Maximum input voltage. LTC2974 and LTC2978 only. -in1_lcrit Critical minimum input voltage. LTC2974 and LTC2978 - only. +in1_max Maximum input voltage. + LTC2974, LTC2977, and LTC2978 only. +in1_lcrit Critical minimum input voltage. + LTC2974, LTC2977, and LTC2978 only. in1_crit Critical maximum input voltage. in1_min_alarm Input voltage low alarm. -in1_max_alarm Input voltage high alarm. LTC2974 and LTC2978 only. -in1_lcrit_alarm Input voltage critical low alarm. LTC2974 and LTC2978 - only. +in1_max_alarm Input voltage high alarm. + LTC2974, LTC2977, and LTC2978 only. +in1_lcrit_alarm Input voltage critical low alarm. + LTC2974, LTC2977, and LTC2978 only. in1_crit_alarm Input voltage critical high alarm. -in1_lowest Lowest input voltage. LTC2974 and LTC2978 only. +in1_lowest Lowest input voltage. + LTC2974, LTC2977, and LTC2978 only. in1_highest Highest input voltage. in1_reset_history Reset input voltage history. in[N]_label "vout[1-8]". LTC2974: N=2-5 + LTC2977: N=2-9 LTC2978: N=2-9 LTC3880: N=2-3 LTC3883: N=2 @@ -83,21 +92,23 @@ in[N]_reset_history Reset output voltage history. temp[N]_input Measured temperature. On LTC2974, temp[1-4] report external temperatures, and temp5 reports the chip temperature. - On LTC2978, only one temperature measurement is - supported and reports the chip temperature. + On LTC2977 and LTC2978, only one temperature measurement + is supported and reports the chip temperature. On LTC3880, temp1 and temp2 report external temperatures, and temp3 reports the chip temperature. On LTC3883, temp1 reports an external temperature, and temp2 reports the chip temperature. -temp[N]_min Mimimum temperature. LTC2974 and LTC2978 only. +temp[N]_min Mimimum temperature. LTC2974, LCT2977, and LTC2978 only. temp[N]_max Maximum temperature. temp[N]_lcrit Critical low temperature. temp[N]_crit Critical high temperature. -temp[N]_min_alarm Temperature low alarm. LTC2974 and LTC2978 only. +temp[N]_min_alarm Temperature low alarm. + LTC2974, LTC2977, and LTC2978 only. temp[N]_max_alarm Temperature high alarm. temp[N]_lcrit_alarm Temperature critical low alarm. temp[N]_crit_alarm Temperature critical high alarm. -temp[N]_lowest Lowest measured temperature. LTC2974 and LTC2978 only. +temp[N]_lowest Lowest measured temperature. + LTC2974, LTC2977, and LTC2978 only. Not supported for chip temperature sensor on LTC2974. temp[N]_highest Highest measured temperature. Not supported for chip temperature sensor on LTC2974. @@ -109,6 +120,7 @@ power1_input Measured input power. power[N]_label "pout[1-4]". LTC2974: N=1-4 + LTC2977: Not supported LTC2978: Not supported LTC3880: N=1-2 LTC3883: N=2 @@ -123,6 +135,7 @@ curr1_reset_history Reset input current history. LTC3883 only. curr[N]_label "iout[1-4]". LTC2974: N=1-4 + LTC2977: not supported LTC2978: not supported LTC3880: N=2-3 LTC3883: N=2 diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c index 586a89e..e05ff56 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -1,5 +1,6 @@ /* - * Hardware monitoring driver for LTC2974, LTC2978, LTC3880, and LTC3883 + * Hardware monitoring driver for LTC2974, LTC2977, LTC2978, LTC3880, + * and LTC3883 * * Copyright (c) 2011 Ericsson AB. * Copyright (c) 2013 Guenter Roeck @@ -27,7 +28,7 @@ #include #include "pmbus.h" -enum chips { ltc2974, ltc2978, ltc3880, ltc3883 }; +enum chips { ltc2974, ltc2977, ltc2978, ltc3880, ltc3883 }; /* Common for all chips */ #define LTC2978_MFR_VOUT_PEAK 0xdd @@ -35,7 +36,7 @@ enum chips { ltc2974, ltc2978, ltc3880, ltc3883 }; #define LTC2978_MFR_TEMPERATURE_PEAK 0xdf #define LTC2978_MFR_SPECIAL_ID 0xe7 -/* LTC2974 and LTC2978 */ +/* LTC2974, LCT2977, and LTC2978 */ #define LTC2978_MFR_VOUT_MIN 0xfb #define LTC2978_MFR_VIN_MIN 0xfc #define LTC2978_MFR_TEMPERATURE_MIN 0xfd @@ -53,6 +54,7 @@ enum chips { ltc2974, ltc2978, ltc3880, ltc3883 }; #define LTC3883_MFR_IIN_PEAK 0xe1 #define LTC2974_ID 0x0212 +#define LTC2977_ID 0x0130 #define LTC2978_ID_REV1 0x0121 #define LTC2978_ID_REV2 0x0122 #define LTC3880_ID 0x4000 @@ -363,6 +365,7 @@ static int ltc2978_write_word_data(struct i2c_client *client, int page, static const struct i2c_device_id ltc2978_id[] = { {"ltc2974", ltc2974}, + {"ltc2977", ltc2977}, {"ltc2978", ltc2978}, {"ltc3880", ltc3880}, {"ltc3883", ltc3883}, @@ -392,6 +395,8 @@ static int ltc2978_probe(struct i2c_client *client, if (chip_id == LTC2974_ID) { data->id = ltc2974; + } else if (chip_id == LTC2977_ID) { + data->id = ltc2977; } else if (chip_id == LTC2978_ID_REV1 || chip_id == LTC2978_ID_REV2) { data->id = ltc2978; } else if ((chip_id & LTC3880_ID_MASK) == LTC3880_ID) { @@ -438,6 +443,7 @@ static int ltc2978_probe(struct i2c_client *client, | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; } break; + case ltc2977: case ltc2978: info->read_word_data = ltc2978_read_word_data; info->pages = LTC2978_NUM_PAGES; -- cgit v0.10.2 From 3f08d7f49f8365d5c9050522ee733951a503e955 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 23 Sep 2013 11:23:37 -0700 Subject: hwmon: (pmbus/ltc2978): Add support for LTC2978A Detect LTC2978A chip ID. Treat it as LC2978. Signed-off-by: Guenter Roeck diff --git a/Documentation/hwmon/ltc2978 b/Documentation/hwmon/ltc2978 index 6934936..a0546fc 100644 --- a/Documentation/hwmon/ltc2978 +++ b/Documentation/hwmon/ltc2978 @@ -10,10 +10,11 @@ Supported chips: Prefix: 'ltc2977' Addresses scanned: - Datasheet: http://www.linear.com/product/ltc2977 - * Linear Technology LTC2978 + * Linear Technology LTC2978, LTC2978A Prefix: 'ltc2978' Addresses scanned: - Datasheet: http://www.linear.com/product/ltc2978 + http://www.linear.com/product/ltc2978a * Linear Technology LTC3880 Prefix: 'ltc3880' Addresses scanned: - diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c index e05ff56..de3c152 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -57,6 +57,7 @@ enum chips { ltc2974, ltc2977, ltc2978, ltc3880, ltc3883 }; #define LTC2977_ID 0x0130 #define LTC2978_ID_REV1 0x0121 #define LTC2978_ID_REV2 0x0122 +#define LTC2978A_ID 0x0124 #define LTC3880_ID 0x4000 #define LTC3880_ID_MASK 0xff00 #define LTC3883_ID 0x4300 @@ -397,7 +398,8 @@ static int ltc2978_probe(struct i2c_client *client, data->id = ltc2974; } else if (chip_id == LTC2977_ID) { data->id = ltc2977; - } else if (chip_id == LTC2978_ID_REV1 || chip_id == LTC2978_ID_REV2) { + } else if (chip_id == LTC2978_ID_REV1 || chip_id == LTC2978_ID_REV2 || + chip_id == LTC2978A_ID) { data->id = ltc2978; } else if ((chip_id & LTC3880_ID_MASK) == LTC3880_ID) { data->id = ltc3880; -- cgit v0.10.2 From 454aee17fd441b8ee8e956196dd3ddc9c8ee96d6 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 29 Sep 2013 11:49:53 -0700 Subject: hwmon: (emc1403) Convert to use devm_hwmon_device_register_with_groups Simplify code and reduce its size. Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/emc1403.c b/drivers/hwmon/emc1403.c index 142e1cb..dc2a917 100644 --- a/drivers/hwmon/emc1403.c +++ b/drivers/hwmon/emc1403.c @@ -40,7 +40,7 @@ #define THERMAL_REVISION_REG 0xff struct thermal_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex mutex; /* * Cache the hyst value so we don't keep re-reading it. In theory @@ -53,10 +53,11 @@ struct thermal_data { static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); struct sensor_device_attribute *sda = to_sensor_dev_attr(attr); - int retval = i2c_smbus_read_byte_data(client, sda->index); + struct thermal_data *data = dev_get_drvdata(dev); + int retval; + retval = i2c_smbus_read_byte_data(data->client, sda->index); if (retval < 0) return retval; return sprintf(buf, "%d000\n", retval); @@ -65,27 +66,27 @@ static ssize_t show_temp(struct device *dev, static ssize_t show_bit(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); struct sensor_device_attribute_2 *sda = to_sensor_dev_attr_2(attr); - int retval = i2c_smbus_read_byte_data(client, sda->nr); + struct thermal_data *data = dev_get_drvdata(dev); + int retval; + retval = i2c_smbus_read_byte_data(data->client, sda->nr); if (retval < 0) return retval; - retval &= sda->index; - return sprintf(buf, "%d\n", retval ? 1 : 0); + return sprintf(buf, "%d\n", !!(retval & sda->index)); } static ssize_t store_temp(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct sensor_device_attribute *sda = to_sensor_dev_attr(attr); - struct i2c_client *client = to_i2c_client(dev); + struct thermal_data *data = dev_get_drvdata(dev); unsigned long val; int retval; if (kstrtoul(buf, 10, &val)) return -EINVAL; - retval = i2c_smbus_write_byte_data(client, sda->index, + retval = i2c_smbus_write_byte_data(data->client, sda->index, DIV_ROUND_CLOSEST(val, 1000)); if (retval < 0) return retval; @@ -95,9 +96,9 @@ static ssize_t store_temp(struct device *dev, static ssize_t store_bit(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct thermal_data *data = i2c_get_clientdata(client); struct sensor_device_attribute_2 *sda = to_sensor_dev_attr_2(attr); + struct thermal_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned long val; int retval; @@ -124,9 +125,9 @@ fail: static ssize_t show_hyst(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct thermal_data *data = i2c_get_clientdata(client); struct sensor_device_attribute *sda = to_sensor_dev_attr(attr); + struct thermal_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; int retval; int hyst; @@ -147,9 +148,9 @@ static ssize_t show_hyst(struct device *dev, static ssize_t store_hyst(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct thermal_data *data = i2c_get_clientdata(client); struct sensor_device_attribute *sda = to_sensor_dev_attr(attr); + struct thermal_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; int retval; int hyst; unsigned long val; @@ -235,7 +236,7 @@ static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO | S_IWUSR, static SENSOR_DEVICE_ATTR_2(power_state, S_IRUGO | S_IWUSR, show_bit, store_bit, 0x03, 0x40); -static struct attribute *mid_att_thermal[] = { +static struct attribute *emc1403_attrs[] = { &sensor_dev_attr_temp1_min.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, &sensor_dev_attr_temp1_crit.dev_attr.attr, @@ -263,10 +264,7 @@ static struct attribute *mid_att_thermal[] = { &sensor_dev_attr_power_state.dev_attr.attr, NULL }; - -static const struct attribute_group m_thermal_gr = { - .attrs = mid_att_thermal -}; +ATTRIBUTE_GROUPS(emc1403); static int emc1403_detect(struct i2c_client *client, struct i2c_board_info *info) @@ -304,43 +302,25 @@ static int emc1403_detect(struct i2c_client *client, static int emc1403_probe(struct i2c_client *client, const struct i2c_device_id *id) { - int res; struct thermal_data *data; + struct device *hwmon_dev; data = devm_kzalloc(&client->dev, sizeof(struct thermal_data), GFP_KERNEL); if (data == NULL) return -ENOMEM; - i2c_set_clientdata(client, data); + data->client = client; mutex_init(&data->mutex); data->hyst_valid = jiffies - 1; /* Expired */ - res = sysfs_create_group(&client->dev.kobj, &m_thermal_gr); - if (res) { - dev_warn(&client->dev, "create group failed\n"); - return res; - } - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - res = PTR_ERR(data->hwmon_dev); - dev_warn(&client->dev, "register hwmon dev failed\n"); - goto thermal_error; - } - dev_info(&client->dev, "EMC1403 Thermal chip found\n"); - return 0; - -thermal_error: - sysfs_remove_group(&client->dev.kobj, &m_thermal_gr); - return res; -} + hwmon_dev = hwmon_device_register_with_groups(&client->dev, + client->name, data, + emc1403_groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); -static int emc1403_remove(struct i2c_client *client) -{ - struct thermal_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &m_thermal_gr); + dev_info(&client->dev, "EMC1403 Thermal chip found\n"); return 0; } @@ -362,7 +342,6 @@ static struct i2c_driver sensor_emc1403 = { }, .detect = emc1403_detect, .probe = emc1403_probe, - .remove = emc1403_remove, .id_table = emc1403_idtable, .address_list = emc1403_address_list, }; -- cgit v0.10.2 From 0011ddfe6a03279fae60d237f2f4d0d7f7678928 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 29 Sep 2013 13:20:53 -0700 Subject: hwmon: (emc1403) Add support for EMC1404 and EMC1424 EMC1404 and EMC1424 are similar to EMC1403 and EMC1423, but support an additional external temperature sensor. Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/emc1403.c b/drivers/hwmon/emc1403.c index dc2a917..90ec117 100644 --- a/drivers/hwmon/emc1403.c +++ b/drivers/hwmon/emc1403.c @@ -21,7 +21,6 @@ * * TODO * - cache alarm and critical limit registers - * - add emc1404 support */ #include @@ -41,6 +40,7 @@ struct thermal_data { struct i2c_client *client; + const struct attribute_group *groups[3]; struct mutex mutex; /* * Cache the hyst value so we don't keep re-reading it. In theory @@ -233,6 +233,22 @@ static SENSOR_DEVICE_ATTR_2(temp3_crit_alarm, S_IRUGO, static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO | S_IWUSR, show_hyst, store_hyst, 0x1A); +static SENSOR_DEVICE_ATTR(temp4_min, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x2D); +static SENSOR_DEVICE_ATTR(temp4_max, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x2C); +static SENSOR_DEVICE_ATTR(temp4_crit, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x30); +static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 0x2A); +static SENSOR_DEVICE_ATTR_2(temp4_min_alarm, S_IRUGO, + show_bit, NULL, 0x36, 0x08); +static SENSOR_DEVICE_ATTR_2(temp4_max_alarm, S_IRUGO, + show_bit, NULL, 0x35, 0x08); +static SENSOR_DEVICE_ATTR_2(temp4_crit_alarm, S_IRUGO, + show_bit, NULL, 0x37, 0x08); +static SENSOR_DEVICE_ATTR(temp4_crit_hyst, S_IRUGO | S_IWUSR, + show_hyst, store_hyst, 0x30); + static SENSOR_DEVICE_ATTR_2(power_state, S_IRUGO | S_IWUSR, show_bit, store_bit, 0x03, 0x40); @@ -264,7 +280,26 @@ static struct attribute *emc1403_attrs[] = { &sensor_dev_attr_power_state.dev_attr.attr, NULL }; -ATTRIBUTE_GROUPS(emc1403); + +static const struct attribute_group emc1403_group = { + .attrs = emc1403_attrs, +}; + +static struct attribute *emc1404_attrs[] = { + &sensor_dev_attr_temp4_min.dev_attr.attr, + &sensor_dev_attr_temp4_max.dev_attr.attr, + &sensor_dev_attr_temp4_crit.dev_attr.attr, + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp4_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_crit_hyst.dev_attr.attr, + NULL +}; + +static const struct attribute_group emc1404_group = { + .attrs = emc1404_attrs, +}; static int emc1403_detect(struct i2c_client *client, struct i2c_board_info *info) @@ -284,10 +319,12 @@ static int emc1403_detect(struct i2c_client *client, case 0x23: strlcpy(info->type, "emc1423", I2C_NAME_SIZE); break; - /* - * Note: 0x25 is the 1404 which is very similar and this - * driver could be extended - */ + case 0x25: + strlcpy(info->type, "emc1404", I2C_NAME_SIZE); + break; + case 0x27: + strlcpy(info->type, "emc1424", I2C_NAME_SIZE); + break; default: return -ENODEV; } @@ -314,13 +351,17 @@ static int emc1403_probe(struct i2c_client *client, mutex_init(&data->mutex); data->hyst_valid = jiffies - 1; /* Expired */ + data->groups[0] = &emc1403_group; + if (id->driver_data) + data->groups[1] = &emc1404_group; + hwmon_dev = hwmon_device_register_with_groups(&client->dev, client->name, data, - emc1403_groups); + data->groups); if (IS_ERR(hwmon_dev)) return PTR_ERR(hwmon_dev); - dev_info(&client->dev, "EMC1403 Thermal chip found\n"); + dev_info(&client->dev, "%s Thermal chip found\n", id->name); return 0; } @@ -330,7 +371,9 @@ static const unsigned short emc1403_address_list[] = { static const struct i2c_device_id emc1403_idtable[] = { { "emc1403", 0 }, + { "emc1404", 1 }, { "emc1423", 0 }, + { "emc1424", 1 }, { } }; MODULE_DEVICE_TABLE(i2c, emc1403_idtable); -- cgit v0.10.2 From 23b4faa9a36257e75dade0f2945bc3e487e6f463 Mon Sep 17 00:00:00 2001 From: Markus Mayer Date: Fri, 18 Oct 2013 11:50:03 -0700 Subject: gpio: bcm281xx: Don't print addresses of GPIO area in probe() The physical address of the GPIO memory address is already printed implicitly by dev_info() as part of the device name. The virtual address doesn't add any value. In addition, printing a resource_size_t with %x lead to a compiler warning on some platforms. Signed-off-by: Markus Mayer Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index 58188ba..b3d0f81 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -587,8 +587,7 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev) } } - dev_info(&pdev->dev, "Setting up Kona GPIO at 0x%p (phys %#x)\n", - kona_gpio->reg_base, res->start); + dev_info(&pdev->dev, "Setting up Kona GPIO\n"); bcm_kona_gpio_reset(kona_gpio); -- cgit v0.10.2 From 98d6f4dd84a134d942827584a3c5f67ffd8ec35f Mon Sep 17 00:00:00 2001 From: KOSAKI Motohiro Date: Mon, 14 Oct 2013 17:33:16 -0400 Subject: alarmtimer: return EINVAL instead of ENOTSUPP if rtcdev doesn't exist Fedora Ruby maintainer reported latest Ruby doesn't work on Fedora Rawhide on ARM. (http://bugs.ruby-lang.org/issues/9008) Because of, commit 1c6b39ad3f (alarmtimers: Return -ENOTSUPP if no RTC device is present) intruduced to return ENOTSUPP when clock_get{time,res} can't find a RTC device. However this is incorrect. First, ENOTSUPP isn't exported to userland (ENOTSUP or EOPNOTSUP are the closest userland equivlents). Second, Posix and Linux man pages agree that clock_gettime and clock_getres should return EINVAL if clk_id argument is invalid. While the arugment that the clockid is valid, but just not supported on this hardware could be made, this is just a technicality that doesn't help userspace applicaitons, and only complicates error handling. Thus, this patch changes the code to use EINVAL. Cc: Thomas Gleixner Cc: Frederic Weisbecker Cc: stable #3.0 and up Reported-by: Vit Ondruch Signed-off-by: KOSAKI Motohiro [jstultz: Tweaks to commit message to include full rational] Signed-off-by: John Stultz diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c index eec50fc..88c9c65 100644 --- a/kernel/time/alarmtimer.c +++ b/kernel/time/alarmtimer.c @@ -490,7 +490,7 @@ static int alarm_clock_getres(const clockid_t which_clock, struct timespec *tp) clockid_t baseid = alarm_bases[clock2alarm(which_clock)].base_clockid; if (!alarmtimer_get_rtcdev()) - return -ENOTSUPP; + return -EINVAL; return hrtimer_get_res(baseid, tp); } @@ -507,7 +507,7 @@ static int alarm_clock_get(clockid_t which_clock, struct timespec *tp) struct alarm_base *base = &alarm_bases[clock2alarm(which_clock)]; if (!alarmtimer_get_rtcdev()) - return -ENOTSUPP; + return -EINVAL; *tp = ktime_to_timespec(base->gettime()); return 0; -- cgit v0.10.2 From b7bc50e45111e59419474154736f419a555158d9 Mon Sep 17 00:00:00 2001 From: Xie XiuQi Date: Fri, 18 Oct 2013 09:13:30 +0800 Subject: timekeeping: Fix some trivial typos in comments Fix some typos in timekeeping comments. Signed-off-by: Xie XiuQi [jstultz: Commit message tweaks] Signed-off-by: John Stultz diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 947ba25..3abf534 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -1613,9 +1613,10 @@ void get_xtime_and_monotonic_and_sleep_offset(struct timespec *xtim, * ktime_get_update_offsets - hrtimer helper * @offs_real: pointer to storage for monotonic -> realtime offset * @offs_boot: pointer to storage for monotonic -> boottime offset + * @offs_tai: pointer to storage for monotonic -> clock tai offset * * Returns current monotonic time and updates the offsets - * Called from hrtimer_interupt() or retrigger_next_event() + * Called from hrtimer_interrupt() or retrigger_next_event() */ ktime_t ktime_get_update_offsets(ktime_t *offs_real, ktime_t *offs_boot, ktime_t *offs_tai) -- cgit v0.10.2 From 8258e268c15a29c09d7c88d91a948bdf729433d8 Mon Sep 17 00:00:00 2001 From: Tiejun Chen Date: Thu, 20 Jun 2013 18:28:29 +0800 Subject: powerpc/kgdb: use DEFINE_PER_CPU to allocate kgdb's thread_info Use DEFINE_PER_CPU to allocate thread_info statically instead of kmalloc(). This can avoid introducing more memory check codes. Signed-off-by: Tiejun Chen [scottwood@freescale.com: wrapped long line] Signed-off-by: Scott Wood diff --git a/arch/powerpc/kernel/kgdb.c b/arch/powerpc/kernel/kgdb.c index c1eef24..83e89d3 100644 --- a/arch/powerpc/kernel/kgdb.c +++ b/arch/powerpc/kernel/kgdb.c @@ -151,15 +151,16 @@ static int kgdb_handle_breakpoint(struct pt_regs *regs) return 1; } +static DEFINE_PER_CPU(struct thread_info, kgdb_thread_info); static int kgdb_singlestep(struct pt_regs *regs) { struct thread_info *thread_info, *exception_thread_info; - struct thread_info *backup_current_thread_info; + struct thread_info *backup_current_thread_info = + &__get_cpu_var(kgdb_thread_info); if (user_mode(regs)) return 0; - backup_current_thread_info = kmalloc(sizeof(struct thread_info), GFP_KERNEL); /* * On Book E and perhaps other processors, singlestep is handled on * the critical exception stack. This causes current_thread_info() @@ -185,7 +186,6 @@ static int kgdb_singlestep(struct pt_regs *regs) /* Restore current_thread_info lastly. */ memcpy(exception_thread_info, backup_current_thread_info, sizeof *thread_info); - kfree(backup_current_thread_info); return 1; } -- cgit v0.10.2 From 660970fe97f86c0175576b4a942ebd29fb2ec64e Mon Sep 17 00:00:00 2001 From: Bharat Bhushan Date: Thu, 4 Jul 2013 11:45:45 +0530 Subject: powerpc: remove unnecessary line continuations Signed-off-by: Bharat Bhushan Acked-by: Michael Neuling Signed-off-by: Scott Wood diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 8649a3d..83079ef 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -327,7 +327,7 @@ static void set_debug_reg_defaults(struct thread_struct *thread) /* * Force User/Supervisor bits to b11 (user-only MSR[PR]=1) */ - thread->dbcr1 = DBCR1_IAC1US | DBCR1_IAC2US | \ + thread->dbcr1 = DBCR1_IAC1US | DBCR1_IAC2US | DBCR1_IAC3US | DBCR1_IAC4US; /* * Force Data Address Compare User/Supervisor bits to be User-only -- cgit v0.10.2 From 51ae8d4a2b9e4aa9a502061b9c39168e08829b94 Mon Sep 17 00:00:00 2001 From: Bharat Bhushan Date: Thu, 4 Jul 2013 11:45:46 +0530 Subject: powerpc: move debug registers in a structure This way we can use same data type struct with KVM and also help in using other debug related function. Signed-off-by: Bharat Bhushan Acked-by: Michael Neuling [scottwood@freescale.com: removed obvious debug_reg comment] Signed-off-by: Scott Wood diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h index c158307..7794b2b 100644 --- a/arch/powerpc/include/asm/processor.h +++ b/arch/powerpc/include/asm/processor.h @@ -167,21 +167,7 @@ struct thread_vr_state { vector128 vscr __attribute__((aligned(16))); }; -struct thread_struct { - unsigned long ksp; /* Kernel stack pointer */ -#ifdef CONFIG_PPC64 - unsigned long ksp_vsid; -#endif - struct pt_regs *regs; /* Pointer to saved register state */ - mm_segment_t fs; /* for get_fs() validation */ -#ifdef CONFIG_BOOKE - /* BookE base exception scratch space; align on cacheline */ - unsigned long normsave[8] ____cacheline_aligned; -#endif -#ifdef CONFIG_PPC32 - void *pgdir; /* root of page-table tree */ - unsigned long ksp_limit; /* if ksp <= ksp_limit stack overflow */ -#endif +struct debug_reg { #ifdef CONFIG_PPC_ADV_DEBUG_REGS /* * The following help to manage the use of Debug Control Registers @@ -218,6 +204,24 @@ struct thread_struct { unsigned long dvc2; #endif #endif +}; + +struct thread_struct { + unsigned long ksp; /* Kernel stack pointer */ +#ifdef CONFIG_PPC64 + unsigned long ksp_vsid; +#endif + struct pt_regs *regs; /* Pointer to saved register state */ + mm_segment_t fs; /* for get_fs() validation */ +#ifdef CONFIG_BOOKE + /* BookE base exception scratch space; align on cacheline */ + unsigned long normsave[8] ____cacheline_aligned; +#endif +#ifdef CONFIG_PPC32 + void *pgdir; /* root of page-table tree */ + unsigned long ksp_limit; /* if ksp <= ksp_limit stack overflow */ +#endif + struct debug_reg debug; struct thread_fp_state fp_state; struct thread_fp_state *fp_save_area; int fpexc_mode; /* floating-point exception mode */ diff --git a/arch/powerpc/include/asm/reg_booke.h b/arch/powerpc/include/asm/reg_booke.h index ed8f836..2e31aac 100644 --- a/arch/powerpc/include/asm/reg_booke.h +++ b/arch/powerpc/include/asm/reg_booke.h @@ -381,7 +381,7 @@ #define DBCR0_IA34T 0x00004000 /* Instr Addr 3-4 range Toggle */ #define DBCR0_FT 0x00000001 /* Freeze Timers on debug event */ -#define dbcr_iac_range(task) ((task)->thread.dbcr0) +#define dbcr_iac_range(task) ((task)->thread.debug.dbcr0) #define DBCR_IAC12I DBCR0_IA12 /* Range Inclusive */ #define DBCR_IAC12X (DBCR0_IA12 | DBCR0_IA12X) /* Range Exclusive */ #define DBCR_IAC12MODE (DBCR0_IA12 | DBCR0_IA12X) /* IAC 1-2 Mode Bits */ @@ -395,7 +395,7 @@ #define DBCR1_DAC1W 0x20000000 /* DAC1 Write Debug Event */ #define DBCR1_DAC2W 0x10000000 /* DAC2 Write Debug Event */ -#define dbcr_dac(task) ((task)->thread.dbcr1) +#define dbcr_dac(task) ((task)->thread.debug.dbcr1) #define DBCR_DAC1R DBCR1_DAC1R #define DBCR_DAC1W DBCR1_DAC1W #define DBCR_DAC2R DBCR1_DAC2R @@ -441,7 +441,7 @@ #define DBCR0_CRET 0x00000020 /* Critical Return Debug Event */ #define DBCR0_FT 0x00000001 /* Freeze Timers on debug event */ -#define dbcr_dac(task) ((task)->thread.dbcr0) +#define dbcr_dac(task) ((task)->thread.debug.dbcr0) #define DBCR_DAC1R DBCR0_DAC1R #define DBCR_DAC1W DBCR0_DAC1W #define DBCR_DAC2R DBCR0_DAC2R @@ -475,7 +475,7 @@ #define DBCR1_IAC34MX 0x000000C0 /* Instr Addr 3-4 range eXclusive */ #define DBCR1_IAC34AT 0x00000001 /* Instr Addr 3-4 range Toggle */ -#define dbcr_iac_range(task) ((task)->thread.dbcr1) +#define dbcr_iac_range(task) ((task)->thread.debug.dbcr1) #define DBCR_IAC12I DBCR1_IAC12M /* Range Inclusive */ #define DBCR_IAC12X DBCR1_IAC12MX /* Range Exclusive */ #define DBCR_IAC12MODE DBCR1_IAC12MX /* IAC 1-2 Mode Bits */ diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 6278edd..e60a369 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -115,7 +115,7 @@ int main(void) #endif /* CONFIG_SPE */ #endif /* CONFIG_PPC64 */ #if defined(CONFIG_4xx) || defined(CONFIG_BOOKE) - DEFINE(THREAD_DBCR0, offsetof(struct thread_struct, dbcr0)); + DEFINE(THREAD_DBCR0, offsetof(struct thread_struct, debug.dbcr0)); #endif #ifdef CONFIG_KVM_BOOK3S_32_HANDLER DEFINE(THREAD_KVM_SVCPU, offsetof(struct thread_struct, kvm_shadow_vcpu)); diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 83079ef..3db9d7e 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -314,28 +314,28 @@ static DEFINE_PER_CPU(struct arch_hw_breakpoint, current_brk); */ static void set_debug_reg_defaults(struct thread_struct *thread) { - thread->iac1 = thread->iac2 = 0; + thread->debug.iac1 = thread->debug.iac2 = 0; #if CONFIG_PPC_ADV_DEBUG_IACS > 2 - thread->iac3 = thread->iac4 = 0; + thread->debug.iac3 = thread->debug.iac4 = 0; #endif - thread->dac1 = thread->dac2 = 0; + thread->debug.dac1 = thread->debug.dac2 = 0; #if CONFIG_PPC_ADV_DEBUG_DVCS > 0 - thread->dvc1 = thread->dvc2 = 0; + thread->debug.dvc1 = thread->debug.dvc2 = 0; #endif - thread->dbcr0 = 0; + thread->debug.dbcr0 = 0; #ifdef CONFIG_BOOKE /* * Force User/Supervisor bits to b11 (user-only MSR[PR]=1) */ - thread->dbcr1 = DBCR1_IAC1US | DBCR1_IAC2US | + thread->debug.dbcr1 = DBCR1_IAC1US | DBCR1_IAC2US | DBCR1_IAC3US | DBCR1_IAC4US; /* * Force Data Address Compare User/Supervisor bits to be User-only * (0b11 MSR[PR]=1) and set all other bits in DBCR2 register to be 0. */ - thread->dbcr2 = DBCR2_DAC1US | DBCR2_DAC2US; + thread->debug.dbcr2 = DBCR2_DAC1US | DBCR2_DAC2US; #else - thread->dbcr1 = 0; + thread->debug.dbcr1 = 0; #endif } @@ -348,22 +348,22 @@ static void prime_debug_regs(struct thread_struct *thread) */ mtmsr(mfmsr() & ~MSR_DE); - mtspr(SPRN_IAC1, thread->iac1); - mtspr(SPRN_IAC2, thread->iac2); + mtspr(SPRN_IAC1, thread->debug.iac1); + mtspr(SPRN_IAC2, thread->debug.iac2); #if CONFIG_PPC_ADV_DEBUG_IACS > 2 - mtspr(SPRN_IAC3, thread->iac3); - mtspr(SPRN_IAC4, thread->iac4); + mtspr(SPRN_IAC3, thread->debug.iac3); + mtspr(SPRN_IAC4, thread->debug.iac4); #endif - mtspr(SPRN_DAC1, thread->dac1); - mtspr(SPRN_DAC2, thread->dac2); + mtspr(SPRN_DAC1, thread->debug.dac1); + mtspr(SPRN_DAC2, thread->debug.dac2); #if CONFIG_PPC_ADV_DEBUG_DVCS > 0 - mtspr(SPRN_DVC1, thread->dvc1); - mtspr(SPRN_DVC2, thread->dvc2); + mtspr(SPRN_DVC1, thread->debug.dvc1); + mtspr(SPRN_DVC2, thread->debug.dvc2); #endif - mtspr(SPRN_DBCR0, thread->dbcr0); - mtspr(SPRN_DBCR1, thread->dbcr1); + mtspr(SPRN_DBCR0, thread->debug.dbcr0); + mtspr(SPRN_DBCR1, thread->debug.dbcr1); #ifdef CONFIG_BOOKE - mtspr(SPRN_DBCR2, thread->dbcr2); + mtspr(SPRN_DBCR2, thread->debug.dbcr2); #endif } /* @@ -373,8 +373,8 @@ static void prime_debug_regs(struct thread_struct *thread) */ static void switch_booke_debug_regs(struct thread_struct *new_thread) { - if ((current->thread.dbcr0 & DBCR0_IDM) - || (new_thread->dbcr0 & DBCR0_IDM)) + if ((current->thread.debug.dbcr0 & DBCR0_IDM) + || (new_thread->debug.dbcr0 & DBCR0_IDM)) prime_debug_regs(new_thread); } #else /* !CONFIG_PPC_ADV_DEBUG_REGS */ diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index 1ca589c..aedfd41 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c @@ -855,8 +855,8 @@ void user_enable_single_step(struct task_struct *task) if (regs != NULL) { #ifdef CONFIG_PPC_ADV_DEBUG_REGS - task->thread.dbcr0 &= ~DBCR0_BT; - task->thread.dbcr0 |= DBCR0_IDM | DBCR0_IC; + task->thread.debug.dbcr0 &= ~DBCR0_BT; + task->thread.debug.dbcr0 |= DBCR0_IDM | DBCR0_IC; regs->msr |= MSR_DE; #else regs->msr &= ~MSR_BE; @@ -872,8 +872,8 @@ void user_enable_block_step(struct task_struct *task) if (regs != NULL) { #ifdef CONFIG_PPC_ADV_DEBUG_REGS - task->thread.dbcr0 &= ~DBCR0_IC; - task->thread.dbcr0 = DBCR0_IDM | DBCR0_BT; + task->thread.debug.dbcr0 &= ~DBCR0_IC; + task->thread.debug.dbcr0 = DBCR0_IDM | DBCR0_BT; regs->msr |= MSR_DE; #else regs->msr &= ~MSR_SE; @@ -895,16 +895,16 @@ void user_disable_single_step(struct task_struct *task) * And, after doing so, if all debug flags are off, turn * off DBCR0(IDM) and MSR(DE) .... Torez */ - task->thread.dbcr0 &= ~DBCR0_IC; + task->thread.debug.dbcr0 &= ~DBCR0_IC; /* * Test to see if any of the DBCR_ACTIVE_EVENTS bits are set. */ - if (!DBCR_ACTIVE_EVENTS(task->thread.dbcr0, - task->thread.dbcr1)) { + if (!DBCR_ACTIVE_EVENTS(task->thread.debug.dbcr0, + task->thread.debug.dbcr1)) { /* * All debug events were off..... */ - task->thread.dbcr0 &= ~DBCR0_IDM; + task->thread.debug.dbcr0 &= ~DBCR0_IDM; regs->msr &= ~MSR_DE; } #else @@ -1023,14 +1023,14 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr, */ /* DAC's hold the whole address without any mode flags */ - task->thread.dac1 = data & ~0x3UL; + task->thread.debug.dac1 = data & ~0x3UL; - if (task->thread.dac1 == 0) { + if (task->thread.debug.dac1 == 0) { dbcr_dac(task) &= ~(DBCR_DAC1R | DBCR_DAC1W); - if (!DBCR_ACTIVE_EVENTS(task->thread.dbcr0, - task->thread.dbcr1)) { + if (!DBCR_ACTIVE_EVENTS(task->thread.debug.dbcr0, + task->thread.debug.dbcr1)) { task->thread.regs->msr &= ~MSR_DE; - task->thread.dbcr0 &= ~DBCR0_IDM; + task->thread.debug.dbcr0 &= ~DBCR0_IDM; } return 0; } @@ -1042,7 +1042,7 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr, /* Set the Internal Debugging flag (IDM bit 1) for the DBCR0 register */ - task->thread.dbcr0 |= DBCR0_IDM; + task->thread.debug.dbcr0 |= DBCR0_IDM; /* Check for write and read flags and set DBCR0 accordingly */ @@ -1072,10 +1072,10 @@ static long set_instruction_bp(struct task_struct *child, struct ppc_hw_breakpoint *bp_info) { int slot; - int slot1_in_use = ((child->thread.dbcr0 & DBCR0_IAC1) != 0); - int slot2_in_use = ((child->thread.dbcr0 & DBCR0_IAC2) != 0); - int slot3_in_use = ((child->thread.dbcr0 & DBCR0_IAC3) != 0); - int slot4_in_use = ((child->thread.dbcr0 & DBCR0_IAC4) != 0); + int slot1_in_use = ((child->thread.debug.dbcr0 & DBCR0_IAC1) != 0); + int slot2_in_use = ((child->thread.debug.dbcr0 & DBCR0_IAC2) != 0); + int slot3_in_use = ((child->thread.debug.dbcr0 & DBCR0_IAC3) != 0); + int slot4_in_use = ((child->thread.debug.dbcr0 & DBCR0_IAC4) != 0); if (dbcr_iac_range(child) & DBCR_IAC12MODE) slot2_in_use = 1; @@ -1094,9 +1094,9 @@ static long set_instruction_bp(struct task_struct *child, /* We need a pair of IAC regsisters */ if ((!slot1_in_use) && (!slot2_in_use)) { slot = 1; - child->thread.iac1 = bp_info->addr; - child->thread.iac2 = bp_info->addr2; - child->thread.dbcr0 |= DBCR0_IAC1; + child->thread.debug.iac1 = bp_info->addr; + child->thread.debug.iac2 = bp_info->addr2; + child->thread.debug.dbcr0 |= DBCR0_IAC1; if (bp_info->addr_mode == PPC_BREAKPOINT_MODE_RANGE_EXCLUSIVE) dbcr_iac_range(child) |= DBCR_IAC12X; @@ -1105,9 +1105,9 @@ static long set_instruction_bp(struct task_struct *child, #if CONFIG_PPC_ADV_DEBUG_IACS > 2 } else if ((!slot3_in_use) && (!slot4_in_use)) { slot = 3; - child->thread.iac3 = bp_info->addr; - child->thread.iac4 = bp_info->addr2; - child->thread.dbcr0 |= DBCR0_IAC3; + child->thread.debug.iac3 = bp_info->addr; + child->thread.debug.iac4 = bp_info->addr2; + child->thread.debug.dbcr0 |= DBCR0_IAC3; if (bp_info->addr_mode == PPC_BREAKPOINT_MODE_RANGE_EXCLUSIVE) dbcr_iac_range(child) |= DBCR_IAC34X; @@ -1127,30 +1127,30 @@ static long set_instruction_bp(struct task_struct *child, */ if (slot2_in_use || (slot3_in_use == slot4_in_use)) { slot = 1; - child->thread.iac1 = bp_info->addr; - child->thread.dbcr0 |= DBCR0_IAC1; + child->thread.debug.iac1 = bp_info->addr; + child->thread.debug.dbcr0 |= DBCR0_IAC1; goto out; } } if (!slot2_in_use) { slot = 2; - child->thread.iac2 = bp_info->addr; - child->thread.dbcr0 |= DBCR0_IAC2; + child->thread.debug.iac2 = bp_info->addr; + child->thread.debug.dbcr0 |= DBCR0_IAC2; #if CONFIG_PPC_ADV_DEBUG_IACS > 2 } else if (!slot3_in_use) { slot = 3; - child->thread.iac3 = bp_info->addr; - child->thread.dbcr0 |= DBCR0_IAC3; + child->thread.debug.iac3 = bp_info->addr; + child->thread.debug.dbcr0 |= DBCR0_IAC3; } else if (!slot4_in_use) { slot = 4; - child->thread.iac4 = bp_info->addr; - child->thread.dbcr0 |= DBCR0_IAC4; + child->thread.debug.iac4 = bp_info->addr; + child->thread.debug.dbcr0 |= DBCR0_IAC4; #endif } else return -ENOSPC; } out: - child->thread.dbcr0 |= DBCR0_IDM; + child->thread.debug.dbcr0 |= DBCR0_IDM; child->thread.regs->msr |= MSR_DE; return slot; @@ -1160,49 +1160,49 @@ static int del_instruction_bp(struct task_struct *child, int slot) { switch (slot) { case 1: - if ((child->thread.dbcr0 & DBCR0_IAC1) == 0) + if ((child->thread.debug.dbcr0 & DBCR0_IAC1) == 0) return -ENOENT; if (dbcr_iac_range(child) & DBCR_IAC12MODE) { /* address range - clear slots 1 & 2 */ - child->thread.iac2 = 0; + child->thread.debug.iac2 = 0; dbcr_iac_range(child) &= ~DBCR_IAC12MODE; } - child->thread.iac1 = 0; - child->thread.dbcr0 &= ~DBCR0_IAC1; + child->thread.debug.iac1 = 0; + child->thread.debug.dbcr0 &= ~DBCR0_IAC1; break; case 2: - if ((child->thread.dbcr0 & DBCR0_IAC2) == 0) + if ((child->thread.debug.dbcr0 & DBCR0_IAC2) == 0) return -ENOENT; if (dbcr_iac_range(child) & DBCR_IAC12MODE) /* used in a range */ return -EINVAL; - child->thread.iac2 = 0; - child->thread.dbcr0 &= ~DBCR0_IAC2; + child->thread.debug.iac2 = 0; + child->thread.debug.dbcr0 &= ~DBCR0_IAC2; break; #if CONFIG_PPC_ADV_DEBUG_IACS > 2 case 3: - if ((child->thread.dbcr0 & DBCR0_IAC3) == 0) + if ((child->thread.debug.dbcr0 & DBCR0_IAC3) == 0) return -ENOENT; if (dbcr_iac_range(child) & DBCR_IAC34MODE) { /* address range - clear slots 3 & 4 */ - child->thread.iac4 = 0; + child->thread.debug.iac4 = 0; dbcr_iac_range(child) &= ~DBCR_IAC34MODE; } - child->thread.iac3 = 0; - child->thread.dbcr0 &= ~DBCR0_IAC3; + child->thread.debug.iac3 = 0; + child->thread.debug.dbcr0 &= ~DBCR0_IAC3; break; case 4: - if ((child->thread.dbcr0 & DBCR0_IAC4) == 0) + if ((child->thread.debug.dbcr0 & DBCR0_IAC4) == 0) return -ENOENT; if (dbcr_iac_range(child) & DBCR_IAC34MODE) /* Used in a range */ return -EINVAL; - child->thread.iac4 = 0; - child->thread.dbcr0 &= ~DBCR0_IAC4; + child->thread.debug.iac4 = 0; + child->thread.debug.dbcr0 &= ~DBCR0_IAC4; break; #endif default: @@ -1232,18 +1232,18 @@ static int set_dac(struct task_struct *child, struct ppc_hw_breakpoint *bp_info) dbcr_dac(child) |= DBCR_DAC1R; if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_WRITE) dbcr_dac(child) |= DBCR_DAC1W; - child->thread.dac1 = (unsigned long)bp_info->addr; + child->thread.debug.dac1 = (unsigned long)bp_info->addr; #if CONFIG_PPC_ADV_DEBUG_DVCS > 0 if (byte_enable) { - child->thread.dvc1 = + child->thread.debug.dvc1 = (unsigned long)bp_info->condition_value; - child->thread.dbcr2 |= + child->thread.debug.dbcr2 |= ((byte_enable << DBCR2_DVC1BE_SHIFT) | (condition_mode << DBCR2_DVC1M_SHIFT)); } #endif #ifdef CONFIG_PPC_ADV_DEBUG_DAC_RANGE - } else if (child->thread.dbcr2 & DBCR2_DAC12MODE) { + } else if (child->thread.debug.dbcr2 & DBCR2_DAC12MODE) { /* Both dac1 and dac2 are part of a range */ return -ENOSPC; #endif @@ -1253,19 +1253,19 @@ static int set_dac(struct task_struct *child, struct ppc_hw_breakpoint *bp_info) dbcr_dac(child) |= DBCR_DAC2R; if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_WRITE) dbcr_dac(child) |= DBCR_DAC2W; - child->thread.dac2 = (unsigned long)bp_info->addr; + child->thread.debug.dac2 = (unsigned long)bp_info->addr; #if CONFIG_PPC_ADV_DEBUG_DVCS > 0 if (byte_enable) { - child->thread.dvc2 = + child->thread.debug.dvc2 = (unsigned long)bp_info->condition_value; - child->thread.dbcr2 |= + child->thread.debug.dbcr2 |= ((byte_enable << DBCR2_DVC2BE_SHIFT) | (condition_mode << DBCR2_DVC2M_SHIFT)); } #endif } else return -ENOSPC; - child->thread.dbcr0 |= DBCR0_IDM; + child->thread.debug.dbcr0 |= DBCR0_IDM; child->thread.regs->msr |= MSR_DE; return slot + 4; @@ -1277,32 +1277,32 @@ static int del_dac(struct task_struct *child, int slot) if ((dbcr_dac(child) & (DBCR_DAC1R | DBCR_DAC1W)) == 0) return -ENOENT; - child->thread.dac1 = 0; + child->thread.debug.dac1 = 0; dbcr_dac(child) &= ~(DBCR_DAC1R | DBCR_DAC1W); #ifdef CONFIG_PPC_ADV_DEBUG_DAC_RANGE - if (child->thread.dbcr2 & DBCR2_DAC12MODE) { - child->thread.dac2 = 0; - child->thread.dbcr2 &= ~DBCR2_DAC12MODE; + if (child->thread.debug.dbcr2 & DBCR2_DAC12MODE) { + child->thread.debug.dac2 = 0; + child->thread.debug.dbcr2 &= ~DBCR2_DAC12MODE; } - child->thread.dbcr2 &= ~(DBCR2_DVC1M | DBCR2_DVC1BE); + child->thread.debug.dbcr2 &= ~(DBCR2_DVC1M | DBCR2_DVC1BE); #endif #if CONFIG_PPC_ADV_DEBUG_DVCS > 0 - child->thread.dvc1 = 0; + child->thread.debug.dvc1 = 0; #endif } else if (slot == 2) { if ((dbcr_dac(child) & (DBCR_DAC2R | DBCR_DAC2W)) == 0) return -ENOENT; #ifdef CONFIG_PPC_ADV_DEBUG_DAC_RANGE - if (child->thread.dbcr2 & DBCR2_DAC12MODE) + if (child->thread.debug.dbcr2 & DBCR2_DAC12MODE) /* Part of a range */ return -EINVAL; - child->thread.dbcr2 &= ~(DBCR2_DVC2M | DBCR2_DVC2BE); + child->thread.debug.dbcr2 &= ~(DBCR2_DVC2M | DBCR2_DVC2BE); #endif #if CONFIG_PPC_ADV_DEBUG_DVCS > 0 - child->thread.dvc2 = 0; + child->thread.debug.dvc2 = 0; #endif - child->thread.dac2 = 0; + child->thread.debug.dac2 = 0; dbcr_dac(child) &= ~(DBCR_DAC2R | DBCR_DAC2W); } else return -EINVAL; @@ -1344,22 +1344,22 @@ static int set_dac_range(struct task_struct *child, return -EIO; } - if (child->thread.dbcr0 & + if (child->thread.debug.dbcr0 & (DBCR0_DAC1R | DBCR0_DAC1W | DBCR0_DAC2R | DBCR0_DAC2W)) return -ENOSPC; if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_READ) - child->thread.dbcr0 |= (DBCR0_DAC1R | DBCR0_IDM); + child->thread.debug.dbcr0 |= (DBCR0_DAC1R | DBCR0_IDM); if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_WRITE) - child->thread.dbcr0 |= (DBCR0_DAC1W | DBCR0_IDM); - child->thread.dac1 = bp_info->addr; - child->thread.dac2 = bp_info->addr2; + child->thread.debug.dbcr0 |= (DBCR0_DAC1W | DBCR0_IDM); + child->thread.debug.dac1 = bp_info->addr; + child->thread.debug.dac2 = bp_info->addr2; if (mode == PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE) - child->thread.dbcr2 |= DBCR2_DAC12M; + child->thread.debug.dbcr2 |= DBCR2_DAC12M; else if (mode == PPC_BREAKPOINT_MODE_RANGE_EXCLUSIVE) - child->thread.dbcr2 |= DBCR2_DAC12MX; + child->thread.debug.dbcr2 |= DBCR2_DAC12MX; else /* PPC_BREAKPOINT_MODE_MASK */ - child->thread.dbcr2 |= DBCR2_DAC12MM; + child->thread.debug.dbcr2 |= DBCR2_DAC12MM; child->thread.regs->msr |= MSR_DE; return 5; @@ -1490,9 +1490,9 @@ static long ppc_del_hwdebug(struct task_struct *child, long data) rc = del_dac(child, (int)data - 4); if (!rc) { - if (!DBCR_ACTIVE_EVENTS(child->thread.dbcr0, - child->thread.dbcr1)) { - child->thread.dbcr0 &= ~DBCR0_IDM; + if (!DBCR_ACTIVE_EVENTS(child->thread.debug.dbcr0, + child->thread.debug.dbcr1)) { + child->thread.debug.dbcr0 &= ~DBCR0_IDM; child->thread.regs->msr &= ~MSR_DE; } } @@ -1670,7 +1670,7 @@ long arch_ptrace(struct task_struct *child, long request, if (addr > 0) break; #ifdef CONFIG_PPC_ADV_DEBUG_REGS - ret = put_user(child->thread.dac1, datalp); + ret = put_user(child->thread.debug.dac1, datalp); #else dabr_fake = ((child->thread.hw_brk.address & (~HW_BRK_TYPE_DABR)) | (child->thread.hw_brk.type & HW_BRK_TYPE_DABR)); diff --git a/arch/powerpc/kernel/ptrace32.c b/arch/powerpc/kernel/ptrace32.c index 097f8dc..f52b7db 100644 --- a/arch/powerpc/kernel/ptrace32.c +++ b/arch/powerpc/kernel/ptrace32.c @@ -266,7 +266,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, if (addr > 0) break; #ifdef CONFIG_PPC_ADV_DEBUG_REGS - ret = put_user(child->thread.dac1, (u32 __user *)data); + ret = put_user(child->thread.debug.dac1, (u32 __user *)data); #else dabr_fake = ( (child->thread.hw_brk.address & (~HW_BRK_TYPE_DABR)) | diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index c094e28..1a410aa 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c @@ -1312,7 +1312,7 @@ int sys_debug_setcontext(struct ucontext __user *ctx, unsigned char tmp; unsigned long new_msr = regs->msr; #ifdef CONFIG_PPC_ADV_DEBUG_REGS - unsigned long new_dbcr0 = current->thread.dbcr0; + unsigned long new_dbcr0 = current->thread.debug.dbcr0; #endif for (i=0; ithread.dbcr1)) { + current->thread.debug.dbcr1)) { new_msr &= ~MSR_DE; new_dbcr0 &= ~DBCR0_IDM; } @@ -1362,7 +1362,7 @@ int sys_debug_setcontext(struct ucontext __user *ctx, the user is really doing something wrong. */ regs->msr = new_msr; #ifdef CONFIG_PPC_ADV_DEBUG_REGS - current->thread.dbcr0 = new_dbcr0; + current->thread.debug.dbcr0 = new_dbcr0; #endif if (!access_ok(VERIFY_READ, ctx, sizeof(*ctx)) diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 36a1f95..f686686 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -351,8 +351,8 @@ static inline int check_io_access(struct pt_regs *regs) #define REASON_TRAP ESR_PTR /* single-step stuff */ -#define single_stepping(regs) (current->thread.dbcr0 & DBCR0_IC) -#define clear_single_step(regs) (current->thread.dbcr0 &= ~DBCR0_IC) +#define single_stepping(regs) (current->thread.debug.dbcr0 & DBCR0_IC) +#define clear_single_step(regs) (current->thread.debug.dbcr0 &= ~DBCR0_IC) #else /* On non-4xx, the reason for the machine check or program @@ -1489,7 +1489,7 @@ static void handle_debug(struct pt_regs *regs, unsigned long debug_status) if (debug_status & (DBSR_DAC1R | DBSR_DAC1W)) { dbcr_dac(current) &= ~(DBCR_DAC1R | DBCR_DAC1W); #ifdef CONFIG_PPC_ADV_DEBUG_DAC_RANGE - current->thread.dbcr2 &= ~DBCR2_DAC12MODE; + current->thread.debug.dbcr2 &= ~DBCR2_DAC12MODE; #endif do_send_trap(regs, mfspr(SPRN_DAC1), debug_status, TRAP_HWBKPT, 5); @@ -1500,24 +1500,24 @@ static void handle_debug(struct pt_regs *regs, unsigned long debug_status) 6); changed |= 0x01; } else if (debug_status & DBSR_IAC1) { - current->thread.dbcr0 &= ~DBCR0_IAC1; + current->thread.debug.dbcr0 &= ~DBCR0_IAC1; dbcr_iac_range(current) &= ~DBCR_IAC12MODE; do_send_trap(regs, mfspr(SPRN_IAC1), debug_status, TRAP_HWBKPT, 1); changed |= 0x01; } else if (debug_status & DBSR_IAC2) { - current->thread.dbcr0 &= ~DBCR0_IAC2; + current->thread.debug.dbcr0 &= ~DBCR0_IAC2; do_send_trap(regs, mfspr(SPRN_IAC2), debug_status, TRAP_HWBKPT, 2); changed |= 0x01; } else if (debug_status & DBSR_IAC3) { - current->thread.dbcr0 &= ~DBCR0_IAC3; + current->thread.debug.dbcr0 &= ~DBCR0_IAC3; dbcr_iac_range(current) &= ~DBCR_IAC34MODE; do_send_trap(regs, mfspr(SPRN_IAC3), debug_status, TRAP_HWBKPT, 3); changed |= 0x01; } else if (debug_status & DBSR_IAC4) { - current->thread.dbcr0 &= ~DBCR0_IAC4; + current->thread.debug.dbcr0 &= ~DBCR0_IAC4; do_send_trap(regs, mfspr(SPRN_IAC4), debug_status, TRAP_HWBKPT, 4); changed |= 0x01; @@ -1527,19 +1527,20 @@ static void handle_debug(struct pt_regs *regs, unsigned long debug_status) * Check all other debug flags and see if that bit needs to be turned * back on or not. */ - if (DBCR_ACTIVE_EVENTS(current->thread.dbcr0, current->thread.dbcr1)) + if (DBCR_ACTIVE_EVENTS(current->thread.debug.dbcr0, + current->thread.debug.dbcr1)) regs->msr |= MSR_DE; else /* Make sure the IDM flag is off */ - current->thread.dbcr0 &= ~DBCR0_IDM; + current->thread.debug.dbcr0 &= ~DBCR0_IDM; if (changed & 0x01) - mtspr(SPRN_DBCR0, current->thread.dbcr0); + mtspr(SPRN_DBCR0, current->thread.debug.dbcr0); } void __kprobes DebugException(struct pt_regs *regs, unsigned long debug_status) { - current->thread.dbsr = debug_status; + current->thread.debug.dbsr = debug_status; /* Hack alert: On BookE, Branch Taken stops on the branch itself, while * on server, it stops on the target of the branch. In order to simulate @@ -1556,8 +1557,8 @@ void __kprobes DebugException(struct pt_regs *regs, unsigned long debug_status) /* Do the single step trick only when coming from userspace */ if (user_mode(regs)) { - current->thread.dbcr0 &= ~DBCR0_BT; - current->thread.dbcr0 |= DBCR0_IDM | DBCR0_IC; + current->thread.debug.dbcr0 &= ~DBCR0_BT; + current->thread.debug.dbcr0 |= DBCR0_IDM | DBCR0_IC; regs->msr |= MSR_DE; return; } @@ -1585,13 +1586,13 @@ void __kprobes DebugException(struct pt_regs *regs, unsigned long debug_status) return; if (user_mode(regs)) { - current->thread.dbcr0 &= ~DBCR0_IC; - if (DBCR_ACTIVE_EVENTS(current->thread.dbcr0, - current->thread.dbcr1)) + current->thread.debug.dbcr0 &= ~DBCR0_IC; + if (DBCR_ACTIVE_EVENTS(current->thread.debug.dbcr0, + current->thread.debug.dbcr1)) regs->msr |= MSR_DE; else /* Make sure the IDM bit is off */ - current->thread.dbcr0 &= ~DBCR0_IDM; + current->thread.debug.dbcr0 &= ~DBCR0_IDM; } _exception(SIGTRAP, regs, TRAP_TRACE, regs->nip); -- cgit v0.10.2 From 891292a767c2453af0e5be9465e95b06b4b29ebe Mon Sep 17 00:00:00 2001 From: Patrick Palka Date: Fri, 11 Oct 2013 13:11:55 -0400 Subject: time: Fix signedness bug in sysfs_get_uname() and its callers sysfs_get_uname() is erroneously declared as returning size_t even though it may return a negative value, specifically -EINVAL. Its callers then check whether its return value is less than zero and indeed that is never the case for size_t. This patch changes sysfs_get_uname() to return ssize_t and makes sure its callers use ssize_t accordingly. Signed-off-by: Patrick Palka [jstultz: Didn't apply cleanly, as a similar partial fix was also applied so had to resolve the collisions] Signed-off-by: John Stultz diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c index 38959c8..30554b9 100644 --- a/kernel/time/clockevents.c +++ b/kernel/time/clockevents.c @@ -584,7 +584,7 @@ static ssize_t sysfs_unbind_tick_dev(struct device *dev, const char *buf, size_t count) { char name[CS_NAME_LEN]; - size_t ret = sysfs_get_uname(buf, name, count); + ssize_t ret = sysfs_get_uname(buf, name, count); struct clock_event_device *ce; if (ret < 0) diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index c9317e1..ba3e502 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -909,7 +909,7 @@ sysfs_show_current_clocksources(struct device *dev, return count; } -size_t sysfs_get_uname(const char *buf, char *dst, size_t cnt) +ssize_t sysfs_get_uname(const char *buf, char *dst, size_t cnt) { size_t ret = cnt; diff --git a/kernel/time/tick-internal.h b/kernel/time/tick-internal.h index bc906ca..18e71f7 100644 --- a/kernel/time/tick-internal.h +++ b/kernel/time/tick-internal.h @@ -31,7 +31,7 @@ extern void tick_install_replacement(struct clock_event_device *dev); extern void clockevents_shutdown(struct clock_event_device *dev); -extern size_t sysfs_get_uname(const char *buf, char *dst, size_t cnt); +extern ssize_t sysfs_get_uname(const char *buf, char *dst, size_t cnt); /* * NO_HZ / high resolution timer shared code -- cgit v0.10.2 From 3743c9b8ceb638b6e4b78b42f2262e22aa6359f0 Mon Sep 17 00:00:00 2001 From: Bharat Bhushan Date: Thu, 4 Jul 2013 12:27:44 +0530 Subject: powerpc: export debug registers save function for KVM KVM need this function when switching from vcpu to user-space thread. My subsequent patch will use this function. Signed-off-by: Bharat Bhushan Acked-by: Michael Neuling Signed-off-by: Scott Wood diff --git a/arch/powerpc/include/asm/switch_to.h b/arch/powerpc/include/asm/switch_to.h index 2be5618..9ee1261 100644 --- a/arch/powerpc/include/asm/switch_to.h +++ b/arch/powerpc/include/asm/switch_to.h @@ -35,6 +35,7 @@ extern void giveup_vsx(struct task_struct *); extern void enable_kernel_spe(void); extern void giveup_spe(struct task_struct *); extern void load_up_spe(struct task_struct *); +extern void switch_booke_debug_regs(struct thread_struct *new_thread); #ifndef CONFIG_SMP extern void discard_lazy_cpu_state(void); diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 3db9d7e..4d42c4d 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -371,12 +371,13 @@ static void prime_debug_regs(struct thread_struct *thread) * debug registers, set the debug registers from the values * stored in the new thread. */ -static void switch_booke_debug_regs(struct thread_struct *new_thread) +void switch_booke_debug_regs(struct thread_struct *new_thread) { if ((current->thread.debug.dbcr0 & DBCR0_IDM) || (new_thread->debug.dbcr0 & DBCR0_IDM)) prime_debug_regs(new_thread); } +EXPORT_SYMBOL_GPL(switch_booke_debug_regs); #else /* !CONFIG_PPC_ADV_DEBUG_REGS */ #ifndef CONFIG_HAVE_HW_BREAKPOINT static void set_debug_reg_defaults(struct thread_struct *thread) -- cgit v0.10.2 From 682775b8de995d97956447730c04d2ff978d4e13 Mon Sep 17 00:00:00 2001 From: James Yang Date: Fri, 5 Jul 2013 14:49:43 -0500 Subject: powerpc/booke: clear DBCR0_BT in user_disable_single_step() BookE version of user_disable_single_step() clears DBCR0_IC for the instruction completion debug, but did not also clear DBCR0_BT for the branch taken exception. This behavior was lost by the 2/2010 patch. Signed-off-by: James Yang Signed-off-by: Scott Wood diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index aedfd41..01be88b 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c @@ -895,7 +895,7 @@ void user_disable_single_step(struct task_struct *task) * And, after doing so, if all debug flags are off, turn * off DBCR0(IDM) and MSR(DE) .... Torez */ - task->thread.debug.dbcr0 &= ~DBCR0_IC; + task->thread.debug.dbcr0 &= ~(DBCR0_IC|DBCR0_BT); /* * Test to see if any of the DBCR_ACTIVE_EVENTS bits are set. */ -- cgit v0.10.2 From fd2f3b7ee1cf4b5fa77659c5ed4aa3cf5d1b3731 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 18 Oct 2013 17:36:16 -0700 Subject: irq: DocBook/genericirq.tmpl: Correct various typos Typo corrections for DocBook/genericirq.tmpl. Signed-off-by: Randy Dunlap Link: http://lkml.kernel.org/r/5261D400.30400@infradead.org Signed-off-by: Ingo Molnar diff --git a/Documentation/DocBook/genericirq.tmpl b/Documentation/DocBook/genericirq.tmpl index d16d21b..46347f6 100644 --- a/Documentation/DocBook/genericirq.tmpl +++ b/Documentation/DocBook/genericirq.tmpl @@ -87,7 +87,7 @@ Rationale - The original implementation of interrupt handling in Linux is using + The original implementation of interrupt handling in Linux uses the __do_IRQ() super-handler, which is able to deal with every type of interrupt logic. @@ -111,19 +111,19 @@ - This split implementation of highlevel IRQ handlers allows us to + This split implementation of high-level IRQ handlers allows us to optimize the flow of the interrupt handling for each specific - interrupt type. This reduces complexity in that particular codepath + interrupt type. This reduces complexity in that particular code path and allows the optimized handling of a given type. The original general IRQ implementation used hw_interrupt_type structures and their ->ack(), ->end() [etc.] callbacks to differentiate the flow control in the super-handler. This leads to - a mix of flow logic and lowlevel hardware logic, and it also leads - to unnecessary code duplication: for example in i386, there is a - ioapic_level_irq and a ioapic_edge_irq irq-type which share many - of the lowlevel details but have different flow handling. + a mix of flow logic and low-level hardware logic, and it also leads + to unnecessary code duplication: for example in i386, there is an + ioapic_level_irq and an ioapic_edge_irq IRQ-type which share many + of the low-level details but have different flow handling. A more natural abstraction is the clean separation of the @@ -132,23 +132,23 @@ Analysing a couple of architecture's IRQ subsystem implementations reveals that most of them can use a generic set of 'irq flow' - methods and only need to add the chip level specific code. + methods and only need to add the chip-level specific code. The separation is also valuable for (sub)architectures - which need specific quirks in the irq flow itself but not in the - chip-details - and thus provides a more transparent IRQ subsystem + which need specific quirks in the IRQ flow itself but not in the + chip details - and thus provides a more transparent IRQ subsystem design. - Each interrupt descriptor is assigned its own highlevel flow + Each interrupt descriptor is assigned its own high-level flow handler, which is normally one of the generic - implementations. (This highlevel flow handler implementation also + implementations. (This high-level flow handler implementation also makes it simple to provide demultiplexing handlers which can be found in embedded platforms on various architectures.) The separation makes the generic interrupt handling layer more flexible and extensible. For example, an (sub)architecture can - use a generic irq-flow implementation for 'level type' interrupts + use a generic IRQ-flow implementation for 'level type' interrupts and add a (sub)architecture specific 'edge type' implementation. @@ -172,9 +172,9 @@ There are three main levels of abstraction in the interrupt code: - Highlevel driver API - Highlevel IRQ flow handlers - Chiplevel hardware encapsulation + High-level driver API + High-level IRQ flow handlers + Chip-level hardware encapsulation @@ -189,16 +189,16 @@ which are assigned to this interrupt. - Whenever an interrupt triggers, the lowlevel arch code calls into - the generic interrupt code by calling desc->handle_irq(). - This highlevel IRQ handling function only uses desc->irq_data.chip + Whenever an interrupt triggers, the low-level architecture code calls + into the generic interrupt code by calling desc->handle_irq(). + This high-level IRQ handling function only uses desc->irq_data.chip primitives referenced by the assigned chip descriptor structure. - Highlevel Driver API + High-level Driver API - The highlevel Driver API consists of following functions: + The high-level Driver API consists of following functions: request_irq() free_irq() @@ -216,7 +216,7 @@ - Highlevel IRQ flow handlers + High-level IRQ flow handlers The generic layer provides a set of pre-defined irq-flow methods: @@ -228,7 +228,7 @@ handle_edge_eoi_irq handle_bad_irq - The interrupt flow handlers (either predefined or architecture + The interrupt flow handlers (either pre-defined or architecture specific) are assigned to specific interrupts by the architecture either during bootup or during device initialization. @@ -297,7 +297,7 @@ desc->irq_data.chip->irq_unmask(); handle_fasteoi_irq provides a generic implementation for interrupts, which only need an EOI at the end of - the handler + the handler. The following control flow is implemented (simplified excerpt): @@ -394,7 +394,7 @@ if (desc->irq_data.chip->irq_eoi) The generic functions are intended for 'clean' architectures and chips, which have no platform-specific IRQ handling quirks. If an architecture needs to implement quirks on the 'flow' level then it can do so by - overriding the highlevel irq-flow handler. + overriding the high-level irq-flow handler. @@ -419,9 +419,9 @@ if (desc->irq_data.chip->irq_eoi) - Chiplevel hardware encapsulation + Chip-level hardware encapsulation - The chip level hardware descriptor structure irq_chip + The chip-level hardware descriptor structure irq_chip contains all the direct chip relevant functions, which can be utilized by the irq flow implementations. @@ -429,14 +429,14 @@ if (desc->irq_data.chip->irq_eoi) irq_mask_ack() - Optional, recommended for performance irq_mask() irq_unmask() - irq_eoi() - Optional, required for eoi flow handlers + irq_eoi() - Optional, required for EOI flow handlers irq_retrigger() - Optional irq_set_type() - Optional irq_set_wake() - Optional These primitives are strictly intended to mean what they say: ack means ACK, masking means masking of an IRQ line, etc. It is up to the flow - handler(s) to use these basic units of lowlevel functionality. + handler(s) to use these basic units of low-level functionality. @@ -445,7 +445,7 @@ if (desc->irq_data.chip->irq_eoi) __do_IRQ entry point The original implementation __do_IRQ() was an alternative entry - point for all types of interrupts. It not longer exists. + point for all types of interrupts. It no longer exists. This handler turned out to be not suitable for all @@ -468,11 +468,11 @@ if (desc->irq_data.chip->irq_eoi) Generic interrupt chip - To avoid copies of identical implementations of irq chips the + To avoid copies of identical implementations of IRQ chips the core provides a configurable generic interrupt chip implementation. Developers should check carefuly whether the generic chip fits their needs before implementing the same - functionality slightly different themself. + functionality slightly differently themselves. !Ekernel/irq/generic-chip.c -- cgit v0.10.2 From 0963d59bc0bbed48b06733950f5eb167e9b9a8fa Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 19 Oct 2013 15:39:17 +0200 Subject: Fixup "MAINTAINERS: GPIO-INTEL-MID: add maintainer" This reverts commit ee0f5b390a0d7375a8b6af4cb0e167692e5e8a10. The MAINTAINERS entry was not in alphabetic order. Assign david.a.cohen@linux.intel.com as maintainer of Intel MID gpio driver at drivers/gpio/gpio-intel-mid.c. Now do it in alphabetic order instead. Signed-off-by: David Cohen Acked-by: Mika Westerberg Signed-off-by: Linus Walleij diff --git a/MAINTAINERS b/MAINTAINERS index 7534a80..aa8c915 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1910,12 +1910,6 @@ W: http://bu3sch.de/btgpio.php S: Maintained F: drivers/gpio/gpio-bt8xx.c -INTEL-MID GPIO DRIVER -M: David Cohen -L: linux-gpio@vger.kernel.org -S: Maintained -F: drivers/gpio/gpio-intel-mid.c - BTRFS FILE SYSTEM M: Chris Mason L: linux-btrfs@vger.kernel.org @@ -4416,6 +4410,12 @@ F: Documentation/networking/ixgbevf.txt F: Documentation/networking/i40e.txt F: drivers/net/ethernet/intel/ +INTEL-MID GPIO DRIVER +M: David Cohen +L: linux-gpio@vger.kernel.org +S: Maintained +F: drivers/gpio/gpio-intel-mid.c + INTEL PRO/WIRELESS 2100, 2200BG, 2915ABG NETWORK CONNECTION SUPPORT M: Stanislav Yakovlev L: linux-wireless@vger.kernel.org -- cgit v0.10.2 From 1e687e806b8ea1b1428e3e664afd4081e435f6d0 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 19 Oct 2013 11:55:15 +0300 Subject: hwmon: (nct6775) Remove an unused variable We don't actually use "j" for anything. Signed-off-by: Dan Carpenter Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index 7a1b6a7..d17325d 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -939,7 +939,7 @@ nct6775_create_attr_group(struct device *dev, struct sensor_template_group *tg, struct sensor_device_attribute_2 *a2; struct attribute **attrs; struct sensor_device_template **t; - int i, j, count; + int i, count; if (repeat <= 0) return ERR_PTR(-EINVAL); @@ -970,7 +970,7 @@ nct6775_create_attr_group(struct device *dev, struct sensor_template_group *tg, for (i = 0; i < repeat; i++) { t = tg->templates; - for (j = 0; *t != NULL; j++) { + while (*t != NULL) { snprintf(su->name, sizeof(su->name), (*t)->dev_attr.attr.name, tg->base + i); if ((*t)->s2) { -- cgit v0.10.2 From 26336c8a36c0a6a28b9ecf6f1bb8c8f5605d6a21 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 19 Oct 2013 12:12:22 +0300 Subject: hwmon: (w83793) Clean up a signedness issue We cap the upper bound of "mtimeout" but since it's signed we should check for negative values as well. The mistake is harmless. But I have changed it to unsigned as a cleanup. Signed-off-by: Dan Carpenter Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c index b6470ec..9d63d71 100644 --- a/drivers/hwmon/w83793.c +++ b/drivers/hwmon/w83793.c @@ -1199,7 +1199,8 @@ static void w83793_init_client(struct i2c_client *client) static int watchdog_set_timeout(struct w83793_data *data, int timeout) { - int ret, mtimeout; + unsigned int mtimeout; + int ret; mtimeout = DIV_ROUND_UP(timeout, 60); -- cgit v0.10.2 From 6197c34425c3d9a622d3a7031c91104909224a67 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 19 Oct 2013 14:09:27 +0100 Subject: ASoC: cirrus: Enable compile test builds The core support for ep93xx (currently only the DMA driver) does not depend on the architecture at all and everything else has more strict dependencies so enable compile test builds for improved build coverage. Signed-off-by: Mark Brown diff --git a/sound/soc/cirrus/Kconfig b/sound/soc/cirrus/Kconfig index 2c20f01..06f938d 100644 --- a/sound/soc/cirrus/Kconfig +++ b/sound/soc/cirrus/Kconfig @@ -1,6 +1,6 @@ config SND_EP93XX_SOC tristate "SoC Audio support for the Cirrus Logic EP93xx series" - depends on ARCH_EP93XX && SND_SOC + depends on (ARCH_EP93XX || COMPILE_TEST) && SND_SOC select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for codecs attached to -- cgit v0.10.2 From e58f301ec969430cdafd7fa872660458f4939507 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 16 Oct 2013 17:26:22 +0100 Subject: ASoC: rt5640: Power down LDO while suspended If we have control over the LDO then disable it during suspend; the device is already being put into reset so will be non-functional over suspend anyway and this will save a small amount of power. Signed-off-by: Mark Brown Tested-by: Stephen Warren diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 641eeeb..b0cde92 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -1979,12 +1979,20 @@ static int rt5640_suspend(struct snd_soc_codec *codec) rt5640_reset(codec); regcache_cache_only(rt5640->regmap, true); regcache_mark_dirty(rt5640->regmap); + if (gpio_is_valid(rt5640->pdata.ldo1_en)) + gpio_set_value_cansleep(rt5640->pdata.ldo1_en, 0); return 0; } static int rt5640_resume(struct snd_soc_codec *codec) { + struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); + + if (gpio_is_valid(rt5640->pdata.ldo1_en)) { + gpio_set_value_cansleep(rt5640->pdata.ldo1_en, 1); + msleep(400); + } rt5640_set_bias_level(codec, SND_SOC_BIAS_STANDBY); return 0; -- cgit v0.10.2 From ebce31140c2ddfda005e88957ac1ee1eacaa8dc5 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 16 Oct 2013 17:30:44 +0100 Subject: ASoC: rt5640: Don't go to standby on resume There is no need for the CODEC to go to standby on resume since the core will power it up as needed and in any case it is an idle_bias_off CODEC so would normally sit with bias off while idle. Signed-off-by: Mark Brown Tested-by: Stephen Warren diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index b0cde92..4d041d3 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -1993,7 +1993,6 @@ static int rt5640_resume(struct snd_soc_codec *codec) gpio_set_value_cansleep(rt5640->pdata.ldo1_en, 1); msleep(400); } - rt5640_set_bias_level(codec, SND_SOC_BIAS_STANDBY); return 0; } -- cgit v0.10.2 From c0de42bf595238e9dd593405ebc2992cc8470732 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 8 Oct 2013 15:07:59 +0200 Subject: ASoC: dmaengine-pcm: Add support for querying DMA capabilities Currently each platform making use the the generic dmaengine PCM driver still needs to provide a custom snd_pcm_hardware struct which specifies the capabilities of the DMA controller, e.g. the maximum period size that can be supported. This patch adds code which uses the newly introduced dma_get_slave_caps() API to query this information from the dmaengine driver. The new code path will only be taken if the 'pcm_hardware' field of the snd_dmaengine_pcm_config struct is NULL. The patch also introduces a new 'fifo_size' field to the snd_dmaengine_dai_dma_data struct which is used to initialize the snd_pcm_hardware 'fifo_size' field and needs to be set by the DAI driver. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index f11c35c..83b2c3e 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -61,6 +61,7 @@ struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream) * @slave_id: Slave requester id for the DMA channel. * @filter_data: Custom DMA channel filter data, this will usually be used when * requesting the DMA channel. + * @fifo_size: FIFO size of the DAI controller in bytes */ struct snd_dmaengine_dai_dma_data { dma_addr_t addr; @@ -68,6 +69,7 @@ struct snd_dmaengine_dai_dma_data { u32 maxburst; unsigned int slave_id; void *filter_data; + unsigned int fifo_size; }; void snd_dmaengine_pcm_set_config_from_dai_data( diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index e29ec3c..c39e19e 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -36,6 +36,15 @@ static struct dmaengine_pcm *soc_platform_to_pcm(struct snd_soc_platform *p) return container_of(p, struct dmaengine_pcm, platform); } +static struct device *dmaengine_dma_dev(struct dmaengine_pcm *pcm, + struct snd_pcm_substream *substream) +{ + if (!pcm->chan[substream->stream]) + return NULL; + + return pcm->chan[substream->stream]->device->dev; +} + /** * snd_dmaengine_pcm_prepare_slave_config() - Generic prepare_slave_config callback * @substream: PCM substream @@ -92,28 +101,54 @@ static int dmaengine_pcm_hw_params(struct snd_pcm_substream *substream, return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); } -static int dmaengine_pcm_open(struct snd_pcm_substream *substream) +static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform); + struct device *dma_dev = dmaengine_dma_dev(pcm, substream); struct dma_chan *chan = pcm->chan[substream->stream]; + struct snd_dmaengine_dai_dma_data *dma_data; + struct dma_slave_caps dma_caps; + struct snd_pcm_hardware hw; int ret; - ret = snd_soc_set_runtime_hwparams(substream, + if (pcm->config->pcm_hardware) + return snd_soc_set_runtime_hwparams(substream, pcm->config->pcm_hardware); - if (ret) - return ret; - return snd_dmaengine_pcm_open(substream, chan); + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + + memset(&hw, 0, sizeof(hw)); + hw.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED; + hw.periods_min = 2; + hw.periods_max = UINT_MAX; + hw.period_bytes_min = 256; + hw.period_bytes_max = dma_get_max_seg_size(dma_dev); + hw.buffer_bytes_max = SIZE_MAX; + hw.fifo_size = dma_data->fifo_size; + + ret = dma_get_slave_caps(chan, &dma_caps); + if (ret == 0) { + if (dma_caps.cmd_pause) + hw.info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME; + } + + return snd_soc_set_runtime_hwparams(substream, &hw); } -static struct device *dmaengine_dma_dev(struct dmaengine_pcm *pcm, - struct snd_pcm_substream *substream) +static int dmaengine_pcm_open(struct snd_pcm_substream *substream) { - if (!pcm->chan[substream->stream]) - return NULL; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform); + struct dma_chan *chan = pcm->chan[substream->stream]; + int ret; - return pcm->chan[substream->stream]->device->dev; + ret = dmaengine_pcm_set_runtime_hwparams(substream); + if (ret) + return ret; + + return snd_dmaengine_pcm_open(substream, chan); } static void dmaengine_pcm_free(struct snd_pcm *pcm) -- cgit v0.10.2 From fa654e085300e9c222ef931bc0702a9df2542666 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 8 Oct 2013 15:08:00 +0200 Subject: ASoC: dmaengine-pcm: Provide default config This patch adds some default settings for the generic dmaengine PCM driver for the case that no config has been supplied. The following defaults are used: * Use snd_dmaengine_pcm_prepare_slave_config for preparing the DMA slave config. * 512kB for the prealloc buffer size. This value has been chosen based on 'feels about right' and is not backed up by any scientific facts. We may need to come up with something smarter in the future but it should work fine for now. With this infrastructure in place we can finally write DAI drivers which are independent of the DMA controller they are connected to. This is e.g. useful if the DAI IP core is reused across different SoCs, but the SoCs uses different DMA controllers. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index c39e19e..99f9495 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -84,12 +84,19 @@ static int dmaengine_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform); struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); + int (*prepare_slave_config)(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct dma_slave_config *slave_config); struct dma_slave_config slave_config; int ret; - if (pcm->config->prepare_slave_config) { - ret = pcm->config->prepare_slave_config(substream, params, - &slave_config); + if (!pcm->config) + prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config; + else + prepare_slave_config = pcm->config->prepare_slave_config; + + if (prepare_slave_config) { + ret = prepare_slave_config(substream, params, &slave_config); if (ret) return ret; @@ -112,7 +119,7 @@ static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substrea struct snd_pcm_hardware hw; int ret; - if (pcm->config->pcm_hardware) + if (pcm->config && pcm->config->pcm_hardware) return snd_soc_set_runtime_hwparams(substream, pcm->config->pcm_hardware); @@ -177,9 +184,20 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform); const struct snd_dmaengine_pcm_config *config = pcm->config; struct snd_pcm_substream *substream; + size_t prealloc_buffer_size; + size_t max_buffer_size; unsigned int i; int ret; + if (config && config->prealloc_buffer_size) { + prealloc_buffer_size = config->prealloc_buffer_size; + max_buffer_size = config->pcm_hardware->buffer_bytes_max; + } else { + prealloc_buffer_size = 512 * 1024; + max_buffer_size = SIZE_MAX; + } + + for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) { substream = rtd->pcm->streams[i].substream; if (!substream) @@ -200,8 +218,8 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) ret = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, dmaengine_dma_dev(pcm, substream), - config->prealloc_buffer_size, - config->pcm_hardware->buffer_bytes_max); + prealloc_buffer_size, + max_buffer_size); if (ret) goto err_free; } -- cgit v0.10.2 From 79a9becda8940deb2274b5aa4577c86d52ee7ecb Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Thu, 17 Oct 2013 10:21:36 -0700 Subject: gpiolib: export descriptor-based GPIO interface This patch exports the gpiod_* family of API functions, a safer alternative to the legacy GPIO interface. Differences between the gpiod and legacy gpio APIs are: - gpio works with integers, whereas gpiod operates on opaque handlers which cannot be forged or used before proper acquisition - gpiod get/set functions are aware of the active low state of a GPIO - gpio consumers should now include to access the new interface, whereas chips drivers will use The legacy gpio API is now built as inline functions on top of gpiod. Signed-off-by: Alexandre Courbot Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index d66139d..224abdc 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -16,16 +16,11 @@ #define CREATE_TRACE_POINTS #include -/* Optional implementation infrastructure for GPIO interfaces. +/* Implementation infrastructure for GPIO interfaces. * - * Platforms may want to use this if they tend to use very many GPIOs - * that aren't part of a System-On-Chip core; or across I2C/SPI/etc. - * - * When kernel footprint or instruction count is an issue, simpler - * implementations may be preferred. The GPIO programming interface - * allows for inlining speed-critical get/set operations for common - * cases, so that access to SOC-integrated GPIOs can sometimes cost - * only an instruction or two per bit. + * The GPIO programming interface allows for inlining speed-critical + * get/set operations for common cases, so that access to SOC-integrated + * GPIOs can sometimes cost only an instruction or two per bit. */ @@ -57,7 +52,7 @@ struct gpio_desc { #define FLAG_SYSFS 3 /* exported via /sys/class/gpio/control */ #define FLAG_TRIG_FALL 4 /* trigger on falling edge */ #define FLAG_TRIG_RISE 5 /* trigger on rising edge */ -#define FLAG_ACTIVE_LOW 6 /* sysfs value has active low */ +#define FLAG_ACTIVE_LOW 6 /* value has active low */ #define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */ #define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */ #define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */ @@ -81,29 +76,8 @@ static LIST_HEAD(gpio_chips); static DEFINE_IDR(dirent_idr); #endif -/* - * Internal gpiod_* API using descriptors instead of the integer namespace. - * Most of this should eventually go public. - */ static int gpiod_request(struct gpio_desc *desc, const char *label); static void gpiod_free(struct gpio_desc *desc); -static int gpiod_direction_input(struct gpio_desc *desc); -static int gpiod_direction_output(struct gpio_desc *desc, int value); -static int gpiod_get_direction(const struct gpio_desc *desc); -static int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce); -static int gpiod_get_value_cansleep(const struct gpio_desc *desc); -static void gpiod_set_value_cansleep(struct gpio_desc *desc, int value); -static int gpiod_get_value(const struct gpio_desc *desc); -static void gpiod_set_value(struct gpio_desc *desc, int value); -static int gpiod_cansleep(const struct gpio_desc *desc); -static int gpiod_to_irq(const struct gpio_desc *desc); -static int gpiod_lock_as_irq(struct gpio_desc *desc); -static void gpiod_unlock_as_irq(struct gpio_desc *desc); -static int gpiod_export(struct gpio_desc *desc, bool direction_may_change); -static int gpiod_export_link(struct device *dev, const char *name, - struct gpio_desc *desc); -static int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value); -static void gpiod_unexport(struct gpio_desc *desc); #ifdef CONFIG_DEBUG_FS #define gpiod_emerg(desc, fmt, ...) \ @@ -157,13 +131,14 @@ static int gpio_chip_hwgpio(const struct gpio_desc *desc) /** * Convert a GPIO number to its descriptor */ -static struct gpio_desc *gpio_to_desc(unsigned gpio) +struct gpio_desc *gpio_to_desc(unsigned gpio) { if (WARN(!gpio_is_valid(gpio), "invalid GPIO %d\n", gpio)) return NULL; else return &gpio_desc[gpio]; } +EXPORT_SYMBOL_GPL(gpio_to_desc); /** * Convert an offset on a certain chip to a corresponding descriptor @@ -181,10 +156,11 @@ static struct gpio_desc *gpiochip_offset_to_desc(struct gpio_chip *chip, * This should disappear in the future but is needed since we still * use GPIO numbers for error messages and sysfs nodes */ -static int desc_to_gpio(const struct gpio_desc *desc) +int desc_to_gpio(const struct gpio_desc *desc) { return desc - &gpio_desc[0]; } +EXPORT_SYMBOL_GPL(desc_to_gpio); /* Warn when drivers omit gpio_request() calls -- legal but ill-advised @@ -219,16 +195,15 @@ static int gpio_ensure_requested(struct gpio_desc *desc) return 0; } -static struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc) +/** + * gpiod_to_chip - Return the GPIO chip to which a GPIO descriptor belongs + * @desc: descriptor to return the chip of + */ +struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc) { return desc ? desc->chip : NULL; } - -/* caller holds gpio_lock *OR* gpio is marked as requested */ -struct gpio_chip *gpio_to_chip(unsigned gpio) -{ - return gpiod_to_chip(gpio_to_desc(gpio)); -} +EXPORT_SYMBOL_GPL(gpiod_to_chip); /* dynamic allocation of GPIOs, e.g. on a hotplugged device */ static int gpiochip_find_base(int ngpio) @@ -254,8 +229,15 @@ static int gpiochip_find_base(int ngpio) } } -/* caller ensures gpio is valid and requested, chip->get_direction may sleep */ -static int gpiod_get_direction(const struct gpio_desc *desc) +/** + * gpiod_get_direction - return the current direction of a GPIO + * @desc: GPIO to get the direction of + * + * Return GPIOF_DIR_IN or GPIOF_DIR_OUT, or an error code in case of error. + * + * This function may sleep if gpiod_cansleep() is true. + */ +int gpiod_get_direction(const struct gpio_desc *desc) { struct gpio_chip *chip; unsigned offset; @@ -281,6 +263,7 @@ static int gpiod_get_direction(const struct gpio_desc *desc) } return status; } +EXPORT_SYMBOL_GPL(gpiod_get_direction); #ifdef CONFIG_GPIO_SYSFS @@ -365,17 +348,10 @@ static ssize_t gpio_value_show(struct device *dev, mutex_lock(&sysfs_lock); - if (!test_bit(FLAG_EXPORT, &desc->flags)) { + if (!test_bit(FLAG_EXPORT, &desc->flags)) status = -EIO; - } else { - int value; - - value = !!gpiod_get_value_cansleep(desc); - if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) - value = !value; - - status = sprintf(buf, "%d\n", value); - } + else + status = sprintf(buf, "%d\n", gpiod_get_value_cansleep(desc)); mutex_unlock(&sysfs_lock); return status; @@ -398,9 +374,7 @@ static ssize_t gpio_value_store(struct device *dev, status = kstrtol(buf, 0, &value); if (status == 0) { - if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) - value = !value; - gpiod_set_value_cansleep(desc, value != 0); + gpiod_set_value_cansleep(desc, value); status = size; } } @@ -790,7 +764,7 @@ static struct class gpio_class = { /** - * gpio_export - export a GPIO through sysfs + * gpiod_export - export a GPIO through sysfs * @gpio: gpio to make available, already requested * @direction_may_change: true if userspace may change gpio direction * Context: arch_initcall or later @@ -804,7 +778,7 @@ static struct class gpio_class = { * * Returns zero on success, else an error. */ -static int gpiod_export(struct gpio_desc *desc, bool direction_may_change) +int gpiod_export(struct gpio_desc *desc, bool direction_may_change) { unsigned long flags; int status; @@ -882,12 +856,7 @@ fail_unlock: status); return status; } - -int gpio_export(unsigned gpio, bool direction_may_change) -{ - return gpiod_export(gpio_to_desc(gpio), direction_may_change); -} -EXPORT_SYMBOL_GPL(gpio_export); +EXPORT_SYMBOL_GPL(gpiod_export); static int match_export(struct device *dev, const void *data) { @@ -895,7 +864,7 @@ static int match_export(struct device *dev, const void *data) } /** - * gpio_export_link - create a sysfs link to an exported GPIO node + * gpiod_export_link - create a sysfs link to an exported GPIO node * @dev: device under which to create symlink * @name: name of the symlink * @gpio: gpio to create symlink to, already exported @@ -905,8 +874,8 @@ static int match_export(struct device *dev, const void *data) * * Returns zero on success, else an error. */ -static int gpiod_export_link(struct device *dev, const char *name, - struct gpio_desc *desc) +int gpiod_export_link(struct device *dev, const char *name, + struct gpio_desc *desc) { int status = -EINVAL; @@ -937,15 +906,10 @@ static int gpiod_export_link(struct device *dev, const char *name, return status; } - -int gpio_export_link(struct device *dev, const char *name, unsigned gpio) -{ - return gpiod_export_link(dev, name, gpio_to_desc(gpio)); -} -EXPORT_SYMBOL_GPL(gpio_export_link); +EXPORT_SYMBOL_GPL(gpiod_export_link); /** - * gpio_sysfs_set_active_low - set the polarity of gpio sysfs value + * gpiod_sysfs_set_active_low - set the polarity of gpio sysfs value * @gpio: gpio to change * @value: non-zero to use active low, i.e. inverted values * @@ -956,7 +920,7 @@ EXPORT_SYMBOL_GPL(gpio_export_link); * * Returns zero on success, else an error. */ -static int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value) +int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value) { struct device *dev = NULL; int status = -EINVAL; @@ -987,20 +951,15 @@ unlock: return status; } - -int gpio_sysfs_set_active_low(unsigned gpio, int value) -{ - return gpiod_sysfs_set_active_low(gpio_to_desc(gpio), value); -} -EXPORT_SYMBOL_GPL(gpio_sysfs_set_active_low); +EXPORT_SYMBOL_GPL(gpiod_sysfs_set_active_low); /** - * gpio_unexport - reverse effect of gpio_export() + * gpiod_unexport - reverse effect of gpio_export() * @gpio: gpio to make unavailable * * This is implicit on gpio_free(). */ -static void gpiod_unexport(struct gpio_desc *desc) +void gpiod_unexport(struct gpio_desc *desc) { int status = 0; struct device *dev = NULL; @@ -1033,12 +992,7 @@ static void gpiod_unexport(struct gpio_desc *desc) pr_debug("%s: gpio%d status %d\n", __func__, desc_to_gpio(desc), status); } - -void gpio_unexport(unsigned gpio) -{ - gpiod_unexport(gpio_to_desc(gpio)); -} -EXPORT_SYMBOL_GPL(gpio_unexport); +EXPORT_SYMBOL_GPL(gpiod_unexport); static int gpiochip_export(struct gpio_chip *chip) { @@ -1145,27 +1099,6 @@ static inline void gpiochip_unexport(struct gpio_chip *chip) { } -static inline int gpiod_export(struct gpio_desc *desc, - bool direction_may_change) -{ - return -ENOSYS; -} - -static inline int gpiod_export_link(struct device *dev, const char *name, - struct gpio_desc *desc) -{ - return -ENOSYS; -} - -static inline int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value) -{ - return -ENOSYS; -} - -static inline void gpiod_unexport(struct gpio_desc *desc) -{ -} - #endif /* CONFIG_GPIO_SYSFS */ /* @@ -1677,7 +1610,16 @@ EXPORT_SYMBOL_GPL(gpiochip_is_requested); * rely on gpio_request() having been called beforehand. */ -static int gpiod_direction_input(struct gpio_desc *desc) +/** + * gpiod_direction_input - set the GPIO direction to input + * @desc: GPIO to set to input + * + * Set the direction of the passed GPIO to input, such as gpiod_get_value() can + * be called safely on it. + * + * Return 0 in case of success, else an error code. + */ +int gpiod_direction_input(struct gpio_desc *desc) { unsigned long flags; struct gpio_chip *chip; @@ -1734,14 +1676,19 @@ fail: gpiod_dbg(desc, "%s status %d\n", __func__, status); return status; } +EXPORT_SYMBOL_GPL(gpiod_direction_input); -int gpio_direction_input(unsigned gpio) -{ - return gpiod_direction_input(gpio_to_desc(gpio)); -} -EXPORT_SYMBOL_GPL(gpio_direction_input); - -static int gpiod_direction_output(struct gpio_desc *desc, int value) +/** + * gpiod_direction_output - set the GPIO direction to input + * @desc: GPIO to set to output + * @value: initial output value of the GPIO + * + * Set the direction of the passed GPIO to output, such as gpiod_set_value() can + * be called safely on it. The initial value of the output must be specified. + * + * Return 0 in case of success, else an error code. + */ +int gpiod_direction_output(struct gpio_desc *desc, int value) { unsigned long flags; struct gpio_chip *chip; @@ -1814,22 +1761,17 @@ fail: gpiod_dbg(desc, "%s: gpio status %d\n", __func__, status); return status; } - -int gpio_direction_output(unsigned gpio, int value) -{ - return gpiod_direction_output(gpio_to_desc(gpio), value); -} -EXPORT_SYMBOL_GPL(gpio_direction_output); +EXPORT_SYMBOL_GPL(gpiod_direction_output); /** - * gpio_set_debounce - sets @debounce time for a @gpio + * gpiod_set_debounce - sets @debounce time for a @gpio * @gpio: the gpio to set debounce time * @debounce: debounce time is microseconds * * returns -ENOTSUPP if the controller does not support setting * debounce. */ -static int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) +int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) { unsigned long flags; struct gpio_chip *chip; @@ -1871,12 +1813,19 @@ fail: return status; } +EXPORT_SYMBOL_GPL(gpiod_set_debounce); -int gpio_set_debounce(unsigned gpio, unsigned debounce) +/** + * gpiod_is_active_low - test whether a GPIO is active-low or not + * @desc: the gpio descriptor to test + * + * Returns 1 if the GPIO is active-low, 0 otherwise. + */ +int gpiod_is_active_low(const struct gpio_desc *desc) { - return gpiod_set_debounce(gpio_to_desc(gpio), debounce); + return test_bit(FLAG_ACTIVE_LOW, &desc->flags); } -EXPORT_SYMBOL_GPL(gpio_set_debounce); +EXPORT_SYMBOL_GPL(gpiod_is_active_low); /* I/O calls are only valid after configuration completed; the relevant * "is this a valid GPIO" error checks should already have been done. @@ -1900,7 +1849,7 @@ EXPORT_SYMBOL_GPL(gpio_set_debounce); * that the GPIO was actually requested. */ -static int _gpiod_get_value(const struct gpio_desc *desc) +static int _gpiod_get_raw_value(const struct gpio_desc *desc) { struct gpio_chip *chip; int value; @@ -1914,33 +1863,54 @@ static int _gpiod_get_value(const struct gpio_desc *desc) } /** - * __gpio_get_value() - return a gpio's value - * @gpio: gpio whose value will be returned - * Context: any + * gpiod_get_raw_value() - return a gpio's raw value + * @desc: gpio whose value will be returned * - * This is used directly or indirectly to implement gpio_get_value(). - * It returns the zero or nonzero value provided by the associated - * gpio_chip.get() method; or zero if no such method is provided. + * Return the GPIO's raw value, i.e. the value of the physical line disregarding + * its ACTIVE_LOW status. + * + * This function should be called from contexts where we cannot sleep, and will + * complain if the GPIO chip functions potentially sleep. */ -static int gpiod_get_value(const struct gpio_desc *desc) +int gpiod_get_raw_value(const struct gpio_desc *desc) { if (!desc) return 0; /* Should be using gpio_get_value_cansleep() */ WARN_ON(desc->chip->can_sleep); - return _gpiod_get_value(desc); + return _gpiod_get_raw_value(desc); } +EXPORT_SYMBOL_GPL(gpiod_get_raw_value); -int __gpio_get_value(unsigned gpio) +/** + * gpiod_get_value() - return a gpio's value + * @desc: gpio whose value will be returned + * + * Return the GPIO's logical value, i.e. taking the ACTIVE_LOW status into + * account. + * + * This function should be called from contexts where we cannot sleep, and will + * complain if the GPIO chip functions potentially sleep. + */ +int gpiod_get_value(const struct gpio_desc *desc) { - return gpiod_get_value(gpio_to_desc(gpio)); + int value; + if (!desc) + return 0; + /* Should be using gpio_get_value_cansleep() */ + WARN_ON(desc->chip->can_sleep); + + value = _gpiod_get_raw_value(desc); + if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) + value = !value; + + return value; } -EXPORT_SYMBOL_GPL(__gpio_get_value); +EXPORT_SYMBOL_GPL(gpiod_get_value); /* * _gpio_set_open_drain_value() - Set the open drain gpio's value. - * @gpio: Gpio whose state need to be set. - * @chip: Gpio chip. + * @desc: gpio descriptor whose state need to be set. * @value: Non-zero for setting it HIGH otherise it will set to LOW. */ static void _gpio_set_open_drain_value(struct gpio_desc *desc, int value) @@ -1966,9 +1936,8 @@ static void _gpio_set_open_drain_value(struct gpio_desc *desc, int value) } /* - * _gpio_set_open_source() - Set the open source gpio's value. - * @gpio: Gpio whose state need to be set. - * @chip: Gpio chip. + * _gpio_set_open_source_value() - Set the open source gpio's value. + * @desc: gpio descriptor whose state need to be set. * @value: Non-zero for setting it HIGH otherise it will set to LOW. */ static void _gpio_set_open_source_value(struct gpio_desc *desc, int value) @@ -1993,7 +1962,7 @@ static void _gpio_set_open_source_value(struct gpio_desc *desc, int value) __func__, err); } -static void _gpiod_set_value(struct gpio_desc *desc, int value) +static void _gpiod_set_raw_value(struct gpio_desc *desc, int value) { struct gpio_chip *chip; @@ -2008,62 +1977,70 @@ static void _gpiod_set_value(struct gpio_desc *desc, int value) } /** - * __gpio_set_value() - assign a gpio's value - * @gpio: gpio whose value will be assigned + * gpiod_set_raw_value() - assign a gpio's raw value + * @desc: gpio whose value will be assigned * @value: value to assign - * Context: any * - * This is used directly or indirectly to implement gpio_set_value(). - * It invokes the associated gpio_chip.set() method. + * Set the raw value of the GPIO, i.e. the value of its physical line without + * regard for its ACTIVE_LOW status. + * + * This function should be called from contexts where we cannot sleep, and will + * complain if the GPIO chip functions potentially sleep. */ -static void gpiod_set_value(struct gpio_desc *desc, int value) +void gpiod_set_raw_value(struct gpio_desc *desc, int value) { - if (!desc) return; /* Should be using gpio_set_value_cansleep() */ WARN_ON(desc->chip->can_sleep); - _gpiod_set_value(desc, value); + _gpiod_set_raw_value(desc, value); } +EXPORT_SYMBOL_GPL(gpiod_set_raw_value); -void __gpio_set_value(unsigned gpio, int value) +/** + * gpiod_set_value() - assign a gpio's value + * @desc: gpio whose value will be assigned + * @value: value to assign + * + * Set the logical value of the GPIO, i.e. taking its ACTIVE_LOW status into + * account + * + * This function should be called from contexts where we cannot sleep, and will + * complain if the GPIO chip functions potentially sleep. + */ +void gpiod_set_value(struct gpio_desc *desc, int value) { - return gpiod_set_value(gpio_to_desc(gpio), value); + if (!desc) + return; + /* Should be using gpio_set_value_cansleep() */ + WARN_ON(desc->chip->can_sleep); + if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) + value = !value; + _gpiod_set_raw_value(desc, value); } -EXPORT_SYMBOL_GPL(__gpio_set_value); +EXPORT_SYMBOL_GPL(gpiod_set_value); /** - * __gpio_cansleep() - report whether gpio value access will sleep - * @gpio: gpio in question - * Context: any + * gpiod_cansleep() - report whether gpio value access may sleep + * @desc: gpio to check * - * This is used directly or indirectly to implement gpio_cansleep(). It - * returns nonzero if access reading or writing the GPIO value can sleep. */ -static int gpiod_cansleep(const struct gpio_desc *desc) +int gpiod_cansleep(const struct gpio_desc *desc) { if (!desc) return 0; - /* only call this on GPIOs that are valid! */ return desc->chip->can_sleep; } - -int __gpio_cansleep(unsigned gpio) -{ - return gpiod_cansleep(gpio_to_desc(gpio)); -} -EXPORT_SYMBOL_GPL(__gpio_cansleep); +EXPORT_SYMBOL_GPL(gpiod_cansleep); /** - * __gpio_to_irq() - return the IRQ corresponding to a GPIO - * @gpio: gpio whose IRQ will be returned (already requested) - * Context: any + * gpiod_to_irq() - return the IRQ corresponding to a GPIO + * @desc: gpio whose IRQ will be returned (already requested) * - * This is used directly or indirectly to implement gpio_to_irq(). - * It returns the number of the IRQ signaled by this (input) GPIO, - * or a negative errno. + * Return the IRQ corresponding to the passed GPIO, or an error code in case of + * error. */ -static int gpiod_to_irq(const struct gpio_desc *desc) +int gpiod_to_irq(const struct gpio_desc *desc) { struct gpio_chip *chip; int offset; @@ -2074,12 +2051,7 @@ static int gpiod_to_irq(const struct gpio_desc *desc) offset = gpio_chip_hwgpio(desc); return chip->to_irq ? chip->to_irq(chip, offset) : -ENXIO; } - -int __gpio_to_irq(unsigned gpio) -{ - return gpiod_to_irq(gpio_to_desc(gpio)); -} -EXPORT_SYMBOL_GPL(__gpio_to_irq); +EXPORT_SYMBOL_GPL(gpiod_to_irq); /** * gpiod_lock_as_irq() - lock a GPIO to be used as IRQ @@ -2091,7 +2063,7 @@ EXPORT_SYMBOL_GPL(__gpio_to_irq); * of its irq_chip implementation if the GPIO is known from that * code. */ -static int gpiod_lock_as_irq(struct gpio_desc *desc) +int gpiod_lock_as_irq(struct gpio_desc *desc) { if (!desc) return -EINVAL; @@ -2106,6 +2078,7 @@ static int gpiod_lock_as_irq(struct gpio_desc *desc) set_bit(FLAG_USED_AS_IRQ, &desc->flags); return 0; } +EXPORT_SYMBOL_GPL(gpiod_lock_as_irq); int gpio_lock_as_irq(struct gpio_chip *chip, unsigned int offset) { @@ -2120,13 +2093,14 @@ EXPORT_SYMBOL_GPL(gpio_lock_as_irq); * This is used directly by GPIO drivers that want to indicate * that a certain GPIO is no longer used exclusively for IRQ. */ -static void gpiod_unlock_as_irq(struct gpio_desc *desc) +void gpiod_unlock_as_irq(struct gpio_desc *desc) { if (!desc) return; clear_bit(FLAG_USED_AS_IRQ, &desc->flags); } +EXPORT_SYMBOL_GPL(gpiod_unlock_as_irq); void gpio_unlock_as_irq(struct gpio_chip *chip, unsigned int offset) { @@ -2134,37 +2108,89 @@ void gpio_unlock_as_irq(struct gpio_chip *chip, unsigned int offset) } EXPORT_SYMBOL_GPL(gpio_unlock_as_irq); -/* There's no value in making it easy to inline GPIO calls that may sleep. - * Common examples include ones connected to I2C or SPI chips. +/** + * gpiod_get_raw_value_cansleep() - return a gpio's raw value + * @desc: gpio whose value will be returned + * + * Return the GPIO's raw value, i.e. the value of the physical line disregarding + * its ACTIVE_LOW status. + * + * This function is to be called from contexts that can sleep. */ - -static int gpiod_get_value_cansleep(const struct gpio_desc *desc) +int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc) { might_sleep_if(extra_checks); if (!desc) return 0; - return _gpiod_get_value(desc); + return _gpiod_get_raw_value(desc); } +EXPORT_SYMBOL_GPL(gpiod_get_raw_value_cansleep); -int gpio_get_value_cansleep(unsigned gpio) +/** + * gpiod_get_value_cansleep() - return a gpio's value + * @desc: gpio whose value will be returned + * + * Return the GPIO's logical value, i.e. taking the ACTIVE_LOW status into + * account. + * + * This function is to be called from contexts that can sleep. + */ +int gpiod_get_value_cansleep(const struct gpio_desc *desc) { - return gpiod_get_value_cansleep(gpio_to_desc(gpio)); + int value; + + might_sleep_if(extra_checks); + if (!desc) + return 0; + + value = _gpiod_get_raw_value(desc); + if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) + value = !value; + + return value; } -EXPORT_SYMBOL_GPL(gpio_get_value_cansleep); +EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep); -static void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) +/** + * gpiod_set_raw_value_cansleep() - assign a gpio's raw value + * @desc: gpio whose value will be assigned + * @value: value to assign + * + * Set the raw value of the GPIO, i.e. the value of its physical line without + * regard for its ACTIVE_LOW status. + * + * This function is to be called from contexts that can sleep. + */ +void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value) { might_sleep_if(extra_checks); if (!desc) return; - _gpiod_set_value(desc, value); + _gpiod_set_raw_value(desc, value); } +EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep); -void gpio_set_value_cansleep(unsigned gpio, int value) +/** + * gpiod_set_value_cansleep() - assign a gpio's value + * @desc: gpio whose value will be assigned + * @value: value to assign + * + * Set the logical value of the GPIO, i.e. taking its ACTIVE_LOW status into + * account + * + * This function is to be called from contexts that can sleep. + */ +void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) { - return gpiod_set_value_cansleep(gpio_to_desc(gpio), value); + might_sleep_if(extra_checks); + if (!desc) + return; + + if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) + value = !value; + _gpiod_set_raw_value(desc, value); } -EXPORT_SYMBOL_GPL(gpio_set_value_cansleep); +EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep); #ifdef CONFIG_DEBUG_FS diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index b309a5c..00f8f0a 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -10,6 +10,8 @@ #ifdef CONFIG_GPIOLIB #include +#include +#include /* Platforms may implement their GPIO interface with library code, * at a small performance cost for non-inlined operations and some @@ -49,122 +51,11 @@ struct module; struct device_node; struct gpio_desc; -/** - * struct gpio_chip - abstract a GPIO controller - * @label: for diagnostics - * @dev: optional device providing the GPIOs - * @owner: helps prevent removal of modules exporting active GPIOs - * @list: links gpio_chips together for traversal - * @request: optional hook for chip-specific activation, such as - * enabling module power and clock; may sleep - * @free: optional hook for chip-specific deactivation, such as - * disabling module power and clock; may sleep - * @get_direction: returns direction for signal "offset", 0=out, 1=in, - * (same as GPIOF_DIR_XXX), or negative error - * @direction_input: configures signal "offset" as input, or returns error - * @get: returns value for signal "offset"; for output signals this - * returns either the value actually sensed, or zero - * @direction_output: configures signal "offset" as output, or returns error - * @set_debounce: optional hook for setting debounce time for specified gpio in - * interrupt triggered gpio chips - * @set: assigns output value for signal "offset" - * @to_irq: optional hook supporting non-static gpio_to_irq() mappings; - * implementation may not sleep - * @dbg_show: optional routine to show contents in debugfs; default code - * will be used when this is omitted, but custom code can show extra - * state (such as pullup/pulldown configuration). - * @base: identifies the first GPIO number handled by this chip; or, if - * negative during registration, requests dynamic ID allocation. - * @ngpio: the number of GPIOs handled by this controller; the last GPIO - * handled is (base + ngpio - 1). - * @desc: array of ngpio descriptors. Private. - * @can_sleep: flag must be set iff get()/set() methods sleep, as they - * must while accessing GPIO expander chips over I2C or SPI - * @names: if set, must be an array of strings to use as alternative - * names for the GPIOs in this chip. Any entry in the array - * may be NULL if there is no alias for the GPIO, however the - * array must be @ngpio entries long. A name can include a single printk - * format specifier for an unsigned int. It is substituted by the actual - * number of the gpio. - * - * A gpio_chip can help platforms abstract various sources of GPIOs so - * they can all be accessed through a common programing interface. - * Example sources would be SOC controllers, FPGAs, multifunction - * chips, dedicated GPIO expanders, and so on. - * - * Each chip controls a number of signals, identified in method calls - * by "offset" values in the range 0..(@ngpio - 1). When those signals - * are referenced through calls like gpio_get_value(gpio), the offset - * is calculated by subtracting @base from the gpio number. - */ -struct gpio_chip { - const char *label; - struct device *dev; - struct module *owner; - struct list_head list; - - int (*request)(struct gpio_chip *chip, - unsigned offset); - void (*free)(struct gpio_chip *chip, - unsigned offset); - int (*get_direction)(struct gpio_chip *chip, - unsigned offset); - int (*direction_input)(struct gpio_chip *chip, - unsigned offset); - int (*get)(struct gpio_chip *chip, - unsigned offset); - int (*direction_output)(struct gpio_chip *chip, - unsigned offset, int value); - int (*set_debounce)(struct gpio_chip *chip, - unsigned offset, unsigned debounce); - - void (*set)(struct gpio_chip *chip, - unsigned offset, int value); - - int (*to_irq)(struct gpio_chip *chip, - unsigned offset); - - void (*dbg_show)(struct seq_file *s, - struct gpio_chip *chip); - int base; - u16 ngpio; - struct gpio_desc *desc; - const char *const *names; - unsigned can_sleep:1; - unsigned exported:1; - -#if defined(CONFIG_OF_GPIO) - /* - * If CONFIG_OF is enabled, then all GPIO controllers described in the - * device tree automatically may have an OF translation - */ - struct device_node *of_node; - int of_gpio_n_cells; - int (*of_xlate)(struct gpio_chip *gc, - const struct of_phandle_args *gpiospec, u32 *flags); -#endif -#ifdef CONFIG_PINCTRL - /* - * If CONFIG_PINCTRL is enabled, then gpio controllers can optionally - * describe the actual pin range which they serve in an SoC. This - * information would be used by pinctrl subsystem to configure - * corresponding pins for gpio usage. - */ - struct list_head pin_ranges; -#endif -}; - -extern const char *gpiochip_is_requested(struct gpio_chip *chip, - unsigned offset); -extern struct gpio_chip *gpio_to_chip(unsigned gpio); - -/* add/remove chips */ -extern int gpiochip_add(struct gpio_chip *chip); -extern int __must_check gpiochip_remove(struct gpio_chip *chip); -extern struct gpio_chip *gpiochip_find(void *data, - int (*match)(struct gpio_chip *chip, - void *data)); - +/* caller holds gpio_lock *OR* gpio is marked as requested */ +static inline struct gpio_chip *gpio_to_chip(unsigned gpio) +{ + return gpiod_to_chip(gpio_to_desc(gpio)); +} /* Always use the library code for GPIO management calls, * or when sleeping may be involved. @@ -172,25 +63,52 @@ extern struct gpio_chip *gpiochip_find(void *data, extern int gpio_request(unsigned gpio, const char *label); extern void gpio_free(unsigned gpio); -extern int gpio_direction_input(unsigned gpio); -extern int gpio_direction_output(unsigned gpio, int value); +static inline int gpio_direction_input(unsigned gpio) +{ + return gpiod_direction_input(gpio_to_desc(gpio)); +} +static inline int gpio_direction_output(unsigned gpio, int value) +{ + return gpiod_direction_output(gpio_to_desc(gpio), value); +} -extern int gpio_set_debounce(unsigned gpio, unsigned debounce); +static inline int gpio_set_debounce(unsigned gpio, unsigned debounce) +{ + return gpiod_set_debounce(gpio_to_desc(gpio), debounce); +} -extern int gpio_get_value_cansleep(unsigned gpio); -extern void gpio_set_value_cansleep(unsigned gpio, int value); +static inline int gpio_get_value_cansleep(unsigned gpio) +{ + return gpiod_get_raw_value_cansleep(gpio_to_desc(gpio)); +} +static inline void gpio_set_value_cansleep(unsigned gpio, int value) +{ + return gpiod_set_raw_value_cansleep(gpio_to_desc(gpio), value); +} /* A platform's code may want to inline the I/O calls when * the GPIO is constant and refers to some always-present controller, * giving direct access to chip registers and tight bitbanging loops. */ -extern int __gpio_get_value(unsigned gpio); -extern void __gpio_set_value(unsigned gpio, int value); +static inline int __gpio_get_value(unsigned gpio) +{ + return gpiod_get_raw_value(gpio_to_desc(gpio)); +} +static inline void __gpio_set_value(unsigned gpio, int value) +{ + return gpiod_set_raw_value(gpio_to_desc(gpio), value); +} -extern int __gpio_cansleep(unsigned gpio); +static inline int __gpio_cansleep(unsigned gpio) +{ + return gpiod_cansleep(gpio_to_desc(gpio)); +} -extern int __gpio_to_irq(unsigned gpio); +static inline int __gpio_to_irq(unsigned gpio) +{ + return gpiod_to_irq(gpio_to_desc(gpio)); +} extern int gpio_lock_as_irq(struct gpio_chip *chip, unsigned int offset); extern void gpio_unlock_as_irq(struct gpio_chip *chip, unsigned int offset); @@ -199,19 +117,30 @@ extern int gpio_request_one(unsigned gpio, unsigned long flags, const char *labe extern int gpio_request_array(const struct gpio *array, size_t num); extern void gpio_free_array(const struct gpio *array, size_t num); -#ifdef CONFIG_GPIO_SYSFS - /* * A sysfs interface can be exported by individual drivers if they want, * but more typically is configured entirely from userspace. */ -extern int gpio_export(unsigned gpio, bool direction_may_change); -extern int gpio_export_link(struct device *dev, const char *name, - unsigned gpio); -extern int gpio_sysfs_set_active_low(unsigned gpio, int value); -extern void gpio_unexport(unsigned gpio); +static inline int gpio_export(unsigned gpio, bool direction_may_change) +{ + return gpiod_export(gpio_to_desc(gpio), direction_may_change); +} -#endif /* CONFIG_GPIO_SYSFS */ +static inline int gpio_export_link(struct device *dev, const char *name, + unsigned gpio) +{ + return gpiod_export_link(dev, name, gpio_to_desc(gpio)); +} + +static inline int gpio_sysfs_set_active_low(unsigned gpio, int value) +{ + return gpiod_sysfs_set_active_low(gpio_to_desc(gpio), value); +} + +static inline void gpio_unexport(unsigned gpio) +{ + gpiod_unexport(gpio_to_desc(gpio)); +} #ifdef CONFIG_PINCTRL @@ -281,31 +210,4 @@ static inline void gpio_set_value_cansleep(unsigned gpio, int value) #endif /* !CONFIG_GPIOLIB */ -#ifndef CONFIG_GPIO_SYSFS - -struct device; - -/* sysfs support is only available with gpiolib, where it's optional */ - -static inline int gpio_export(unsigned gpio, bool direction_may_change) -{ - return -ENOSYS; -} - -static inline int gpio_export_link(struct device *dev, const char *name, - unsigned gpio) -{ - return -ENOSYS; -} - -static inline int gpio_sysfs_set_active_low(unsigned gpio, int value) -{ - return -ENOSYS; -} - -static inline void gpio_unexport(unsigned gpio) -{ -} -#endif /* CONFIG_GPIO_SYSFS */ - #endif /* _ASM_GENERIC_GPIO_H */ diff --git a/include/linux/gpio.h b/include/linux/gpio.h index a06ec3e..c691df0 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -16,14 +16,17 @@ #define GPIOF_OUT_INIT_LOW (GPIOF_DIR_OUT | GPIOF_INIT_LOW) #define GPIOF_OUT_INIT_HIGH (GPIOF_DIR_OUT | GPIOF_INIT_HIGH) +/* Gpio pin is active-low */ +#define GPIOF_ACTIVE_LOW (1 << 2) + /* Gpio pin is open drain */ -#define GPIOF_OPEN_DRAIN (1 << 2) +#define GPIOF_OPEN_DRAIN (1 << 3) /* Gpio pin is open source */ -#define GPIOF_OPEN_SOURCE (1 << 3) +#define GPIOF_OPEN_SOURCE (1 << 4) -#define GPIOF_EXPORT (1 << 4) -#define GPIOF_EXPORT_CHANGEABLE (1 << 5) +#define GPIOF_EXPORT (1 << 5) +#define GPIOF_EXPORT_CHANGEABLE (1 << 6) #define GPIOF_EXPORT_DIR_FIXED (GPIOF_EXPORT) #define GPIOF_EXPORT_DIR_CHANGEABLE (GPIOF_EXPORT | GPIOF_EXPORT_CHANGEABLE) diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h new file mode 100644 index 0000000..2088eb5 --- /dev/null +++ b/include/linux/gpio/consumer.h @@ -0,0 +1,238 @@ +#ifndef __LINUX_GPIO_CONSUMER_H +#define __LINUX_GPIO_CONSUMER_H + +#include +#include + +#ifdef CONFIG_GPIOLIB + +struct device; +struct gpio_chip; + +/** + * Opaque descriptor for a GPIO. These are obtained using gpiod_get() and are + * preferable to the old integer-based handles. + * + * Contrary to integers, a pointer to a gpio_desc is guaranteed to be valid + * until the GPIO is released. + */ +struct gpio_desc; + +int gpiod_get_direction(const struct gpio_desc *desc); +int gpiod_direction_input(struct gpio_desc *desc); +int gpiod_direction_output(struct gpio_desc *desc, int value); + +/* Value get/set from non-sleeping context */ +int gpiod_get_value(const struct gpio_desc *desc); +void gpiod_set_value(struct gpio_desc *desc, int value); +int gpiod_get_raw_value(const struct gpio_desc *desc); +void gpiod_set_raw_value(struct gpio_desc *desc, int value); + +/* Value get/set from sleeping context */ +int gpiod_get_value_cansleep(const struct gpio_desc *desc); +void gpiod_set_value_cansleep(struct gpio_desc *desc, int value); +int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc); +void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value); + +int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce); + +int gpiod_is_active_low(const struct gpio_desc *desc); +int gpiod_cansleep(const struct gpio_desc *desc); + +int gpiod_to_irq(const struct gpio_desc *desc); + +/* Convert between the old gpio_ and new gpiod_ interfaces */ +struct gpio_desc *gpio_to_desc(unsigned gpio); +int desc_to_gpio(const struct gpio_desc *desc); +struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc); + +#else /* CONFIG_GPIOLIB */ + +static inline struct gpio_desc *__must_check gpiod_get(struct device *dev, + const char *con_id) +{ + return ERR_PTR(-ENOSYS); +} +static inline struct gpio_desc *__must_check gpiod_get_index(struct device *dev, + const char *con_id, + unsigned int idx) +{ + return ERR_PTR(-ENOSYS); +} +static inline void gpiod_put(struct gpio_desc *desc) +{ + might_sleep(); + + /* GPIO can never have been requested */ + WARN_ON(1); +} + +static inline struct gpio_desc *__must_check devm_gpiod_get(struct device *dev, + const char *con_id) +{ + return ERR_PTR(-ENOSYS); +} +static inline +struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev, + const char *con_id, + unsigned int idx) +{ + return ERR_PTR(-ENOSYS); +} +static inline void devm_gpiod_put(struct device *dev, struct gpio_desc *desc) +{ + might_sleep(); + + /* GPIO can never have been requested */ + WARN_ON(1); +} + + +static inline int gpiod_get_direction(const struct gpio_desc *desc) +{ + /* GPIO can never have been requested */ + WARN_ON(1); + return -ENOSYS; +} +static inline int gpiod_direction_input(struct gpio_desc *desc) +{ + /* GPIO can never have been requested */ + WARN_ON(1); + return -ENOSYS; +} +static inline int gpiod_direction_output(struct gpio_desc *desc, int value) +{ + /* GPIO can never have been requested */ + WARN_ON(1); + return -ENOSYS; +} + + +static inline int gpiod_get_value(const struct gpio_desc *desc) +{ + /* GPIO can never have been requested */ + WARN_ON(1); + return 0; +} +static inline void gpiod_set_value(struct gpio_desc *desc, int value) +{ + /* GPIO can never have been requested */ + WARN_ON(1); +} +static inline int gpiod_get_raw_value(const struct gpio_desc *desc) +{ + /* GPIO can never have been requested */ + WARN_ON(1); + return 0; +} +static inline void gpiod_set_raw_value(struct gpio_desc *desc, int value) +{ + /* GPIO can never have been requested */ + WARN_ON(1); +} + +static inline int gpiod_get_value_cansleep(const struct gpio_desc *desc) +{ + /* GPIO can never have been requested */ + WARN_ON(1); + return 0; +} +static inline void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) +{ + /* GPIO can never have been requested */ + WARN_ON(1); +} +static inline int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc) +{ + /* GPIO can never have been requested */ + WARN_ON(1); + return 0; +} +static inline void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, + int value) +{ + /* GPIO can never have been requested */ + WARN_ON(1); +} + +static inline int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) +{ + /* GPIO can never have been requested */ + WARN_ON(1); + return -ENOSYS; +} + +static inline int gpiod_is_active_low(const struct gpio_desc *desc) +{ + /* GPIO can never have been requested */ + WARN_ON(1); + return 0; +} +static inline int gpiod_cansleep(const struct gpio_desc *desc) +{ + /* GPIO can never have been requested */ + WARN_ON(1); + return 0; +} + +static inline int gpiod_to_irq(const struct gpio_desc *desc) +{ + /* GPIO can never have been requested */ + WARN_ON(1); + return -EINVAL; +} + +static inline struct gpio_desc *gpio_to_desc(unsigned gpio) +{ + return ERR_PTR(-EINVAL); +} +static inline int desc_to_gpio(const struct gpio_desc *desc) +{ + /* GPIO can never have been requested */ + WARN_ON(1); + return -EINVAL; +} +static inline struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc) +{ + /* GPIO can never have been requested */ + WARN_ON(1); + return ERR_PTR(-ENODEV); +} + + +#endif /* CONFIG_GPIOLIB */ + +#if IS_ENABLED(CONFIG_GPIOLIB) && IS_ENABLED(CONFIG_GPIO_SYSFS) + +int gpiod_export(struct gpio_desc *desc, bool direction_may_change); +int gpiod_export_link(struct device *dev, const char *name, + struct gpio_desc *desc); +int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value); +void gpiod_unexport(struct gpio_desc *desc); + +#else /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */ + +static inline int gpiod_export(struct gpio_desc *desc, + bool direction_may_change) +{ + return -ENOSYS; +} + +static inline int gpiod_export_link(struct device *dev, const char *name, + struct gpio_desc *desc) +{ + return -ENOSYS; +} + +static inline int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value) +{ + return -ENOSYS; +} + +static inline void gpiod_unexport(struct gpio_desc *desc) +{ +} + +#endif /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */ + +#endif diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h new file mode 100644 index 0000000..5dc172c --- /dev/null +++ b/include/linux/gpio/driver.h @@ -0,0 +1,127 @@ +#ifndef __LINUX_GPIO_DRIVER_H +#define __LINUX_GPIO_DRIVER_H + +#include + +struct device; +struct gpio_desc; + +/** + * struct gpio_chip - abstract a GPIO controller + * @label: for diagnostics + * @dev: optional device providing the GPIOs + * @owner: helps prevent removal of modules exporting active GPIOs + * @list: links gpio_chips together for traversal + * @request: optional hook for chip-specific activation, such as + * enabling module power and clock; may sleep + * @free: optional hook for chip-specific deactivation, such as + * disabling module power and clock; may sleep + * @get_direction: returns direction for signal "offset", 0=out, 1=in, + * (same as GPIOF_DIR_XXX), or negative error + * @direction_input: configures signal "offset" as input, or returns error + * @direction_output: configures signal "offset" as output, or returns error + * @get: returns value for signal "offset"; for output signals this + * returns either the value actually sensed, or zero + * @set: assigns output value for signal "offset" + * @set_debounce: optional hook for setting debounce time for specified gpio in + * interrupt triggered gpio chips + * @to_irq: optional hook supporting non-static gpio_to_irq() mappings; + * implementation may not sleep + * @dbg_show: optional routine to show contents in debugfs; default code + * will be used when this is omitted, but custom code can show extra + * state (such as pullup/pulldown configuration). + * @base: identifies the first GPIO number handled by this chip; or, if + * negative during registration, requests dynamic ID allocation. + * @ngpio: the number of GPIOs handled by this controller; the last GPIO + * handled is (base + ngpio - 1). + * @desc: array of ngpio descriptors. Private. + * @can_sleep: flag must be set iff get()/set() methods sleep, as they + * must while accessing GPIO expander chips over I2C or SPI + * @names: if set, must be an array of strings to use as alternative + * names for the GPIOs in this chip. Any entry in the array + * may be NULL if there is no alias for the GPIO, however the + * array must be @ngpio entries long. A name can include a single printk + * format specifier for an unsigned int. It is substituted by the actual + * number of the gpio. + * + * A gpio_chip can help platforms abstract various sources of GPIOs so + * they can all be accessed through a common programing interface. + * Example sources would be SOC controllers, FPGAs, multifunction + * chips, dedicated GPIO expanders, and so on. + * + * Each chip controls a number of signals, identified in method calls + * by "offset" values in the range 0..(@ngpio - 1). When those signals + * are referenced through calls like gpio_get_value(gpio), the offset + * is calculated by subtracting @base from the gpio number. + */ +struct gpio_chip { + const char *label; + struct device *dev; + struct module *owner; + struct list_head list; + + int (*request)(struct gpio_chip *chip, + unsigned offset); + void (*free)(struct gpio_chip *chip, + unsigned offset); + int (*get_direction)(struct gpio_chip *chip, + unsigned offset); + int (*direction_input)(struct gpio_chip *chip, + unsigned offset); + int (*direction_output)(struct gpio_chip *chip, + unsigned offset, int value); + int (*get)(struct gpio_chip *chip, + unsigned offset); + void (*set)(struct gpio_chip *chip, + unsigned offset, int value); + int (*set_debounce)(struct gpio_chip *chip, + unsigned offset, + unsigned debounce); + + int (*to_irq)(struct gpio_chip *chip, + unsigned offset); + + void (*dbg_show)(struct seq_file *s, + struct gpio_chip *chip); + int base; + u16 ngpio; + struct gpio_desc *desc; + const char *const *names; + unsigned can_sleep:1; + unsigned exported:1; + +#if defined(CONFIG_OF_GPIO) + /* + * If CONFIG_OF is enabled, then all GPIO controllers described in the + * device tree automatically may have an OF translation + */ + struct device_node *of_node; + int of_gpio_n_cells; + int (*of_xlate)(struct gpio_chip *gc, + const struct of_phandle_args *gpiospec, u32 *flags); +#endif +#ifdef CONFIG_PINCTRL + /* + * If CONFIG_PINCTRL is enabled, then gpio controllers can optionally + * describe the actual pin range which they serve in an SoC. This + * information would be used by pinctrl subsystem to configure + * corresponding pins for gpio usage. + */ + struct list_head pin_ranges; +#endif +}; + +extern const char *gpiochip_is_requested(struct gpio_chip *chip, + unsigned offset); + +/* add/remove chips */ +extern int gpiochip_add(struct gpio_chip *chip); +extern int __must_check gpiochip_remove(struct gpio_chip *chip); +extern struct gpio_chip *gpiochip_find(void *data, + int (*match)(struct gpio_chip *chip, void *data)); + +/* lock/unlock as IRQ */ +int gpiod_lock_as_irq(struct gpio_desc *desc); +void gpiod_unlock_as_irq(struct gpio_desc *desc); + +#endif -- cgit v0.10.2 From af8b6375a8291fe2cf77707f3edec86b98a999cc Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Thu, 17 Oct 2013 10:21:37 -0700 Subject: gpiolib: port of_ functions to use gpiod Refactor the of_ functions of gpiolib to use the now public gpiod interface, and export of_get_named_gpiod_flags() and of_get_gpiod_flags() functions. Signed-off-by: Alexandre Courbot Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 0dfaf20..32a396d 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -15,19 +15,21 @@ #include #include #include -#include +#include #include #include #include #include #include +struct gpio_desc; + /* Private data structure for of_gpiochip_find_and_xlate */ struct gg_data { enum of_gpio_flags *flags; struct of_phandle_args gpiospec; - int out_gpio; + struct gpio_desc *out_gpio; }; /* Private function for resolving node pointer to gpio_chip */ @@ -45,28 +47,31 @@ static int of_gpiochip_find_and_xlate(struct gpio_chip *gc, void *data) if (ret < 0) return false; - gg_data->out_gpio = ret + gc->base; + gg_data->out_gpio = gpio_to_desc(ret + gc->base); return true; } /** - * of_get_named_gpio_flags() - Get a GPIO number and flags to use with GPIO API + * of_get_named_gpiod_flags() - Get a GPIO descriptor and flags for GPIO API * @np: device node to get GPIO from * @propname: property name containing gpio specifier(s) * @index: index of the GPIO * @flags: a flags pointer to fill in * - * Returns GPIO number to use with Linux generic GPIO API, or one of the errno + * Returns GPIO descriptor to use with Linux GPIO API, or one of the errno * value on the error condition. If @flags is not NULL the function also fills * in flags for the GPIO. */ -int of_get_named_gpio_flags(struct device_node *np, const char *propname, - int index, enum of_gpio_flags *flags) +struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, + const char *propname, int index, enum of_gpio_flags *flags) { /* Return -EPROBE_DEFER to support probe() functions to be called * later when the GPIO actually becomes available */ - struct gg_data gg_data = { .flags = flags, .out_gpio = -EPROBE_DEFER }; + struct gg_data gg_data = { + .flags = flags, + .out_gpio = ERR_PTR(-EPROBE_DEFER) + }; int ret; /* .of_xlate might decide to not fill in the flags, so clear it. */ @@ -78,16 +83,17 @@ int of_get_named_gpio_flags(struct device_node *np, const char *propname, if (ret) { pr_debug("%s: can't parse gpios property of node '%s[%d]'\n", __func__, np->full_name, index); - return ret; + return ERR_PTR(ret); } gpiochip_find(&gg_data, of_gpiochip_find_and_xlate); of_node_put(gg_data.gpiospec.np); - pr_debug("%s exited with status %d\n", __func__, gg_data.out_gpio); + pr_debug("%s exited with status %d\n", __func__, + PTR_RET(gg_data.out_gpio)); return gg_data.out_gpio; } -EXPORT_SYMBOL(of_get_named_gpio_flags); +EXPORT_SYMBOL(of_get_named_gpiod_flags); /** * of_gpio_simple_xlate - translate gpio_spec to the GPIO number and flags diff --git a/include/linux/of_gpio.h b/include/linux/of_gpio.h index a83dc6f..d71f2cc 100644 --- a/include/linux/of_gpio.h +++ b/include/linux/of_gpio.h @@ -21,6 +21,7 @@ #include struct device_node; +struct gpio_desc; /* * This is Linux-specific flags. By default controllers' and Linux' mapping @@ -47,7 +48,7 @@ static inline struct of_mm_gpio_chip *to_of_mm_gpio_chip(struct gpio_chip *gc) return container_of(gc, struct of_mm_gpio_chip, gc); } -extern int of_get_named_gpio_flags(struct device_node *np, +extern struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, const char *list_name, int index, enum of_gpio_flags *flags); extern int of_mm_gpiochip_add(struct device_node *np, @@ -62,10 +63,10 @@ extern int of_gpio_simple_xlate(struct gpio_chip *gc, #else /* CONFIG_OF_GPIO */ /* Drivers may not strictly depend on the GPIO support, so let them link. */ -static inline int of_get_named_gpio_flags(struct device_node *np, +static inline struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, const char *list_name, int index, enum of_gpio_flags *flags) { - return -ENOSYS; + return ERR_PTR(-ENOSYS); } static inline int of_gpio_simple_xlate(struct gpio_chip *gc, @@ -80,6 +81,18 @@ static inline void of_gpiochip_remove(struct gpio_chip *gc) { } #endif /* CONFIG_OF_GPIO */ +static inline int of_get_named_gpio_flags(struct device_node *np, + const char *list_name, int index, enum of_gpio_flags *flags) +{ + struct gpio_desc *desc; + desc = of_get_named_gpiod_flags(np, list_name, index, flags); + + if (IS_ERR(desc)) + return PTR_ERR(desc); + else + return desc_to_gpio(desc); +} + /** * of_gpio_named_count() - Count GPIOs for a device * @np: device node to count GPIOs for @@ -117,15 +130,21 @@ static inline int of_gpio_count(struct device_node *np) } /** - * of_get_gpio_flags() - Get a GPIO number and flags to use with GPIO API + * of_get_gpiod_flags() - Get a GPIO descriptor and flags to use with GPIO API * @np: device node to get GPIO from * @index: index of the GPIO * @flags: a flags pointer to fill in * - * Returns GPIO number to use with Linux generic GPIO API, or one of the errno + * Returns GPIO descriptor to use with Linux generic GPIO API, or a errno * value on the error condition. If @flags is not NULL the function also fills * in flags for the GPIO. */ +static inline struct gpio_desc *of_get_gpiod_flags(struct device_node *np, + int index, enum of_gpio_flags *flags) +{ + return of_get_named_gpiod_flags(np, "gpios", index, flags); +} + static inline int of_get_gpio_flags(struct device_node *np, int index, enum of_gpio_flags *flags) { -- cgit v0.10.2 From bae48da237fcedd7ad09569025483b988635efb7 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Thu, 17 Oct 2013 10:21:38 -0700 Subject: gpiolib: add gpiod_get() and gpiod_put() functions Add gpiod_get(), gpiod_get_index() and gpiod_put() functions that provide safer management of GPIOs. These functions put the GPIO framework in line with the conventions of other frameworks in the kernel, and help ensure every GPIO is declared properly and valid while it is used. Signed-off-by: Alexandre Courbot Signed-off-by: Linus Walleij diff --git a/drivers/gpio/devres.c b/drivers/gpio/devres.c index 3e7812f..2caa257 100644 --- a/drivers/gpio/devres.c +++ b/drivers/gpio/devres.c @@ -19,6 +19,89 @@ #include #include +static void devm_gpiod_release(struct device *dev, void *res) +{ + struct gpio_desc **desc = res; + + gpiod_put(*desc); +} + +static int devm_gpiod_match(struct device *dev, void *res, void *data) +{ + struct gpio_desc **this = res, **gpio = data; + + return *this == *gpio; +} + +/** + * devm_gpiod_get - Resource-managed gpiod_get() + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * + * Managed gpiod_get(). GPIO descriptors returned from this function are + * automatically disposed on driver detach. See gpiod_get() for detailed + * information about behavior and return values. + */ +struct gpio_desc *__must_check devm_gpiod_get(struct device *dev, + const char *con_id) +{ + return devm_gpiod_get_index(dev, con_id, 0); +} +EXPORT_SYMBOL(devm_gpiod_get); + +/** + * devm_gpiod_get_index - Resource-managed gpiod_get_index() + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * @idx: index of the GPIO to obtain in the consumer + * + * Managed gpiod_get_index(). GPIO descriptors returned from this function are + * automatically disposed on driver detach. See gpiod_get_index() for detailed + * information about behavior and return values. + */ +struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev, + const char *con_id, + unsigned int idx) +{ + struct gpio_desc **dr; + struct gpio_desc *desc; + + dr = devres_alloc(devm_gpiod_release, sizeof(struct gpiod_desc *), + GFP_KERNEL); + if (!dr) + return ERR_PTR(-ENOMEM); + + desc = gpiod_get_index(dev, con_id, idx); + if (IS_ERR(desc)) { + devres_free(dr); + return desc; + } + + *dr = desc; + devres_add(dev, dr); + + return 0; +} +EXPORT_SYMBOL(devm_gpiod_get_index); + +/** + * devm_gpiod_put - Resource-managed gpiod_put() + * @desc: GPIO descriptor to dispose of + * + * Dispose of a GPIO descriptor obtained with devm_gpiod_get() or + * devm_gpiod_get_index(). Normally this function will not be called as the GPIO + * will be disposed of by the resource management code. + */ +void devm_gpiod_put(struct device *dev, struct gpio_desc *desc) +{ + WARN_ON(devres_release(dev, devm_gpiod_release, devm_gpiod_match, + &desc)); +} +EXPORT_SYMBOL(devm_gpiod_put); + + + + static void devm_gpio_release(struct device *dev, void *res) { unsigned *gpio = res; diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 224abdc..9263c7b 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -70,6 +70,8 @@ static struct gpio_desc gpio_desc[ARCH_NR_GPIOS]; #define GPIO_OFFSET_VALID(chip, offset) (offset >= 0 && offset < chip->ngpio) +static DEFINE_MUTEX(gpio_lookup_lock); +static LIST_HEAD(gpio_lookup_list); static LIST_HEAD(gpio_chips); #ifdef CONFIG_GPIO_SYSFS @@ -2192,6 +2194,207 @@ void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) } EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep); +/** + * gpiod_add_table() - register GPIO device consumers + * @table: array of consumers to register + * @num: number of consumers in table + */ +void gpiod_add_table(struct gpiod_lookup *table, size_t size) +{ + mutex_lock(&gpio_lookup_lock); + + while (size--) { + list_add_tail(&table->list, &gpio_lookup_list); + table++; + } + + mutex_unlock(&gpio_lookup_lock); +} + +/* + * Caller must have a acquired gpio_lookup_lock + */ +static struct gpio_chip *find_chip_by_name(const char *name) +{ + struct gpio_chip *chip = NULL; + + list_for_each_entry(chip, &gpio_lookup_list, list) { + if (chip->label == NULL) + continue; + if (!strcmp(chip->label, name)) + break; + } + + return chip; +} + +#ifdef CONFIG_OF +static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, + unsigned int idx, unsigned long *flags) +{ + char prop_name[32]; /* 32 is max size of property name */ + enum of_gpio_flags of_flags; + struct gpio_desc *desc; + + if (con_id) + snprintf(prop_name, 32, "%s-gpios", con_id); + else + snprintf(prop_name, 32, "gpios"); + + desc = of_get_named_gpiod_flags(dev->of_node, prop_name, idx, + &of_flags); + + if (IS_ERR(desc)) + return desc; + + if (of_flags & OF_GPIO_ACTIVE_LOW) + *flags |= GPIOF_ACTIVE_LOW; + + return desc; +} +#else +static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, + unsigned int idx, unsigned long *flags) +{ + return ERR_PTR(-ENODEV); +} +#endif + +static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, + unsigned int idx, unsigned long *flags) +{ + const char *dev_id = dev ? dev_name(dev) : NULL; + struct gpio_desc *desc = ERR_PTR(-ENODEV); + unsigned int match, best = 0; + struct gpiod_lookup *p; + + mutex_lock(&gpio_lookup_lock); + + list_for_each_entry(p, &gpio_lookup_list, list) { + match = 0; + + if (p->dev_id) { + if (!dev_id || strcmp(p->dev_id, dev_id)) + continue; + + match += 2; + } + + if (p->con_id) { + if (!con_id || strcmp(p->con_id, con_id)) + continue; + + match += 1; + } + + if (p->idx != idx) + continue; + + if (match > best) { + struct gpio_chip *chip; + + chip = find_chip_by_name(p->chip_label); + + if (!chip) { + dev_warn(dev, "cannot find GPIO chip %s\n", + p->chip_label); + continue; + } + + if (chip->ngpio >= p->chip_hwnum) { + dev_warn(dev, "GPIO chip %s has %d GPIOs\n", + chip->label, chip->ngpio); + continue; + } + + desc = gpio_to_desc(chip->base + p->chip_hwnum); + *flags = p->flags; + + if (match != 3) + best = match; + else + break; + } + } + + mutex_unlock(&gpio_lookup_lock); + + return desc; +} + +/** + * gpio_get - obtain a GPIO for a given GPIO function + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * + * Return the GPIO descriptor corresponding to the function con_id of device + * dev, or an IS_ERR() condition if an error occured. + */ +struct gpio_desc *__must_check gpiod_get(struct device *dev, const char *con_id) +{ + return gpiod_get_index(dev, con_id, 0); +} +EXPORT_SYMBOL_GPL(gpiod_get); + +/** + * gpiod_get_index - obtain a GPIO from a multi-index GPIO function + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * @idx: index of the GPIO to obtain in the consumer + * + * This variant of gpiod_get() allows to access GPIOs other than the first + * defined one for functions that define several GPIOs. + * + * Return a valid GPIO descriptor, or an IS_ERR() condition in case of error. + */ +struct gpio_desc *__must_check gpiod_get_index(struct device *dev, + const char *con_id, + unsigned int idx) +{ + struct gpio_desc *desc; + int status; + unsigned long flags = 0; + + dev_dbg(dev, "GPIO lookup for consumer %s\n", con_id); + + /* Using device tree? */ + if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) { + dev_dbg(dev, "using device tree for GPIO lookup\n"); + desc = of_find_gpio(dev, con_id, idx, &flags); + } else { + dev_dbg(dev, "using lookup tables for GPIO lookup"); + desc = gpiod_find(dev, con_id, idx, &flags); + } + + if (IS_ERR(desc)) { + dev_warn(dev, "lookup for GPIO %s failed\n", con_id); + return desc; + } + + status = gpiod_request(desc, con_id); + + if (status < 0) + return ERR_PTR(status); + + if (flags & GPIOF_ACTIVE_LOW) + set_bit(FLAG_ACTIVE_LOW, &desc->flags); + + return desc; +} +EXPORT_SYMBOL_GPL(gpiod_get_index); + +/** + * gpiod_put - dispose of a GPIO descriptor + * @desc: GPIO descriptor to dispose of + * + * No descriptor can be used after gpiod_put() has been called on it. + */ +void gpiod_put(struct gpio_desc *desc) +{ + gpiod_free(desc); +} +EXPORT_SYMBOL_GPL(gpiod_put); + #ifdef CONFIG_DEBUG_FS static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 2088eb5..4d34dbb 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -18,6 +18,21 @@ struct gpio_chip; */ struct gpio_desc; +/* Acquire and dispose GPIOs */ +struct gpio_desc *__must_check gpiod_get(struct device *dev, + const char *con_id); +struct gpio_desc *__must_check gpiod_get_index(struct device *dev, + const char *con_id, + unsigned int idx); +void gpiod_put(struct gpio_desc *desc); + +struct gpio_desc *__must_check devm_gpiod_get(struct device *dev, + const char *con_id); +struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev, + const char *con_id, + unsigned int idx); +void devm_gpiod_put(struct device *dev, struct gpio_desc *desc); + int gpiod_get_direction(const struct gpio_desc *desc); int gpiod_direction_input(struct gpio_desc *desc); int gpiod_direction_output(struct gpio_desc *desc, int value); diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 5dc172c..cd9da38 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -124,4 +124,60 @@ extern struct gpio_chip *gpiochip_find(void *data, int gpiod_lock_as_irq(struct gpio_desc *desc); void gpiod_unlock_as_irq(struct gpio_desc *desc); +/** + * Lookup table for associating GPIOs to specific devices and functions using + * platform data. + */ +struct gpiod_lookup { + struct list_head list; + /* + * name of the chip the GPIO belongs to + */ + const char *chip_label; + /* + * hardware number (i.e. relative to the chip) of the GPIO + */ + u16 chip_hwnum; + /* + * name of device that can claim this GPIO + */ + const char *dev_id; + /* + * name of the GPIO from the device's point of view + */ + const char *con_id; + /* + * index of the GPIO in case several GPIOs share the same name + */ + unsigned int idx; + /* + * mask of GPIOF_* values + */ + unsigned long flags; +}; + +/* + * Simple definition of a single GPIO under a con_id + */ +#define GPIO_LOOKUP(_chip_label, _chip_hwnum, _dev_id, _con_id, _flags) \ + GPIO_LOOKUP_IDX(_chip_label, _chip_hwnum, _dev_id, _con_id, 0, _flags) + +/* + * Use this macro if you need to have several GPIOs under the same con_id. + * Each GPIO needs to use a different index and can be accessed using + * gpiod_get_index() + */ +#define GPIO_LOOKUP_IDX(_chip_label, _chip_hwnum, _dev_id, _con_id, _idx, \ + _flags) \ +{ \ + .chip_label = _chip_label, \ + .chip_hwnum = _chip_hwnum, \ + .dev_id = _dev_id, \ + .con_id = _con_id, \ + .idx = _idx, \ + .flags = _flags, \ +} + +void gpiod_add_table(struct gpiod_lookup *table, size_t size); + #endif -- cgit v0.10.2 From 936e15dd2128eb5aa71251766f1176552b45f43c Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 10 Oct 2013 11:01:08 +0300 Subject: gpiolib / ACPI: convert to gpiod interfaces The new GPIO descriptor based interface is now preferred over the old integer based one. This patch converts the ACPI GPIO helpers to use this new interface internally. In addition to that provide compatibility function acpi_get_gpio_by_index() that converts the returned GPIO descriptor to an integer. We also drop acpi_get_gpio() as it is not used anywhere outside gpiolib-acpi and even there we use acpi_get_gpiod() instead. Signed-off-by: Mika Westerberg Acked-by: Rafael J. Wysocki Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 1745ce5..03187d0 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -11,7 +11,7 @@ */ #include -#include +#include #include #include #include @@ -33,14 +33,15 @@ static int acpi_gpiochip_find(struct gpio_chip *gc, void *data) } /** - * acpi_get_gpio() - Translate ACPI GPIO pin to GPIO number usable with GPIO API + * acpi_get_gpiod() - Translate ACPI GPIO pin to GPIO descriptor usable with GPIO API * @path: ACPI GPIO controller full path name, (e.g. "\\_SB.GPO1") * @pin: ACPI GPIO pin number (0-based, controller-relative) * - * Returns GPIO number to use with Linux generic GPIO API, or errno error value + * Returns GPIO descriptor to use with Linux generic GPIO API, or ERR_PTR + * error value */ -int acpi_get_gpio(char *path, int pin) +static struct gpio_desc *acpi_get_gpiod(char *path, int pin) { struct gpio_chip *chip; acpi_handle handle; @@ -48,18 +49,17 @@ int acpi_get_gpio(char *path, int pin) status = acpi_get_handle(NULL, path, &handle); if (ACPI_FAILURE(status)) - return -ENODEV; + return ERR_PTR(-ENODEV); chip = gpiochip_find(handle, acpi_gpiochip_find); if (!chip) - return -ENODEV; + return ERR_PTR(-ENODEV); - if (!gpio_is_valid(chip->base + pin)) - return -EINVAL; + if (pin < 0 || pin > chip->ngpio) + return ERR_PTR(-EINVAL); - return chip->base + pin; + return gpio_to_desc(chip->base + pin); } -EXPORT_SYMBOL_GPL(acpi_get_gpio); static irqreturn_t acpi_gpio_irq_handler(int irq, void *data) { @@ -235,7 +235,7 @@ EXPORT_SYMBOL(acpi_gpiochip_free_interrupts); struct acpi_gpio_lookup { struct acpi_gpio_info info; int index; - int gpio; + struct gpio_desc *desc; int n; }; @@ -246,11 +246,11 @@ static int acpi_find_gpio(struct acpi_resource *ares, void *data) if (ares->type != ACPI_RESOURCE_TYPE_GPIO) return 1; - if (lookup->n++ == lookup->index && lookup->gpio < 0) { + if (lookup->n++ == lookup->index && !lookup->desc) { const struct acpi_resource_gpio *agpio = &ares->data.gpio; - lookup->gpio = acpi_get_gpio(agpio->resource_source.string_ptr, - agpio->pin_table[0]); + lookup->desc = acpi_get_gpiod(agpio->resource_source.string_ptr, + agpio->pin_table[0]); lookup->info.gpioint = agpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT; } @@ -259,24 +259,24 @@ static int acpi_find_gpio(struct acpi_resource *ares, void *data) } /** - * acpi_get_gpio_by_index() - get a GPIO number from device resources + * acpi_get_gpiod_by_index() - get a GPIO descriptor from device resources * @dev: pointer to a device to get GPIO from * @index: index of GpioIo/GpioInt resource (starting from %0) * @info: info pointer to fill in (optional) * * Function goes through ACPI resources for @dev and based on @index looks - * up a GpioIo/GpioInt resource, translates it to the Linux GPIO number, + * up a GpioIo/GpioInt resource, translates it to the Linux GPIO descriptor, * and returns it. @index matches GpioIo/GpioInt resources only so if there * are total %3 GPIO resources, the index goes from %0 to %2. * - * If the GPIO cannot be translated or there is an error, negative errno is + * If the GPIO cannot be translated or there is an error an ERR_PTR is * returned. * * Note: if the GPIO resource has multiple entries in the pin list, this * function only returns the first. */ -int acpi_get_gpio_by_index(struct device *dev, int index, - struct acpi_gpio_info *info) +struct gpio_desc *acpi_get_gpiod_by_index(struct device *dev, int index, + struct acpi_gpio_info *info) { struct acpi_gpio_lookup lookup; struct list_head resource_list; @@ -285,27 +285,26 @@ int acpi_get_gpio_by_index(struct device *dev, int index, int ret; if (!dev) - return -EINVAL; + return ERR_PTR(-EINVAL); handle = ACPI_HANDLE(dev); if (!handle || acpi_bus_get_device(handle, &adev)) - return -ENODEV; + return ERR_PTR(-ENODEV); memset(&lookup, 0, sizeof(lookup)); lookup.index = index; - lookup.gpio = -ENODEV; INIT_LIST_HEAD(&resource_list); ret = acpi_dev_get_resources(adev, &resource_list, acpi_find_gpio, &lookup); if (ret < 0) - return ret; + return ERR_PTR(ret); acpi_dev_free_resource_list(&resource_list); - if (lookup.gpio >= 0 && info) + if (lookup.desc && info) *info = lookup.info; - return lookup.gpio; + return lookup.desc ? lookup.desc : ERR_PTR(-ENODEV); } -EXPORT_SYMBOL_GPL(acpi_get_gpio_by_index); +EXPORT_SYMBOL_GPL(acpi_get_gpiod_by_index); diff --git a/include/linux/acpi_gpio.h b/include/linux/acpi_gpio.h index 4c120a1..b6ce601 100644 --- a/include/linux/acpi_gpio.h +++ b/include/linux/acpi_gpio.h @@ -2,8 +2,10 @@ #define _LINUX_ACPI_GPIO_H_ #include +#include #include #include +#include /** * struct acpi_gpio_info - ACPI GPIO specific information @@ -15,23 +17,18 @@ struct acpi_gpio_info { #ifdef CONFIG_GPIO_ACPI -int acpi_get_gpio(char *path, int pin); -int acpi_get_gpio_by_index(struct device *dev, int index, - struct acpi_gpio_info *info); +struct gpio_desc *acpi_get_gpiod_by_index(struct device *dev, int index, + struct acpi_gpio_info *info); void acpi_gpiochip_request_interrupts(struct gpio_chip *chip); void acpi_gpiochip_free_interrupts(struct gpio_chip *chip); #else /* CONFIG_GPIO_ACPI */ -static inline int acpi_get_gpio(char *path, int pin) +static inline struct gpio_desc * +acpi_get_gpiod_by_index(struct device *dev, int index, + struct acpi_gpio_info *info) { - return -ENODEV; -} - -static inline int acpi_get_gpio_by_index(struct device *dev, int index, - struct acpi_gpio_info *info) -{ - return -ENODEV; + return ERR_PTR(-ENOSYS); } static inline void acpi_gpiochip_request_interrupts(struct gpio_chip *chip) { } @@ -39,4 +36,14 @@ static inline void acpi_gpiochip_free_interrupts(struct gpio_chip *chip) { } #endif /* CONFIG_GPIO_ACPI */ +static inline int acpi_get_gpio_by_index(struct device *dev, int index, + struct acpi_gpio_info *info) +{ + struct gpio_desc *desc = acpi_get_gpiod_by_index(dev, index, info); + + if (IS_ERR(desc)) + return PTR_ERR(desc); + return desc_to_gpio(desc); +} + #endif /* _LINUX_ACPI_GPIO_H_ */ -- cgit v0.10.2 From 81f59e9d138c3ba0bd0f97d4e14f856d987e3f1d Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 10 Oct 2013 11:01:09 +0300 Subject: gpiolib / ACPI: add ACPI support for gpiod_get_index() gpiod_get_index() and gpiod_get() are now the new preferred way to request GPIOs. Add support for finding the corresponding GPIO descriptor from ACPI namespace. Signed-off-by: Mika Westerberg Acked-by: Rafael J. Wysocki Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 9263c7b..bee93c8 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -2260,6 +2261,12 @@ static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, } #endif +static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id, + unsigned int idx, unsigned long *flags) +{ + return acpi_get_gpiod_by_index(dev, idx, NULL); +} + static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, unsigned int idx, unsigned long *flags) { @@ -2361,6 +2368,9 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev, if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) { dev_dbg(dev, "using device tree for GPIO lookup\n"); desc = of_find_gpio(dev, con_id, idx, &flags); + } else if (IS_ENABLED(CONFIG_ACPI) && dev && ACPI_HANDLE(dev)) { + dev_dbg(dev, "using ACPI for GPIO lookup\n"); + desc = acpi_find_gpio(dev, con_id, idx, &flags); } else { dev_dbg(dev, "using lookup tables for GPIO lookup"); desc = gpiod_find(dev, con_id, idx, &flags); -- cgit v0.10.2 From e01f440a689aeb2d0e81c696fe2069f8d01d5d49 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 10 Oct 2013 11:01:10 +0300 Subject: gpiolib / ACPI: allow passing GPIOF_ACTIVE_LOW for GpioInt resources The ACPI GpioInt resources contain polarity field that is used to specify whether the interrupt is active high or low. Since gpiolib supports GPIOF_ACTIVE_LOW we can pass this information in the flags field in acpi_find_gpio(), analogous to the DeviceTree version. Signed-off-by: Mika Westerberg Acked-by: Rafael J. Wysocki Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 03187d0..ae0ffdc 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -253,6 +253,8 @@ static int acpi_find_gpio(struct acpi_resource *ares, void *data) agpio->pin_table[0]); lookup->info.gpioint = agpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT; + lookup->info.active_low = + agpio->polarity == ACPI_ACTIVE_LOW; } return 1; diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index bee93c8..9f3326b 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2264,7 +2264,17 @@ static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id, unsigned int idx, unsigned long *flags) { - return acpi_get_gpiod_by_index(dev, idx, NULL); + struct acpi_gpio_info info; + struct gpio_desc *desc; + + desc = acpi_get_gpiod_by_index(dev, idx, &info); + if (IS_ERR(desc)) + return desc; + + if (info.gpioint && info.active_low) + *flags |= GPIOF_ACTIVE_LOW; + + return desc; } static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, diff --git a/include/linux/acpi_gpio.h b/include/linux/acpi_gpio.h index b6ce601..d875bc3 100644 --- a/include/linux/acpi_gpio.h +++ b/include/linux/acpi_gpio.h @@ -10,9 +10,11 @@ /** * struct acpi_gpio_info - ACPI GPIO specific information * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo + * @active_low: in case of @gpioint, the pin is active low */ struct acpi_gpio_info { bool gpioint; + bool active_low; }; #ifdef CONFIG_GPIO_ACPI -- cgit v0.10.2 From 45f394391f93596782c6a5ec14f0a5428f60f9b3 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 10 Oct 2013 11:01:11 +0300 Subject: gpiolib / ACPI: document the GPIO descriptor based interface In addition to the existing ACPI specific GPIO interface, document the new descriptor based GPIO interface in Documentation/acpi/enumeration.txt, so it is clear that this new interface is preferred over the ACPI specific version. Signed-off-by: Mika Westerberg Acked-by: Rafael J. Wysocki Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij diff --git a/Documentation/acpi/enumeration.txt b/Documentation/acpi/enumeration.txt index aca4e69..b994bcb 100644 --- a/Documentation/acpi/enumeration.txt +++ b/Documentation/acpi/enumeration.txt @@ -295,10 +295,6 @@ These GPIO numbers are controller relative and path "\\_SB.PCI0.GPI0" specifies the path to the controller. In order to use these GPIOs in Linux we need to translate them to the Linux GPIO numbers. -The driver can do this by including and then calling -acpi_get_gpio(path, gpio). This will return the Linux GPIO number or -negative errno if there was no translation found. - In a simple case of just getting the Linux GPIO number from device resources one can use acpi_get_gpio_by_index() helper function. It takes pointer to the device and index of the GpioIo/GpioInt descriptor in the @@ -322,3 +318,25 @@ suitable to the gpiolib before passing them. In case of GpioInt resource an additional call to gpio_to_irq() must be done before calling request_irq(). + +Note that the above API is ACPI specific and not recommended for drivers +that need to support non-ACPI systems. The recommended way is to use +the descriptor based GPIO interfaces. The above example looks like this +when converted to the GPIO desc: + + #include + ... + + struct gpio_desc *irq_desc, *power_desc; + + irq_desc = gpiod_get_index(dev, NULL, 1); + if (IS_ERR(irq_desc)) + /* handle error */ + + power_desc = gpiod_get_index(dev, NULL, 0); + if (IS_ERR(power_desc)) + /* handle error */ + + /* Now we can use the GPIO descriptors */ + +See also Documentation/gpio.txt. -- cgit v0.10.2 From ecfc0c04f236f1e2a95094792ec10cf27be39f7c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 17 Oct 2013 21:13:19 +0100 Subject: ASoC: dai: Provide interface for setting DMA data at probe time Allow DMA data to be set at probe time for devices that can do that, avoiding the need to do it every time we start a stream and supporting non-DT dmaengine users using the helpers. Signed-off-by: Mark Brown diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index ae9a227..97943fd 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -276,6 +276,13 @@ static inline void snd_soc_dai_set_dma_data(struct snd_soc_dai *dai, dai->capture_dma_data = data; } +static inline void snd_soc_dai_init_dma_data(struct snd_soc_dai *dai, + void *playback, void *capture) +{ + dai->playback_dma_data = playback; + dai->capture_dma_data = capture; +} + static inline void snd_soc_dai_set_drvdata(struct snd_soc_dai *dai, void *data) { -- cgit v0.10.2 From 511e30331745e0c3452b89354a4b94c0e60f15a4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 17 Oct 2013 21:18:40 +0100 Subject: ASoC: samsung: Initialise DMA data at device probe time This is a minor simplification and will help with converting the platform to use the dmaengine helpers. Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index b302f3b..3e08b6c 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -702,13 +702,6 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, } writel(mod, i2s->addr + I2SMOD); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - snd_soc_dai_set_dma_data(dai, substream, - (void *)&i2s->dma_playback); - else - snd_soc_dai_set_dma_data(dai, substream, - (void *)&i2s->dma_capture); - i2s->frmclk = params_rate(params); return 0; @@ -970,6 +963,8 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) } clk_prepare_enable(i2s->clk); + snd_soc_dai_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); + if (other) { other->addr = i2s->addr; other->clk = i2s->clk; -- cgit v0.10.2 From e244bb9bc1883547d44642c99f483c2e57e2a940 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 17 Oct 2013 22:46:49 -0700 Subject: ASoC: simple-card: un-implemented set_fmt is not error Current simple-card returns error if DAI doesn't support .set_fmt callback. But the error is -ENOTSUPP (= not supported), and it is not error. This patch avoids such case Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 8c49147..b2fbb70 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -27,6 +27,11 @@ static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai, if (!ret && daifmt) ret = snd_soc_dai_set_fmt(dai, daifmt); + if (ret == -ENOTSUPP) { + dev_dbg(dai->dev, "ASoC: set_fmt is not supported\n"); + ret = 0; + } + if (!ret && set->sysclk) ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0); -- cgit v0.10.2 From cdcfcac968a1ec648434892b6addd80e66a5a892 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 17 Oct 2013 22:50:59 -0700 Subject: ASoC: rcar: add rsnd_scu_hpbif_is_enable() Current SSI needs RSND_SSI_DEPENDENT flag to decide dependent/independent mode. And SCU needs RSND_SCU_USE_HPBIF flag to decide HPBIF is enable/disable. But these 2 means same things. This patch adds new rsnd_scu_hpbif_is_enable() function, and merges above methods. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index fe66533..6b4211c 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -36,7 +36,6 @@ #define RSND_SSI_CLK_PIN_SHARE (1 << 31) #define RSND_SSI_CLK_FROM_ADG (1 << 30) /* clock parent is master */ #define RSND_SSI_SYNC (1 << 29) /* SSI34_sync etc */ -#define RSND_SSI_DEPENDENT (1 << 28) /* SSI needs SRU/SCU */ #define RSND_SSI_PLAY (1 << 24) diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 3868aaf..5feb67c 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -281,6 +281,7 @@ int rsnd_scu_probe(struct platform_device *pdev, void rsnd_scu_remove(struct platform_device *pdev, struct rsnd_priv *priv); struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id); +bool rsnd_scu_hpbif_is_enable(struct rsnd_mod *mod); #define rsnd_scu_nr(priv) ((priv)->scu_nr) /* diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c index 2df2e91..1ab1bce 100644 --- a/sound/soc/sh/rcar/scu.c +++ b/sound/soc/sh/rcar/scu.c @@ -146,20 +146,26 @@ static int rsnd_scu_set_hpbif(struct rsnd_priv *priv, return 0; } +bool rsnd_scu_hpbif_is_enable(struct rsnd_mod *mod) +{ + struct rsnd_scu *scu = rsnd_mod_to_scu(mod); + u32 flags = rsnd_scu_mode_flags(scu); + + return !!(flags & RSND_SCU_USE_HPBIF); +} + static int rsnd_scu_start(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_scu *scu = rsnd_mod_to_scu(mod); struct device *dev = rsnd_priv_to_dev(priv); - u32 flags = rsnd_scu_mode_flags(scu); int ret; /* * SCU will be used if it has RSND_SCU_USE_HPBIF flags */ - if (!(flags & RSND_SCU_USE_HPBIF)) { + if (!rsnd_scu_hpbif_is_enable(mod)) { /* it use PIO transter */ dev_dbg(dev, "%s%d is not used\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index fae26d3..7613256 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -106,6 +106,7 @@ static void rsnd_ssi_mode_init(struct rsnd_priv *priv, { struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_ssi *ssi; + struct rsnd_mod *scu; u32 flags; u32 val; int i; @@ -116,13 +117,14 @@ static void rsnd_ssi_mode_init(struct rsnd_priv *priv, ssiu->ssi_mode0 = 0; for_each_rsnd_ssi(ssi, priv, i) { flags = rsnd_ssi_mode_flags(ssi); + scu = rsnd_scu_mod_get(priv, rsnd_mod_id(&ssi->mod)); /* see also BUSIF_MODE */ - if (!(flags & RSND_SSI_DEPENDENT)) { + if (rsnd_scu_hpbif_is_enable(scu)) { + dev_dbg(dev, "SSI%d uses DEPENDENT mode\n", i); + } else { ssiu->ssi_mode0 |= (1 << i); dev_dbg(dev, "SSI%d uses INDEPENDENT mode\n", i); - } else { - dev_dbg(dev, "SSI%d uses DEPENDENT mode\n", i); } } -- cgit v0.10.2 From 92eba04e4bcd469518cc759ac1bf1a49acaa5cc1 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 17 Oct 2013 22:51:40 -0700 Subject: ASoC: rcar: remove RSND_SSI_CLK_FROM_ADG R-Car sound has clock pin for each SSI, and sometimes, these pins are shared with paired SSI. It may sometimes become "SSI-A clock pin is master" and "SSI-B clock pin is slave", but "SSI-A/B clock pins are shared". SSI-B needs SSI-A clock in this case. Current R-Car sound driver is using RSND_SSI_xxx flag to control this kind of shared pin behavior. But, this information, especially clock master setting, can be got from ASoC set_fmt settings. This patch removes rsnd_ssi_mode_init() and extend rsnd_ssi_mode_set() to controlling pin settings via .set_fmt. This patch doesn't removes RSND_SSI_CLK_FROM_ADG flag at this point to avoid conflict branch merging between ASoC <-> SH-ARM. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 7613256..b71cf9d 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -101,31 +101,30 @@ struct rsnd_ssiu { #define rsnd_ssi_to_ssiu(ssi)\ (((struct rsnd_ssiu *)((ssi) - rsnd_mod_id(&(ssi)->mod))) - 1) -static void rsnd_ssi_mode_init(struct rsnd_priv *priv, - struct rsnd_ssiu *ssiu) +static void rsnd_ssi_mode_set(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_ssi *ssi) { struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_ssi *ssi; struct rsnd_mod *scu; + struct rsnd_ssiu *ssiu = rsnd_ssi_to_ssiu(ssi); + int id = rsnd_mod_id(&ssi->mod); u32 flags; u32 val; - int i; + + scu = rsnd_scu_mod_get(priv, rsnd_mod_id(&ssi->mod)); /* * SSI_MODE0 */ - ssiu->ssi_mode0 = 0; - for_each_rsnd_ssi(ssi, priv, i) { - flags = rsnd_ssi_mode_flags(ssi); - scu = rsnd_scu_mod_get(priv, rsnd_mod_id(&ssi->mod)); - - /* see also BUSIF_MODE */ - if (rsnd_scu_hpbif_is_enable(scu)) { - dev_dbg(dev, "SSI%d uses DEPENDENT mode\n", i); - } else { - ssiu->ssi_mode0 |= (1 << i); - dev_dbg(dev, "SSI%d uses INDEPENDENT mode\n", i); - } + + /* see also BUSIF_MODE */ + if (rsnd_scu_hpbif_is_enable(scu)) { + ssiu->ssi_mode0 &= ~(1 << id); + dev_dbg(dev, "SSI%d uses DEPENDENT mode\n", id); + } else { + ssiu->ssi_mode0 |= (1 << id); + dev_dbg(dev, "SSI%d uses INDEPENDENT mode\n", id); } /* @@ -134,7 +133,7 @@ static void rsnd_ssi_mode_init(struct rsnd_priv *priv, #define ssi_parent_set(p, sync, adg, ext) \ do { \ ssi->parent = ssiu->ssi + p; \ - if (flags & RSND_SSI_CLK_FROM_ADG) \ + if (rsnd_rdai_is_clk_master(rdai)) \ val = adg; \ else \ val = ext; \ @@ -142,15 +141,11 @@ static void rsnd_ssi_mode_init(struct rsnd_priv *priv, val |= sync; \ } while (0) - ssiu->ssi_mode1 = 0; - for_each_rsnd_ssi(ssi, priv, i) { - flags = rsnd_ssi_mode_flags(ssi); - - if (!(flags & RSND_SSI_CLK_PIN_SHARE)) - continue; + flags = rsnd_ssi_mode_flags(ssi); + if (flags & RSND_SSI_CLK_PIN_SHARE) { val = 0; - switch (i) { + switch (id) { case 1: ssi_parent_set(0, (1 << 4), (0x2 << 0), (0x1 << 0)); break; @@ -167,11 +162,6 @@ static void rsnd_ssi_mode_init(struct rsnd_priv *priv, ssiu->ssi_mode1 |= val; } -} - -static void rsnd_ssi_mode_set(struct rsnd_ssi *ssi) -{ - struct rsnd_ssiu *ssiu = rsnd_ssi_to_ssiu(ssi); rsnd_mod_write(&ssi->mod, SSI_MODE0, ssiu->ssi_mode0); rsnd_mod_write(&ssi->mod, SSI_MODE1, ssiu->ssi_mode1); @@ -381,7 +371,7 @@ static int rsnd_ssi_init(struct rsnd_mod *mod, ssi->cr_own = cr; ssi->err = -1; /* ignore 1st error */ - rsnd_ssi_mode_set(ssi); + rsnd_ssi_mode_set(priv, rdai, ssi); dev_dbg(dev, "%s.%d init\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); @@ -708,8 +698,6 @@ int rsnd_ssi_probe(struct platform_device *pdev, rsnd_mod_init(priv, &ssi->mod, ops, i); } - rsnd_ssi_mode_init(priv, ssiu); - dev_dbg(dev, "ssi probed\n"); return 0; -- cgit v0.10.2 From 1f1b65796ef882bb9101d22b17e1a1824b3a6489 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Fri, 18 Oct 2013 20:34:52 +0200 Subject: ASoC: kirkwood: prefer external clock over internal clock When there is an external clock, always use this one. This prevents the two Dove audio devices to use the same DCO clock at different rates. Signed-off-by: Jean-Francois Moine Signed-off-by: Mark Brown diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c index 3e59af9..d0504a2 100644 --- a/sound/soc/kirkwood/kirkwood-i2s.c +++ b/sound/soc/kirkwood/kirkwood-i2s.c @@ -103,7 +103,7 @@ static void kirkwood_set_rate(struct snd_soc_dai *dai, { uint32_t clks_ctrl; - if (rate == 44100 || rate == 48000 || rate == 96000) { + if (IS_ERR(priv->extclk)) { /* use internal dco for the supported rates * defined in kirkwood_i2s_dai */ dev_dbg(dai->dev, "%s: dco set rate = %lu\n", -- cgit v0.10.2 From 7b09eea52939d2b979f19de40e34b8670feff4c5 Mon Sep 17 00:00:00 2001 From: Brian Austin Date: Fri, 18 Oct 2013 14:30:01 -0500 Subject: ASoC: cs42l73: Add Device Tree support for CS42L73 This patch adds support for device tree for the CS42L73 CODEC Signed-off-by: Brian Austin Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/cs42l73.txt b/Documentation/devicetree/bindings/sound/cs42l73.txt new file mode 100644 index 0000000..80ae910 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/cs42l73.txt @@ -0,0 +1,22 @@ +CS42L73 audio CODEC + +Required properties: + + - compatible : "cirrus,cs42l73" + + - reg : the I2C address of the device for I2C + +Optional properties: + + - reset_gpio : a GPIO spec for the reset pin. + - chgfreq : Charge Pump Frequency values 0x00-0x0F + + +Example: + +codec: cs42l73@4a { + compatible = "cirrus,cs42l73"; + reg = <0x4a>; + reset_gpio = <&gpio 10 0>; + chgfreq = <0x05>; +}; \ No newline at end of file diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c index 89efc3c..549d5d6 100644 --- a/sound/soc/codecs/cs42l73.c +++ b/sound/soc/codecs/cs42l73.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include @@ -1416,6 +1416,7 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client, int ret; unsigned int devid = 0; unsigned int reg; + u32 val32; cs42l73 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42l73_private), GFP_KERNEL); @@ -1431,8 +1432,25 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client, return ret; } - if (pdata) + if (pdata) { cs42l73->pdata = *pdata; + } else { + pdata = devm_kzalloc(&i2c_client->dev, + sizeof(struct cs42l73_platform_data), + GFP_KERNEL); + if (!pdata) { + dev_err(&i2c_client->dev, "could not allocate pdata\n"); + return -ENOMEM; + } + if (i2c_client->dev.of_node) { + if (of_property_read_u32(i2c_client->dev.of_node, + "chgfreq", &val32) >= 0) + pdata->chgfreq = val32; + } + pdata->reset_gpio = of_get_named_gpio(i2c_client->dev.of_node, + "reset-gpio", 0); + cs42l73->pdata = *pdata; + } i2c_set_clientdata(i2c_client, cs42l73); @@ -1493,6 +1511,12 @@ static int cs42l73_i2c_remove(struct i2c_client *client) return 0; } +static const struct of_device_id cs42l73_of_match[] = { + { .compatible = "cirrus,cs42l73", }, + {}, +}; +MODULE_DEVICE_TABLE(of, cs42l73_of_match); + static const struct i2c_device_id cs42l73_id[] = { {"cs42l73", 0}, {} @@ -1504,6 +1528,7 @@ static struct i2c_driver cs42l73_i2c_driver = { .driver = { .name = "cs42l73", .owner = THIS_MODULE, + .of_match_table = cs42l73_of_match, }, .id_table = cs42l73_id, .probe = cs42l73_i2c_probe, -- cgit v0.10.2 From 6abce9e63d44e94b16f08794fb6f5ad6d1025c79 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 4 Sep 2011 22:11:14 +0200 Subject: ALSA: dice: optimize bus reset handling After a bus reset, do not stop the stream completely to avoid having to reconfigure the device when restarting the stream. Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index ac71b2b..b081021 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -559,24 +559,92 @@ static int dice_close(struct snd_pcm_substream *substream) return 0; } -static void dice_stop_stream(struct dice *dice) +static int dice_stream_start_packets(struct dice *dice) { - __be32 channel; + int err; + + if (dice->stream_running) + return 0; - if (dice->stream_running) { - dice_enable_clear(dice); + err = amdtp_out_stream_start(&dice->stream, dice->resources.channel, + fw_parent_device(dice->unit)->max_speed); + if (err < 0) + return err; + err = dice_enable_set(dice); + if (err < 0) { amdtp_out_stream_stop(&dice->stream); + return err; + } - channel = cpu_to_be32((u32)-1); - snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, - rx_address(dice, RX_ISOCHRONOUS), - &channel, 4); + dice->stream_running = true; - fw_iso_resources_free(&dice->resources); + return 0; +} - dice->stream_running = false; +static int dice_stream_start(struct dice *dice) +{ + __be32 channel; + int err; + + if (!dice->resources.allocated) { + err = fw_iso_resources_allocate(&dice->resources, + amdtp_out_stream_get_max_payload(&dice->stream), + fw_parent_device(dice->unit)->max_speed); + if (err < 0) + goto error; + + channel = cpu_to_be32(dice->resources.channel); + err = snd_fw_transaction(dice->unit, + TCODE_WRITE_QUADLET_REQUEST, + rx_address(dice, RX_ISOCHRONOUS), + &channel, 4); + if (err < 0) + goto err_resources; } + + err = dice_stream_start_packets(dice); + if (err < 0) + goto err_rx_channel; + + return 0; + +err_rx_channel: + channel = cpu_to_be32((u32)-1); + snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, + rx_address(dice, RX_ISOCHRONOUS), &channel, 4); +err_resources: + fw_iso_resources_free(&dice->resources); +error: + return err; +} + +static void dice_stream_stop_packets(struct dice *dice) +{ + if (!dice->stream_running) + return; + + dice_enable_clear(dice); + + amdtp_out_stream_stop(&dice->stream); + + dice->stream_running = false; +} + +static void dice_stream_stop(struct dice *dice) +{ + __be32 channel; + + dice_stream_stop_packets(dice); + + if (!dice->resources.allocated) + return; + + channel = cpu_to_be32((u32)-1); + snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, + rx_address(dice, RX_ISOCHRONOUS), &channel, 4); + + fw_iso_resources_free(&dice->resources); } static int dice_hw_params(struct snd_pcm_substream *substream, @@ -586,7 +654,7 @@ static int dice_hw_params(struct snd_pcm_substream *substream, int err; mutex_lock(&dice->mutex); - dice_stop_stream(dice); + dice_stream_stop(dice); mutex_unlock(&dice->mutex); err = snd_pcm_lib_alloc_vmalloc_buffer(substream, @@ -608,7 +676,7 @@ static int dice_hw_free(struct snd_pcm_substream *substream) struct dice *dice = substream->private_data; mutex_lock(&dice->mutex); - dice_stop_stream(dice); + dice_stream_stop(dice); mutex_unlock(&dice->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); @@ -617,42 +685,17 @@ static int dice_hw_free(struct snd_pcm_substream *substream) static int dice_prepare(struct snd_pcm_substream *substream) { struct dice *dice = substream->private_data; - struct fw_device *device = fw_parent_device(dice->unit); - __be32 channel; int err; mutex_lock(&dice->mutex); if (amdtp_out_streaming_error(&dice->stream)) - dice_stop_stream(dice); - - if (!dice->stream_running) { - err = fw_iso_resources_allocate(&dice->resources, - amdtp_out_stream_get_max_payload(&dice->stream), - device->max_speed); - if (err < 0) - goto error; - - //TODO: RX_SEQ_START - channel = cpu_to_be32(dice->resources.channel); - err = snd_fw_transaction(dice->unit, - TCODE_WRITE_QUADLET_REQUEST, - rx_address(dice, RX_ISOCHRONOUS), - &channel, 4); - if (err < 0) - goto err_resources; - - err = amdtp_out_stream_start(&dice->stream, - dice->resources.channel, - device->max_speed); - if (err < 0) - goto err_resources; - - err = dice_enable_set(dice); - if (err < 0) - goto err_stream; + dice_stream_stop_packets(dice); - dice->stream_running = true; + err = dice_stream_start(dice); + if (err < 0) { + mutex_unlock(&dice->mutex); + return err; } mutex_unlock(&dice->mutex); @@ -660,15 +703,6 @@ static int dice_prepare(struct snd_pcm_substream *substream) amdtp_out_stream_pcm_prepare(&dice->stream); return 0; - -err_stream: - amdtp_out_stream_stop(&dice->stream); -err_resources: - fw_iso_resources_free(&dice->resources); -error: - mutex_unlock(&dice->mutex); - - return err; } static int dice_trigger(struct snd_pcm_substream *substream, int cmd) @@ -941,7 +975,7 @@ static void dice_remove(struct fw_unit *unit) mutex_lock(&dice->mutex); amdtp_out_stream_pcm_abort(&dice->stream); - dice_stop_stream(dice); + dice_stream_stop(dice); dice_owner_clear(dice); mutex_unlock(&dice->mutex); @@ -953,8 +987,8 @@ static void dice_bus_reset(struct fw_unit *unit) struct dice *dice = dev_get_drvdata(&unit->device); mutex_lock(&dice->mutex); + /* - * XXX is the following true? * On a bus reset, the DICE firmware disables streaming and then goes * off contemplating its own navel for hundreds of milliseconds before * it can react to any of our attempts to reenable streaming. This @@ -962,9 +996,13 @@ static void dice_bus_reset(struct fw_unit *unit) * to stop so that the application can restart them in an orderly * manner. */ - dice_owner_update(dice); amdtp_out_stream_pcm_abort(&dice->stream); - dice_stop_stream(dice); + dice_stream_stop_packets(dice); + + dice_owner_update(dice); + + fw_iso_resources_update(&dice->resources); + mutex_unlock(&dice->mutex); } -- cgit v0.10.2 From 341682cd4f5603fccbf559d201402539880c04a5 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 4 Sep 2011 22:12:06 +0200 Subject: ALSA: dice: allow all sample rates Instead of forcing a constant 44.1 kHz, read the current sample rate from the device when opening the PCM device. Actually changing the sample rate requires some separate controller application. Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index b081021..d3f3eb7 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -75,6 +75,7 @@ #define CLOCK_RATE_ANY_MID 0x00000800 #define CLOCK_RATE_ANY_HIGH 0x00000900 #define CLOCK_RATE_NONE 0x00000a00 +#define CLOCK_RATE_SHIFT 8 #define GLOBAL_ENABLE 0x050 #define ENABLE 0x00000001 #define GLOBAL_STATUS 0x054 @@ -248,6 +249,16 @@ MODULE_DESCRIPTION("DICE driver"); MODULE_AUTHOR("Clemens Ladisch "); MODULE_LICENSE("GPL v2"); +static const unsigned int dice_rates[] = { + [0] = 32000, + [1] = 44100, + [2] = 48000, + [3] = 88200, + [4] = 96000, + [5] = 176400, + [6] = 192000, +}; + static inline u64 global_address(struct dice *dice, unsigned int offset) { return DICE_PRIVATE_SPACE + dice->global_offset + offset; @@ -508,9 +519,6 @@ static int dice_open(struct snd_pcm_substream *substream) SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER, .formats = AMDTP_OUT_PCM_FORMAT_BITS, - .rates = SNDRV_PCM_RATE_44100, - .rate_min = 44100, - .rate_max = 44100, .buffer_bytes_max = 16 * 1024 * 1024, .period_bytes_min = 1, .period_bytes_max = UINT_MAX, @@ -519,10 +527,21 @@ static int dice_open(struct snd_pcm_substream *substream) }; struct dice *dice = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; - __be32 number_audio, number_midi; + __be32 clock_sel, number_audio, number_midi; + unsigned int rate; int err; err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, + global_address(dice, GLOBAL_CLOCK_SELECT), + &clock_sel, 4); + if (err < 0) + return err; + rate = (be32_to_cpu(clock_sel) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT; + if (rate >= ARRAY_SIZE(dice_rates)) + return -ENXIO; + rate = dice_rates[rate]; + + err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, rx_address(dice, RX_NUMBER_AUDIO), &number_audio, 4); if (err < 0) @@ -534,10 +553,14 @@ static int dice_open(struct snd_pcm_substream *substream) return err; runtime->hw = hardware; + + runtime->hw.rates = snd_pcm_rate_to_rate_bit(rate); + snd_pcm_limit_hw_rates(runtime); + runtime->hw.channels_min = be32_to_cpu(number_audio); runtime->hw.channels_max = be32_to_cpu(number_audio); - amdtp_out_stream_set_rate(&dice->stream, 44100); + amdtp_out_stream_set_rate(&dice->stream, rate); amdtp_out_stream_set_pcm(&dice->stream, be32_to_cpu(number_audio)); amdtp_out_stream_set_midi(&dice->stream, be32_to_cpu(number_midi)); @@ -746,17 +769,9 @@ static int dice_create_pcm(struct dice *dice) .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; - __be32 clock; struct snd_pcm *pcm; int err; - clock = cpu_to_be32(CLOCK_SOURCE_ARX1 | CLOCK_RATE_44100); - err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, - global_address(dice, GLOBAL_CLOCK_SELECT), - &clock, 4); - if (err < 0) - return err; - err = snd_pcm_new(dice->card, "DICE", 0, 1, 0, &pcm); if (err < 0) return err; @@ -897,6 +912,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) { struct snd_card *card; struct dice *dice; + __be32 clock_sel; int err; err = snd_card_create(-1, NULL, THIS_MODULE, sizeof(*dice), &card); @@ -938,6 +954,19 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) dice_card_strings(dice); + err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, + global_address(dice, GLOBAL_CLOCK_SELECT), + &clock_sel, 4); + if (err < 0) + goto error; + clock_sel &= cpu_to_be32(~CLOCK_SOURCE_MASK); + clock_sel |= cpu_to_be32(CLOCK_SOURCE_ARX1); + err = snd_fw_transaction(unit, TCODE_WRITE_QUADLET_REQUEST, + global_address(dice, GLOBAL_CLOCK_SELECT), + &clock_sel, 4); + if (err < 0) + goto error; + err = dice_create_pcm(dice); if (err < 0) goto error; -- cgit v0.10.2 From d13109673ac49cd4b992df17238ee030be7ed7f0 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 4 Sep 2011 22:12:20 +0200 Subject: ALSA: dice: reduce noisy logging The notification bits are not of general interest; log them only when debugging. Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index d3f3eb7..02c7b5a 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -505,8 +505,8 @@ static void dice_notification(struct fw_card *card, struct fw_request *request, fw_send_response(card, request, RCODE_ADDRESS_ERROR); return; } - dev_info(&dice->unit->device, - "notification: %08x\n", be32_to_cpup(data)); + dev_dbg(&dice->unit->device, + "notification: %08x\n", be32_to_cpup(data)); fw_send_response(card, request, RCODE_COMPLETE); } -- cgit v0.10.2 From e84d15f619c13e83b33023c84527ee35ef01b6b4 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 4 Sep 2011 22:12:48 +0200 Subject: ALSA: dice, firewire-lib: add blocking mode Allow AMDTP output streams to use blocking mode. Use it for DICE devices, because the old DICE-II chip will in some cases not be able to lock to non-blocking streams (erratum E7). Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index ea995af..efb2e29 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -42,9 +42,6 @@ static void pcm_period_tasklet(unsigned long data); int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit, enum cip_out_flags flags) { - if (flags != CIP_NONBLOCKING) - return -EINVAL; - s->unit = fw_unit_get(unit); s->flags = flags; s->context = ERR_PTR(-1); @@ -96,12 +93,20 @@ void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate) return; for (sfc = 0; sfc < ARRAY_SIZE(rate_info); ++sfc) - if (rate_info[sfc].rate == rate) { - s->sfc = sfc; - s->syt_interval = rate_info[sfc].syt_interval; - return; - } + if (rate_info[sfc].rate == rate) + goto sfc_found; WARN_ON(1); + return; + +sfc_found: + s->sfc = sfc; + s->syt_interval = rate_info[sfc].syt_interval; + + /* default buffering in the device */ + s->transfer_delay = TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE; + if (s->flags & CIP_BLOCKING) + /* additional buffering needed to adjust for no-data packets */ + s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate; } EXPORT_SYMBOL(amdtp_out_stream_set_rate); @@ -110,25 +115,15 @@ EXPORT_SYMBOL(amdtp_out_stream_set_rate); * @s: the AMDTP output stream * * This function must not be called before the stream has been configured - * with amdtp_out_stream_set_hw_params(), amdtp_out_stream_set_pcm(), and + * with amdtp_out_stream_set_rate(), amdtp_out_stream_set_pcm(), and * amdtp_out_stream_set_midi(). */ unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s) { - static const unsigned int max_data_blocks[] = { - [CIP_SFC_32000] = 4, - [CIP_SFC_44100] = 6, - [CIP_SFC_48000] = 6, - [CIP_SFC_88200] = 12, - [CIP_SFC_96000] = 12, - [CIP_SFC_176400] = 23, - [CIP_SFC_192000] = 24, - }; - s->data_block_quadlets = s->pcm_channels; s->data_block_quadlets += DIV_ROUND_UP(s->midi_ports, 8); - return 8 + max_data_blocks[s->sfc] * 4 * s->data_block_quadlets; + return 8 + s->syt_interval * s->data_block_quadlets * 4; } EXPORT_SYMBOL(amdtp_out_stream_get_max_payload); @@ -248,7 +243,7 @@ static unsigned int calculate_syt(struct amdtp_out_stream *s, s->last_syt_offset = syt_offset; if (syt_offset < TICKS_PER_CYCLE) { - syt_offset += TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE; + syt_offset += s->transfer_delay; syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12; syt += syt_offset % TICKS_PER_CYCLE; @@ -344,8 +339,17 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle) return; index = s->packet_index; - data_blocks = calculate_data_blocks(s); syt = calculate_syt(s, cycle); + if (!(s->flags & CIP_BLOCKING)) { + data_blocks = calculate_data_blocks(s); + } else { + if (syt != 0xffff) { + data_blocks = s->syt_interval; + } else { + data_blocks = 0; + syt = 0xffffff; + } + } buffer = s->buffer.packets[index].buffer; buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) | @@ -455,9 +459,9 @@ static int queue_initial_skip_packets(struct amdtp_out_stream *s) * @speed: firewire speed code * * The stream cannot be started until it has been configured with - * amdtp_out_stream_set_hw_params(), amdtp_out_stream_set_pcm(), and - * amdtp_out_stream_set_midi(); and it must be started before any - * PCM or MIDI device can be started. + * amdtp_out_stream_set_rate(), amdtp_out_stream_set_pcm(), + * amdtp_out_stream_set_midi(), and amdtp_out_stream_set_format(); + * and it must be started before any PCM or MIDI device can be started. */ int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed) { diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index f6103d6..fd4ce30 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -11,9 +11,13 @@ * sample_rate/8000 samples, with rounding up or down to adjust * for clock skew and left-over fractional samples. This should * be used if supported by the device. + * @CIP_BLOCKING: In blocking mode, each packet contains either zero or + * SYT_INTERVAL samples, with these two types alternating so that + * the overall sample rate comes out right. */ enum cip_out_flags { - CIP_NONBLOCKING = 0, + CIP_NONBLOCKING = 0x00, + CIP_BLOCKING = 0x01, }; /** @@ -51,6 +55,7 @@ struct amdtp_out_stream { __be32 *buffer, unsigned int frames); unsigned int syt_interval; + unsigned int transfer_delay; unsigned int source_node_id_field; struct iso_packets_buffer buffer; diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index 02c7b5a..63446f8 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -942,7 +942,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) goto err_notification_handler; dice->resources.channels_mask = 0x00000000ffffffffuLL; - err = amdtp_out_stream_init(&dice->stream, unit, CIP_NONBLOCKING); + err = amdtp_out_stream_init(&dice->stream, unit, CIP_BLOCKING); if (err < 0) goto err_resources; -- cgit v0.10.2 From 4ed31f20bb5bb90f003c91734c6b9d18169ae27e Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 4 Sep 2011 22:13:09 +0200 Subject: ALSA: dice: fix hang when unplugging a running device When aborting a PCM stream, the xrun is signaled only if the stream is running. When disconnecting a PCM stream, calling snd_card_disconnect() too early would change the stream into a non-running state and thus prevent the xrun from being noticed by user space. To prevent this, move the snd_card_disconnect() call after the xrun. Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index 63446f8..d0575a9 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -1000,12 +1000,15 @@ static void dice_remove(struct fw_unit *unit) { struct dice *dice = dev_get_drvdata(&unit->device); - snd_card_disconnect(dice->card); - mutex_lock(&dice->mutex); + amdtp_out_stream_pcm_abort(&dice->stream); + + snd_card_disconnect(dice->card); + dice_stream_stop(dice); dice_owner_clear(dice); + mutex_unlock(&dice->mutex); snd_card_free_when_closed(dice->card); -- cgit v0.10.2 From 0c29c9180fe14b0abb4bfc68b37dda66254689b3 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 4 Sep 2011 22:14:15 +0200 Subject: ALSA: dice: implement hwdep device Implement the hwdep locking and notification mechanisms. Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index d0575a9..7225878 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -5,6 +5,7 @@ * Licensed under the terms of the GNU General Public License, version 2. */ +#include #include #include #include @@ -13,8 +14,11 @@ #include #include #include +#include +#include #include #include +#include #include #include #include @@ -233,13 +237,18 @@ struct dice { struct snd_card *card; struct fw_unit *unit; + spinlock_t lock; struct mutex mutex; unsigned int global_offset; unsigned int rx_offset; struct fw_address_handler notification_handler; int owner_generation; + int dev_lock_count; /* > 0 driver, < 0 userspace */ + bool dev_lock_changed; bool global_enabled; bool stream_running; + wait_queue_head_t hwdep_wait; + u32 notification_bits; struct snd_pcm_substream *pcm; struct fw_iso_resources resources; struct amdtp_out_stream stream; @@ -259,6 +268,47 @@ static const unsigned int dice_rates[] = { [6] = 192000, }; +static void dice_lock_changed(struct dice *dice) +{ + dice->dev_lock_changed = true; + wake_up(&dice->hwdep_wait); +} + +static int dice_try_lock(struct dice *dice) +{ + int err; + + spin_lock_irq(&dice->lock); + + if (dice->dev_lock_count < 0) { + err = -EBUSY; + goto out; + } + + if (dice->dev_lock_count++ == 0) + dice_lock_changed(dice); + err = 0; + +out: + spin_unlock_irq(&dice->lock); + + return err; +} + +static void dice_unlock(struct dice *dice) +{ + spin_lock_irq(&dice->lock); + + if (WARN_ON(dice->dev_lock_count <= 0)) + goto out; + + if (--dice->dev_lock_count == 0) + dice_lock_changed(dice); + +out: + spin_unlock_irq(&dice->lock); +} + static inline u64 global_address(struct dice *dice, unsigned int offset) { return DICE_PRIVATE_SPACE + dice->global_offset + offset; @@ -496,6 +546,7 @@ static void dice_notification(struct fw_card *card, struct fw_request *request, void *data, size_t length, void *callback_data) { struct dice *dice = callback_data; + unsigned long flags; if (tcode != TCODE_WRITE_QUADLET_REQUEST) { fw_send_response(card, request, RCODE_TYPE_ERROR); @@ -505,9 +556,11 @@ static void dice_notification(struct fw_card *card, struct fw_request *request, fw_send_response(card, request, RCODE_ADDRESS_ERROR); return; } - dev_dbg(&dice->unit->device, - "notification: %08x\n", be32_to_cpup(data)); + spin_lock_irqsave(&dice->lock, flags); + dice->notification_bits |= be32_to_cpup(data); + spin_unlock_irqrestore(&dice->lock, flags); fw_send_response(card, request, RCODE_COMPLETE); + wake_up(&dice->hwdep_wait); } static int dice_open(struct snd_pcm_substream *substream) @@ -531,26 +584,32 @@ static int dice_open(struct snd_pcm_substream *substream) unsigned int rate; int err; + err = dice_try_lock(dice); + if (err < 0) + goto error; + err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, global_address(dice, GLOBAL_CLOCK_SELECT), &clock_sel, 4); if (err < 0) - return err; + goto err_lock; rate = (be32_to_cpu(clock_sel) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT; - if (rate >= ARRAY_SIZE(dice_rates)) - return -ENXIO; + if (rate >= ARRAY_SIZE(dice_rates)) { + err = -ENXIO; + goto err_lock; + } rate = dice_rates[rate]; err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, rx_address(dice, RX_NUMBER_AUDIO), &number_audio, 4); if (err < 0) - return err; + goto err_lock; err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, rx_address(dice, RX_NUMBER_MIDI), &number_midi, 4); if (err < 0) - return err; + goto err_lock; runtime->hw = hardware; @@ -568,17 +627,26 @@ static int dice_open(struct snd_pcm_substream *substream) SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5000, 8192000); if (err < 0) - return err; + goto err_lock; err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); if (err < 0) - return err; + goto err_lock; return 0; + +err_lock: + dice_unlock(dice); +error: + return err; } static int dice_close(struct snd_pcm_substream *substream) { + struct dice *dice = substream->private_data; + + dice_unlock(dice); + return 0; } @@ -783,45 +851,156 @@ static int dice_create_pcm(struct dice *dice) return 0; } -// TODO: implement these - static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, loff_t *offset) { - return -EIO; + struct dice *dice = hwdep->private_data; + DEFINE_WAIT(wait); + union snd_firewire_event event; + + spin_lock_irq(&dice->lock); + + while (!dice->dev_lock_changed && dice->notification_bits == 0) { + prepare_to_wait(&dice->hwdep_wait, &wait, TASK_INTERRUPTIBLE); + spin_unlock_irq(&dice->lock); + schedule(); + finish_wait(&dice->hwdep_wait, &wait); + if (signal_pending(current)) + return -ERESTARTSYS; + spin_lock_irq(&dice->lock); + } + + memset(&event, 0, sizeof(event)); + if (dice->dev_lock_changed) { + event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; + event.lock_status.status = dice->dev_lock_count > 0; + dice->dev_lock_changed = false; + + count = min(count, (long)sizeof(event.lock_status)); + } else { + event.dice_notification.type = SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION; + event.dice_notification.notification = dice->notification_bits; + dice->notification_bits = 0; + + count = min(count, (long)sizeof(event.dice_notification)); + } + + spin_unlock_irq(&dice->lock); + + if (copy_to_user(buf, &event, count)) + return -EFAULT; + + return count; } -static int dice_hwdep_open(struct snd_hwdep *hwdep, struct file *file) +static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file, + poll_table *wait) { - return -EIO; + struct dice *dice = hwdep->private_data; + unsigned int events; + + poll_wait(file, &dice->hwdep_wait, wait); + + spin_lock_irq(&dice->lock); + if (dice->dev_lock_changed || dice->notification_bits != 0) + events = POLLIN | POLLRDNORM; + else + events = 0; + spin_unlock_irq(&dice->lock); + + return events; } -static int dice_hwdep_release(struct snd_hwdep *hwdep, struct file *file) +static int dice_hwdep_get_info(struct dice *dice, void __user *arg) { + struct fw_device *dev = fw_parent_device(dice->unit); + struct snd_firewire_get_info info; + + memset(&info, 0, sizeof(info)); + info.type = SNDRV_FIREWIRE_TYPE_DICE; + info.card = dev->card->index; + *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]); + *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]); + strlcpy(info.device_name, dev_name(&dev->device), + sizeof(info.device_name)); + + if (copy_to_user(arg, &info, sizeof(info))) + return -EFAULT; + return 0; } -static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file, - poll_table *wait) +static int dice_hwdep_lock(struct dice *dice) +{ + int err; + + spin_lock_irq(&dice->lock); + + if (dice->dev_lock_count == 0) { + dice->dev_lock_count = -1; + err = 0; + } else { + err = -EBUSY; + } + + spin_unlock_irq(&dice->lock); + + return err; +} + +static int dice_hwdep_unlock(struct dice *dice) { - return POLLERR | POLLHUP; + int err; + + spin_lock_irq(&dice->lock); + + if (dice->dev_lock_count == -1) { + dice->dev_lock_count = 0; + err = 0; + } else { + err = -EBADFD; + } + + spin_unlock_irq(&dice->lock); + + return err; } static int dice_hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, unsigned int cmd, unsigned long arg) { - return -EIO; + struct dice *dice = hwdep->private_data; + + switch (cmd) { + case SNDRV_FIREWIRE_IOCTL_GET_INFO: + return dice_hwdep_get_info(dice, (void __user *)arg); + case SNDRV_FIREWIRE_IOCTL_LOCK: + return dice_hwdep_lock(dice); + case SNDRV_FIREWIRE_IOCTL_UNLOCK: + return dice_hwdep_unlock(dice); + default: + return -ENOIOCTLCMD; + } +} + +#ifdef CONFIG_COMPAT +static int dice_hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return dice_hwdep_ioctl(hwdep, file, cmd, + (unsigned long)compat_ptr(arg)); } +#else +#define dice_hwdep_compat_ioctl NULL +#endif static int dice_create_hwdep(struct dice *dice) { static const struct snd_hwdep_ops ops = { .read = dice_hwdep_read, - .open = dice_hwdep_open, - .release = dice_hwdep_release, .poll = dice_hwdep_poll, .ioctl = dice_hwdep_ioctl, - .ioctl_compat = dice_hwdep_ioctl, + .ioctl_compat = dice_hwdep_compat_ioctl, }; struct snd_hwdep *hwdep; int err; @@ -922,8 +1101,10 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) dice = card->private_data; dice->card = card; + spin_lock_init(&dice->lock); mutex_init(&dice->mutex); dice->unit = unit; + init_waitqueue_head(&dice->hwdep_wait); err = dice_init_offsets(dice); if (err < 0) -- cgit v0.10.2 From 9dd81e3143b57d1bf7e8377ab29b86090baa55a8 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 4 Sep 2011 22:14:54 +0200 Subject: ALSA: dice: clear device lock when closing hwdep device Ensure that misbehaving or aborted userspace programs do not accidentally keep the lock. Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index 7225878..ef04089 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -966,6 +966,18 @@ static int dice_hwdep_unlock(struct dice *dice) return err; } +static int dice_hwdep_release(struct snd_hwdep *hwdep, struct file *file) +{ + struct dice *dice = hwdep->private_data; + + spin_lock_irq(&dice->lock); + if (dice->dev_lock_count == -1) + dice->dev_lock_count = 0; + spin_unlock_irq(&dice->lock); + + return 0; +} + static int dice_hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, unsigned int cmd, unsigned long arg) { @@ -998,6 +1010,7 @@ static int dice_create_hwdep(struct dice *dice) { static const struct snd_hwdep_ops ops = { .read = dice_hwdep_read, + .release = dice_hwdep_release, .poll = dice_hwdep_poll, .ioctl = dice_hwdep_ioctl, .ioctl_compat = dice_hwdep_compat_ioctl, -- cgit v0.10.2 From 20b65dd040ce38e2bc0fa3cae13b954c865b61fe Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 4 Sep 2011 22:15:44 +0200 Subject: ALSA: firewire: introduce amdtp_out_stream_running() Introduce the helper function amdtp_out_stream_running(). This makes many checks in amdtp.c clearer and frees the device drivers from having to track this with a separate variable. Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index efb2e29..d56b8e7 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -59,7 +59,7 @@ EXPORT_SYMBOL(amdtp_out_stream_init); */ void amdtp_out_stream_destroy(struct amdtp_out_stream *s) { - WARN_ON(!IS_ERR(s->context)); + WARN_ON(amdtp_out_stream_running(s)); mutex_destroy(&s->mutex); fw_unit_put(s->unit); } @@ -89,7 +89,7 @@ void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate) }; unsigned int sfc; - if (WARN_ON(!IS_ERR(s->context))) + if (WARN_ON(amdtp_out_stream_running(s))) return; for (sfc = 0; sfc < ARRAY_SIZE(rate_info); ++sfc) @@ -145,7 +145,7 @@ static void amdtp_write_s32(struct amdtp_out_stream *s, void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, snd_pcm_format_t format) { - if (WARN_ON(!IS_ERR(s->context))) + if (WARN_ON(amdtp_out_stream_running(s))) return; switch (format) { @@ -481,7 +481,7 @@ int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed) mutex_lock(&s->mutex); - if (WARN_ON(!IS_ERR(s->context) || + if (WARN_ON(amdtp_out_stream_running(s) || (!s->pcm_channels && !s->midi_ports))) { err = -EBADFD; goto err_unlock; @@ -577,7 +577,7 @@ void amdtp_out_stream_stop(struct amdtp_out_stream *s) { mutex_lock(&s->mutex); - if (IS_ERR(s->context)) { + if (!amdtp_out_stream_running(s)) { mutex_unlock(&s->mutex); return; } diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index fd4ce30..28b1bf5 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -1,6 +1,7 @@ #ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED #define SOUND_FIREWIRE_AMDTP_H_INCLUDED +#include #include #include #include "packets-buffer.h" @@ -92,6 +93,11 @@ void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s); unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s); void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s); +static inline bool amdtp_out_stream_running(struct amdtp_out_stream *s) +{ + return !IS_ERR(s->context); +} + /** * amdtp_out_stream_set_pcm - configure format of PCM samples * @s: the AMDTP output stream to be configured diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index ef04089..3591aeb 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -246,7 +246,6 @@ struct dice { int dev_lock_count; /* > 0 driver, < 0 userspace */ bool dev_lock_changed; bool global_enabled; - bool stream_running; wait_queue_head_t hwdep_wait; u32 notification_bits; struct snd_pcm_substream *pcm; @@ -654,7 +653,7 @@ static int dice_stream_start_packets(struct dice *dice) { int err; - if (dice->stream_running) + if (amdtp_out_stream_running(&dice->stream)) return 0; err = amdtp_out_stream_start(&dice->stream, dice->resources.channel, @@ -668,8 +667,6 @@ static int dice_stream_start_packets(struct dice *dice) return err; } - dice->stream_running = true; - return 0; } @@ -712,14 +709,10 @@ error: static void dice_stream_stop_packets(struct dice *dice) { - if (!dice->stream_running) - return; - - dice_enable_clear(dice); - - amdtp_out_stream_stop(&dice->stream); - - dice->stream_running = false; + if (amdtp_out_stream_running(&dice->stream)) { + dice_enable_clear(dice); + amdtp_out_stream_stop(&dice->stream); + } } static void dice_stream_stop(struct dice *dice) diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c index 2c63865..0ac5630 100644 --- a/sound/firewire/speakers.c +++ b/sound/firewire/speakers.c @@ -53,7 +53,6 @@ struct fwspk { struct mutex mutex; struct cmp_connection connection; struct amdtp_out_stream stream; - bool stream_running; bool mute; s16 volume[6]; s16 volume_min; @@ -189,10 +188,9 @@ static int fwspk_close(struct snd_pcm_substream *substream) static void fwspk_stop_stream(struct fwspk *fwspk) { - if (fwspk->stream_running) { + if (amdtp_out_stream_running(&fwspk->stream)) { amdtp_out_stream_stop(&fwspk->stream); cmp_connection_break(&fwspk->connection); - fwspk->stream_running = false; } } @@ -286,7 +284,7 @@ static int fwspk_prepare(struct snd_pcm_substream *substream) if (amdtp_out_streaming_error(&fwspk->stream)) fwspk_stop_stream(fwspk); - if (!fwspk->stream_running) { + if (!amdtp_out_stream_running(&fwspk->stream)) { err = cmp_connection_establish(&fwspk->connection, amdtp_out_stream_get_max_payload(&fwspk->stream)); if (err < 0) @@ -297,8 +295,6 @@ static int fwspk_prepare(struct snd_pcm_substream *substream) fwspk->connection.speed); if (err < 0) goto err_connection; - - fwspk->stream_running = true; } mutex_unlock(&fwspk->mutex); -- cgit v0.10.2 From 54e72f0ba31e1562e6d6277d0e4d39a7004f814d Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 4 Sep 2011 22:15:54 +0200 Subject: ALSA: dice: reorganize interface definitions Move the DICE interface symbols into a separate header file, and add more documentation. Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/dice-interface.h b/sound/firewire/dice-interface.h new file mode 100644 index 0000000..af916b9 --- /dev/null +++ b/sound/firewire/dice-interface.h @@ -0,0 +1,371 @@ +#ifndef SOUND_FIREWIRE_DICE_INTERFACE_H_INCLUDED +#define SOUND_FIREWIRE_DICE_INTERFACE_H_INCLUDED + +/* + * DICE device interface definitions + */ + +/* + * Generally, all registers can be read like memory, i.e., with quadlet read or + * block read transactions with any alignment or length. Writes are not + * allowed except where noted; quadlet-sized registers must be written with + * a quadlet write transaction. + * + * All values are in big endian. The DICE firmware runs on a little-endian CPU + * and just byte-swaps _all_ quadlets on the bus, so values without endianness + * (e.g. strings) get scrambled and must be byte-swapped again by the driver. + */ + +/* + * Streaming is handled by the "DICE driver" interface. Its registers are + * located in this private address space. + */ +#define DICE_PRIVATE_SPACE 0xffffe0000000uLL + +/* + * The registers are organized in several sections, which are organized + * separately to allow them to be extended individually. Whether a register is + * supported can be detected by checking its offset against its section's size. + * + * The section offset values are relative to DICE_PRIVATE_SPACE; the offset/ + * size values are measured in quadlets. Read-only. + */ +#define DICE_GLOBAL_OFFSET 0x00 +#define DICE_GLOBAL_SIZE 0x04 +#define DICE_TX_OFFSET 0x08 +#define DICE_TX_SIZE 0x0c +#define DICE_RX_OFFSET 0x10 +#define DICE_RX_SIZE 0x14 +#define DICE_EXT_SYNC_OFFSET 0x18 +#define DICE_EXT_SYNC_SIZE 0x1c +#define DICE_UNUSED2_OFFSET 0x20 +#define DICE_UNUSED2_SIZE 0x24 + +/* + * Global settings. + */ + +/* + * Stores the full 64-bit address (node ID and offset in the node's address + * space) where the device will send notifications. Must be changed with + * a compare/swap transaction by the owner. This register is automatically + * cleared on a bus reset. + */ +#define GLOBAL_OWNER 0x000 +#define OWNER_NO_OWNER 0xffff000000000000uLL +#define OWNER_NODE_SHIFT 48 + +/* + * A bitmask with asynchronous events; read-only. When any event(s) happen, + * the bits of previous events are cleared, and the value of this register is + * also written to the address stored in the owner register. + */ +#define GLOBAL_NOTIFICATION 0x008 +/* Some registers in the Rx/Tx sections may have changed. */ +#define NOTIFY_RX_CFG_CHG 0x00000001 +#define NOTIFY_TX_CFG_CHG 0x00000002 +/* Lock status of the current clock source may have changed. */ +#define NOTIFY_LOCK_CHG 0x00000010 +/* Write to the clock select register has been finished. */ +#define NOTIFY_CLOCK_ACCEPTED 0x00000020 +/* Lock status of some clock source has changed. */ +#define NOTIFY_EXT_STATUS 0x00000040 +/* Other bits may be used for device-specific events. */ + +/* + * A name that can be customized for each device; read/write. Padded with zero + * bytes. Quadlets are byte-swapped. The encoding is whatever the host driver + * happens to be using. + */ +#define GLOBAL_NICK_NAME 0x00c +#define NICK_NAME_SIZE 64 + +/* + * The current sample rate and clock source; read/write. Whether a clock + * source or sample rate is supported is device-specific; the internal clock + * source is always available. Low/mid/high = up to 48/96/192 kHz. This + * register can be changed even while streams are running. + */ +#define GLOBAL_CLOCK_SELECT 0x04c +#define CLOCK_SOURCE_MASK 0x000000ff +#define CLOCK_SOURCE_AES1 0x00000000 +#define CLOCK_SOURCE_AES2 0x00000001 +#define CLOCK_SOURCE_AES3 0x00000002 +#define CLOCK_SOURCE_AES4 0x00000003 +#define CLOCK_SOURCE_AES_ANY 0x00000004 +#define CLOCK_SOURCE_ADAT 0x00000005 +#define CLOCK_SOURCE_TDIF 0x00000006 +#define CLOCK_SOURCE_WC 0x00000007 +#define CLOCK_SOURCE_ARX1 0x00000008 +#define CLOCK_SOURCE_ARX2 0x00000009 +#define CLOCK_SOURCE_ARX3 0x0000000a +#define CLOCK_SOURCE_ARX4 0x0000000b +#define CLOCK_SOURCE_INTERNAL 0x0000000c +#define CLOCK_RATE_MASK 0x0000ff00 +#define CLOCK_RATE_32000 0x00000000 +#define CLOCK_RATE_44100 0x00000100 +#define CLOCK_RATE_48000 0x00000200 +#define CLOCK_RATE_88200 0x00000300 +#define CLOCK_RATE_96000 0x00000400 +#define CLOCK_RATE_176400 0x00000500 +#define CLOCK_RATE_192000 0x00000600 +#define CLOCK_RATE_ANY_LOW 0x00000700 +#define CLOCK_RATE_ANY_MID 0x00000800 +#define CLOCK_RATE_ANY_HIGH 0x00000900 +#define CLOCK_RATE_NONE 0x00000a00 +#define CLOCK_RATE_SHIFT 8 + +/* + * Enable streaming; read/write. Writing a non-zero value (re)starts all + * streams that have a valid iso channel set; zero stops all streams. The + * streams' parameters must be configured before starting. This register is + * automatically cleared on a bus reset. + */ +#define GLOBAL_ENABLE 0x050 + +/* + * Status of the sample clock; read-only. + */ +#define GLOBAL_STATUS 0x054 +/* The current clock source is locked. */ +#define STATUS_SOURCE_LOCKED 0x00000001 +/* The actual sample rate; CLOCK_RATE_32000-_192000 or _NONE. */ +#define STATUS_NOMINAL_RATE_MASK 0x0000ff00 + +/* + * Status of all clock sources; read-only. + */ +#define GLOBAL_EXTENDED_STATUS 0x058 +/* + * The _LOCKED bits always show the current status; any change generates + * a notification. + */ +#define EXT_STATUS_AES1_LOCKED 0x00000001 +#define EXT_STATUS_AES2_LOCKED 0x00000002 +#define EXT_STATUS_AES3_LOCKED 0x00000004 +#define EXT_STATUS_AES4_LOCKED 0x00000008 +#define EXT_STATUS_ADAT_LOCKED 0x00000010 +#define EXT_STATUS_TDIF_LOCKED 0x00000020 +#define EXT_STATUS_ARX1_LOCKED 0x00000040 +#define EXT_STATUS_ARX2_LOCKED 0x00000080 +#define EXT_STATUS_ARX3_LOCKED 0x00000100 +#define EXT_STATUS_ARX4_LOCKED 0x00000200 +#define EXT_STATUS_WC_LOCKED 0x00000400 +/* + * The _SLIP bits do not generate notifications; a set bit indicates that an + * error occurred since the last time when this register was read with + * a quadlet read transaction. + */ +#define EXT_STATUS_AES1_SLIP 0x00010000 +#define EXT_STATUS_AES2_SLIP 0x00020000 +#define EXT_STATUS_AES3_SLIP 0x00040000 +#define EXT_STATUS_AES4_SLIP 0x00080000 +#define EXT_STATUS_ADAT_SLIP 0x00100000 +#define EXT_STATUS_TDIF_SLIP 0x00200000 +#define EXT_STATUS_ARX1_SLIP 0x00400000 +#define EXT_STATUS_ARX2_SLIP 0x00800000 +#define EXT_STATUS_ARX3_SLIP 0x01000000 +#define EXT_STATUS_ARX4_SLIP 0x02000000 +#define EXT_STATUS_WC_SLIP 0x04000000 + +/* + * The measured rate of the current clock source, in Hz; read-only. + */ +#define GLOBAL_SAMPLE_RATE 0x05c + +/* + * The version of the DICE driver specification that this device conforms to; + * read-only. + */ +#define GLOBAL_VERSION 0x060 + +/* Some old firmware versions do not have the following global registers: */ + +/* + * Supported sample rates and clock sources; read-only. + */ +#define GLOBAL_CLOCK_CAPABILITIES 0x064 +#define CLOCK_CAP_RATE_32000 0x00000001 +#define CLOCK_CAP_RATE_44100 0x00000002 +#define CLOCK_CAP_RATE_48000 0x00000004 +#define CLOCK_CAP_RATE_88200 0x00000008 +#define CLOCK_CAP_RATE_96000 0x00000010 +#define CLOCK_CAP_RATE_176400 0x00000020 +#define CLOCK_CAP_RATE_192000 0x00000040 +#define CLOCK_CAP_SOURCE_AES1 0x00010000 +#define CLOCK_CAP_SOURCE_AES2 0x00020000 +#define CLOCK_CAP_SOURCE_AES3 0x00040000 +#define CLOCK_CAP_SOURCE_AES4 0x00080000 +#define CLOCK_CAP_SOURCE_AES_ANY 0x00100000 +#define CLOCK_CAP_SOURCE_ADAT 0x00200000 +#define CLOCK_CAP_SOURCE_TDIF 0x00400000 +#define CLOCK_CAP_SOURCE_WC 0x00800000 +#define CLOCK_CAP_SOURCE_ARX1 0x01000000 +#define CLOCK_CAP_SOURCE_ARX2 0x02000000 +#define CLOCK_CAP_SOURCE_ARX3 0x04000000 +#define CLOCK_CAP_SOURCE_ARX4 0x08000000 +#define CLOCK_CAP_SOURCE_INTERNAL 0x10000000 + +/* + * Names of all clock sources; read-only. Quadlets are byte-swapped. Names + * are separated with one backslash, the list is terminated with two + * backslashes. Unused clock sources are included. + */ +#define GLOBAL_CLOCK_SOURCE_NAMES 0x068 +#define CLOCK_SOURCE_NAMES_SIZE 256 + +/* + * Capture stream settings. This section includes the number/size registers + * and the registers of all streams. + */ + +/* + * The number of supported capture streams; read-only. + */ +#define TX_NUMBER 0x000 + +/* + * The size of one stream's register block, in quadlets; read-only. The + * registers of the first stream follow immediately afterwards; the registers + * of the following streams are offset by this register's value. + */ +#define TX_SIZE 0x004 + +/* + * The isochronous channel number on which packets are sent, or -1 if the + * stream is not to be used; read/write. + */ +#define TX_ISOCHRONOUS 0x008 + +/* + * The number of audio channels; read-only. There will be one quadlet per + * channel; the first channel is the first quadlet in a data block. + */ +#define TX_NUMBER_AUDIO 0x00c + +/* + * The number of MIDI ports, 0-8; read-only. If > 0, there will be one + * additional quadlet in each data block, following the audio quadlets. + */ +#define TX_NUMBER_MIDI 0x010 + +/* + * The speed at which the packets are sent, SCODE_100-_400; read/write. + */ +#define TX_SPEED 0x014 + +/* + * Names of all audio channels; read-only. Quadlets are byte-swapped. Names + * are separated with one backslash, the list is terminated with two + * backslashes. + */ +#define TX_NAMES 0x018 +#define TX_NAMES_SIZE 256 + +/* + * Audio IEC60958 capabilities; read-only. Bitmask with one bit per audio + * channel. + */ +#define TX_AC3_CAPABILITIES 0x118 + +/* + * Send audio data with IEC60958 label; read/write. Bitmask with one bit per + * audio channel. This register can be changed even while the stream is + * running. + */ +#define TX_AC3_ENABLE 0x11c + +/* + * Playback stream settings. This section includes the number/size registers + * and the registers of all streams. + */ + +/* + * The number of supported playback streams; read-only. + */ +#define RX_NUMBER 0x000 + +/* + * The size of one stream's register block, in quadlets; read-only. The + * registers of the first stream follow immediately afterwards; the registers + * of the following streams are offset by this register's value. + */ +#define RX_SIZE 0x004 + +/* + * The isochronous channel number on which packets are received, or -1 if the + * stream is not to be used; read/write. + */ +#define RX_ISOCHRONOUS 0x008 + +/* + * Index of first quadlet to be interpreted; read/write. If > 0, that many + * quadlets at the beginning of each data block will be ignored, and all the + * audio and MIDI quadlets will follow. + */ +#define RX_SEQ_START 0x00c + +/* + * The number of audio channels; read-only. There will be one quadlet per + * channel. + */ +#define RX_NUMBER_AUDIO 0x010 + +/* + * The number of MIDI ports, 0-8; read-only. If > 0, there will be one + * additional quadlet in each data block, following the audio quadlets. + */ +#define RX_NUMBER_MIDI 0x014 + +/* + * Names of all audio channels; read-only. Quadlets are byte-swapped. Names + * are separated with one backslash, the list is terminated with two + * backslashes. + */ +#define RX_NAMES 0x018 +#define RX_NAMES_SIZE 256 + +/* + * Audio IEC60958 capabilities; read-only. Bitmask with one bit per audio + * channel. + */ +#define RX_AC3_CAPABILITIES 0x118 + +/* + * Receive audio data with IEC60958 label; read/write. Bitmask with one bit + * per audio channel. This register can be changed even while the stream is + * running. + */ +#define RX_AC3_ENABLE 0x11c + +/* + * Extended synchronization information. + * This section can be read completely with a block read request. + */ + +/* + * Current clock source; read-only. + */ +#define EXT_SYNC_CLOCK_SOURCE 0x000 + +/* + * Clock source is locked (boolean); read-only. + */ +#define EXT_SYNC_LOCKED 0x004 + +/* + * Current sample rate (CLOCK_RATE_* >> CLOCK_RATE_SHIFT), _32000-_192000 or + * _NONE; read-only. + */ +#define EXT_SYNC_RATE 0x008 + +/* + * ADAT user data bits; read-only. + */ +#define EXT_SYNC_ADAT_USER_DATA 0x00c +/* The data bits, if available. */ +#define ADAT_USER_DATA_MASK 0x0f +/* The data bits are not available. */ +#define ADAT_USER_DATA_NO_DATA 0x10 + +#endif diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index 3591aeb..1da1dde 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -26,212 +26,7 @@ #include "amdtp.h" #include "iso-resources.h" #include "lib.h" - -#define DICE_PRIVATE_SPACE 0xffffe0000000uLL - -/* offset from DICE_PRIVATE_SPACE; offsets and sizes in quadlets */ -#define DICE_GLOBAL_OFFSET 0x00 -#define DICE_GLOBAL_SIZE 0x04 -#define DICE_TX_OFFSET 0x08 -#define DICE_TX_SIZE 0x0c -#define DICE_RX_OFFSET 0x10 -#define DICE_RX_SIZE 0x14 - -/* pointed to by DICE_GLOBAL_OFFSET */ -#define GLOBAL_OWNER 0x000 -#define OWNER_NO_OWNER 0xffff000000000000uLL -#define OWNER_NODE_SHIFT 48 -#define GLOBAL_NOTIFICATION 0x008 -#define NOTIFY_RX_CFG_CHG 0x00000001 -#define NOTIFY_TX_CFG_CHG 0x00000002 -#define NOTIFY_DUP_ISOC 0x00000004 -#define NOTIFY_BW_ERR 0x00000008 -#define NOTIFY_LOCK_CHG 0x00000010 -#define NOTIFY_CLOCK_ACCEPTED 0x00000020 -#define NOTIFY_INTERFACE_CHG 0x00000040 -#define NOTIFY_MESSAGE 0x00100000 -#define GLOBAL_NICK_NAME 0x00c -#define NICK_NAME_SIZE 64 -#define GLOBAL_CLOCK_SELECT 0x04c -#define CLOCK_SOURCE_MASK 0x000000ff -#define CLOCK_SOURCE_AES1 0x00000000 -#define CLOCK_SOURCE_AES2 0x00000001 -#define CLOCK_SOURCE_AES3 0x00000002 -#define CLOCK_SOURCE_AES4 0x00000003 -#define CLOCK_SOURCE_AES_ANY 0x00000004 -#define CLOCK_SOURCE_ADAT 0x00000005 -#define CLOCK_SOURCE_TDIF 0x00000006 -#define CLOCK_SOURCE_WC 0x00000007 -#define CLOCK_SOURCE_ARX1 0x00000008 -#define CLOCK_SOURCE_ARX2 0x00000009 -#define CLOCK_SOURCE_ARX3 0x0000000a -#define CLOCK_SOURCE_ARX4 0x0000000b -#define CLOCK_SOURCE_INTERNAL 0x0000000c -#define CLOCK_RATE_MASK 0x0000ff00 -#define CLOCK_RATE_32000 0x00000000 -#define CLOCK_RATE_44100 0x00000100 -#define CLOCK_RATE_48000 0x00000200 -#define CLOCK_RATE_88200 0x00000300 -#define CLOCK_RATE_96000 0x00000400 -#define CLOCK_RATE_176400 0x00000500 -#define CLOCK_RATE_192000 0x00000600 -#define CLOCK_RATE_ANY_LOW 0x00000700 -#define CLOCK_RATE_ANY_MID 0x00000800 -#define CLOCK_RATE_ANY_HIGH 0x00000900 -#define CLOCK_RATE_NONE 0x00000a00 -#define CLOCK_RATE_SHIFT 8 -#define GLOBAL_ENABLE 0x050 -#define ENABLE 0x00000001 -#define GLOBAL_STATUS 0x054 -#define STATUS_SOURCE_LOCKED 0x00000001 -#define STATUS_RATE_CONFLICT 0x00000002 -#define STATUS_NOMINAL_RATE_MASK 0x0000ff00 -#define GLOBAL_EXTENDED_STATUS 0x058 -#define EXT_STATUS_AES1_LOCKED 0x00000001 -#define EXT_STATUS_AES2_LOCKED 0x00000002 -#define EXT_STATUS_AES3_LOCKED 0x00000004 -#define EXT_STATUS_AES4_LOCKED 0x00000008 -#define EXT_STATUS_ADAT_LOCKED 0x00000010 -#define EXT_STATUS_TDIF_LOCKED 0x00000020 -#define EXT_STATUS_ARX1_LOCKED 0x00000040 -#define EXT_STATUS_ARX2_LOCKED 0x00000080 -#define EXT_STATUS_ARX3_LOCKED 0x00000100 -#define EXT_STATUS_ARX4_LOCKED 0x00000200 -#define EXT_STATUS_WC_LOCKED 0x00000400 -#define EXT_STATUS_AES1_SLIP 0x00010000 -#define EXT_STATUS_AES2_SLIP 0x00020000 -#define EXT_STATUS_AES3_SLIP 0x00040000 -#define EXT_STATUS_AES4_SLIP 0x00080000 -#define EXT_STATUS_ADAT_SLIP 0x00100000 -#define EXT_STATUS_TDIF_SLIP 0x00200000 -#define EXT_STATUS_ARX1_SLIP 0x00400000 -#define EXT_STATUS_ARX2_SLIP 0x00800000 -#define EXT_STATUS_ARX3_SLIP 0x01000000 -#define EXT_STATUS_ARX4_SLIP 0x02000000 -#define EXT_STATUS_WC_SLIP 0x04000000 -#define GLOBAL_SAMPLE_RATE 0x05c -#define GLOBAL_VERSION 0x060 -#define GLOBAL_CLOCK_CAPABILITIES 0x064 -#define CLOCK_CAP_RATE_32000 0x00000001 -#define CLOCK_CAP_RATE_44100 0x00000002 -#define CLOCK_CAP_RATE_48000 0x00000004 -#define CLOCK_CAP_RATE_88200 0x00000008 -#define CLOCK_CAP_RATE_96000 0x00000010 -#define CLOCK_CAP_RATE_176400 0x00000020 -#define CLOCK_CAP_RATE_192000 0x00000040 -#define CLOCK_CAP_SOURCE_AES1 0x00010000 -#define CLOCK_CAP_SOURCE_AES2 0x00020000 -#define CLOCK_CAP_SOURCE_AES3 0x00040000 -#define CLOCK_CAP_SOURCE_AES4 0x00080000 -#define CLOCK_CAP_SOURCE_AES_ANY 0x00100000 -#define CLOCK_CAP_SOURCE_ADAT 0x00200000 -#define CLOCK_CAP_SOURCE_TDIF 0x00400000 -#define CLOCK_CAP_SOURCE_WC 0x00800000 -#define CLOCK_CAP_SOURCE_ARX1 0x01000000 -#define CLOCK_CAP_SOURCE_ARX2 0x02000000 -#define CLOCK_CAP_SOURCE_ARX3 0x04000000 -#define CLOCK_CAP_SOURCE_ARX4 0x08000000 -#define CLOCK_CAP_SOURCE_INTERNAL 0x10000000 -#define GLOBAL_CLOCK_SOURCE_NAMES 0x068 -#define CLOCK_SOURCE_NAMES_SIZE 256 - -/* pointed to by DICE_TX_OFFSET */ -#define TX_NUMBER 0x000 -#define TX_SIZE 0x004 -/* repeated TX_NUMBER times, offset by TX_SIZE quadlets */ -#define TX_ISOCHRONOUS 0x008 -#define TX_NUMBER_AUDIO 0x00c -#define TX_NUMBER_MIDI 0x010 -#define TX_SPEED 0x014 -#define TX_NAMES 0x018 -#define TX_NAMES_SIZE 256 -#define TX_AC3_CAPABILITIES 0x118 -#define TX_AC3_ENABLE 0x11c - -/* pointed to by DICE_RX_OFFSET */ -#define RX_NUMBER 0x000 -#define RX_SIZE 0x004 -/* repeated RX_NUMBER times, offset by RX_SIZE quadlets */ -#define RX_ISOCHRONOUS 0x008 -#define RX_SEQ_START 0x00c -#define RX_NUMBER_AUDIO 0x010 -#define RX_NUMBER_MIDI 0x014 -#define RX_NAMES 0x018 -#define RX_NAMES_SIZE 256 -#define RX_AC3_CAPABILITIES 0x118 -#define RX_AC3_ENABLE 0x11c - - -#define FIRMWARE_LOAD_SPACE 0xffffe0100000uLL - -/* offset from FIRMWARE_LOAD_SPACE */ -#define FIRMWARE_VERSION 0x000 -#define FIRMWARE_OPCODE 0x004 -#define OPCODE_MASK 0x00000fff -#define OPCODE_GET_IMAGE_DESC 0x00000000 -#define OPCODE_DELETE_IMAGE 0x00000001 -#define OPCODE_CREATE_IMAGE 0x00000002 -#define OPCODE_UPLOAD 0x00000003 -#define OPCODE_UPLOAD_STAT 0x00000004 -#define OPCODE_RESET_IMAGE 0x00000005 -#define OPCODE_TEST_ACTION 0x00000006 -#define OPCODE_GET_RUNNING_IMAGE_VINFO 0x0000000a -#define OPCODE_EXECUTE 0x80000000 -#define FIRMWARE_RETURN_STATUS 0x008 -#define FIRMWARE_PROGRESS 0x00c -#define PROGRESS_CURR_MASK 0x00000fff -#define PROGRESS_MAX_MASK 0x00fff000 -#define PROGRESS_TOUT_MASK 0x0f000000 -#define PROGRESS_FLAG 0x80000000 -#define FIRMWARE_CAPABILITIES 0x010 -#define FL_CAP_AUTOERASE 0x00000001 -#define FL_CAP_PROGRESS 0x00000002 -#define FIRMWARE_DATA 0x02c -#define TEST_CMD_POKE 0x00000001 -#define TEST_CMD_PEEK 0x00000002 -#define CMD_GET_AVS_CNT 0x00000003 -#define CMD_CLR_AVS_CNT 0x00000004 -#define CMD_SET_MODE 0x00000005 -#define CMD_SET_MIDIBP 0x00000006 -#define CMD_GET_AVSPHASE 0x00000007 -#define CMD_ENABLE_BNC_SYNC 0x00000008 -#define CMD_PULSE_BNC_SYNC 0x00000009 -#define CMD_EMUL_SLOW_CMD 0x0000000a -#define FIRMWARE_TEST_DELAY 0xfd8 -#define FIRMWARE_TEST_BUF 0xfdc - - -/* EAP */ -#define EAP_PRIVATE_SPACE 0xffffe0200000uLL - -#define EAP_CAPABILITY_OFFSET 0x000 -#define EAP_CAPABILITY_SIZE 0x004 -/* ... */ - -#define EAP_ROUTER_CAPS 0x000 -#define ROUTER_EXPOSED 0x00000001 -#define ROUTER_READ_ONLY 0x00000002 -#define ROUTER_FLASH 0x00000004 -#define MAX_ROUTES_MASK 0xffff0000 -#define EAP_MIXER_CAPS 0x004 -#define MIXER_EXPOSED 0x00000001 -#define MIXER_READ_ONLY 0x00000002 -#define MIXER_FLASH 0x00000004 -#define MIXER_IN_DEV_MASK 0x000000f0 -#define MIXER_OUT_DEV_MASK 0x00000f00 -#define MIXER_INPUTS_MASK 0x00ff0000 -#define MIXER_OUTPUTS_MASK 0xff000000 -#define EAP_GENERAL_CAPS 0x008 -#define GENERAL_STREAM_CONFIG 0x00000001 -#define GENERAL_FLASH 0x00000002 -#define GENERAL_PEAK 0x00000004 -#define GENERAL_MAX_TX_STREAMS_MASK 0x000000f0 -#define GENERAL_MAX_RX_STREAMS_MASK 0x00000f00 -#define GENERAL_STREAM_CONFIG_FLASH 0x00001000 -#define GENERAL_CHIP_MASK 0x00ff0000 -#define GENERAL_CHIP_DICE_II 0x00000000 -#define GENERAL_CHIP_DICE_MINI 0x00010000 -#define GENERAL_CHIP_DICE_JR 0x00020000 +#include "dice-interface.h" struct dice { @@ -479,7 +274,7 @@ static int dice_enable_set(struct dice *dice) __be32 value; int rcode, err, errors = 0; - value = cpu_to_be32(ENABLE); + value = cpu_to_be32(1); for (;;) { rcode = fw_run_transaction(device->card, TCODE_WRITE_QUADLET_REQUEST, -- cgit v0.10.2 From cbab328ddc78589233be8be2f1e6a5f9d97b81db Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 4 Sep 2011 22:16:02 +0200 Subject: ALSA: dice: fix device detection for other vendors DICE devices do not have a unique specifier ID in their unit directory (it's always the same as the device vendor's ID), so rely on just the version ID for driver loading, and use a heuristic in the probe callback to detect actual DICE devices. Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index 1da1dde..b4827ff 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -827,28 +827,93 @@ static void dice_card_free(struct snd_card *card) mutex_destroy(&dice->mutex); } +#define DICE_CATEGORY_ID 0x04 + +static int dice_interface_check(struct fw_unit *unit) +{ + static const int min_values[10] = { + 10, 0x64 / 4, + 10, 0x18 / 4, + 10, 0x18 / 4, + 0, 0, + 0, 0, + }; + struct fw_device *device = fw_parent_device(unit); + struct fw_csr_iterator it; + int key, value, vendor = -1, model = -1, err; + unsigned int i; + __be32 pointers[ARRAY_SIZE(min_values)]; + __be32 version; + + /* + * Check that GUID and unit directory are constructed according to DICE + * rules, i.e., that the specifier ID is the GUID's OUI, and that the + * GUID chip ID consists of the 8-bit DICE category ID, the 10-bit + * product ID, and a 22-bit serial number. + */ + fw_csr_iterator_init(&it, unit->directory); + while (fw_csr_iterator_next(&it, &key, &value)) { + switch (key) { + case CSR_SPECIFIER_ID: + vendor = value; + break; + case CSR_MODEL: + model = value; + break; + } + } + if (device->config_rom[3] != ((vendor << 8) | DICE_CATEGORY_ID) || + device->config_rom[4] >> 22 != model) + return -ENODEV; + + /* + * Check that the sub address spaces exist and are located inside the + * private address space. The minimum values are chosen so that all + * minimally required registers are included. + */ + err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST, + DICE_PRIVATE_SPACE, + pointers, sizeof(pointers)); + if (err < 0) + return -ENODEV; + for (i = 0; i < ARRAY_SIZE(pointers); ++i) { + value = be32_to_cpu(pointers[i]); + if (value < min_values[i] || value >= 0x40000) + return -ENODEV; + } + + /* + * Check that the implemented DICE driver specification major version + * number matches. + */ + err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, + DICE_PRIVATE_SPACE + + be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION, + &version, 4); + if (err < 0) + return -ENODEV; + if ((version & cpu_to_be32(0xff000000)) != cpu_to_be32(0x01000000)) { + dev_err(&unit->device, + "unknown DICE version: 0x%08x\n", be32_to_cpu(version)); + return -ENODEV; + } + + return 0; +} + static int dice_init_offsets(struct dice *dice) { __be32 pointers[6]; - unsigned int global_size, rx_size; int err; err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, - DICE_PRIVATE_SPACE, &pointers, 6 * 4); + DICE_PRIVATE_SPACE, + pointers, sizeof(pointers)); if (err < 0) return err; dice->global_offset = be32_to_cpu(pointers[0]) * 4; - global_size = be32_to_cpu(pointers[1]); dice->rx_offset = be32_to_cpu(pointers[4]) * 4; - rx_size = be32_to_cpu(pointers[5]); - - /* some sanity checks to ensure that we actually have a DICE */ - if (dice->global_offset < 10 * 4 || global_size < 0x168 / 4 || - dice->rx_offset < 10 * 4 || rx_size < 0x120 / 4) { - dev_err(&dice->unit->device, "invalid register pointers\n"); - return -ENXIO; - } return 0; } @@ -881,8 +946,8 @@ static void dice_card_strings(struct dice *dice) strcpy(model, "?"); fw_csr_string(dice->unit->directory, CSR_MODEL, model, sizeof(model)); snprintf(card->longname, sizeof(card->longname), - "%s %s, GUID %08x%08x at %s, S%d", - vendor, model, dev->config_rom[3], dev->config_rom[4], + "%s %s (serial %u) at %s, S%d", + vendor, model, dev->config_rom[4] & 0x3fffff, dev_name(&dice->unit->device), 100 << dev->max_speed); strcpy(card->mixername, "DICE"); @@ -895,6 +960,10 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) __be32 clock_sel; int err; + err = dice_interface_check(unit); + if (err < 0) + return err; + err = snd_card_create(-1, NULL, THIS_MODULE, sizeof(*dice), &card); if (err < 0) return err; @@ -1020,15 +1089,12 @@ static void dice_bus_reset(struct fw_unit *unit) mutex_unlock(&dice->mutex); } -#define TC_OUI 0x000166 #define DICE_INTERFACE 0x000001 static const struct ieee1394_device_id dice_id_table[] = { { - .match_flags = IEEE1394_MATCH_SPECIFIER_ID | - IEEE1394_MATCH_VERSION, - .specifier_id = TC_OUI, - .version = DICE_INTERFACE, + .match_flags = IEEE1394_MATCH_VERSION, + .version = DICE_INTERFACE, }, { } }; -- cgit v0.10.2 From a7304e3bf0489d3fc0260bdb9c1441427a26a38f Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 4 Sep 2011 22:16:10 +0200 Subject: ALSA: dice: support dual-wire stream format at 192 kHz Change the AMDTP streaming code to handle the non-standard stream format that DICE devices use at sample rates greater than 96 kHz. Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index d56b8e7..a09c3b3 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -65,42 +65,66 @@ void amdtp_out_stream_destroy(struct amdtp_out_stream *s) } EXPORT_SYMBOL(amdtp_out_stream_destroy); +unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = { + [CIP_SFC_32000] = 8, + [CIP_SFC_44100] = 8, + [CIP_SFC_48000] = 8, + [CIP_SFC_88200] = 16, + [CIP_SFC_96000] = 16, + [CIP_SFC_176400] = 32, + [CIP_SFC_192000] = 32, +}; +EXPORT_SYMBOL(amdtp_syt_intervals); + /** - * amdtp_out_stream_set_rate - set the sample rate + * amdtp_out_stream_set_parameters - set stream parameters * @s: the AMDTP output stream to configure * @rate: the sample rate + * @pcm_channels: the number of PCM samples in each data block, to be encoded + * as AM824 multi-bit linear audio + * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels) * - * The sample rate must be set before the stream is started, and must not be + * The parameters must be set before the stream is started, and must not be * changed while the stream is running. */ -void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate) +void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s, + unsigned int rate, + unsigned int pcm_channels, + unsigned int midi_ports) { - static const struct { - unsigned int rate; - unsigned int syt_interval; - } rate_info[] = { - [CIP_SFC_32000] = { 32000, 8, }, - [CIP_SFC_44100] = { 44100, 8, }, - [CIP_SFC_48000] = { 48000, 8, }, - [CIP_SFC_88200] = { 88200, 16, }, - [CIP_SFC_96000] = { 96000, 16, }, - [CIP_SFC_176400] = { 176400, 32, }, - [CIP_SFC_192000] = { 192000, 32, }, + static const unsigned int rates[] = { + [CIP_SFC_32000] = 32000, + [CIP_SFC_44100] = 44100, + [CIP_SFC_48000] = 48000, + [CIP_SFC_88200] = 88200, + [CIP_SFC_96000] = 96000, + [CIP_SFC_176400] = 176400, + [CIP_SFC_192000] = 192000, }; unsigned int sfc; if (WARN_ON(amdtp_out_stream_running(s))) return; - for (sfc = 0; sfc < ARRAY_SIZE(rate_info); ++sfc) - if (rate_info[sfc].rate == rate) + for (sfc = 0; sfc < CIP_SFC_COUNT; ++sfc) + if (rates[sfc] == rate) goto sfc_found; WARN_ON(1); return; sfc_found: + s->dual_wire = (s->flags & CIP_HI_DUALWIRE) && sfc > CIP_SFC_96000; + if (s->dual_wire) { + sfc -= 2; + rate /= 2; + pcm_channels *= 2; + } s->sfc = sfc; - s->syt_interval = rate_info[sfc].syt_interval; + s->data_block_quadlets = pcm_channels + DIV_ROUND_UP(midi_ports, 8); + s->pcm_channels = pcm_channels; + s->midi_ports = midi_ports; + + s->syt_interval = amdtp_syt_intervals[sfc]; /* default buffering in the device */ s->transfer_delay = TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE; @@ -108,21 +132,17 @@ sfc_found: /* additional buffering needed to adjust for no-data packets */ s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate; } -EXPORT_SYMBOL(amdtp_out_stream_set_rate); +EXPORT_SYMBOL(amdtp_out_stream_set_parameters); /** * amdtp_out_stream_get_max_payload - get the stream's packet size * @s: the AMDTP output stream * * This function must not be called before the stream has been configured - * with amdtp_out_stream_set_rate(), amdtp_out_stream_set_pcm(), and - * amdtp_out_stream_set_midi(). + * with amdtp_out_stream_set_parameters(). */ unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s) { - s->data_block_quadlets = s->pcm_channels; - s->data_block_quadlets += DIV_ROUND_UP(s->midi_ports, 8); - return 8 + s->syt_interval * s->data_block_quadlets * 4; } EXPORT_SYMBOL(amdtp_out_stream_get_max_payload); @@ -133,14 +153,21 @@ static void amdtp_write_s16(struct amdtp_out_stream *s, static void amdtp_write_s32(struct amdtp_out_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames); +static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); +static void amdtp_write_s32_dualwire(struct amdtp_out_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); /** * amdtp_out_stream_set_pcm_format - set the PCM format * @s: the AMDTP output stream to configure * @format: the format of the ALSA PCM device * - * The sample format must be set before the stream is started, and must not be - * changed while the stream is running. + * The sample format must be set after the other paramters (rate/PCM channels/ + * MIDI) and before the stream is started, and must not be changed while the + * stream is running. */ void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, snd_pcm_format_t format) @@ -153,10 +180,16 @@ void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, WARN_ON(1); /* fall through */ case SNDRV_PCM_FORMAT_S16: - s->transfer_samples = amdtp_write_s16; + if (s->dual_wire) + s->transfer_samples = amdtp_write_s16_dualwire; + else + s->transfer_samples = amdtp_write_s16; break; case SNDRV_PCM_FORMAT_S32: - s->transfer_samples = amdtp_write_s32; + if (s->dual_wire) + s->transfer_samples = amdtp_write_s32_dualwire; + else + s->transfer_samples = amdtp_write_s32; break; } } @@ -305,6 +338,68 @@ static void amdtp_write_s16(struct amdtp_out_stream *s, } } +static void amdtp_write_s32_dualwire(struct amdtp_out_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, frame_adjust_1, frame_adjust_2, i, c; + const u32 *src; + + channels = s->pcm_channels; + src = (void *)runtime->dma_area + + s->pcm_buffer_pointer * (runtime->frame_bits / 8); + frame_adjust_1 = channels - 1; + frame_adjust_2 = 1 - (s->data_block_quadlets - channels); + + channels /= 2; + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + *buffer = cpu_to_be32((*src >> 8) | 0x40000000); + src++; + buffer += 2; + } + buffer -= frame_adjust_1; + for (c = 0; c < channels; ++c) { + *buffer = cpu_to_be32((*src >> 8) | 0x40000000); + src++; + buffer += 2; + } + buffer -= frame_adjust_2; + } +} + +static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, frame_adjust_1, frame_adjust_2, i, c; + const u16 *src; + + channels = s->pcm_channels; + src = (void *)runtime->dma_area + + s->pcm_buffer_pointer * (runtime->frame_bits / 8); + frame_adjust_1 = channels - 1; + frame_adjust_2 = 1 - (s->data_block_quadlets - channels); + + channels /= 2; + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + *buffer = cpu_to_be32((*src << 8) | 0x40000000); + src++; + buffer += 2; + } + buffer -= frame_adjust_1; + for (c = 0; c < channels; ++c) { + *buffer = cpu_to_be32((*src << 8) | 0x40000000); + src++; + buffer += 2; + } + buffer -= frame_adjust_2; + } +} + static void amdtp_fill_pcm_silence(struct amdtp_out_stream *s, __be32 *buffer, unsigned int frames) { @@ -390,6 +485,9 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle) s->packet_index = index; if (pcm) { + if (s->dual_wire) + data_blocks *= 2; + ptr = s->pcm_buffer_pointer + data_blocks; if (ptr >= pcm->runtime->buffer_size) ptr -= pcm->runtime->buffer_size; @@ -459,8 +557,7 @@ static int queue_initial_skip_packets(struct amdtp_out_stream *s) * @speed: firewire speed code * * The stream cannot be started until it has been configured with - * amdtp_out_stream_set_rate(), amdtp_out_stream_set_pcm(), - * amdtp_out_stream_set_midi(), and amdtp_out_stream_set_format(); + * amdtp_out_stream_set_parameters() and amdtp_out_stream_set_pcm_format(), * and it must be started before any PCM or MIDI device can be started. */ int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed) diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 28b1bf5..f3d03dd 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -15,10 +15,15 @@ * @CIP_BLOCKING: In blocking mode, each packet contains either zero or * SYT_INTERVAL samples, with these two types alternating so that * the overall sample rate comes out right. + * @CIP_HI_DUALWIRE: At rates above 96 kHz, pretend that the stream runs + * at half the actual sample rate with twice the number of channels; + * two samples of a channel are stored consecutively in the packet. + * Requires blocking mode and SYT_INTERVAL-aligned PCM buffer size. */ enum cip_out_flags { CIP_NONBLOCKING = 0x00, CIP_BLOCKING = 0x01, + CIP_HI_DUALWIRE = 0x02, }; /** @@ -32,6 +37,7 @@ enum cip_sfc { CIP_SFC_96000 = 4, CIP_SFC_176400 = 5, CIP_SFC_192000 = 6, + CIP_SFC_COUNT }; #define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \ @@ -48,6 +54,7 @@ struct amdtp_out_stream { struct mutex mutex; enum cip_sfc sfc; + bool dual_wire; unsigned int data_block_quadlets; unsigned int pcm_channels; unsigned int midi_ports; @@ -80,7 +87,10 @@ int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit, enum cip_out_flags flags); void amdtp_out_stream_destroy(struct amdtp_out_stream *s); -void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate); +void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s, + unsigned int rate, + unsigned int pcm_channels, + unsigned int midi_ports); unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s); int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed); @@ -93,39 +103,14 @@ void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s); unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s); void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s); +extern unsigned int amdtp_syt_intervals[CIP_SFC_COUNT]; + static inline bool amdtp_out_stream_running(struct amdtp_out_stream *s) { return !IS_ERR(s->context); } /** - * amdtp_out_stream_set_pcm - configure format of PCM samples - * @s: the AMDTP output stream to be configured - * @pcm_channels: the number of PCM samples in each data block, to be encoded - * as AM824 multi-bit linear audio - * - * This function must not be called while the stream is running. - */ -static inline void amdtp_out_stream_set_pcm(struct amdtp_out_stream *s, - unsigned int pcm_channels) -{ - s->pcm_channels = pcm_channels; -} - -/** - * amdtp_out_stream_set_midi - configure format of MIDI data - * @s: the AMDTP output stream to be configured - * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels) - * - * This function must not be called while the stream is running. - */ -static inline void amdtp_out_stream_set_midi(struct amdtp_out_stream *s, - unsigned int midi_ports) -{ - s->midi_ports = midi_ports; -} - -/** * amdtp_out_streaming_error - check for streaming error * @s: the AMDTP output stream * diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index b4827ff..8804e42 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -375,7 +375,7 @@ static int dice_open(struct snd_pcm_substream *substream) struct dice *dice = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; __be32 clock_sel, number_audio, number_midi; - unsigned int rate; + unsigned int rate_index, rate; int err; err = dice_try_lock(dice); @@ -387,12 +387,13 @@ static int dice_open(struct snd_pcm_substream *substream) &clock_sel, 4); if (err < 0) goto err_lock; - rate = (be32_to_cpu(clock_sel) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT; - if (rate >= ARRAY_SIZE(dice_rates)) { + rate_index = (be32_to_cpu(clock_sel) & CLOCK_RATE_MASK) + >> CLOCK_RATE_SHIFT; + if (rate_index >= ARRAY_SIZE(dice_rates)) { err = -ENXIO; goto err_lock; } - rate = dice_rates[rate]; + rate = dice_rates[rate_index]; err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, rx_address(dice, RX_NUMBER_AUDIO), @@ -413,9 +414,20 @@ static int dice_open(struct snd_pcm_substream *substream) runtime->hw.channels_min = be32_to_cpu(number_audio); runtime->hw.channels_max = be32_to_cpu(number_audio); - amdtp_out_stream_set_rate(&dice->stream, rate); - amdtp_out_stream_set_pcm(&dice->stream, be32_to_cpu(number_audio)); - amdtp_out_stream_set_midi(&dice->stream, be32_to_cpu(number_midi)); + amdtp_out_stream_set_parameters(&dice->stream, rate, + be32_to_cpu(number_audio), + be32_to_cpu(number_midi)); + + err = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + amdtp_syt_intervals[rate_index]); + if (err < 0) + goto err_lock; + err = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + amdtp_syt_intervals[rate_index]); + if (err < 0) + goto err_lock; err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, @@ -993,7 +1005,8 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) goto err_notification_handler; dice->resources.channels_mask = 0x00000000ffffffffuLL; - err = amdtp_out_stream_init(&dice->stream, unit, CIP_BLOCKING); + err = amdtp_out_stream_init(&dice->stream, unit, + CIP_BLOCKING | CIP_HI_DUALWIRE); if (err < 0) goto err_resources; diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c index 0ac5630..6a68caf 100644 --- a/sound/firewire/speakers.c +++ b/sound/firewire/speakers.c @@ -245,8 +245,10 @@ static int fwspk_hw_params(struct snd_pcm_substream *substream, if (err < 0) goto error; - amdtp_out_stream_set_rate(&fwspk->stream, params_rate(hw_params)); - amdtp_out_stream_set_pcm(&fwspk->stream, params_channels(hw_params)); + amdtp_out_stream_set_parameters(&fwspk->stream, + params_rate(hw_params), + params_channels(hw_params), + 0); amdtp_out_stream_set_pcm_format(&fwspk->stream, params_format(hw_params)); -- cgit v0.10.2 From a644a9473f7f9519e2fe519136959dd0e671572a Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 4 Sep 2011 22:17:31 +0200 Subject: ALSA: dice: optimize reading of consecutive registers Instead of reading two consecutive register with two quadlet requests, use one block read request. Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index 8804e42..e1d8dff 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -374,8 +374,8 @@ static int dice_open(struct snd_pcm_substream *substream) }; struct dice *dice = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; - __be32 clock_sel, number_audio, number_midi; - unsigned int rate_index, rate; + __be32 clock_sel, data[2]; + unsigned int rate_index, number_audio, number_midi; int err; err = dice_try_lock(dice); @@ -393,30 +393,25 @@ static int dice_open(struct snd_pcm_substream *substream) err = -ENXIO; goto err_lock; } - rate = dice_rates[rate_index]; - err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, + err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, rx_address(dice, RX_NUMBER_AUDIO), - &number_audio, 4); - if (err < 0) - goto err_lock; - err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, - rx_address(dice, RX_NUMBER_MIDI), - &number_midi, 4); + data, 2 * 4); if (err < 0) goto err_lock; + number_audio = be32_to_cpu(data[0]); + number_midi = be32_to_cpu(data[1]); runtime->hw = hardware; - runtime->hw.rates = snd_pcm_rate_to_rate_bit(rate); + runtime->hw.rates = snd_pcm_rate_to_rate_bit(dice_rates[rate_index]); snd_pcm_limit_hw_rates(runtime); - runtime->hw.channels_min = be32_to_cpu(number_audio); - runtime->hw.channels_max = be32_to_cpu(number_audio); + runtime->hw.channels_min = number_audio; + runtime->hw.channels_max = number_audio; - amdtp_out_stream_set_parameters(&dice->stream, rate, - be32_to_cpu(number_audio), - be32_to_cpu(number_midi)); + amdtp_out_stream_set_parameters(&dice->stream, dice_rates[rate_index], + number_audio, number_midi); err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -- cgit v0.10.2 From 1b70485f135a39d5f2d8c392a16817456fa3a5cd Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 4 Sep 2011 22:17:38 +0200 Subject: ALSA: firewire: extend snd_fw_transaction() Add a flag to snd_fw_transaction() to allow it to abort when a bus reset happens. This removes most of the duplicated error handling loops that were required around calls to the low-level fw_run_transaction(). Also add a flag to suppress error messages; errors are expected when we attempt to clean up after the device was unplugged. Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c index 645cb0b..efdbf58 100644 --- a/sound/firewire/cmp.c +++ b/sound/firewire/cmp.c @@ -48,9 +48,6 @@ static int pcr_modify(struct cmp_connection *c, int (*check)(struct cmp_connection *c, __be32 pcr), enum bus_reset_handling bus_reset_handling) { - struct fw_device *device = fw_parent_device(c->resources.unit); - int generation = c->resources.generation; - int rcode, errors = 0; __be32 old_arg, buffer[2]; int err; @@ -59,36 +56,31 @@ static int pcr_modify(struct cmp_connection *c, old_arg = buffer[0]; buffer[1] = modify(c, buffer[0]); - rcode = fw_run_transaction( - device->card, TCODE_LOCK_COMPARE_SWAP, - device->node_id, generation, device->max_speed, + err = snd_fw_transaction( + c->resources.unit, TCODE_LOCK_COMPARE_SWAP, CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index), - buffer, 8); - - if (rcode == RCODE_COMPLETE) { - if (buffer[0] == old_arg) /* success? */ - break; - - if (check) { - err = check(c, buffer[0]); - if (err < 0) - return err; - } - } else if (rcode == RCODE_GENERATION) - goto bus_reset; - else if (rcode_is_permanent_error(rcode) || ++errors >= 3) - goto io_error; + buffer, 8, + FW_FIXED_GENERATION | c->resources.generation); + + if (err < 0) { + if (err == -EAGAIN && + bus_reset_handling == SUCCEED_ON_BUS_RESET) + err = 0; + return err; + } + + if (buffer[0] == old_arg) /* success? */ + break; + + if (check) { + err = check(c, buffer[0]); + if (err < 0) + return err; + } } c->last_pcr_value = buffer[1]; return 0; - -io_error: - cmp_error(c, "transaction failed: %s\n", fw_rcode_string(rcode)); - return -EIO; - -bus_reset: - return bus_reset_handling == ABORT_ON_BUS_RESET ? -EAGAIN : 0; } @@ -108,7 +100,7 @@ int cmp_connection_init(struct cmp_connection *c, err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, CSR_REGISTER_BASE + CSR_IMPR, - &impr_be, 4); + &impr_be, 4, 0); if (err < 0) return err; impr = be32_to_cpu(impr_be); diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index e1d8dff..59d5ca4 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -118,7 +118,7 @@ static int dice_owner_set(struct dice *dice) { struct fw_device *device = fw_parent_device(dice->unit); __be64 *buffer; - int rcode, err, errors = 0; + int err, errors = 0; buffer = kmalloc(2 * 8, GFP_KERNEL); if (!buffer) @@ -132,31 +132,24 @@ static int dice_owner_set(struct dice *dice) dice->owner_generation = device->generation; smp_rmb(); /* node_id vs. generation */ - rcode = fw_run_transaction(device->card, - TCODE_LOCK_COMPARE_SWAP, - device->node_id, - dice->owner_generation, - device->max_speed, - global_address(dice, GLOBAL_OWNER), - buffer, 2 * 8); - - if (rcode == RCODE_COMPLETE) { - if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER)) { - err = 0; - } else { + err = snd_fw_transaction(dice->unit, + TCODE_LOCK_COMPARE_SWAP, + global_address(dice, GLOBAL_OWNER), + buffer, 2 * 8, + FW_FIXED_GENERATION | + dice->owner_generation); + + if (err == 0) { + if (buffer[0] != cpu_to_be64(OWNER_NO_OWNER)) { dev_err(&dice->unit->device, "device is already in use\n"); err = -EBUSY; } break; } - if (rcode_is_permanent_error(rcode) || ++errors >= 3) { - dev_err(&dice->unit->device, - "setting device owner failed: %s\n", - fw_rcode_string(rcode)); - err = -EIO; + if (err != -EAGAIN || ++errors >= 3) break; - } + msleep(20); } @@ -169,7 +162,7 @@ static int dice_owner_update(struct dice *dice) { struct fw_device *device = fw_parent_device(dice->unit); __be64 *buffer; - int rcode, err, errors = 0; + int err; if (dice->owner_generation == -1) return 0; @@ -178,44 +171,26 @@ static int dice_owner_update(struct dice *dice) if (!buffer) return -ENOMEM; - for (;;) { - buffer[0] = cpu_to_be64(OWNER_NO_OWNER); - buffer[1] = cpu_to_be64( - ((u64)device->card->node_id << OWNER_NODE_SHIFT) | - dice->notification_handler.offset); + buffer[0] = cpu_to_be64(OWNER_NO_OWNER); + buffer[1] = cpu_to_be64( + ((u64)device->card->node_id << OWNER_NODE_SHIFT) | + dice->notification_handler.offset); - dice->owner_generation = device->generation; - smp_rmb(); /* node_id vs. generation */ - rcode = fw_run_transaction(device->card, - TCODE_LOCK_COMPARE_SWAP, - device->node_id, - dice->owner_generation, - device->max_speed, - global_address(dice, GLOBAL_OWNER), - buffer, 2 * 8); - - if (rcode == RCODE_COMPLETE) { - if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER)) { - err = 0; - } else { - dev_err(&dice->unit->device, - "device is already in use\n"); - err = -EBUSY; - } - break; - } - if (rcode == RCODE_GENERATION) { - err = 0; /* try again later */ - break; - } - if (rcode_is_permanent_error(rcode) || ++errors >= 3) { + dice->owner_generation = device->generation; + smp_rmb(); /* node_id vs. generation */ + err = snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP, + global_address(dice, GLOBAL_OWNER), + buffer, 2 * 8, + FW_FIXED_GENERATION | dice->owner_generation); + + if (err == 0) { + if (buffer[0] != cpu_to_be64(OWNER_NO_OWNER)) { dev_err(&dice->unit->device, - "setting device owner failed: %s\n", - fw_rcode_string(rcode)); - err = -EIO; - break; + "device is already in use\n"); + err = -EBUSY; } - msleep(20); + } else if (err == -EAGAIN) { + err = 0; /* try again later */ } kfree(buffer); @@ -230,38 +205,19 @@ static void dice_owner_clear(struct dice *dice) { struct fw_device *device = fw_parent_device(dice->unit); __be64 *buffer; - int rcode, errors = 0; buffer = kmalloc(2 * 8, GFP_KERNEL); if (!buffer) return; - for (;;) { - buffer[0] = cpu_to_be64( - ((u64)device->card->node_id << OWNER_NODE_SHIFT) | - dice->notification_handler.offset); - buffer[1] = cpu_to_be64(OWNER_NO_OWNER); - - rcode = fw_run_transaction(device->card, - TCODE_LOCK_COMPARE_SWAP, - device->node_id, - dice->owner_generation, - device->max_speed, - global_address(dice, GLOBAL_OWNER), - buffer, 2 * 8); - - if (rcode == RCODE_COMPLETE) - break; - if (rcode == RCODE_GENERATION) - break; - if (rcode_is_permanent_error(rcode) || ++errors >= 3) { - dev_err(&dice->unit->device, - "clearing device owner failed: %s\n", - fw_rcode_string(rcode)); - break; - } - msleep(20); - } + buffer[0] = cpu_to_be64( + ((u64)device->card->node_id << OWNER_NODE_SHIFT) | + dice->notification_handler.offset); + buffer[1] = cpu_to_be64(OWNER_NO_OWNER); + snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP, + global_address(dice, GLOBAL_OWNER), + buffer, 2 * 8, FW_QUIET | + FW_FIXED_GENERATION | dice->owner_generation); kfree(buffer); @@ -270,67 +226,32 @@ static void dice_owner_clear(struct dice *dice) static int dice_enable_set(struct dice *dice) { - struct fw_device *device = fw_parent_device(dice->unit); __be32 value; - int rcode, err, errors = 0; + int err; value = cpu_to_be32(1); - for (;;) { - rcode = fw_run_transaction(device->card, - TCODE_WRITE_QUADLET_REQUEST, - device->node_id, - dice->owner_generation, - device->max_speed, - global_address(dice, GLOBAL_ENABLE), - &value, 4); - if (rcode == RCODE_COMPLETE) { - dice->global_enabled = true; - err = 0; - break; - } - if (rcode == RCODE_GENERATION) { - err = -EAGAIN; - break; - } - if (rcode_is_permanent_error(rcode) || ++errors >= 3) { - dev_err(&dice->unit->device, - "device enabling failed: %s\n", - fw_rcode_string(rcode)); - err = -EIO; - break; - } - msleep(20); - } + err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, + global_address(dice, GLOBAL_ENABLE), + &value, 4, + FW_FIXED_GENERATION | dice->owner_generation); + if (err < 0) + return err; - return err; + dice->global_enabled = true; + + return 0; } static void dice_enable_clear(struct dice *dice) { - struct fw_device *device = fw_parent_device(dice->unit); __be32 value; - int rcode, errors = 0; value = 0; - for (;;) { - rcode = fw_run_transaction(device->card, - TCODE_WRITE_QUADLET_REQUEST, - device->node_id, - dice->owner_generation, - device->max_speed, - global_address(dice, GLOBAL_ENABLE), - &value, 4); - if (rcode == RCODE_COMPLETE || - rcode == RCODE_GENERATION) - break; - if (rcode_is_permanent_error(rcode) || ++errors >= 3) { - dev_err(&dice->unit->device, - "device disabling failed: %s\n", - fw_rcode_string(rcode)); - break; - } - msleep(20); - } + snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, + global_address(dice, GLOBAL_ENABLE), + &value, 4, FW_QUIET | + FW_FIXED_GENERATION | dice->owner_generation); + dice->global_enabled = false; } @@ -384,7 +305,7 @@ static int dice_open(struct snd_pcm_substream *substream) err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, global_address(dice, GLOBAL_CLOCK_SELECT), - &clock_sel, 4); + &clock_sel, 4, 0); if (err < 0) goto err_lock; rate_index = (be32_to_cpu(clock_sel) & CLOCK_RATE_MASK) @@ -396,7 +317,7 @@ static int dice_open(struct snd_pcm_substream *substream) err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, rx_address(dice, RX_NUMBER_AUDIO), - data, 2 * 4); + data, 2 * 4, 0); if (err < 0) goto err_lock; number_audio = be32_to_cpu(data[0]); @@ -488,7 +409,7 @@ static int dice_stream_start(struct dice *dice) err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, rx_address(dice, RX_ISOCHRONOUS), - &channel, 4); + &channel, 4, 0); if (err < 0) goto err_resources; } @@ -502,7 +423,7 @@ static int dice_stream_start(struct dice *dice) err_rx_channel: channel = cpu_to_be32((u32)-1); snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, - rx_address(dice, RX_ISOCHRONOUS), &channel, 4); + rx_address(dice, RX_ISOCHRONOUS), &channel, 4, 0); err_resources: fw_iso_resources_free(&dice->resources); error: @@ -528,7 +449,7 @@ static void dice_stream_stop(struct dice *dice) channel = cpu_to_be32((u32)-1); snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, - rx_address(dice, RX_ISOCHRONOUS), &channel, 4); + rx_address(dice, RX_ISOCHRONOUS), &channel, 4, 0); fw_iso_resources_free(&dice->resources); } @@ -880,7 +801,7 @@ static int dice_interface_check(struct fw_unit *unit) */ err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST, DICE_PRIVATE_SPACE, - pointers, sizeof(pointers)); + pointers, sizeof(pointers), 0); if (err < 0) return -ENODEV; for (i = 0; i < ARRAY_SIZE(pointers); ++i) { @@ -896,7 +817,7 @@ static int dice_interface_check(struct fw_unit *unit) err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, DICE_PRIVATE_SPACE + be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION, - &version, 4); + &version, 4, 0); if (err < 0) return -ENODEV; if ((version & cpu_to_be32(0xff000000)) != cpu_to_be32(0x01000000)) { @@ -915,7 +836,7 @@ static int dice_init_offsets(struct dice *dice) err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, DICE_PRIVATE_SPACE, - pointers, sizeof(pointers)); + pointers, sizeof(pointers), 0); if (err < 0) return err; @@ -939,7 +860,7 @@ static void dice_card_strings(struct dice *dice) BUILD_BUG_ON(NICK_NAME_SIZE < sizeof(card->shortname)); err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, global_address(dice, GLOBAL_NICK_NAME), - card->shortname, sizeof(card->shortname)); + card->shortname, sizeof(card->shortname), 0); if (err >= 0) { /* DICE strings are returned in "always-wrong" endianness */ BUILD_BUG_ON(sizeof(card->shortname) % 4 != 0); @@ -1015,14 +936,14 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, global_address(dice, GLOBAL_CLOCK_SELECT), - &clock_sel, 4); + &clock_sel, 4, 0); if (err < 0) goto error; clock_sel &= cpu_to_be32(~CLOCK_SOURCE_MASK); clock_sel |= cpu_to_be32(CLOCK_SOURCE_ARX1); err = snd_fw_transaction(unit, TCODE_WRITE_QUADLET_REQUEST, global_address(dice, GLOBAL_CLOCK_SELECT), - &clock_sel, 4); + &clock_sel, 4, 0); if (err < 0) goto error; diff --git a/sound/firewire/fcp.c b/sound/firewire/fcp.c index ec578b5..860c080 100644 --- a/sound/firewire/fcp.c +++ b/sound/firewire/fcp.c @@ -90,7 +90,7 @@ int fcp_avc_transaction(struct fw_unit *unit, : TCODE_WRITE_BLOCK_REQUEST; ret = snd_fw_transaction(t.unit, tcode, CSR_REGISTER_BASE + CSR_FCP_COMMAND, - (void *)command, command_size); + (void *)command, command_size, 0); if (ret < 0) break; diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c index 58a5afe..fd42e6b 100644 --- a/sound/firewire/isight.c +++ b/sound/firewire/isight.c @@ -217,7 +217,7 @@ static void isight_packet(struct fw_iso_context *context, u32 cycle, static int isight_connect(struct isight *isight) { - int ch, err, rcode, errors = 0; + int ch, err; __be32 value; retry_after_bus_reset: @@ -230,27 +230,19 @@ retry_after_bus_reset: } value = cpu_to_be32(ch | (isight->device->max_speed << SPEED_SHIFT)); - for (;;) { - rcode = fw_run_transaction( - isight->device->card, - TCODE_WRITE_QUADLET_REQUEST, - isight->device->node_id, - isight->resources.generation, - isight->device->max_speed, - isight->audio_base + REG_ISO_TX_CONFIG, - &value, 4); - if (rcode == RCODE_COMPLETE) { - return 0; - } else if (rcode == RCODE_GENERATION) { - fw_iso_resources_free(&isight->resources); - goto retry_after_bus_reset; - } else if (rcode_is_permanent_error(rcode) || ++errors >= 3) { - err = -EIO; - goto err_resources; - } - msleep(5); + err = snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST, + isight->audio_base + REG_ISO_TX_CONFIG, + &value, 4, FW_FIXED_GENERATION | + isight->resources.generation); + if (err == -EAGAIN) { + fw_iso_resources_free(&isight->resources); + goto retry_after_bus_reset; + } else if (err < 0) { + goto err_resources; } + return 0; + err_resources: fw_iso_resources_free(&isight->resources); error: @@ -315,17 +307,19 @@ static int isight_hw_params(struct snd_pcm_substream *substream, static int reg_read(struct isight *isight, int offset, __be32 *value) { return snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST, - isight->audio_base + offset, value, 4); + isight->audio_base + offset, value, 4, 0); } static int reg_write(struct isight *isight, int offset, __be32 value) { return snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST, - isight->audio_base + offset, &value, 4); + isight->audio_base + offset, &value, 4, 0); } static void isight_stop_streaming(struct isight *isight) { + __be32 value; + if (!isight->context) return; @@ -333,7 +327,10 @@ static void isight_stop_streaming(struct isight *isight) fw_iso_context_destroy(isight->context); isight->context = NULL; fw_iso_resources_free(&isight->resources); - reg_write(isight, REG_AUDIO_ENABLE, 0); + value = 0; + snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST, + isight->audio_base + REG_AUDIO_ENABLE, + &value, 4, FW_QUIET); } static int isight_hw_free(struct snd_pcm_substream *substream) diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c index 14eb414..7409edb 100644 --- a/sound/firewire/lib.c +++ b/sound/firewire/lib.c @@ -11,7 +11,7 @@ #include #include "lib.h" -#define ERROR_RETRY_DELAY_MS 5 +#define ERROR_RETRY_DELAY_MS 20 /** * snd_fw_transaction - send a request and wait for its completion @@ -20,6 +20,9 @@ * @offset: the address in the target's address space * @buffer: input/output data * @length: length of @buffer + * @flags: use %FW_FIXED_GENERATION and add the generation value to attempt the + * request only in that generation; use %FW_QUIET to suppress error + * messages * * Submits an asynchronous request to the target device, and waits for the * response. The node ID and the current generation are derived from @unit. @@ -27,14 +30,18 @@ * Returns zero on success, or a negative error code. */ int snd_fw_transaction(struct fw_unit *unit, int tcode, - u64 offset, void *buffer, size_t length) + u64 offset, void *buffer, size_t length, + unsigned int flags) { struct fw_device *device = fw_parent_device(unit); int generation, rcode, tries = 0; + generation = flags & FW_GENERATION_MASK; for (;;) { - generation = device->generation; - smp_rmb(); /* node_id vs. generation */ + if (!(flags & FW_FIXED_GENERATION)) { + generation = device->generation; + smp_rmb(); /* node_id vs. generation */ + } rcode = fw_run_transaction(device->card, tcode, device->node_id, generation, device->max_speed, offset, @@ -43,9 +50,14 @@ int snd_fw_transaction(struct fw_unit *unit, int tcode, if (rcode == RCODE_COMPLETE) return 0; + if (rcode == RCODE_GENERATION && (flags & FW_FIXED_GENERATION)) + return -EAGAIN; + if (rcode_is_permanent_error(rcode) || ++tries >= 3) { - dev_err(&unit->device, "transaction failed: %s\n", - fw_rcode_string(rcode)); + if (!(flags & FW_QUIET)) + dev_err(&unit->device, + "transaction failed: %s\n", + fw_rcode_string(rcode)); return -EIO; } diff --git a/sound/firewire/lib.h b/sound/firewire/lib.h index aef3014..02cfabc 100644 --- a/sound/firewire/lib.h +++ b/sound/firewire/lib.h @@ -6,8 +6,13 @@ struct fw_unit; +#define FW_GENERATION_MASK 0x00ff +#define FW_FIXED_GENERATION 0x0100 +#define FW_QUIET 0x0200 + int snd_fw_transaction(struct fw_unit *unit, int tcode, - u64 offset, void *buffer, size_t length); + u64 offset, void *buffer, size_t length, + unsigned int flags); /* returns true if retrying the transaction would not make sense */ static inline bool rcode_is_permanent_error(int rcode) diff --git a/sound/firewire/scs1x.c b/sound/firewire/scs1x.c index 505fc81..858023c 100644 --- a/sound/firewire/scs1x.c +++ b/sound/firewire/scs1x.c @@ -369,7 +369,7 @@ static int scs_init_hss_address(struct scs *scs) data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) | scs->hss_handler.offset); err = snd_fw_transaction(scs->unit, TCODE_WRITE_BLOCK_REQUEST, - HSS1394_ADDRESS, &data, 8); + HSS1394_ADDRESS, &data, 8, 0); if (err < 0) dev_err(&scs->unit->device, "HSS1394 communication failed\n"); @@ -455,12 +455,16 @@ err_card: static void scs_update(struct fw_unit *unit) { struct scs *scs = dev_get_drvdata(&unit->device); + int generation; __be64 data; data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) | scs->hss_handler.offset); + generation = fw_parent_device(unit)->generation; + smp_rmb(); /* node_id vs. generation */ snd_fw_transaction(scs->unit, TCODE_WRITE_BLOCK_REQUEST, - HSS1394_ADDRESS, &data, 8); + HSS1394_ADDRESS, &data, 8, + FW_FIXED_GENERATION | generation); } static void scs_remove(struct fw_unit *unit) diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c index 6a68caf..eb3f7dc 100644 --- a/sound/firewire/speakers.c +++ b/sound/firewire/speakers.c @@ -647,7 +647,7 @@ static u32 fwspk_read_firmware_version(struct fw_unit *unit) int err; err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, - OXFORD_FIRMWARE_ID_ADDRESS, &data, 4); + OXFORD_FIRMWARE_ID_ADDRESS, &data, 4, 0); return err >= 0 ? be32_to_cpu(data) : 0; } -- cgit v0.10.2 From eadce07faa8e71d8a0fc7501a5167fb999200225 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 4 Sep 2011 22:17:45 +0200 Subject: ALSA: dice: avoid superflous write at bus reset When a bus reset happens, the enable register is automatically cleared, so we do not need to clear it manually when stopping the stream. Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index 59d5ca4..cfa98a8 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -246,6 +246,9 @@ static void dice_enable_clear(struct dice *dice) { __be32 value; + if (!dice->global_enabled) + return; + value = 0; snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, global_address(dice, GLOBAL_ENABLE), @@ -1009,6 +1012,8 @@ static void dice_bus_reset(struct fw_unit *unit) * manner. */ amdtp_out_stream_pcm_abort(&dice->stream); + + dice->global_enabled = false; dice_stream_stop_packets(dice); dice_owner_update(dice); -- cgit v0.10.2 From 435a9be8bdb7422b82c194fad2d279ef436addc1 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 4 Sep 2011 22:17:51 +0200 Subject: ALSA: dice: remove 10s period length limit Since commit f2b3614cefb6 (Don't check DMA time-out too shortly), we need no longer to restrict the period length to less than 10 s. Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index cfa98a8..57ceb13 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -350,7 +350,7 @@ static int dice_open(struct snd_pcm_substream *substream) err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, - 5000, 8192000); + 5000, UINT_MAX); if (err < 0) goto err_lock; -- cgit v0.10.2 From 8709f1e4d68b0b3caf9783cf2463e5747943bff8 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Tue, 11 Oct 2011 17:51:16 +0200 Subject: ALSA: dice: remove superfluous field The pcm field was not actually used. Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index 57ceb13..2d198ae 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -43,7 +43,6 @@ struct dice { bool global_enabled; wait_queue_head_t hwdep_wait; u32 notification_bits; - struct snd_pcm_substream *pcm; struct fw_iso_resources resources; struct amdtp_out_stream stream; }; @@ -564,8 +563,7 @@ static int dice_create_pcm(struct dice *dice) return err; pcm->private_data = dice; strcpy(pcm->name, dice->card->shortname); - dice->pcm = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; - dice->pcm->ops = &ops; + pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->ops = &ops; return 0; } -- cgit v0.10.2 From a8c558f6a3eedfb9bfd7d9d82f9d00f2f807ce7c Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sat, 27 Aug 2011 20:05:15 +0200 Subject: ALSA: dice: fix locking Avoid a lock inversion between dice->mutex and pcm->open_mutex. Signed-off-by: Stefan Richter Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index 2d198ae..2d3a04e 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -981,12 +981,12 @@ static void dice_remove(struct fw_unit *unit) { struct dice *dice = dev_get_drvdata(&unit->device); - mutex_lock(&dice->mutex); - amdtp_out_stream_pcm_abort(&dice->stream); snd_card_disconnect(dice->card); + mutex_lock(&dice->mutex); + dice_stream_stop(dice); dice_owner_clear(dice); @@ -999,8 +999,6 @@ static void dice_bus_reset(struct fw_unit *unit) { struct dice *dice = dev_get_drvdata(&unit->device); - mutex_lock(&dice->mutex); - /* * On a bus reset, the DICE firmware disables streaming and then goes * off contemplating its own navel for hundreds of milliseconds before @@ -1011,6 +1009,8 @@ static void dice_bus_reset(struct fw_unit *unit) */ amdtp_out_stream_pcm_abort(&dice->stream); + mutex_lock(&dice->mutex); + dice->global_enabled = false; dice_stream_stop_packets(dice); -- cgit v0.10.2 From c5280e996fdd14ffacfbbdf8ce5ac7f913b101d2 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 16 Oct 2011 21:39:00 +0200 Subject: ALSA: dice: make amdtp_rates[] const Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index a09c3b3..5540f70 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -65,7 +65,7 @@ void amdtp_out_stream_destroy(struct amdtp_out_stream *s) } EXPORT_SYMBOL(amdtp_out_stream_destroy); -unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = { +const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = { [CIP_SFC_32000] = 8, [CIP_SFC_44100] = 8, [CIP_SFC_48000] = 8, diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index f3d03dd..839ebf8 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -103,7 +103,7 @@ void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s); unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s); void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s); -extern unsigned int amdtp_syt_intervals[CIP_SFC_COUNT]; +extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT]; static inline bool amdtp_out_stream_running(struct amdtp_out_stream *s) { -- cgit v0.10.2 From a0301998aeebad206fa7e1b77300c9961c8c3d12 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 4 Dec 2011 21:47:00 +0100 Subject: ALSA: dice: get clock capabilities In preparation for sample rate selection support, ensure that the driver knows about the device's clock capabilities. Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index 2d3a04e..06fef47 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -36,6 +36,7 @@ struct dice { struct mutex mutex; unsigned int global_offset; unsigned int rx_offset; + unsigned int clock_caps; struct fw_address_handler notification_handler; int owner_generation; int dev_lock_count; /* > 0 driver, < 0 userspace */ @@ -830,9 +831,10 @@ static int dice_interface_check(struct fw_unit *unit) return 0; } -static int dice_init_offsets(struct dice *dice) +static int dice_read_params(struct dice *dice) { __be32 pointers[6]; + __be32 value; int err; err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, @@ -844,6 +846,23 @@ static int dice_init_offsets(struct dice *dice) dice->global_offset = be32_to_cpu(pointers[0]) * 4; dice->rx_offset = be32_to_cpu(pointers[4]) * 4; + /* some very old firmwares don't tell about their clock support */ + if (be32_to_cpu(pointers[1]) * 4 >= GLOBAL_CLOCK_CAPABILITIES + 4) { + err = snd_fw_transaction( + dice->unit, TCODE_READ_QUADLET_REQUEST, + global_address(dice, GLOBAL_CLOCK_CAPABILITIES), + &value, 4, 0); + if (err < 0) + return err; + dice->clock_caps = be32_to_cpu(value); + } else { + /* this should be supported by any device */ + dice->clock_caps = CLOCK_CAP_RATE_44100 | + CLOCK_CAP_RATE_48000 | + CLOCK_CAP_SOURCE_ARX1 | + CLOCK_CAP_SOURCE_INTERNAL; + } + return 0; } @@ -905,7 +924,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) dice->unit = unit; init_waitqueue_head(&dice->hwdep_wait); - err = dice_init_offsets(dice); + err = dice_read_params(dice); if (err < 0) goto err_mutex; -- cgit v0.10.2 From 5ea4018e4321f24e8305ea8a8b0a9c3e270456ae Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 5 Dec 2011 22:09:42 +0100 Subject: ALSA: dice: allow notifications during initialization Reorganize the initialization order so that the driver can receive notifications earlier. Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index 06fef47..49b47ba 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -924,10 +924,6 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) dice->unit = unit; init_waitqueue_head(&dice->hwdep_wait); - err = dice_read_params(dice); - if (err < 0) - goto err_mutex; - dice->notification_handler.length = 4; dice->notification_handler.address_callback = dice_notification; dice->notification_handler.callback_data = dice; @@ -936,9 +932,17 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) if (err < 0) goto err_mutex; - err = fw_iso_resources_init(&dice->resources, unit); + err = dice_owner_set(dice); if (err < 0) goto err_notification_handler; + + err = dice_read_params(dice); + if (err < 0) + goto err_owner; + + err = fw_iso_resources_init(&dice->resources, unit); + if (err < 0) + goto err_owner; dice->resources.channels_mask = 0x00000000ffffffffuLL; err = amdtp_out_stream_init(&dice->stream, unit, @@ -946,10 +950,6 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) if (err < 0) goto err_resources; - err = dice_owner_set(dice); - if (err < 0) - goto err_stream; - card->private_free = dice_card_free; dice_card_strings(dice); @@ -983,10 +983,10 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) return 0; -err_stream: - amdtp_out_stream_destroy(&dice->stream); err_resources: fw_iso_resources_destroy(&dice->resources); +err_owner: + dice_owner_clear(dice); err_notification_handler: fw_core_remove_address_handler(&dice->notification_handler); err_mutex: -- cgit v0.10.2 From 15a75c8bed591dd23a3d221f5ccd91843c109670 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 4 Dec 2011 22:23:59 +0100 Subject: ALSA: dice: get rate-dependent parameters In preparation for sample rate selection support, read the stream parameters that might change when running at different sample rates. Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index 49b47ba..e6bba6d 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -6,10 +6,12 @@ */ #include +#include #include #include #include #include +#include #include #include #include @@ -37,11 +39,14 @@ struct dice { unsigned int global_offset; unsigned int rx_offset; unsigned int clock_caps; + unsigned int rx_channels[3]; + unsigned int rx_midi_ports[3]; struct fw_address_handler notification_handler; int owner_generation; int dev_lock_count; /* > 0 driver, < 0 userspace */ bool dev_lock_changed; bool global_enabled; + struct completion clock_accepted; wait_queue_head_t hwdep_wait; u32 notification_bits; struct fw_iso_resources resources; @@ -53,15 +58,23 @@ MODULE_AUTHOR("Clemens Ladisch "); MODULE_LICENSE("GPL v2"); static const unsigned int dice_rates[] = { + /* mode 0 */ [0] = 32000, [1] = 44100, [2] = 48000, + /* mode 1 */ [3] = 88200, [4] = 96000, + /* mode 2 */ [5] = 176400, [6] = 192000, }; +static unsigned int rate_index_to_mode(unsigned int rate_index) +{ + return ((int)rate_index - 1) / 2; +} + static void dice_lock_changed(struct dice *dice) { dice->dev_lock_changed = true; @@ -264,6 +277,7 @@ static void dice_notification(struct fw_card *card, struct fw_request *request, void *data, size_t length, void *callback_data) { struct dice *dice = callback_data; + u32 bits; unsigned long flags; if (tcode != TCODE_WRITE_QUADLET_REQUEST) { @@ -274,10 +288,17 @@ static void dice_notification(struct fw_card *card, struct fw_request *request, fw_send_response(card, request, RCODE_ADDRESS_ERROR); return; } + + bits = be32_to_cpup(data); + spin_lock_irqsave(&dice->lock, flags); - dice->notification_bits |= be32_to_cpup(data); + dice->notification_bits |= bits; spin_unlock_irqrestore(&dice->lock, flags); + fw_send_response(card, request, RCODE_COMPLETE); + + if (bits & NOTIFY_CLOCK_ACCEPTED) + complete(&dice->clock_accepted); wake_up(&dice->hwdep_wait); } @@ -457,6 +478,26 @@ static void dice_stream_stop(struct dice *dice) fw_iso_resources_free(&dice->resources); } +static int dice_change_rate(struct dice *dice, unsigned int clock_rate) +{ + __be32 value; + int err; + + INIT_COMPLETION(dice->clock_accepted); + + value = cpu_to_be32(clock_rate | CLOCK_SOURCE_ARX1); + err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, + global_address(dice, GLOBAL_CLOCK_SELECT), + &value, 4, 0); + if (err < 0) + return err; + + wait_for_completion_timeout(&dice->clock_accepted, + msecs_to_jiffies(100)); + + return 0; +} + static int dice_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { @@ -831,11 +872,51 @@ static int dice_interface_check(struct fw_unit *unit) return 0; } +static int highest_supported_mode_rate(struct dice *dice, unsigned int mode) +{ + int i; + + for (i = ARRAY_SIZE(dice_rates) - 1; i >= 0; --i) + if ((dice->clock_caps & (1 << i)) && + rate_index_to_mode(i) == mode) + return i; + + return -1; +} + +static int dice_read_mode_params(struct dice *dice, unsigned int mode) +{ + __be32 values[2]; + int rate_index, err; + + rate_index = highest_supported_mode_rate(dice, mode); + if (rate_index < 0) { + dice->rx_channels[mode] = 0; + dice->rx_midi_ports[mode] = 0; + return 0; + } + + err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT); + if (err < 0) + return err; + + err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, + rx_address(dice, RX_NUMBER_AUDIO), + values, 2 * 4, 0); + if (err < 0) + return err; + + dice->rx_channels[mode] = be32_to_cpu(values[0]); + dice->rx_midi_ports[mode] = be32_to_cpu(values[1]); + + return 0; +} + static int dice_read_params(struct dice *dice) { __be32 pointers[6]; __be32 value; - int err; + int mode, err; err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, DICE_PRIVATE_SPACE, @@ -863,6 +944,12 @@ static int dice_read_params(struct dice *dice) CLOCK_CAP_SOURCE_INTERNAL; } + for (mode = 2; mode >= 0; --mode) { + err = dice_read_mode_params(dice, mode); + if (err < 0) + return err; + } + return 0; } @@ -922,6 +1009,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) spin_lock_init(&dice->lock); mutex_init(&dice->mutex); dice->unit = unit; + init_completion(&dice->clock_accepted); init_waitqueue_head(&dice->hwdep_wait); dice->notification_handler.length = 4; -- cgit v0.10.2 From 4edeb831f32d17fba056eb752f7afc26a19674a0 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 4 Dec 2011 22:07:01 +0100 Subject: ALSA: dice: dynamic sample rate selection Instead of relying of some control panel application to configure some fixed sample rate, allow applications to set it automatically. Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index e6bba6d..61dd00c 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -70,6 +70,17 @@ static const unsigned int dice_rates[] = { [6] = 192000, }; +static unsigned int rate_to_index(unsigned int rate) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) + if (dice_rates[i] == rate) + return i; + + return 0; +} + static unsigned int rate_index_to_mode(unsigned int rate_index) { return ((int)rate_index - 1) / 2; @@ -302,6 +313,59 @@ static void dice_notification(struct fw_card *card, struct fw_request *request, wake_up(&dice->hwdep_wait); } +static int dice_rate_constraint(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct dice *dice = rule->private; + const struct snd_interval *channels = + hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *rate = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval allowed_rates = { + .min = UINT_MAX, .max = 0, .integer = 1 + }; + unsigned int i, mode; + + for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) { + mode = rate_index_to_mode(i); + if ((dice->clock_caps & (1 << i)) && + snd_interval_test(channels, dice->rx_channels[mode])) { + allowed_rates.min = min(allowed_rates.min, + dice_rates[i]); + allowed_rates.max = max(allowed_rates.max, + dice_rates[i]); + } + } + + return snd_interval_refine(rate, &allowed_rates); +} + +static int dice_channels_constraint(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct dice *dice = rule->private; + const struct snd_interval *rate = + hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval allowed_channels = { + .min = UINT_MAX, .max = 0, .integer = 1 + }; + unsigned int i, mode; + + for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) + if ((dice->clock_caps & (1 << i)) && + snd_interval_test(rate, dice_rates[i])) { + mode = rate_index_to_mode(i); + allowed_channels.min = min(allowed_channels.min, + dice->rx_channels[mode]); + allowed_channels.max = max(allowed_channels.max, + dice->rx_channels[mode]); + } + + return snd_interval_refine(channels, &allowed_channels); +} + static int dice_open(struct snd_pcm_substream *substream) { static const struct snd_pcm_hardware hardware = { @@ -311,6 +375,8 @@ static int dice_open(struct snd_pcm_substream *substream) SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER, .formats = AMDTP_OUT_PCM_FORMAT_BITS, + .channels_min = UINT_MAX, + .channels_max = 0, .buffer_bytes_max = 16 * 1024 * 1024, .period_bytes_min = 1, .period_bytes_max = UINT_MAX, @@ -319,53 +385,46 @@ static int dice_open(struct snd_pcm_substream *substream) }; struct dice *dice = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; - __be32 clock_sel, data[2]; - unsigned int rate_index, number_audio, number_midi; + unsigned int i; int err; err = dice_try_lock(dice); if (err < 0) goto error; - err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, - global_address(dice, GLOBAL_CLOCK_SELECT), - &clock_sel, 4, 0); - if (err < 0) - goto err_lock; - rate_index = (be32_to_cpu(clock_sel) & CLOCK_RATE_MASK) - >> CLOCK_RATE_SHIFT; - if (rate_index >= ARRAY_SIZE(dice_rates)) { - err = -ENXIO; - goto err_lock; - } - - err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, - rx_address(dice, RX_NUMBER_AUDIO), - data, 2 * 4, 0); - if (err < 0) - goto err_lock; - number_audio = be32_to_cpu(data[0]); - number_midi = be32_to_cpu(data[1]); - runtime->hw = hardware; - runtime->hw.rates = snd_pcm_rate_to_rate_bit(dice_rates[rate_index]); + for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) + if (dice->clock_caps & (1 << i)) + runtime->hw.rates |= + snd_pcm_rate_to_rate_bit(dice_rates[i]); snd_pcm_limit_hw_rates(runtime); - runtime->hw.channels_min = number_audio; - runtime->hw.channels_max = number_audio; + for (i = 0; i < 3; ++i) + if (dice->rx_channels[i]) { + runtime->hw.channels_min = min(runtime->hw.channels_min, + dice->rx_channels[i]); + runtime->hw.channels_max = max(runtime->hw.channels_max, + dice->rx_channels[i]); + } - amdtp_out_stream_set_parameters(&dice->stream, dice_rates[rate_index], - number_audio, number_midi); + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + dice_rate_constraint, dice, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (err < 0) + goto err_lock; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + dice_channels_constraint, dice, + SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + goto err_lock; err = snd_pcm_hw_constraint_step(runtime, 0, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE, - amdtp_syt_intervals[rate_index]); + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32); if (err < 0) goto err_lock; err = snd_pcm_hw_constraint_step(runtime, 0, - SNDRV_PCM_HW_PARAM_BUFFER_SIZE, - amdtp_syt_intervals[rate_index]); + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32); if (err < 0) goto err_lock; @@ -502,6 +561,7 @@ static int dice_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct dice *dice = substream->private_data; + unsigned int rate_index, mode; int err; mutex_lock(&dice->mutex); @@ -511,15 +571,22 @@ static int dice_hw_params(struct snd_pcm_substream *substream, err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) - goto error; + return err; + rate_index = rate_to_index(params_rate(hw_params)); + err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT); + if (err < 0) + return err; + + mode = rate_index_to_mode(rate_index); + amdtp_out_stream_set_parameters(&dice->stream, + params_rate(hw_params), + params_channels(hw_params), + dice->rx_midi_ports[mode]); amdtp_out_stream_set_pcm_format(&dice->stream, params_format(hw_params)); return 0; - -error: - return err; } static int dice_hw_free(struct snd_pcm_substream *substream) -- cgit v0.10.2 From 640d9b421d4d8c7593aa8647479a4c7c6fe0ca65 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 5 Jan 2012 22:16:24 +0100 Subject: ALSA: dice: check clock change timeout Output a warning if the wait for the clock change notification times out. Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index 61dd00c..3395c8b 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -551,8 +551,9 @@ static int dice_change_rate(struct dice *dice, unsigned int clock_rate) if (err < 0) return err; - wait_for_completion_timeout(&dice->clock_accepted, - msecs_to_jiffies(100)); + if (!wait_for_completion_timeout(&dice->clock_accepted, + msecs_to_jiffies(100))) + dev_warn(&dice->unit->device, "clock change timed out\n"); return 0; } -- cgit v0.10.2 From c614475b0ea9f7e6b3f76a46be315579bb899397 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 5 Jan 2012 22:36:08 +0100 Subject: ALSA: dice: add a proc file to show device information For easier debugging, add a proc file to show the device's capabilities and current status. Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index 3395c8b..25a9636 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -857,6 +858,249 @@ static int dice_create_hwdep(struct dice *dice) return 0; } +static int dice_proc_read_mem(struct dice *dice, void *buffer, + unsigned int offset_q, unsigned int quadlets) +{ + unsigned int i; + int err; + + err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, + DICE_PRIVATE_SPACE + 4 * offset_q, + buffer, 4 * quadlets, 0); + if (err < 0) + return err; + + for (i = 0; i < quadlets; ++i) + be32_to_cpus(&((u32 *)buffer)[i]); + + return 0; +} + +static const char *str_from_array(const char *const strs[], unsigned int count, + unsigned int i) +{ + if (i < count) + return strs[i]; + else + return "(unknown)"; +} + +static void dice_proc_fixup_string(char *s, unsigned int size) +{ + unsigned int i; + + for (i = 0; i < size; i += 4) + cpu_to_le32s((u32 *)(s + i)); + + for (i = 0; i < size - 2; ++i) { + if (s[i] == '\0') + return; + if (s[i] == '\\' && s[i + 1] == '\\') { + s[i + 2] = '\0'; + return; + } + } + s[size - 1] = '\0'; +} + +static void dice_proc_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + static const char *const section_names[5] = { + "global", "tx", "rx", "ext_sync", "unused2" + }; + static const char *const clock_sources[] = { + "aes1", "aes2", "aes3", "aes4", "aes", "adat", "tdif", + "wc", "arx1", "arx2", "arx3", "arx4", "internal" + }; + static const char *const rates[] = { + "32000", "44100", "48000", "88200", "96000", "176400", "192000", + "any low", "any mid", "any high", "none" + }; + struct dice *dice = entry->private_data; + u32 sections[ARRAY_SIZE(section_names) * 2]; + struct { + u32 number; + u32 size; + } tx_rx_header; + union { + struct { + u32 owner_hi, owner_lo; + u32 notification; + char nick_name[NICK_NAME_SIZE]; + u32 clock_select; + u32 enable; + u32 status; + u32 extended_status; + u32 sample_rate; + u32 version; + u32 clock_caps; + char clock_source_names[CLOCK_SOURCE_NAMES_SIZE]; + } global; + struct { + u32 iso; + u32 number_audio; + u32 number_midi; + u32 speed; + char names[TX_NAMES_SIZE]; + u32 ac3_caps; + u32 ac3_enable; + } tx; + struct { + u32 iso; + u32 seq_start; + u32 number_audio; + u32 number_midi; + char names[RX_NAMES_SIZE]; + u32 ac3_caps; + u32 ac3_enable; + } rx; + struct { + u32 clock_source; + u32 locked; + u32 rate; + u32 adat_user_data; + } ext_sync; + } buf; + unsigned int quadlets, stream, i; + + if (dice_proc_read_mem(dice, sections, 0, ARRAY_SIZE(sections)) < 0) + return; + snd_iprintf(buffer, "sections:\n"); + for (i = 0; i < ARRAY_SIZE(section_names); ++i) + snd_iprintf(buffer, " %s: offset %u, size %u\n", + section_names[i], + sections[i * 2], sections[i * 2 + 1]); + + quadlets = min_t(u32, sections[1], sizeof(buf.global) / 4); + if (dice_proc_read_mem(dice, &buf.global, sections[0], quadlets) < 0) + return; + snd_iprintf(buffer, "global:\n"); + snd_iprintf(buffer, " owner: %04x:%04x%08x\n", + buf.global.owner_hi >> 16, + buf.global.owner_hi & 0xffff, buf.global.owner_lo); + snd_iprintf(buffer, " notification: %08x\n", buf.global.notification); + dice_proc_fixup_string(buf.global.nick_name, NICK_NAME_SIZE); + snd_iprintf(buffer, " nick name: %s\n", buf.global.nick_name); + snd_iprintf(buffer, " clock select: %s %s\n", + str_from_array(clock_sources, ARRAY_SIZE(clock_sources), + buf.global.clock_select & CLOCK_SOURCE_MASK), + str_from_array(rates, ARRAY_SIZE(rates), + (buf.global.clock_select & CLOCK_RATE_MASK) + >> CLOCK_RATE_SHIFT)); + snd_iprintf(buffer, " enable: %u\n", buf.global.enable); + snd_iprintf(buffer, " status: %slocked %s\n", + buf.global.status & STATUS_SOURCE_LOCKED ? "" : "un", + str_from_array(rates, ARRAY_SIZE(rates), + (buf.global.status & + STATUS_NOMINAL_RATE_MASK) + >> CLOCK_RATE_SHIFT)); + snd_iprintf(buffer, " ext status: %08x\n", buf.global.extended_status); + snd_iprintf(buffer, " sample rate: %u\n", buf.global.sample_rate); + snd_iprintf(buffer, " version: %u.%u.%u.%u\n", + (buf.global.version >> 24) & 0xff, + (buf.global.version >> 16) & 0xff, + (buf.global.version >> 8) & 0xff, + (buf.global.version >> 0) & 0xff); + if (quadlets >= 90) { + snd_iprintf(buffer, " clock caps:"); + for (i = 0; i <= 6; ++i) + if (buf.global.clock_caps & (1 << i)) + snd_iprintf(buffer, " %s", rates[i]); + for (i = 0; i <= 12; ++i) + if (buf.global.clock_caps & (1 << (16 + i))) + snd_iprintf(buffer, " %s", clock_sources[i]); + snd_iprintf(buffer, "\n"); + dice_proc_fixup_string(buf.global.clock_source_names, + CLOCK_SOURCE_NAMES_SIZE); + snd_iprintf(buffer, " clock source names: %s\n", + buf.global.clock_source_names); + } + + if (dice_proc_read_mem(dice, &tx_rx_header, sections[2], 2) < 0) + return; + quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.tx)); + for (stream = 0; stream < tx_rx_header.number; ++stream) { + if (dice_proc_read_mem(dice, &buf.tx, sections[2] + 2 + + stream * tx_rx_header.size, + quadlets) < 0) + break; + snd_iprintf(buffer, "tx %u:\n", stream); + snd_iprintf(buffer, " iso channel: %d\n", (int)buf.tx.iso); + snd_iprintf(buffer, " audio channels: %u\n", + buf.tx.number_audio); + snd_iprintf(buffer, " midi ports: %u\n", buf.tx.number_midi); + snd_iprintf(buffer, " speed: S%u\n", 100u << buf.tx.speed); + if (quadlets >= 68) { + dice_proc_fixup_string(buf.tx.names, TX_NAMES_SIZE); + snd_iprintf(buffer, " names: %s\n", buf.tx.names); + } + if (quadlets >= 70) { + snd_iprintf(buffer, " ac3 caps: %08x\n", + buf.tx.ac3_caps); + snd_iprintf(buffer, " ac3 enable: %08x\n", + buf.tx.ac3_enable); + } + } + + if (dice_proc_read_mem(dice, &tx_rx_header, sections[4], 2) < 0) + return; + quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.rx)); + for (stream = 0; stream < tx_rx_header.number; ++stream) { + if (dice_proc_read_mem(dice, &buf.rx, sections[4] + 2 + + stream * tx_rx_header.size, + quadlets) < 0) + break; + snd_iprintf(buffer, "rx %u:\n", stream); + snd_iprintf(buffer, " iso channel: %d\n", (int)buf.rx.iso); + snd_iprintf(buffer, " sequence start: %u\n", + (int)buf.rx.seq_start); + snd_iprintf(buffer, " audio channels: %u\n", + buf.rx.number_audio); + snd_iprintf(buffer, " midi ports: %u\n", buf.rx.number_midi); + if (quadlets >= 68) { + dice_proc_fixup_string(buf.rx.names, RX_NAMES_SIZE); + snd_iprintf(buffer, " names: %s\n", buf.rx.names); + } + if (quadlets >= 70) { + snd_iprintf(buffer, " ac3 caps: %08x\n", + buf.rx.ac3_caps); + snd_iprintf(buffer, " ac3 enable: %08x\n", + buf.rx.ac3_enable); + } + } + + quadlets = min_t(u32, sections[7], sizeof(buf.ext_sync) / 4); + if (quadlets >= 4) { + if (dice_proc_read_mem(dice, &buf.ext_sync, + sections[6], 4) < 0) + return; + snd_iprintf(buffer, "ext status:\n"); + snd_iprintf(buffer, " clock source: %s\n", + str_from_array(clock_sources, + ARRAY_SIZE(clock_sources), + buf.ext_sync.clock_source)); + snd_iprintf(buffer, " locked: %u\n", buf.ext_sync.locked); + snd_iprintf(buffer, " rate: %s\n", + str_from_array(rates, ARRAY_SIZE(rates), + buf.ext_sync.rate)); + snd_iprintf(buffer, " adat user data: "); + if (buf.ext_sync.adat_user_data & ADAT_USER_DATA_NO_DATA) + snd_iprintf(buffer, "-\n"); + else + snd_iprintf(buffer, "%x\n", + buf.ext_sync.adat_user_data); + } +} + +static void dice_create_proc(struct dice *dice) +{ + struct snd_info_entry *entry; + + if (!snd_card_proc_new(dice->card, "dice", &entry)) + snd_info_set_text_ops(entry, dice, dice_proc_read); +} + static void dice_card_free(struct snd_card *card) { struct dice *dice = card->private_data; @@ -1131,6 +1375,8 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) if (err < 0) goto error; + dice_create_proc(dice); + err = snd_card_register(card); if (err < 0) goto error; -- cgit v0.10.2 From 61b8cf0222b256b4f793d99c8bdc9b216d067a76 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 8 Jan 2012 22:18:00 +0100 Subject: ALSA: dice: document quadlet alignment Doing accesses without quadlet alignment is a bad idea because the firmware's byte-swapping would garble the data; clarify this in the documentation. Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/dice-interface.h b/sound/firewire/dice-interface.h index af916b9..27b044f 100644 --- a/sound/firewire/dice-interface.h +++ b/sound/firewire/dice-interface.h @@ -7,9 +7,9 @@ /* * Generally, all registers can be read like memory, i.e., with quadlet read or - * block read transactions with any alignment or length. Writes are not - * allowed except where noted; quadlet-sized registers must be written with - * a quadlet write transaction. + * block read transactions with at least quadlet-aligned offset and length. + * Writes are not allowed except where noted; quadlet-sized registers must be + * written with a quadlet write transaction. * * All values are in big endian. The DICE firmware runs on a little-endian CPU * and just byte-swaps _all_ quadlets on the bus, so values without endianness -- cgit v0.10.2 From ed7e48264cfd3b000ab8dd100e6aa4c1447dd93a Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 22 Jan 2012 16:46:23 +0100 Subject: ALSA: dice: dice_proc_read: remove wrong typecast Remove a wrong typecast that resulted from a copy-and-paste error. Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index 25a9636..5f0f102 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -1053,8 +1053,7 @@ static void dice_proc_read(struct snd_info_entry *entry, break; snd_iprintf(buffer, "rx %u:\n", stream); snd_iprintf(buffer, " iso channel: %d\n", (int)buf.rx.iso); - snd_iprintf(buffer, " sequence start: %u\n", - (int)buf.rx.seq_start); + snd_iprintf(buffer, " sequence start: %u\n", buf.rx.seq_start); snd_iprintf(buffer, " audio channels: %u\n", buf.rx.number_audio); snd_iprintf(buffer, " midi ports: %u\n", buf.rx.number_midi); -- cgit v0.10.2 From a471fcde8c2c4b65f110bb4210af3513ee4deeb8 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 13 Feb 2012 21:55:13 +0100 Subject: ALSA: dice: fix detection of Weiss devices While most DICE devices keep TCAT's default category ID of 0x04, Weiss devices identify themselves with 0x00. Reported-by: Rolf Anderegg Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index 5f0f102..49d630b 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -1109,7 +1109,10 @@ static void dice_card_free(struct snd_card *card) mutex_destroy(&dice->mutex); } -#define DICE_CATEGORY_ID 0x04 +#define OUI_WEISS 0x001c6a + +#define DICE_CATEGORY_ID 0x04 +#define WEISS_CATEGORY_ID 0x00 static int dice_interface_check(struct fw_unit *unit) { @@ -1123,15 +1126,15 @@ static int dice_interface_check(struct fw_unit *unit) struct fw_device *device = fw_parent_device(unit); struct fw_csr_iterator it; int key, value, vendor = -1, model = -1, err; - unsigned int i; + unsigned int category, i; __be32 pointers[ARRAY_SIZE(min_values)]; __be32 version; /* * Check that GUID and unit directory are constructed according to DICE * rules, i.e., that the specifier ID is the GUID's OUI, and that the - * GUID chip ID consists of the 8-bit DICE category ID, the 10-bit - * product ID, and a 22-bit serial number. + * GUID chip ID consists of the 8-bit category ID, the 10-bit product + * ID, and a 22-bit serial number. */ fw_csr_iterator_init(&it, unit->directory); while (fw_csr_iterator_next(&it, &key, &value)) { @@ -1144,7 +1147,11 @@ static int dice_interface_check(struct fw_unit *unit) break; } } - if (device->config_rom[3] != ((vendor << 8) | DICE_CATEGORY_ID) || + if (vendor == OUI_WEISS) + category = WEISS_CATEGORY_ID; + else + category = DICE_CATEGORY_ID; + if (device->config_rom[3] != ((vendor << 8) | category) || device->config_rom[4] >> 22 != model) return -ENODEV; -- cgit v0.10.2 From b20be8de1b3972ccf9af72850b045214faa8d830 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Tue, 15 Oct 2013 20:26:05 +0200 Subject: ALSA: dice: restrict the driver to playback-only devices At the moment, this driver supports only playback, while FFADO supports (only) full-duplex devices. So, prevent conflicts by not claiming devices that would be better handled by FFADO. Signed-off-by: Clemens Ladisch diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index 9153309..b3e274f 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -12,14 +12,16 @@ config SND_FIREWIRE_LIB depends on SND_PCM config SND_DICE - tristate "DICE devices (EXPERIMENTAL)" + tristate "DICE-based DACs (EXPERIMENTAL)" select SND_HWDEP select SND_PCM select SND_FIREWIRE_LIB help - Say Y here to include support for many FireWire audio interfaces - based on the DICE chip family (DICE-II/Jr/Mini) from TC Applied - Technologies. + Say Y here to include support for many DACs based on the DICE + chip family (DICE-II/Jr/Mini) from TC Applied Technologies. + + At the moment, this driver supports playback only. If you + want to use devices that support capturing, use FFADO instead. To compile this driver as a module, choose M here: the module will be called snd-dice. diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index 49d630b..6feee66 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -1128,6 +1128,7 @@ static int dice_interface_check(struct fw_unit *unit) int key, value, vendor = -1, model = -1, err; unsigned int category, i; __be32 pointers[ARRAY_SIZE(min_values)]; + __be32 tx_data[4]; __be32 version; /* @@ -1171,6 +1172,14 @@ static int dice_interface_check(struct fw_unit *unit) return -ENODEV; } + /* We support playback only. Let capture devices be handled by FFADO. */ + err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST, + DICE_PRIVATE_SPACE + + be32_to_cpu(pointers[2]) * 4, + tx_data, sizeof(tx_data), 0); + if (err < 0 || (tx_data[0] && tx_data[3])) + return -ENODEV; + /* * Check that the implemented DICE driver specification major version * number matches. -- cgit v0.10.2 From cfcff69af8447df8dd3c5b14349c3b84b8b569a5 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 20 Oct 2013 18:14:20 +0100 Subject: ASoC: si476x: Fix locking of core The conversion of the si476x to regmap removed locking of the core during register updates, allowing things like power state changes for the MFD to happen during a register update. Avoid this by taking the core lock in the DAI operations (which are the only things that do register updates) as we used to do in the open coded register I/O functions. Signed-off-by: Mark Brown Acked-by: Andrey Smirnov diff --git a/sound/soc/codecs/si476x.c b/sound/soc/codecs/si476x.c index 03645ce..52e7cb0 100644 --- a/sound/soc/codecs/si476x.c +++ b/sound/soc/codecs/si476x.c @@ -73,6 +73,7 @@ static const struct snd_soc_dapm_route si476x_dapm_routes[] = { static int si476x_codec_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { + struct si476x_core *core = i2c_mfd_cell_to_core(codec_dai->dev); int err; u16 format = 0; @@ -136,9 +137,14 @@ static int si476x_codec_set_dai_fmt(struct snd_soc_dai *codec_dai, return -EINVAL; } + si476x_core_lock(core); + err = snd_soc_update_bits(codec_dai->codec, SI476X_DIGITAL_IO_OUTPUT_FORMAT, SI476X_DIGITAL_IO_OUTPUT_FORMAT_MASK, format); + + si476x_core_unlock(core); + if (err < 0) { dev_err(codec_dai->codec->dev, "Failed to set output format\n"); return err; @@ -151,6 +157,7 @@ static int si476x_codec_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { + struct si476x_core *core = i2c_mfd_cell_to_core(dai->dev); int rate, width, err; rate = params_rate(params); @@ -176,11 +183,13 @@ static int si476x_codec_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + si476x_core_lock(core); + err = snd_soc_write(dai->codec, SI476X_DIGITAL_IO_OUTPUT_SAMPLE_RATE, rate); if (err < 0) { dev_err(dai->codec->dev, "Failed to set sample rate\n"); - return err; + goto out; } err = snd_soc_update_bits(dai->codec, SI476X_DIGITAL_IO_OUTPUT_FORMAT, @@ -189,10 +198,13 @@ static int si476x_codec_hw_params(struct snd_pcm_substream *substream, (width << SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT)); if (err < 0) { dev_err(dai->codec->dev, "Failed to set output width\n"); - return err; + goto out; } - return 0; +out: + si476x_core_unlock(core); + + return err; } static int si476x_codec_probe(struct snd_soc_codec *codec) -- cgit v0.10.2 From cc1a95d9f6423ced191b6f264e9657d98844ea0d Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Sun, 20 Oct 2013 23:03:31 -0400 Subject: ALSA: hda - add codec ID for Valleyview2 display codec This patch adds codec ID (0x80862882) and module alias for Valleyview2 display codec. Signed-off-by: Mengdong Lin Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 804adb8..fbd1859 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2779,6 +2779,7 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = { { .id = 0x80862806, .name = "PantherPoint HDMI", .patch = patch_generic_hdmi }, { .id = 0x80862807, .name = "Haswell HDMI", .patch = patch_generic_hdmi }, { .id = 0x80862880, .name = "CedarTrail HDMI", .patch = patch_generic_hdmi }, +{ .id = 0x80862882, .name = "Valleyview2 HDMI", .patch = patch_generic_hdmi }, { .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_generic_hdmi }, {} /* terminator */ }; @@ -2833,6 +2834,7 @@ MODULE_ALIAS("snd-hda-codec-id:80862805"); MODULE_ALIAS("snd-hda-codec-id:80862806"); MODULE_ALIAS("snd-hda-codec-id:80862807"); MODULE_ALIAS("snd-hda-codec-id:80862880"); +MODULE_ALIAS("snd-hda-codec-id:80862882"); MODULE_ALIAS("snd-hda-codec-id:808629fb"); MODULE_LICENSE("GPL"); -- cgit v0.10.2 From dd44bc6be05e4a948124053c8105cfa581177554 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 18 Oct 2013 15:29:01 +0300 Subject: perf evsel: Add missing decrement in id sample parsing The final array decrement in id sample parsing is missing, which may trip up the next person adding a sample format, so add it in. Signed-off-by: Adrian Hunter Acked-by: Jiri Olsa Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382099356-4918-5-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 291b18a..ec0cc1e 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1218,6 +1218,7 @@ static int perf_evsel__parse_id_sample(const struct perf_evsel *evsel, sample->pid = u.val32[0]; sample->tid = u.val32[1]; + array--; } return 0; -- cgit v0.10.2 From 4f624685f92719565981eb6f8d9195bb578522a3 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 18 Oct 2013 15:29:00 +0300 Subject: perf record: Improve write_output error message Improve the error message from write_output() to say what failed to write and give the error number. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382099356-4918-4-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 92ca541..d269dfa 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -88,7 +88,7 @@ static int write_output(struct perf_record *rec, void *buf, size_t size) int ret = write(rec->output, buf, size); if (ret < 0) { - pr_err("failed to write\n"); + pr_err("failed to write perf data, error: %m\n"); return -1; } -- cgit v0.10.2 From 8c16b649606ff9f6d742ad6f71c76fc0ee996c8e Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 18 Oct 2013 15:29:02 +0300 Subject: perf session: Add missing sample flush for piped events Piped events can be sorted so a final flush is needed. Add that and remove a redundant 'err = 0'. Signed-off-by: Adrian Hunter Reviewed-by: Jiri Olsa Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382099356-4918-6-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index d1e4495..d51e62d 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1263,7 +1263,9 @@ more: if (!session_done()) goto more; done: - err = 0; + /* do the final flush for ordered samples */ + self->ordered_samples.next_flush = ULLONG_MAX; + err = flush_sample_queue(self, tool); out_err: free(buf); perf_session__warn_about_errors(self, tool); @@ -1392,13 +1394,13 @@ more: "Processing events..."); } - err = 0; if (session_done()) - goto out_err; + goto out; if (file_pos < file_size) goto more; +out: /* do the final flush for ordered samples */ session->ordered_samples.next_flush = ULLONG_MAX; err = flush_sample_queue(session, tool); -- cgit v0.10.2 From 7db5952846dfa015d74e64b5e8656acac979490b Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 18 Oct 2013 15:29:03 +0300 Subject: perf session: Add missing members to perf_event__attr_swap() The perf_event__attr_swap() method needs to swap all members of struct perf_event_attr. Add missing ones. Signed-off-by: Adrian Hunter Acked-by: Jiri Olsa Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382099356-4918-7-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index d51e62d..d4559ca 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -453,6 +453,9 @@ void perf_event__attr_swap(struct perf_event_attr *attr) attr->bp_type = bswap_32(attr->bp_type); attr->bp_addr = bswap_64(attr->bp_addr); attr->bp_len = bswap_64(attr->bp_len); + attr->branch_sample_type = bswap_64(attr->branch_sample_type); + attr->sample_regs_user = bswap_64(attr->sample_regs_user); + attr->sample_stack_user = bswap_32(attr->sample_stack_user); swap_bitfield((u8 *) (&attr->read_format + 1), sizeof(u64)); } -- cgit v0.10.2 From 2af68ef50c6afc1632edc984e9c834545d90f597 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 18 Oct 2013 15:29:07 +0300 Subject: perf evlist: Fix 32-bit build error util/evlist.c: In function 'perf_evlist__mmap': util/evlist.c:772:2: error: format '%lu' expects argument of type 'long unsigned int', but argument 3 has type 'size_t' [-Werror=format] cc1: all warnings being treated as errors Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382099356-4918-11-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 0b5425b..07ba0a4 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -769,7 +769,7 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, evlist->overwrite = overwrite; evlist->mmap_len = perf_evlist__mmap_size(pages); - pr_debug("mmap size %luB\n", evlist->mmap_len); + pr_debug("mmap size %zuB\n", evlist->mmap_len); mask = evlist->mmap_len - page_size - 1; list_for_each_entry(evsel, &evlist->entries, node) { -- cgit v0.10.2 From 9402802a416c96b48b2bd9331c070ba2d7550b36 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 18 Oct 2013 15:29:08 +0300 Subject: perf tools: Fix test_on_exit for 32-bit build builtin-record.c:42:12: error: static declaration of 'on_exit' follows non-static declaration In file included from util/util.h:51:0, from builtin.h:4, from builtin-record.c:8: /usr/include/stdlib.h:536:12: note: previous declaration of 'on_exit' was here Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382099356-4918-12-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/config/feature-checks/test-on-exit.c b/tools/perf/config/feature-checks/test-on-exit.c index 8f64ed3..8e88b16 100644 --- a/tools/perf/config/feature-checks/test-on-exit.c +++ b/tools/perf/config/feature-checks/test-on-exit.c @@ -1,4 +1,5 @@ #include +#include static void exit_fn(int status, void *__data) { -- cgit v0.10.2 From 2100f778d44b9c25bd13d38c24a999e2caf1ae3d Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 18 Oct 2013 15:29:09 +0300 Subject: perf tools: Fix bench/numa.c for 32-bit build bench/numa.c: In function 'worker_thread': bench/numa.c:1123:20: error: comparison between signed and unsigned integer expressions [-Werror=sign-compare] bench/numa.c:1171:6: error: format '%lx' expects argument of type 'long unsigned int', but argument 5 has type 'u64' [-Werror=format] cc1: all warnings being treated as errors Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382099356-4918-13-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/bench/numa.c b/tools/perf/bench/numa.c index 64fa01c..d4c83c6 100644 --- a/tools/perf/bench/numa.c +++ b/tools/perf/bench/numa.c @@ -1120,7 +1120,7 @@ static void *worker_thread(void *__tdata) /* Check whether our max runtime timed out: */ if (g->p.nr_secs) { timersub(&stop, &start0, &diff); - if (diff.tv_sec >= g->p.nr_secs) { + if ((u32)diff.tv_sec >= g->p.nr_secs) { g->stop_work = true; break; } @@ -1167,7 +1167,7 @@ static void *worker_thread(void *__tdata) runtime_ns_max += diff.tv_usec * 1000; if (details >= 0) { - printf(" #%2d / %2d: %14.2lf nsecs/op [val: %016lx]\n", + printf(" #%2d / %2d: %14.2lf nsecs/op [val: %016"PRIx64"]\n", process_nr, thread_nr, runtime_ns_max / bytes_done, val); } fflush(stdout); -- cgit v0.10.2 From c83fa7f2549cb60bc4d429de31096546f659ce6d Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 18 Oct 2013 15:29:12 +0300 Subject: perf evlist: Fix perf_evlist__mmap comments Put the comments into the correct kernel-doc format and correct reference to perf_evlist__read_on_cpu() which should be perf_evlist__mmap_read(). Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382099356-4918-16-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 07ba0a4..acef94a 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -738,20 +738,17 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, return 0; } -/** perf_evlist__mmap - Create per cpu maps to receive events - * - * @evlist - list of events - * @pages - map length in pages - * @overwrite - overwrite older events? - * - * If overwrite is false the user needs to signal event consuption using: - * - * struct perf_mmap *m = &evlist->mmap[cpu]; - * unsigned int head = perf_mmap__read_head(m); +/** + * perf_evlist__mmap - Create mmaps to receive events. + * @evlist: list of events + * @pages: map length in pages + * @overwrite: overwrite older events? * - * perf_mmap__write_tail(m, head) + * If @overwrite is %false the user needs to signal event consumption using + * perf_mmap__write_tail(). Using perf_evlist__mmap_read() does this + * automatically. * - * Using perf_evlist__read_on_cpu does this automatically. + * Return: %0 on success, negative error code otherwise. */ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, bool overwrite) -- cgit v0.10.2 From 04e213148c434544e3aa88d227b5375fd375738b Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 18 Oct 2013 15:29:13 +0300 Subject: perf evlist: Factor out duplicated mmap code The same code is used in perf_evlist__mmap_per_cpu() and perf_evlist__mmap_per_thread(). Factor it out into a separate function perf_evlist__mmap_per_evsel(). Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382099356-4918-17-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index acef94a..85c4c80 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -608,9 +608,36 @@ static int __perf_evlist__mmap(struct perf_evlist *evlist, return 0; } -static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int mask) +static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx, + int prot, int mask, int cpu, int thread, + int *output) { struct perf_evsel *evsel; + + list_for_each_entry(evsel, &evlist->entries, node) { + int fd = FD(evsel, cpu, thread); + + if (*output == -1) { + *output = fd; + if (__perf_evlist__mmap(evlist, idx, prot, mask, + *output) < 0) + return -1; + } else { + if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, *output) != 0) + return -1; + } + + if ((evsel->attr.read_format & PERF_FORMAT_ID) && + perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0) + return -1; + } + + return 0; +} + +static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, + int mask) +{ int cpu, thread; int nr_cpus = cpu_map__nr(evlist->cpus); int nr_threads = thread_map__nr(evlist->threads); @@ -620,23 +647,9 @@ static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int m int output = -1; for (thread = 0; thread < nr_threads; thread++) { - list_for_each_entry(evsel, &evlist->entries, node) { - int fd = FD(evsel, cpu, thread); - - if (output == -1) { - output = fd; - if (__perf_evlist__mmap(evlist, cpu, - prot, mask, output) < 0) - goto out_unmap; - } else { - if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, output) != 0) - goto out_unmap; - } - - if ((evsel->attr.read_format & PERF_FORMAT_ID) && - perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0) - goto out_unmap; - } + if (perf_evlist__mmap_per_evsel(evlist, cpu, prot, mask, + cpu, thread, &output)) + goto out_unmap; } } @@ -648,9 +661,9 @@ out_unmap: return -1; } -static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, int mask) +static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, + int mask) { - struct perf_evsel *evsel; int thread; int nr_threads = thread_map__nr(evlist->threads); @@ -658,23 +671,9 @@ static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, in for (thread = 0; thread < nr_threads; thread++) { int output = -1; - list_for_each_entry(evsel, &evlist->entries, node) { - int fd = FD(evsel, 0, thread); - - if (output == -1) { - output = fd; - if (__perf_evlist__mmap(evlist, thread, - prot, mask, output) < 0) - goto out_unmap; - } else { - if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, output) != 0) - goto out_unmap; - } - - if ((evsel->attr.read_format & PERF_FORMAT_ID) && - perf_evlist__id_add_fd(evlist, evsel, 0, thread, fd) < 0) - goto out_unmap; - } + if (perf_evlist__mmap_per_evsel(evlist, thread, prot, mask, 0, + thread, &output)) + goto out_unmap; } return 0; -- cgit v0.10.2 From b55447a7301b12d509df4b2909ed38d125ad83d4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 21 Oct 2013 16:31:45 +0200 Subject: ALSA: hda - Fix typos in patch_hdmi.c ... which was introduced by the previous commit a4e9a38b, causing build errors without CONFIG_PROC_FS. Reported-by: Fengguang Wu Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index fbd1859..0f9b103 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -539,12 +539,12 @@ static void eld_proc_free(struct hdmi_spec_per_pin *per_pin) } } #else -static inline int snd_hda_eld_proc_new(struct hdmi_spec_per_pin *per_pin, - int index) +static inline int eld_proc_new(struct hdmi_spec_per_pin *per_pin, + int index) { return 0; } -static inline void snd_hda_eld_proc_free(struct hdmi_spec_per_pin *per_pin) +static inline void eld_proc_free(struct hdmi_spec_per_pin *per_pin) { } #endif -- cgit v0.10.2 From 243be3dd7c14454899e51334a6d66d29a41ae6ab Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 18 Oct 2013 15:29:14 +0300 Subject: perf script: Print addr by default for BTS The addr field is not displayed by default for hardware events, however for branch events it is the target of the branch so for BTS display it by default if it was recorded. Signed-off-by: Adrian Hunter Acked-by: David Ahern Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382099356-4918-18-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 9c333ff..ebb2b5f 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -409,7 +409,9 @@ static void print_sample_bts(union perf_event *event, printf(" => "); /* print branch_to information */ - if (PRINT_FIELD(ADDR)) + if (PRINT_FIELD(ADDR) || + ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) && + !output[attr->type].user_set)) print_sample_addr(event, sample, machine, thread, attr); printf("\n"); -- cgit v0.10.2 From f11cfc6f294dbd83b0d58037404df2bd16066238 Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Tue, 15 Oct 2013 23:07:27 +0300 Subject: perf list: Show error if tracepoints not available Tracepoints are not visible in "perf list" on Fedora 19 because regular users have no permission to /sys/kernel/debug by default. Show an error message so that the user knows about it instead of assuming that tracepoints are not supported on the system. Signed-off-by: Pekka Enberg Acked-by: Ingo Molnar Cc: Ingo Molnar Link: http://lkml.kernel.org/r/1381867647-8594-1-git-send-email-penberg@kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 9812531..c90e55c 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -998,8 +998,10 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob, char evt_path[MAXPATHLEN]; char dir_path[MAXPATHLEN]; - if (debugfs_valid_mountpoint(tracing_events_path)) + if (debugfs_valid_mountpoint(tracing_events_path)) { + printf(" [ Tracepoints not available: %s ]\n", strerror(errno)); return; + } sys_dir = opendir(tracing_events_path); if (!sys_dir) -- cgit v0.10.2 From e369517ce5f796945c6af047b4e8b1d650e03458 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 11 Oct 2013 14:15:36 +0900 Subject: perf callchain: Convert children list to rbtree Current collapse stage has a scalability problem which can be reproduced easily with a parallel kernel build. This is because it needs to traverse every children of callchains linearly during the collapse/merge stage. Converting it to a rbtree reduced the overhead significantly. On my 400MB perf.data file which recorded with make -j32 kernel build: $ time perf --no-pager report --stdio > /dev/null before: real 6m22.073s user 6m18.683s sys 0m0.706s after: real 0m20.780s user 0m19.962s sys 0m0.689s During the perf report the overhead on append_chain_children went down from 96.69% to 18.16%: - 18.16% perf perf [.] append_chain_children - append_chain_children - 77.48% append_chain_children + 69.79% merge_chain_branch - 22.96% append_chain_children + 67.44% merge_chain_branch + 30.15% append_chain_children + 2.41% callchain_append + 7.25% callchain_append + 12.26% callchain_append + 10.22% merge_chain_branch + 11.58% perf perf [.] dso__find_symbol + 8.02% perf perf [.] sort__comm_cmp + 5.48% perf libc-2.17.so [.] malloc_consolidate Reported-by: Linus Torvalds Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Linus Torvalds Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1381468543-25334-2-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 482f680..e3970e3 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -21,12 +21,6 @@ __thread struct callchain_cursor callchain_cursor; -#define chain_for_each_child(child, parent) \ - list_for_each_entry(child, &parent->children, siblings) - -#define chain_for_each_child_safe(child, next, parent) \ - list_for_each_entry_safe(child, next, &parent->children, siblings) - static void rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, enum chain_mode mode) @@ -71,10 +65,16 @@ static void __sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node, u64 min_hit) { + struct rb_node *n; struct callchain_node *child; - chain_for_each_child(child, node) + n = rb_first(&node->rb_root_in); + while (n) { + child = rb_entry(n, struct callchain_node, rb_node_in); + n = rb_next(n); + __sort_chain_flat(rb_root, child, min_hit); + } if (node->hit && node->hit >= min_hit) rb_insert_callchain(rb_root, node, CHAIN_FLAT); @@ -94,11 +94,16 @@ sort_chain_flat(struct rb_root *rb_root, struct callchain_root *root, static void __sort_chain_graph_abs(struct callchain_node *node, u64 min_hit) { + struct rb_node *n; struct callchain_node *child; node->rb_root = RB_ROOT; + n = rb_first(&node->rb_root_in); + + while (n) { + child = rb_entry(n, struct callchain_node, rb_node_in); + n = rb_next(n); - chain_for_each_child(child, node) { __sort_chain_graph_abs(child, min_hit); if (callchain_cumul_hits(child) >= min_hit) rb_insert_callchain(&node->rb_root, child, @@ -117,13 +122,18 @@ sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_root *chain_root, static void __sort_chain_graph_rel(struct callchain_node *node, double min_percent) { + struct rb_node *n; struct callchain_node *child; u64 min_hit; node->rb_root = RB_ROOT; min_hit = ceil(node->children_hit * min_percent); - chain_for_each_child(child, node) { + n = rb_first(&node->rb_root_in); + while (n) { + child = rb_entry(n, struct callchain_node, rb_node_in); + n = rb_next(n); + __sort_chain_graph_rel(child, min_percent); if (callchain_cumul_hits(child) >= min_hit) rb_insert_callchain(&node->rb_root, child, @@ -173,19 +183,26 @@ create_child(struct callchain_node *parent, bool inherit_children) return NULL; } new->parent = parent; - INIT_LIST_HEAD(&new->children); INIT_LIST_HEAD(&new->val); if (inherit_children) { - struct callchain_node *next; + struct rb_node *n; + struct callchain_node *child; + + new->rb_root_in = parent->rb_root_in; + parent->rb_root_in = RB_ROOT; - list_splice(&parent->children, &new->children); - INIT_LIST_HEAD(&parent->children); + n = rb_first(&new->rb_root_in); + while (n) { + child = rb_entry(n, struct callchain_node, rb_node_in); + child->parent = new; + n = rb_next(n); + } - chain_for_each_child(next, new) - next->parent = new; + /* make it the first child */ + rb_link_node(&new->rb_node_in, NULL, &parent->rb_root_in.rb_node); + rb_insert_color(&new->rb_node_in, &parent->rb_root_in); } - list_add_tail(&new->siblings, &parent->children); return new; } @@ -223,7 +240,7 @@ fill_node(struct callchain_node *node, struct callchain_cursor *cursor) } } -static void +static struct callchain_node * add_child(struct callchain_node *parent, struct callchain_cursor *cursor, u64 period) @@ -235,6 +252,19 @@ add_child(struct callchain_node *parent, new->children_hit = 0; new->hit = period; + return new; +} + +static s64 match_chain(struct callchain_cursor_node *node, + struct callchain_list *cnode) +{ + struct symbol *sym = node->sym; + + if (cnode->ms.sym && sym && + callchain_param.key == CCKEY_FUNCTION) + return cnode->ms.sym->start - sym->start; + else + return cnode->ip - node->ip; } /* @@ -272,9 +302,33 @@ split_add_child(struct callchain_node *parent, /* create a new child for the new branch if any */ if (idx_total < cursor->nr) { + struct callchain_node *first; + struct callchain_list *cnode; + struct callchain_cursor_node *node; + struct rb_node *p, **pp; + parent->hit = 0; - add_child(parent, cursor, period); parent->children_hit += period; + + node = callchain_cursor_current(cursor); + new = add_child(parent, cursor, period); + + /* + * This is second child since we moved parent's children + * to new (first) child above. + */ + p = parent->rb_root_in.rb_node; + first = rb_entry(p, struct callchain_node, rb_node_in); + cnode = list_first_entry(&first->val, struct callchain_list, + list); + + if (match_chain(node, cnode) < 0) + pp = &p->rb_left; + else + pp = &p->rb_right; + + rb_link_node(&new->rb_node_in, p, pp); + rb_insert_color(&new->rb_node_in, &parent->rb_root_in); } else { parent->hit = period; } @@ -291,16 +345,40 @@ append_chain_children(struct callchain_node *root, u64 period) { struct callchain_node *rnode; + struct callchain_cursor_node *node; + struct rb_node **p = &root->rb_root_in.rb_node; + struct rb_node *parent = NULL; + + node = callchain_cursor_current(cursor); + if (!node) + return; /* lookup in childrens */ - chain_for_each_child(rnode, root) { - unsigned int ret = append_chain(rnode, cursor, period); + while (*p) { + s64 ret; + struct callchain_list *cnode; - if (!ret) + parent = *p; + rnode = rb_entry(parent, struct callchain_node, rb_node_in); + cnode = list_first_entry(&rnode->val, struct callchain_list, + list); + + /* just check first entry */ + ret = match_chain(node, cnode); + if (ret == 0) { + append_chain(rnode, cursor, period); goto inc_children_hit; + } + + if (ret < 0) + p = &parent->rb_left; + else + p = &parent->rb_right; } /* nothing in children, add to the current node */ - add_child(root, cursor, period); + rnode = add_child(root, cursor, period); + rb_link_node(&rnode->rb_node_in, parent, p); + rb_insert_color(&rnode->rb_node_in, &root->rb_root_in); inc_children_hit: root->children_hit += period; @@ -325,28 +403,20 @@ append_chain(struct callchain_node *root, */ list_for_each_entry(cnode, &root->val, list) { struct callchain_cursor_node *node; - struct symbol *sym; node = callchain_cursor_current(cursor); if (!node) break; - sym = node->sym; - - if (cnode->ms.sym && sym && - callchain_param.key == CCKEY_FUNCTION) { - if (cnode->ms.sym->start != sym->start) - break; - } else if (cnode->ip != node->ip) + if (match_chain(node, cnode) != 0) break; - if (!found) - found = true; + found = true; callchain_cursor_advance(cursor); } - /* matches not, relay on the parent */ + /* matches not, relay no the parent */ if (!found) { cursor->curr = curr_snap; cursor->pos = start; @@ -395,8 +465,9 @@ merge_chain_branch(struct callchain_cursor *cursor, struct callchain_node *dst, struct callchain_node *src) { struct callchain_cursor_node **old_last = cursor->last; - struct callchain_node *child, *next_child; + struct callchain_node *child; struct callchain_list *list, *next_list; + struct rb_node *n; int old_pos = cursor->nr; int err = 0; @@ -412,12 +483,16 @@ merge_chain_branch(struct callchain_cursor *cursor, append_chain_children(dst, cursor, src->hit); } - chain_for_each_child_safe(child, next_child, src) { + n = rb_first(&src->rb_root_in); + while (n) { + child = container_of(n, struct callchain_node, rb_node_in); + n = rb_next(n); + rb_erase(&child->rb_node_in, &src->rb_root_in); + err = merge_chain_branch(cursor, dst, child); if (err) break; - list_del(&child->siblings); free(child); } diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 2b585bc..7bb3602 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -21,11 +21,11 @@ enum chain_order { struct callchain_node { struct callchain_node *parent; - struct list_head siblings; - struct list_head children; struct list_head val; - struct rb_node rb_node; /* to sort nodes in an rbtree */ - struct rb_root rb_root; /* sorted tree of children */ + struct rb_node rb_node_in; /* to insert nodes in an rbtree */ + struct rb_node rb_node; /* to sort nodes in an output tree */ + struct rb_root rb_root_in; /* input tree of children */ + struct rb_root rb_root; /* sorted output tree of children */ unsigned int val_nr; u64 hit; u64 children_hit; @@ -86,13 +86,12 @@ extern __thread struct callchain_cursor callchain_cursor; static inline void callchain_init(struct callchain_root *root) { - INIT_LIST_HEAD(&root->node.siblings); - INIT_LIST_HEAD(&root->node.children); INIT_LIST_HEAD(&root->node.val); root->node.parent = NULL; root->node.hit = 0; root->node.children_hit = 0; + root->node.rb_root_in = RB_ROOT; root->max_depth = 0; } -- cgit v0.10.2 From 09600e0f9ebb06235b852a646a3644b7d4a71aca Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 15 Oct 2013 11:01:56 +0900 Subject: perf tools: Compare dso's also when comparing symbols Linus reported that sometimes 'perf report -s symbol' exits without any message on TUI. David and Jiri found that it's because it failed to add a hist entry due to an invalid symbol length. It turns out that sorting by symbol (address) was broken since it only compares symbol addresses. The symbol address is a relative address within a dso thus just checking its address can result in merging unrelated symbols together. Fix it by checking dso before comparing symbol address. Reported-by: Linus Torvalds Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1381802517-18812-1-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 32c5637..1f9821d 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -182,9 +182,19 @@ static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r) static int64_t sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) { + int64_t ret; + if (!left->ms.sym && !right->ms.sym) return right->level - left->level; + /* + * comparing symbol address alone is not enough since it's a + * relative address within a dso. + */ + ret = sort__dso_cmp(left, right); + if (ret != 0) + return ret; + return _sort__sym_cmp(left->ms.sym, right->ms.sym); } -- cgit v0.10.2 From f5fc14124c5cefdd052a2b2a6a3f0ed531540113 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 15 Oct 2013 16:27:32 +0200 Subject: perf tools: Add data object to handle perf data file This patch is adding 'struct perf_data_file' object as a placeholder for all attributes regarding perf.data file handling. Changing perf_session__new to take it as an argument. The rest of the functionality will be added later to keep this change simple enough, because all the places using perf_session are changed now. Signed-off-by: Jiri Olsa Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Andi Kleen Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1381847254-28809-2-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 94f9a8e..95df683 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -28,6 +28,7 @@ #include "util/hist.h" #include "util/session.h" #include "util/tool.h" +#include "util/data.h" #include "arch/common.h" #include @@ -199,9 +200,13 @@ static int __cmd_annotate(struct perf_annotate *ann) struct perf_session *session; struct perf_evsel *pos; u64 total_nr_samples; + struct perf_data_file file = { + .path = input_name, + .mode = PERF_DATA_MODE_READ, + .force = ann->force, + }; - session = perf_session__new(input_name, O_RDONLY, - ann->force, false, &ann->tool); + session = perf_session__new(&file, false, &ann->tool); if (session == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c index 8140b7b..cfede86 100644 --- a/tools/perf/builtin-buildid-cache.c +++ b/tools/perf/builtin-buildid-cache.c @@ -221,8 +221,12 @@ static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused) static int build_id_cache__fprintf_missing(const char *filename, bool force, FILE *fp) { - struct perf_session *session = perf_session__new(filename, O_RDONLY, - force, false, NULL); + struct perf_data_file file = { + .path = filename, + .mode = PERF_DATA_MODE_READ, + .force = force, + }; + struct perf_session *session = perf_session__new(&file, false, NULL); if (session == NULL) return -1; diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index e74366a..0164c1c 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c @@ -15,6 +15,7 @@ #include "util/parse-options.h" #include "util/session.h" #include "util/symbol.h" +#include "util/data.h" static int sysfs__fprintf_build_id(FILE *fp) { @@ -52,6 +53,11 @@ static bool dso__skip_buildid(struct dso *dso, int with_hits) static int perf_session__list_build_ids(bool force, bool with_hits) { struct perf_session *session; + struct perf_data_file file = { + .path = input_name, + .mode = PERF_DATA_MODE_READ, + .force = force, + }; symbol__elf_init(); /* @@ -60,8 +66,7 @@ static int perf_session__list_build_ids(bool force, bool with_hits) if (filename__fprintf_build_id(input_name, stdout)) goto out; - session = perf_session__new(input_name, O_RDONLY, force, false, - &build_id__mark_dso_hit_ops); + session = perf_session__new(&file, false, &build_id__mark_dso_hit_ops); if (session == NULL) return -1; /* diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 2a78dc8..419d27d 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -16,6 +16,7 @@ #include "util/sort.h" #include "util/symbol.h" #include "util/util.h" +#include "util/data.h" #include #include @@ -42,7 +43,7 @@ struct diff_hpp_fmt { struct data__file { struct perf_session *session; - const char *file; + struct perf_data_file file; int idx; struct hists *hists; struct diff_hpp_fmt fmt[PERF_HPP_DIFF__MAX_INDEX]; @@ -601,7 +602,7 @@ static void data__fprintf(void) data__for_each_file(i, d) fprintf(stdout, "# [%d] %s %s\n", - d->idx, d->file, + d->idx, d->file.path, !d->idx ? "(Baseline)" : ""); fprintf(stdout, "#\n"); @@ -663,17 +664,16 @@ static int __cmd_diff(void) int ret = -EINVAL, i; data__for_each_file(i, d) { - d->session = perf_session__new(d->file, O_RDONLY, force, - false, &tool); + d->session = perf_session__new(&d->file, false, &tool); if (!d->session) { - pr_err("Failed to open %s\n", d->file); + pr_err("Failed to open %s\n", d->file.path); ret = -ENOMEM; goto out_delete; } ret = perf_session__process_events(d->session, &tool); if (ret) { - pr_err("Failed to process %s\n", d->file); + pr_err("Failed to process %s\n", d->file.path); goto out_delete; } @@ -1016,7 +1016,12 @@ static int data_init(int argc, const char **argv) return -ENOMEM; data__for_each_file(i, d) { - d->file = use_default ? defaults[i] : argv[i]; + struct perf_data_file *file = &d->file; + + file->path = use_default ? defaults[i] : argv[i]; + file->mode = PERF_DATA_MODE_READ, + file->force = force, + d->idx = i; } diff --git a/tools/perf/builtin-evlist.c b/tools/perf/builtin-evlist.c index 05bd9df..20b0f12 100644 --- a/tools/perf/builtin-evlist.c +++ b/tools/perf/builtin-evlist.c @@ -14,13 +14,18 @@ #include "util/parse-events.h" #include "util/parse-options.h" #include "util/session.h" +#include "util/data.h" static int __cmd_evlist(const char *file_name, struct perf_attr_details *details) { struct perf_session *session; struct perf_evsel *pos; + struct perf_data_file file = { + .path = file_name, + .mode = PERF_DATA_MODE_READ, + }; - session = perf_session__new(file_name, O_RDONLY, 0, false, NULL); + session = perf_session__new(&file, 0, NULL); if (session == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index f51a963..4aa6d78 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -15,6 +15,7 @@ #include "util/tool.h" #include "util/debug.h" #include "util/build-id.h" +#include "util/data.h" #include "util/parse-options.h" @@ -345,6 +346,10 @@ static int __cmd_inject(struct perf_inject *inject) { struct perf_session *session; int ret = -EINVAL; + struct perf_data_file file = { + .path = inject->input_name, + .mode = PERF_DATA_MODE_READ, + }; signal(SIGINT, sig_handler); @@ -355,7 +360,7 @@ static int __cmd_inject(struct perf_inject *inject) inject->tool.tracing_data = perf_event__repipe_tracing_data; } - session = perf_session__new(inject->input_name, O_RDONLY, false, true, &inject->tool); + session = perf_session__new(&file, true, &inject->tool); if (session == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 9b5f077..1126382 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -13,6 +13,7 @@ #include "util/parse-options.h" #include "util/trace-event.h" +#include "util/data.h" #include "util/debug.h" @@ -486,8 +487,12 @@ static int __cmd_kmem(void) { "kmem:kfree", perf_evsel__process_free_event, }, { "kmem:kmem_cache_free", perf_evsel__process_free_event, }, }; + struct perf_data_file file = { + .path = input_name, + .mode = PERF_DATA_MODE_READ, + }; - session = perf_session__new(input_name, O_RDONLY, 0, false, &perf_kmem); + session = perf_session__new(&file, false, &perf_kmem); if (session == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index cfa0b79..188bb29 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -17,6 +17,7 @@ #include "util/tool.h" #include "util/stat.h" #include "util/top.h" +#include "util/data.h" #include #include @@ -1215,10 +1216,13 @@ static int read_events(struct perf_kvm_stat *kvm) .comm = perf_event__process_comm, .ordered_samples = true, }; + struct perf_data_file file = { + .path = input_name, + .mode = PERF_DATA_MODE_READ, + }; kvm->tool = eops; - kvm->session = perf_session__new(kvm->file_name, O_RDONLY, 0, false, - &kvm->tool); + kvm->session = perf_session__new(&file, false, &kvm->tool); if (!kvm->session) { pr_err("Initializing perf session failed\n"); return -EINVAL; @@ -1450,6 +1454,9 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, "perf kvm stat live []", NULL }; + struct perf_data_file file = { + .mode = PERF_DATA_MODE_WRITE, + }; /* event handling */ @@ -1514,7 +1521,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, /* * perf session */ - kvm->session = perf_session__new(NULL, O_WRONLY, false, false, &kvm->tool); + kvm->session = perf_session__new(&file, false, &kvm->tool); if (kvm->session == NULL) { err = -ENOMEM; goto out; diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 6a9076f..33c7253 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -15,6 +15,7 @@ #include "util/debug.h" #include "util/session.h" #include "util/tool.h" +#include "util/data.h" #include #include @@ -853,8 +854,12 @@ static int __cmd_report(bool display_info) .comm = perf_event__process_comm, .ordered_samples = true, }; + struct perf_data_file file = { + .path = input_name, + .mode = PERF_DATA_MODE_READ, + }; - session = perf_session__new(input_name, O_RDONLY, 0, false, &eops); + session = perf_session__new(&file, false, &eops); if (!session) { pr_err("Initializing perf session failed\n"); return -ENOMEM; diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index 253133a..31c00f1 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c @@ -5,6 +5,7 @@ #include "util/trace-event.h" #include "util/tool.h" #include "util/session.h" +#include "util/data.h" #define MEM_OPERATION_LOAD "load" #define MEM_OPERATION_STORE "store" @@ -119,10 +120,14 @@ static int process_sample_event(struct perf_tool *tool, static int report_raw_events(struct perf_mem *mem) { + struct perf_data_file file = { + .path = input_name, + .mode = PERF_DATA_MODE_READ, + }; int err = -EINVAL; int ret; - struct perf_session *session = perf_session__new(input_name, O_RDONLY, - 0, false, &mem->tool); + struct perf_session *session = perf_session__new(&file, false, + &mem->tool); if (session == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index d269dfa..4ea46ff 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -24,6 +24,7 @@ #include "util/symbol.h" #include "util/cpumap.h" #include "util/thread_map.h" +#include "util/data.h" #include #include @@ -65,11 +66,10 @@ struct perf_record { struct perf_tool tool; struct perf_record_opts opts; u64 bytes_written; - const char *output_name; + struct perf_data_file file; struct perf_evlist *evlist; struct perf_session *session; const char *progname; - int output; int realtime_prio; bool no_buildid; bool no_buildid_cache; @@ -84,8 +84,10 @@ static void advance_output(struct perf_record *rec, size_t size) static int write_output(struct perf_record *rec, void *buf, size_t size) { + struct perf_data_file *file = &rec->file; + while (size) { - int ret = write(rec->output, buf, size); + int ret = write(file->fd, buf, size); if (ret < 0) { pr_err("failed to write perf data, error: %m\n"); @@ -248,13 +250,15 @@ out: static int process_buildids(struct perf_record *rec) { - u64 size = lseek(rec->output, 0, SEEK_CUR); + struct perf_data_file *file = &rec->file; + struct perf_session *session = rec->session; + u64 size = lseek(file->fd, 0, SEEK_CUR); if (size == 0) return 0; - rec->session->fd = rec->output; - return __perf_session__process_events(rec->session, rec->post_processing_offset, + session->fd = file->fd; + return __perf_session__process_events(session, rec->post_processing_offset, size - rec->post_processing_offset, size, &build_id__mark_dso_hit_ops); } @@ -262,17 +266,18 @@ static int process_buildids(struct perf_record *rec) static void perf_record__exit(int status, void *arg) { struct perf_record *rec = arg; + struct perf_data_file *file = &rec->file; if (status != 0) return; - if (!rec->opts.pipe_output) { + if (!file->is_pipe) { rec->session->header.data_size += rec->bytes_written; if (!rec->no_buildid) process_buildids(rec); perf_session__write_header(rec->session, rec->evlist, - rec->output, true); + file->fd, true); perf_session__delete(rec->session); perf_evlist__delete(rec->evlist); symbol__exit(); @@ -342,14 +347,15 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) { struct stat st; int flags; - int err, output, feat; + int err, feat; unsigned long waking = 0; const bool forks = argc > 0; struct machine *machine; struct perf_tool *tool = &rec->tool; struct perf_record_opts *opts = &rec->opts; struct perf_evlist *evsel_list = rec->evlist; - const char *output_name = rec->output_name; + struct perf_data_file *file = &rec->file; + const char *output_name = file->path; struct perf_session *session; bool disabled = false; @@ -363,13 +369,13 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) if (!output_name) { if (!fstat(STDOUT_FILENO, &st) && S_ISFIFO(st.st_mode)) - opts->pipe_output = true; + file->is_pipe = true; else - rec->output_name = output_name = "perf.data"; + file->path = output_name = "perf.data"; } if (output_name) { if (!strcmp(output_name, "-")) - opts->pipe_output = true; + file->is_pipe = true; else if (!stat(output_name, &st) && st.st_size) { char oldname[PATH_MAX]; snprintf(oldname, sizeof(oldname), "%s.old", @@ -381,19 +387,16 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) flags = O_CREAT|O_RDWR|O_TRUNC; - if (opts->pipe_output) - output = STDOUT_FILENO; + if (file->is_pipe) + file->fd = STDOUT_FILENO; else - output = open(output_name, flags, S_IRUSR | S_IWUSR); - if (output < 0) { + file->fd = open(output_name, flags, S_IRUSR | S_IWUSR); + if (file->fd < 0) { perror("failed to create output file"); return -1; } - rec->output = output; - - session = perf_session__new(output_name, O_WRONLY, - true, false, NULL); + session = perf_session__new(file, false, NULL); if (session == NULL) { pr_err("Not enough memory for reading perf file header\n"); return -1; @@ -415,7 +418,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) if (forks) { err = perf_evlist__prepare_workload(evsel_list, &opts->target, - argv, opts->pipe_output, + argv, file->is_pipe, true); if (err < 0) { pr_err("Couldn't run the workload!\n"); @@ -436,13 +439,13 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) */ on_exit(perf_record__exit, rec); - if (opts->pipe_output) { - err = perf_header__write_pipe(output); + if (file->is_pipe) { + err = perf_header__write_pipe(file->fd); if (err < 0) goto out_delete_session; } else { err = perf_session__write_header(session, evsel_list, - output, false); + file->fd, false); if (err < 0) goto out_delete_session; } @@ -455,11 +458,11 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) goto out_delete_session; } - rec->post_processing_offset = lseek(output, 0, SEEK_CUR); + rec->post_processing_offset = lseek(file->fd, 0, SEEK_CUR); machine = &session->machines.host; - if (opts->pipe_output) { + if (file->is_pipe) { err = perf_event__synthesize_attrs(tool, session, process_synthesized_event); if (err < 0) { @@ -476,7 +479,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) * return this more properly and also * propagate errors that now are calling die() */ - err = perf_event__synthesize_tracing_data(tool, output, evsel_list, + err = perf_event__synthesize_tracing_data(tool, file->fd, evsel_list, process_synthesized_event); if (err <= 0) { pr_err("Couldn't record tracing data.\n"); @@ -845,7 +848,7 @@ const struct option record_options[] = { OPT_STRING('C', "cpu", &record.opts.target.cpu_list, "cpu", "list of cpus to monitor"), OPT_U64('c', "count", &record.opts.user_interval, "event period to sample"), - OPT_STRING('o', "output", &record.output_name, "file", + OPT_STRING('o', "output", &record.file.path, "file", "output file name"), OPT_BOOLEAN('i', "no-inherit", &record.opts.no_inherit, "child tasks do not inherit counters"), diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 21b5c2f..60d7f8e 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -33,6 +33,7 @@ #include "util/thread.h" #include "util/sort.h" #include "util/hist.h" +#include "util/data.h" #include "arch/common.h" #include @@ -857,6 +858,9 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) "Don't show entries under that percent", parse_percent_limit), OPT_END() }; + struct perf_data_file file = { + .mode = PERF_DATA_MODE_READ, + }; perf_config(perf_report_config, &report); @@ -886,9 +890,11 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) perf_hpp__init(); } + file.path = input_name; + file.force = report.force; + repeat: - session = perf_session__new(input_name, O_RDONLY, - report.force, false, &report.tool); + session = perf_session__new(&file, false, &report.tool); if (session == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index d8c51b2..5a46b10 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1446,8 +1446,12 @@ static int perf_sched__read_events(struct perf_sched *sched, { "sched:sched_migrate_task", process_sched_migrate_task_event, }, }; struct perf_session *session; + struct perf_data_file file = { + .path = input_name, + .mode = PERF_DATA_MODE_READ, + }; - session = perf_session__new(input_name, O_RDONLY, 0, false, &sched->tool); + session = perf_session__new(&file, false, &sched->tool); if (session == NULL) { pr_debug("No Memory for session\n"); return -1; diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index ebb2b5f..f0c77a1 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -15,6 +15,7 @@ #include "util/evlist.h" #include "util/evsel.h" #include "util/sort.h" +#include "util/data.h" #include static char const *script_name; @@ -1115,10 +1116,14 @@ int find_scripts(char **scripts_array, char **scripts_path_array) char scripts_path[MAXPATHLEN], lang_path[MAXPATHLEN]; DIR *scripts_dir, *lang_dir; struct perf_session *session; + struct perf_data_file file = { + .path = input_name, + .mode = PERF_DATA_MODE_READ, + }; char *temp; int i = 0; - session = perf_session__new(input_name, O_RDONLY, 0, false, NULL); + session = perf_session__new(&file, false, NULL); if (!session) return -1; @@ -1319,12 +1324,17 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) "perf script [] [script-args]", NULL }; + struct perf_data_file file = { + .mode = PERF_DATA_MODE_READ, + }; setup_scripting(); argc = parse_options(argc, argv, options, script_usage, PARSE_OPT_STOP_AT_NON_OPTION); + file.path = input_name; + if (argc > 1 && !strncmp(argv[0], "rec", strlen("rec"))) { rec_script_path = get_script_path(argv[1], RECORD_SUFFIX); if (!rec_script_path) @@ -1488,8 +1498,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) if (!script_name) setup_pager(); - session = perf_session__new(input_name, O_RDONLY, 0, false, - &perf_script); + session = perf_session__new(&file, false, &perf_script); if (session == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index c2e0231..e11c61d 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -36,6 +36,7 @@ #include "util/session.h" #include "util/svghelper.h" #include "util/tool.h" +#include "util/data.h" #define SUPPORT_OLD_POWER_EVENTS 1 #define PWR_EVENT_EXIT -1 @@ -990,8 +991,13 @@ static int __cmd_timechart(const char *output_name) { "power:power_frequency", process_sample_power_frequency }, #endif }; - struct perf_session *session = perf_session__new(input_name, O_RDONLY, - 0, false, &perf_timechart); + struct perf_data_file file = { + .path = input_name, + .mode = PERF_DATA_MODE_READ, + }; + + struct perf_session *session = perf_session__new(&file, false, + &perf_timechart); int ret = -EINVAL; if (session == NULL) diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 65c49b2..752bebe 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -929,11 +929,15 @@ static int __cmd_top(struct perf_top *top) struct perf_record_opts *opts = &top->record_opts; pthread_t thread; int ret; + struct perf_data_file file = { + .mode = PERF_DATA_MODE_WRITE, + }; + /* * FIXME: perf_session__new should allow passing a O_MMAP, so that all this * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. */ - top->session = perf_session__new(NULL, O_WRONLY, false, false, NULL); + top->session = perf_session__new(&file, false, NULL); if (top->session == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index db959ac..fa620bc 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1834,7 +1834,10 @@ static int trace__replay(struct trace *trace) { "raw_syscalls:sys_exit", trace__sys_exit, }, { "probe:vfs_getname", trace__vfs_getname, }, }; - + struct perf_data_file file = { + .path = input_name, + .mode = PERF_DATA_MODE_READ, + }; struct perf_session *session; int err = -1; @@ -1857,8 +1860,7 @@ static int trace__replay(struct trace *trace) if (symbol__init() < 0) return -1; - session = perf_session__new(input_name, O_RDONLY, 0, false, - &trace->tool); + session = perf_session__new(&file, false, &trace->tool); if (session == NULL) return -ENOMEM; diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 84502e8..f61c230 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -220,7 +220,6 @@ struct perf_record_opts { bool no_delay; bool no_inherit; bool no_samples; - bool pipe_output; bool raw_samples; bool sample_address; bool sample_weight; diff --git a/tools/perf/util/data.h b/tools/perf/util/data.h new file mode 100644 index 0000000..ffa0186 --- /dev/null +++ b/tools/perf/util/data.h @@ -0,0 +1,29 @@ +#ifndef __PERF_DATA_H +#define __PERF_DATA_H + +#include + +enum perf_data_mode { + PERF_DATA_MODE_WRITE, + PERF_DATA_MODE_READ, +}; + +struct perf_data_file { + const char *path; + int fd; + bool is_pipe; + bool force; + enum perf_data_mode mode; +}; + +static inline bool perf_data_file__is_read(struct perf_data_file *file) +{ + return file->mode == PERF_DATA_MODE_READ; +} + +static inline bool perf_data_file__is_write(struct perf_data_file *file) +{ + return file->mode == PERF_DATA_MODE_WRITE; +} + +#endif /* __PERF_DATA_H */ diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index d4559ca..e3f63df 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -106,11 +106,11 @@ static void perf_session__destroy_kernel_maps(struct perf_session *self) machines__destroy_kernel_maps(&self->machines); } -struct perf_session *perf_session__new(const char *filename, int mode, - bool force, bool repipe, - struct perf_tool *tool) +struct perf_session *perf_session__new(struct perf_data_file *file, + bool repipe, struct perf_tool *tool) { struct perf_session *self; + const char *filename = file->path; struct stat st; size_t len; @@ -134,11 +134,11 @@ struct perf_session *perf_session__new(const char *filename, int mode, INIT_LIST_HEAD(&self->ordered_samples.to_free); machines__init(&self->machines); - if (mode == O_RDONLY) { - if (perf_session__open(self, force) < 0) + if (perf_data_file__is_read(file)) { + if (perf_session__open(self, file->force) < 0) goto out_delete; perf_session__set_id_hdr_size(self); - } else if (mode == O_WRONLY) { + } else if (perf_data_file__is_write(file)) { /* * In O_RDONLY mode this will be performed when reading the * kernel MMAP event, in perf_event__process_mmap(). diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 04bf737..f2f6251 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -7,6 +7,7 @@ #include "machine.h" #include "symbol.h" #include "thread.h" +#include "data.h" #include #include @@ -49,9 +50,8 @@ struct perf_session { struct perf_tool; -struct perf_session *perf_session__new(const char *filename, int mode, - bool force, bool repipe, - struct perf_tool *tool); +struct perf_session *perf_session__new(struct perf_data_file *file, + bool repipe, struct perf_tool *tool); void perf_session__delete(struct perf_session *session); void perf_event_header__bswap(struct perf_event_header *self); -- cgit v0.10.2 From 6a4d98d787b38a130a67e78b64182b419899623a Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 15 Oct 2013 16:27:33 +0200 Subject: perf tools: Add perf_data_file__open interface to data object Adding perf_data_file__open interface to data object to open the perf.data file for both read and write. Signed-off-by: Jiri Olsa Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Andi Kleen Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1381847254-28809-3-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index c873e03..326a26e 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -365,6 +365,7 @@ LIB_OBJS += $(OUTPUT)util/vdso.o LIB_OBJS += $(OUTPUT)util/stat.o LIB_OBJS += $(OUTPUT)util/record.o LIB_OBJS += $(OUTPUT)util/srcline.o +LIB_OBJS += $(OUTPUT)util/data.o LIB_OBJS += $(OUTPUT)ui/setup.o LIB_OBJS += $(OUTPUT)ui/helpline.o diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 4ea46ff..428e28f 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -345,8 +345,6 @@ out: static int __cmd_record(struct perf_record *rec, int argc, const char **argv) { - struct stat st; - int flags; int err, feat; unsigned long waking = 0; const bool forks = argc > 0; @@ -355,7 +353,6 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) struct perf_record_opts *opts = &rec->opts; struct perf_evlist *evsel_list = rec->evlist; struct perf_data_file *file = &rec->file; - const char *output_name = file->path; struct perf_session *session; bool disabled = false; @@ -367,35 +364,6 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) signal(SIGUSR1, sig_handler); signal(SIGTERM, sig_handler); - if (!output_name) { - if (!fstat(STDOUT_FILENO, &st) && S_ISFIFO(st.st_mode)) - file->is_pipe = true; - else - file->path = output_name = "perf.data"; - } - if (output_name) { - if (!strcmp(output_name, "-")) - file->is_pipe = true; - else if (!stat(output_name, &st) && st.st_size) { - char oldname[PATH_MAX]; - snprintf(oldname, sizeof(oldname), "%s.old", - output_name); - unlink(oldname); - rename(output_name, oldname); - } - } - - flags = O_CREAT|O_RDWR|O_TRUNC; - - if (file->is_pipe) - file->fd = STDOUT_FILENO; - else - file->fd = open(output_name, flags, S_IRUSR | S_IWUSR); - if (file->fd < 0) { - perror("failed to create output file"); - return -1; - } - session = perf_session__new(file, false, NULL); if (session == NULL) { pr_err("Not enough memory for reading perf file header\n"); @@ -586,7 +554,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) fprintf(stderr, "[ perf record: Captured and wrote %.3f MB %s (~%" PRIu64 " samples) ]\n", (double)rec->bytes_written / 1024.0 / 1024.0, - output_name, + file->path, rec->bytes_written / 24); return 0; diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 752bebe..d934f70 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -929,15 +929,8 @@ static int __cmd_top(struct perf_top *top) struct perf_record_opts *opts = &top->record_opts; pthread_t thread; int ret; - struct perf_data_file file = { - .mode = PERF_DATA_MODE_WRITE, - }; - /* - * FIXME: perf_session__new should allow passing a O_MMAP, so that all this - * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. - */ - top->session = perf_session__new(&file, false, NULL); + top->session = perf_session__new(NULL, false, NULL); if (top->session == NULL) return -ENOMEM; diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c new file mode 100644 index 0000000..7d09faf --- /dev/null +++ b/tools/perf/util/data.c @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include +#include + +#include "data.h" +#include "util.h" + +static bool check_pipe(struct perf_data_file *file) +{ + struct stat st; + bool is_pipe = false; + int fd = perf_data_file__is_read(file) ? + STDIN_FILENO : STDOUT_FILENO; + + if (!file->path) { + if (!fstat(fd, &st) && S_ISFIFO(st.st_mode)) + is_pipe = true; + } else { + if (!strcmp(file->path, "-")) + is_pipe = true; + } + + if (is_pipe) + file->fd = fd; + + return file->is_pipe = is_pipe; +} + +static int check_backup(struct perf_data_file *file) +{ + struct stat st; + + if (!stat(file->path, &st) && st.st_size) { + /* TODO check errors properly */ + char oldname[PATH_MAX]; + snprintf(oldname, sizeof(oldname), "%s.old", + file->path); + unlink(oldname); + rename(file->path, oldname); + } + + return 0; +} + +static int open_file_read(struct perf_data_file *file) +{ + struct stat st; + int fd; + + fd = open(file->path, O_RDONLY); + if (fd < 0) { + int err = errno; + + pr_err("failed to open %s: %s", file->path, strerror(err)); + if (err == ENOENT && !strcmp(file->path, "perf.data")) + pr_err(" (try 'perf record' first)"); + pr_err("\n"); + return -err; + } + + if (fstat(fd, &st) < 0) + goto out_close; + + if (!file->force && st.st_uid && (st.st_uid != geteuid())) { + pr_err("file %s not owned by current user or root\n", + file->path); + goto out_close; + } + + if (!st.st_size) { + pr_info("zero-sized file (%s), nothing to do!\n", + file->path); + goto out_close; + } + + file->size = st.st_size; + return fd; + + out_close: + close(fd); + return -1; +} + +static int open_file_write(struct perf_data_file *file) +{ + if (check_backup(file)) + return -1; + + return open(file->path, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR); +} + +static int open_file(struct perf_data_file *file) +{ + int fd; + + fd = perf_data_file__is_read(file) ? + open_file_read(file) : open_file_write(file); + + file->fd = fd; + return fd < 0 ? -1 : 0; +} + +int perf_data_file__open(struct perf_data_file *file) +{ + if (check_pipe(file)) + return 0; + + if (!file->path) + file->path = "perf.data"; + + return open_file(file); +} + +void perf_data_file__close(struct perf_data_file *file) +{ + close(file->fd); +} diff --git a/tools/perf/util/data.h b/tools/perf/util/data.h index ffa0186..d6c262e 100644 --- a/tools/perf/util/data.h +++ b/tools/perf/util/data.h @@ -13,6 +13,7 @@ struct perf_data_file { int fd; bool is_pipe; bool force; + unsigned long size; enum perf_data_mode mode; }; @@ -26,4 +27,7 @@ static inline bool perf_data_file__is_write(struct perf_data_file *file) return file->mode == PERF_DATA_MODE_WRITE; } +int perf_data_file__open(struct perf_data_file *file); +void perf_data_file__close(struct perf_data_file *file); + #endif /* __PERF_DATA_H */ diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index e3f63df..d857c18 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -16,73 +16,35 @@ #include "perf_regs.h" #include "vdso.h" -static int perf_session__open(struct perf_session *self, bool force) +static int perf_session__open(struct perf_session *self) { - struct stat input_stat; - - if (!strcmp(self->filename, "-")) { - self->fd_pipe = true; - self->fd = STDIN_FILENO; - + if (self->fd_pipe) { if (perf_session__read_header(self) < 0) pr_err("incompatible file format (rerun with -v to learn more)"); - return 0; } - self->fd = open(self->filename, O_RDONLY); - if (self->fd < 0) { - int err = errno; - - pr_err("failed to open %s: %s", self->filename, strerror(err)); - if (err == ENOENT && !strcmp(self->filename, "perf.data")) - pr_err(" (try 'perf record' first)"); - pr_err("\n"); - return -errno; - } - - if (fstat(self->fd, &input_stat) < 0) - goto out_close; - - if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) { - pr_err("file %s not owned by current user or root\n", - self->filename); - goto out_close; - } - - if (!input_stat.st_size) { - pr_info("zero-sized file (%s), nothing to do!\n", - self->filename); - goto out_close; - } - if (perf_session__read_header(self) < 0) { pr_err("incompatible file format (rerun with -v to learn more)"); - goto out_close; + return -1; } if (!perf_evlist__valid_sample_type(self->evlist)) { pr_err("non matching sample_type"); - goto out_close; + return -1; } if (!perf_evlist__valid_sample_id_all(self->evlist)) { pr_err("non matching sample_id_all"); - goto out_close; + return -1; } if (!perf_evlist__valid_read_format(self->evlist)) { pr_err("non matching read_format"); - goto out_close; + return -1; } - self->size = input_stat.st_size; return 0; - -out_close: - close(self->fd); - self->fd = -1; - return -1; } void perf_session__set_id_hdr_size(struct perf_session *session) @@ -110,35 +72,35 @@ struct perf_session *perf_session__new(struct perf_data_file *file, bool repipe, struct perf_tool *tool) { struct perf_session *self; - const char *filename = file->path; - struct stat st; - size_t len; - - if (!filename || !strlen(filename)) { - if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode)) - filename = "-"; - else - filename = "perf.data"; - } - len = strlen(filename); - self = zalloc(sizeof(*self) + len); - - if (self == NULL) + self = zalloc(sizeof(*self)); + if (!self) goto out; - memcpy(self->filename, filename, len); self->repipe = repipe; INIT_LIST_HEAD(&self->ordered_samples.samples); INIT_LIST_HEAD(&self->ordered_samples.sample_cache); INIT_LIST_HEAD(&self->ordered_samples.to_free); machines__init(&self->machines); - if (perf_data_file__is_read(file)) { - if (perf_session__open(self, file->force) < 0) + if (file) { + if (perf_data_file__open(file)) goto out_delete; - perf_session__set_id_hdr_size(self); - } else if (perf_data_file__is_write(file)) { + + self->fd = file->fd; + self->fd_pipe = file->is_pipe; + self->filename = file->path; + self->size = file->size; + + if (perf_data_file__is_read(file)) { + if (perf_session__open(self) < 0) + goto out_close; + + perf_session__set_id_hdr_size(self); + } + } + + if (!file || perf_data_file__is_write(file)) { /* * In O_RDONLY mode this will be performed when reading the * kernel MMAP event, in perf_event__process_mmap(). @@ -153,10 +115,13 @@ struct perf_session *perf_session__new(struct perf_data_file *file, tool->ordered_samples = false; } -out: return self; -out_delete: + + out_close: + perf_data_file__close(file); + out_delete: perf_session__delete(self); + out: return NULL; } diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index f2f6251..e1ca2d0 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -39,7 +39,7 @@ struct perf_session { bool fd_pipe; bool repipe; struct ordered_samples ordered_samples; - char filename[1]; + const char *filename; }; #define PRINT_IP_OPT_IP (1<<0) -- cgit v0.10.2 From cc9784bd9fa9d8e27fdea61142398cb85ce401a8 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 15 Oct 2013 16:27:34 +0200 Subject: perf session: Separating data file properties from session Removing 'fd, fd_pipe, filename, size' from struct perf_session and replacing them with struct perf_data_file object. Signed-off-by: Jiri Olsa Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Andi Kleen Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1381847254-28809-4-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 95df683..03cfa59 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -259,7 +259,7 @@ static int __cmd_annotate(struct perf_annotate *ann) } if (total_nr_samples == 0) { - ui__error("The %s file has no samples!\n", session->filename); + ui__error("The %s file has no samples!\n", file.path); goto out_delete; } diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index 0164c1c..ed3873b 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c @@ -73,7 +73,7 @@ static int perf_session__list_build_ids(bool force, bool with_hits) * in pipe-mode, the only way to get the buildids is to parse * the record stream. Buildids are stored as RECORD_HEADER_BUILD_ID */ - if (with_hits || session->fd_pipe) + if (with_hits || perf_data_file__is_pipe(&file)) perf_session__process_events(session, &build_id__mark_dso_hit_ops); perf_session__fprintf_dsos_buildid(session, stdout, dso__skip_buildid, with_hits); diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 428e28f..ab8d15e 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -257,7 +257,6 @@ static int process_buildids(struct perf_record *rec) if (size == 0) return 0; - session->fd = file->fd; return __perf_session__process_events(session, rec->post_processing_offset, size - rec->post_processing_offset, size, &build_id__mark_dso_hit_ops); diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 60d7f8e..fa68a36 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -368,8 +368,9 @@ static int perf_report__setup_sample_type(struct perf_report *rep) { struct perf_session *self = rep->session; u64 sample_type = perf_evlist__combined_sample_type(self->evlist); + bool is_pipe = perf_data_file__is_pipe(self->file); - if (!self->fd_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) { + if (!is_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) { if (sort__has_parent) { ui__error("Selected --sort parent, but no " "callchain data. Did you call " @@ -392,7 +393,7 @@ static int perf_report__setup_sample_type(struct perf_report *rep) } if (sort__mode == SORT_MODE__BRANCH) { - if (!self->fd_pipe && + if (!is_pipe && !(sample_type & PERF_SAMPLE_BRANCH_STACK)) { ui__error("Selected -b but no branch data. " "Did you call perf record without -b?\n"); @@ -488,6 +489,7 @@ static int __cmd_report(struct perf_report *rep) struct map *kernel_map; struct kmap *kernel_kmap; const char *help = "For a higher level overview, try: perf report --sort comm,dso"; + struct perf_data_file *file = session->file; signal(SIGINT, sig_handler); @@ -572,7 +574,7 @@ static int __cmd_report(struct perf_report *rep) return 0; if (nr_samples == 0) { - ui__error("The %s file has no samples!\n", session->filename); + ui__error("The %s file has no samples!\n", file->path); return 0; } diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index f0c77a1..27de606 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -1525,7 +1525,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) return -1; } - input = open(session->filename, O_RDONLY); /* input_name */ + input = open(file.path, O_RDONLY); /* input_name */ if (input < 0) { perror("failed to open file"); return -1; diff --git a/tools/perf/util/data.h b/tools/perf/util/data.h index d6c262e..8c2df80 100644 --- a/tools/perf/util/data.h +++ b/tools/perf/util/data.h @@ -27,6 +27,21 @@ static inline bool perf_data_file__is_write(struct perf_data_file *file) return file->mode == PERF_DATA_MODE_WRITE; } +static inline int perf_data_file__is_pipe(struct perf_data_file *file) +{ + return file->is_pipe; +} + +static inline int perf_data_file__fd(struct perf_data_file *file) +{ + return file->fd; +} + +static inline unsigned long perf_data_file__size(struct perf_data_file *file) +{ + return file->size; +} + int perf_data_file__open(struct perf_data_file *file); void perf_data_file__close(struct perf_data_file *file); diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index c3e5a3b..26d9520 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -22,6 +22,7 @@ #include "vdso.h" #include "strbuf.h" #include "build-id.h" +#include "data.h" static bool no_buildid_cache = false; @@ -2189,7 +2190,7 @@ int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full) { struct header_print_data hd; struct perf_header *header = &session->header; - int fd = session->fd; + int fd = perf_data_file__fd(session->file); hd.fp = fp; hd.full = full; @@ -2650,7 +2651,8 @@ static int perf_header__read_pipe(struct perf_session *session) struct perf_header *header = &session->header; struct perf_pipe_file_header f_header; - if (perf_file_header__read_pipe(&f_header, header, session->fd, + if (perf_file_header__read_pipe(&f_header, header, + perf_data_file__fd(session->file), session->repipe) < 0) { pr_debug("incompatible file format\n"); return -EINVAL; @@ -2751,18 +2753,19 @@ static int perf_evlist__prepare_tracepoint_events(struct perf_evlist *evlist, int perf_session__read_header(struct perf_session *session) { + struct perf_data_file *file = session->file; struct perf_header *header = &session->header; struct perf_file_header f_header; struct perf_file_attr f_attr; u64 f_id; int nr_attrs, nr_ids, i, j; - int fd = session->fd; + int fd = perf_data_file__fd(file); session->evlist = perf_evlist__new(); if (session->evlist == NULL) return -ENOMEM; - if (session->fd_pipe) + if (perf_data_file__is_pipe(file)) return perf_header__read_pipe(session); if (perf_file_header__read(&f_header, header, fd) < 0) @@ -2777,7 +2780,7 @@ int perf_session__read_header(struct perf_session *session) if (f_header.data.size == 0) { pr_warning("WARNING: The %s file's data size field is 0 which is unexpected.\n" "Was the 'perf record' command properly terminated?\n", - session->filename); + file->path); } nr_attrs = f_header.attrs.size / f_header.attr_size; @@ -2990,18 +2993,19 @@ int perf_event__process_tracing_data(struct perf_tool *tool __maybe_unused, struct perf_session *session) { ssize_t size_read, padding, size = event->tracing_data.size; - off_t offset = lseek(session->fd, 0, SEEK_CUR); + int fd = perf_data_file__fd(session->file); + off_t offset = lseek(fd, 0, SEEK_CUR); char buf[BUFSIZ]; /* setup for reading amidst mmap */ - lseek(session->fd, offset + sizeof(struct tracing_data_event), + lseek(fd, offset + sizeof(struct tracing_data_event), SEEK_SET); - size_read = trace_report(session->fd, &session->pevent, + size_read = trace_report(fd, &session->pevent, session->repipe); padding = PERF_ALIGN(size_read, sizeof(u64)) - size_read; - if (readn(session->fd, buf, padding) < 0) { + if (readn(fd, buf, padding) < 0) { pr_err("%s: reading input file", __func__); return -1; } diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index d857c18..19fc716 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -18,17 +18,16 @@ static int perf_session__open(struct perf_session *self) { - if (self->fd_pipe) { - if (perf_session__read_header(self) < 0) - pr_err("incompatible file format (rerun with -v to learn more)"); - return 0; - } + struct perf_data_file *file = self->file; if (perf_session__read_header(self) < 0) { pr_err("incompatible file format (rerun with -v to learn more)"); return -1; } + if (perf_data_file__is_pipe(file)) + return 0; + if (!perf_evlist__valid_sample_type(self->evlist)) { pr_err("non matching sample_type"); return -1; @@ -87,10 +86,7 @@ struct perf_session *perf_session__new(struct perf_data_file *file, if (perf_data_file__open(file)) goto out_delete; - self->fd = file->fd; - self->fd_pipe = file->is_pipe; - self->filename = file->path; - self->size = file->size; + self->file = file; if (perf_data_file__is_read(file)) { if (perf_session__open(self) < 0) @@ -158,7 +154,8 @@ void perf_session__delete(struct perf_session *self) perf_session__delete_threads(self); perf_session_env__delete(&self->header.env); machines__exit(&self->machines); - close(self->fd); + if (self->file) + perf_data_file__close(self->file); free(self); vdso__exit(); } @@ -1015,6 +1012,7 @@ static int perf_session_deliver_event(struct perf_session *session, static int perf_session__process_user_event(struct perf_session *session, union perf_event *event, struct perf_tool *tool, u64 file_offset) { + int fd = perf_data_file__fd(session->file); int err; dump_event(session, event, file_offset, NULL); @@ -1028,7 +1026,7 @@ static int perf_session__process_user_event(struct perf_session *session, union return err; case PERF_RECORD_HEADER_TRACING_DATA: /* setup for reading amidst mmap */ - lseek(session->fd, file_offset, SEEK_SET); + lseek(fd, file_offset, SEEK_SET); return tool->tracing_data(tool, event, session); case PERF_RECORD_HEADER_BUILD_ID: return tool->build_id(tool, event, session); @@ -1154,6 +1152,7 @@ volatile int session_done; static int __perf_session__process_pipe_events(struct perf_session *self, struct perf_tool *tool) { + int fd = perf_data_file__fd(self->file); union perf_event *event; uint32_t size, cur_size = 0; void *buf = NULL; @@ -1172,7 +1171,7 @@ static int __perf_session__process_pipe_events(struct perf_session *self, return -errno; more: event = buf; - err = readn(self->fd, event, sizeof(struct perf_event_header)); + err = readn(fd, event, sizeof(struct perf_event_header)); if (err <= 0) { if (err == 0) goto done; @@ -1204,7 +1203,7 @@ more: p += sizeof(struct perf_event_header); if (size - sizeof(struct perf_event_header)) { - err = readn(self->fd, p, size - sizeof(struct perf_event_header)); + err = readn(fd, p, size - sizeof(struct perf_event_header)); if (err <= 0) { if (err == 0) { pr_err("unexpected end of event stream\n"); @@ -1285,6 +1284,7 @@ int __perf_session__process_events(struct perf_session *session, u64 data_offset, u64 data_size, u64 file_size, struct perf_tool *tool) { + int fd = perf_data_file__fd(session->file); u64 head, page_offset, file_offset, file_pos, progress_next; int err, mmap_prot, mmap_flags, map_idx = 0; size_t mmap_size; @@ -1317,7 +1317,7 @@ int __perf_session__process_events(struct perf_session *session, mmap_flags = MAP_PRIVATE; } remap: - buf = mmap(NULL, mmap_size, mmap_prot, mmap_flags, session->fd, + buf = mmap(NULL, mmap_size, mmap_prot, mmap_flags, fd, file_offset); if (buf == MAP_FAILED) { pr_err("failed to mmap file\n"); @@ -1382,16 +1382,17 @@ out_err: int perf_session__process_events(struct perf_session *self, struct perf_tool *tool) { + u64 size = perf_data_file__size(self->file); int err; if (perf_session__register_idle_thread(self) == NULL) return -ENOMEM; - if (!self->fd_pipe) + if (!perf_data_file__is_pipe(self->file)) err = __perf_session__process_events(self, self->header.data_offset, self->header.data_size, - self->size, tool); + size, tool); else err = __perf_session__process_pipe_events(self, tool); @@ -1615,13 +1616,14 @@ int perf_session__cpu_bitmap(struct perf_session *session, void perf_session__fprintf_info(struct perf_session *session, FILE *fp, bool full) { + int fd = perf_data_file__fd(session->file); struct stat st; int ret; if (session == NULL || fp == NULL) return; - ret = fstat(session->fd, &st); + ret = fstat(fd, &st); if (ret == -1) return; diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index e1ca2d0..27c74d3 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -30,16 +30,13 @@ struct ordered_samples { struct perf_session { struct perf_header header; - unsigned long size; struct machines machines; struct perf_evlist *evlist; struct pevent *pevent; struct events_stats stats; - int fd; - bool fd_pipe; bool repipe; struct ordered_samples ordered_samples; - const char *filename; + struct perf_data_file *file; }; #define PRINT_IP_OPT_IP (1<<0) -- cgit v0.10.2 From 91e95617429cb272fd908b1928a1915b37b9655f Mon Sep 17 00:00:00 2001 From: Waiman Long Date: Fri, 18 Oct 2013 10:38:48 -0400 Subject: perf report: Add --max-stack option to limit callchain stack scan When callgraph data was included in the perf data file, it may take a long time to scan all those data and merge them together especially if the stored callchains are long and the perf data file itself is large, like a Gbyte or so. The callchain stack is currently limited to PERF_MAX_STACK_DEPTH (127). This is a large value. Usually the callgraph data that developers are most interested in are the first few levels, the rests are usually not looked at. This patch adds a new --max-stack option to perf-report to limit the depth of callchain stack data to look at to reduce the time it takes for perf-report to finish its processing. It trades the presence of trailing stack information with faster speed. The following table shows the elapsed time of doing perf-report on a perf.data file of size 985,531,828 bytes. --max_stack Elapsed Time Output data size ----------- ------------ ---------------- not set 88.0s 124,422,651 64 87.5s 116,303,213 32 87.2s 112,023,804 16 86.6s 94,326,380 8 59.9s 33,697,248 4 40.7s 10,116,637 -g none 27.1s 2,555,810 Signed-off-by: Waiman Long Acked-by: David Ahern Cc: Adrian Hunter Cc: Aswin Chandramouleeswaran Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Scott J Norton Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382107129-2010-4-git-send-email-Waiman.Long@hp.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index be5ad87..10a2798 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -141,6 +141,14 @@ OPTIONS Default: fractal,0.5,callee,function. +--max-stack:: + Set the stack depth limit when parsing the callchain, anything + beyond the specified depth will be ignored. This is a trade-off + between information loss and faster processing especially for + workloads that can have a very long callchain stack. + + Default: 127 + -G:: --inverted:: alias for inverted caller based call graph. diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index fa68a36..81addca 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -49,6 +49,7 @@ struct perf_report { bool show_threads; bool inverted_callchain; bool mem_mode; + int max_stack; struct perf_read_values show_threads_values; const char *pretty_printing_style; const char *cpu_list; @@ -90,7 +91,8 @@ static int perf_report__add_mem_hist_entry(struct perf_tool *tool, if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { err = machine__resolve_callchain(machine, evsel, al->thread, - sample, &parent, al); + sample, &parent, al, + rep->max_stack); if (err) return err; } @@ -181,7 +183,8 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool, if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { err = machine__resolve_callchain(machine, evsel, al->thread, - sample, &parent, al); + sample, &parent, al, + rep->max_stack); if (err) return err; } @@ -244,18 +247,21 @@ out: return err; } -static int perf_evsel__add_hist_entry(struct perf_evsel *evsel, +static int perf_evsel__add_hist_entry(struct perf_tool *tool, + struct perf_evsel *evsel, struct addr_location *al, struct perf_sample *sample, struct machine *machine) { + struct perf_report *rep = container_of(tool, struct perf_report, tool); struct symbol *parent = NULL; int err = 0; struct hist_entry *he; if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { err = machine__resolve_callchain(machine, evsel, al->thread, - sample, &parent, al); + sample, &parent, al, + rep->max_stack); if (err) return err; } @@ -332,7 +338,8 @@ static int process_sample_event(struct perf_tool *tool, if (al.map != NULL) al.map->dso->hit = 1; - ret = perf_evsel__add_hist_entry(evsel, &al, sample, machine); + ret = perf_evsel__add_hist_entry(tool, evsel, &al, sample, + machine); if (ret < 0) pr_debug("problem incrementing symbol period, skipping event\n"); } @@ -772,6 +779,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) .ordered_samples = true, .ordering_requires_timestamps = true, }, + .max_stack = PERF_MAX_STACK_DEPTH, .pretty_printing_style = "normal", }; const struct option options[] = { @@ -812,6 +820,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order", "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address). " "Default: fractal,0.5,callee,function", &parse_callchain_opt, callchain_default_opt), + OPT_INTEGER(0, "max-stack", &report.max_stack, + "Set the maximum stack depth when parsing the callchain, " + "anything beyond the specified depth will be ignored. " + "Default: " __stringify(PERF_MAX_STACK_DEPTH)), OPT_BOOLEAN('G', "inverted", &report.inverted_callchain, "alias for inverted call graph"), OPT_CALLBACK(0, "ignore-callees", NULL, "regex", diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index d934f70..112cb7d 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -770,7 +770,8 @@ static void perf_event__process_sample(struct perf_tool *tool, sample->callchain) { err = machine__resolve_callchain(machine, evsel, al.thread, sample, - &parent, &al); + &parent, &al, + PERF_MAX_STACK_DEPTH); if (err) return; } diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 6b861ae..ea93425 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1253,10 +1253,12 @@ static int machine__resolve_callchain_sample(struct machine *machine, struct thread *thread, struct ip_callchain *chain, struct symbol **parent, - struct addr_location *root_al) + struct addr_location *root_al, + int max_stack) { u8 cpumode = PERF_RECORD_MISC_USER; - unsigned int i; + int chain_nr = min(max_stack, (int)chain->nr); + int i; int err; callchain_cursor_reset(&callchain_cursor); @@ -1266,7 +1268,7 @@ static int machine__resolve_callchain_sample(struct machine *machine, return 0; } - for (i = 0; i < chain->nr; i++) { + for (i = 0; i < chain_nr; i++) { u64 ip; struct addr_location al; @@ -1338,12 +1340,14 @@ int machine__resolve_callchain(struct machine *machine, struct thread *thread, struct perf_sample *sample, struct symbol **parent, - struct addr_location *root_al) + struct addr_location *root_al, + int max_stack) { int ret; ret = machine__resolve_callchain_sample(machine, thread, - sample->callchain, parent, root_al); + sample->callchain, parent, + root_al, max_stack); if (ret) return ret; diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index d44c09b..4c1f5d5 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -92,7 +92,8 @@ int machine__resolve_callchain(struct machine *machine, struct thread *thread, struct perf_sample *sample, struct symbol **parent, - struct addr_location *root_al); + struct addr_location *root_al, + int max_stack); /* * Default guest kernel is defined by parameter --guestkallsyms diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 19fc716..854c5aa 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1512,7 +1512,8 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event, if (symbol_conf.use_callchain && sample->callchain) { if (machine__resolve_callchain(machine, evsel, al.thread, - sample, NULL, NULL) != 0) { + sample, NULL, NULL, + PERF_MAX_STACK_DEPTH) != 0) { if (verbose) error("Failed to resolve callchain. Skipping\n"); return; -- cgit v0.10.2 From 5dbb6e81d85e55ee2b4cf523c1738e16f63e5400 Mon Sep 17 00:00:00 2001 From: Waiman Long Date: Fri, 18 Oct 2013 10:38:49 -0400 Subject: perf top: Add --max-stack option to limit callchain stack scan When the callgraph function is enabled (-G), it may take a long time to scan all the stack data and merge them accordingly. This patch adds a new --max-stack option to perf-top to limit the depth of callchain stack data to look at to reduce the time it takes for perf-top to finish its processing. It reduces the amount of information provided to the user in exchange for faster speed. Signed-off-by: Waiman Long Acked-by: David Ahern Tested-by: Davidlohr Bueso Cc: Adrian Hunter Cc: Aswin Chandramouleeswaran Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Scott J Norton Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382107129-2010-5-git-send-email-Waiman.Long@hp.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index f65777c..c16a09e 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt @@ -158,6 +158,14 @@ Default is to monitor all CPUS. Default: fractal,0.5,callee. +--max-stack:: + Set the stack depth limit when parsing the callchain, anything + beyond the specified depth will be ignored. This is a trade-off + between information loss and faster processing especially for + workloads that can have a very long callchain stack. + + Default: 127 + --ignore-callees=:: Ignore callees of the function(s) matching the given regex. This has the effect of collecting the callers of each such diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 112cb7d..386d833 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -771,7 +771,7 @@ static void perf_event__process_sample(struct perf_tool *tool, err = machine__resolve_callchain(machine, evsel, al.thread, sample, &parent, &al, - PERF_MAX_STACK_DEPTH); + top->max_stack); if (err) return; } @@ -1048,10 +1048,11 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) .user_freq = UINT_MAX, .user_interval = ULLONG_MAX, .freq = 4000, /* 4 KHz */ - .target = { + .target = { .uses_mmap = true, }, }, + .max_stack = PERF_MAX_STACK_DEPTH, .sym_pcnt_filter = 5, }; struct perf_record_opts *opts = &top.record_opts; @@ -1110,6 +1111,9 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) OPT_CALLBACK_DEFAULT('G', "call-graph", &top.record_opts, "mode[,dump_size]", record_callchain_help, &parse_callchain_opt, "fp"), + OPT_INTEGER(0, "max-stack", &top.max_stack, + "Set the maximum stack depth when parsing the callchain. " + "Default: " __stringify(PERF_MAX_STACK_DEPTH)), OPT_CALLBACK(0, "ignore-callees", NULL, "regex", "ignore callees of these functions in call graphs", report_parse_ignore_callees_opt), diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index b554ffc..88cfeaf 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h @@ -24,6 +24,7 @@ struct perf_top { u64 exact_samples; u64 guest_us_samples, guest_kernel_samples; int print_entries, count_filter, delay_secs; + int max_stack; bool hide_kernel_symbols, hide_user_symbols, zero; bool use_tui, use_stdio; bool kptr_restrict_warned; -- cgit v0.10.2 From 833ba4b1ba653f279c78e8e3352527acce8946e6 Mon Sep 17 00:00:00 2001 From: "Chen, Gong" Date: Fri, 18 Oct 2013 14:27:51 -0700 Subject: ACPI, APEI, CPER: Fix status check during error printing Commit aaf9d93be71c: ACPI / APEI: fix error status check condition for CPER only catches condition check before print, but a similar check is needed during printing CPER error sections. Signed-off-by: Chen, Gong Reviewed-by: Borislav Petkov Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Tony Luck diff --git a/drivers/acpi/apei/cper.c b/drivers/acpi/apei/cper.c index 33dc6a0..f827f02 100644 --- a/drivers/acpi/apei/cper.c +++ b/drivers/acpi/apei/cper.c @@ -353,7 +353,7 @@ void apei_estatus_print(const char *pfx, cper_severity_str(severity)); data_len = estatus->data_length; gdata = (struct acpi_hest_generic_data *)(estatus + 1); - while (data_len > sizeof(*gdata)) { + while (data_len >= sizeof(*gdata)) { gedata_len = gdata->error_data_length; apei_estatus_print_section(pfx, gdata, sec_no); data_len -= gedata_len + sizeof(*gdata); -- cgit v0.10.2 From 88f074f4871a8c212b212b725e4dcdcdb09613c1 Mon Sep 17 00:00:00 2001 From: "Chen, Gong" Date: Fri, 18 Oct 2013 14:28:59 -0700 Subject: ACPI, CPER: Update cper info We have a lot of confusing names of functions and data structures in amongs the the error reporting code. In particular the "apei" prefix has been applied to many objects that are not part of APEI. Since we will be using these routines for extended error log reporting it will be clearer if we fix up the names first. Signed-off-by: Chen, Gong Acked-by: Borislav Petkov Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Tony Luck diff --git a/drivers/acpi/apei/apei-internal.h b/drivers/acpi/apei/apei-internal.h index f220d64..21ba34a 100644 --- a/drivers/acpi/apei/apei-internal.h +++ b/drivers/acpi/apei/apei-internal.h @@ -122,11 +122,11 @@ struct dentry; struct dentry *apei_get_debugfs_dir(void); #define apei_estatus_for_each_section(estatus, section) \ - for (section = (struct acpi_hest_generic_data *)(estatus + 1); \ + for (section = (struct acpi_generic_data *)(estatus + 1); \ (void *)section - (void *)estatus < estatus->data_length; \ section = (void *)(section+1) + section->error_data_length) -static inline u32 apei_estatus_len(struct acpi_hest_generic_status *estatus) +static inline u32 cper_estatus_len(struct acpi_generic_status *estatus) { if (estatus->raw_data_length) return estatus->raw_data_offset + \ @@ -135,10 +135,10 @@ static inline u32 apei_estatus_len(struct acpi_hest_generic_status *estatus) return sizeof(*estatus) + estatus->data_length; } -void apei_estatus_print(const char *pfx, - const struct acpi_hest_generic_status *estatus); -int apei_estatus_check_header(const struct acpi_hest_generic_status *estatus); -int apei_estatus_check(const struct acpi_hest_generic_status *estatus); +void cper_estatus_print(const char *pfx, + const struct acpi_generic_status *estatus); +int cper_estatus_check_header(const struct acpi_generic_status *estatus); +int cper_estatus_check(const struct acpi_generic_status *estatus); int apei_osc_setup(void); #endif diff --git a/drivers/acpi/apei/cper.c b/drivers/acpi/apei/cper.c index f827f02..eb5f6d6 100644 --- a/drivers/acpi/apei/cper.c +++ b/drivers/acpi/apei/cper.c @@ -5,7 +5,7 @@ * Author: Huang Ying * * CPER is the format used to describe platform hardware error by - * various APEI tables, such as ERST, BERT and HEST etc. + * various tables, such as ERST, BERT and HEST etc. * * For more information about CPER, please refer to Appendix N of UEFI * Specification version 2.3. @@ -73,7 +73,7 @@ static const char *cper_severity_str(unsigned int severity) * printed, with @pfx is printed at the beginning of each line. */ void cper_print_bits(const char *pfx, unsigned int bits, - const char *strs[], unsigned int strs_size) + const char * const strs[], unsigned int strs_size) { int i, len = 0; const char *str; @@ -98,32 +98,32 @@ void cper_print_bits(const char *pfx, unsigned int bits, printk("%s\n", buf); } -static const char *cper_proc_type_strs[] = { +static const char * const cper_proc_type_strs[] = { "IA32/X64", "IA64", }; -static const char *cper_proc_isa_strs[] = { +static const char * const cper_proc_isa_strs[] = { "IA32", "IA64", "X64", }; -static const char *cper_proc_error_type_strs[] = { +static const char * const cper_proc_error_type_strs[] = { "cache error", "TLB error", "bus error", "micro-architectural error", }; -static const char *cper_proc_op_strs[] = { +static const char * const cper_proc_op_strs[] = { "unknown or generic", "data read", "data write", "instruction execution", }; -static const char *cper_proc_flag_strs[] = { +static const char * const cper_proc_flag_strs[] = { "restartable", "precise IP", "overflow", @@ -248,7 +248,7 @@ static const char *cper_pcie_port_type_strs[] = { }; static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie, - const struct acpi_hest_generic_data *gdata) + const struct acpi_generic_data *gdata) { if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE) printk("%s""port_type: %d, %s\n", pfx, pcie->port_type, @@ -283,17 +283,17 @@ static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie, pfx, pcie->bridge.secondary_status, pcie->bridge.control); } -static const char *apei_estatus_section_flag_strs[] = { +static const char * const cper_estatus_section_flag_strs[] = { "primary", "containment warning", "reset", - "threshold exceeded", + "error threshold exceeded", "resource not accessible", "latent error", }; -static void apei_estatus_print_section( - const char *pfx, const struct acpi_hest_generic_data *gdata, int sec_no) +static void cper_estatus_print_section( + const char *pfx, const struct acpi_generic_data *gdata, int sec_no) { uuid_le *sec_type = (uuid_le *)gdata->section_type; __u16 severity; @@ -302,8 +302,8 @@ static void apei_estatus_print_section( printk("%s""section: %d, severity: %d, %s\n", pfx, sec_no, severity, cper_severity_str(severity)); printk("%s""flags: 0x%02x\n", pfx, gdata->flags); - cper_print_bits(pfx, gdata->flags, apei_estatus_section_flag_strs, - ARRAY_SIZE(apei_estatus_section_flag_strs)); + cper_print_bits(pfx, gdata->flags, cper_estatus_section_flag_strs, + ARRAY_SIZE(cper_estatus_section_flag_strs)); if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID) printk("%s""fru_id: %pUl\n", pfx, (uuid_le *)gdata->fru_id); if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) @@ -339,34 +339,34 @@ err_section_too_small: pr_err(FW_WARN "error section length is too small\n"); } -void apei_estatus_print(const char *pfx, - const struct acpi_hest_generic_status *estatus) +void cper_estatus_print(const char *pfx, + const struct acpi_generic_status *estatus) { - struct acpi_hest_generic_data *gdata; + struct acpi_generic_data *gdata; unsigned int data_len, gedata_len; int sec_no = 0; __u16 severity; - printk("%s""APEI generic hardware error status\n", pfx); + printk("%s""Generic Hardware Error Status\n", pfx); severity = estatus->error_severity; printk("%s""severity: %d, %s\n", pfx, severity, cper_severity_str(severity)); data_len = estatus->data_length; - gdata = (struct acpi_hest_generic_data *)(estatus + 1); + gdata = (struct acpi_generic_data *)(estatus + 1); while (data_len >= sizeof(*gdata)) { gedata_len = gdata->error_data_length; - apei_estatus_print_section(pfx, gdata, sec_no); + cper_estatus_print_section(pfx, gdata, sec_no); data_len -= gedata_len + sizeof(*gdata); gdata = (void *)(gdata + 1) + gedata_len; sec_no++; } } -EXPORT_SYMBOL_GPL(apei_estatus_print); +EXPORT_SYMBOL_GPL(cper_estatus_print); -int apei_estatus_check_header(const struct acpi_hest_generic_status *estatus) +int cper_estatus_check_header(const struct acpi_generic_status *estatus) { if (estatus->data_length && - estatus->data_length < sizeof(struct acpi_hest_generic_data)) + estatus->data_length < sizeof(struct acpi_generic_data)) return -EINVAL; if (estatus->raw_data_length && estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length) @@ -374,19 +374,19 @@ int apei_estatus_check_header(const struct acpi_hest_generic_status *estatus) return 0; } -EXPORT_SYMBOL_GPL(apei_estatus_check_header); +EXPORT_SYMBOL_GPL(cper_estatus_check_header); -int apei_estatus_check(const struct acpi_hest_generic_status *estatus) +int cper_estatus_check(const struct acpi_generic_status *estatus) { - struct acpi_hest_generic_data *gdata; + struct acpi_generic_data *gdata; unsigned int data_len, gedata_len; int rc; - rc = apei_estatus_check_header(estatus); + rc = cper_estatus_check_header(estatus); if (rc) return rc; data_len = estatus->data_length; - gdata = (struct acpi_hest_generic_data *)(estatus + 1); + gdata = (struct acpi_generic_data *)(estatus + 1); while (data_len >= sizeof(*gdata)) { gedata_len = gdata->error_data_length; if (gedata_len > data_len - sizeof(*gdata)) @@ -399,4 +399,4 @@ int apei_estatus_check(const struct acpi_hest_generic_status *estatus) return 0; } -EXPORT_SYMBOL_GPL(apei_estatus_check); +EXPORT_SYMBOL_GPL(cper_estatus_check); diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 8ec37bb..0db6e4f 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -75,13 +75,13 @@ #define GHES_ESTATUS_CACHE_LEN(estatus_len) \ (sizeof(struct ghes_estatus_cache) + (estatus_len)) #define GHES_ESTATUS_FROM_CACHE(estatus_cache) \ - ((struct acpi_hest_generic_status *) \ + ((struct acpi_generic_status *) \ ((struct ghes_estatus_cache *)(estatus_cache) + 1)) #define GHES_ESTATUS_NODE_LEN(estatus_len) \ (sizeof(struct ghes_estatus_node) + (estatus_len)) -#define GHES_ESTATUS_FROM_NODE(estatus_node) \ - ((struct acpi_hest_generic_status *) \ +#define GHES_ESTATUS_FROM_NODE(estatus_node) \ + ((struct acpi_generic_status *) \ ((struct ghes_estatus_node *)(estatus_node) + 1)) bool ghes_disable; @@ -378,17 +378,17 @@ static int ghes_read_estatus(struct ghes *ghes, int silent) ghes->flags |= GHES_TO_CLEAR; rc = -EIO; - len = apei_estatus_len(ghes->estatus); + len = cper_estatus_len(ghes->estatus); if (len < sizeof(*ghes->estatus)) goto err_read_block; if (len > ghes->generic->error_block_length) goto err_read_block; - if (apei_estatus_check_header(ghes->estatus)) + if (cper_estatus_check_header(ghes->estatus)) goto err_read_block; ghes_copy_tofrom_phys(ghes->estatus + 1, buf_paddr + sizeof(*ghes->estatus), len - sizeof(*ghes->estatus), 1); - if (apei_estatus_check(ghes->estatus)) + if (cper_estatus_check(ghes->estatus)) goto err_read_block; rc = 0; @@ -409,7 +409,7 @@ static void ghes_clear_estatus(struct ghes *ghes) ghes->flags &= ~GHES_TO_CLEAR; } -static void ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata, int sev) +static void ghes_handle_memory_failure(struct acpi_generic_data *gdata, int sev) { #ifdef CONFIG_ACPI_APEI_MEMORY_FAILURE unsigned long pfn; @@ -438,10 +438,10 @@ static void ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata, int } static void ghes_do_proc(struct ghes *ghes, - const struct acpi_hest_generic_status *estatus) + const struct acpi_generic_status *estatus) { int sev, sec_sev; - struct acpi_hest_generic_data *gdata; + struct acpi_generic_data *gdata; sev = ghes_severity(estatus->error_severity); apei_estatus_for_each_section(estatus, gdata) { @@ -496,7 +496,7 @@ static void ghes_do_proc(struct ghes *ghes, static void __ghes_print_estatus(const char *pfx, const struct acpi_hest_generic *generic, - const struct acpi_hest_generic_status *estatus) + const struct acpi_generic_status *estatus) { static atomic_t seqno; unsigned int curr_seqno; @@ -513,12 +513,12 @@ static void __ghes_print_estatus(const char *pfx, snprintf(pfx_seq, sizeof(pfx_seq), "%s{%u}" HW_ERR, pfx, curr_seqno); printk("%s""Hardware error from APEI Generic Hardware Error Source: %d\n", pfx_seq, generic->header.source_id); - apei_estatus_print(pfx_seq, estatus); + cper_estatus_print(pfx_seq, estatus); } static int ghes_print_estatus(const char *pfx, const struct acpi_hest_generic *generic, - const struct acpi_hest_generic_status *estatus) + const struct acpi_generic_status *estatus) { /* Not more than 2 messages every 5 seconds */ static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5*HZ, 2); @@ -540,15 +540,15 @@ static int ghes_print_estatus(const char *pfx, * GHES error status reporting throttle, to report more kinds of * errors, instead of just most frequently occurred errors. */ -static int ghes_estatus_cached(struct acpi_hest_generic_status *estatus) +static int ghes_estatus_cached(struct acpi_generic_status *estatus) { u32 len; int i, cached = 0; unsigned long long now; struct ghes_estatus_cache *cache; - struct acpi_hest_generic_status *cache_estatus; + struct acpi_generic_status *cache_estatus; - len = apei_estatus_len(estatus); + len = cper_estatus_len(estatus); rcu_read_lock(); for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) { cache = rcu_dereference(ghes_estatus_caches[i]); @@ -571,19 +571,19 @@ static int ghes_estatus_cached(struct acpi_hest_generic_status *estatus) static struct ghes_estatus_cache *ghes_estatus_cache_alloc( struct acpi_hest_generic *generic, - struct acpi_hest_generic_status *estatus) + struct acpi_generic_status *estatus) { int alloced; u32 len, cache_len; struct ghes_estatus_cache *cache; - struct acpi_hest_generic_status *cache_estatus; + struct acpi_generic_status *cache_estatus; alloced = atomic_add_return(1, &ghes_estatus_cache_alloced); if (alloced > GHES_ESTATUS_CACHE_ALLOCED_MAX) { atomic_dec(&ghes_estatus_cache_alloced); return NULL; } - len = apei_estatus_len(estatus); + len = cper_estatus_len(estatus); cache_len = GHES_ESTATUS_CACHE_LEN(len); cache = (void *)gen_pool_alloc(ghes_estatus_pool, cache_len); if (!cache) { @@ -603,7 +603,7 @@ static void ghes_estatus_cache_free(struct ghes_estatus_cache *cache) { u32 len; - len = apei_estatus_len(GHES_ESTATUS_FROM_CACHE(cache)); + len = cper_estatus_len(GHES_ESTATUS_FROM_CACHE(cache)); len = GHES_ESTATUS_CACHE_LEN(len); gen_pool_free(ghes_estatus_pool, (unsigned long)cache, len); atomic_dec(&ghes_estatus_cache_alloced); @@ -619,7 +619,7 @@ static void ghes_estatus_cache_rcu_free(struct rcu_head *head) static void ghes_estatus_cache_add( struct acpi_hest_generic *generic, - struct acpi_hest_generic_status *estatus) + struct acpi_generic_status *estatus) { int i, slot = -1, count; unsigned long long now, duration, period, max_period = 0; @@ -751,7 +751,7 @@ static void ghes_proc_in_irq(struct irq_work *irq_work) struct llist_node *llnode, *next; struct ghes_estatus_node *estatus_node; struct acpi_hest_generic *generic; - struct acpi_hest_generic_status *estatus; + struct acpi_generic_status *estatus; u32 len, node_len; llnode = llist_del_all(&ghes_estatus_llist); @@ -765,7 +765,7 @@ static void ghes_proc_in_irq(struct irq_work *irq_work) estatus_node = llist_entry(llnode, struct ghes_estatus_node, llnode); estatus = GHES_ESTATUS_FROM_NODE(estatus_node); - len = apei_estatus_len(estatus); + len = cper_estatus_len(estatus); node_len = GHES_ESTATUS_NODE_LEN(len); ghes_do_proc(estatus_node->ghes, estatus); if (!ghes_estatus_cached(estatus)) { @@ -784,7 +784,7 @@ static void ghes_print_queued_estatus(void) struct llist_node *llnode; struct ghes_estatus_node *estatus_node; struct acpi_hest_generic *generic; - struct acpi_hest_generic_status *estatus; + struct acpi_generic_status *estatus; u32 len, node_len; llnode = llist_del_all(&ghes_estatus_llist); @@ -797,7 +797,7 @@ static void ghes_print_queued_estatus(void) estatus_node = llist_entry(llnode, struct ghes_estatus_node, llnode); estatus = GHES_ESTATUS_FROM_NODE(estatus_node); - len = apei_estatus_len(estatus); + len = cper_estatus_len(estatus); node_len = GHES_ESTATUS_NODE_LEN(len); generic = estatus_node->generic; ghes_print_estatus(NULL, generic, estatus); @@ -843,7 +843,7 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) #ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG u32 len, node_len; struct ghes_estatus_node *estatus_node; - struct acpi_hest_generic_status *estatus; + struct acpi_generic_status *estatus; #endif if (!(ghes->flags & GHES_TO_CLEAR)) continue; @@ -851,7 +851,7 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) if (ghes_estatus_cached(ghes->estatus)) goto next; /* Save estatus for further processing in IRQ context */ - len = apei_estatus_len(ghes->estatus); + len = cper_estatus_len(ghes->estatus); node_len = GHES_ESTATUS_NODE_LEN(len); estatus_node = (void *)gen_pool_alloc(ghes_estatus_pool, node_len); @@ -923,7 +923,7 @@ static int ghes_probe(struct platform_device *ghes_dev) rc = -EIO; if (generic->error_block_length < - sizeof(struct acpi_hest_generic_status)) { + sizeof(struct acpi_generic_status)) { pr_warning(FW_BUG GHES_PFX "Invalid error block length: %u for generic hardware error source: %d\n", generic->error_block_length, generic->header.source_id); diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h index 0bd750e..556c83ee 100644 --- a/include/acpi/actbl1.h +++ b/include/acpi/actbl1.h @@ -596,7 +596,7 @@ struct acpi_hest_generic { /* Generic Error Status block */ -struct acpi_hest_generic_status { +struct acpi_generic_status { u32 block_status; u32 raw_data_offset; u32 raw_data_length; @@ -606,15 +606,15 @@ struct acpi_hest_generic_status { /* Values for block_status flags above */ -#define ACPI_HEST_UNCORRECTABLE (1) -#define ACPI_HEST_CORRECTABLE (1<<1) -#define ACPI_HEST_MULTIPLE_UNCORRECTABLE (1<<2) -#define ACPI_HEST_MULTIPLE_CORRECTABLE (1<<3) -#define ACPI_HEST_ERROR_ENTRY_COUNT (0xFF<<4) /* 8 bits, error count */ +#define ACPI_GEN_ERR_UC BIT(0) +#define ACPI_GEN_ERR_CE BIT(1) +#define ACPI_GEN_ERR_MULTI_UC BIT(2) +#define ACPI_GEN_ERR_MULTI_CE BIT(3) +#define ACPI_GEN_ERR_COUNT_SHIFT (0xFF<<4) /* 8 bits, error count */ /* Generic Error Data entry */ -struct acpi_hest_generic_data { +struct acpi_generic_data { u8 section_type[16]; u32 error_severity; u16 revision; diff --git a/include/acpi/ghes.h b/include/acpi/ghes.h index 720446c..dfd60d0 100644 --- a/include/acpi/ghes.h +++ b/include/acpi/ghes.h @@ -14,7 +14,7 @@ struct ghes { struct acpi_hest_generic *generic; - struct acpi_hest_generic_status *estatus; + struct acpi_generic_status *estatus; u64 buffer_paddr; unsigned long flags; union { diff --git a/include/linux/cper.h b/include/linux/cper.h index c230494..09ebe21 100644 --- a/include/linux/cper.h +++ b/include/linux/cper.h @@ -389,6 +389,6 @@ struct cper_sec_pcie { u64 cper_next_record_id(void); void cper_print_bits(const char *prefix, unsigned int bits, - const char *strs[], unsigned int strs_size); + const char * const strs[], unsigned int strs_size); #endif -- cgit v0.10.2 From 10ef6b0dffe404bcc54e94cb2ca1a5b18445a66b Mon Sep 17 00:00:00 2001 From: "Chen, Gong" Date: Fri, 18 Oct 2013 14:29:07 -0700 Subject: bitops: Introduce a more generic BITMASK macro GENMASK is used to create a contiguous bitmask([hi:lo]). It is implemented twice in current kernel. One is in EDAC driver, the other is in SiS/XGI FB driver. Move it to a more generic place for other usage. Signed-off-by: Chen, Gong Cc: Borislav Petkov Cc: Thomas Winischhofer Cc: Jean-Christophe Plagniol-Villard Cc: Tomi Valkeinen Acked-by: Borislav Petkov Acked-by: Mauro Carvalho Chehab Signed-off-by: Tony Luck diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 3c9e4e9..b53d0de 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -339,8 +339,8 @@ static void get_cs_base_and_mask(struct amd64_pvt *pvt, int csrow, u8 dct, if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) { csbase = pvt->csels[dct].csbases[csrow]; csmask = pvt->csels[dct].csmasks[csrow]; - base_bits = GENMASK(21, 31) | GENMASK(9, 15); - mask_bits = GENMASK(21, 29) | GENMASK(9, 15); + base_bits = GENMASK_ULL(31, 21) | GENMASK_ULL(15, 9); + mask_bits = GENMASK_ULL(29, 21) | GENMASK_ULL(15, 9); addr_shift = 4; /* @@ -352,16 +352,16 @@ static void get_cs_base_and_mask(struct amd64_pvt *pvt, int csrow, u8 dct, csbase = pvt->csels[dct].csbases[csrow]; csmask = pvt->csels[dct].csmasks[csrow >> 1]; - *base = (csbase & GENMASK(5, 15)) << 6; - *base |= (csbase & GENMASK(19, 30)) << 8; + *base = (csbase & GENMASK_ULL(15, 5)) << 6; + *base |= (csbase & GENMASK_ULL(30, 19)) << 8; *mask = ~0ULL; /* poke holes for the csmask */ - *mask &= ~((GENMASK(5, 15) << 6) | - (GENMASK(19, 30) << 8)); + *mask &= ~((GENMASK_ULL(15, 5) << 6) | + (GENMASK_ULL(30, 19) << 8)); - *mask |= (csmask & GENMASK(5, 15)) << 6; - *mask |= (csmask & GENMASK(19, 30)) << 8; + *mask |= (csmask & GENMASK_ULL(15, 5)) << 6; + *mask |= (csmask & GENMASK_ULL(30, 19)) << 8; return; } else { @@ -370,9 +370,11 @@ static void get_cs_base_and_mask(struct amd64_pvt *pvt, int csrow, u8 dct, addr_shift = 8; if (pvt->fam == 0x15) - base_bits = mask_bits = GENMASK(19,30) | GENMASK(5,13); + base_bits = mask_bits = + GENMASK_ULL(30,19) | GENMASK_ULL(13,5); else - base_bits = mask_bits = GENMASK(19,28) | GENMASK(5,13); + base_bits = mask_bits = + GENMASK_ULL(28,19) | GENMASK_ULL(13,5); } *base = (csbase & base_bits) << addr_shift; @@ -561,7 +563,7 @@ static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr) * section 3.4.2 of AMD publication 24592: AMD x86-64 Architecture * Programmer's Manual Volume 1 Application Programming. */ - dram_addr = (sys_addr & GENMASK(0, 39)) - dram_base; + dram_addr = (sys_addr & GENMASK_ULL(39, 0)) - dram_base; edac_dbg(2, "using DRAM Base register to translate SysAddr 0x%lx to DramAddr 0x%lx\n", (unsigned long)sys_addr, (unsigned long)dram_addr); @@ -597,7 +599,7 @@ static u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr) * concerning translating a DramAddr to an InputAddr. */ intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0)); - input_addr = ((dram_addr >> intlv_shift) & GENMASK(12, 35)) + + input_addr = ((dram_addr >> intlv_shift) & GENMASK_ULL(35, 12)) + (dram_addr & 0xfff); edac_dbg(2, " Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n", @@ -849,7 +851,7 @@ static u64 get_error_address(struct amd64_pvt *pvt, struct mce *m) end_bit = 39; } - addr = m->addr & GENMASK(start_bit, end_bit); + addr = m->addr & GENMASK_ULL(end_bit, start_bit); /* * Erratum 637 workaround @@ -861,7 +863,7 @@ static u64 get_error_address(struct amd64_pvt *pvt, struct mce *m) u16 mce_nid; u8 intlv_en; - if ((addr & GENMASK(24, 47)) >> 24 != 0x00fdf7) + if ((addr & GENMASK_ULL(47, 24)) >> 24 != 0x00fdf7) return addr; mce_nid = amd_get_nb_id(m->extcpu); @@ -871,7 +873,7 @@ static u64 get_error_address(struct amd64_pvt *pvt, struct mce *m) intlv_en = tmp >> 21 & 0x7; /* add [47:27] + 3 trailing bits */ - cc6_base = (tmp & GENMASK(0, 20)) << 3; + cc6_base = (tmp & GENMASK_ULL(20, 0)) << 3; /* reverse and add DramIntlvEn */ cc6_base |= intlv_en ^ 0x7; @@ -880,18 +882,18 @@ static u64 get_error_address(struct amd64_pvt *pvt, struct mce *m) cc6_base <<= 24; if (!intlv_en) - return cc6_base | (addr & GENMASK(0, 23)); + return cc6_base | (addr & GENMASK_ULL(23, 0)); amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_BASE, &tmp); /* faster log2 */ - tmp_addr = (addr & GENMASK(12, 23)) << __fls(intlv_en + 1); + tmp_addr = (addr & GENMASK_ULL(23, 12)) << __fls(intlv_en + 1); /* OR DramIntlvSel into bits [14:12] */ - tmp_addr |= (tmp & GENMASK(21, 23)) >> 9; + tmp_addr |= (tmp & GENMASK_ULL(23, 21)) >> 9; /* add remaining [11:0] bits from original MC4_ADDR */ - tmp_addr |= addr & GENMASK(0, 11); + tmp_addr |= addr & GENMASK_ULL(11, 0); return cc6_base | tmp_addr; } @@ -952,12 +954,12 @@ static void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range) amd64_read_pci_cfg(f1, DRAM_LOCAL_NODE_LIM, &llim); - pvt->ranges[range].lim.lo &= GENMASK(0, 15); + pvt->ranges[range].lim.lo &= GENMASK_ULL(15, 0); /* {[39:27],111b} */ pvt->ranges[range].lim.lo |= ((llim & 0x1fff) << 3 | 0x7) << 16; - pvt->ranges[range].lim.hi &= GENMASK(0, 7); + pvt->ranges[range].lim.hi &= GENMASK_ULL(7, 0); /* [47:40] */ pvt->ranges[range].lim.hi |= llim >> 13; @@ -1330,7 +1332,7 @@ static u64 f1x_get_norm_dct_addr(struct amd64_pvt *pvt, u8 range, chan_off = dram_base; } - return (sys_addr & GENMASK(6,47)) - (chan_off & GENMASK(23,47)); + return (sys_addr & GENMASK_ULL(47,6)) - (chan_off & GENMASK_ULL(47,23)); } /* diff --git a/drivers/edac/amd64_edac.h b/drivers/edac/amd64_edac.h index d2443cf..6dc1fcc 100644 --- a/drivers/edac/amd64_edac.h +++ b/drivers/edac/amd64_edac.h @@ -160,14 +160,6 @@ #define OFF false /* - * Create a contiguous bitmask starting at bit position @lo and ending at - * position @hi. For example - * - * GENMASK(21, 39) gives us the 64bit vector 0x000000ffffe00000. - */ -#define GENMASK(lo, hi) (((1ULL << ((hi) - (lo) + 1)) - 1) << (lo)) - -/* * PCI-defined configuration space registers */ #define PCI_DEVICE_ID_AMD_15H_M30H_NB_F1 0x141b diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c index e04462b..88f60c5 100644 --- a/drivers/edac/sb_edac.c +++ b/drivers/edac/sb_edac.c @@ -50,7 +50,7 @@ static int probed; * Get a bit field at register value , from bit to bit */ #define GET_BITFIELD(v, lo, hi) \ - (((v) & ((1ULL << ((hi) - (lo) + 1)) - 1) << (lo)) >> (lo)) + (((v) & GENMASK_ULL(hi, lo)) >> (lo)) /* * sbridge Memory Controller Registers diff --git a/drivers/video/sis/init.c b/drivers/video/sis/init.c index f082ae5..4f26bc2 100644 --- a/drivers/video/sis/init.c +++ b/drivers/video/sis/init.c @@ -3320,9 +3320,8 @@ SiSSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo) } #ifndef GETBITSTR -#define BITMASK(h,l) (((unsigned)(1U << ((h)-(l)+1))-1)<<(l)) -#define GENMASK(mask) BITMASK(1?mask,0?mask) -#define GETBITS(var,mask) (((var) & GENMASK(mask)) >> (0?mask)) +#define GENBITSMASK(mask) GENMASK(1?mask,0?mask) +#define GETBITS(var,mask) (((var) & GENBITSMASK(mask)) >> (0?mask)) #define GETBITSTR(val,from,to) ((GETBITS(val,from)) << (0?to)) #endif diff --git a/include/linux/bitops.h b/include/linux/bitops.h index a3b6b82..bd0c459 100644 --- a/include/linux/bitops.h +++ b/include/linux/bitops.h @@ -10,6 +10,14 @@ #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) #endif +/* + * Create a contiguous bitmask starting at bit position @l and ending at + * position @h. For example + * GENMASK_ULL(39, 21) gives us the 64bit vector 0x000000ffffe00000. + */ +#define GENMASK(h, l) (((U32_C(1) << ((h) - (l) + 1)) - 1) << (l)) +#define GENMASK_ULL(h, l) (((U64_C(1) << ((h) - (l) + 1)) - 1) << (l)) + extern unsigned int __sw_hweight8(unsigned int w); extern unsigned int __sw_hweight16(unsigned int w); extern unsigned int __sw_hweight32(unsigned int w); -- cgit v0.10.2 From 30a765d6433413c0eba90c969eecf12dfa2d111a Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 21 Oct 2013 19:07:34 +0530 Subject: ASoC: dont call dapm_sync while reporting jack always While reporting the jack status snd_soc_jack_report() invokes snd_soc_dapm_sync() always. This should be required when we have pins associated with jack and reporting enables or disables these. So add a check for this case Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index 71358e3..23d43da 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -65,6 +65,7 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) struct snd_soc_codec *codec; struct snd_soc_dapm_context *dapm; struct snd_soc_jack_pin *pin; + unsigned int sync = 0; int enable; trace_snd_soc_jack_report(jack, mask, status); @@ -92,12 +93,16 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) snd_soc_dapm_enable_pin(dapm, pin->pin); else snd_soc_dapm_disable_pin(dapm, pin->pin); + + /* we need to sync for this case only */ + sync = 1; } /* Report before the DAPM sync to help users updating micbias status */ blocking_notifier_call_chain(&jack->notifier, jack->status, jack); - snd_soc_dapm_sync(dapm); + if (sync) + snd_soc_dapm_sync(dapm); snd_jack_report(jack->jack, jack->status); -- cgit v0.10.2 From 347de6bab4bd0166115a5c90491a05bf9df508ee Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 21 Oct 2013 15:42:49 +0200 Subject: spi/s3c64xx: Do not ignore return value of spi_master_resume/suspend During PM resume and suspend do not ignore the return value of spi_master_suspend() or spi_master_resume(). Instead pass it further. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Kyungmin Park Reviewed-by: Sylwester Nawrocki Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 8e732a1..435406b 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1477,7 +1477,9 @@ static int s3c64xx_spi_suspend(struct device *dev) struct spi_master *master = dev_get_drvdata(dev); struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); - spi_master_suspend(master); + int ret = spi_master_suspend(master); + if (ret) + return ret; /* Disable the clock */ clk_disable_unprepare(sdd->src_clk); @@ -1503,9 +1505,7 @@ static int s3c64xx_spi_resume(struct device *dev) s3c64xx_spi_hwinit(sdd, sdd->port_id); - spi_master_resume(master); - - return 0; + return spi_master_resume(master); } #endif /* CONFIG_PM_SLEEP */ -- cgit v0.10.2 From 9d7fd21acb2094d38d8560b0b3c91fa31efa6a71 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 21 Oct 2013 15:42:50 +0200 Subject: spi/s3c64xx: Fix doubled clock disable on suspend Fix doubled clock disable and unprepare during PM suspend which triggered the warnings: WARNING: at drivers/clk/clk.c:800 clk_disable+0x18/0x24() Modules linked in: CPU: 0 PID: 1745 Comm: sh Not tainted 3.10.14-01211-ge2549bb-dirty #62 [] (unwind_backtrace+0x0/0x138) from [] (show_stack+0x10/0x14) [] (show_stack+0x10/0x14) from [] (warn_slowpath_common+0x4c/0x68) [] (warn_slowpath_common+0x4c/0x68) from [] (warn_slowpath_null+0x1c/0x24) [] (warn_slowpath_null+0x1c/0x24) from [] (clk_disable+0x18/0x24) [] (clk_disable+0x18/0x24) from [] (s3c64xx_spi_suspend+0x28/0x54) [] (s3c64xx_spi_suspend+0x28/0x54) from [] (platform_pm_suspend+0x2c/0x5c) [] (platform_pm_suspend+0x2c/0x5c) from [] (dpm_run_callback+0x44/0x7c) [] (dpm_run_callback+0x44/0x7c) from [] (__device_suspend+0x108/0x300) [] (__device_suspend+0x108/0x300) from [] (dpm_suspend+0x54/0x208) [] (dpm_suspend+0x54/0x208) from [] (suspend_devices_and_enter+0x98/0x458) [] (suspend_devices_and_enter+0x98/0x458) from [] (pm_suspend+0x1c4/0x25c) [] (pm_suspend+0x1c4/0x25c) from [] (state_store+0x6c/0xbc) [] (state_store+0x6c/0xbc) from [] (kobj_attr_store+0x14/0x20) [] (kobj_attr_store+0x14/0x20) from [] (sysfs_write_file+0xfc/0x164) [] (sysfs_write_file+0xfc/0x164) from [] (vfs_write+0xbc/0x1bc) [] (vfs_write+0xbc/0x1bc) from [] (SyS_write+0x40/0x68) [] (SyS_write+0x40/0x68) from [] (ret_fast_syscall+0x0/0x3c) The clocks may be already disabled before suspending. Check PM runtime suspend status and disable clocks only if device is not suspended. During resume do not enable the clocks if device is runtime suspended. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Kyungmin Park Reviewed-by: Sylwester Nawrocki Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 435406b..1dbdcc7 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1481,9 +1481,10 @@ static int s3c64xx_spi_suspend(struct device *dev) if (ret) return ret; - /* Disable the clock */ - clk_disable_unprepare(sdd->src_clk); - clk_disable_unprepare(sdd->clk); + if (!pm_runtime_suspended(dev)) { + clk_disable_unprepare(sdd->clk); + clk_disable_unprepare(sdd->src_clk); + } sdd->cur_speed = 0; /* Output Clock is stopped */ @@ -1499,9 +1500,10 @@ static int s3c64xx_spi_resume(struct device *dev) if (sci->cfg_gpio) sci->cfg_gpio(); - /* Enable the clock */ - clk_prepare_enable(sdd->src_clk); - clk_prepare_enable(sdd->clk); + if (!pm_runtime_suspended(dev)) { + clk_prepare_enable(sdd->src_clk); + clk_prepare_enable(sdd->clk); + } s3c64xx_spi_hwinit(sdd, sdd->port_id); -- cgit v0.10.2 From 75b9b65ee5a80e99efe2fd551d08bc86f115550f Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 21 Oct 2013 10:50:49 +0200 Subject: ASoC: kirkwood: add S/PDIF support This patch adds S/PDIF input/output for mvebu DT boards. Signed-off-by: Jean-Francois Moine Signed-off-by: Mark Brown diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c index d0504a2..9ec38d1 100644 --- a/sound/soc/kirkwood/kirkwood-i2s.c +++ b/sound/soc/kirkwood/kirkwood-i2s.c @@ -160,9 +160,11 @@ static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream, case SNDRV_PCM_FORMAT_S16_LE: i2s_value |= KIRKWOOD_I2S_CTL_SIZE_16; ctl_play = KIRKWOOD_PLAYCTL_SIZE_16_C | - KIRKWOOD_PLAYCTL_I2S_EN; + KIRKWOOD_PLAYCTL_I2S_EN | + KIRKWOOD_PLAYCTL_SPDIF_EN; ctl_rec = KIRKWOOD_RECCTL_SIZE_16_C | - KIRKWOOD_RECCTL_I2S_EN; + KIRKWOOD_RECCTL_I2S_EN | + KIRKWOOD_RECCTL_SPDIF_EN; break; /* * doesn't work... S20_3LE != kirkwood 20bit format ? @@ -178,9 +180,11 @@ static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream, case SNDRV_PCM_FORMAT_S24_LE: i2s_value |= KIRKWOOD_I2S_CTL_SIZE_24; ctl_play = KIRKWOOD_PLAYCTL_SIZE_24 | - KIRKWOOD_PLAYCTL_I2S_EN; + KIRKWOOD_PLAYCTL_I2S_EN | + KIRKWOOD_PLAYCTL_SPDIF_EN; ctl_rec = KIRKWOOD_RECCTL_SIZE_24 | - KIRKWOOD_RECCTL_I2S_EN; + KIRKWOOD_RECCTL_I2S_EN | + KIRKWOOD_RECCTL_SPDIF_EN; break; case SNDRV_PCM_FORMAT_S32_LE: i2s_value |= KIRKWOOD_I2S_CTL_SIZE_32; @@ -240,6 +244,11 @@ static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream, ctl); } + if (dai->id == 0) + ctl &= ~KIRKWOOD_PLAYCTL_SPDIF_EN; /* i2s */ + else + ctl &= ~KIRKWOOD_PLAYCTL_I2S_EN; /* spdif */ + switch (cmd) { case SNDRV_PCM_TRIGGER_START: /* configure */ @@ -258,7 +267,8 @@ static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_STOP: /* stop audio, disable interrupts */ - ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE; + ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE | + KIRKWOOD_PLAYCTL_SPDIF_MUTE; writel(ctl, priv->io + KIRKWOOD_PLAYCTL); value = readl(priv->io + KIRKWOOD_INT_MASK); @@ -272,13 +282,15 @@ static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: - ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE; + ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE | + KIRKWOOD_PLAYCTL_SPDIF_MUTE; writel(ctl, priv->io + KIRKWOOD_PLAYCTL); break; case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - ctl &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE); + ctl &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE | + KIRKWOOD_PLAYCTL_SPDIF_MUTE); writel(ctl, priv->io + KIRKWOOD_PLAYCTL); break; @@ -301,7 +313,13 @@ static int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_START: /* configure */ ctl = priv->ctl_rec; - value = ctl & ~KIRKWOOD_RECCTL_I2S_EN; + if (dai->id == 0) + ctl &= ~KIRKWOOD_RECCTL_SPDIF_EN; /* i2s */ + else + ctl &= ~KIRKWOOD_RECCTL_I2S_EN; /* spdif */ + + value = ctl & ~(KIRKWOOD_RECCTL_I2S_EN | + KIRKWOOD_RECCTL_SPDIF_EN); writel(value, priv->io + KIRKWOOD_RECCTL); /* enable interrupts */ @@ -361,9 +379,8 @@ static int kirkwood_i2s_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } -static int kirkwood_i2s_probe(struct snd_soc_dai *dai) +static int kirkwood_i2s_init(struct kirkwood_dma_data *priv) { - struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); unsigned long value; unsigned int reg_data; @@ -404,9 +421,29 @@ static const struct snd_soc_dai_ops kirkwood_i2s_dai_ops = { .set_fmt = kirkwood_i2s_set_fmt, }; - -static struct snd_soc_dai_driver kirkwood_i2s_dai = { - .probe = kirkwood_i2s_probe, +static struct snd_soc_dai_driver kirkwood_i2s_dai[2] = { + { + .name = "i2s", + .id = 0, + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000, + .formats = KIRKWOOD_I2S_FORMATS, + }, + .capture = { + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000, + .formats = KIRKWOOD_I2S_FORMATS, + }, + .ops = &kirkwood_i2s_dai_ops, + }, + { + .name = "spdif", + .id = 1, .playback = { .channels_min = 1, .channels_max = 2, @@ -422,10 +459,34 @@ static struct snd_soc_dai_driver kirkwood_i2s_dai = { .formats = KIRKWOOD_I2S_FORMATS, }, .ops = &kirkwood_i2s_dai_ops, + }, }; -static struct snd_soc_dai_driver kirkwood_i2s_dai_extclk = { - .probe = kirkwood_i2s_probe, +static struct snd_soc_dai_driver kirkwood_i2s_dai_extclk[2] = { + { + .name = "i2s", + .id = 0, + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000 | + SNDRV_PCM_RATE_CONTINUOUS | + SNDRV_PCM_RATE_KNOT, + .formats = KIRKWOOD_I2S_FORMATS, + }, + .capture = { + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000 | + SNDRV_PCM_RATE_CONTINUOUS | + SNDRV_PCM_RATE_KNOT, + .formats = KIRKWOOD_I2S_FORMATS, + }, + .ops = &kirkwood_i2s_dai_ops, + }, + { + .name = "spdif", + .id = 1, .playback = { .channels_min = 1, .channels_max = 2, @@ -443,6 +504,7 @@ static struct snd_soc_dai_driver kirkwood_i2s_dai_extclk = { .formats = KIRKWOOD_I2S_FORMATS, }, .ops = &kirkwood_i2s_dai_ops, + }, }; static const struct snd_soc_component_driver kirkwood_i2s_component = { @@ -452,7 +514,7 @@ static const struct snd_soc_component_driver kirkwood_i2s_component = { static int kirkwood_i2s_dev_probe(struct platform_device *pdev) { struct kirkwood_asoc_platform_data *data = pdev->dev.platform_data; - struct snd_soc_dai_driver *soc_dai = &kirkwood_i2s_dai; + struct snd_soc_dai_driver *soc_dai = kirkwood_i2s_dai; struct kirkwood_dma_data *priv; struct resource *mem; struct device_node *np = pdev->dev.of_node; @@ -524,7 +586,7 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev) } err = snd_soc_register_component(&pdev->dev, &kirkwood_i2s_component, - soc_dai, 1); + soc_dai, 2); if (err) { dev_err(&pdev->dev, "snd_soc_register_component failed\n"); goto err_component; @@ -535,6 +597,9 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev) dev_err(&pdev->dev, "snd_soc_register_platform failed\n"); goto err_platform; } + + kirkwood_i2s_init(priv); + return 0; err_platform: snd_soc_unregister_component(&pdev->dev); diff --git a/sound/soc/kirkwood/kirkwood-openrd.c b/sound/soc/kirkwood/kirkwood-openrd.c index 025be0e..65f2a5b 100644 --- a/sound/soc/kirkwood/kirkwood-openrd.c +++ b/sound/soc/kirkwood/kirkwood-openrd.c @@ -52,7 +52,7 @@ static struct snd_soc_dai_link openrd_client_dai[] = { { .name = "CS42L51", .stream_name = "CS42L51 HiFi", - .cpu_dai_name = "mvebu-audio", + .cpu_dai_name = "i2s", .platform_name = "mvebu-audio", .codec_dai_name = "cs42l51-hifi", .codec_name = "cs42l51-codec.0-004a", diff --git a/sound/soc/kirkwood/kirkwood-t5325.c b/sound/soc/kirkwood/kirkwood-t5325.c index 27545b0..d213832 100644 --- a/sound/soc/kirkwood/kirkwood-t5325.c +++ b/sound/soc/kirkwood/kirkwood-t5325.c @@ -68,7 +68,7 @@ static struct snd_soc_dai_link t5325_dai[] = { { .name = "ALC5621", .stream_name = "ALC5621 HiFi", - .cpu_dai_name = "mvebu-audio", + .cpu_dai_name = "i2s", .platform_name = "mvebu-audio", .codec_dai_name = "alc5621-hifi", .codec_name = "alc562x-codec.0-001a", -- cgit v0.10.2 From 256ba181cb2ddeef8e0a9b0540b09e0f77bf5540 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Fri, 18 Oct 2013 18:37:42 +0300 Subject: ASoC: davinci-mcasp: Add location for data port registers to DT This patch adds a separate register location for data port registers to mcasp DT bindings. On am33xx SoCs the McASP registers are mapped trough L4 interconnect, but data port registers are also mapped trough L3 bus to a different memory location. Signed-off-by: Hebbar, Gururaja Signed-off-by: Darren Etheridge Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt index 374e145..c2ab869 100644 --- a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt +++ b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt @@ -6,7 +6,11 @@ Required properties: "ti,da830-mcasp-audio" : for both DA830 & DA850 platforms "ti,omap2-mcasp-audio" : for OMAP2 platforms (TI81xx, AM33xx) -- reg : Should contain McASP registers offset and length +- reg : Should contain reg specifiers for the entries in the reg-names property. +- reg-names : Should contain: + * "mpu" for the main registers (required). For compatibility with + existing software, it is recommended this is the first entry. + * "dat" for separate data port register access (optional). - interrupts : Interrupt number for McASP - op-mode : I2S/DIT ops mode. - tdm-slots : Slots for TDM operation. @@ -15,7 +19,6 @@ Required properties: to "num-serializer" parameter. Each entry is a number indication serializer pin direction. (0 - INACTIVE, 1 - TX, 2 - RX) - Optional properties: - ti,hwmods : Must be "mcasp", n is controller instance starting 0 @@ -31,6 +34,7 @@ mcasp0: mcasp0@1d00000 { #address-cells = <1>; #size-cells = <0>; reg = <0x100000 0x3000>; + reg-names "mpu"; interrupts = <82 83>; op-mode = <0>; /* MCASP_IIS_MODE */ tdm-slots = <2>; diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index cdfe959..806bec3 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -1001,18 +1001,40 @@ static const struct snd_soc_component_driver davinci_mcasp_component = { .name = "davinci-mcasp", }; +/* Some HW specific values and defaults. The rest is filled in from DT. */ +static struct snd_platform_data dm646x_mcasp_pdata = { + .tx_dma_offset = 0x400, + .rx_dma_offset = 0x400, + .asp_chan_q = EVENTQ_0, + .version = MCASP_VERSION_1, +}; + +static struct snd_platform_data da830_mcasp_pdata = { + .tx_dma_offset = 0x2000, + .rx_dma_offset = 0x2000, + .asp_chan_q = EVENTQ_0, + .version = MCASP_VERSION_2, +}; + +static struct snd_platform_data omap2_mcasp_pdata = { + .tx_dma_offset = 0, + .rx_dma_offset = 0, + .asp_chan_q = EVENTQ_0, + .version = MCASP_VERSION_3, +}; + static const struct of_device_id mcasp_dt_ids[] = { { .compatible = "ti,dm646x-mcasp-audio", - .data = (void *)MCASP_VERSION_1, + .data = &dm646x_mcasp_pdata, }, { .compatible = "ti,da830-mcasp-audio", - .data = (void *)MCASP_VERSION_2, + .data = &da830_mcasp_pdata, }, { .compatible = "ti,omap2-mcasp-audio", - .data = (void *)MCASP_VERSION_3, + .data = &omap2_mcasp_pdata, }, { /* sentinel */ } }; @@ -1035,20 +1057,13 @@ static struct snd_platform_data *davinci_mcasp_set_pdata_from_of( pdata = pdev->dev.platform_data; return pdata; } else if (match) { - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) { - ret = -ENOMEM; - goto nodata; - } + pdata = (struct snd_platform_data *) match->data; } else { /* control shouldn't reach here. something is wrong */ ret = -EINVAL; goto nodata; } - if (match->data) - pdata->version = (u8)((int)match->data); - ret = of_property_read_u32(np, "op-mode", &val); if (ret >= 0) pdata->op_mode = val; @@ -1124,7 +1139,7 @@ nodata: static int davinci_mcasp_probe(struct platform_device *pdev) { struct davinci_pcm_dma_params *dma_data; - struct resource *mem, *ioarea, *res; + struct resource *mem, *ioarea, *res, *dat; struct snd_platform_data *pdata; struct davinci_audio_dev *dev; int ret; @@ -1145,10 +1160,15 @@ static int davinci_mcasp_probe(struct platform_device *pdev) return -EINVAL; } - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); if (!mem) { - dev_err(&pdev->dev, "no mem resource?\n"); - return -ENODEV; + dev_warn(dev->dev, + "\"mpu\" mem resource not found, using index 0\n"); + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "no mem resource?\n"); + return -ENODEV; + } } ioarea = devm_request_mem_region(&pdev->dev, mem->start, @@ -1182,13 +1202,16 @@ static int davinci_mcasp_probe(struct platform_device *pdev) dev->rxnumevt = pdata->rxnumevt; dev->dev = &pdev->dev; + dat = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat"); + if (!dat) + dat = mem; + dma_data = &dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]; dma_data->asp_chan_q = pdata->asp_chan_q; dma_data->ram_chan_q = pdata->ram_chan_q; dma_data->sram_pool = pdata->sram_pool; dma_data->sram_size = pdata->sram_size_playback; - dma_data->dma_addr = (dma_addr_t) (pdata->tx_dma_offset + - mem->start); + dma_data->dma_addr = dat->start + pdata->tx_dma_offset; /* first TX, then RX */ res = platform_get_resource(pdev, IORESOURCE_DMA, 0); @@ -1205,8 +1228,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) dma_data->ram_chan_q = pdata->ram_chan_q; dma_data->sram_pool = pdata->sram_pool; dma_data->sram_size = pdata->sram_size_capture; - dma_data->dma_addr = (dma_addr_t)(pdata->rx_dma_offset + - mem->start); + dma_data->dma_addr = dat->start + pdata->rx_dma_offset; res = platform_get_resource(pdev, IORESOURCE_DMA, 1); if (!res) { @@ -1305,4 +1327,3 @@ module_platform_driver(davinci_mcasp_driver); MODULE_AUTHOR("Steve Chen"); MODULE_DESCRIPTION("TI DAVINCI McASP SoC Interface"); MODULE_LICENSE("GPL"); - -- cgit v0.10.2 From 4023fe6ff2192d6050647571ea54f5497b2ec8f6 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Fri, 18 Oct 2013 18:37:43 +0300 Subject: ASoC: davinci-mcasp: Extract DMA channels directly from DT Extract DMA channels directly from DT as they can not be found from platform resources anymore. This is a work-around until davinci audio driver is updated to use dmaengine. Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt index c2ab869..c3ccde7 100644 --- a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt +++ b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt @@ -18,6 +18,11 @@ Required properties: - serial-dir : A list of serializer pin mode. The list number should be equal to "num-serializer" parameter. Each entry is a number indication serializer pin direction. (0 - INACTIVE, 1 - TX, 2 - RX) +- dmas: two element list of DMA controller phandles and DMA request line + ordered pairs. +- dma-names: identifier string for each DMA request line in the dmas property. + These strings correspond 1:1 with the ordered pairs in dmas. The dma + identifiers must be "rx" and "tx". Optional properties: diff --git a/include/linux/platform_data/davinci_asp.h b/include/linux/platform_data/davinci_asp.h index 8db5ae0..689a856 100644 --- a/include/linux/platform_data/davinci_asp.h +++ b/include/linux/platform_data/davinci_asp.h @@ -84,6 +84,8 @@ struct snd_platform_data { u8 version; u8 txnumevt; u8 rxnumevt; + int tx_dma_channel; + int rx_dma_channel; }; enum { diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 806bec3..4c20750 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -1047,6 +1047,7 @@ static struct snd_platform_data *davinci_mcasp_set_pdata_from_of( struct snd_platform_data *pdata = NULL; const struct of_device_id *match = of_match_device(mcasp_dt_ids, &pdev->dev); + struct of_phandle_args dma_spec; const u32 *of_serial_dir32; u8 *of_serial_dir; @@ -1109,6 +1110,28 @@ static struct snd_platform_data *davinci_mcasp_set_pdata_from_of( pdata->serial_dir = of_serial_dir; } + ret = of_property_match_string(np, "dma-names", "tx"); + if (ret < 0) + goto nodata; + + ret = of_parse_phandle_with_args(np, "dmas", "#dma-cells", ret, + &dma_spec); + if (ret < 0) + goto nodata; + + pdata->tx_dma_channel = dma_spec.args[0]; + + ret = of_property_match_string(np, "dma-names", "rx"); + if (ret < 0) + goto nodata; + + ret = of_parse_phandle_with_args(np, "dmas", "#dma-cells", ret, + &dma_spec); + if (ret < 0) + goto nodata; + + pdata->rx_dma_channel = dma_spec.args[0]; + ret = of_property_read_u32(np, "tx-num-evt", &val); if (ret >= 0) pdata->txnumevt = val; @@ -1213,15 +1236,11 @@ static int davinci_mcasp_probe(struct platform_device *pdev) dma_data->sram_size = pdata->sram_size_playback; dma_data->dma_addr = dat->start + pdata->tx_dma_offset; - /* first TX, then RX */ res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!res) { - dev_err(&pdev->dev, "no DMA resource\n"); - ret = -ENODEV; - goto err_release_clk; - } - - dma_data->channel = res->start; + if (res) + dma_data->channel = res->start; + else + dma_data->channel = pdata->tx_dma_channel; dma_data = &dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]; dma_data->asp_chan_q = pdata->asp_chan_q; @@ -1231,13 +1250,11 @@ static int davinci_mcasp_probe(struct platform_device *pdev) dma_data->dma_addr = dat->start + pdata->rx_dma_offset; res = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (!res) { - dev_err(&pdev->dev, "no DMA resource\n"); - ret = -ENODEV; - goto err_release_clk; - } + if (res) + dma_data->channel = res->start; + else + dma_data->channel = pdata->rx_dma_channel; - dma_data->channel = res->start; dev_set_drvdata(&pdev->dev, dev); ret = snd_soc_register_component(&pdev->dev, &davinci_mcasp_component, &davinci_mcasp_dai[pdata->op_mode], 1); -- cgit v0.10.2 From 3af9e0315699b60762157662f721f50fd1fe529b Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Fri, 18 Oct 2013 18:37:44 +0300 Subject: ASoC: davinci-mcasp: Change compatible property model to more accurate Change the model omap2-mcasp-audio in compatible property to am33xx-mcasp-audio as omap2 does not have mcasp. Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt index c3ccde7..1945aec 100644 --- a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt +++ b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt @@ -4,7 +4,7 @@ Required properties: - compatible : "ti,dm646x-mcasp-audio" : for DM646x platforms "ti,da830-mcasp-audio" : for both DA830 & DA850 platforms - "ti,omap2-mcasp-audio" : for OMAP2 platforms (TI81xx, AM33xx) + "ti,am33xx-mcasp-audio" : for AM33xx platforms (AM33xx, TI81xx) - reg : Should contain reg specifiers for the entries in the reg-names property. - reg-names : Should contain: diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 4c20750..bbc9a07 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -1033,7 +1033,7 @@ static const struct of_device_id mcasp_dt_ids[] = { .data = &da830_mcasp_pdata, }, { - .compatible = "ti,omap2-mcasp-audio", + .compatible = "ti,am33xx-mcasp-audio", .data = &omap2_mcasp_pdata, }, { /* sentinel */ } -- cgit v0.10.2 From 4bd7145b194af7cd96fc56d2ebee583b3edf03d3 Mon Sep 17 00:00:00 2001 From: Yi Zhang Date: Tue, 22 Oct 2013 18:44:32 +0800 Subject: regmap: irq: clear status when disable irq clear the status bit if the mask register doesn't prevent the chip level irq from being asserted OR in the following sequence, there will be irq storm happens: 1) interrupt is triggered; 2) another thread disables it(the mask bit is set); 3) _Then_ the interrupt thread is not ACKed(the status bit is not cleared), and it's ignored; 4) if the irq is still asserted because of the uncleared status bit, the irq storm happens; Signed-off-by: Yi Zhang Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index d10456f..763c60d 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -105,6 +105,22 @@ static void regmap_irq_sync_unlock(struct irq_data *data) "Failed to sync wakes in %x: %d\n", reg, ret); } + + if (!d->chip->init_ack_masked) + continue; + /* + * Ack all the masked interrupts uncondictionly, + * OR if there is masked interrupt which hasn't been Acked, + * it'll be ignored in irq handler, then may introduce irq storm + */ + if (d->mask_buf[i] && d->chip->ack_base) { + reg = d->chip->ack_base + + (i * map->reg_stride * d->irq_reg_stride); + ret = regmap_write(map, reg, d->mask_buf[i]); + if (ret != 0) + dev_err(d->map->dev, "Failed to ack 0x%x: %d\n", + reg, ret); + } } if (d->chip->runtime_pm) -- cgit v0.10.2 From 2491c5c8c99dfcf2519ca73798be60f874a2b16e Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 11 Sep 2013 21:20:29 -0700 Subject: leds: blinkm: Remove redundant break MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'break' after return is redundant. Remove it. Signed-off-by: Sachin Kamat Acked-by: Jan-Simon Möller Signed-off-by: Bryan Wu diff --git a/drivers/leds/leds-blinkm.c b/drivers/leds/leds-blinkm.c index a502678..66d0a57 100644 --- a/drivers/leds/leds-blinkm.c +++ b/drivers/leds/leds-blinkm.c @@ -161,13 +161,10 @@ static ssize_t show_color_common(struct device *dev, char *buf, int color) switch (color) { case RED: return scnprintf(buf, PAGE_SIZE, "%02X\n", data->red); - break; case GREEN: return scnprintf(buf, PAGE_SIZE, "%02X\n", data->green); - break; case BLUE: return scnprintf(buf, PAGE_SIZE, "%02X\n", data->blue); - break; default: return -EINVAL; } -- cgit v0.10.2 From f65f0a1a9836abbfbe5c9b8fa0452e4d8eb7bf00 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sun, 15 Sep 2013 03:50:17 -0700 Subject: leds: lp55xx: enable setting default trigger This enables setting a default trigger on an LP55xx channel, either from platform data or device tree. This mechanism is identical to the mechanism for GPIO LEDs and references the common LEDs device tree bindings. Signed-off-by: Linus Walleij Tested-by: Milo Kim Acked-by: Milo Kim Signed-off-by: Bryan Wu diff --git a/Documentation/devicetree/bindings/leds/leds-lp55xx.txt b/Documentation/devicetree/bindings/leds/leds-lp55xx.txt index a61727f..d221e75 100644 --- a/Documentation/devicetree/bindings/leds/leds-lp55xx.txt +++ b/Documentation/devicetree/bindings/leds/leds-lp55xx.txt @@ -17,12 +17,15 @@ Optional properties: 2: D1~6 with VOUT, D7~9 with VDD 3: D1~9 are connected to VOUT -Alternatively, each child can have specific channel name -- chan-name: Name of each channel name +Alternatively, each child can have a specific channel name and trigger: +- chan-name (optional): name of channel +- linux,default-trigger (optional): see + Documentation/devicetree/bindings/leds/common.txt example 1) LP5521 3 LED channels, external clock used. Channel names are 'lp5521_pri:channel0', -'lp5521_pri:channel1' and 'lp5521_pri:channel2' +'lp5521_pri:channel1' and 'lp5521_pri:channel2', with a heartbeat trigger +on channel 0. lp5521@32 { compatible = "national,lp5521"; @@ -33,6 +36,7 @@ lp5521@32 { chan0 { led-cur = /bits/ 8 <0x2f>; max-cur = /bits/ 8 <0x5f>; + linux,default-trigger = "heartbeat"; }; chan1 { diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 351825b..075acf5 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -165,6 +165,7 @@ static int lp55xx_init_led(struct lp55xx_led *led, led->led_current = pdata->led_config[chan].led_current; led->max_current = pdata->led_config[chan].max_current; led->chan_nr = pdata->led_config[chan].chan_nr; + led->cdev.default_trigger = pdata->led_config[chan].default_trigger; if (led->chan_nr >= max_channel) { dev_err(dev, "Use channel numbers between 0 and %d\n", @@ -586,6 +587,8 @@ int lp55xx_of_populate_pdata(struct device *dev, struct device_node *np) of_property_read_string(child, "chan-name", &cfg[i].name); of_property_read_u8(child, "led-cur", &cfg[i].led_current); of_property_read_u8(child, "max-cur", &cfg[i].max_current); + cfg[i].default_trigger = + of_get_property(child, "linux,default-trigger", NULL); i++; } diff --git a/include/linux/platform_data/leds-lp55xx.h b/include/linux/platform_data/leds-lp55xx.h index 51a2ff5..c32de4d 100644 --- a/include/linux/platform_data/leds-lp55xx.h +++ b/include/linux/platform_data/leds-lp55xx.h @@ -22,6 +22,7 @@ struct lp55xx_led_config { const char *name; + const char *default_trigger; u8 chan_nr; u8 led_current; /* mA x10, 0 if led is not connected */ u8 max_current; -- cgit v0.10.2 From b4f31ad6184fd5124abdb69b3b7ee5c18ac76db6 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 19 Sep 2013 21:27:59 -0700 Subject: leds: dac124s085: Remove redundant spi_set_drvdata Driver core sets driver data to NULL upon failure or remove. Signed-off-by: Sachin Kamat Cc: Guennadi Liakhovetski Signed-off-by: Bryan Wu diff --git a/drivers/leds/leds-dac124s085.c b/drivers/leds/leds-dac124s085.c index 1f9d8e6..db3ba8b 100644 --- a/drivers/leds/leds-dac124s085.c +++ b/drivers/leds/leds-dac124s085.c @@ -101,7 +101,6 @@ eledcr: while (i--) led_classdev_unregister(&dac->leds[i].ldev); - spi_set_drvdata(spi, NULL); return ret; } @@ -115,8 +114,6 @@ static int dac124s085_remove(struct spi_device *spi) cancel_work_sync(&dac->leds[i].work); } - spi_set_drvdata(spi, NULL); - return 0; } -- cgit v0.10.2 From c68f46dd6aec29b4db9eb85d014981bbdd686428 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sat, 28 Sep 2013 04:38:30 -0700 Subject: leds: Include linux/of.h header 'of_match_ptr' is defined in linux/of.h. Include it explicitly. Signed-off-by: Sachin Kamat Signed-off-by: Bryan Wu diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index e8b01e5..7ccafde 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index fe3bcbb..6b553d9 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include diff --git a/drivers/leds/leds-lp5562.c b/drivers/leds/leds-lp5562.c index 2585cfd..bf006f4 100644 --- a/drivers/leds/leds-lp5562.c +++ b/drivers/leds/leds-lp5562.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include diff --git a/drivers/leds/leds-lp8501.c b/drivers/leds/leds-lp8501.c index 8d55a780..f1c704f 100644 --- a/drivers/leds/leds-lp8501.c +++ b/drivers/leds/leds-lp8501.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include diff --git a/drivers/leds/leds-ns2.c b/drivers/leds/leds-ns2.c index 141f134..c7a4230 100644 --- a/drivers/leds/leds-ns2.c +++ b/drivers/leds/leds-ns2.c @@ -30,6 +30,7 @@ #include #include #include +#include #include /* -- cgit v0.10.2 From 1e08f72dd248645450b01c86ccc066c0a90767d8 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sat, 28 Sep 2013 04:38:31 -0700 Subject: leds: pwm: Remove redundant of_match_ptr The data structure of_match_ptr() protects is always compiled in. Hence of_match_ptr() is not needed. Signed-off-by: Sachin Kamat Signed-off-by: Bryan Wu diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index bb6f948..2848171 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -232,7 +232,7 @@ static struct platform_driver led_pwm_driver = { .driver = { .name = "leds_pwm", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(of_pwm_leds_match), + .of_match_table = of_pwm_leds_match, }, }; -- cgit v0.10.2 From bb6febdc90efe7f664328075c204eed8e9af7ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20G=C3=BCntner?= Date: Wed, 16 Oct 2013 18:09:17 -0700 Subject: leds: Added driver for the NXP PCA9685 I2C chip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The NXP PCA9685 supports 16 channels/leds using a 12-bit PWM (4095 levels of brightness) This driver supports configuration using platform_data. Signed-off-by: Maximilian Güntner Signed-off-by: Bryan Wu diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 875bbe4..72156c1 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -300,6 +300,16 @@ config LEDS_PCA963X LED driver chip accessed via the I2C bus. Supported devices include PCA9633 and PCA9634 +config LEDS_PCA9685 + tristate "LED support for PCA9685 I2C chip" + depends on LEDS_CLASS + depends on I2C + help + This option enables support for LEDs connected to the PCA9685 + LED driver chip accessed via the I2C bus. + The PCA9685 offers 12-bit PWM (4095 levels of brightness) on + 16 individual channels. + config LEDS_WM831X_STATUS tristate "LED support for status LEDs on WM831x PMICs" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 8979b0b..3cd76db 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_LEDS_OT200) += leds-ot200.o obj-$(CONFIG_LEDS_FSG) += leds-fsg.o obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o obj-$(CONFIG_LEDS_PCA963X) += leds-pca963x.o +obj-$(CONFIG_LEDS_PCA9685) += leds-pca9685.o obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o obj-$(CONFIG_LEDS_DA9052) += leds-da9052.o obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o diff --git a/drivers/leds/leds-pca9685.c b/drivers/leds/leds-pca9685.c new file mode 100644 index 0000000..6e1ef3a --- /dev/null +++ b/drivers/leds/leds-pca9685.c @@ -0,0 +1,213 @@ +/* + * Copyright 2013 Maximilian Güntner + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + * + * Based on leds-pca963x.c driver by + * Peter Meerwald + * + * Driver for the NXP PCA9685 12-Bit PWM LED driver chip. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Register Addresses */ +#define PCA9685_MODE1 0x00 +#define PCA9685_MODE2 0x01 +#define PCA9685_LED0_ON_L 0x06 +#define PCA9685_ALL_LED_ON_L 0xFA + +/* MODE1 Register */ +#define PCA9685_ALLCALL 0x00 +#define PCA9685_SLEEP 0x04 +#define PCA9685_AI 0x05 + +/* MODE2 Register */ +#define PCA9685_INVRT 0x04 +#define PCA9685_OUTDRV 0x02 + +static const struct i2c_device_id pca9685_id[] = { + { "pca9685", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pca9685_id); + +struct pca9685_led { + struct i2c_client *client; + struct work_struct work; + u16 brightness; + struct led_classdev led_cdev; + int led_num; /* 0-15 */ + char name[32]; +}; + +static void pca9685_write_msg(struct i2c_client *client, u8 *buf, u8 len) +{ + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0x00, + .len = len, + .buf = buf + }; + i2c_transfer(client->adapter, &msg, 1); +} + +static void pca9685_all_off(struct i2c_client *client) +{ + u8 i2c_buffer[5] = {PCA9685_ALL_LED_ON_L, 0x00, 0x00, 0x00, 0x10}; + pca9685_write_msg(client, i2c_buffer, 5); +} + +static void pca9685_led_work(struct work_struct *work) +{ + struct pca9685_led *pca9685; + u8 i2c_buffer[5]; + + pca9685 = container_of(work, struct pca9685_led, work); + i2c_buffer[0] = PCA9685_LED0_ON_L + 4 * pca9685->led_num; + /* + * 4095 is the maximum brightness, so we set the ON time to 0x1000 + * which disables the PWM generator for that LED + */ + if (pca9685->brightness == 4095) + *((__le16 *)(i2c_buffer+1)) = cpu_to_le16(0x1000); + else + *((__le16 *)(i2c_buffer+1)) = 0x0000; + + if (pca9685->brightness == 0) + *((__le16 *)(i2c_buffer+3)) = cpu_to_le16(0x1000); + else if (pca9685->brightness == 4095) + *((__le16 *)(i2c_buffer+3)) = 0x0000; + else + *((__le16 *)(i2c_buffer+3)) = cpu_to_le16(pca9685->brightness); + + pca9685_write_msg(pca9685->client, i2c_buffer, 5); +} + +static void pca9685_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct pca9685_led *pca9685; + pca9685 = container_of(led_cdev, struct pca9685_led, led_cdev); + pca9685->brightness = value; + + schedule_work(&pca9685->work); +} + +static int pca9685_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pca9685_led *pca9685; + struct pca9685_platform_data *pdata; + int err; + u8 i; + + pdata = dev_get_platdata(&client->dev); + if (pdata) { + if (pdata->leds.num_leds < 1 || pdata->leds.num_leds > 15) { + dev_err(&client->dev, "board info must claim 1-16 LEDs"); + return -EINVAL; + } + } + + pca9685 = devm_kzalloc(&client->dev, 16 * sizeof(*pca9685), GFP_KERNEL); + if (!pca9685) + return -ENOMEM; + + i2c_set_clientdata(client, pca9685); + pca9685_all_off(client); + + for (i = 0; i < 16; i++) { + pca9685[i].client = client; + pca9685[i].led_num = i; + pca9685[i].name[0] = '\0'; + if (pdata && i < pdata->leds.num_leds) { + if (pdata->leds.leds[i].name) + strncpy(pca9685[i].name, + pdata->leds.leds[i].name, + sizeof(pca9685[i].name)-1); + if (pdata->leds.leds[i].default_trigger) + pca9685[i].led_cdev.default_trigger = + pdata->leds.leds[i].default_trigger; + } + if (strlen(pca9685[i].name) == 0) { + /* + * Write adapter and address to the name as well. + * Otherwise multiple chips attached to one host would + * not work. + */ + snprintf(pca9685[i].name, sizeof(pca9685[i].name), + "pca9685:%d:x%.2x:%d", + client->adapter->nr, client->addr, i); + } + pca9685[i].led_cdev.name = pca9685[i].name; + pca9685[i].led_cdev.max_brightness = 0xfff; + pca9685[i].led_cdev.brightness_set = pca9685_led_set; + + INIT_WORK(&pca9685[i].work, pca9685_led_work); + err = led_classdev_register(&client->dev, &pca9685[i].led_cdev); + if (err < 0) + goto exit; + } + + if (pdata) + i2c_smbus_write_byte_data(client, PCA9685_MODE2, + pdata->outdrv << PCA9685_OUTDRV | + pdata->inverted << PCA9685_INVRT); + else + i2c_smbus_write_byte_data(client, PCA9685_MODE2, + PCA9685_TOTEM_POLE << PCA9685_OUTDRV); + /* Enable Auto-Increment, enable oscillator, ALLCALL/SUBADDR disabled */ + i2c_smbus_write_byte_data(client, PCA9685_MODE1, BIT(PCA9685_AI)); + + return 0; + +exit: + while (i--) { + led_classdev_unregister(&pca9685[i].led_cdev); + cancel_work_sync(&pca9685[i].work); + } + return err; +} + +static int pca9685_remove(struct i2c_client *client) +{ + struct pca9685_led *pca9685 = i2c_get_clientdata(client); + u8 i; + + for (i = 0; i < 16; i++) { + led_classdev_unregister(&pca9685[i].led_cdev); + cancel_work_sync(&pca9685[i].work); + } + pca9685_all_off(client); + return 0; +} + +static struct i2c_driver pca9685_driver = { + .driver = { + .name = "leds-pca9685", + .owner = THIS_MODULE, + }, + .probe = pca9685_probe, + .remove = pca9685_remove, + .id_table = pca9685_id, +}; + +module_i2c_driver(pca9685_driver); + +MODULE_AUTHOR("Maximilian Güntner "); +MODULE_DESCRIPTION("PCA9685 LED Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/leds-pca9685.h b/include/linux/platform_data/leds-pca9685.h new file mode 100644 index 0000000..778e9e4 --- /dev/null +++ b/include/linux/platform_data/leds-pca9685.h @@ -0,0 +1,35 @@ +/* + * Copyright 2013 Maximilian Güntner + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + * + * Based on leds-pca963x.h by Peter Meerwald + * + * LED driver for the NXP PCA9685 PWM chip + * + */ + +#ifndef __LINUX_PCA9685_H +#define __LINUX_PCA9685_H + +#include + +enum pca9685_outdrv { + PCA9685_OPEN_DRAIN, + PCA9685_TOTEM_POLE, +}; + +enum pca9685_inverted { + PCA9685_NOT_INVERTED, + PCA9685_INVERTED, +}; + +struct pca9685_platform_data { + struct led_platform_data leds; + enum pca9685_outdrv outdrv; + enum pca9685_inverted inverted; +}; + +#endif /* __LINUX_PCA9685_H */ -- cgit v0.10.2 From fdfcab17a26ab2179c9d233f8a1b63e93cff9a4a Mon Sep 17 00:00:00 2001 From: Shinya Kuribayashi Date: Tue, 8 Oct 2013 18:01:28 +0900 Subject: clocksource: em_sti: convert to clk_prepare/unprepare Add calls to clk_prepare and unprepare so that EMMA Mobile EV2 can migrate to the common clock framework. Signed-off-by: Shinya Kuribayashi Acked-by: Daniel Lezcano Acked-by: Laurent Pinchart Acked-by: Magnus Damm Signed-off-by: Simon Horman Signed-off-by: Daniel Lezcano diff --git a/drivers/clocksource/em_sti.c b/drivers/clocksource/em_sti.c index 3a5909c..9d17083 100644 --- a/drivers/clocksource/em_sti.c +++ b/drivers/clocksource/em_sti.c @@ -78,7 +78,7 @@ static int em_sti_enable(struct em_sti_priv *p) int ret; /* enable clock */ - ret = clk_enable(p->clk); + ret = clk_prepare_enable(p->clk); if (ret) { dev_err(&p->pdev->dev, "cannot enable clock\n"); return ret; @@ -107,7 +107,7 @@ static void em_sti_disable(struct em_sti_priv *p) em_sti_write(p, STI_INTENCLR, 3); /* stop clock */ - clk_disable(p->clk); + clk_disable_unprepare(p->clk); } static cycle_t em_sti_count(struct em_sti_priv *p) -- cgit v0.10.2 From 9c9b781804e0a278e258f81dfc31c50f80867730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 3 Oct 2013 21:56:29 +0200 Subject: clocksource: Provide timekeeping for efm32 SoCs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An efm32 features 4 16-bit timers with a 10-bit prescaler. This driver provides clocksource and clock event device using one timer instance each. Signed-off-by: Uwe Kleine-König Signed-off-by: Daniel Lezcano diff --git a/Documentation/devicetree/bindings/timer/efm32,timer.txt b/Documentation/devicetree/bindings/timer/efm32,timer.txt new file mode 100644 index 0000000..97a568f --- /dev/null +++ b/Documentation/devicetree/bindings/timer/efm32,timer.txt @@ -0,0 +1,23 @@ +* EFM32 timer hardware + +The efm32 Giant Gecko SoCs come with four 16 bit timers. Two counters can be +connected to form a 32 bit counter. Each timer has three Compare/Capture +channels and can be used as PWM or Quadrature Decoder. Available clock sources +are the cpu's HFPERCLK (with a 10-bit prescaler) or an external pin. + +Required properties: +- compatible : Should be efm32,timer +- reg : Address and length of the register set +- clocks : Should contain a reference to the HFPERCLK + +Optional properties: +- interrupts : Reference to the timer interrupt + +Example: + +timer@40010c00 { + compatible = "efm32,timer"; + reg = <0x40010c00 0x400>; + interrupts = <14>; + clocks = <&cmu clk_HFPERCLKTIMER3>; +}; diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 5e940f8..915a388 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -71,6 +71,14 @@ config CLKSRC_DBX500_PRCMU_SCHED_CLOCK help Use the always on PRCMU Timer as sched_clock +config CLKSRC_EFM32 + bool "Clocksource for Energy Micro's EFM32 SoCs" if !ARCH_EFM32 + depends on OF && ARM && (ARCH_EFM32 || COMPILE_TEST) + default ARCH_EFM32 + help + Support to use the timers of EFM32 SoCs as clock source and clock + event device. + config ARM_ARCH_TIMER bool select CLKSRC_OF if OF diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 704d6d3..33621ef 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o obj-$(CONFIG_ARCH_NSPIRE) += zevio-timer.o obj-$(CONFIG_ARCH_BCM) += bcm_kona_timer.o obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o +obj-$(CONFIG_CLKSRC_EFM32) += time-efm32.o obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o obj-$(CONFIG_VF_PIT_TIMER) += vf_pit_timer.o diff --git a/drivers/clocksource/time-efm32.c b/drivers/clocksource/time-efm32.c new file mode 100644 index 0000000..1a6205b --- /dev/null +++ b/drivers/clocksource/time-efm32.c @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2013 Pengutronix + * Uwe Kleine-Koenig + * + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TIMERn_CTRL 0x00 +#define TIMERn_CTRL_PRESC(val) (((val) & 0xf) << 24) +#define TIMERn_CTRL_PRESC_1024 TIMERn_CTRL_PRESC(10) +#define TIMERn_CTRL_CLKSEL(val) (((val) & 0x3) << 16) +#define TIMERn_CTRL_CLKSEL_PRESCHFPERCLK TIMERn_CTRL_CLKSEL(0) +#define TIMERn_CTRL_OSMEN 0x00000010 +#define TIMERn_CTRL_MODE(val) (((val) & 0x3) << 0) +#define TIMERn_CTRL_MODE_UP TIMERn_CTRL_MODE(0) +#define TIMERn_CTRL_MODE_DOWN TIMERn_CTRL_MODE(1) + +#define TIMERn_CMD 0x04 +#define TIMERn_CMD_START 0x00000001 +#define TIMERn_CMD_STOP 0x00000002 + +#define TIMERn_IEN 0x0c +#define TIMERn_IF 0x10 +#define TIMERn_IFS 0x14 +#define TIMERn_IFC 0x18 +#define TIMERn_IRQ_UF 0x00000002 + +#define TIMERn_TOP 0x1c +#define TIMERn_CNT 0x24 + +struct efm32_clock_event_ddata { + struct clock_event_device evtdev; + void __iomem *base; + unsigned periodic_top; +}; + +static void efm32_clock_event_set_mode(enum clock_event_mode mode, + struct clock_event_device *evtdev) +{ + struct efm32_clock_event_ddata *ddata = + container_of(evtdev, struct efm32_clock_event_ddata, evtdev); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + writel_relaxed(TIMERn_CMD_STOP, ddata->base + TIMERn_CMD); + writel_relaxed(ddata->periodic_top, ddata->base + TIMERn_TOP); + writel_relaxed(TIMERn_CTRL_PRESC_1024 | + TIMERn_CTRL_CLKSEL_PRESCHFPERCLK | + TIMERn_CTRL_MODE_DOWN, + ddata->base + TIMERn_CTRL); + writel_relaxed(TIMERn_CMD_START, ddata->base + TIMERn_CMD); + break; + + case CLOCK_EVT_MODE_ONESHOT: + writel_relaxed(TIMERn_CMD_STOP, ddata->base + TIMERn_CMD); + writel_relaxed(TIMERn_CTRL_PRESC_1024 | + TIMERn_CTRL_CLKSEL_PRESCHFPERCLK | + TIMERn_CTRL_OSMEN | + TIMERn_CTRL_MODE_DOWN, + ddata->base + TIMERn_CTRL); + break; + + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + writel_relaxed(TIMERn_CMD_STOP, ddata->base + TIMERn_CMD); + break; + + case CLOCK_EVT_MODE_RESUME: + break; + } +} + +static int efm32_clock_event_set_next_event(unsigned long evt, + struct clock_event_device *evtdev) +{ + struct efm32_clock_event_ddata *ddata = + container_of(evtdev, struct efm32_clock_event_ddata, evtdev); + + writel_relaxed(TIMERn_CMD_STOP, ddata->base + TIMERn_CMD); + writel_relaxed(evt, ddata->base + TIMERn_CNT); + writel_relaxed(TIMERn_CMD_START, ddata->base + TIMERn_CMD); + + return 0; +} + +static irqreturn_t efm32_clock_event_handler(int irq, void *dev_id) +{ + struct efm32_clock_event_ddata *ddata = dev_id; + + writel_relaxed(TIMERn_IRQ_UF, ddata->base + TIMERn_IFC); + + ddata->evtdev.event_handler(&ddata->evtdev); + + return IRQ_HANDLED; +} + +static struct efm32_clock_event_ddata clock_event_ddata = { + .evtdev = { + .name = "efm32 clockevent", + .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_MODE_PERIODIC, + .set_mode = efm32_clock_event_set_mode, + .set_next_event = efm32_clock_event_set_next_event, + .rating = 200, + }, +}; + +static struct irqaction efm32_clock_event_irq = { + .name = "efm32 clockevent", + .flags = IRQF_TIMER, + .handler = efm32_clock_event_handler, + .dev_id = &clock_event_ddata, +}; + +static int __init efm32_clocksource_init(struct device_node *np) +{ + struct clk *clk; + void __iomem *base; + unsigned long rate; + int ret; + + clk = of_clk_get(np, 0); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + pr_err("failed to get clock for clocksource (%d)\n", ret); + goto err_clk_get; + } + + ret = clk_prepare_enable(clk); + if (ret) { + pr_err("failed to enable timer clock for clocksource (%d)\n", + ret); + goto err_clk_enable; + } + rate = clk_get_rate(clk); + + base = of_iomap(np, 0); + if (!base) { + ret = -EADDRNOTAVAIL; + pr_err("failed to map registers for clocksource\n"); + goto err_iomap; + } + + writel_relaxed(TIMERn_CTRL_PRESC_1024 | + TIMERn_CTRL_CLKSEL_PRESCHFPERCLK | + TIMERn_CTRL_MODE_UP, base + TIMERn_CTRL); + writel_relaxed(TIMERn_CMD_START, base + TIMERn_CMD); + + ret = clocksource_mmio_init(base + TIMERn_CNT, "efm32 timer", + DIV_ROUND_CLOSEST(rate, 1024), 200, 16, + clocksource_mmio_readl_up); + if (ret) { + pr_err("failed to init clocksource (%d)\n", ret); + goto err_clocksource_init; + } + + return 0; + +err_clocksource_init: + + iounmap(base); +err_iomap: + + clk_disable_unprepare(clk); +err_clk_enable: + + clk_put(clk); +err_clk_get: + + return ret; +} + +static int __init efm32_clockevent_init(struct device_node *np) +{ + struct clk *clk; + void __iomem *base; + unsigned long rate; + int irq; + int ret; + + clk = of_clk_get(np, 0); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + pr_err("failed to get clock for clockevent (%d)\n", ret); + goto err_clk_get; + } + + ret = clk_prepare_enable(clk); + if (ret) { + pr_err("failed to enable timer clock for clockevent (%d)\n", + ret); + goto err_clk_enable; + } + rate = clk_get_rate(clk); + + base = of_iomap(np, 0); + if (!base) { + ret = -EADDRNOTAVAIL; + pr_err("failed to map registers for clockevent\n"); + goto err_iomap; + } + + irq = irq_of_parse_and_map(np, 0); + if (!irq) { + ret = -ENOENT; + pr_err("failed to get irq for clockevent\n"); + goto err_get_irq; + } + + writel_relaxed(TIMERn_IRQ_UF, base + TIMERn_IEN); + + clock_event_ddata.base = base; + clock_event_ddata.periodic_top = DIV_ROUND_CLOSEST(rate, 1024 * HZ); + + setup_irq(irq, &efm32_clock_event_irq); + + clockevents_config_and_register(&clock_event_ddata.evtdev, + DIV_ROUND_CLOSEST(rate, 1024), + 0xf, 0xffff); + + return 0; + +err_get_irq: + + iounmap(base); +err_iomap: + + clk_disable_unprepare(clk); +err_clk_enable: + + clk_put(clk); +err_clk_get: + + return ret; +} + +/* + * This function asserts that we have exactly one clocksource and one + * clock_event_device in the end. + */ +static void __init efm32_timer_init(struct device_node *np) +{ + static int has_clocksource, has_clockevent; + int ret; + + if (!has_clocksource) { + ret = efm32_clocksource_init(np); + if (!ret) { + has_clocksource = 1; + return; + } + } + + if (!has_clockevent) { + ret = efm32_clockevent_init(np); + if (!ret) { + has_clockevent = 1; + return; + } + } +} +CLOCKSOURCE_OF_DECLARE(efm32, "efm32,timer", efm32_timer_init); -- cgit v0.10.2 From 71c568c00026f082ca16980e2d9bc506e7d3b145 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 14 Oct 2013 21:07:46 +0200 Subject: clocksource: sun4i: Select CLKSRC_MMIO The Allwinner SoCs timer use the clocksource MMIO functions. We thus need to select them in Kconfig. Signed-off-by: Maxime Ripard Signed-off-by: Daniel Lezcano diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 915a388..bdb953e 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -34,6 +34,7 @@ config ORION_TIMER bool config SUN4I_TIMER + select CLKSRC_MMIO bool config VT8500_TIMER -- cgit v0.10.2 From 12e1480bcb4920c1b02b3793cb48756497919a60 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 14 Oct 2013 21:07:47 +0200 Subject: clocksource: sun4i: Report the minimum tick that we can program We need to wait for at least 2 clock cycles whenever we reprogram our clockevent timer. Report that the minimum number of ticks we can handle is 3 ticks, and remove 3 ticks to the interval programmed to reflect this. Signed-off-by: Maxime Ripard Signed-off-by: Daniel Lezcano diff --git a/drivers/clocksource/sun4i_timer.c b/drivers/clocksource/sun4i_timer.c index 8ead025..46008f9 100644 --- a/drivers/clocksource/sun4i_timer.c +++ b/drivers/clocksource/sun4i_timer.c @@ -37,6 +37,8 @@ #define TIMER_INTVAL_REG(val) (0x10 * (val) + 0x14) #define TIMER_CNTVAL_REG(val) (0x10 * (val) + 0x18) +#define TIMER_SYNC_TICKS 3 + static void __iomem *timer_base; static u32 ticks_per_jiffy; @@ -50,7 +52,7 @@ static void sun4i_clkevt_sync(void) { u32 old = readl(timer_base + TIMER_CNTVAL_REG(1)); - while ((old - readl(timer_base + TIMER_CNTVAL_REG(1))) < 3) + while ((old - readl(timer_base + TIMER_CNTVAL_REG(1))) < TIMER_SYNC_TICKS) cpu_relax(); } @@ -104,7 +106,7 @@ static int sun4i_clkevt_next_event(unsigned long evt, struct clock_event_device *unused) { sun4i_clkevt_time_stop(0); - sun4i_clkevt_time_setup(0, evt); + sun4i_clkevt_time_setup(0, evt - TIMER_SYNC_TICKS); sun4i_clkevt_time_start(0, false); return 0; @@ -187,8 +189,8 @@ static void __init sun4i_timer_init(struct device_node *node) sun4i_clockevent.cpumask = cpumask_of(0); - clockevents_config_and_register(&sun4i_clockevent, rate, 0x1, - 0xffffffff); + clockevents_config_and_register(&sun4i_clockevent, rate, + TIMER_SYNC_TICKS, 0xffffffff); } CLOCKSOURCE_OF_DECLARE(sun4i, "allwinner,sun4i-timer", sun4i_timer_init); -- cgit v0.10.2 From 3353652ce0eb22854b1748b8320c2b81912953a1 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 14 Oct 2013 21:07:48 +0200 Subject: clocksource: sun4i: remove IRQF_DISABLED IRQF_DISABLED is a no-op nowadays, so we can safely remove it. Signed-off-by: Maxime Ripard Signed-off-by: Daniel Lezcano diff --git a/drivers/clocksource/sun4i_timer.c b/drivers/clocksource/sun4i_timer.c index 46008f9..2fb4695 100644 --- a/drivers/clocksource/sun4i_timer.c +++ b/drivers/clocksource/sun4i_timer.c @@ -133,7 +133,7 @@ static irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id) static struct irqaction sun4i_timer_irq = { .name = "sun4i_timer0", - .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, + .flags = IRQF_TIMER | IRQF_IRQPOLL, .handler = sun4i_timer_interrupt, .dev_id = &sun4i_clockevent, }; -- cgit v0.10.2 From 5fcdb9dc98e76001060bb89e3b1269384f9f5201 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sun, 20 Oct 2013 15:14:57 -0700 Subject: gpiolib: devres: fix devm_gpiod_get_index() Fix the return value if devm_gpiod_get_index(). It was returning 0 while it should return the obtained GPIO descriptor. Signed-off-by: Alexandre Courbot Signed-off-by: Linus Walleij diff --git a/drivers/gpio/devres.c b/drivers/gpio/devres.c index 2caa257..fceebdc 100644 --- a/drivers/gpio/devres.c +++ b/drivers/gpio/devres.c @@ -80,7 +80,7 @@ struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev, *dr = desc; devres_add(dev, dr); - return 0; + return desc; } EXPORT_SYMBOL(devm_gpiod_get_index); -- cgit v0.10.2 From 86467ff2ddca94c0d8d10b92b5916e68c0cad8a9 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 21 Oct 2013 10:55:29 +0800 Subject: pinctrl: tb10x: fix the error handling in tb10x_pinctrl_probe() This patch fix the error handling in tb10x_pinctrl_probe(): - devm_ioremap_resource() return ERR_PTR() and never return NULL - remove the dev_err call to avoid redundant error message - pinctrl_register() returns NULL not ERR_PTR() Signed-off-by: Wei Yongjun Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/pinctrl-tb10x.c b/drivers/pinctrl/pinctrl-tb10x.c index 2e1ea56..9f7fa9b 100644 --- a/drivers/pinctrl/pinctrl-tb10x.c +++ b/drivers/pinctrl/pinctrl-tb10x.c @@ -806,9 +806,8 @@ static int tb10x_pinctrl_probe(struct platform_device *pdev) mutex_init(&state->mutex); state->base = devm_ioremap_resource(dev, mem); - if (!state->base) { - dev_err(dev, "Request register region failed.\n"); - ret = -EBUSY; + if (IS_ERR(state->base)) { + ret = PTR_ERR(state->base); goto fail; } @@ -830,9 +829,9 @@ static int tb10x_pinctrl_probe(struct platform_device *pdev) } state->pctl = pinctrl_register(&tb10x_pindesc, dev, state); - if (IS_ERR(state->pctl)) { + if (!state->pctl) { dev_err(dev, "could not register TB10x pin driver\n"); - ret = PTR_ERR(state->pctl); + ret = -EINVAL; goto fail; } -- cgit v0.10.2 From 7ddd183738fc39cbe3fbfa1954082d4189f14a5c Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 23 Oct 2013 10:46:00 +0200 Subject: pinctrl: tb10x: use module_platform_driver to simplify the code module_platform_driver() makes the code simpler by eliminating boilerplate code. Signed-off-by: Wei Yongjun Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/pinctrl-tb10x.c b/drivers/pinctrl/pinctrl-tb10x.c index 9f7fa9b..c5e0f69 100644 --- a/drivers/pinctrl/pinctrl-tb10x.c +++ b/drivers/pinctrl/pinctrl-tb10x.c @@ -869,17 +869,7 @@ static struct platform_driver tb10x_pinctrl_pdrv = { } }; -static int __init tb10x_iopinctrl_init(void) -{ - return platform_driver_register(&tb10x_pinctrl_pdrv); -} - -static void __exit tb10x_iopinctrl_exit(void) -{ - platform_driver_unregister(&tb10x_pinctrl_pdrv); -} +module_platform_driver(tb10x_pinctrl_pdrv); MODULE_AUTHOR("Christian Ruppert "); MODULE_LICENSE("GPL"); -module_init(tb10x_iopinctrl_init); -module_exit(tb10x_iopinctrl_exit); -- cgit v0.10.2 From f2e9394d1a3bc7a4f646345d93468c8aa1f7d2ee Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 21 Oct 2013 14:47:14 +0530 Subject: pinctrl: mvebu: remove redundant of_match_ptr The data structure of_match_ptr() protects is always compiled in. Hence of_match_ptr() is not needed. Signed-off-by: Sachin Kamat Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-370.c b/drivers/pinctrl/mvebu/pinctrl-armada-370.c index 48e21a2..ae1f760 100644 --- a/drivers/pinctrl/mvebu/pinctrl-armada-370.c +++ b/drivers/pinctrl/mvebu/pinctrl-armada-370.c @@ -408,7 +408,7 @@ static struct platform_driver armada_370_pinctrl_driver = { .driver = { .name = "armada-370-pinctrl", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(armada_370_pinctrl_of_match), + .of_match_table = armada_370_pinctrl_of_match, }, .probe = armada_370_pinctrl_probe, .remove = armada_370_pinctrl_remove, diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-xp.c b/drivers/pinctrl/mvebu/pinctrl-armada-xp.c index ab5dc04..843a51f 100644 --- a/drivers/pinctrl/mvebu/pinctrl-armada-xp.c +++ b/drivers/pinctrl/mvebu/pinctrl-armada-xp.c @@ -455,7 +455,7 @@ static struct platform_driver armada_xp_pinctrl_driver = { .driver = { .name = "armada-xp-pinctrl", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(armada_xp_pinctrl_of_match), + .of_match_table = armada_xp_pinctrl_of_match, }, .probe = armada_xp_pinctrl_probe, .remove = armada_xp_pinctrl_remove, diff --git a/drivers/pinctrl/mvebu/pinctrl-dove.c b/drivers/pinctrl/mvebu/pinctrl-dove.c index 360b9b2..4726839 100644 --- a/drivers/pinctrl/mvebu/pinctrl-dove.c +++ b/drivers/pinctrl/mvebu/pinctrl-dove.c @@ -806,7 +806,7 @@ static struct platform_driver dove_pinctrl_driver = { .driver = { .name = "dove-pinctrl", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(dove_pinctrl_of_match), + .of_match_table = dove_pinctrl_of_match, }, .probe = dove_pinctrl_probe, .remove = dove_pinctrl_remove, diff --git a/drivers/pinctrl/mvebu/pinctrl-kirkwood.c b/drivers/pinctrl/mvebu/pinctrl-kirkwood.c index cdd483d..6b504b5 100644 --- a/drivers/pinctrl/mvebu/pinctrl-kirkwood.c +++ b/drivers/pinctrl/mvebu/pinctrl-kirkwood.c @@ -471,7 +471,7 @@ static struct platform_driver kirkwood_pinctrl_driver = { .driver = { .name = "kirkwood-pinctrl", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(kirkwood_pinctrl_of_match), + .of_match_table = kirkwood_pinctrl_of_match, }, .probe = kirkwood_pinctrl_probe, .remove = kirkwood_pinctrl_remove, -- cgit v0.10.2 From 9e556dcc55774c9a1032f32baa0e5cfafede8b70 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Wed, 23 Oct 2013 16:31:50 +0800 Subject: spi: spi-imx: only enable the clocks when we start to transfer a message Current code keeps the clocks enabled all the time, it wastes the power when there is no operaiton on the spi controller. In order to save the power, this patch adds the two hooks: spi_imx_prepare_message: enable the clocks for this message spi_imx_unprepare_message: disable the clocks. This patch also disables the clocks in the end of the probe. Signed-off-by: Huang Shijie Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 15323d8..cc9defe 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -749,6 +749,35 @@ static void spi_imx_cleanup(struct spi_device *spi) { } +static int +spi_imx_prepare_message(struct spi_master *master, struct spi_message *msg) +{ + struct spi_imx_data *spi_imx = spi_master_get_devdata(master); + int ret; + + ret = clk_enable(spi_imx->clk_per); + if (ret) + return ret; + + ret = clk_enable(spi_imx->clk_ipg); + if (ret) { + clk_disable(spi_imx->clk_per); + return ret; + } + + return 0; +} + +static int +spi_imx_unprepare_message(struct spi_master *master, struct spi_message *msg) +{ + struct spi_imx_data *spi_imx = spi_master_get_devdata(master); + + clk_disable(spi_imx->clk_ipg); + clk_disable(spi_imx->clk_per); + return 0; +} + static int spi_imx_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -810,6 +839,8 @@ static int spi_imx_probe(struct platform_device *pdev) spi_imx->bitbang.txrx_bufs = spi_imx_transfer; spi_imx->bitbang.master->setup = spi_imx_setup; spi_imx->bitbang.master->cleanup = spi_imx_cleanup; + spi_imx->bitbang.master->prepare_message = spi_imx_prepare_message; + spi_imx->bitbang.master->unprepare_message = spi_imx_unprepare_message; spi_imx->bitbang.master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; init_completion(&spi_imx->xfer_done); @@ -872,6 +903,8 @@ static int spi_imx_probe(struct platform_device *pdev) dev_info(&pdev->dev, "probed\n"); + clk_disable(spi_imx->clk_ipg); + clk_disable(spi_imx->clk_per); return ret; out_clk_put: -- cgit v0.10.2 From 61e310a1edf6f325939245c05d3e9b662c70a43d Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Wed, 16 Oct 2013 16:12:33 +0200 Subject: pinctrl: at91: correct a few typos Signed-off-by: Alexandre Belloni Acked-by: Nicolas Ferre Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index 0c8bf93..c4598dc 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -243,7 +243,7 @@ static int at91_dt_node_to_map(struct pinctrl_dev *pctldev, int i; /* - * first find the group of this node and check if we need create + * first find the group of this node and check if we need to create * config maps for pins */ grp = at91_pinctrl_find_group_by_name(info, np->name); @@ -572,7 +572,7 @@ static int at91_pmx_enable(struct pinctrl_dev *pctldev, unsigned selector, info->functions[selector].name, info->groups[group].name); /* first check that all the pins of the group are valid with a valid - * paramter */ + * parameter */ for (i = 0; i < npins; i++) { pin = &pins_conf[i]; ret = pin_check_config(info, info->groups[group].name, i, pin); @@ -966,7 +966,7 @@ static int at91_pinctrl_probe_dt(struct platform_device *pdev, at91_pinctrl_child_count(info, np); if (info->nbanks < 1) { - dev_err(&pdev->dev, "you need to specify atleast one gpio-controller\n"); + dev_err(&pdev->dev, "you need to specify at least one gpio-controller\n"); return -EINVAL; } @@ -1503,7 +1503,7 @@ static int at91_gpio_of_irq_setup(struct device_node *node, if (at91_gpio->pioc_idx) prev = gpio_chips[at91_gpio->pioc_idx - 1]; - /* The toplevel handler handles one bank of GPIOs, except + /* The top level handler handles one bank of GPIOs, except * on some SoC it can handles up to three... * We only set up the handler for the first of the list. */ -- cgit v0.10.2 From 543c954d6807ad0682c37846b7b9c423cd941415 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 21 Oct 2013 11:12:02 +0800 Subject: spi: atmel: fix return value check in atmel_spi_probe() In case of error, the function devm_ioremap_resource() returns ERR_PTR() and never returns NULL. The NULL test in the return value check should be replaced with IS_ERR(). Signed-off-by: Wei Yongjun Acked-by: Nicolas Ferre Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 118a938..273db0b 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -1547,8 +1547,10 @@ static int atmel_spi_probe(struct platform_device *pdev) as->pdev = pdev; as->regs = devm_ioremap_resource(&pdev->dev, regs); - if (!as->regs) + if (IS_ERR(as->regs)) { + ret = PTR_ERR(as->regs); goto out_free_buffer; + } as->phybase = regs->start; as->irq = irq; as->clk = clk; -- cgit v0.10.2 From 62561b39ea346ec2e48e01de9fd6f38383b67bd3 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Fri, 18 Oct 2013 18:37:45 +0300 Subject: ASoC: davinci-mcasp: Improve DT bindings document Makes interrupts property optional as the interrupts are not currently used by the driver and adds interrupt-names property to name listed interrupts. Currently know interrupt names are "tx" and "rx". - Improve tdm-slots propery description - Improve op-mode property description - Add pinctrl-names and pinctrl-0 properties - Remove #address-cells and #size-cells as they are not needed. - Bracket named interrupts property tuples for uniformity. - Add missing "for" to serial-dir prop in DT bindings doc. Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt index 1945aec..b925bf9 100644 --- a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt +++ b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt @@ -11,13 +11,14 @@ Required properties: * "mpu" for the main registers (required). For compatibility with existing software, it is recommended this is the first entry. * "dat" for separate data port register access (optional). -- interrupts : Interrupt number for McASP -- op-mode : I2S/DIT ops mode. -- tdm-slots : Slots for TDM operation. +- op-mode : I2S/DIT ops mode. 0 for I2S mode. 1 for DIT mode used for S/PDIF, + IEC60958-1, and AES-3 formats. +- tdm-slots : Slots for TDM operation. Indicates number of channels transmitted + or received over one serializer. - num-serializer : Serializers used by McASP. -- serial-dir : A list of serializer pin mode. The list number should be equal - to "num-serializer" parameter. Each entry is a number indication - serializer pin direction. (0 - INACTIVE, 1 - TX, 2 - RX) +- serial-dir : A list of serializer configuration. Each entry is a number + indication for serializer pin direction. + (0 - INACTIVE, 1 - TX, 2 - RX) - dmas: two element list of DMA controller phandles and DMA request line ordered pairs. - dma-names: identifier string for each DMA request line in the dmas property. @@ -31,16 +32,21 @@ Optional properties: - rx-num-evt : FIFO levels. - sram-size-playback : size of sram to be allocated during playback - sram-size-capture : size of sram to be allocated during capture +- interrupts : Interrupt numbers for McASP, currently not used by the driver +- interrupt-names : Known interrupt names are "tx" and "rx" +- pinctrl-0: Should specify pin control group used for this controller. +- pinctrl-names: Should contain only one value - "default", for more details + please refer to pinctrl-bindings.txt + Example: mcasp0: mcasp0@1d00000 { compatible = "ti,da830-mcasp-audio"; - #address-cells = <1>; - #size-cells = <0>; reg = <0x100000 0x3000>; reg-names "mpu"; - interrupts = <82 83>; + interrupts = <82>, <83>; + interrupts-names = "tx", "rx"; op-mode = <0>; /* MCASP_IIS_MODE */ tdm-slots = <2>; num-serializer = <16>; -- cgit v0.10.2 From 1427e660b49e87cd842dba94158b0fc73030c17e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 18 Oct 2013 18:37:46 +0300 Subject: ASoC: davinci-mcasp: Remove redundant num-serializer DT parameter The serial-dir array gives this information so there is no need to have the num-serializer property in DT description. Just ignore the property in the driver the DTS files can be updated separately without regression. Update the documentation at the same time for davinci-mcasp Signed-off-by: Peter Ujfalusi Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt index b925bf9..0aa416b 100644 --- a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt +++ b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt @@ -49,7 +49,6 @@ mcasp0: mcasp0@1d00000 { interrupts-names = "tx", "rx"; op-mode = <0>; /* MCASP_IIS_MODE */ tdm-slots = <2>; - num-serializer = <16>; serial-dir = < 0 0 0 0 /* 0: INACTIVE, 1: TX, 2: RX */ 0 0 0 0 diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index bbc9a07..71e14bb3 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -1050,7 +1050,6 @@ static struct snd_platform_data *davinci_mcasp_set_pdata_from_of( struct of_phandle_args dma_spec; const u32 *of_serial_dir32; - u8 *of_serial_dir; u32 val; int i, ret = 0; @@ -1081,32 +1080,21 @@ static struct snd_platform_data *davinci_mcasp_set_pdata_from_of( pdata->tdm_slots = val; } - ret = of_property_read_u32(np, "num-serializer", &val); - if (ret >= 0) - pdata->num_serializer = val; - of_serial_dir32 = of_get_property(np, "serial-dir", &val); val /= sizeof(u32); - if (val != pdata->num_serializer) { - dev_err(&pdev->dev, - "num-serializer(%d) != serial-dir size(%d)\n", - pdata->num_serializer, val); - ret = -EINVAL; - goto nodata; - } - if (of_serial_dir32) { - of_serial_dir = devm_kzalloc(&pdev->dev, - (sizeof(*of_serial_dir) * val), - GFP_KERNEL); + u8 *of_serial_dir = devm_kzalloc(&pdev->dev, + (sizeof(*of_serial_dir) * val), + GFP_KERNEL); if (!of_serial_dir) { ret = -ENOMEM; goto nodata; } - for (i = 0; i < pdata->num_serializer; i++) + for (i = 0; i < val; i++) of_serial_dir[i] = be32_to_cpup(&of_serial_dir32[i]); + pdata->num_serializer = val; pdata->serial_dir = of_serial_dir; } -- cgit v0.10.2 From 7fcd427465e710d0c4e2737d2f02b2ffa14b9bb3 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 15 Oct 2013 20:14:21 +0100 Subject: mfd: Allow mapping regulator supplies to MFD device from children Occasionally, it is useful to map supplies from a child device onto the MFD device. A typical usecase for this would be if the MFD device is represented as a single node in device tree. All supplies will be defined in device tree as existing on the MFD device. When a child depends on frameworks which might have no knowledge of MFD to lookup supplies on its behalf the supply will not be found. This patch adds a list of supplies that should be looked up on the parent rather than the child as part of the mfd_cell structure. Signed-off-by: Charles Keepax Acked-by: Lee Jones Signed-off-by: Mark Brown diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index f421586..adc8ea3 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -20,6 +20,7 @@ #include #include #include +#include static struct device_type mfd_dev_type = { .name = "mfd_device", @@ -99,6 +100,13 @@ static int mfd_add_device(struct device *parent, int id, pdev->dev.dma_mask = parent->dma_mask; pdev->dev.dma_parms = parent->dma_parms; + ret = devm_regulator_bulk_register_supply_alias( + &pdev->dev, cell->parent_supplies, + parent, cell->parent_supplies, + cell->num_parent_supplies); + if (ret < 0) + goto fail_res; + if (parent->of_node && cell->of_compatible) { for_each_child_of_node(parent->of_node, np) { if (of_device_is_compatible(np, cell->of_compatible)) { @@ -112,12 +120,12 @@ static int mfd_add_device(struct device *parent, int id, ret = platform_device_add_data(pdev, cell->platform_data, cell->pdata_size); if (ret) - goto fail_res; + goto fail_alias; } ret = mfd_platform_add_cell(pdev, cell); if (ret) - goto fail_res; + goto fail_alias; for (r = 0; r < cell->num_resources; r++) { res[r].name = cell->resources[r].name; @@ -152,17 +160,17 @@ static int mfd_add_device(struct device *parent, int id, if (!cell->ignore_resource_conflicts) { ret = acpi_check_resource_conflict(&res[r]); if (ret) - goto fail_res; + goto fail_alias; } } ret = platform_device_add_resources(pdev, res, cell->num_resources); if (ret) - goto fail_res; + goto fail_alias; ret = platform_device_add(pdev); if (ret) - goto fail_res; + goto fail_alias; if (cell->pm_runtime_no_callbacks) pm_runtime_no_callbacks(&pdev->dev); @@ -171,6 +179,10 @@ static int mfd_add_device(struct device *parent, int id, return 0; +fail_alias: + devm_regulator_bulk_unregister_supply_alias(&pdev->dev, + cell->parent_supplies, + cell->num_parent_supplies); fail_res: kfree(res); fail_device: diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index cebe97e..7314fc4 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h @@ -59,6 +59,12 @@ struct mfd_cell { * pm_runtime_no_callbacks(). */ bool pm_runtime_no_callbacks; + + /* A list of regulator supplies that should be mapped to the MFD + * device rather than the child device when requested + */ + const char **parent_supplies; + int num_parent_supplies; }; /* -- cgit v0.10.2 From 32dadef2190efd2e06331825b11881daf100d6d9 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 15 Oct 2013 20:14:22 +0100 Subject: mfd: arizona: Specify supply mappings for Arizona CODECs The CODEC power supplies should be looked up on the Arizona device as they will be created here by device tree also update the only user of non-device tree bindings. Signed-off-by: Charles Keepax Acked-by: Lee Jones Signed-off-by: Mark Brown diff --git a/arch/arm/mach-s3c64xx/mach-crag6410.c b/arch/arm/mach-s3c64xx/mach-crag6410.c index eb8e5a1..f27ca3b 100644 --- a/arch/arm/mach-s3c64xx/mach-crag6410.c +++ b/arch/arm/mach-s3c64xx/mach-crag6410.c @@ -310,10 +310,6 @@ static struct regulator_consumer_supply wallvdd_consumers[] = { REGULATOR_SUPPLY("SPKVDDL", "spi0.1"), REGULATOR_SUPPLY("SPKVDDR", "spi0.1"), - REGULATOR_SUPPLY("SPKVDDL", "wm5102-codec"), - REGULATOR_SUPPLY("SPKVDDR", "wm5102-codec"), - REGULATOR_SUPPLY("SPKVDDL", "wm5110-codec"), - REGULATOR_SUPPLY("SPKVDDR", "wm5110-codec"), REGULATOR_SUPPLY("DC1VDD", "0-0034"), REGULATOR_SUPPLY("DC2VDD", "0-0034"), @@ -653,14 +649,6 @@ static struct regulator_consumer_supply pvdd_1v8_consumers[] = { REGULATOR_SUPPLY("DBVDD3", "spi0.1"), REGULATOR_SUPPLY("LDOVDD", "spi0.1"), REGULATOR_SUPPLY("CPVDD", "spi0.1"), - - REGULATOR_SUPPLY("DBVDD2", "wm5102-codec"), - REGULATOR_SUPPLY("DBVDD3", "wm5102-codec"), - REGULATOR_SUPPLY("CPVDD", "wm5102-codec"), - - REGULATOR_SUPPLY("DBVDD2", "wm5110-codec"), - REGULATOR_SUPPLY("DBVDD3", "wm5110-codec"), - REGULATOR_SUPPLY("CPVDD", "wm5110-codec"), }; static struct regulator_init_data pvdd_1v8 = { diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 5ac3aa4..022b186 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -569,13 +569,25 @@ static struct mfd_cell early_devs[] = { { .name = "arizona-ldo1" }, }; +static const char *wm5102_supplies[] = { + "DBVDD2", + "DBVDD3", + "CPVDD", + "SPKVDDL", + "SPKVDDR", +}; + static struct mfd_cell wm5102_devs[] = { { .name = "arizona-micsupp" }, { .name = "arizona-extcon" }, { .name = "arizona-gpio" }, { .name = "arizona-haptics" }, { .name = "arizona-pwm" }, - { .name = "wm5102-codec" }, + { + .name = "wm5102-codec", + .parent_supplies = wm5102_supplies, + .num_parent_supplies = ARRAY_SIZE(wm5102_supplies), + }, }; static struct mfd_cell wm5110_devs[] = { @@ -584,7 +596,17 @@ static struct mfd_cell wm5110_devs[] = { { .name = "arizona-gpio" }, { .name = "arizona-haptics" }, { .name = "arizona-pwm" }, - { .name = "wm5110-codec" }, + { + .name = "wm5110-codec", + .parent_supplies = wm5102_supplies, + .num_parent_supplies = ARRAY_SIZE(wm5102_supplies), + }, +}; + +static const char *wm8997_supplies[] = { + "DBVDD2", + "CPVDD", + "SPKVDD", }; static struct mfd_cell wm8997_devs[] = { @@ -593,7 +615,11 @@ static struct mfd_cell wm8997_devs[] = { { .name = "arizona-gpio" }, { .name = "arizona-haptics" }, { .name = "arizona-pwm" }, - { .name = "wm8997-codec" }, + { + .name = "wm8997-codec", + .parent_supplies = wm8997_supplies, + .num_parent_supplies = ARRAY_SIZE(wm8997_supplies), + }, }; int arizona_dev_init(struct arizona *arizona) -- cgit v0.10.2 From 955c1cab809edfb5429603c68493363074ac20cf Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Wed, 23 Oct 2013 09:40:02 +0100 Subject: powerpc: Don't corrupt user registers on 32-bit Commit de79f7b9f6 ("powerpc: Put FP/VSX and VR state into structures") modified load_up_fpu() and load_up_altivec() in such a way that they now use r7 and r8. Unfortunately, the callers of these functions on 32-bit machines then return to userspace via fast_exception_return, which doesn't restore all of the volatile GPRs, but only r1, r3 -- r6 and r9 -- r12. This was causing userspace segfaults and other userspace misbehaviour on 32-bit machines. This fixes the problem by changing the register usage of load_up_fpu() and load_up_altivec() to avoid using r7 and r8 and instead use r6 and r10. This also adds comments to those functions saying which registers may be used. Signed-off-by: Paul Mackerras Tested-by: Scott Wood (on e500mc, so no altivec) Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/fpu.S b/arch/powerpc/kernel/fpu.S index 4dca05e..f7f5b8b 100644 --- a/arch/powerpc/kernel/fpu.S +++ b/arch/powerpc/kernel/fpu.S @@ -106,6 +106,8 @@ _GLOBAL(store_fp_state) * and save its floating-point registers in its thread_struct. * Load up this task's FP registers from its thread_struct, * enable the FPU for the current task and return to the task. + * Note that on 32-bit this can only use registers that will be + * restored by fast_exception_return, i.e. r3 - r6, r10 and r11. */ _GLOBAL(load_up_fpu) mfmsr r5 @@ -131,10 +133,10 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX) beq 1f toreal(r4) addi r4,r4,THREAD /* want last_task_used_math->thread */ - addi r8,r4,THREAD_FPSTATE - SAVE_32FPVSRS(0, R5, R8) + addi r10,r4,THREAD_FPSTATE + SAVE_32FPVSRS(0, R5, R10) mffs fr0 - stfd fr0,FPSTATE_FPSCR(r8) + stfd fr0,FPSTATE_FPSCR(r10) PPC_LL r5,PT_REGS(r4) toreal(r5) PPC_LL r4,_MSR-STACK_FRAME_OVERHEAD(r5) @@ -157,10 +159,10 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX) or r12,r12,r4 std r12,_MSR(r1) #endif - addi r7,r5,THREAD_FPSTATE - lfd fr0,FPSTATE_FPSCR(r7) + addi r10,r5,THREAD_FPSTATE + lfd fr0,FPSTATE_FPSCR(r10) MTFSF_L(fr0) - REST_32FPVSRS(0, R4, R7) + REST_32FPVSRS(0, R4, R10) #ifndef CONFIG_SMP subi r4,r5,THREAD fromreal(r4) diff --git a/arch/powerpc/kernel/vector.S b/arch/powerpc/kernel/vector.S index eacda4e..0458a9a 100644 --- a/arch/powerpc/kernel/vector.S +++ b/arch/powerpc/kernel/vector.S @@ -64,6 +64,9 @@ _GLOBAL(store_vr_state) * Enables the VMX for use in the kernel on return. * On SMP we know the VMX is free, since we give it up every * switch (ie, no lazy save of the vector registers). + * + * Note that on 32-bit this can only use registers that will be + * restored by fast_exception_return, i.e. r3 - r6, r10 and r11. */ _GLOBAL(load_up_altivec) mfmsr r5 /* grab the current MSR */ @@ -89,11 +92,11 @@ _GLOBAL(load_up_altivec) /* Save VMX state to last_task_used_altivec's THREAD struct */ toreal(r4) addi r4,r4,THREAD - addi r7,r4,THREAD_VRSTATE - SAVE_32VRS(0,r5,r7) + addi r6,r4,THREAD_VRSTATE + SAVE_32VRS(0,r5,r6) mfvscr vr0 li r10,VRSTATE_VSCR - stvx vr0,r10,r7 + stvx vr0,r10,r6 /* Disable VMX for last_task_used_altivec */ PPC_LL r5,PT_REGS(r4) toreal(r5) @@ -125,13 +128,13 @@ _GLOBAL(load_up_altivec) oris r12,r12,MSR_VEC@h std r12,_MSR(r1) #endif - addi r7,r5,THREAD_VRSTATE + addi r6,r5,THREAD_VRSTATE li r4,1 li r10,VRSTATE_VSCR stw r4,THREAD_USED_VR(r5) - lvx vr0,r10,r7 + lvx vr0,r10,r6 mtvscr vr0 - REST_32VRS(0,r4,r7) + REST_32VRS(0,r4,r6) #ifndef CONFIG_SMP /* Update last_task_used_altivec to 'current' */ subi r4,r5,THREAD /* Back to 'current' */ -- cgit v0.10.2 From d7711dc5930ced241c4f6e9b14df2a92814f9f12 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 19 Oct 2013 14:13:04 +0100 Subject: ASoC: ep93xx: Open code dma channel request Currently the ep93xx DMA code is one of the few users relying on the fact that the compat code uses the dma_data as the filter data for non-DT channel requests. Since the rest of the core expects this to be a struct snd_dmaengine_dai_data this isn't terribly helpful this will be changed to use the already existing filter data so avoid breaking ep93xx by open coding the current behaviour. Signed-off-by: Mark Brown Acked-by: Lars-Peter Clausen diff --git a/sound/soc/cirrus/ep93xx-pcm.c b/sound/soc/cirrus/ep93xx-pcm.c index 0e9f56e..cfe517e 100644 --- a/sound/soc/cirrus/ep93xx-pcm.c +++ b/sound/soc/cirrus/ep93xx-pcm.c @@ -57,9 +57,22 @@ static bool ep93xx_pcm_dma_filter(struct dma_chan *chan, void *filter_param) return false; } +static struct dma_chan *ep93xx_compat_request_channel( + struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream) +{ + struct snd_dmaengine_dai_dma_data *dma_data; + + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + + return snd_dmaengine_pcm_request_channel(ep93xx_pcm_dma_filter, + dma_data); +} + static const struct snd_dmaengine_pcm_config ep93xx_dmaengine_pcm_config = { .pcm_hardware = &ep93xx_pcm_hardware, .compat_filter_fn = ep93xx_pcm_dma_filter, + .compat_request_channel = ep93xx_compat_request_channel, .prealloc_buffer_size = 131072, }; -- cgit v0.10.2 From 0eef5381b7271702c7e65c637cb46804c482a90a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 19 Oct 2013 14:17:03 +0100 Subject: ASoC: tegra: Remove redundant initialisation of compat_filter_fn Setting a field in a static struct to NULL has no effect so don't bother (and don't generate false positives for grep). Signed-off-by: Mark Brown Acked-by: Lars-Peter Clausen Acked-by: Stephen Warren diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c index f056f63..7b2d23b 100644 --- a/sound/soc/tegra/tegra_pcm.c +++ b/sound/soc/tegra/tegra_pcm.c @@ -56,7 +56,6 @@ static const struct snd_pcm_hardware tegra_pcm_hardware = { static const struct snd_dmaengine_pcm_config tegra_dmaengine_pcm_config = { .pcm_hardware = &tegra_pcm_hardware, .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, - .compat_filter_fn = NULL, .prealloc_buffer_size = PAGE_SIZE * 8, }; -- cgit v0.10.2 From 92ec11809565cf6429c75204e99e0f583b5c9d7c Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 23 Oct 2013 13:40:55 +0200 Subject: sched/wait: Fix build breakage The wait_event_interruptible_lock_irq() macro is missing a semi-colon which causes a build failure in the i915 DRM driver. Signed-off-by: Thierry Reding Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1382528455-29911-1-git-send-email-treding@nvidia.com Signed-off-by: Ingo Molnar diff --git a/include/linux/wait.h b/include/linux/wait.h index ec099b0..3b23afa 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -732,7 +732,7 @@ do { \ int __ret = 0; \ if (!(condition)) \ __ret = __wait_event_interruptible_lock_irq(wq, \ - condition, lock,) \ + condition, lock,); \ __ret; \ }) -- cgit v0.10.2 From 11a4d435a2d75918039540f08b259969c63b8635 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 22 Oct 2013 15:24:58 -0300 Subject: perf test: Clarify the "sample parsing" test entry Before: [root@sandy ~]# perf test -v 22 22: Test sample parsing : --- start --- sample format has changed - test needs updating ---- end ---- Test sample parsing: FAILED! [root@sandy ~]# After: [root@sandy ~]# perf test -v 22 22: Test sample parsing : --- start --- sample format has changed, some new PERF_SAMPLE_ bit was introduced - test needs updating ---- end ---- Test sample parsing: FAILED! [root@sandy ~]# Cc: Adrian Hunter Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-8cazc2fpmk70jcbww8c0cobx@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-parsing.c index 77f598d..17d000c 100644 --- a/tools/perf/tests/sample-parsing.c +++ b/tools/perf/tests/sample-parsing.c @@ -276,7 +276,7 @@ int test__sample_parsing(void) * were added. */ if (PERF_SAMPLE_MAX > PERF_SAMPLE_IDENTIFIER << 1) { - pr_debug("sample format has changed - test needs updating\n"); + pr_debug("sample format has changed, some new PERF_SAMPLE_ bit was introduced - test needs updating\n"); return -1; } -- cgit v0.10.2 From 4ac2f1c1014a121f1493a9d5207258793c576438 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 22 Oct 2013 15:28:11 -0300 Subject: perf test: Consider PERF_SAMPLE_TRANSACTION in the "sample parsing" test [root@sandy ~]# perf test -v 22 22: Test sample parsing : --- start --- sample format has changed, some new PERF_SAMPLE_ bit was introduced - test needs updating ---- end ---- Test sample parsing: FAILED! [root@sandy ~]# Cc: Adrian Hunter Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-cx83wuzz30m10m4s1xt0ocyq@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-parsing.c index 17d000c..61c9da2 100644 --- a/tools/perf/tests/sample-parsing.c +++ b/tools/perf/tests/sample-parsing.c @@ -275,7 +275,7 @@ int test__sample_parsing(void) * Fail the test if it has not been updated when new sample format bits * were added. */ - if (PERF_SAMPLE_MAX > PERF_SAMPLE_IDENTIFIER << 1) { + if (PERF_SAMPLE_MAX > PERF_SAMPLE_TRANSACTION << 1) { pr_debug("sample format has changed, some new PERF_SAMPLE_ bit was introduced - test needs updating\n"); return -1; } -- cgit v0.10.2 From c824c4338ac47979c69ba6f8faab33670ae179df Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 22 Oct 2013 19:01:31 -0300 Subject: perf tools: Stop using 'self' in some more places As suggested by tglx, 'self' should be replaced by something that is more useful. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-fmblhc6tbb99tk1q8vowtsbj@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 03cfa59..17c6b49 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -118,11 +118,11 @@ static int hist_entry__tty_annotate(struct hist_entry *he, ann->print_line, ann->full_paths, 0, 0); } -static void hists__find_annotations(struct hists *self, +static void hists__find_annotations(struct hists *hists, struct perf_evsel *evsel, struct perf_annotate *ann) { - struct rb_node *nd = rb_first(&self->entries), *next; + struct rb_node *nd = rb_first(&hists->entries), *next; int key = K_RIGHT; while (nd) { diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 419d27d..9c82888 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -303,12 +303,11 @@ static int formula_fprintf(struct hist_entry *he, struct hist_entry *pair, return -1; } -static int hists__add_entry(struct hists *self, +static int hists__add_entry(struct hists *hists, struct addr_location *al, u64 period, u64 weight, u64 transaction) { - if (__hists__add_entry(self, al, NULL, period, weight, transaction) - != NULL) + if (__hists__add_entry(hists, al, NULL, period, weight, transaction) != NULL) return 0; return -ENOMEM; } diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 4aa6d78..eb1a594 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -162,38 +162,38 @@ static int perf_event__repipe_tracing_data(struct perf_tool *tool, return err; } -static int dso__read_build_id(struct dso *self) +static int dso__read_build_id(struct dso *dso) { - if (self->has_build_id) + if (dso->has_build_id) return 0; - if (filename__read_build_id(self->long_name, self->build_id, - sizeof(self->build_id)) > 0) { - self->has_build_id = true; + if (filename__read_build_id(dso->long_name, dso->build_id, + sizeof(dso->build_id)) > 0) { + dso->has_build_id = true; return 0; } return -1; } -static int dso__inject_build_id(struct dso *self, struct perf_tool *tool, +static int dso__inject_build_id(struct dso *dso, struct perf_tool *tool, struct machine *machine) { u16 misc = PERF_RECORD_MISC_USER; int err; - if (dso__read_build_id(self) < 0) { - pr_debug("no build_id found for %s\n", self->long_name); + if (dso__read_build_id(dso) < 0) { + pr_debug("no build_id found for %s\n", dso->long_name); return -1; } - if (self->kernel) + if (dso->kernel) misc = PERF_RECORD_MISC_KERNEL; - err = perf_event__synthesize_build_id(tool, self, misc, perf_event__repipe, + err = perf_event__synthesize_build_id(tool, dso, misc, perf_event__repipe, machine); if (err) { - pr_err("Can't synthesize build_id event for %s\n", self->long_name); + pr_err("Can't synthesize build_id event for %s\n", dso->long_name); return -1; } diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 81addca..e3598a4 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -373,9 +373,9 @@ static int process_read_event(struct perf_tool *tool, /* For pipe mode, sample_type is not currently set */ static int perf_report__setup_sample_type(struct perf_report *rep) { - struct perf_session *self = rep->session; - u64 sample_type = perf_evlist__combined_sample_type(self->evlist); - bool is_pipe = perf_data_file__is_pipe(self->file); + struct perf_session *session = rep->session; + u64 sample_type = perf_evlist__combined_sample_type(session->evlist); + bool is_pipe = perf_data_file__is_pipe(session->file); if (!is_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) { if (sort__has_parent) { @@ -417,14 +417,14 @@ static void sig_handler(int sig __maybe_unused) } static size_t hists__fprintf_nr_sample_events(struct perf_report *rep, - struct hists *self, + struct hists *hists, const char *evname, FILE *fp) { size_t ret; char unit; - unsigned long nr_samples = self->stats.nr_events[PERF_RECORD_SAMPLE]; - u64 nr_events = self->stats.total_period; - struct perf_evsel *evsel = hists_to_evsel(self); + unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; + u64 nr_events = hists->stats.total_period; + struct perf_evsel *evsel = hists_to_evsel(hists); char buf[512]; size_t size = sizeof(buf); diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 7ded71d..a92770c 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -89,14 +89,14 @@ int build_id__sprintf(const u8 *build_id, int len, char *bf) return raw - build_id; } -char *dso__build_id_filename(struct dso *self, char *bf, size_t size) +char *dso__build_id_filename(struct dso *dso, char *bf, size_t size) { char build_id_hex[BUILD_ID_SIZE * 2 + 1]; - if (!self->has_build_id) + if (!dso->has_build_id) return NULL; - build_id__sprintf(self->build_id, sizeof(self->build_id), build_id_hex); + build_id__sprintf(dso->build_id, sizeof(dso->build_id), build_id_hex); if (bf == NULL) { if (asprintf(&bf, "%s/.build-id/%.2s/%s", buildid_dir, build_id_hex, build_id_hex + 2) < 0) diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index cca03831..f0b863e 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -406,7 +406,7 @@ out: return he; } -struct hist_entry *__hists__add_mem_entry(struct hists *self, +struct hist_entry *__hists__add_mem_entry(struct hists *hists, struct addr_location *al, struct symbol *sym_parent, struct mem_info *mi, @@ -429,14 +429,14 @@ struct hist_entry *__hists__add_mem_entry(struct hists *self, .level = al->level, .parent = sym_parent, .filtered = symbol__parent_filter(sym_parent), - .hists = self, + .hists = hists, .mem_info = mi, .branch_info = NULL, }; - return add_hist_entry(self, &entry, al, period, weight); + return add_hist_entry(hists, &entry, al, period, weight); } -struct hist_entry *__hists__add_branch_entry(struct hists *self, +struct hist_entry *__hists__add_branch_entry(struct hists *hists, struct addr_location *al, struct symbol *sym_parent, struct branch_info *bi, @@ -460,14 +460,14 @@ struct hist_entry *__hists__add_branch_entry(struct hists *self, .parent = sym_parent, .filtered = symbol__parent_filter(sym_parent), .branch_info = bi, - .hists = self, + .hists = hists, .mem_info = NULL, }; - return add_hist_entry(self, &entry, al, period, weight); + return add_hist_entry(hists, &entry, al, period, weight); } -struct hist_entry *__hists__add_entry(struct hists *self, +struct hist_entry *__hists__add_entry(struct hists *hists, struct addr_location *al, struct symbol *sym_parent, u64 period, u64 weight, u64 transaction) @@ -488,13 +488,13 @@ struct hist_entry *__hists__add_entry(struct hists *self, }, .parent = sym_parent, .filtered = symbol__parent_filter(sym_parent), - .hists = self, + .hists = hists, .branch_info = NULL, .mem_info = NULL, .transaction = transaction, }; - return add_hist_entry(self, &entry, al, period, weight); + return add_hist_entry(hists, &entry, al, period, weight); } int64_t diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 1f9821d..19b4aa2 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -60,11 +60,11 @@ sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) return right->thread->tid - left->thread->tid; } -static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { return repsep_snprintf(bf, size, "%*s:%5d", width - 6, - self->thread->comm ?: "", self->thread->tid); + he->thread->comm ?: "", he->thread->tid); } struct sort_entry sort_thread = { @@ -94,10 +94,10 @@ sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) return strcmp(comm_l, comm_r); } -static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { - return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); + return repsep_snprintf(bf, size, "%*s", width, he->thread->comm); } struct sort_entry sort_comm = { @@ -148,10 +148,10 @@ static int _hist_entry__dso_snprintf(struct map *map, char *bf, return repsep_snprintf(bf, size, "%-*s", width, "[unknown]"); } -static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { - return _hist_entry__dso_snprintf(self->ms.map, bf, size, width); + return _hist_entry__dso_snprintf(he->ms.map, bf, size, width); } struct sort_entry sort_dso = { @@ -234,11 +234,11 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, return ret; } -static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { - return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip, - self->level, bf, size, width); + return _hist_entry__sym_snprintf(he->ms.map, he->ms.sym, he->ip, + he->level, bf, size, width); } struct sort_entry sort_sym = { @@ -274,11 +274,11 @@ sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right) return strcmp(left->srcline, right->srcline); } -static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width __maybe_unused) { - return repsep_snprintf(bf, size, "%s", self->srcline); + return repsep_snprintf(bf, size, "%s", he->srcline); } struct sort_entry sort_srcline = { @@ -302,11 +302,11 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) return strcmp(sym_l->name, sym_r->name); } -static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { return repsep_snprintf(bf, size, "%-*s", width, - self->parent ? self->parent->name : "[other]"); + he->parent ? he->parent->name : "[other]"); } struct sort_entry sort_parent = { @@ -324,10 +324,10 @@ sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) return right->cpu - left->cpu; } -static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, - size_t size, unsigned int width) +static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf, + size_t size, unsigned int width) { - return repsep_snprintf(bf, size, "%*d", width, self->cpu); + return repsep_snprintf(bf, size, "%*d", width, he->cpu); } struct sort_entry sort_cpu = { @@ -346,10 +346,10 @@ sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right) right->branch_info->from.map); } -static int hist_entry__dso_from_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { - return _hist_entry__dso_snprintf(self->branch_info->from.map, + return _hist_entry__dso_snprintf(he->branch_info->from.map, bf, size, width); } @@ -360,10 +360,10 @@ sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right) right->branch_info->to.map); } -static int hist_entry__dso_to_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { - return _hist_entry__dso_snprintf(self->branch_info->to.map, + return _hist_entry__dso_snprintf(he->branch_info->to.map, bf, size, width); } @@ -391,21 +391,21 @@ sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right) return _sort__sym_cmp(to_l->sym, to_r->sym); } -static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { - struct addr_map_symbol *from = &self->branch_info->from; + struct addr_map_symbol *from = &he->branch_info->from; return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, - self->level, bf, size, width); + he->level, bf, size, width); } -static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { - struct addr_map_symbol *to = &self->branch_info->to; + struct addr_map_symbol *to = &he->branch_info->to; return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, - self->level, bf, size, width); + he->level, bf, size, width); } @@ -448,13 +448,13 @@ sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right) return mp || p; } -static int hist_entry__mispredict_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width){ static const char *out = "N/A"; - if (self->branch_info->flags.predicted) + if (he->branch_info->flags.predicted) out = "N"; - else if (self->branch_info->flags.mispred) + else if (he->branch_info->flags.mispred) out = "Y"; return repsep_snprintf(bf, size, "%-*s", width, out); @@ -474,19 +474,19 @@ sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right) return (int64_t)(r - l); } -static int hist_entry__daddr_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__daddr_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { uint64_t addr = 0; struct map *map = NULL; struct symbol *sym = NULL; - if (self->mem_info) { - addr = self->mem_info->daddr.addr; - map = self->mem_info->daddr.map; - sym = self->mem_info->daddr.sym; + if (he->mem_info) { + addr = he->mem_info->daddr.addr; + map = he->mem_info->daddr.map; + sym = he->mem_info->daddr.sym; } - return _hist_entry__sym_snprintf(map, sym, addr, self->level, bf, size, + return _hist_entry__sym_snprintf(map, sym, addr, he->level, bf, size, width); } @@ -504,13 +504,13 @@ sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right) return _sort__dso_cmp(map_l, map_r); } -static int hist_entry__dso_daddr_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__dso_daddr_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { struct map *map = NULL; - if (self->mem_info) - map = self->mem_info->daddr.map; + if (he->mem_info) + map = he->mem_info->daddr.map; return _hist_entry__dso_snprintf(map, bf, size, width); } @@ -534,14 +534,14 @@ sort__locked_cmp(struct hist_entry *left, struct hist_entry *right) return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock); } -static int hist_entry__locked_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__locked_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { const char *out; u64 mask = PERF_MEM_LOCK_NA; - if (self->mem_info) - mask = self->mem_info->data_src.mem_lock; + if (he->mem_info) + mask = he->mem_info->data_src.mem_lock; if (mask & PERF_MEM_LOCK_NA) out = "N/A"; @@ -583,7 +583,7 @@ static const char * const tlb_access[] = { }; #define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *)) -static int hist_entry__tlb_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__tlb_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { char out[64]; @@ -594,8 +594,8 @@ static int hist_entry__tlb_snprintf(struct hist_entry *self, char *bf, out[0] = '\0'; - if (self->mem_info) - m = self->mem_info->data_src.mem_dtlb; + if (he->mem_info) + m = he->mem_info->data_src.mem_dtlb; hit = m & PERF_MEM_TLB_HIT; miss = m & PERF_MEM_TLB_MISS; @@ -660,7 +660,7 @@ static const char * const mem_lvl[] = { }; #define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *)) -static int hist_entry__lvl_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__lvl_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { char out[64]; @@ -669,8 +669,8 @@ static int hist_entry__lvl_snprintf(struct hist_entry *self, char *bf, u64 m = PERF_MEM_LVL_NA; u64 hit, miss; - if (self->mem_info) - m = self->mem_info->data_src.mem_lvl; + if (he->mem_info) + m = he->mem_info->data_src.mem_lvl; out[0] = '\0'; @@ -728,7 +728,7 @@ static const char * const snoop_access[] = { }; #define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *)) -static int hist_entry__snoop_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { char out[64]; @@ -738,8 +738,8 @@ static int hist_entry__snoop_snprintf(struct hist_entry *self, char *bf, out[0] = '\0'; - if (self->mem_info) - m = self->mem_info->data_src.mem_snoop; + if (he->mem_info) + m = he->mem_info->data_src.mem_snoop; for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) { if (!(m & 0x1)) @@ -776,10 +776,10 @@ sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right) return he_weight(left) - he_weight(right); } -static int hist_entry__local_weight_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__local_weight_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { - return repsep_snprintf(bf, size, "%-*llu", width, he_weight(self)); + return repsep_snprintf(bf, size, "%-*llu", width, he_weight(he)); } struct sort_entry sort_local_weight = { @@ -795,10 +795,10 @@ sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right) return left->stat.weight - right->stat.weight; } -static int hist_entry__global_weight_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__global_weight_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { - return repsep_snprintf(bf, size, "%-*llu", width, self->stat.weight); + return repsep_snprintf(bf, size, "%-*llu", width, he->stat.weight); } struct sort_entry sort_global_weight = { @@ -857,12 +857,12 @@ sort__abort_cmp(struct hist_entry *left, struct hist_entry *right) right->branch_info->flags.abort; } -static int hist_entry__abort_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { static const char *out = "."; - if (self->branch_info->flags.abort) + if (he->branch_info->flags.abort) out = "A"; return repsep_snprintf(bf, size, "%-*s", width, out); } @@ -881,12 +881,12 @@ sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right) right->branch_info->flags.in_tx; } -static int hist_entry__in_tx_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { static const char *out = "."; - if (self->branch_info->flags.in_tx) + if (he->branch_info->flags.in_tx) out = "T"; return repsep_snprintf(bf, size, "%-*s", width, out); @@ -940,10 +940,10 @@ int hist_entry__transaction_len(void) return len; } -static int hist_entry__transaction_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__transaction_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { - u64 t = self->transaction; + u64 t = he->transaction; char buf[128]; char *p = buf; int i; @@ -1125,7 +1125,7 @@ int setup_sorting(void) return ret; } -static void sort_entry__setup_elide(struct sort_entry *self, +static void sort_entry__setup_elide(struct sort_entry *se, struct strlist *list, const char *list_name, FILE *fp) { @@ -1133,7 +1133,7 @@ static void sort_entry__setup_elide(struct sort_entry *self, if (fp != NULL) fprintf(fp, "# %s: %s\n", list_name, strlist__entry(list, 0)->s); - self->elide = true; + se->elide = true; } } diff --git a/tools/perf/util/strfilter.c b/tools/perf/util/strfilter.c index 834c8eb..67e4a00 100644 --- a/tools/perf/util/strfilter.c +++ b/tools/perf/util/strfilter.c @@ -10,22 +10,22 @@ static const char *OP_not = "!"; /* Logical NOT */ #define is_operator(c) ((c) == '|' || (c) == '&' || (c) == '!') #define is_separator(c) (is_operator(c) || (c) == '(' || (c) == ')') -static void strfilter_node__delete(struct strfilter_node *self) +static void strfilter_node__delete(struct strfilter_node *node) { - if (self) { - if (self->p && !is_operator(*self->p)) - free((char *)self->p); - strfilter_node__delete(self->l); - strfilter_node__delete(self->r); - free(self); + if (node) { + if (node->p && !is_operator(*node->p)) + free((char *)node->p); + strfilter_node__delete(node->l); + strfilter_node__delete(node->r); + free(node); } } -void strfilter__delete(struct strfilter *self) +void strfilter__delete(struct strfilter *filter) { - if (self) { - strfilter_node__delete(self->root); - free(self); + if (filter) { + strfilter_node__delete(filter->root); + free(filter); } } @@ -170,30 +170,30 @@ struct strfilter *strfilter__new(const char *rules, const char **err) return ret; } -static bool strfilter_node__compare(struct strfilter_node *self, +static bool strfilter_node__compare(struct strfilter_node *node, const char *str) { - if (!self || !self->p) + if (!node || !node->p) return false; - switch (*self->p) { + switch (*node->p) { case '|': /* OR */ - return strfilter_node__compare(self->l, str) || - strfilter_node__compare(self->r, str); + return strfilter_node__compare(node->l, str) || + strfilter_node__compare(node->r, str); case '&': /* AND */ - return strfilter_node__compare(self->l, str) && - strfilter_node__compare(self->r, str); + return strfilter_node__compare(node->l, str) && + strfilter_node__compare(node->r, str); case '!': /* NOT */ - return !strfilter_node__compare(self->r, str); + return !strfilter_node__compare(node->r, str); default: - return strglobmatch(str, self->p); + return strglobmatch(str, node->p); } } /* Return true if STR matches the filter rules */ -bool strfilter__compare(struct strfilter *self, const char *str) +bool strfilter__compare(struct strfilter *node, const char *str) { - if (!self) + if (!node) return false; - return strfilter_node__compare(self->root, str); + return strfilter_node__compare(node->root, str); } diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index e3d4a55..80d19a0 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -9,51 +9,51 @@ struct thread *thread__new(pid_t pid, pid_t tid) { - struct thread *self = zalloc(sizeof(*self)); + struct thread *thread = zalloc(sizeof(*thread)); - if (self != NULL) { - map_groups__init(&self->mg); - self->pid_ = pid; - self->tid = tid; - self->ppid = -1; - self->comm = malloc(32); - if (self->comm) - snprintf(self->comm, 32, ":%d", self->tid); + if (thread != NULL) { + map_groups__init(&thread->mg); + thread->pid_ = pid; + thread->tid = tid; + thread->ppid = -1; + thread->comm = malloc(32); + if (thread->comm) + snprintf(thread->comm, 32, ":%d", thread->tid); } - return self; + return thread; } -void thread__delete(struct thread *self) +void thread__delete(struct thread *thread) { - map_groups__exit(&self->mg); - free(self->comm); - free(self); + map_groups__exit(&thread->mg); + free(thread->comm); + free(thread); } -int thread__set_comm(struct thread *self, const char *comm) +int thread__set_comm(struct thread *thread, const char *comm) { int err; - if (self->comm) - free(self->comm); - self->comm = strdup(comm); - err = self->comm == NULL ? -ENOMEM : 0; + if (thread->comm) + free(thread->comm); + thread->comm = strdup(comm); + err = thread->comm == NULL ? -ENOMEM : 0; if (!err) { - self->comm_set = true; + thread->comm_set = true; } return err; } -int thread__comm_len(struct thread *self) +int thread__comm_len(struct thread *thread) { - if (!self->comm_len) { - if (!self->comm) + if (!thread->comm_len) { + if (!thread->comm) return 0; - self->comm_len = strlen(self->comm); + thread->comm_len = strlen(thread->comm); } - return self->comm_len; + return thread->comm_len; } size_t thread__fprintf(struct thread *thread, FILE *fp) @@ -62,30 +62,30 @@ size_t thread__fprintf(struct thread *thread, FILE *fp) map_groups__fprintf(&thread->mg, verbose, fp); } -void thread__insert_map(struct thread *self, struct map *map) +void thread__insert_map(struct thread *thread, struct map *map) { - map_groups__fixup_overlappings(&self->mg, map, verbose, stderr); - map_groups__insert(&self->mg, map); + map_groups__fixup_overlappings(&thread->mg, map, verbose, stderr); + map_groups__insert(&thread->mg, map); } -int thread__fork(struct thread *self, struct thread *parent) +int thread__fork(struct thread *thread, struct thread *parent) { int i; if (parent->comm_set) { - if (self->comm) - free(self->comm); - self->comm = strdup(parent->comm); - if (!self->comm) + if (thread->comm) + free(thread->comm); + thread->comm = strdup(parent->comm); + if (!thread->comm) return -ENOMEM; - self->comm_set = true; + thread->comm_set = true; } for (i = 0; i < MAP__NR_TYPES; ++i) - if (map_groups__clone(&self->mg, &parent->mg, i) < 0) + if (map_groups__clone(&thread->mg, &parent->mg, i) < 0) return -ENOMEM; - self->ppid = parent->tid; + thread->ppid = parent->tid; return 0; } -- cgit v0.10.2 From 7969ec7728ba6340de8000ddb0a8833273629d6a Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Fri, 11 Oct 2013 16:10:23 +0900 Subject: perf probe: Support "$vars" meta argument syntax for local variables Support "$vars" meta argument syntax for tracing all local variables at probe point. Now you can trace all available local variables (including function parameters) at the probe point by passing $vars. # perf probe --add foo $vars This automatically finds all local variables at foo() and adds it as probe arguments. Signed-off-by: Masami Hiramatsu Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20131011071023.15557.51770.stgit@udc4-manage.rcp.hitachi.co.jp Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 779b2da..9c6989c 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -47,7 +47,6 @@ #include "session.h" #define MAX_CMDLEN 256 -#define MAX_PROBE_ARGS 128 #define PERFPROBE_GROUP "probe" bool probe_event_dry_run; /* Dry run flag */ diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index c09e0a9..5a46be9 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -1136,12 +1136,78 @@ found: return ret; } +struct local_vars_finder { + struct probe_finder *pf; + struct perf_probe_arg *args; + int max_args; + int nargs; + int ret; +}; + +/* Collect available variables in this scope */ +static int copy_variables_cb(Dwarf_Die *die_mem, void *data) +{ + struct local_vars_finder *vf = data; + int tag; + + tag = dwarf_tag(die_mem); + if (tag == DW_TAG_formal_parameter || + tag == DW_TAG_variable) { + if (convert_variable_location(die_mem, vf->pf->addr, + vf->pf->fb_ops, NULL) == 0) { + vf->args[vf->nargs].var = (char *)dwarf_diename(die_mem); + if (vf->args[vf->nargs].var == NULL) { + vf->ret = -ENOMEM; + return DIE_FIND_CB_END; + } + pr_debug(" %s", vf->args[vf->nargs].var); + vf->nargs++; + } + } + + if (dwarf_haspc(die_mem, vf->pf->addr)) + return DIE_FIND_CB_CONTINUE; + else + return DIE_FIND_CB_SIBLING; +} + +static int expand_probe_args(Dwarf_Die *sc_die, struct probe_finder *pf, + struct perf_probe_arg *args) +{ + Dwarf_Die die_mem; + int i; + int n = 0; + struct local_vars_finder vf = {.pf = pf, .args = args, + .max_args = MAX_PROBE_ARGS, .ret = 0}; + + for (i = 0; i < pf->pev->nargs; i++) { + /* var never be NULL */ + if (strcmp(pf->pev->args[i].var, "$vars") == 0) { + pr_debug("Expanding $vars into:"); + vf.nargs = n; + /* Special local variables */ + die_find_child(sc_die, copy_variables_cb, (void *)&vf, + &die_mem); + pr_debug(" (%d)\n", vf.nargs - n); + if (vf.ret < 0) + return vf.ret; + n = vf.nargs; + } else { + /* Copy normal argument */ + args[n] = pf->pev->args[i]; + n++; + } + } + return n; +} + /* Add a found probe point into trace event list */ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) { struct trace_event_finder *tf = container_of(pf, struct trace_event_finder, pf); struct probe_trace_event *tev; + struct perf_probe_arg *args; int ret, i; /* Check number of tevs */ @@ -1161,21 +1227,35 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, tev->point.offset); - /* Find each argument */ - tev->nargs = pf->pev->nargs; - tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); - if (tev->args == NULL) + /* Expand special probe argument if exist */ + args = zalloc(sizeof(struct perf_probe_arg) * MAX_PROBE_ARGS); + if (args == NULL) return -ENOMEM; - for (i = 0; i < pf->pev->nargs; i++) { - pf->pvar = &pf->pev->args[i]; + + ret = expand_probe_args(sc_die, pf, args); + if (ret < 0) + goto end; + + tev->nargs = ret; + tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); + if (tev->args == NULL) { + ret = -ENOMEM; + goto end; + } + + /* Find each argument */ + for (i = 0; i < tev->nargs; i++) { + pf->pvar = &args[i]; pf->tvar = &tev->args[i]; /* Variable should be found from scope DIE */ ret = find_variable(sc_die, pf); if (ret != 0) - return ret; + break; } - return 0; +end: + free(args); + return ret; } /* Find probe_trace_events specified by perf_probe_event from debuginfo */ diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 3f0c29d..d6dab0e 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -7,6 +7,7 @@ #define MAX_PROBE_BUFFER 1024 #define MAX_PROBES 128 +#define MAX_PROBE_ARGS 128 static inline int is_c_varname(const char *name) { -- cgit v0.10.2 From 3d918a12a1b3088ac16ff37fa52760639d6e2403 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Fri, 11 Oct 2013 16:10:26 +0900 Subject: perf probe: Find fentry mcount fuzzed parameter location At this point, --fentry (mcount function entry) option for gcc fuzzes the debuginfo variable locations by skipping the mcount instruction offset (on x86, this is a 5 byte call instruction). This makes variable searching fail at the entry of functions which are mcount'ed. e.g.) Available variables at vfs_read @ (No matched variables) This patch adds additional location search at the function entry point to solve this issue, which tries to find the earliest address for the variable location. Note that this only works with function parameters (formal parameters) because any local variables should not exist on the function entry address (those are not initialized yet). With this patch, perf probe shows correct parameters if possible; # perf probe --vars vfs_read Available variables at vfs_read @ char* buf loff_t* pos size_t count struct file* file Signed-off-by: Masami Hiramatsu Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20131011071025.15557.13275.stgit@udc4-manage.rcp.hitachi.co.jp Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 5a46be9..c044052 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -273,12 +273,15 @@ static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs) /* * Convert a location into trace_arg. * If tvar == NULL, this just checks variable can be converted. + * If fentry == true and vr_die is a parameter, do huristic search + * for the location fuzzed by function entry mcount. */ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, - Dwarf_Op *fb_ops, + Dwarf_Op *fb_ops, Dwarf_Die *sp_die, struct probe_trace_arg *tvar) { Dwarf_Attribute attr; + Dwarf_Addr tmp = 0; Dwarf_Op *op; size_t nops; unsigned int regn; @@ -291,12 +294,29 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, goto static_var; /* TODO: handle more than 1 exprs */ - if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL || - dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0 || - nops == 0) { - /* TODO: Support const_value */ + if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) + return -EINVAL; /* Broken DIE ? */ + if (dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0) { + ret = dwarf_entrypc(sp_die, &tmp); + if (ret || addr != tmp || + dwarf_tag(vr_die) != DW_TAG_formal_parameter || + dwarf_highpc(sp_die, &tmp)) + return -ENOENT; + /* + * This is fuzzed by fentry mcount. We try to find the + * parameter location at the earliest address. + */ + for (addr += 1; addr <= tmp; addr++) { + if (dwarf_getlocation_addr(&attr, addr, &op, + &nops, 1) > 0) + goto found; + } return -ENOENT; } +found: + if (nops == 0) + /* TODO: Support const_value */ + return -ENOENT; if (op->atom == DW_OP_addr) { static_var: @@ -600,7 +620,7 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) dwarf_diename(vr_die)); ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops, - pf->tvar); + &pf->sp_die, pf->tvar); if (ret == -ENOENT) pr_err("Failed to find the location of %s at this address.\n" " Perhaps, it has been optimized out.\n", pf->pvar->var); @@ -1148,13 +1168,15 @@ struct local_vars_finder { static int copy_variables_cb(Dwarf_Die *die_mem, void *data) { struct local_vars_finder *vf = data; + struct probe_finder *pf = vf->pf; int tag; tag = dwarf_tag(die_mem); if (tag == DW_TAG_formal_parameter || tag == DW_TAG_variable) { if (convert_variable_location(die_mem, vf->pf->addr, - vf->pf->fb_ops, NULL) == 0) { + vf->pf->fb_ops, &pf->sp_die, + NULL) == 0) { vf->args[vf->nargs].var = (char *)dwarf_diename(die_mem); if (vf->args[vf->nargs].var == NULL) { vf->ret = -ENOMEM; @@ -1302,7 +1324,8 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data) if (tag == DW_TAG_formal_parameter || tag == DW_TAG_variable) { ret = convert_variable_location(die_mem, af->pf.addr, - af->pf.fb_ops, NULL); + af->pf.fb_ops, &af->pf.sp_die, + NULL); if (ret == 0) { ret = die_get_varname(die_mem, buf, MAX_VAR_LEN); pr_debug2("Add new var: %s\n", buf); -- cgit v0.10.2 From 4157922a9070aef6a516573111fb1c0c67b891ac Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 23 Oct 2013 14:37:56 +0200 Subject: perf bench: Change the procps visible command-name of invididual benchmark tests plus cleanups Before this patch, looking at 'perf bench sched pipe' behavior over 'top' only told us that something related to perf is running: PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 19934 mingo 20 0 54836 1296 952 R 18.6 0.0 0:00.56 perf 19935 mingo 20 0 54836 384 36 S 18.6 0.0 0:00.56 perf After the patch it's clearly visible what's going on: PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 19744 mingo 20 0 125m 3536 2644 R 68.2 0.0 0:01.12 sched-pipe 19745 mingo 20 0 125m 1172 276 R 68.2 0.0 0:01.12 sched-pipe The benchmark-subsystem name is concatenated with the individual testcase name. Unfortunately 'perf top' does not show the reconfigured name, possibly because it caches ->comm[] values and does not recognize changes to them? Also clean up a few bits in builtin-bench.c while at it and reorganize the code and the output strings to be consistent. Use iterators to access the various arrays. Rename 'suites' concept to 'benchmark collection' and the 'bench_suite' to 'benchmark/bench'. The many repetitions of 'suite' made the code harder to read and understand. The new output is: comet:~/tip/tools/perf> ./perf bench Usage: perf bench [] [] # List of all available benchmark collections: sched: Scheduler and IPC benchmarks mem: Memory access benchmarks numa: NUMA scheduling and MM benchmarks all: All benchmarks comet:~/tip/tools/perf> ./perf bench sched # List of available benchmarks for collection 'sched': messaging: Benchmark for scheduling and IPC pipe: Benchmark for pipe() between two processes all: Test all scheduler benchmarks comet:~/tip/tools/perf> ./perf bench mem # List of available benchmarks for collection 'mem': memcpy: Benchmark for memcpy() memset: Benchmark for memset() tests all: Test all memory benchmarks comet:~/tip/tools/perf> ./perf bench numa # List of available benchmarks for collection 'numa': mem: Benchmark for NUMA workloads all: Test all NUMA benchmarks Individual benchmark modules were not touched. Signed-off-by: Ingo Molnar Cc: David Ahern Cc: Hitoshi Mitake Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20131023123756.GA17871@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c index 33af80f..e47f90c 100644 --- a/tools/perf/builtin-bench.c +++ b/tools/perf/builtin-bench.c @@ -1,21 +1,18 @@ /* - * * builtin-bench.c * - * General benchmarking subsystem provided by perf + * General benchmarking collections provided by perf * * Copyright (C) 2009, Hitoshi Mitake - * */ /* + * Available benchmark collection list: * - * Available subsystem list: - * sched ... scheduler and IPC mechanism + * sched ... scheduler and IPC performance * mem ... memory access performance - * + * numa ... NUMA scheduling and MM performance */ - #include "perf.h" #include "util/util.h" #include "util/parse-options.h" @@ -25,112 +22,92 @@ #include #include #include +#include -struct bench_suite { - const char *name; - const char *summary; - int (*fn)(int, const char **, const char *); +typedef int (*bench_fn_t)(int argc, const char **argv, const char *prefix); + +struct bench { + const char *name; + const char *summary; + bench_fn_t fn; }; - \ -/* sentinel: easy for help */ -#define suite_all { "all", "Test all benchmark suites", NULL } #ifdef HAVE_LIBNUMA_SUPPORT -static struct bench_suite numa_suites[] = { - { "mem", - "Benchmark for NUMA workloads", - bench_numa }, - suite_all, - { NULL, - NULL, - NULL } +static struct bench numa_benchmarks[] = { + { "mem", "Benchmark for NUMA workloads", bench_numa }, + { "all", "Test all NUMA benchmarks", NULL }, + { NULL, NULL, NULL } }; #endif -static struct bench_suite sched_suites[] = { - { "messaging", - "Benchmark for scheduler and IPC mechanisms", - bench_sched_messaging }, - { "pipe", - "Flood of communication over pipe() between two processes", - bench_sched_pipe }, - suite_all, - { NULL, - NULL, - NULL } +static struct bench sched_benchmarks[] = { + { "messaging", "Benchmark for scheduling and IPC", bench_sched_messaging }, + { "pipe", "Benchmark for pipe() between two processes", bench_sched_pipe }, + { "all", "Test all scheduler benchmarks", NULL }, + { NULL, NULL, NULL } }; -static struct bench_suite mem_suites[] = { - { "memcpy", - "Simple memory copy in various ways", - bench_mem_memcpy }, - { "memset", - "Simple memory set in various ways", - bench_mem_memset }, - suite_all, - { NULL, - NULL, - NULL } +static struct bench mem_benchmarks[] = { + { "memcpy", "Benchmark for memcpy()", bench_mem_memcpy }, + { "memset", "Benchmark for memset() tests", bench_mem_memset }, + { "all", "Test all memory benchmarks", NULL }, + { NULL, NULL, NULL } }; -struct bench_subsys { - const char *name; - const char *summary; - struct bench_suite *suites; +struct collection { + const char *name; + const char *summary; + struct bench *benchmarks; }; -static struct bench_subsys subsystems[] = { +static struct collection collections[] = { + { "sched", "Scheduler and IPC benchmarks", sched_benchmarks }, + { "mem", "Memory access benchmarks", mem_benchmarks }, #ifdef HAVE_LIBNUMA_SUPPORT - { "numa", - "NUMA scheduling and MM behavior", - numa_suites }, + { "numa", "NUMA scheduling and MM benchmarks", numa_benchmarks }, #endif - { "sched", - "scheduler and IPC mechanism", - sched_suites }, - { "mem", - "memory access performance", - mem_suites }, - { "all", /* sentinel: easy for help */ - "all benchmark subsystem", - NULL }, - { NULL, - NULL, - NULL } + { "all", "All benchmarks", NULL }, + { NULL, NULL, NULL } }; -static void dump_suites(int subsys_index) +/* Iterate over all benchmark collections: */ +#define for_each_collection(coll) \ + for (coll = collections; coll->name; coll++) + +/* Iterate over all benchmarks within a collection: */ +#define for_each_bench(coll, bench) \ + for (bench = coll->benchmarks; bench->name; bench++) + +static void dump_benchmarks(struct collection *coll) { - int i; + struct bench *bench; - printf("# List of available suites for %s...\n\n", - subsystems[subsys_index].name); + printf("\n # List of available benchmarks for collection '%s':\n\n", coll->name); - for (i = 0; subsystems[subsys_index].suites[i].name; i++) - printf("%14s: %s\n", - subsystems[subsys_index].suites[i].name, - subsystems[subsys_index].suites[i].summary); + for_each_bench(coll, bench) + printf("%14s: %s\n", bench->name, bench->summary); printf("\n"); - return; } static const char *bench_format_str; + +/* Output/formatting style, exported to benchmark modules: */ int bench_format = BENCH_FORMAT_DEFAULT; static const struct option bench_options[] = { - OPT_STRING('f', "format", &bench_format_str, "default", - "Specify format style"), + OPT_STRING('f', "format", &bench_format_str, "default", "Specify format style"), OPT_END() }; static const char * const bench_usage[] = { - "perf bench [] []", + "perf bench [] []", NULL }; static void print_usage(void) { + struct collection *coll; int i; printf("Usage: \n"); @@ -138,11 +115,10 @@ static void print_usage(void) printf("\t%s\n", bench_usage[i]); printf("\n"); - printf("# List of available subsystems...\n\n"); + printf(" # List of all available benchmark collections:\n\n"); - for (i = 0; subsystems[i].name; i++) - printf("%14s: %s\n", - subsystems[i].name, subsystems[i].summary); + for_each_collection(coll) + printf("%14s: %s\n", coll->name, coll->summary); printf("\n"); } @@ -159,44 +135,74 @@ static int bench_str2int(const char *str) return BENCH_FORMAT_UNKNOWN; } -static void all_suite(struct bench_subsys *subsys) /* FROM HERE */ +/* + * Run a specific benchmark but first rename the running task's ->comm[] + * to something meaningful: + */ +static int run_bench(const char *coll_name, const char *bench_name, bench_fn_t fn, + int argc, const char **argv, const char *prefix) { - int i; + int size; + char *name; + int ret; + + size = strlen(coll_name) + 1 + strlen(bench_name) + 1; + + name = zalloc(size); + BUG_ON(!name); + + scnprintf(name, size, "%s-%s", coll_name, bench_name); + + prctl(PR_SET_NAME, name); + argv[0] = name; + + ret = fn(argc, argv, prefix); + + free(name); + + return ret; +} + +static void run_collection(struct collection *coll) +{ + struct bench *bench; const char *argv[2]; - struct bench_suite *suites = subsys->suites; argv[1] = NULL; /* * TODO: - * preparing preset parameters for + * + * Preparing preset parameters for * embedded, ordinary PC, HPC, etc... - * will be helpful + * would be helpful. */ - for (i = 0; suites[i].fn; i++) { - printf("# Running %s/%s benchmark...\n", - subsys->name, - suites[i].name); + for_each_bench(coll, bench) { + if (!bench->fn) + break; + printf("# Running %s/%s benchmark...\n", coll->name, bench->name); fflush(stdout); - argv[1] = suites[i].name; - suites[i].fn(1, argv, NULL); + argv[1] = bench->name; + run_bench(coll->name, bench->name, bench->fn, 1, argv, NULL); printf("\n"); } } -static void all_subsystem(void) +static void run_all_collections(void) { - int i; - for (i = 0; subsystems[i].suites; i++) - all_suite(&subsystems[i]); + struct collection *coll; + + for_each_collection(coll) + run_collection(coll); } int cmd_bench(int argc, const char **argv, const char *prefix __maybe_unused) { - int i, j, status = 0; + struct collection *coll; + int ret = 0; if (argc < 2) { - /* No subsystem specified. */ + /* No collection specified. */ print_usage(); goto end; } @@ -206,7 +212,7 @@ int cmd_bench(int argc, const char **argv, const char *prefix __maybe_unused) bench_format = bench_str2int(bench_format_str); if (bench_format == BENCH_FORMAT_UNKNOWN) { - printf("Unknown format descriptor:%s\n", bench_format_str); + printf("Unknown format descriptor: '%s'\n", bench_format_str); goto end; } @@ -216,52 +222,51 @@ int cmd_bench(int argc, const char **argv, const char *prefix __maybe_unused) } if (!strcmp(argv[0], "all")) { - all_subsystem(); + run_all_collections(); goto end; } - for (i = 0; subsystems[i].name; i++) { - if (strcmp(subsystems[i].name, argv[0])) + for_each_collection(coll) { + struct bench *bench; + + if (strcmp(coll->name, argv[0])) continue; if (argc < 2) { - /* No suite specified. */ - dump_suites(i); + /* No bench specified. */ + dump_benchmarks(coll); goto end; } if (!strcmp(argv[1], "all")) { - all_suite(&subsystems[i]); + run_collection(coll); goto end; } - for (j = 0; subsystems[i].suites[j].name; j++) { - if (strcmp(subsystems[i].suites[j].name, argv[1])) + for_each_bench(coll, bench) { + if (strcmp(bench->name, argv[1])) continue; if (bench_format == BENCH_FORMAT_DEFAULT) - printf("# Running %s/%s benchmark...\n", - subsystems[i].name, - subsystems[i].suites[j].name); + printf("# Running '%s/%s' benchmark:\n", coll->name, bench->name); fflush(stdout); - status = subsystems[i].suites[j].fn(argc - 1, - argv + 1, prefix); + ret = run_bench(coll->name, bench->name, bench->fn, argc-1, argv+1, prefix); goto end; } if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { - dump_suites(i); + dump_benchmarks(coll); goto end; } - printf("Unknown suite:%s for %s\n", argv[1], argv[0]); - status = 1; + printf("Unknown benchmark: '%s' for collection '%s'\n", argv[1], argv[0]); + ret = 1; goto end; } - printf("Unknown subsystem:%s\n", argv[0]); - status = 1; + printf("Unknown collection: '%s'\n", argv[0]); + ret = 1; end: - return status; + return ret; } -- cgit v0.10.2 From 8a39df8faa1cb130f136d5e404332c16fbb936c0 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 22 Oct 2013 10:34:15 +0300 Subject: perf sched: Make struct perf_sched sched a local variable Change "struct perf_sched sched" from being global to being local. The build slowdown cured by f36f83f947ed is dealt with in the following patch, by programatically setting perf_sched.curr_pid. Signed-off-by: Adrian Hunter Acked-by: David Ahern Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382427258-17495-12-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 5a46b10..5a33856 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1655,29 +1655,28 @@ static int __cmd_record(int argc, const char **argv) return cmd_record(i, rec_argv, NULL); } -static const char default_sort_order[] = "avg, max, switch, runtime"; -static struct perf_sched sched = { - .tool = { - .sample = perf_sched__process_tracepoint_sample, - .comm = perf_event__process_comm, - .lost = perf_event__process_lost, - .fork = perf_sched__process_fork_event, - .ordered_samples = true, - }, - .cmp_pid = LIST_HEAD_INIT(sched.cmp_pid), - .sort_list = LIST_HEAD_INIT(sched.sort_list), - .start_work_mutex = PTHREAD_MUTEX_INITIALIZER, - .work_done_wait_mutex = PTHREAD_MUTEX_INITIALIZER, - .curr_pid = { [0 ... MAX_CPUS - 1] = -1 }, - .sort_order = default_sort_order, - .replay_repeat = 10, - .profile_cpu = -1, - .next_shortname1 = 'A', - .next_shortname2 = '0', -}; - int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) { + const char default_sort_order[] = "avg, max, switch, runtime"; + struct perf_sched sched = { + .tool = { + .sample = perf_sched__process_tracepoint_sample, + .comm = perf_event__process_comm, + .lost = perf_event__process_lost, + .fork = perf_sched__process_fork_event, + .ordered_samples = true, + }, + .cmp_pid = LIST_HEAD_INIT(sched.cmp_pid), + .sort_list = LIST_HEAD_INIT(sched.sort_list), + .start_work_mutex = PTHREAD_MUTEX_INITIALIZER, + .work_done_wait_mutex = PTHREAD_MUTEX_INITIALIZER, + .curr_pid = { [0 ... MAX_CPUS - 1] = -1 }, + .sort_order = default_sort_order, + .replay_repeat = 10, + .profile_cpu = -1, + .next_shortname1 = 'A', + .next_shortname2 = '0', + }; const struct option latency_options[] = { OPT_STRING('s', "sort", &sched.sort_order, "key[,key2...]", "sort by key(s): runtime, switch, avg, max"), -- cgit v0.10.2 From 156a2b022907687f28c72d1ba601015f295cd99e Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 22 Oct 2013 10:34:16 +0300 Subject: perf sched: Optimize build time builtin-sched.c took a log time to build with -O6 optimization. This turned out to be caused by: .curr_pid = { [0 ... MAX_CPUS - 1] = -1 }, Fix by initializing curr_pid programmatically. This addresses the problem cured in f36f83f947ed using a smaller hammer. Signed-off-by: Adrian Hunter Acked-by: David Ahern Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382427258-17495-13-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 5a33856..ddb5dc1 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1670,7 +1670,6 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) .sort_list = LIST_HEAD_INIT(sched.sort_list), .start_work_mutex = PTHREAD_MUTEX_INITIALIZER, .work_done_wait_mutex = PTHREAD_MUTEX_INITIALIZER, - .curr_pid = { [0 ... MAX_CPUS - 1] = -1 }, .sort_order = default_sort_order, .replay_repeat = 10, .profile_cpu = -1, @@ -1732,6 +1731,10 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) .switch_event = replay_switch_event, .fork_event = replay_fork_event, }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(sched.curr_pid); i++) + sched.curr_pid[i] = -1; argc = parse_options(argc, argv, sched_options, sched_usage, PARSE_OPT_STOP_AT_NON_OPTION); -- cgit v0.10.2 From 6f3e5eda9d6cc74538430d8f9e8e4baa01249160 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 22 Oct 2013 10:34:07 +0300 Subject: perf script: Make perf_script a local variable Change perf_script from being global to being local. Signed-off-by: Adrian Hunter Acked-by: David Ahern Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382427258-17495-4-git-send-email-adrian.hunter@intel.com [ Made the minor consistency changes suggested by David Ahern ] Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 27de606..0ae88c2 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -542,18 +542,9 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, return 0; } -static struct perf_tool perf_script = { - .sample = process_sample_event, - .mmap = perf_event__process_mmap, - .mmap2 = perf_event__process_mmap2, - .comm = perf_event__process_comm, - .exit = perf_event__process_exit, - .fork = perf_event__process_fork, - .attr = perf_event__process_attr, - .tracing_data = perf_event__process_tracing_data, - .build_id = perf_event__process_build_id, - .ordered_samples = true, - .ordering_requires_timestamps = true, +struct perf_script { + struct perf_tool tool; + struct perf_session *session; }; static void sig_handler(int sig __maybe_unused) @@ -561,13 +552,13 @@ static void sig_handler(int sig __maybe_unused) session_done = 1; } -static int __cmd_script(struct perf_session *session) +static int __cmd_script(struct perf_script *script) { int ret; signal(SIGINT, sig_handler); - ret = perf_session__process_events(session, &perf_script); + ret = perf_session__process_events(script->session, &script->tool); if (debug_mode) pr_err("Misordered timestamps: %" PRIu64 "\n", nr_unordered); @@ -1273,6 +1264,21 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) char *script_path = NULL; const char **__argv; int i, j, err; + struct perf_script script = { + .tool = { + .sample = process_sample_event, + .mmap = perf_event__process_mmap, + .mmap2 = perf_event__process_mmap2, + .comm = perf_event__process_comm, + .exit = perf_event__process_exit, + .fork = perf_event__process_fork, + .attr = perf_event__process_attr, + .tracing_data = perf_event__process_tracing_data, + .build_id = perf_event__process_build_id, + .ordered_samples = true, + .ordering_requires_timestamps = true, + }, + }; const struct option options[] = { OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), @@ -1498,10 +1504,12 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) if (!script_name) setup_pager(); - session = perf_session__new(&file, false, &perf_script); + session = perf_session__new(&file, false, &script.tool); if (session == NULL) return -ENOMEM; + script.session = session; + if (cpu_list) { if (perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap)) return -1; @@ -1565,7 +1573,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) if (err < 0) goto out; - err = __cmd_script(session); + err = __cmd_script(&script); perf_session__delete(session); cleanup_scripting(); -- cgit v0.10.2 From 89c97d936e76b064a52ee056602b2a62b3f1ef70 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 22 Oct 2013 10:34:09 +0300 Subject: perf inject: Do not repipe attributes to a perf.data file perf.data files contain the attributes separately, do not put them in the event stream as well. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382427258-17495-6-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index eb1a594..409ceaf 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -72,12 +72,17 @@ static int perf_event__repipe_attr(struct perf_tool *tool, union perf_event *event, struct perf_evlist **pevlist) { + struct perf_inject *inject = container_of(tool, struct perf_inject, + tool); int ret; ret = perf_event__process_attr(tool, event, pevlist); if (ret) return ret; + if (!inject->pipe_output) + return 0; + return perf_event__repipe_synth(tool, event); } -- cgit v0.10.2 From 56921becdd1eb0720603fc2e6e4c7f518196d917 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 22 Oct 2013 10:34:17 +0300 Subject: perf tools: Do not accept parse_tag_value() overflow parse_tag_value() accepts an "unsigned long" and multiplies it according to a tag character. Do not accept the value if the multiplication overflows. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382427258-17495-14-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index c25e57b..28a0a89 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -386,6 +386,8 @@ unsigned long parse_tag_value(const char *str, struct parse_tag *tags) if (s != endptr) break; + if (value > ULONG_MAX / i->mult) + break; value *= i->mult; return value; } -- cgit v0.10.2 From 2fbe4abe944868aafdde233557ac85379b60ce46 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 22 Oct 2013 10:34:18 +0300 Subject: perf evlist: Validate that mmap_pages is not too big Amend perf_evlist__parse_mmap_pages() to check that the mmap_pages entered via the --mmap_pages/-m option is not too big. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382427258-17495-15-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 85c4c80..2ce92ec 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -698,7 +698,8 @@ static size_t perf_evlist__mmap_size(unsigned long pages) int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, int unset __maybe_unused) { - unsigned int pages, val, *mmap_pages = opt->value; + unsigned int *mmap_pages = opt->value; + unsigned long pages, val; size_t size; static struct parse_tag tags[] = { { .tag = 'B', .mult = 1 }, @@ -709,12 +710,12 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, }; val = parse_tag_value(str, tags); - if (val != (unsigned int) -1) { + if (val != (unsigned long) -1) { /* we got file size value */ pages = PERF_ALIGN(val, page_size) / page_size; - if (!is_power_of_2(pages)) { + if (pages < (1UL << 31) && !is_power_of_2(pages)) { pages = next_pow2(pages); - pr_info("rounding mmap pages size to %u (%u pages)\n", + pr_info("rounding mmap pages size to %lu (%lu pages)\n", pages * page_size, pages); } } else { @@ -727,6 +728,11 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, } } + if (pages > UINT_MAX || pages > SIZE_MAX / page_size) { + pr_err("--mmap_pages/-m value too big\n"); + return -1; + } + size = perf_evlist__mmap_size(pages); if (!size) { pr_err("--mmap_pages/-m value must be a power of two."); -- cgit v0.10.2 From 74af377bc25dd9ebcb0be12836abb6b401b5dd08 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 22 Oct 2013 10:34:05 +0300 Subject: perf tools: Fix non-debug build In the absence of s DEBUG variable definition on the command line perf tools was building without optimization. Fix by assigning DEBUG if it is not defined. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382427258-17495-2-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index c516d6b..543aa95 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -66,6 +66,10 @@ ifneq ($(WERROR),0) CFLAGS += -Werror endif +ifndef DEBUG + DEBUG := 0 +endif + ifeq ($(DEBUG),0) CFLAGS += -O6 endif -- cgit v0.10.2 From d79e07c95d1328773509c69131e5f25cac5dbf50 Mon Sep 17 00:00:00 2001 From: "Hebbar, Gururaja" Date: Wed, 23 Oct 2013 15:30:13 +0300 Subject: ASoC: davinci: Add support for AM33xx SoC Audio AM33xx uses same McASP IP as the Davinci Platform. This patch updates Kconfig and makefile to enable build for McASP, PCM & Codec drivers. Signed-off-by: Hebbar, Gururaja Signed-off-by: Darren Etheridge Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig index c82f89c..95970f5 100644 --- a/sound/soc/davinci/Kconfig +++ b/sound/soc/davinci/Kconfig @@ -1,9 +1,10 @@ config SND_DAVINCI_SOC - tristate "SoC Audio for the TI DAVINCI chip" - depends on ARCH_DAVINCI + tristate "SoC Audio for the TI DAVINCI or AM33XX chip" + depends on ARCH_DAVINCI || SOC_AM33XX help + Platform driver for daVinci or AM33xx Say Y or M if you want to add support for codecs attached to - the DAVINCI AC97 or I2S interface. You will also need + the DAVINCI AC97, I2S, or McASP interface. You will also need to select the audio interfaces to support below. config SND_DAVINCI_SOC_I2S @@ -15,6 +16,17 @@ config SND_DAVINCI_SOC_MCASP config SND_DAVINCI_SOC_VCIF tristate +config SND_AM33XX_SOC_EVM + tristate "SoC Audio for the AM33XX chip based boards" + depends on SND_DAVINCI_SOC && SOC_AM33XX + select SND_SOC_TLV320AIC3X + select SND_DAVINCI_SOC_MCASP + help + Say Y or M if you want to add support for SoC audio on AM33XX + boards using McASP and TLV320AIC3X codec. For example AM335X-EVM, + AM335X-EVMSK, and BeagelBone with AudioCape boards have this + setup. + config SND_DAVINCI_SOC_EVM tristate "SoC Audio support for DaVinci DM6446, DM355 or DM365 EVM" depends on SND_DAVINCI_SOC diff --git a/sound/soc/davinci/Makefile b/sound/soc/davinci/Makefile index a396ab6..bc81e79 100644 --- a/sound/soc/davinci/Makefile +++ b/sound/soc/davinci/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_SND_DAVINCI_SOC_VCIF) += snd-soc-davinci-vcif.o snd-soc-evm-objs := davinci-evm.o obj-$(CONFIG_SND_DAVINCI_SOC_EVM) += snd-soc-evm.o +obj-$(CONFIG_SND_AM33XX_SOC_EVM) += snd-soc-evm.o obj-$(CONFIG_SND_DM6467_SOC_EVM) += snd-soc-evm.o obj-$(CONFIG_SND_DA830_SOC_EVM) += snd-soc-evm.o obj-$(CONFIG_SND_DA850_SOC_EVM) += snd-soc-evm.o -- cgit v0.10.2 From d5faaa34262d432130f07fed958c8161ef2715ab Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Wed, 23 Oct 2013 15:30:15 +0300 Subject: ASoC: davinci-mcasp: Remove last reference to num-serializer in DT doc Remove last reference to num-serializer in davinci-mcasp devicetree binding document. Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt index 0aa416b..ed785b3 100644 --- a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt +++ b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt @@ -15,7 +15,6 @@ Required properties: IEC60958-1, and AES-3 formats. - tdm-slots : Slots for TDM operation. Indicates number of channels transmitted or received over one serializer. -- num-serializer : Serializers used by McASP. - serial-dir : A list of serializer configuration. Each entry is a number indication for serializer pin direction. (0 - INACTIVE, 1 - TX, 2 - RX) -- cgit v0.10.2 From ee2f615d6e59cea2b9a415661a7f27caffcb3528 Mon Sep 17 00:00:00 2001 From: "Hebbar, Gururaja" Date: Wed, 23 Oct 2013 15:30:14 +0300 Subject: ASoC: davinci-evm: Add device tree binding Device tree support for Davinci Machine driver When the board boots with device tree, the driver will receive card, codec, dai interface details (like the card name, DAPM routing map, phandle for the audio components described in the dts file, codec mclk speed). The card will be set up based on this information. Since the routing is provided via DT we can mark the card fully routed so core can take care of disconnecting the unused pins. Signed-off-by: Hebbar, Gururaja Signed-off-by: Darren Etheridge Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt b/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt new file mode 100644 index 0000000..865178d --- /dev/null +++ b/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt @@ -0,0 +1,42 @@ +* Texas Instruments SoC audio setups with TLV320AIC3X Codec + +Required properties: +- compatible : "ti,da830-evm-audio" : forDM365/DA8xx/OMAPL1x/AM33xx +- ti,model : The user-visible name of this sound complex. +- ti,audio-codec : The phandle of the TLV320AIC3x audio codec +- ti,mcasp-controller : The phandle of the McASP controller +- ti,codec-clock-rate : The Codec Clock rate (in Hz) applied to the Codec +- ti,audio-routing : A list of the connections between audio components. + Each entry is a pair of strings, the first being the connection's sink, + the second being the connection's source. Valid names for sources and + sinks are the codec's pins, and the jacks on the board: + + Board connectors: + + * Headphone Jack + * Line Out + * Mic Jack + * Line In + + +Example: + +sound { + compatible = "ti,da830-evm-audio"; + ti,model = "DA830 EVM"; + ti,audio-codec = <&tlv320aic3x>; + ti,mcasp-controller = <&mcasp1>; + ti,codec-clock-rate = <12000000>; + ti,audio-routing = + "Headphone Jack", "HPLOUT", + "Headphone Jack", "HPROUT", + "Line Out", "LLOUT", + "Line Out", "RLOUT", + "MIC3L", "Mic Bias 2V", + "MIC3R", "Mic Bias 2V", + "Mic Bias 2V", "Mic Jack", + "LINE1L", "Line In", + "LINE2L", "Line In", + "LINE1R", "Line In", + "LINE2R", "Line In"; +}; diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c index 2f8161c..623eb5e 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/davinci/davinci-evm.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,8 @@ #include #include +#include + #include "davinci-pcm.h" #include "davinci-i2s.h" #include "davinci-mcasp.h" @@ -121,13 +124,22 @@ static int evm_aic3x_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; struct snd_soc_dapm_context *dapm = &codec->dapm; + struct device_node *np = codec->card->dev->of_node; + int ret; /* Add davinci-evm specific widgets */ snd_soc_dapm_new_controls(dapm, aic3x_dapm_widgets, ARRAY_SIZE(aic3x_dapm_widgets)); - /* Set up davinci-evm specific audio path audio_map */ - snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); + if (np) { + ret = snd_soc_of_parse_audio_routing(codec->card, + "ti,audio-routing"); + if (ret) + return ret; + } else { + /* Set up davinci-evm specific audio path audio_map */ + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); + } /* not connected */ snd_soc_dapm_disable_pin(dapm, "MONO_LOUT"); @@ -312,6 +324,98 @@ static struct snd_soc_card da850_snd_soc_card = { .drvdata = &da850_snd_soc_card_drvdata, }; +#if defined(CONFIG_OF) + +/* + * The struct is used as place holder. It will be completely + * filled with data from dt node. + */ +static struct snd_soc_dai_link evm_dai_tlv320aic3x = { + .name = "TLV320AIC3X", + .stream_name = "AIC3X", + .codec_dai_name = "tlv320aic3x-hifi", + .ops = &evm_ops, + .init = evm_aic3x_init, +}; + +static const struct of_device_id davinci_evm_dt_ids[] = { + { + .compatible = "ti,da830-evm-audio", + .data = (void *) &evm_dai_tlv320aic3x, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, davinci_evm_dt_ids); + +/* davinci evm audio machine driver */ +static struct snd_soc_card evm_soc_card = { + .owner = THIS_MODULE, + .num_links = 1, +}; + +static int davinci_evm_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *match = + of_match_device(of_match_ptr(davinci_evm_dt_ids), &pdev->dev); + struct snd_soc_dai_link *dai = (struct snd_soc_dai_link *) match->data; + struct snd_soc_card_drvdata_davinci *drvdata = NULL; + int ret = 0; + + evm_soc_card.dai_link = dai; + + dai->codec_of_node = of_parse_phandle(np, "ti,audio-codec", 0); + if (!dai->codec_of_node) + return -EINVAL; + + dai->cpu_of_node = of_parse_phandle(np, "ti,mcasp-controller", 0); + if (!dai->cpu_of_node) + return -EINVAL; + + dai->platform_of_node = dai->cpu_of_node; + + evm_soc_card.dev = &pdev->dev; + ret = snd_soc_of_parse_card_name(&evm_soc_card, "ti,model"); + if (ret) + return ret; + + drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + ret = of_property_read_u32(np, "ti,codec-clock-rate", &drvdata->sysclk); + if (ret < 0) + return -EINVAL; + + snd_soc_card_set_drvdata(&evm_soc_card, drvdata); + ret = devm_snd_soc_register_card(&pdev->dev, &evm_soc_card); + + if (ret) + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); + + return ret; +} + +static int davinci_evm_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + + return 0; +} + +static struct platform_driver davinci_evm_driver = { + .probe = davinci_evm_probe, + .remove = davinci_evm_remove, + .driver = { + .name = "davinci_evm", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(davinci_evm_dt_ids), + }, +}; +#endif + static struct platform_device *evm_snd_device; static int __init evm_init(void) @@ -320,6 +424,15 @@ static int __init evm_init(void) int index; int ret; + /* + * If dtb is there, the devices will be created dynamically. + * Only register platfrom driver structure. + */ +#if defined(CONFIG_OF) + if (of_have_populated_dt()) + return platform_driver_register(&davinci_evm_driver); +#endif + if (machine_is_davinci_evm()) { evm_snd_dev_data = &dm6446_snd_soc_card_evm; index = 0; @@ -355,6 +468,13 @@ static int __init evm_init(void) static void __exit evm_exit(void) { +#if defined(CONFIG_OF) + if (of_have_populated_dt()) { + platform_driver_unregister(&davinci_evm_driver); + return; + } +#endif + platform_device_unregister(evm_snd_device); } -- cgit v0.10.2 From f95a48834cb9c581eec952215666a323136f339f Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Wed, 23 Oct 2013 14:03:28 +0200 Subject: ASoC: tpa6130a2: Add device tree support Add device tree support to tpa6130a2 driver and document the bindings. Signed-off-by: Sebastian Reichel Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/tpa6130a2.txt b/Documentation/devicetree/bindings/sound/tpa6130a2.txt new file mode 100644 index 0000000..6dfa740 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/tpa6130a2.txt @@ -0,0 +1,27 @@ +Texas Instruments - tpa6130a2 Codec module + +The tpa6130a2 serial control bus communicates through I2C protocols + +Required properties: + +- compatible - "string" - One of: + "ti,tpa6130a2" - TPA6130A2 + "ti,tpa6140a2" - TPA6140A2 + + +- reg - - I2C slave address + +- Vdd-supply - - power supply regulator + +Optional properties: + +- power-gpio - gpio pin to power the device + +Example: + +tpa6130a2: tpa6130a2@60 { + compatible = "ti,tpa6130a2"; + reg = <0x60>; + Vdd-supply = <&vmmc2>; + power-gpio = <&gpio4 2 GPIO_ACTIVE_HIGH>; +}; diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c index c58bee8..998555f 100644 --- a/sound/soc/codecs/tpa6130a2.c +++ b/sound/soc/codecs/tpa6130a2.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "tpa6130a2.h" @@ -364,30 +365,33 @@ static int tpa6130a2_probe(struct i2c_client *client, { struct device *dev; struct tpa6130a2_data *data; - struct tpa6130a2_platform_data *pdata; + struct tpa6130a2_platform_data *pdata = client->dev.platform_data; + struct device_node *np = client->dev.of_node; const char *regulator; int ret; dev = &client->dev; - if (client->dev.platform_data == NULL) { - dev_err(dev, "Platform data not set\n"); - dump_stack(); - return -ENODEV; - } - data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); if (data == NULL) { dev_err(dev, "Can not allocate memory\n"); return -ENOMEM; } + if (pdata) { + data->power_gpio = pdata->power_gpio; + } else if (np) { + data->power_gpio = of_get_named_gpio(np, "power-gpio", 0); + } else { + dev_err(dev, "Platform data not set\n"); + dump_stack(); + return -ENODEV; + } + tpa6130a2_client = client; i2c_set_clientdata(tpa6130a2_client, data); - pdata = client->dev.platform_data; - data->power_gpio = pdata->power_gpio; data->id = id->driver_data; mutex_init(&data->mutex); @@ -466,10 +470,20 @@ static const struct i2c_device_id tpa6130a2_id[] = { }; MODULE_DEVICE_TABLE(i2c, tpa6130a2_id); +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id tpa6130a2_of_match[] = { + { .compatible = "ti,tpa6130a2", }, + { .compatible = "ti,tpa6140a2" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tpa6130a2_of_match); +#endif + static struct i2c_driver tpa6130a2_i2c_driver = { .driver = { .name = "tpa6130a2", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tpa6130a2_of_match), }, .probe = tpa6130a2_probe, .remove = tpa6130a2_remove, -- cgit v0.10.2 From 4b3db708b114fc35ff1e0cd28a2bfb1490dbb5d3 Mon Sep 17 00:00:00 2001 From: "Chen, Gong" Date: Mon, 21 Oct 2013 14:29:25 -0700 Subject: ACPI, x86: Extended error log driver for x86 platform This H/W error log driver (a.k.a eMCA driver) is implemented based on http://www.intel.com/content/www/us/en/architecture-and-technology/enhanced-mca-logging-xeon-paper.html After errors are captured, more detailed platform specific information can be got via this new enhanced H/W error log driver. Most notably we can track memory errors back to the DIMM slot silk screen label. Signed-off-by: Chen, Gong Signed-off-by: Tony Luck diff --git a/arch/x86/include/asm/mce.h b/arch/x86/include/asm/mce.h index cbe6b9e..c696a86 100644 --- a/arch/x86/include/asm/mce.h +++ b/arch/x86/include/asm/mce.h @@ -16,6 +16,7 @@ #define MCG_EXT_CNT_SHIFT 16 #define MCG_EXT_CNT(c) (((c) & MCG_EXT_CNT_MASK) >> MCG_EXT_CNT_SHIFT) #define MCG_SER_P (1ULL<<24) /* MCA recovery/new status bits */ +#define MCG_ELOG_P (1ULL<<26) /* Extended error log supported */ /* MCG_STATUS register defines */ #define MCG_STATUS_RIPV (1ULL<<0) /* restart ip valid */ diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 6efe2ac..252f0e8 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -372,4 +372,23 @@ config ACPI_BGRT source "drivers/acpi/apei/Kconfig" +config ACPI_EXTLOG + tristate "Extended Error Log support" + depends on X86_MCE && ACPI_APEI + default n + help + Certain usages such as Predictive Failure Analysis (PFA) require + more information about the error than what can be described in + processor machine check banks. Most server processors log + additional information about the error in processor uncore + registers. Since the addresses and layout of these registers vary + widely from one processor to another, system software cannot + readily make use of them. To complicate matters further, some of + the additional error information cannot be constructed without + detailed knowledge about platform topology. + + Enhanced MCA Logging allows firmware to provide additional error + information to system software, synchronous with MCE or CMCI. This + driver adds support for that functionality. + endif # ACPI diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index cdaf68b..bce34af 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -82,3 +82,5 @@ processor-$(CONFIG_CPU_FREQ) += processor_perflib.o obj-$(CONFIG_ACPI_PROCESSOR_AGGREGATOR) += acpi_pad.o obj-$(CONFIG_ACPI_APEI) += apei/ + +obj-$(CONFIG_ACPI_EXTLOG) += acpi_extlog.o diff --git a/drivers/acpi/acpi_extlog.c b/drivers/acpi/acpi_extlog.c new file mode 100644 index 0000000..a6869e1 --- /dev/null +++ b/drivers/acpi/acpi_extlog.c @@ -0,0 +1,327 @@ +/* + * Extended Error Log driver + * + * Copyright (C) 2013 Intel Corp. + * Author: Chen, Gong + * + * This file is licensed under GPLv2. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "apei/apei-internal.h" + +#define EXT_ELOG_ENTRY_MASK GENMASK_ULL(51, 0) /* elog entry address mask */ + +#define EXTLOG_DSM_REV 0x0 +#define EXTLOG_FN_QUERY 0x0 +#define EXTLOG_FN_ADDR 0x1 + +#define FLAG_OS_OPTIN BIT(0) +#define EXTLOG_QUERY_L1_EXIST BIT(1) +#define ELOG_ENTRY_VALID (1ULL<<63) +#define ELOG_ENTRY_LEN 0x1000 + +#define EMCA_BUG \ + "Can not request iomem region <0x%016llx-0x%016llx> - eMCA disabled\n" + +struct extlog_l1_head { + u32 ver; /* Header Version */ + u32 hdr_len; /* Header Length */ + u64 total_len; /* entire L1 Directory length including this header */ + u64 elog_base; /* MCA Error Log Directory base address */ + u64 elog_len; /* MCA Error Log Directory length */ + u32 flags; /* bit 0 - OS/VMM Opt-in */ + u8 rev0[12]; + u32 entries; /* Valid L1 Directory entries per logical processor */ + u8 rev1[12]; +}; + +static u8 extlog_dsm_uuid[] = "663E35AF-CC10-41A4-88EA-5470AF055295"; + +/* L1 table related physical address */ +static u64 elog_base; +static size_t elog_size; +static u64 l1_dirbase; +static size_t l1_size; + +/* L1 table related virtual address */ +static void __iomem *extlog_l1_addr; +static void __iomem *elog_addr; + +static void *elog_buf; + +static u64 *l1_entry_base; +static u32 l1_percpu_entry; + +#define ELOG_IDX(cpu, bank) \ + (cpu_physical_id(cpu) * l1_percpu_entry + (bank)) + +#define ELOG_ENTRY_DATA(idx) \ + (*(l1_entry_base + (idx))) + +#define ELOG_ENTRY_ADDR(phyaddr) \ + (phyaddr - elog_base + (u8 *)elog_addr) + +static struct acpi_generic_status *extlog_elog_entry_check(int cpu, int bank) +{ + int idx; + u64 data; + struct acpi_generic_status *estatus; + + WARN_ON(cpu < 0); + idx = ELOG_IDX(cpu, bank); + data = ELOG_ENTRY_DATA(idx); + if ((data & ELOG_ENTRY_VALID) == 0) + return NULL; + + data &= EXT_ELOG_ENTRY_MASK; + estatus = (struct acpi_generic_status *)ELOG_ENTRY_ADDR(data); + + /* if no valid data in elog entry, just return */ + if (estatus->block_status == 0) + return NULL; + + return estatus; +} + +static void __print_extlog_rcd(const char *pfx, + struct acpi_generic_status *estatus, int cpu) +{ + static atomic_t seqno; + unsigned int curr_seqno; + char pfx_seq[64]; + + if (!pfx) { + if (estatus->error_severity <= CPER_SEV_CORRECTED) + pfx = KERN_INFO; + else + pfx = KERN_ERR; + } + curr_seqno = atomic_inc_return(&seqno); + snprintf(pfx_seq, sizeof(pfx_seq), "%s{%u}", pfx, curr_seqno); + printk("%s""Hardware error detected on CPU%d\n", pfx_seq, cpu); + cper_estatus_print(pfx_seq, estatus); +} + +static int print_extlog_rcd(const char *pfx, + struct acpi_generic_status *estatus, int cpu) +{ + /* Not more than 2 messages every 5 seconds */ + static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5*HZ, 2); + static DEFINE_RATELIMIT_STATE(ratelimit_uncorrected, 5*HZ, 2); + struct ratelimit_state *ratelimit; + + if (estatus->error_severity == CPER_SEV_CORRECTED || + (estatus->error_severity == CPER_SEV_INFORMATIONAL)) + ratelimit = &ratelimit_corrected; + else + ratelimit = &ratelimit_uncorrected; + if (__ratelimit(ratelimit)) { + __print_extlog_rcd(pfx, estatus, cpu); + return 0; + } + + return 1; +} + +static int extlog_print(struct notifier_block *nb, unsigned long val, + void *data) +{ + struct mce *mce = (struct mce *)data; + int bank = mce->bank; + int cpu = mce->extcpu; + struct acpi_generic_status *estatus; + int rc; + + estatus = extlog_elog_entry_check(cpu, bank); + if (estatus == NULL) + return NOTIFY_DONE; + + memcpy(elog_buf, (void *)estatus, ELOG_ENTRY_LEN); + /* clear record status to enable BIOS to update it again */ + estatus->block_status = 0; + + rc = print_extlog_rcd(NULL, (struct acpi_generic_status *)elog_buf, cpu); + + return NOTIFY_DONE; +} + +static int extlog_get_dsm(acpi_handle handle, int rev, int func, u64 *ret) +{ + struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL}; + struct acpi_object_list input; + union acpi_object params[4], *obj; + u8 uuid[16]; + int i; + + acpi_str_to_uuid(extlog_dsm_uuid, uuid); + input.count = 4; + input.pointer = params; + params[0].type = ACPI_TYPE_BUFFER; + params[0].buffer.length = 16; + params[0].buffer.pointer = uuid; + params[1].type = ACPI_TYPE_INTEGER; + params[1].integer.value = rev; + params[2].type = ACPI_TYPE_INTEGER; + params[2].integer.value = func; + params[3].type = ACPI_TYPE_PACKAGE; + params[3].package.count = 0; + params[3].package.elements = NULL; + + if (ACPI_FAILURE(acpi_evaluate_object(handle, "_DSM", &input, &buf))) + return -1; + + *ret = 0; + obj = (union acpi_object *)buf.pointer; + if (obj->type == ACPI_TYPE_INTEGER) { + *ret = obj->integer.value; + } else if (obj->type == ACPI_TYPE_BUFFER) { + if (obj->buffer.length <= 8) { + for (i = 0; i < obj->buffer.length; i++) + *ret |= (obj->buffer.pointer[i] << (i * 8)); + } + } + kfree(buf.pointer); + + return 0; +} + +static bool extlog_get_l1addr(void) +{ + acpi_handle handle; + u64 ret; + + if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle))) + return false; + + if (extlog_get_dsm(handle, EXTLOG_DSM_REV, EXTLOG_FN_QUERY, &ret) || + !(ret & EXTLOG_QUERY_L1_EXIST)) + return false; + + if (extlog_get_dsm(handle, EXTLOG_DSM_REV, EXTLOG_FN_ADDR, &ret)) + return false; + + l1_dirbase = ret; + /* Spec says L1 directory must be 4K aligned, bail out if it isn't */ + if (l1_dirbase & ((1 << 12) - 1)) { + pr_warn(FW_BUG "L1 Directory is invalid at physical %llx\n", + l1_dirbase); + return false; + } + + return true; +} +static struct notifier_block extlog_mce_dec = { + .notifier_call = extlog_print, +}; + +static int __init extlog_init(void) +{ + struct extlog_l1_head *l1_head; + void __iomem *extlog_l1_hdr; + size_t l1_hdr_size; + struct resource *r; + u64 cap; + int rc; + + rc = -ENODEV; + + rdmsrl(MSR_IA32_MCG_CAP, cap); + if (!(cap & MCG_ELOG_P)) + return rc; + + if (!extlog_get_l1addr()) + return rc; + + rc = -EINVAL; + /* get L1 header to fetch necessary information */ + l1_hdr_size = sizeof(struct extlog_l1_head); + r = request_mem_region(l1_dirbase, l1_hdr_size, "L1 DIR HDR"); + if (!r) { + pr_warn(FW_BUG EMCA_BUG, + (unsigned long long)l1_dirbase, + (unsigned long long)l1_dirbase + l1_hdr_size); + goto err; + } + + extlog_l1_hdr = acpi_os_map_memory(l1_dirbase, l1_hdr_size); + l1_head = (struct extlog_l1_head *)extlog_l1_hdr; + l1_size = l1_head->total_len; + l1_percpu_entry = l1_head->entries; + elog_base = l1_head->elog_base; + elog_size = l1_head->elog_len; + acpi_os_unmap_memory(extlog_l1_hdr, l1_hdr_size); + release_mem_region(l1_dirbase, l1_hdr_size); + + /* remap L1 header again based on completed information */ + r = request_mem_region(l1_dirbase, l1_size, "L1 Table"); + if (!r) { + pr_warn(FW_BUG EMCA_BUG, + (unsigned long long)l1_dirbase, + (unsigned long long)l1_dirbase + l1_size); + goto err; + } + extlog_l1_addr = acpi_os_map_memory(l1_dirbase, l1_size); + l1_entry_base = (u64 *)((u8 *)extlog_l1_addr + l1_hdr_size); + + /* remap elog table */ + r = request_mem_region(elog_base, elog_size, "Elog Table"); + if (!r) { + pr_warn(FW_BUG EMCA_BUG, + (unsigned long long)elog_base, + (unsigned long long)elog_base + elog_size); + goto err_release_l1_dir; + } + elog_addr = acpi_os_map_memory(elog_base, elog_size); + + rc = -ENOMEM; + /* allocate buffer to save elog record */ + elog_buf = kmalloc(ELOG_ENTRY_LEN, GFP_KERNEL); + if (elog_buf == NULL) + goto err_release_elog; + + mce_register_decode_chain(&extlog_mce_dec); + /* enable OS to be involved to take over management from BIOS */ + ((struct extlog_l1_head *)extlog_l1_addr)->flags |= FLAG_OS_OPTIN; + + return 0; + +err_release_elog: + if (elog_addr) + acpi_os_unmap_memory(elog_addr, elog_size); + release_mem_region(elog_base, elog_size); +err_release_l1_dir: + if (extlog_l1_addr) + acpi_os_unmap_memory(extlog_l1_addr, l1_size); + release_mem_region(l1_dirbase, l1_size); +err: + pr_warn(FW_BUG "Extended error log disabled because of problems parsing f/w tables\n"); + return rc; +} + +static void __exit extlog_exit(void) +{ + mce_unregister_decode_chain(&extlog_mce_dec); + ((struct extlog_l1_head *)extlog_l1_addr)->flags &= ~FLAG_OS_OPTIN; + if (extlog_l1_addr) + acpi_os_unmap_memory(extlog_l1_addr, l1_size); + if (elog_addr) + acpi_os_unmap_memory(elog_addr, elog_size); + release_mem_region(elog_base, elog_size); + release_mem_region(l1_dirbase, l1_size); + kfree(elog_buf); +} + +module_init(extlog_init); +module_exit(extlog_exit); + +MODULE_AUTHOR("Chen, Gong "); +MODULE_DESCRIPTION("Extended MCA Error Log Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index b587ec8..e1bd9a1 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -174,7 +174,7 @@ static void acpi_print_osc_error(acpi_handle handle, printk("\n"); } -static acpi_status acpi_str_to_uuid(char *str, u8 *uuid) +acpi_status acpi_str_to_uuid(char *str, u8 *uuid) { int i; static int opc_map_to_uuid[16] = {6, 4, 2, 0, 11, 9, 16, 14, 19, 21, @@ -195,6 +195,7 @@ static acpi_status acpi_str_to_uuid(char *str, u8 *uuid) } return AE_OK; } +EXPORT_SYMBOL_GPL(acpi_str_to_uuid); acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context) { diff --git a/include/linux/acpi.h b/include/linux/acpi.h index a5db4ae..c30bac8 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -311,6 +311,7 @@ struct acpi_osc_context { #define OSC_INVALID_REVISION_ERROR 8 #define OSC_CAPABILITIES_MASK_ERROR 16 +acpi_status acpi_str_to_uuid(char *str, u8 *uuid); acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context); /* platform-wide _OSC bits */ -- cgit v0.10.2 From dd6dad4288cb93e79bd7abfa6c6a338c47454d1a Mon Sep 17 00:00:00 2001 From: "Chen, Gong" Date: Fri, 18 Oct 2013 14:29:25 -0700 Subject: DMI: Parse memory device (type 17) in SMBIOS This patch adds a new interface to decode memory device (type 17) to help error reporting on DIMMs. Original-author: Tony Luck Signed-off-by: Chen, Gong Acked-by: Naveen N. Rao Acked-by: Borislav Petkov Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Tony Luck diff --git a/arch/ia64/kernel/setup.c b/arch/ia64/kernel/setup.c index 4fc2e95..d86669b 100644 --- a/arch/ia64/kernel/setup.c +++ b/arch/ia64/kernel/setup.c @@ -1063,6 +1063,7 @@ check_bugs (void) static int __init run_dmi_scan(void) { dmi_scan_machine(); + dmi_memdev_walk(); dmi_set_dump_stack_arch_desc(); return 0; } diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index f0de629..918d489 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -993,6 +993,7 @@ void __init setup_arch(char **cmdline_p) efi_init(); dmi_scan_machine(); + dmi_memdev_walk(); dmi_set_dump_stack_arch_desc(); /* diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index fa0affb..59579a7 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -25,6 +25,13 @@ static int dmi_initialized; /* DMI system identification string used during boot */ static char dmi_ids_string[128] __initdata; +static struct dmi_memdev_info { + const char *device; + const char *bank; + u16 handle; +} *dmi_memdev; +static int dmi_memdev_nr; + static const char * __init dmi_string_nosave(const struct dmi_header *dm, u8 s) { const u8 *bp = ((u8 *) dm) + dm->length; @@ -322,6 +329,42 @@ static void __init dmi_save_extended_devices(const struct dmi_header *dm) dmi_save_one_device(*d & 0x7f, dmi_string_nosave(dm, *(d - 1))); } +static void __init count_mem_devices(const struct dmi_header *dm, void *v) +{ + if (dm->type != DMI_ENTRY_MEM_DEVICE) + return; + dmi_memdev_nr++; +} + +static void __init save_mem_devices(const struct dmi_header *dm, void *v) +{ + const char *d = (const char *)dm; + static int nr; + + if (dm->type != DMI_ENTRY_MEM_DEVICE) + return; + if (nr >= dmi_memdev_nr) { + pr_warn(FW_BUG "Too many DIMM entries in SMBIOS table\n"); + return; + } + dmi_memdev[nr].handle = dm->handle; + dmi_memdev[nr].device = dmi_string(dm, d[0x10]); + dmi_memdev[nr].bank = dmi_string(dm, d[0x11]); + nr++; +} + +void __init dmi_memdev_walk(void) +{ + if (!dmi_available) + return; + + if (dmi_walk_early(count_mem_devices) == 0 && dmi_memdev_nr) { + dmi_memdev = dmi_alloc(sizeof(*dmi_memdev) * dmi_memdev_nr); + if (dmi_memdev) + dmi_walk_early(save_mem_devices); + } +} + /* * Process a DMI table entry. Right now all we care about are the BIOS * and machine entries. For 2.5 we should pull the smbus controller info @@ -815,3 +858,20 @@ bool dmi_match(enum dmi_field f, const char *str) return !strcmp(info, str); } EXPORT_SYMBOL_GPL(dmi_match); + +void dmi_memdev_name(u16 handle, const char **bank, const char **device) +{ + int n; + + if (dmi_memdev == NULL) + return; + + for (n = 0; n < dmi_memdev_nr; n++) { + if (handle == dmi_memdev[n].handle) { + *bank = dmi_memdev[n].bank; + *device = dmi_memdev[n].device; + break; + } + } +} +EXPORT_SYMBOL_GPL(dmi_memdev_name); diff --git a/include/linux/dmi.h b/include/linux/dmi.h index b6eb7a0..f820f0a 100644 --- a/include/linux/dmi.h +++ b/include/linux/dmi.h @@ -99,6 +99,7 @@ extern const char * dmi_get_system_info(int field); extern const struct dmi_device * dmi_find_device(int type, const char *name, const struct dmi_device *from); extern void dmi_scan_machine(void); +extern void dmi_memdev_walk(void); extern void dmi_set_dump_stack_arch_desc(void); extern bool dmi_get_date(int field, int *yearp, int *monthp, int *dayp); extern int dmi_name_in_vendors(const char *str); @@ -107,6 +108,7 @@ extern int dmi_available; extern int dmi_walk(void (*decode)(const struct dmi_header *, void *), void *private_data); extern bool dmi_match(enum dmi_field f, const char *str); +extern void dmi_memdev_name(u16 handle, const char **bank, const char **device); #else @@ -115,6 +117,7 @@ static inline const char * dmi_get_system_info(int field) { return NULL; } static inline const struct dmi_device * dmi_find_device(int type, const char *name, const struct dmi_device *from) { return NULL; } static inline void dmi_scan_machine(void) { return; } +static inline void dmi_memdev_walk(void) { } static inline void dmi_set_dump_stack_arch_desc(void) { } static inline bool dmi_get_date(int field, int *yearp, int *monthp, int *dayp) { @@ -133,6 +136,8 @@ static inline int dmi_walk(void (*decode)(const struct dmi_header *, void *), void *private_data) { return -1; } static inline bool dmi_match(enum dmi_field f, const char *str) { return false; } +static inline void dmi_memdev_name(u16 handle, const char **bank, + const char **device) { } static inline const struct dmi_system_id * dmi_first_match(const struct dmi_system_id *list) { return NULL; } -- cgit v0.10.2 From 147de14772ed897727dba7353916b02d1e0f17f4 Mon Sep 17 00:00:00 2001 From: "Chen, Gong" Date: Fri, 18 Oct 2013 14:30:13 -0700 Subject: ACPI, APEI, CPER: Add UEFI 2.4 support for memory error In latest UEFI spec(by now it is 2.4) memory error definition for CPER (UEFI 2.4 Appendix N Common Platform Error Record) adds some new fields. These fields help people to locate memory error to an actual DIMM location. Original-author: Tony Luck Signed-off-by: Chen, Gong Reviewed-by: Borislav Petkov Reviewed-by: Mauro Carvalho Chehab Acked-by: Naveen N. Rao Signed-off-by: Tony Luck diff --git a/arch/x86/kernel/cpu/mcheck/mce-apei.c b/arch/x86/kernel/cpu/mcheck/mce-apei.c index cd8b166..de8b60a 100644 --- a/arch/x86/kernel/cpu/mcheck/mce-apei.c +++ b/arch/x86/kernel/cpu/mcheck/mce-apei.c @@ -42,8 +42,7 @@ void apei_mce_report_mem_error(int corrected, struct cper_sec_mem_err *mem_err) struct mce m; /* Only corrected MC is reported */ - if (!corrected || !(mem_err->validation_bits & - CPER_MEM_VALID_PHYSICAL_ADDRESS)) + if (!corrected || !(mem_err->validation_bits & CPER_MEM_VALID_PA)) return; mce_setup(&m); diff --git a/drivers/acpi/apei/cper.c b/drivers/acpi/apei/cper.c index eb5f6d6..946ef52 100644 --- a/drivers/acpi/apei/cper.c +++ b/drivers/acpi/apei/cper.c @@ -8,7 +8,7 @@ * various tables, such as ERST, BERT and HEST etc. * * For more information about CPER, please refer to Appendix N of UEFI - * Specification version 2.3. + * Specification version 2.4. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version @@ -191,16 +191,17 @@ static const char *cper_mem_err_type_strs[] = { "memory sparing", "scrub corrected error", "scrub uncorrected error", + "physical memory map-out event", }; static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem) { if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS) printk("%s""error_status: 0x%016llx\n", pfx, mem->error_status); - if (mem->validation_bits & CPER_MEM_VALID_PHYSICAL_ADDRESS) + if (mem->validation_bits & CPER_MEM_VALID_PA) printk("%s""physical_address: 0x%016llx\n", pfx, mem->physical_addr); - if (mem->validation_bits & CPER_MEM_VALID_PHYSICAL_ADDRESS_MASK) + if (mem->validation_bits & CPER_MEM_VALID_PA_MASK) printk("%s""physical_address_mask: 0x%016llx\n", pfx, mem->physical_addr_mask); if (mem->validation_bits & CPER_MEM_VALID_NODE) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 0db6e4f..a30bc31 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -419,7 +419,7 @@ static void ghes_handle_memory_failure(struct acpi_generic_data *gdata, int sev) if (sec_sev == GHES_SEV_CORRECTED && (gdata->flags & CPER_SEC_ERROR_THRESHOLD_EXCEEDED) && - (mem_err->validation_bits & CPER_MEM_VALID_PHYSICAL_ADDRESS)) { + (mem_err->validation_bits & CPER_MEM_VALID_PA)) { pfn = mem_err->physical_addr >> PAGE_SHIFT; if (pfn_valid(pfn)) memory_failure_queue(pfn, 0, MF_SOFT_OFFLINE); @@ -430,7 +430,7 @@ static void ghes_handle_memory_failure(struct acpi_generic_data *gdata, int sev) } if (sev == GHES_SEV_RECOVERABLE && sec_sev == GHES_SEV_RECOVERABLE && - mem_err->validation_bits & CPER_MEM_VALID_PHYSICAL_ADDRESS) { + mem_err->validation_bits & CPER_MEM_VALID_PA) { pfn = mem_err->physical_addr >> PAGE_SHIFT; memory_failure_queue(pfn, 0, 0); } diff --git a/drivers/edac/ghes_edac.c b/drivers/edac/ghes_edac.c index bb53467..0ad797b 100644 --- a/drivers/edac/ghes_edac.c +++ b/drivers/edac/ghes_edac.c @@ -297,15 +297,14 @@ void ghes_edac_report_mem_error(struct ghes *ghes, int sev, } /* Error address */ - if (mem_err->validation_bits & CPER_MEM_VALID_PHYSICAL_ADDRESS) { + if (mem_err->validation_bits & CPER_MEM_VALID_PA) { e->page_frame_number = mem_err->physical_addr >> PAGE_SHIFT; e->offset_in_page = mem_err->physical_addr & ~PAGE_MASK; } /* Error grain */ - if (mem_err->validation_bits & CPER_MEM_VALID_PHYSICAL_ADDRESS_MASK) { + if (mem_err->validation_bits & CPER_MEM_VALID_PA_MASK) e->grain = ~(mem_err->physical_addr_mask & ~PAGE_MASK); - } /* Memory error location, mapped on e->location */ p = e->location; diff --git a/include/linux/cper.h b/include/linux/cper.h index 09ebe21..2fc0ec3 100644 --- a/include/linux/cper.h +++ b/include/linux/cper.h @@ -218,8 +218,8 @@ enum { #define CPER_PROC_VALID_IP 0x1000 #define CPER_MEM_VALID_ERROR_STATUS 0x0001 -#define CPER_MEM_VALID_PHYSICAL_ADDRESS 0x0002 -#define CPER_MEM_VALID_PHYSICAL_ADDRESS_MASK 0x0004 +#define CPER_MEM_VALID_PA 0x0002 +#define CPER_MEM_VALID_PA_MASK 0x0004 #define CPER_MEM_VALID_NODE 0x0008 #define CPER_MEM_VALID_CARD 0x0010 #define CPER_MEM_VALID_MODULE 0x0020 @@ -232,6 +232,9 @@ enum { #define CPER_MEM_VALID_RESPONDER_ID 0x1000 #define CPER_MEM_VALID_TARGET_ID 0x2000 #define CPER_MEM_VALID_ERROR_TYPE 0x4000 +#define CPER_MEM_VALID_RANK_NUMBER 0x8000 +#define CPER_MEM_VALID_CARD_HANDLE 0x10000 +#define CPER_MEM_VALID_MODULE_HANDLE 0x20000 #define CPER_PCIE_VALID_PORT_TYPE 0x0001 #define CPER_PCIE_VALID_VERSION 0x0002 @@ -347,6 +350,10 @@ struct cper_sec_mem_err { __u64 responder_id; __u64 target_id; __u8 error_type; + __u8 reserved; + __u16 rank; + __u16 mem_array_handle; /* card handle in UEFI 2.4 */ + __u16 mem_dev_handle; /* module handle in UEFI 2.4 */ }; struct cper_sec_pcie { -- cgit v0.10.2 From fbeef85fd2ccdd61568c86fe33d6ad6b79851a53 Mon Sep 17 00:00:00 2001 From: "Chen, Gong" Date: Fri, 18 Oct 2013 14:30:21 -0700 Subject: ACPI, APEI, CPER: Enhance memory reporting capability After H/W error happens under FFM enabled mode, lots of information are shown but new fields added by UEFI 2.4 (e.g. DIMM location) need to be added. Original-author: Tony Luck Signed-off-by: Chen, Gong Acked-by: Naveen N. Rao Acked-by: Borislav Petkov Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Tony Luck diff --git a/drivers/acpi/apei/cper.c b/drivers/acpi/apei/cper.c index 946ef52..b1a8a55 100644 --- a/drivers/acpi/apei/cper.c +++ b/drivers/acpi/apei/cper.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -210,6 +211,8 @@ static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem) printk("%s""card: %d\n", pfx, mem->card); if (mem->validation_bits & CPER_MEM_VALID_MODULE) printk("%s""module: %d\n", pfx, mem->module); + if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER) + printk("%s""rank: %d\n", pfx, mem->rank); if (mem->validation_bits & CPER_MEM_VALID_BANK) printk("%s""bank: %d\n", pfx, mem->bank); if (mem->validation_bits & CPER_MEM_VALID_DEVICE) @@ -232,6 +235,15 @@ static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem) etype < ARRAY_SIZE(cper_mem_err_type_strs) ? cper_mem_err_type_strs[etype] : "unknown"); } + if (mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE) { + const char *bank = NULL, *device = NULL; + dmi_memdev_name(mem->mem_dev_handle, &bank, &device); + if (bank != NULL && device != NULL) + printk("%s""DIMM location: %s %s", pfx, bank, device); + else + printk("%s""DIMM DMI handle: 0x%.4x", + pfx, mem->mem_dev_handle); + } } static const char *cper_pcie_port_type_strs[] = { -- cgit v0.10.2 From f6edea77c8c83760d74356ce6bd45d530d32b27f Mon Sep 17 00:00:00 2001 From: "Chen, Gong" Date: Fri, 18 Oct 2013 14:30:29 -0700 Subject: ACPI, APEI, CPER: Cleanup CPER memory error output format Memory error reporting is much too verbose. Most users do not care about the DIMM internal bank/row/column information. Downgrade the fine details to "pr_debug" status so that those few who do care can get them if they really want to. The detail information will be later be provided by perf/trace interface. Since things are still a bit scary, and users are sometimes overly nervous, provide a reassuring message that corrected errors do not generally require any further action. Suggested-by: Tony Luck Signed-off-by: Chen, Gong Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Tony Luck diff --git a/drivers/acpi/apei/cper.c b/drivers/acpi/apei/cper.c index b1a8a55..1491dd4 100644 --- a/drivers/acpi/apei/cper.c +++ b/drivers/acpi/apei/cper.c @@ -33,6 +33,7 @@ #include #include +#define INDENT_SP " " /* * CPER record ID need to be unique even after reboot, because record * ID is used as index for ERST storage, while CPER records from @@ -206,29 +207,29 @@ static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem) printk("%s""physical_address_mask: 0x%016llx\n", pfx, mem->physical_addr_mask); if (mem->validation_bits & CPER_MEM_VALID_NODE) - printk("%s""node: %d\n", pfx, mem->node); + pr_debug("node: %d\n", mem->node); if (mem->validation_bits & CPER_MEM_VALID_CARD) - printk("%s""card: %d\n", pfx, mem->card); + pr_debug("card: %d\n", mem->card); if (mem->validation_bits & CPER_MEM_VALID_MODULE) - printk("%s""module: %d\n", pfx, mem->module); + pr_debug("module: %d\n", mem->module); if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER) - printk("%s""rank: %d\n", pfx, mem->rank); + pr_debug("rank: %d\n", mem->rank); if (mem->validation_bits & CPER_MEM_VALID_BANK) - printk("%s""bank: %d\n", pfx, mem->bank); + pr_debug("bank: %d\n", mem->bank); if (mem->validation_bits & CPER_MEM_VALID_DEVICE) - printk("%s""device: %d\n", pfx, mem->device); + pr_debug("device: %d\n", mem->device); if (mem->validation_bits & CPER_MEM_VALID_ROW) - printk("%s""row: %d\n", pfx, mem->row); + pr_debug("row: %d\n", mem->row); if (mem->validation_bits & CPER_MEM_VALID_COLUMN) - printk("%s""column: %d\n", pfx, mem->column); + pr_debug("column: %d\n", mem->column); if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION) - printk("%s""bit_position: %d\n", pfx, mem->bit_pos); + pr_debug("bit_position: %d\n", mem->bit_pos); if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID) - printk("%s""requestor_id: 0x%016llx\n", pfx, mem->requestor_id); + pr_debug("requestor_id: 0x%016llx\n", mem->requestor_id); if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID) - printk("%s""responder_id: 0x%016llx\n", pfx, mem->responder_id); + pr_debug("responder_id: 0x%016llx\n", mem->responder_id); if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID) - printk("%s""target_id: 0x%016llx\n", pfx, mem->target_id); + pr_debug("target_id: 0x%016llx\n", mem->target_id); if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) { u8 etype = mem->error_type; printk("%s""error_type: %d, %s\n", pfx, etype, @@ -296,55 +297,45 @@ static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie, pfx, pcie->bridge.secondary_status, pcie->bridge.control); } -static const char * const cper_estatus_section_flag_strs[] = { - "primary", - "containment warning", - "reset", - "error threshold exceeded", - "resource not accessible", - "latent error", -}; - static void cper_estatus_print_section( const char *pfx, const struct acpi_generic_data *gdata, int sec_no) { uuid_le *sec_type = (uuid_le *)gdata->section_type; __u16 severity; + char newpfx[64]; severity = gdata->error_severity; - printk("%s""section: %d, severity: %d, %s\n", pfx, sec_no, severity, + printk("%s""Error %d, type: %s\n", pfx, sec_no, cper_severity_str(severity)); - printk("%s""flags: 0x%02x\n", pfx, gdata->flags); - cper_print_bits(pfx, gdata->flags, cper_estatus_section_flag_strs, - ARRAY_SIZE(cper_estatus_section_flag_strs)); if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID) printk("%s""fru_id: %pUl\n", pfx, (uuid_le *)gdata->fru_id); if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text); + snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP); if (!uuid_le_cmp(*sec_type, CPER_SEC_PROC_GENERIC)) { struct cper_sec_proc_generic *proc_err = (void *)(gdata + 1); - printk("%s""section_type: general processor error\n", pfx); + printk("%s""section_type: general processor error\n", newpfx); if (gdata->error_data_length >= sizeof(*proc_err)) - cper_print_proc_generic(pfx, proc_err); + cper_print_proc_generic(newpfx, proc_err); else goto err_section_too_small; } else if (!uuid_le_cmp(*sec_type, CPER_SEC_PLATFORM_MEM)) { struct cper_sec_mem_err *mem_err = (void *)(gdata + 1); - printk("%s""section_type: memory error\n", pfx); + printk("%s""section_type: memory error\n", newpfx); if (gdata->error_data_length >= sizeof(*mem_err)) - cper_print_mem(pfx, mem_err); + cper_print_mem(newpfx, mem_err); else goto err_section_too_small; } else if (!uuid_le_cmp(*sec_type, CPER_SEC_PCIE)) { struct cper_sec_pcie *pcie = (void *)(gdata + 1); - printk("%s""section_type: PCIe error\n", pfx); + printk("%s""section_type: PCIe error\n", newpfx); if (gdata->error_data_length >= sizeof(*pcie)) - cper_print_pcie(pfx, pcie, gdata); + cper_print_pcie(newpfx, pcie, gdata); else goto err_section_too_small; } else - printk("%s""section type: unknown, %pUl\n", pfx, sec_type); + printk("%s""section type: unknown, %pUl\n", newpfx, sec_type); return; @@ -358,17 +349,21 @@ void cper_estatus_print(const char *pfx, struct acpi_generic_data *gdata; unsigned int data_len, gedata_len; int sec_no = 0; + char newpfx[64]; __u16 severity; - printk("%s""Generic Hardware Error Status\n", pfx); severity = estatus->error_severity; - printk("%s""severity: %d, %s\n", pfx, severity, - cper_severity_str(severity)); + if (severity == CPER_SEV_CORRECTED) + printk("%s%s\n", pfx, + "It has been corrected by h/w " + "and requires no further action"); + printk("%s""event severity: %s\n", pfx, cper_severity_str(severity)); data_len = estatus->data_length; gdata = (struct acpi_generic_data *)(estatus + 1); + snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP); while (data_len >= sizeof(*gdata)) { gedata_len = gdata->error_data_length; - cper_estatus_print_section(pfx, gdata, sec_no); + cper_estatus_print_section(newpfx, gdata, sec_no); data_len -= gedata_len + sizeof(*gdata); gdata = (void *)(gdata + 1) + gedata_len; sec_no++; -- cgit v0.10.2 From 56507694de3453076d73e0e9813349586ee67e59 Mon Sep 17 00:00:00 2001 From: "Chen, Gong" Date: Fri, 18 Oct 2013 14:30:38 -0700 Subject: EDAC, GHES: Update ghes error record info In latest UEFI spec(by now it's 2.4) there are some new fields for memory error reporting. Add these new fields for ghes_edac interface. Signed-off-by: Chen, Gong Cc: Mauro Carvalho Chehab Signed-off-by: Tony Luck diff --git a/drivers/edac/ghes_edac.c b/drivers/edac/ghes_edac.c index 0ad797b..d5a98a4 100644 --- a/drivers/edac/ghes_edac.c +++ b/drivers/edac/ghes_edac.c @@ -314,6 +314,8 @@ void ghes_edac_report_mem_error(struct ghes *ghes, int sev, p += sprintf(p, "card:%d ", mem_err->card); if (mem_err->validation_bits & CPER_MEM_VALID_MODULE) p += sprintf(p, "module:%d ", mem_err->module); + if (mem_err->validation_bits & CPER_MEM_VALID_RANK_NUMBER) + p += sprintf(p, "rank:%d ", mem_err->rank); if (mem_err->validation_bits & CPER_MEM_VALID_BANK) p += sprintf(p, "bank:%d ", mem_err->bank); if (mem_err->validation_bits & CPER_MEM_VALID_ROW) @@ -322,6 +324,15 @@ void ghes_edac_report_mem_error(struct ghes *ghes, int sev, p += sprintf(p, "col:%d ", mem_err->column); if (mem_err->validation_bits & CPER_MEM_VALID_BIT_POSITION) p += sprintf(p, "bit_pos:%d ", mem_err->bit_pos); + if (mem_err->validation_bits & CPER_MEM_VALID_MODULE_HANDLE) { + const char *bank = NULL, *device = NULL; + dmi_memdev_name(mem_err->mem_dev_handle, &bank, &device); + if (bank != NULL && device != NULL) + p += sprintf(p, "DIMM location:%s %s ", bank, device); + else + p += sprintf(p, "DIMM DMI handle: 0x%.4x ", + mem_err->mem_dev_handle); + } if (p > e->location) *(p - 1) = '\0'; diff --git a/include/linux/edac.h b/include/linux/edac.h index 5c6d7fb..dbdffe8 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -51,7 +51,7 @@ static inline void opstate_init(void) #define EDAC_MC_LABEL_LEN 31 /* Maximum size of the location string */ -#define LOCATION_SIZE 80 +#define LOCATION_SIZE 256 /* Defines the maximum number of labels that can be reported */ #define EDAC_MAX_LABELS 8 -- cgit v0.10.2 From 4779a2e99af80e133ee1c70c7093dc6cc13429a1 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 23 Oct 2013 14:08:48 -0300 Subject: perf ui: Rename ui_progress to ui_progress_ops Reserving 'struct ui_progress' to the per progress instances, not to the particular set of operations used to implmenet a progress bar in the current UI (GTK, TUI, etc). Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-zjqbfp9gx3yo45s0rp9uv42n@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 326a26e..8a9ca38 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -487,6 +487,7 @@ ifndef NO_SLANG LIB_OBJS += $(OUTPUT)ui/tui/util.o LIB_OBJS += $(OUTPUT)ui/tui/helpline.o LIB_OBJS += $(OUTPUT)ui/tui/progress.o + LIB_H += ui/tui/tui.h LIB_H += ui/browser.h LIB_H += ui/browsers/map.h LIB_H += ui/keysyms.h diff --git a/tools/perf/ui/gtk/gtk.h b/tools/perf/ui/gtk/gtk.h index 8576cf1..0a9173f 100644 --- a/tools/perf/ui/gtk/gtk.h +++ b/tools/perf/ui/gtk/gtk.h @@ -34,7 +34,7 @@ struct perf_gtk_context *perf_gtk__activate_context(GtkWidget *window); int perf_gtk__deactivate_context(struct perf_gtk_context **ctx); void perf_gtk__init_helpline(void); -void perf_gtk__init_progress(void); +void gtk_ui_progress__init(void); void perf_gtk__init_hpp(void); void perf_gtk__signal(int sig); diff --git a/tools/perf/ui/gtk/progress.c b/tools/perf/ui/gtk/progress.c index 482bcf3..195c7f9 100644 --- a/tools/perf/ui/gtk/progress.c +++ b/tools/perf/ui/gtk/progress.c @@ -7,7 +7,7 @@ static GtkWidget *dialog; static GtkWidget *progress; -static void gtk_progress_update(u64 curr, u64 total, const char *title) +static void gtk_ui_progress__update(u64 curr, u64 total, const char *title) { double fraction = total ? 1.0 * curr / total : 0.0; char buf[1024]; @@ -40,7 +40,7 @@ static void gtk_progress_update(u64 curr, u64 total, const char *title) gtk_main_iteration(); } -static void gtk_progress_finish(void) +static void gtk_ui_progress__finish(void) { /* this will also destroy all of its children */ gtk_widget_destroy(dialog); @@ -48,12 +48,12 @@ static void gtk_progress_finish(void) dialog = NULL; } -static struct ui_progress gtk_progress_fns = { - .update = gtk_progress_update, - .finish = gtk_progress_finish, +static struct ui_progress_ops gtk_ui_progress__ops = { + .update = gtk_ui_progress__update, + .finish = gtk_ui_progress__finish, }; -void perf_gtk__init_progress(void) +void gtk_ui_progress__init(void) { - progress_fns = >k_progress_fns; + ui_progress__ops = >k_ui_progress__ops; } diff --git a/tools/perf/ui/gtk/setup.c b/tools/perf/ui/gtk/setup.c index 6c2dd2e..1d57676 100644 --- a/tools/perf/ui/gtk/setup.c +++ b/tools/perf/ui/gtk/setup.c @@ -8,7 +8,7 @@ int perf_gtk__init(void) { perf_error__register(&perf_gtk_eops); perf_gtk__init_helpline(); - perf_gtk__init_progress(); + gtk_ui_progress__init(); perf_gtk__init_hpp(); return gtk_init_check(NULL, NULL) ? 0 : -1; diff --git a/tools/perf/ui/progress.c b/tools/perf/ui/progress.c index 3ec69560..d753821 100644 --- a/tools/perf/ui/progress.c +++ b/tools/perf/ui/progress.c @@ -1,26 +1,26 @@ #include "../cache.h" #include "progress.h" -static void nop_progress_update(u64 curr __maybe_unused, - u64 total __maybe_unused, - const char *title __maybe_unused) +static void null_progress__update(u64 curr __maybe_unused, + u64 total __maybe_unused, + const char *title __maybe_unused) { } -static struct ui_progress default_progress_fns = +static struct ui_progress_ops null_progress__ops = { - .update = nop_progress_update, + .update = null_progress__update, }; -struct ui_progress *progress_fns = &default_progress_fns; +struct ui_progress_ops *ui_progress__ops = &null_progress__ops; void ui_progress__update(u64 curr, u64 total, const char *title) { - return progress_fns->update(curr, total, title); + return ui_progress__ops->update(curr, total, title); } void ui_progress__finish(void) { - if (progress_fns->finish) - progress_fns->finish(); + if (ui_progress__ops->finish) + ui_progress__ops->finish(); } diff --git a/tools/perf/ui/progress.h b/tools/perf/ui/progress.h index 257cc22..d41bde5 100644 --- a/tools/perf/ui/progress.h +++ b/tools/perf/ui/progress.h @@ -3,14 +3,12 @@ #include <../types.h> -struct ui_progress { +struct ui_progress_ops { void (*update)(u64, u64, const char *); void (*finish)(void); }; -extern struct ui_progress *progress_fns; - -void ui_progress__init(void); +extern struct ui_progress_ops *ui_progress__ops; void ui_progress__update(u64 curr, u64 total, const char *title); void ui_progress__finish(void); diff --git a/tools/perf/ui/tui/progress.c b/tools/perf/ui/tui/progress.c index 6c2184d..0fcc5a1 100644 --- a/tools/perf/ui/tui/progress.c +++ b/tools/perf/ui/tui/progress.c @@ -2,6 +2,7 @@ #include "../progress.h" #include "../libslang.h" #include "../ui.h" +#include "tui.h" #include "../browser.h" static void tui_progress__update(u64 curr, u64 total, const char *title) @@ -31,12 +32,12 @@ static void tui_progress__update(u64 curr, u64 total, const char *title) pthread_mutex_unlock(&ui__lock); } -static struct ui_progress tui_progress_fns = +static struct ui_progress_ops tui_progress__ops = { .update = tui_progress__update, }; -void ui_progress__init(void) +void tui_progress__init(void) { - progress_fns = &tui_progress_fns; + ui_progress__ops = &tui_progress__ops; } diff --git a/tools/perf/ui/tui/setup.c b/tools/perf/ui/tui/setup.c index b940148..2f61256 100644 --- a/tools/perf/ui/tui/setup.c +++ b/tools/perf/ui/tui/setup.c @@ -9,6 +9,7 @@ #include "../util.h" #include "../libslang.h" #include "../keysyms.h" +#include "tui.h" static volatile int ui__need_resize; @@ -119,7 +120,7 @@ int ui__init(void) ui_helpline__init(); ui_browser__init(); - ui_progress__init(); + tui_progress__init(); signal(SIGSEGV, ui__signal); signal(SIGFPE, ui__signal); diff --git a/tools/perf/ui/tui/tui.h b/tools/perf/ui/tui/tui.h new file mode 100644 index 0000000..18961c7 --- /dev/null +++ b/tools/perf/ui/tui/tui.h @@ -0,0 +1,6 @@ +#ifndef _PERF_TUI_H_ +#define _PERF_TUI_H_ 1 + +void tui_progress__init(void); + +#endif /* _PERF_TUI_H_ */ -- cgit v0.10.2 From 4d3001fdfdfacd2b35ee74ff0f037274eeebd3f6 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 23 Oct 2013 15:40:38 -0300 Subject: perf ui progress: Per progress bar state That will ease using a progress bar across multiple functions, like in the upcoming patches that will present a progress bar when collapsing histograms. Based on a previous patch by Namhyung Kim. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-cr7lq7ud9fj21bg7wvq27w1u@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/ui/gtk/progress.c b/tools/perf/ui/gtk/progress.c index 195c7f9..b656655 100644 --- a/tools/perf/ui/gtk/progress.c +++ b/tools/perf/ui/gtk/progress.c @@ -7,14 +7,14 @@ static GtkWidget *dialog; static GtkWidget *progress; -static void gtk_ui_progress__update(u64 curr, u64 total, const char *title) +static void gtk_ui_progress__update(struct ui_progress *p) { - double fraction = total ? 1.0 * curr / total : 0.0; + double fraction = p->total ? 1.0 * p->curr / p->total : 0.0; char buf[1024]; if (dialog == NULL) { GtkWidget *vbox = gtk_vbox_new(TRUE, 5); - GtkWidget *label = gtk_label_new(title); + GtkWidget *label = gtk_label_new(p->title); dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL); progress = gtk_progress_bar_new(); @@ -32,7 +32,7 @@ static void gtk_ui_progress__update(u64 curr, u64 total, const char *title) } gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), fraction); - snprintf(buf, sizeof(buf), "%"PRIu64" / %"PRIu64, curr, total); + snprintf(buf, sizeof(buf), "%"PRIu64" / %"PRIu64, p->curr, p->total); gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress), buf); /* we didn't call gtk_main yet, so do it manually */ diff --git a/tools/perf/ui/progress.c b/tools/perf/ui/progress.c index d753821..a0f24c7 100644 --- a/tools/perf/ui/progress.c +++ b/tools/perf/ui/progress.c @@ -1,9 +1,7 @@ #include "../cache.h" #include "progress.h" -static void null_progress__update(u64 curr __maybe_unused, - u64 total __maybe_unused, - const char *title __maybe_unused) +static void null_progress__update(struct ui_progress *p __maybe_unused) { } @@ -14,9 +12,23 @@ static struct ui_progress_ops null_progress__ops = struct ui_progress_ops *ui_progress__ops = &null_progress__ops; -void ui_progress__update(u64 curr, u64 total, const char *title) +void ui_progress__update(struct ui_progress *p, u64 adv) { - return ui_progress__ops->update(curr, total, title); + p->curr += adv; + + if (p->curr >= p->next) { + p->next += p->step; + ui_progress__ops->update(p); + } +} + +void ui_progress__init(struct ui_progress *p, u64 total, const char *title) +{ + p->curr = 0; + p->next = p->step = total / 16; + p->total = total; + p->title = title; + } void ui_progress__finish(void) diff --git a/tools/perf/ui/progress.h b/tools/perf/ui/progress.h index d41bde5..29ec8ef 100644 --- a/tools/perf/ui/progress.h +++ b/tools/perf/ui/progress.h @@ -3,14 +3,21 @@ #include <../types.h> +void ui_progress__finish(void); + +struct ui_progress { + const char *title; + u64 curr, next, step, total; +}; + +void ui_progress__init(struct ui_progress *p, u64 total, const char *title); +void ui_progress__update(struct ui_progress *p, u64 adv); + struct ui_progress_ops { - void (*update)(u64, u64, const char *); + void (*update)(struct ui_progress *p); void (*finish)(void); }; extern struct ui_progress_ops *ui_progress__ops; -void ui_progress__update(u64 curr, u64 total, const char *title); -void ui_progress__finish(void); - #endif diff --git a/tools/perf/ui/tui/progress.c b/tools/perf/ui/tui/progress.c index 0fcc5a1..3e2d936 100644 --- a/tools/perf/ui/tui/progress.c +++ b/tools/perf/ui/tui/progress.c @@ -5,7 +5,7 @@ #include "tui.h" #include "../browser.h" -static void tui_progress__update(u64 curr, u64 total, const char *title) +static void tui_progress__update(struct ui_progress *p) { int bar, y; /* @@ -15,7 +15,7 @@ static void tui_progress__update(u64 curr, u64 total, const char *title) if (use_browser <= 0) return; - if (total == 0) + if (p->total == 0) return; ui__refresh_dimensions(true); @@ -24,9 +24,9 @@ static void tui_progress__update(u64 curr, u64 total, const char *title) SLsmg_set_color(0); SLsmg_draw_box(y, 0, 3, SLtt_Screen_Cols); SLsmg_gotorc(y++, 1); - SLsmg_write_string((char *)title); + SLsmg_write_string((char *)p->title); SLsmg_set_color(HE_COLORSET_SELECTED); - bar = ((SLtt_Screen_Cols - 2) * curr) / total; + bar = ((SLtt_Screen_Cols - 2) * p->curr) / p->total; SLsmg_fill_region(y, 1, 1, bar, ' '); SLsmg_refresh(); pthread_mutex_unlock(&ui__lock); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 854c5aa..4ba7b54 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -503,13 +503,16 @@ static int flush_sample_queue(struct perf_session *s, struct perf_sample sample; u64 limit = os->next_flush; u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; - unsigned idx = 0, progress_next = os->nr_samples / 16; bool show_progress = limit == ULLONG_MAX; + struct ui_progress prog; int ret; if (!tool->ordered_samples || !limit) return 0; + if (show_progress) + ui_progress__init(&prog, os->nr_samples, "Processing time ordered events..."); + list_for_each_entry_safe(iter, tmp, head, list) { if (session_done()) return 0; @@ -530,11 +533,9 @@ static int flush_sample_queue(struct perf_session *s, os->last_flush = iter->timestamp; list_del(&iter->list); list_add(&iter->list, &os->sample_cache); - if (show_progress && (++idx >= progress_next)) { - progress_next += os->nr_samples / 16; - ui_progress__update(idx, os->nr_samples, - "Processing time ordered events..."); - } + + if (show_progress) + ui_progress__update(&prog, 1); } if (list_empty(head)) { @@ -1285,12 +1286,13 @@ int __perf_session__process_events(struct perf_session *session, u64 file_size, struct perf_tool *tool) { int fd = perf_data_file__fd(session->file); - u64 head, page_offset, file_offset, file_pos, progress_next; + u64 head, page_offset, file_offset, file_pos; int err, mmap_prot, mmap_flags, map_idx = 0; size_t mmap_size; char *buf, *mmaps[NUM_MMAPS]; union perf_event *event; uint32_t size; + struct ui_progress prog; perf_tool__fill_defaults(tool); @@ -1301,7 +1303,7 @@ int __perf_session__process_events(struct perf_session *session, if (data_size && (data_offset + data_size < file_size)) file_size = data_offset + data_size; - progress_next = file_size / 16; + ui_progress__init(&prog, file_size, "Processing events..."); mmap_size = MMAP_SIZE; if (mmap_size > file_size) @@ -1356,11 +1358,7 @@ more: head += size; file_pos += size; - if (file_pos >= progress_next) { - progress_next += file_size / 16; - ui_progress__update(file_pos, file_size, - "Processing events..."); - } + ui_progress__update(&prog, size); if (session_done()) goto out; -- cgit v0.10.2 From c1fb5651bb40f9efaf32d280f39e06df7e352673 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 11 Oct 2013 14:15:38 +0900 Subject: perf tools: Show progress on histogram collapsing It can take quite amount of time so add progress bar UI to inform user. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Linus Torvalds Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1381468543-25334-4-git-send-email-namhyung@kernel.org [ perf_progress -> ui_progress ] Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 17c6b49..6c5ae57 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -247,7 +247,7 @@ static int __cmd_annotate(struct perf_annotate *ann) if (nr_samples > 0) { total_nr_samples += nr_samples; - hists__collapse_resort(hists); + hists__collapse_resort(hists, NULL); hists__output_resort(hists); if (symbol_conf.event_group && diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 9c82888..b605009 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -369,7 +369,7 @@ static void perf_evlist__collapse_resort(struct perf_evlist *evlist) list_for_each_entry(evsel, &evlist->entries, node) { struct hists *hists = &evsel->hists; - hists__collapse_resort(hists); + hists__collapse_resort(hists, NULL); } } diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index e3598a4..98d3891 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -496,6 +496,7 @@ static int __cmd_report(struct perf_report *rep) struct map *kernel_map; struct kmap *kernel_kmap; const char *help = "For a higher level overview, try: perf report --sort comm,dso"; + struct ui_progress prog; struct perf_data_file *file = session->file; signal(SIGINT, sig_handler); @@ -558,13 +559,19 @@ static int __cmd_report(struct perf_report *rep) } nr_samples = 0; + list_for_each_entry(pos, &session->evlist->entries, node) + nr_samples += pos->hists.nr_entries; + + ui_progress__init(&prog, nr_samples, "Merging related events..."); + + nr_samples = 0; list_for_each_entry(pos, &session->evlist->entries, node) { struct hists *hists = &pos->hists; if (pos->idx == 0) hists->symbol_filter_str = rep->symbol_filter_str; - hists__collapse_resort(hists); + hists__collapse_resort(hists, &prog); nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE]; /* Non-group events are considered as leader */ @@ -576,6 +583,7 @@ static int __cmd_report(struct perf_report *rep) hists__link(leader_hists, hists); } } + ui_progress__finish(); if (session_done()) return 0; diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 386d833..76c9264 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -286,7 +286,7 @@ static void perf_top__print_sym_table(struct perf_top *top) return; } - hists__collapse_resort(&top->sym_evsel->hists); + hists__collapse_resort(&top->sym_evsel->hists, NULL); hists__output_resort(&top->sym_evsel->hists); hists__decay_entries(&top->sym_evsel->hists, top->hide_user_symbols, @@ -552,7 +552,7 @@ static void perf_top__sort_new_samples(void *arg) if (t->evlist->selected != NULL) t->sym_evsel = t->evlist->selected; - hists__collapse_resort(&t->sym_evsel->hists); + hists__collapse_resort(&t->sym_evsel->hists, NULL); hists__output_resort(&t->sym_evsel->hists); hists__decay_entries(&t->sym_evsel->hists, t->hide_user_symbols, diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index 025503a..b51abcb 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c @@ -467,7 +467,7 @@ int test__hists_link(void) goto out; list_for_each_entry(evsel, &evlist->entries, node) { - hists__collapse_resort(&evsel->hists); + hists__collapse_resort(&evsel->hists, NULL); if (verbose > 2) print_hists(&evsel->hists); diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index f0b863e..7e80253 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -399,6 +399,7 @@ static struct hist_entry *add_hist_entry(struct hists *hists, if (!he) return NULL; + hists->nr_entries++; rb_link_node(&he->rb_node_in, parent, p); rb_insert_color(&he->rb_node_in, hists->entries_in); out: @@ -604,7 +605,7 @@ static void hists__apply_filters(struct hists *hists, struct hist_entry *he) hists__filter_entry_by_symbol(hists, he); } -void hists__collapse_resort(struct hists *hists) +void hists__collapse_resort(struct hists *hists, struct ui_progress *prog) { struct rb_root *root; struct rb_node *next; @@ -631,6 +632,8 @@ void hists__collapse_resort(struct hists *hists) */ hists__apply_filters(hists, n); } + if (prog) + ui_progress__update(prog, 1); } } diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 20b1758..0c7ce8b 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -5,6 +5,7 @@ #include #include "callchain.h" #include "header.h" +#include "ui/progress.h" extern struct callchain_param callchain_param; @@ -108,7 +109,7 @@ struct hist_entry *__hists__add_mem_entry(struct hists *self, u64 weight); void hists__output_resort(struct hists *self); -void hists__collapse_resort(struct hists *self); +void hists__collapse_resort(struct hists *self, struct ui_progress *prog); void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); void hists__output_recalc_col_len(struct hists *hists, int max_rows); -- cgit v0.10.2 From 055032142c42d2821c4aa617915292d6a08d56fc Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Wed, 23 Oct 2013 11:47:43 +0800 Subject: ALSA: Add SoC on-chip internal ram support for DMA buffer allocation Now it's quite common that an SoC contains its on-chip internal RAM. By using this RAM space for DMA buffer during audio playback/record, we can shutdown the voltage for external RAM to save power. So add new DEV type with iram malloc()/free() and accordingly modify current default mmap() for the iram circumstance. Signed-off-by: Nicolin Chen Reviewed-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/include/sound/memalloc.h b/include/sound/memalloc.h index cf15b82..510aec4 100644 --- a/include/sound/memalloc.h +++ b/include/sound/memalloc.h @@ -52,6 +52,7 @@ struct snd_dma_device { #else #define SNDRV_DMA_TYPE_DEV_SG SNDRV_DMA_TYPE_DEV /* no SG-buf support */ #endif +#define SNDRV_DMA_TYPE_DEV_IRAM 4 /* generic device iram-buffer */ /* * info for buffer allocation diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index bdf826f..18c1d47 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -157,6 +158,46 @@ static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr, dec_snd_pages(pg); dma_free_coherent(dev, PAGE_SIZE << pg, ptr, dma); } + +/** + * snd_malloc_dev_iram - allocate memory from on-chip internal ram + * @dmab: buffer allocation record to store the allocated data + * @size: number of bytes to allocate from the iram + * + * This function requires iram phandle provided via of_node + */ +void snd_malloc_dev_iram(struct snd_dma_buffer *dmab, size_t size) +{ + struct device *dev = dmab->dev.dev; + struct gen_pool *pool = NULL; + + if (dev->of_node) + pool = of_get_named_gen_pool(dev->of_node, "iram", 0); + + if (!pool) + return; + + /* Assign the pool into private_data field */ + dmab->private_data = pool; + + dmab->area = (void *)gen_pool_alloc(pool, size); + if (!dmab->area) + return; + + dmab->addr = gen_pool_virt_to_phys(pool, (unsigned long)dmab->area); +} + +/** + * snd_free_dev_iram - free allocated specific memory from on-chip internal ram + * @dmab: buffer allocation record to store the allocated data + */ +void snd_free_dev_iram(struct snd_dma_buffer *dmab) +{ + struct gen_pool *pool = dmab->private_data; + + if (pool && dmab->area) + gen_pool_free(pool, (unsigned long)dmab->area, dmab->bytes); +} #endif /* CONFIG_HAS_DMA */ /* @@ -197,6 +238,14 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, dmab->addr = 0; break; #ifdef CONFIG_HAS_DMA + case SNDRV_DMA_TYPE_DEV_IRAM: + snd_malloc_dev_iram(dmab, size); + if (dmab->area) + break; + /* Internal memory might have limited size and no enough space, + * so if we fail to malloc, try to fetch memory traditionally. + */ + dmab->dev.type = SNDRV_DMA_TYPE_DEV; case SNDRV_DMA_TYPE_DEV: dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr); break; @@ -269,6 +318,9 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab) snd_free_pages(dmab->area, dmab->bytes); break; #ifdef CONFIG_HAS_DMA + case SNDRV_DMA_TYPE_DEV_IRAM: + snd_free_dev_iram(dmab); + break; case SNDRV_DMA_TYPE_DEV: snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr); break; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index a68d4c6..513f095 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3199,6 +3199,12 @@ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *area) { area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; + if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_IRAM) { + area->vm_page_prot = pgprot_writecombine(area->vm_page_prot); + return remap_pfn_range(area, area->vm_start, + substream->dma_buffer.addr >> PAGE_SHIFT, + area->vm_end - area->vm_start, area->vm_page_prot); + } #ifdef ARCH_HAS_DMA_MMAP_COHERENT if (!substream->ops->page && substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) -- cgit v0.10.2 From ea73b7ddf13548afd666373dc5e26ee7c812a3fe Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 19 Oct 2013 17:43:51 +0100 Subject: ASoC: dmaengine: Support custom channel names Some devices have more than just simple TX and RX DMA channels, for example modern Samsung I2S IPs support a secondary transmit DMA stream which is mixed into the primary stream during playback. Allow such devices to specify the names of the channels to be requested in their dma_data. Signed-off-by: Mark Brown Acked-by: Lars-Peter Clausen diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index 83b2c3e..1501731 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -61,6 +61,7 @@ struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream) * @slave_id: Slave requester id for the DMA channel. * @filter_data: Custom DMA channel filter data, this will usually be used when * requesting the DMA channel. + * @chan_name: Custom channel name to use when requesting DMA channel. * @fifo_size: FIFO size of the DAI controller in bytes */ struct snd_dmaengine_dai_dma_data { @@ -69,6 +70,7 @@ struct snd_dmaengine_dai_dma_data { u32 maxburst; unsigned int slave_id; void *filter_data; + const char *chan_name; unsigned int fifo_size; }; @@ -98,6 +100,10 @@ void snd_dmaengine_pcm_set_config_from_dai_data( * playback. */ #define SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX BIT(3) +/* + * The PCM streams have custom channel names specified. + */ +#define SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME BIT(4) /** * struct snd_dmaengine_pcm_config - Configuration data for dmaengine based PCM diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index 99f9495..793cd6c 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -183,6 +183,8 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) { struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform); const struct snd_dmaengine_pcm_config *config = pcm->config; + struct device *dev = rtd->platform->dev; + struct snd_dmaengine_dai_dma_data *dma_data; struct snd_pcm_substream *substream; size_t prealloc_buffer_size; size_t max_buffer_size; @@ -203,6 +205,13 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) if (!substream) continue; + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + + if (!pcm->chan[i] && + (pcm->flags & SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME)) + pcm->chan[i] = dma_request_slave_channel(dev, + dma_data->chan_name); + if (!pcm->chan[i] && (pcm->flags & SND_DMAENGINE_PCM_FLAG_COMPAT)) { pcm->chan[i] = dmaengine_pcm_compat_request_channel(rtd, substream); @@ -275,7 +284,9 @@ static void dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, { unsigned int i; - if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_DT) || !dev->of_node) + if ((pcm->flags & (SND_DMAENGINE_PCM_FLAG_NO_DT | + SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME)) || + !dev->of_node) return; if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) { -- cgit v0.10.2 From d706b1e4930fec12cc3b8d6e0e28d9e8321abcea Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 16 Oct 2013 17:33:59 +0800 Subject: regulator: da9052: Revert se apply_[reg|bit] with regmap based voltage_sel operations This reverts commit 68f7506017ba67f1334cf086ffab76606f2c5ac4. Michael reported that with this patch we loose the fix_io code path from da9052_reg_update. Thus revert it. Reported-by: Michael Grzeschik Signed-off-by: Axel Lin Signed-off-by: Mark Brown diff --git a/drivers/regulator/da9052-regulator.c b/drivers/regulator/da9052-regulator.c index 1e4d483..e4bbe64 100644 --- a/drivers/regulator/da9052-regulator.c +++ b/drivers/regulator/da9052-regulator.c @@ -70,6 +70,7 @@ struct da9052_regulator_info { int step_uV; int min_uV; int max_uV; + unsigned char activate_bit; }; struct da9052_regulator { @@ -209,6 +210,36 @@ static int da9052_map_voltage(struct regulator_dev *rdev, return sel; } +static int da9052_regulator_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + struct da9052_regulator *regulator = rdev_get_drvdata(rdev); + struct da9052_regulator_info *info = regulator->info; + int id = rdev_get_id(rdev); + int ret; + + ret = da9052_reg_update(regulator->da9052, rdev->desc->vsel_reg, + rdev->desc->vsel_mask, selector); + if (ret < 0) + return ret; + + /* Some LDOs and DCDCs are DVC controlled which requires enabling of + * the activate bit to implment the changes on the output. + */ + switch (id) { + case DA9052_ID_BUCK1: + case DA9052_ID_BUCK2: + case DA9052_ID_BUCK3: + case DA9052_ID_LDO2: + case DA9052_ID_LDO3: + ret = da9052_reg_update(regulator->da9052, DA9052_SUPPLY_REG, + info->activate_bit, info->activate_bit); + break; + } + + return ret; +} + static struct regulator_ops da9052_dcdc_ops = { .get_current_limit = da9052_dcdc_get_current_limit, .set_current_limit = da9052_dcdc_set_current_limit, @@ -216,7 +247,7 @@ static struct regulator_ops da9052_dcdc_ops = { .list_voltage = da9052_list_voltage, .map_voltage = da9052_map_voltage, .get_voltage_sel = regulator_get_voltage_sel_regmap, - .set_voltage_sel = regulator_set_voltage_sel_regmap, + .set_voltage_sel = da9052_regulator_set_voltage_sel, .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -226,7 +257,7 @@ static struct regulator_ops da9052_ldo_ops = { .list_voltage = da9052_list_voltage, .map_voltage = da9052_map_voltage, .get_voltage_sel = regulator_get_voltage_sel_regmap, - .set_voltage_sel = regulator_set_voltage_sel_regmap, + .set_voltage_sel = da9052_regulator_set_voltage_sel, .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -243,14 +274,13 @@ static struct regulator_ops da9052_ldo_ops = { .owner = THIS_MODULE,\ .vsel_reg = DA9052_BUCKCORE_REG + DA9052_ID_##_id, \ .vsel_mask = (1 << (sbits)) - 1,\ - .apply_reg = DA9052_SUPPLY_REG, \ - .apply_bit = (abits), \ .enable_reg = DA9052_BUCKCORE_REG + DA9052_ID_##_id, \ .enable_mask = 1 << (ebits),\ },\ .min_uV = (min) * 1000,\ .max_uV = (max) * 1000,\ .step_uV = (step) * 1000,\ + .activate_bit = (abits),\ } #define DA9052_DCDC(_id, step, min, max, sbits, ebits, abits) \ @@ -264,14 +294,13 @@ static struct regulator_ops da9052_ldo_ops = { .owner = THIS_MODULE,\ .vsel_reg = DA9052_BUCKCORE_REG + DA9052_ID_##_id, \ .vsel_mask = (1 << (sbits)) - 1,\ - .apply_reg = DA9052_SUPPLY_REG, \ - .apply_bit = (abits), \ .enable_reg = DA9052_BUCKCORE_REG + DA9052_ID_##_id, \ .enable_mask = 1 << (ebits),\ },\ .min_uV = (min) * 1000,\ .max_uV = (max) * 1000,\ .step_uV = (step) * 1000,\ + .activate_bit = (abits),\ } static struct da9052_regulator_info da9052_regulator_info[] = { -- cgit v0.10.2 From b7baa9a0e324e32433d894374c3bddea29961aa0 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 23 Oct 2013 12:14:54 -0700 Subject: ALSA: hda - Remove OOM message after input_allocate_device Emitting an OOM message isn't necessary after input_allocate_device as there's a generic OOM and a dump_stack already done. Signed-off-by: Joe Perches Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index 63c9909..20d065a 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -151,10 +151,8 @@ static int snd_hda_do_attach(struct hda_beep *beep) int err; input_dev = input_allocate_device(); - if (!input_dev) { - printk(KERN_INFO "hda_beep: unable to allocate input device\n"); + if (!input_dev) return -ENOMEM; - } /* setup digital beep device */ input_dev->name = "HDA Digital PCBeep"; -- cgit v0.10.2 From 1d04c9de5c76df113e4af7120feb53c628b5efcc Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Thu, 24 Oct 2013 11:35:18 +0200 Subject: ALSA: hda - Add support of ALC255 codecs It's just another variant of ALC269 & co. Signed-off-by: Kailang Yang Cc: Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 52c26d3..1c4f8c5 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2540,6 +2540,7 @@ enum { ALC269_TYPE_ALC283, ALC269_TYPE_ALC284, ALC269_TYPE_ALC286, + ALC269_TYPE_ALC255, }; /* @@ -2565,6 +2566,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec) case ALC269_TYPE_ALC282: case ALC269_TYPE_ALC283: case ALC269_TYPE_ALC286: + case ALC269_TYPE_ALC255: ssids = alc269_ssids; break; default: @@ -4102,6 +4104,9 @@ static int patch_alc269(struct hda_codec *codec) case 0x10ec0286: spec->codec_variant = ALC269_TYPE_ALC286; break; + case 0x10ec0255: + spec->codec_variant = ALC269_TYPE_ALC255; + break; } if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) { @@ -4814,6 +4819,7 @@ static int patch_alc680(struct hda_codec *codec) static const struct hda_codec_preset snd_hda_preset_realtek[] = { { .id = 0x10ec0221, .name = "ALC221", .patch = patch_alc269 }, { .id = 0x10ec0233, .name = "ALC233", .patch = patch_alc269 }, + { .id = 0x10ec0255, .name = "ALC255", .patch = patch_alc269 }, { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 }, { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 }, { .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 }, -- cgit v0.10.2 From 161ebf295e2ad7876195143342362d2cf6ffaca2 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Thu, 24 Oct 2013 11:36:33 +0200 Subject: ALSA: hda - Add support of ALC285 / ALC293 codecs Yet another variants of ALC269. Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 1c4f8c5..3016467c 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2539,6 +2539,7 @@ enum { ALC269_TYPE_ALC282, ALC269_TYPE_ALC283, ALC269_TYPE_ALC284, + ALC269_TYPE_ALC285, ALC269_TYPE_ALC286, ALC269_TYPE_ALC255, }; @@ -2559,6 +2560,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec) case ALC269_TYPE_ALC269VC: case ALC269_TYPE_ALC280: case ALC269_TYPE_ALC284: + case ALC269_TYPE_ALC285: ssids = alc269va_ssids; break; case ALC269_TYPE_ALC269VB: @@ -4101,6 +4103,10 @@ static int patch_alc269(struct hda_codec *codec) case 0x10ec0292: spec->codec_variant = ALC269_TYPE_ALC284; break; + case 0x10ec0285: + case 0x10ec0293: + spec->codec_variant = ALC269_TYPE_ALC285; + break; case 0x10ec0286: spec->codec_variant = ALC269_TYPE_ALC286; break; @@ -4833,9 +4839,11 @@ static const struct hda_codec_preset snd_hda_preset_realtek[] = { { .id = 0x10ec0282, .name = "ALC282", .patch = patch_alc269 }, { .id = 0x10ec0283, .name = "ALC283", .patch = patch_alc269 }, { .id = 0x10ec0284, .name = "ALC284", .patch = patch_alc269 }, + { .id = 0x10ec0285, .name = "ALC285", .patch = patch_alc269 }, { .id = 0x10ec0286, .name = "ALC286", .patch = patch_alc269 }, { .id = 0x10ec0290, .name = "ALC290", .patch = patch_alc269 }, { .id = 0x10ec0292, .name = "ALC292", .patch = patch_alc269 }, + { .id = 0x10ec0293, .name = "ALC293", .patch = patch_alc269 }, { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660", .patch = patch_alc861 }, { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd }, -- cgit v0.10.2 From 95f74c41b2e53f541f2f66c8ba3dac1601ebd409 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 24 Oct 2013 01:24:15 +0200 Subject: ALSA: hda - Fix mute LED on HP laptops in runtime suspend When HP laptops with mute and mic-record LEDs go to runtime suspend, these LEDs are turned on forcibly no matter whether GPIO pis are on or off. This strange behavior seems triggered by resetting the HD-audio bus link at azx_rutime_suspend(). So, just add a new hda_bus flag to avoid the link reset at runtime suspend and set it for these HP machines. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 7aa9870..77db694 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -698,6 +698,7 @@ struct hda_bus { unsigned int in_reset:1; /* during reset operation */ unsigned int power_keep_link_on:1; /* don't power off HDA link */ unsigned int no_response_fallback:1; /* don't fallback at RIRB error */ + unsigned int avoid_link_reset:1; /* don't reset link at runtime PM */ int primary_dig_out_type; /* primary digital out PCM type */ }; diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 6e61a01..a0a06f7 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2986,7 +2986,8 @@ static int azx_runtime_suspend(struct device *dev) STATESTS_INT_MASK); azx_stop_chip(chip); - azx_enter_link_reset(chip); + if (!chip->bus->avoid_link_reset) + azx_enter_link_reset(chip); azx_clear_irq_pending(chip); if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) hda_display_power(false); diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index fba0cef..69a549a 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -2091,8 +2091,10 @@ static void stac92hd83xxx_fixup_hp_mic_led(struct hda_codec *codec, { struct sigmatel_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) + if (action == HDA_FIXUP_ACT_PRE_PROBE) { spec->mic_mute_led_gpio = 0x08; /* GPIO3 */ + codec->bus->avoid_link_reset = 1; + } } static void stac92hd83xxx_fixup_headset_jack(struct hda_codec *codec, -- cgit v0.10.2 From 90130d2e8f75c7181cef514e8a1491925f386a16 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 19 Oct 2013 21:38:26 +0100 Subject: ASoC: dmaengine: Use filter_data rather than dma_data for compat requests When using the legacy filter function channel requests we currently pass the audio specific struct snd_dmaengine_dai_dma_data which isn't likely to be helpful for actual filtering. Since there's already a field in the structure called filter_data clearly intended for use here convert the driver to use that. All existing users of plain filter functions have been converted to use an explicit compat function to override this behaviour except i.MX which is working around this issue in its filter function and is updated to just use filter_data directly here. Signed-off-by: Mark Brown Acked-by: Lars-Peter Clausen diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c index 4dc1296..aee2307 100644 --- a/sound/soc/fsl/imx-pcm-dma.c +++ b/sound/soc/fsl/imx-pcm-dma.c @@ -25,12 +25,10 @@ static bool filter(struct dma_chan *chan, void *param) { - struct snd_dmaengine_dai_dma_data *dma_data = param; - if (!imx_dma_is_general_purpose(chan)) return false; - chan->private = dma_data->filter_data; + chan->private = param; return true; } diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index 793cd6c..0c469cb 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -168,6 +168,9 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel( struct snd_pcm_substream *substream) { struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform); + struct snd_dmaengine_dai_dma_data *dma_data; + + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) && pcm->chan[0]) return pcm->chan[0]; @@ -176,7 +179,7 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel( return pcm->config->compat_request_channel(rtd, substream); return snd_dmaengine_pcm_request_channel(pcm->config->compat_filter_fn, - snd_soc_dai_get_dma_data(rtd->cpu_dai, substream)); + dma_data->filter_data); } static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) -- cgit v0.10.2 From 033054e8603036e43ddfad3155b3e5b78ed49e04 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Thu, 10 Oct 2013 10:41:35 +0900 Subject: regulator: s5m8767: Modify parse_dt function to parse data related to ramp This patch parse 'buck[2-4]_ramp_enable and buck_ramp_delay' platform data from dts file. Signed-off-by: Chanwoo Choi Signed-off-by: Kyungmin Park Signed-off-by: Mark Brown diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index c24448b..cb6cdb3 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -640,6 +640,22 @@ static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, return -EINVAL; } + if (of_get_property(pmic_np, "s5m8767,pmic-buck2-ramp-enable", NULL)) + pdata->buck2_ramp_enable = true; + + if (of_get_property(pmic_np, "s5m8767,pmic-buck3-ramp-enable", NULL)) + pdata->buck3_ramp_enable = true; + + if (of_get_property(pmic_np, "s5m8767,pmic-buck4-ramp-enable", NULL)) + pdata->buck4_ramp_enable = true; + + if (pdata->buck2_ramp_enable || pdata->buck3_ramp_enable + || pdata->buck4_ramp_enable) { + if (of_property_read_u32(pmic_np, "s5m8767,pmic-buck-ramp-delay", + &pdata->buck_ramp_delay)) + pdata->buck_ramp_delay = 0; + } + return 0; } #else -- cgit v0.10.2 From 04f9f068a619cc45a8e656e7121df2c772fa14ba Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Thu, 10 Oct 2013 10:41:36 +0900 Subject: regulator: s5m8767: Modify parsing method of the voltage table of buck2/3/4 The s5m8767 regulator driver parse always the voltage table of buck2/3/4. If gpio_dvs feature isn't used and dts haven't included the voltage table of buck2/3/4, s5m8767 regulator driver return error and file probe state. This patch check only voltage table of buck on s5m8767_pmic_dt_parse_pdata() if buck[2-4]_gpiodvs is true. Signed-off-by: Chanwoo Choi Signed-off-by: YoungJun Cho Signed-off-by: Kyungmin Park Signed-off-by: Mark Brown diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index cb6cdb3..29999c0 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -522,7 +522,7 @@ static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, struct device_node *pmic_np, *regulators_np, *reg_np; struct sec_regulator_data *rdata; struct sec_opmode_data *rmode; - unsigned int i, dvs_voltage_nr = 1, ret; + unsigned int i, dvs_voltage_nr = 8, ret; pmic_np = iodev->dev->of_node; if (!pmic_np) { @@ -586,15 +586,39 @@ static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, rmode++; } - if (of_get_property(pmic_np, "s5m8767,pmic-buck2-uses-gpio-dvs", NULL)) + if (of_get_property(pmic_np, "s5m8767,pmic-buck2-uses-gpio-dvs", NULL)) { pdata->buck2_gpiodvs = true; - if (of_get_property(pmic_np, "s5m8767,pmic-buck3-uses-gpio-dvs", NULL)) + if (of_property_read_u32_array(pmic_np, + "s5m8767,pmic-buck2-dvs-voltage", + pdata->buck2_voltage, dvs_voltage_nr)) { + dev_err(iodev->dev, "buck2 voltages not specified\n"); + return -EINVAL; + } + } + + if (of_get_property(pmic_np, "s5m8767,pmic-buck3-uses-gpio-dvs", NULL)) { pdata->buck3_gpiodvs = true; - if (of_get_property(pmic_np, "s5m8767,pmic-buck4-uses-gpio-dvs", NULL)) + if (of_property_read_u32_array(pmic_np, + "s5m8767,pmic-buck3-dvs-voltage", + pdata->buck3_voltage, dvs_voltage_nr)) { + dev_err(iodev->dev, "buck3 voltages not specified\n"); + return -EINVAL; + } + } + + if (of_get_property(pmic_np, "s5m8767,pmic-buck4-uses-gpio-dvs", NULL)) { pdata->buck4_gpiodvs = true; + if (of_property_read_u32_array(pmic_np, + "s5m8767,pmic-buck4-dvs-voltage", + pdata->buck4_voltage, dvs_voltage_nr)) { + dev_err(iodev->dev, "buck4 voltages not specified\n"); + return -EINVAL; + } + } + if (pdata->buck2_gpiodvs || pdata->buck3_gpiodvs || pdata->buck4_gpiodvs) { ret = s5m8767_pmic_dt_parse_dvs_gpio(iodev, pdata, pmic_np); @@ -612,34 +636,12 @@ static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, "invalid value for default dvs index, use 0\n"); } } - dvs_voltage_nr = 8; } ret = s5m8767_pmic_dt_parse_ds_gpio(iodev, pdata, pmic_np); if (ret) return -EINVAL; - if (of_property_read_u32_array(pmic_np, - "s5m8767,pmic-buck2-dvs-voltage", - pdata->buck2_voltage, dvs_voltage_nr)) { - dev_err(iodev->dev, "buck2 voltages not specified\n"); - return -EINVAL; - } - - if (of_property_read_u32_array(pmic_np, - "s5m8767,pmic-buck3-dvs-voltage", - pdata->buck3_voltage, dvs_voltage_nr)) { - dev_err(iodev->dev, "buck3 voltages not specified\n"); - return -EINVAL; - } - - if (of_property_read_u32_array(pmic_np, - "s5m8767,pmic-buck4-dvs-voltage", - pdata->buck4_voltage, dvs_voltage_nr)) { - dev_err(iodev->dev, "buck4 voltages not specified\n"); - return -EINVAL; - } - if (of_get_property(pmic_np, "s5m8767,pmic-buck2-ramp-enable", NULL)) pdata->buck2_ramp_enable = true; -- cgit v0.10.2 From 1abe729f783fece81d93e9a0253fd8079f19d7f6 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Thu, 24 Oct 2013 18:15:29 +0800 Subject: ASoC: fsl: Add missing pm to current machine drivers Add missing pm to current machine drivers so that all of them would correctly do suspend/resume. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/imx-mc13783.c b/sound/soc/fsl/imx-mc13783.c index a2fd732..79cee78 100644 --- a/sound/soc/fsl/imx-mc13783.c +++ b/sound/soc/fsl/imx-mc13783.c @@ -160,6 +160,7 @@ static struct platform_driver imx_mc13783_audio_driver = { .driver = { .name = "imx_mc13783", .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, }, .probe = imx_mc13783_probe, .remove = imx_mc13783_remove diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c index ca1be1d..73da709 100644 --- a/sound/soc/fsl/imx-sgtl5000.c +++ b/sound/soc/fsl/imx-sgtl5000.c @@ -202,6 +202,7 @@ static struct platform_driver imx_sgtl5000_driver = { .driver = { .name = "imx-sgtl5000", .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, .of_match_table = imx_sgtl5000_dt_ids, }, .probe = imx_sgtl5000_probe, diff --git a/sound/soc/fsl/imx-wm8962.c b/sound/soc/fsl/imx-wm8962.c index 722afe6..8e5b2c6 100644 --- a/sound/soc/fsl/imx-wm8962.c +++ b/sound/soc/fsl/imx-wm8962.c @@ -311,6 +311,7 @@ static struct platform_driver imx_wm8962_driver = { .driver = { .name = "imx-wm8962", .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, .of_match_table = imx_wm8962_dt_ids, }, .probe = imx_wm8962_probe, -- cgit v0.10.2 From 0c02c8007ea5554d028f99fd3e29fc201fdeeab3 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 19 Sep 2013 11:22:36 -0500 Subject: of/irq: Rename of_irq_map_* functions to of_irq_parse_* The OF irq handling code has been overloading the term 'map' to refer to both parsing the data in the device tree and mapping it to the internal linux irq system. This is probably because the device tree does have the concept of an 'interrupt-map' function for translating interrupt references from one node to another, but 'map' is still confusing when the primary purpose of some of the functions are to parse the DT data. This patch renames all the of_irq_map_* functions to of_irq_parse_* which makes it clear that there is a difference between the parsing phase and the mapping phase. Kernel code can make use of just the parsing or just the mapping support as needed by the subsystem. The patch was generated mechanically with a handful of sed commands. Signed-off-by: Grant Likely Acked-by: Michal Simek Acked-by: Tony Lindgren Cc: Ralf Baechle Cc: Benjamin Herrenschmidt diff --git a/arch/arm/mach-integrator/pci_v3.c b/arch/arm/mach-integrator/pci_v3.c index bef1005..0f496cc 100644 --- a/arch/arm/mach-integrator/pci_v3.c +++ b/arch/arm/mach-integrator/pci_v3.c @@ -840,9 +840,9 @@ static int __init pci_v3_map_irq_dt(const struct pci_dev *dev, u8 slot, u8 pin) struct of_irq oirq; int ret; - ret = of_irq_map_pci(dev, &oirq); + ret = of_irq_parse_pci(dev, &oirq); if (ret) { - dev_err(&dev->dev, "of_irq_map_pci() %d\n", ret); + dev_err(&dev->dev, "of_irq_parse_pci() %d\n", ret); /* Proper return code 0 == NO_IRQ */ return 0; } diff --git a/arch/microblaze/pci/pci-common.c b/arch/microblaze/pci/pci-common.c index 1b93bf0..4d5b1a9 100644 --- a/arch/microblaze/pci/pci-common.c +++ b/arch/microblaze/pci/pci-common.c @@ -217,7 +217,7 @@ int pci_read_irq_line(struct pci_dev *pci_dev) memset(&oirq, 0xff, sizeof(oirq)); #endif /* Try to get a mapping from the device-tree */ - if (of_irq_map_pci(pci_dev, &oirq)) { + if (of_irq_parse_pci(pci_dev, &oirq)) { u8 line, pin; /* If that fails, lets fallback to what is in the config diff --git a/arch/mips/pci/fixup-lantiq.c b/arch/mips/pci/fixup-lantiq.c index 6c829df..2e8dbfe 100644 --- a/arch/mips/pci/fixup-lantiq.c +++ b/arch/mips/pci/fixup-lantiq.c @@ -28,7 +28,7 @@ int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) struct of_irq dev_irq; int irq; - if (of_irq_map_pci(dev, &dev_irq)) { + if (of_irq_parse_pci(dev, &dev_irq)) { dev_err(&dev->dev, "trying to map irq for unknown slot:%d pin:%d\n", slot, pin); return 0; diff --git a/arch/mips/pci/pci-rt3883.c b/arch/mips/pci/pci-rt3883.c index 95c9d41..cae92a0 100644 --- a/arch/mips/pci/pci-rt3883.c +++ b/arch/mips/pci/pci-rt3883.c @@ -587,7 +587,7 @@ int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) int err; int irq; - err = of_irq_map_pci(dev, &dev_irq); + err = of_irq_parse_pci(dev, &dev_irq); if (err) { pr_err("pci %s: unable to get irq map, err=%d\n", pci_name((struct pci_dev *) dev), err); diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 905a24b..2f41854 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -237,7 +237,7 @@ static int pci_read_irq_line(struct pci_dev *pci_dev) memset(&oirq, 0xff, sizeof(oirq)); #endif /* Try to get a mapping from the device-tree */ - if (of_irq_map_pci(pci_dev, &oirq)) { + if (of_irq_parse_pci(pci_dev, &oirq)) { u8 line, pin; /* If that fails, lets fallback to what is in the config diff --git a/arch/powerpc/platforms/cell/celleb_scc_pciex.c b/arch/powerpc/platforms/cell/celleb_scc_pciex.c index 14be2bd..40bc371 100644 --- a/arch/powerpc/platforms/cell/celleb_scc_pciex.c +++ b/arch/powerpc/platforms/cell/celleb_scc_pciex.c @@ -507,7 +507,7 @@ static __init int celleb_setup_pciex(struct device_node *node, phb->ops = &scc_pciex_pci_ops; /* internal interrupt handler */ - if (of_irq_map_one(node, 1, &oirq)) { + if (of_irq_parse_one(node, 1, &oirq)) { pr_err("PCIEXC:Failed to map irq\n"); goto error; } diff --git a/arch/powerpc/platforms/cell/celleb_scc_sio.c b/arch/powerpc/platforms/cell/celleb_scc_sio.c index 9c339ec..96388b2 100644 --- a/arch/powerpc/platforms/cell/celleb_scc_sio.c +++ b/arch/powerpc/platforms/cell/celleb_scc_sio.c @@ -53,7 +53,7 @@ static int __init txx9_serial_init(void) if (!(txx9_serial_bitmap & (1<host->of_node, 0, &oirq) == 0) { + if (of_irq_parse_one(pic->host->of_node, 0, &oirq) == 0) { virq = irq_create_of_mapping(oirq.controller, oirq.specifier, oirq.size); return virq; diff --git a/arch/powerpc/platforms/cell/spu_manage.c b/arch/powerpc/platforms/cell/spu_manage.c index 2bb6977..e6cdb81 100644 --- a/arch/powerpc/platforms/cell/spu_manage.c +++ b/arch/powerpc/platforms/cell/spu_manage.c @@ -182,7 +182,7 @@ static int __init spu_map_interrupts(struct spu *spu, struct device_node *np) int i; for (i=0; i < 3; i++) { - ret = of_irq_map_one(np, i, &oirq); + ret = of_irq_parse_one(np, i, &oirq); if (ret) { pr_debug("spu_new: failed to get irq %d\n", i); goto err; diff --git a/arch/powerpc/platforms/fsl_uli1575.c b/arch/powerpc/platforms/fsl_uli1575.c index 92ac9b5..ac539c1 100644 --- a/arch/powerpc/platforms/fsl_uli1575.c +++ b/arch/powerpc/platforms/fsl_uli1575.c @@ -333,7 +333,7 @@ static void hpcd_final_uli5288(struct pci_dev *dev) laddr[0] = (hose->first_busno << 16) | (PCI_DEVFN(31, 0) << 8); laddr[1] = laddr[2] = 0; - of_irq_map_raw(hosenode, &pin, 1, laddr, &oirq); + of_irq_parse_raw(hosenode, &pin, 1, laddr, &oirq); virq = irq_create_of_mapping(oirq.controller, oirq.specifier, oirq.size); dev->irq = virq; diff --git a/arch/powerpc/platforms/powermac/pic.c b/arch/powerpc/platforms/powermac/pic.c index 31036b5..720663e 100644 --- a/arch/powerpc/platforms/powermac/pic.c +++ b/arch/powerpc/platforms/powermac/pic.c @@ -393,7 +393,7 @@ static void __init pmac_pic_probe_oldstyle(void) #endif } -int of_irq_map_oldworld(struct device_node *device, int index, +int of_irq_parse_oldworld(struct device_node *device, int index, struct of_irq *out_irq) { const u32 *ints = NULL; diff --git a/arch/powerpc/platforms/pseries/event_sources.c b/arch/powerpc/platforms/pseries/event_sources.c index 2605c31..850c18c 100644 --- a/arch/powerpc/platforms/pseries/event_sources.c +++ b/arch/powerpc/platforms/pseries/event_sources.c @@ -55,7 +55,7 @@ void request_event_sources_irqs(struct device_node *np, /* Else use normal interrupt tree parsing */ else { /* First try to do a proper OF tree parsing */ - for (index = 0; of_irq_map_one(np, index, &oirq) == 0; + for (index = 0; of_irq_parse_one(np, index, &oirq) == 0; index++) { if (count > 15) break; diff --git a/arch/powerpc/sysdev/mpic_msi.c b/arch/powerpc/sysdev/mpic_msi.c index bbf342c..463e3a7 100644 --- a/arch/powerpc/sysdev/mpic_msi.c +++ b/arch/powerpc/sysdev/mpic_msi.c @@ -63,7 +63,7 @@ static int mpic_msi_reserve_u3_hwirqs(struct mpic *mpic) pr_debug("mpic: mapping hwirqs for %s\n", np->full_name); index = 0; - while (of_irq_map_one(np, index++, &oirq) == 0) { + while (of_irq_parse_one(np, index++, &oirq) == 0) { ops->xlate(mpic->irqhost, NULL, oirq.specifier, oirq.size, &hwirq, &flags); msi_bitmap_reserve_hwirq(&mpic->msi_bitmap, hwirq); diff --git a/arch/x86/kernel/devicetree.c b/arch/x86/kernel/devicetree.c index 376dc78..3ac6398 100644 --- a/arch/x86/kernel/devicetree.c +++ b/arch/x86/kernel/devicetree.c @@ -116,7 +116,7 @@ static int x86_of_pci_irq_enable(struct pci_dev *dev) if (!pin) return 0; - ret = of_irq_map_pci(dev, &oirq); + ret = of_irq_parse_pci(dev, &oirq); if (ret) return ret; diff --git a/drivers/of/address.c b/drivers/of/address.c index 71180b9..994f293 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -532,12 +532,12 @@ static u64 __of_translate_address(struct device_node *dev, pbus->count_cells(dev, &pna, &pns); if (!OF_CHECK_COUNTS(pna, pns)) { printk(KERN_ERR "prom_parse: Bad cell count for %s\n", - dev->full_name); + of_node_full_name(dev)); break; } pr_debug("OF: parent bus is %s (na=%d, ns=%d) on %s\n", - pbus->name, pna, pns, parent->full_name); + pbus->name, pna, pns, of_node_full_name(parent)); /* Apply bus translation */ if (of_translate_one(dev, bus, pbus, addr, na, ns, pna, rprop)) diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 90068f9..410aa24 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -31,14 +31,14 @@ * @dev: Device node of the device whose interrupt is to be mapped * @index: Index of the interrupt to map * - * This function is a wrapper that chains of_irq_map_one() and + * This function is a wrapper that chains of_irq_parse_one() and * irq_create_of_mapping() to make things easier to callers */ unsigned int irq_of_parse_and_map(struct device_node *dev, int index) { struct of_irq oirq; - if (of_irq_map_one(dev, index, &oirq)) + if (of_irq_parse_one(dev, index, &oirq)) return 0; return irq_create_of_mapping(oirq.controller, oirq.specifier, @@ -79,7 +79,7 @@ struct device_node *of_irq_find_parent(struct device_node *child) } /** - * of_irq_map_raw - Low level interrupt tree parsing + * of_irq_parse_raw - Low level interrupt tree parsing * @parent: the device interrupt parent * @intspec: interrupt specifier ("interrupts" property of the device) * @ointsize: size of the passed in interrupt specifier @@ -93,7 +93,7 @@ struct device_node *of_irq_find_parent(struct device_node *child) * properties, for example when resolving PCI interrupts when no device * node exist for the parent. */ -int of_irq_map_raw(struct device_node *parent, const __be32 *intspec, +int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec, u32 ointsize, const __be32 *addr, struct of_irq *out_irq) { struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL; @@ -101,7 +101,7 @@ int of_irq_map_raw(struct device_node *parent, const __be32 *intspec, u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0; int imaplen, match, i; - pr_debug("of_irq_map_raw: par=%s,intspec=[0x%08x 0x%08x...],ointsize=%d\n", + pr_debug("of_irq_parse_raw: par=%s,intspec=[0x%08x 0x%08x...],ointsize=%d\n", of_node_full_name(parent), be32_to_cpup(intspec), be32_to_cpup(intspec + 1), ointsize); @@ -126,7 +126,7 @@ int of_irq_map_raw(struct device_node *parent, const __be32 *intspec, goto fail; } - pr_debug("of_irq_map_raw: ipar=%s, size=%d\n", of_node_full_name(ipar), intsize); + pr_debug("of_irq_parse_raw: ipar=%s, size=%d\n", of_node_full_name(ipar), intsize); if (ointsize != intsize) return -EINVAL; @@ -269,29 +269,29 @@ int of_irq_map_raw(struct device_node *parent, const __be32 *intspec, return -EINVAL; } -EXPORT_SYMBOL_GPL(of_irq_map_raw); +EXPORT_SYMBOL_GPL(of_irq_parse_raw); /** - * of_irq_map_one - Resolve an interrupt for a device + * of_irq_parse_one - Resolve an interrupt for a device * @device: the device whose interrupt is to be resolved * @index: index of the interrupt to resolve * @out_irq: structure of_irq filled by this function * * This function resolves an interrupt, walking the tree, for a given - * device-tree node. It's the high level pendant to of_irq_map_raw(). + * device-tree node. It's the high level pendant to of_irq_parse_raw(). */ -int of_irq_map_one(struct device_node *device, int index, struct of_irq *out_irq) +int of_irq_parse_one(struct device_node *device, int index, struct of_irq *out_irq) { struct device_node *p; const __be32 *intspec, *tmp, *addr; u32 intsize, intlen; int res = -EINVAL; - pr_debug("of_irq_map_one: dev=%s, index=%d\n", of_node_full_name(device), index); + pr_debug("of_irq_parse_one: dev=%s, index=%d\n", of_node_full_name(device), index); /* OldWorld mac stuff is "special", handle out of line */ if (of_irq_workarounds & OF_IMAP_OLDWORLD_MAC) - return of_irq_map_oldworld(device, index, out_irq); + return of_irq_parse_oldworld(device, index, out_irq); /* Get the interrupts property */ intspec = of_get_property(device, "interrupts", &intlen); @@ -322,13 +322,13 @@ int of_irq_map_one(struct device_node *device, int index, struct of_irq *out_irq goto out; /* Get new specifier and map it */ - res = of_irq_map_raw(p, intspec + index * intsize, intsize, + res = of_irq_parse_raw(p, intspec + index * intsize, intsize, addr, out_irq); out: of_node_put(p); return res; } -EXPORT_SYMBOL_GPL(of_irq_map_one); +EXPORT_SYMBOL_GPL(of_irq_parse_one); /** * of_irq_to_resource - Decode a node's IRQ and return it as a resource diff --git a/drivers/of/of_pci_irq.c b/drivers/of/of_pci_irq.c index 6770538..dceec10 100644 --- a/drivers/of/of_pci_irq.c +++ b/drivers/of/of_pci_irq.c @@ -5,7 +5,7 @@ #include /** - * of_irq_map_pci - Resolve the interrupt for a PCI device + * of_irq_parse_pci - Resolve the interrupt for a PCI device * @pdev: the device whose interrupt is to be resolved * @out_irq: structure of_irq filled by this function * @@ -15,7 +15,7 @@ * PCI tree until an device-node is found, at which point it will finish * resolving using the OF tree walking. */ -int of_irq_map_pci(const struct pci_dev *pdev, struct of_irq *out_irq) +int of_irq_parse_pci(const struct pci_dev *pdev, struct of_irq *out_irq) { struct device_node *dn, *ppnode; struct pci_dev *ppdev; @@ -30,7 +30,7 @@ int of_irq_map_pci(const struct pci_dev *pdev, struct of_irq *out_irq) */ dn = pci_device_to_OF_node(pdev); if (dn) { - rc = of_irq_map_one(dn, 0, out_irq); + rc = of_irq_parse_one(dn, 0, out_irq); if (!rc) return rc; } @@ -88,6 +88,6 @@ int of_irq_map_pci(const struct pci_dev *pdev, struct of_irq *out_irq) lspec_be = cpu_to_be32(lspec); laddr[0] = cpu_to_be32((pdev->bus->number << 16) | (pdev->devfn << 8)); laddr[1] = laddr[2] = cpu_to_be32(0); - return of_irq_map_raw(ppnode, &lspec_be, 1, laddr, out_irq); + return of_irq_parse_raw(ppnode, &lspec_be, 1, laddr, out_irq); } -EXPORT_SYMBOL_GPL(of_irq_map_pci); +EXPORT_SYMBOL_GPL(of_irq_parse_pci); diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index 729d5a1..05f8180 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -650,7 +650,7 @@ static int __init mvebu_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) struct of_irq oirq; int ret; - ret = of_irq_map_pci(dev, &oirq); + ret = of_irq_parse_pci(dev, &oirq); if (ret) return ret; diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h index fcd63ba..a00bc71 100644 --- a/include/linux/of_irq.h +++ b/include/linux/of_irq.h @@ -35,12 +35,12 @@ typedef int (*of_irq_init_cb_t)(struct device_node *, struct device_node *); #if defined(CONFIG_PPC32) && defined(CONFIG_PPC_PMAC) extern unsigned int of_irq_workarounds; extern struct device_node *of_irq_dflt_pic; -extern int of_irq_map_oldworld(struct device_node *device, int index, +extern int of_irq_parse_oldworld(struct device_node *device, int index, struct of_irq *out_irq); #else /* CONFIG_PPC32 && CONFIG_PPC_PMAC */ #define of_irq_workarounds (0) #define of_irq_dflt_pic (NULL) -static inline int of_irq_map_oldworld(struct device_node *device, int index, +static inline int of_irq_parse_oldworld(struct device_node *device, int index, struct of_irq *out_irq) { return -EINVAL; @@ -48,10 +48,10 @@ static inline int of_irq_map_oldworld(struct device_node *device, int index, #endif /* CONFIG_PPC32 && CONFIG_PPC_PMAC */ -extern int of_irq_map_raw(struct device_node *parent, const __be32 *intspec, +extern int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec, u32 ointsize, const __be32 *addr, struct of_irq *out_irq); -extern int of_irq_map_one(struct device_node *device, int index, +extern int of_irq_parse_one(struct device_node *device, int index, struct of_irq *out_irq); extern unsigned int irq_create_of_mapping(struct device_node *controller, const u32 *intspec, diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h index fd9c408..839ba20 100644 --- a/include/linux/of_pci.h +++ b/include/linux/of_pci.h @@ -6,7 +6,7 @@ struct pci_dev; struct of_irq; -int of_irq_map_pci(const struct pci_dev *pdev, struct of_irq *out_irq); +int of_irq_parse_pci(const struct pci_dev *pdev, struct of_irq *out_irq); struct device_node; struct device_node *of_pci_find_child_device(struct device_node *parent, -- cgit v0.10.2 From 530210c7814e83564c7ca7bca8192515042c0b63 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Sun, 15 Sep 2013 16:39:11 +0100 Subject: of/irq: Replace of_irq with of_phandle_args struct of_irq and struct of_phandle_args are exactly the same structure. This patch makes the kernel use of_phandle_args everywhere. This in itself isn't a big deal, but it makes some follow-on patches simpler. Signed-off-by: Grant Likely Acked-by: Michal Simek Acked-by: Tony Lindgren Cc: Russell King Cc: Ralf Baechle Cc: Benjamin Herrenschmidt diff --git a/arch/arm/mach-integrator/pci_v3.c b/arch/arm/mach-integrator/pci_v3.c index 0f496cc..2d6b4da 100644 --- a/arch/arm/mach-integrator/pci_v3.c +++ b/arch/arm/mach-integrator/pci_v3.c @@ -837,7 +837,7 @@ static struct hw_pci pci_v3 __initdata = { static int __init pci_v3_map_irq_dt(const struct pci_dev *dev, u8 slot, u8 pin) { - struct of_irq oirq; + struct of_phandle_args oirq; int ret; ret = of_irq_parse_pci(dev, &oirq); @@ -847,8 +847,7 @@ static int __init pci_v3_map_irq_dt(const struct pci_dev *dev, u8 slot, u8 pin) return 0; } - return irq_create_of_mapping(oirq.controller, oirq.specifier, - oirq.size); + return irq_create_of_mapping(oirq.np, oirq.args, oirq.args_count); } static int __init pci_v3_dtprobe(struct platform_device *pdev, diff --git a/arch/microblaze/pci/pci-common.c b/arch/microblaze/pci/pci-common.c index 4d5b1a9..c9302c4 100644 --- a/arch/microblaze/pci/pci-common.c +++ b/arch/microblaze/pci/pci-common.c @@ -199,7 +199,7 @@ void pcibios_set_master(struct pci_dev *dev) */ int pci_read_irq_line(struct pci_dev *pci_dev) { - struct of_irq oirq; + struct of_phandle_args oirq; unsigned int virq; /* The current device-tree that iSeries generates from the HV @@ -243,11 +243,10 @@ int pci_read_irq_line(struct pci_dev *pci_dev) irq_set_irq_type(virq, IRQ_TYPE_LEVEL_LOW); } else { pr_debug(" Got one, spec %d cells (0x%08x 0x%08x...) on %s\n", - oirq.size, oirq.specifier[0], oirq.specifier[1], - of_node_full_name(oirq.controller)); + oirq.args_count, oirq.args[0], oirq.args[1], + of_node_full_name(oirq.np)); - virq = irq_create_of_mapping(oirq.controller, oirq.specifier, - oirq.size); + virq = irq_create_of_mapping(oirq.np, oirq.args, oirq.args_count); } if (!virq) { pr_debug(" Failed to map !\n"); diff --git a/arch/mips/pci/fixup-lantiq.c b/arch/mips/pci/fixup-lantiq.c index 2e8dbfe..81ff0b5 100644 --- a/arch/mips/pci/fixup-lantiq.c +++ b/arch/mips/pci/fixup-lantiq.c @@ -25,7 +25,7 @@ int pcibios_plat_dev_init(struct pci_dev *dev) int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { - struct of_irq dev_irq; + struct of_phandle_args dev_irq; int irq; if (of_irq_parse_pci(dev, &dev_irq)) { @@ -33,8 +33,7 @@ int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) slot, pin); return 0; } - irq = irq_create_of_mapping(dev_irq.controller, dev_irq.specifier, - dev_irq.size); + irq = irq_create_of_mapping(dev_irq.np, dev_irq.args, dev_irq.args_count); dev_info(&dev->dev, "SLOT:%d PIN:%d IRQ:%d\n", slot, pin, irq); return irq; } diff --git a/arch/mips/pci/pci-rt3883.c b/arch/mips/pci/pci-rt3883.c index cae92a0..0f08a5b 100644 --- a/arch/mips/pci/pci-rt3883.c +++ b/arch/mips/pci/pci-rt3883.c @@ -583,7 +583,7 @@ err_put_intc_node: int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { - struct of_irq dev_irq; + struct of_phandle_args dev_irq; int err; int irq; @@ -594,9 +594,7 @@ int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) return 0; } - irq = irq_create_of_mapping(dev_irq.controller, - dev_irq.specifier, - dev_irq.size); + irq = irq_create_of_mapping(dev_irq.np, dev_irq.args, dev_irq.args_count); if (irq == 0) pr_crit("pci %s: no irq found for pin %u\n", diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 2f41854..96c4623 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -228,7 +228,7 @@ int pcibios_add_platform_entries(struct pci_dev *pdev) */ static int pci_read_irq_line(struct pci_dev *pci_dev) { - struct of_irq oirq; + struct of_phandle_args oirq; unsigned int virq; pr_debug("PCI: Try to map irq for %s...\n", pci_name(pci_dev)); @@ -263,11 +263,10 @@ static int pci_read_irq_line(struct pci_dev *pci_dev) irq_set_irq_type(virq, IRQ_TYPE_LEVEL_LOW); } else { pr_debug(" Got one, spec %d cells (0x%08x 0x%08x...) on %s\n", - oirq.size, oirq.specifier[0], oirq.specifier[1], - of_node_full_name(oirq.controller)); + oirq.args_count, oirq.args[0], oirq.args[1], + of_node_full_name(oirq.np)); - virq = irq_create_of_mapping(oirq.controller, oirq.specifier, - oirq.size); + virq = irq_create_of_mapping(oirq.np, oirq.args, oirq.args_count); } if(virq == NO_IRQ) { pr_debug(" Failed to map !\n"); diff --git a/arch/powerpc/platforms/cell/celleb_scc_pciex.c b/arch/powerpc/platforms/cell/celleb_scc_pciex.c index 40bc371..e8d34d1 100644 --- a/arch/powerpc/platforms/cell/celleb_scc_pciex.c +++ b/arch/powerpc/platforms/cell/celleb_scc_pciex.c @@ -486,7 +486,7 @@ static __init int celleb_setup_pciex(struct device_node *node, struct pci_controller *phb) { struct resource r; - struct of_irq oirq; + struct of_phandle_args oirq; int virq; /* SMMIO registers; used inside this file */ @@ -511,8 +511,7 @@ static __init int celleb_setup_pciex(struct device_node *node, pr_err("PCIEXC:Failed to map irq\n"); goto error; } - virq = irq_create_of_mapping(oirq.controller, oirq.specifier, - oirq.size); + virq = irq_create_of_mapping(oirq.np, oirq.args, oirq.args_count); if (request_irq(virq, pciex_handle_internal_irq, 0, "pciex", (void *)phb)) { pr_err("PCIEXC:Failed to request irq\n"); diff --git a/arch/powerpc/platforms/cell/celleb_scc_sio.c b/arch/powerpc/platforms/cell/celleb_scc_sio.c index 96388b2..06046d5 100644 --- a/arch/powerpc/platforms/cell/celleb_scc_sio.c +++ b/arch/powerpc/platforms/cell/celleb_scc_sio.c @@ -45,7 +45,7 @@ static int __init txx9_serial_init(void) struct device_node *node; int i; struct uart_port req; - struct of_irq irq; + struct of_phandle_args irq; struct resource res; for_each_compatible_node(node, "serial", "toshiba,sio-scc") { @@ -66,8 +66,7 @@ static int __init txx9_serial_init(void) #ifdef CONFIG_SERIAL_TXX9_CONSOLE req.membase = ioremap(req.mapbase, 0x24); #endif - req.irq = irq_create_of_mapping(irq.controller, - irq.specifier, irq.size); + req.irq = irq_create_of_mapping(irq.np, irq.args, irq.args_count); req.flags |= UPF_IOREMAP | UPF_BUGGY_UART /*HAVE_CTS_LINE*/; req.uartclk = 83300000; diff --git a/arch/powerpc/platforms/cell/spider-pic.c b/arch/powerpc/platforms/cell/spider-pic.c index b491f40..6e842fd 100644 --- a/arch/powerpc/platforms/cell/spider-pic.c +++ b/arch/powerpc/platforms/cell/spider-pic.c @@ -235,10 +235,10 @@ static unsigned int __init spider_find_cascade_and_node(struct spider_pic *pic) /* First, we check whether we have a real "interrupts" in the device * tree in case the device-tree is ever fixed */ - struct of_irq oirq; + struct of_phandle_args oirq; if (of_irq_parse_one(pic->host->of_node, 0, &oirq) == 0) { - virq = irq_create_of_mapping(oirq.controller, oirq.specifier, - oirq.size); + virq = irq_create_of_mapping(oirq.np, oirq.args, + oirq.args_count); return virq; } diff --git a/arch/powerpc/platforms/cell/spu_manage.c b/arch/powerpc/platforms/cell/spu_manage.c index e6cdb81..e9eb4f8 100644 --- a/arch/powerpc/platforms/cell/spu_manage.c +++ b/arch/powerpc/platforms/cell/spu_manage.c @@ -177,7 +177,7 @@ out: static int __init spu_map_interrupts(struct spu *spu, struct device_node *np) { - struct of_irq oirq; + struct of_phandle_args oirq; int ret; int i; @@ -188,10 +188,10 @@ static int __init spu_map_interrupts(struct spu *spu, struct device_node *np) goto err; } ret = -EINVAL; - pr_debug(" irq %d no 0x%x on %s\n", i, oirq.specifier[0], - oirq.controller->full_name); - spu->irqs[i] = irq_create_of_mapping(oirq.controller, - oirq.specifier, oirq.size); + pr_debug(" irq %d no 0x%x on %s\n", i, oirq.args[0], + oirq.np->full_name); + spu->irqs[i] = irq_create_of_mapping(oirq.np, + oirq.args, oirq.args_count); if (spu->irqs[i] == NO_IRQ) { pr_debug("spu_new: failed to map it !\n"); goto err; @@ -200,7 +200,7 @@ static int __init spu_map_interrupts(struct spu *spu, struct device_node *np) return 0; err: - pr_debug("failed to map irq %x for spu %s\n", *oirq.specifier, + pr_debug("failed to map irq %x for spu %s\n", *oirq.args, spu->name); for (; i >= 0; i--) { if (spu->irqs[i] != NO_IRQ) diff --git a/arch/powerpc/platforms/fsl_uli1575.c b/arch/powerpc/platforms/fsl_uli1575.c index ac539c1..288226d 100644 --- a/arch/powerpc/platforms/fsl_uli1575.c +++ b/arch/powerpc/platforms/fsl_uli1575.c @@ -321,8 +321,8 @@ static void hpcd_final_uli5288(struct pci_dev *dev) { struct pci_controller *hose = pci_bus_to_host(dev->bus); struct device_node *hosenode = hose ? hose->dn : NULL; - struct of_irq oirq; - int virq, pin = 2; + struct of_phandle_args oirq; + int pin = 2; u32 laddr[3]; if (!machine_is(mpc86xx_hpcd)) @@ -334,9 +334,7 @@ static void hpcd_final_uli5288(struct pci_dev *dev) laddr[0] = (hose->first_busno << 16) | (PCI_DEVFN(31, 0) << 8); laddr[1] = laddr[2] = 0; of_irq_parse_raw(hosenode, &pin, 1, laddr, &oirq); - virq = irq_create_of_mapping(oirq.controller, oirq.specifier, - oirq.size); - dev->irq = virq; + dev->irq = irq_create_of_mapping(oirq.np, oirq.args, oirq.args_count); } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AL, 0x1575, hpcd_quirk_uli1575); diff --git a/arch/powerpc/platforms/powermac/pic.c b/arch/powerpc/platforms/powermac/pic.c index 720663e..4c24bf6 100644 --- a/arch/powerpc/platforms/powermac/pic.c +++ b/arch/powerpc/platforms/powermac/pic.c @@ -394,7 +394,7 @@ static void __init pmac_pic_probe_oldstyle(void) } int of_irq_parse_oldworld(struct device_node *device, int index, - struct of_irq *out_irq) + struct of_phandle_args *out_irq) { const u32 *ints = NULL; int intlen; @@ -422,9 +422,9 @@ int of_irq_parse_oldworld(struct device_node *device, int index, if (index >= intlen) return -EINVAL; - out_irq->controller = NULL; - out_irq->specifier[0] = ints[index]; - out_irq->size = 1; + out_irq->np = NULL; + out_irq->args[0] = ints[index]; + out_irq->args_count = 1; return 0; } diff --git a/arch/powerpc/platforms/pseries/event_sources.c b/arch/powerpc/platforms/pseries/event_sources.c index 850c18c..6dcf9cc 100644 --- a/arch/powerpc/platforms/pseries/event_sources.c +++ b/arch/powerpc/platforms/pseries/event_sources.c @@ -25,7 +25,7 @@ void request_event_sources_irqs(struct device_node *np, const char *name) { int i, index, count = 0; - struct of_irq oirq; + struct of_phandle_args oirq; const u32 *opicprop; unsigned int opicplen; unsigned int virqs[16]; @@ -59,9 +59,8 @@ void request_event_sources_irqs(struct device_node *np, index++) { if (count > 15) break; - virqs[count] = irq_create_of_mapping(oirq.controller, - oirq.specifier, - oirq.size); + virqs[count] = irq_create_of_mapping(oirq.np, oirq.args, + oirq.args_count); if (virqs[count] == NO_IRQ) { pr_err("event-sources: Unable to allocate " "interrupt number for %s\n", diff --git a/arch/powerpc/sysdev/mpic_msi.c b/arch/powerpc/sysdev/mpic_msi.c index 463e3a7..7dc39f3 100644 --- a/arch/powerpc/sysdev/mpic_msi.c +++ b/arch/powerpc/sysdev/mpic_msi.c @@ -35,7 +35,7 @@ static int mpic_msi_reserve_u3_hwirqs(struct mpic *mpic) const struct irq_domain_ops *ops = mpic->irqhost->ops; struct device_node *np; int flags, index, i; - struct of_irq oirq; + struct of_phandle_args oirq; pr_debug("mpic: found U3, guessing msi allocator setup\n"); @@ -64,8 +64,8 @@ static int mpic_msi_reserve_u3_hwirqs(struct mpic *mpic) index = 0; while (of_irq_parse_one(np, index++, &oirq) == 0) { - ops->xlate(mpic->irqhost, NULL, oirq.specifier, - oirq.size, &hwirq, &flags); + ops->xlate(mpic->irqhost, NULL, oirq.args, + oirq.args_count, &hwirq, &flags); msi_bitmap_reserve_hwirq(&mpic->msi_bitmap, hwirq); } } diff --git a/arch/x86/kernel/devicetree.c b/arch/x86/kernel/devicetree.c index 3ac6398..0098698 100644 --- a/arch/x86/kernel/devicetree.c +++ b/arch/x86/kernel/devicetree.c @@ -105,7 +105,7 @@ struct device_node *pcibios_get_phb_of_node(struct pci_bus *bus) static int x86_of_pci_irq_enable(struct pci_dev *dev) { - struct of_irq oirq; + struct of_phandle_args oirq; u32 virq; int ret; u8 pin; @@ -120,8 +120,7 @@ static int x86_of_pci_irq_enable(struct pci_dev *dev) if (ret) return ret; - virq = irq_create_of_mapping(oirq.controller, oirq.specifier, - oirq.size); + virq = irq_create_of_mapping(oirq.np, oirq.args, oirq.args_count); if (virq == 0) return -EINVAL; dev->irq = virq; diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 410aa24..a7db38a 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -36,13 +36,12 @@ */ unsigned int irq_of_parse_and_map(struct device_node *dev, int index) { - struct of_irq oirq; + struct of_phandle_args oirq; if (of_irq_parse_one(dev, index, &oirq)) return 0; - return irq_create_of_mapping(oirq.controller, oirq.specifier, - oirq.size); + return irq_create_of_mapping(oirq.np, oirq.args, oirq.args_count); } EXPORT_SYMBOL_GPL(irq_of_parse_and_map); @@ -94,7 +93,7 @@ struct device_node *of_irq_find_parent(struct device_node *child) * node exist for the parent. */ int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec, - u32 ointsize, const __be32 *addr, struct of_irq *out_irq) + u32 ointsize, const __be32 *addr, struct of_phandle_args *out_irq) { struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL; const __be32 *tmp, *imap, *imask; @@ -156,10 +155,10 @@ int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec, NULL) { pr_debug(" -> got it !\n"); for (i = 0; i < intsize; i++) - out_irq->specifier[i] = + out_irq->args[i] = of_read_number(intspec +i, 1); - out_irq->size = intsize; - out_irq->controller = ipar; + out_irq->args_count = intsize; + out_irq->np = ipar; of_node_put(old); return 0; } @@ -280,7 +279,7 @@ EXPORT_SYMBOL_GPL(of_irq_parse_raw); * This function resolves an interrupt, walking the tree, for a given * device-tree node. It's the high level pendant to of_irq_parse_raw(). */ -int of_irq_parse_one(struct device_node *device, int index, struct of_irq *out_irq) +int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_args *out_irq) { struct device_node *p; const __be32 *intspec, *tmp, *addr; diff --git a/drivers/of/of_pci_irq.c b/drivers/of/of_pci_irq.c index dceec10..ee3293d 100644 --- a/drivers/of/of_pci_irq.c +++ b/drivers/of/of_pci_irq.c @@ -15,7 +15,7 @@ * PCI tree until an device-node is found, at which point it will finish * resolving using the OF tree walking. */ -int of_irq_parse_pci(const struct pci_dev *pdev, struct of_irq *out_irq) +int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq) { struct device_node *dn, *ppnode; struct pci_dev *ppdev; diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index 05f8180..c5e57f8 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -647,15 +647,14 @@ static int __init mvebu_pcie_setup(int nr, struct pci_sys_data *sys) static int __init mvebu_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { - struct of_irq oirq; + struct of_phandle_args oirq; int ret; ret = of_irq_parse_pci(dev, &oirq); if (ret) return ret; - return irq_create_of_mapping(oirq.controller, oirq.specifier, - oirq.size); + return irq_create_of_mapping(oirq.np, oirq.args, oirq.args_count); } static struct pci_bus *mvebu_pcie_scan_bus(int nr, struct pci_sys_data *sys) diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h index a00bc71..8d9f855 100644 --- a/include/linux/of_irq.h +++ b/include/linux/of_irq.h @@ -8,22 +8,6 @@ #include #include -/** - * of_irq - container for device_node/irq_specifier pair for an irq controller - * @controller: pointer to interrupt controller device tree node - * @size: size of interrupt specifier - * @specifier: array of cells @size long specifing the specific interrupt - * - * This structure is returned when an interrupt is mapped. The controller - * field needs to be put() after use - */ -#define OF_MAX_IRQ_SPEC 4 /* We handle specifiers of at most 4 cells */ -struct of_irq { - struct device_node *controller; /* Interrupt controller node */ - u32 size; /* Specifier size */ - u32 specifier[OF_MAX_IRQ_SPEC]; /* Specifier copy */ -}; - typedef int (*of_irq_init_cb_t)(struct device_node *, struct device_node *); /* @@ -36,12 +20,12 @@ typedef int (*of_irq_init_cb_t)(struct device_node *, struct device_node *); extern unsigned int of_irq_workarounds; extern struct device_node *of_irq_dflt_pic; extern int of_irq_parse_oldworld(struct device_node *device, int index, - struct of_irq *out_irq); + struct of_phandle_args *out_irq); #else /* CONFIG_PPC32 && CONFIG_PPC_PMAC */ #define of_irq_workarounds (0) #define of_irq_dflt_pic (NULL) static inline int of_irq_parse_oldworld(struct device_node *device, int index, - struct of_irq *out_irq) + struct of_phandle_args *out_irq) { return -EINVAL; } @@ -50,9 +34,9 @@ static inline int of_irq_parse_oldworld(struct device_node *device, int index, extern int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec, u32 ointsize, const __be32 *addr, - struct of_irq *out_irq); + struct of_phandle_args *out_irq); extern int of_irq_parse_one(struct device_node *device, int index, - struct of_irq *out_irq); + struct of_phandle_args *out_irq); extern unsigned int irq_create_of_mapping(struct device_node *controller, const u32 *intspec, unsigned int intsize); diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h index 839ba20..f297237 100644 --- a/include/linux/of_pci.h +++ b/include/linux/of_pci.h @@ -5,8 +5,8 @@ #include struct pci_dev; -struct of_irq; -int of_irq_parse_pci(const struct pci_dev *pdev, struct of_irq *out_irq); +struct of_phandle_args; +int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq); struct device_node; struct device_node *of_pci_find_child_device(struct device_node *parent, -- cgit v0.10.2 From e6d30ab1e7d1281784672c0fc2ffa385cfb7279e Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Sun, 15 Sep 2013 16:55:53 +0100 Subject: of/irq: simplify args to irq_create_of_mapping All the callers of irq_create_of_mapping() pass the contents of a struct of_phandle_args structure to the function. Since all the callers already have an of_phandle_args pointer, why not pass it directly to irq_create_of_mapping()? Signed-off-by: Grant Likely Acked-by: Michal Simek Acked-by: Tony Lindgren Cc: Thomas Gleixner Cc: Russell King Cc: Ralf Baechle Cc: Benjamin Herrenschmidt diff --git a/arch/arm/mach-integrator/pci_v3.c b/arch/arm/mach-integrator/pci_v3.c index 2d6b4da..bb3aeb3 100644 --- a/arch/arm/mach-integrator/pci_v3.c +++ b/arch/arm/mach-integrator/pci_v3.c @@ -847,7 +847,7 @@ static int __init pci_v3_map_irq_dt(const struct pci_dev *dev, u8 slot, u8 pin) return 0; } - return irq_create_of_mapping(oirq.np, oirq.args, oirq.args_count); + return irq_create_of_mapping(&oirq); } static int __init pci_v3_dtprobe(struct platform_device *pdev, diff --git a/arch/microblaze/pci/pci-common.c b/arch/microblaze/pci/pci-common.c index c9302c4..60b386c 100644 --- a/arch/microblaze/pci/pci-common.c +++ b/arch/microblaze/pci/pci-common.c @@ -246,7 +246,7 @@ int pci_read_irq_line(struct pci_dev *pci_dev) oirq.args_count, oirq.args[0], oirq.args[1], of_node_full_name(oirq.np)); - virq = irq_create_of_mapping(oirq.np, oirq.args, oirq.args_count); + virq = irq_create_of_mapping(&oirq); } if (!virq) { pr_debug(" Failed to map !\n"); diff --git a/arch/mips/pci/fixup-lantiq.c b/arch/mips/pci/fixup-lantiq.c index 81ff0b5..aef60e7 100644 --- a/arch/mips/pci/fixup-lantiq.c +++ b/arch/mips/pci/fixup-lantiq.c @@ -33,7 +33,7 @@ int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) slot, pin); return 0; } - irq = irq_create_of_mapping(dev_irq.np, dev_irq.args, dev_irq.args_count); + irq = irq_create_of_mapping(&dev_irq); dev_info(&dev->dev, "SLOT:%d PIN:%d IRQ:%d\n", slot, pin, irq); return irq; } diff --git a/arch/mips/pci/pci-rt3883.c b/arch/mips/pci/pci-rt3883.c index 0f08a5b..eadc431 100644 --- a/arch/mips/pci/pci-rt3883.c +++ b/arch/mips/pci/pci-rt3883.c @@ -594,7 +594,7 @@ int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) return 0; } - irq = irq_create_of_mapping(dev_irq.np, dev_irq.args, dev_irq.args_count); + irq = irq_create_of_mapping(&dev_irq); if (irq == 0) pr_crit("pci %s: no irq found for pin %u\n", diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 96c4623..a1e3e40 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -266,7 +266,7 @@ static int pci_read_irq_line(struct pci_dev *pci_dev) oirq.args_count, oirq.args[0], oirq.args[1], of_node_full_name(oirq.np)); - virq = irq_create_of_mapping(oirq.np, oirq.args, oirq.args_count); + virq = irq_create_of_mapping(&oirq); } if(virq == NO_IRQ) { pr_debug(" Failed to map !\n"); diff --git a/arch/powerpc/platforms/cell/celleb_scc_pciex.c b/arch/powerpc/platforms/cell/celleb_scc_pciex.c index e8d34d1..b3ea96d 100644 --- a/arch/powerpc/platforms/cell/celleb_scc_pciex.c +++ b/arch/powerpc/platforms/cell/celleb_scc_pciex.c @@ -511,7 +511,7 @@ static __init int celleb_setup_pciex(struct device_node *node, pr_err("PCIEXC:Failed to map irq\n"); goto error; } - virq = irq_create_of_mapping(oirq.np, oirq.args, oirq.args_count); + virq = irq_create_of_mapping(&oirq); if (request_irq(virq, pciex_handle_internal_irq, 0, "pciex", (void *)phb)) { pr_err("PCIEXC:Failed to request irq\n"); diff --git a/arch/powerpc/platforms/cell/celleb_scc_sio.c b/arch/powerpc/platforms/cell/celleb_scc_sio.c index 06046d5..c8eb571 100644 --- a/arch/powerpc/platforms/cell/celleb_scc_sio.c +++ b/arch/powerpc/platforms/cell/celleb_scc_sio.c @@ -66,7 +66,7 @@ static int __init txx9_serial_init(void) #ifdef CONFIG_SERIAL_TXX9_CONSOLE req.membase = ioremap(req.mapbase, 0x24); #endif - req.irq = irq_create_of_mapping(irq.np, irq.args, irq.args_count); + req.irq = irq_create_of_mapping(&irq); req.flags |= UPF_IOREMAP | UPF_BUGGY_UART /*HAVE_CTS_LINE*/; req.uartclk = 83300000; diff --git a/arch/powerpc/platforms/cell/spider-pic.c b/arch/powerpc/platforms/cell/spider-pic.c index 6e842fd..d206804 100644 --- a/arch/powerpc/platforms/cell/spider-pic.c +++ b/arch/powerpc/platforms/cell/spider-pic.c @@ -236,11 +236,8 @@ static unsigned int __init spider_find_cascade_and_node(struct spider_pic *pic) * tree in case the device-tree is ever fixed */ struct of_phandle_args oirq; - if (of_irq_parse_one(pic->host->of_node, 0, &oirq) == 0) { - virq = irq_create_of_mapping(oirq.np, oirq.args, - oirq.args_count); - return virq; - } + if (of_irq_parse_one(pic->host->of_node, 0, &oirq) == 0) + return irq_create_of_mapping(&oirq); /* Now do the horrible hacks */ tmp = of_get_property(pic->host->of_node, "#interrupt-cells", NULL); diff --git a/arch/powerpc/platforms/cell/spu_manage.c b/arch/powerpc/platforms/cell/spu_manage.c index e9eb4f8..c3327f3 100644 --- a/arch/powerpc/platforms/cell/spu_manage.c +++ b/arch/powerpc/platforms/cell/spu_manage.c @@ -190,8 +190,7 @@ static int __init spu_map_interrupts(struct spu *spu, struct device_node *np) ret = -EINVAL; pr_debug(" irq %d no 0x%x on %s\n", i, oirq.args[0], oirq.np->full_name); - spu->irqs[i] = irq_create_of_mapping(oirq.np, - oirq.args, oirq.args_count); + spu->irqs[i] = irq_create_of_mapping(&oirq); if (spu->irqs[i] == NO_IRQ) { pr_debug("spu_new: failed to map it !\n"); goto err; diff --git a/arch/powerpc/platforms/fsl_uli1575.c b/arch/powerpc/platforms/fsl_uli1575.c index 288226d..8904046 100644 --- a/arch/powerpc/platforms/fsl_uli1575.c +++ b/arch/powerpc/platforms/fsl_uli1575.c @@ -334,7 +334,7 @@ static void hpcd_final_uli5288(struct pci_dev *dev) laddr[0] = (hose->first_busno << 16) | (PCI_DEVFN(31, 0) << 8); laddr[1] = laddr[2] = 0; of_irq_parse_raw(hosenode, &pin, 1, laddr, &oirq); - dev->irq = irq_create_of_mapping(oirq.np, oirq.args, oirq.args_count); + dev->irq = irq_create_of_mapping(&oirq); } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AL, 0x1575, hpcd_quirk_uli1575); diff --git a/arch/powerpc/platforms/pseries/event_sources.c b/arch/powerpc/platforms/pseries/event_sources.c index 6dcf9cc..18380e8 100644 --- a/arch/powerpc/platforms/pseries/event_sources.c +++ b/arch/powerpc/platforms/pseries/event_sources.c @@ -59,8 +59,7 @@ void request_event_sources_irqs(struct device_node *np, index++) { if (count > 15) break; - virqs[count] = irq_create_of_mapping(oirq.np, oirq.args, - oirq.args_count); + virqs[count] = irq_create_of_mapping(&oirq); if (virqs[count] == NO_IRQ) { pr_err("event-sources: Unable to allocate " "interrupt number for %s\n", diff --git a/arch/x86/kernel/devicetree.c b/arch/x86/kernel/devicetree.c index 0098698..d39948f 100644 --- a/arch/x86/kernel/devicetree.c +++ b/arch/x86/kernel/devicetree.c @@ -120,7 +120,7 @@ static int x86_of_pci_irq_enable(struct pci_dev *dev) if (ret) return ret; - virq = irq_create_of_mapping(oirq.np, oirq.args, oirq.args_count); + virq = irq_create_of_mapping(&oirq); if (virq == 0) return -EINVAL; dev->irq = virq; diff --git a/drivers/of/irq.c b/drivers/of/irq.c index a7db38a..84184c4 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -41,7 +41,7 @@ unsigned int irq_of_parse_and_map(struct device_node *dev, int index) if (of_irq_parse_one(dev, index, &oirq)) return 0; - return irq_create_of_mapping(oirq.np, oirq.args, oirq.args_count); + return irq_create_of_mapping(&oirq); } EXPORT_SYMBOL_GPL(irq_of_parse_and_map); diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index c5e57f8..3a8d01e 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -654,7 +654,7 @@ static int __init mvebu_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) if (ret) return ret; - return irq_create_of_mapping(oirq.np, oirq.args, oirq.args_count); + return irq_create_of_mapping(&oirq); } static struct pci_bus *mvebu_pcie_scan_bus(int nr, struct pci_sys_data *sys) diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h index 8d9f855..3bbba8d 100644 --- a/include/linux/of_irq.h +++ b/include/linux/of_irq.h @@ -37,9 +37,7 @@ extern int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec, struct of_phandle_args *out_irq); extern int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_args *out_irq); -extern unsigned int irq_create_of_mapping(struct device_node *controller, - const u32 *intspec, - unsigned int intsize); +extern unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data); extern int of_irq_to_resource(struct device_node *dev, int index, struct resource *r); extern int of_irq_count(struct device_node *dev); diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 706724e..cf68bb3 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -465,27 +465,26 @@ int irq_create_strict_mappings(struct irq_domain *domain, unsigned int irq_base, } EXPORT_SYMBOL_GPL(irq_create_strict_mappings); -unsigned int irq_create_of_mapping(struct device_node *controller, - const u32 *intspec, unsigned int intsize) +unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data) { struct irq_domain *domain; irq_hw_number_t hwirq; unsigned int type = IRQ_TYPE_NONE; unsigned int virq; - domain = controller ? irq_find_host(controller) : irq_default_domain; + domain = irq_data->np ? irq_find_host(irq_data->np) : irq_default_domain; if (!domain) { pr_warn("no irq domain found for %s !\n", - of_node_full_name(controller)); + of_node_full_name(irq_data->np)); return 0; } /* If domain has no translation, then we assume interrupt line */ if (domain->ops->xlate == NULL) - hwirq = intspec[0]; + hwirq = irq_data->args[0]; else { - if (domain->ops->xlate(domain, controller, intspec, intsize, - &hwirq, &type)) + if (domain->ops->xlate(domain, irq_data->np, irq_data->args, + irq_data->args_count, &hwirq, &type)) return 0; } -- cgit v0.10.2 From 2361613206e66ce59cc0e08efa8d98ec15b84ed1 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Sun, 15 Sep 2013 22:32:39 +0100 Subject: of/irq: Refactor interrupt-map parsing All the users of of_irq_parse_raw pass in a raw interrupt specifier from the device tree and expect it to be returned (possibly modified) in an of_phandle_args structure. However, the primary function of of_irq_parse_raw() is to check for translations due to the presence of one or more interrupt-map properties. The actual placing of the data into an of_phandle_args structure is trivial. If it is refactored to accept an of_phandle_args structure directly, then it becomes possible to consume of_phandle_args from other sources. This is important for an upcoming patch that allows a device to be connected to more than one interrupt parent. It also simplifies the code a bit. The biggest complication with this patch is that the old version works on the interrupt specifiers in __be32 form, but the of_phandle_args structure is intended to carry it in the cpu-native version. A bit of churn was required to make this work. In the end it results in tighter code, so the churn is worth it. Signed-off-by: Grant Likely Acked-by: Tony Lindgren Cc: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/fsl_uli1575.c b/arch/powerpc/platforms/fsl_uli1575.c index 8904046..b97f6f3 100644 --- a/arch/powerpc/platforms/fsl_uli1575.c +++ b/arch/powerpc/platforms/fsl_uli1575.c @@ -322,7 +322,6 @@ static void hpcd_final_uli5288(struct pci_dev *dev) struct pci_controller *hose = pci_bus_to_host(dev->bus); struct device_node *hosenode = hose ? hose->dn : NULL; struct of_phandle_args oirq; - int pin = 2; u32 laddr[3]; if (!machine_is(mpc86xx_hpcd)) @@ -331,9 +330,12 @@ static void hpcd_final_uli5288(struct pci_dev *dev) if (!hosenode) return; + oirq.np = hosenode; + oirq.args[0] = 2; + oirq.args_count = 1; laddr[0] = (hose->first_busno << 16) | (PCI_DEVFN(31, 0) << 8); laddr[1] = laddr[2] = 0; - of_irq_parse_raw(hosenode, &pin, 1, laddr, &oirq); + of_irq_parse_raw(laddr, &oirq); dev->irq = irq_create_of_mapping(&oirq); } diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 84184c4..0ed5ed4 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -80,31 +80,32 @@ struct device_node *of_irq_find_parent(struct device_node *child) /** * of_irq_parse_raw - Low level interrupt tree parsing * @parent: the device interrupt parent - * @intspec: interrupt specifier ("interrupts" property of the device) - * @ointsize: size of the passed in interrupt specifier - * @addr: address specifier (start of "reg" property of the device) - * @out_irq: structure of_irq filled by this function + * @addr: address specifier (start of "reg" property of the device) in be32 format + * @out_irq: structure of_irq updated by this function * * Returns 0 on success and a negative number on error * * This function is a low-level interrupt tree walking function. It * can be used to do a partial walk with synthetized reg and interrupts * properties, for example when resolving PCI interrupts when no device - * node exist for the parent. + * node exist for the parent. It takes an interrupt specifier structure as + * input, walks the tree looking for any interrupt-map properties, translates + * the specifier for each map, and then returns the translated map. */ -int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec, - u32 ointsize, const __be32 *addr, struct of_phandle_args *out_irq) +int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) { struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL; - const __be32 *tmp, *imap, *imask; + __be32 initial_match_array[8]; + const __be32 *match_array = initial_match_array; + const __be32 *tmp, *imap, *imask, dummy_imask[] = { ~0, ~0, ~0, ~0, ~0 }; u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0; int imaplen, match, i; pr_debug("of_irq_parse_raw: par=%s,intspec=[0x%08x 0x%08x...],ointsize=%d\n", - of_node_full_name(parent), be32_to_cpup(intspec), - be32_to_cpup(intspec + 1), ointsize); + of_node_full_name(out_irq->np), out_irq->args[0], out_irq->args[1], + out_irq->args_count); - ipar = of_node_get(parent); + ipar = of_node_get(out_irq->np); /* First get the #interrupt-cells property of the current cursor * that tells us how to interpret the passed-in intspec. If there @@ -127,7 +128,7 @@ int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec, pr_debug("of_irq_parse_raw: ipar=%s, size=%d\n", of_node_full_name(ipar), intsize); - if (ointsize != intsize) + if (out_irq->args_count != intsize) return -EINVAL; /* Look for this #address-cells. We have to implement the old linux @@ -146,6 +147,21 @@ int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec, pr_debug(" -> addrsize=%d\n", addrsize); + /* If we were passed no "reg" property and we attempt to parse + * an interrupt-map, then #address-cells must be 0. + * Fail if it's not. + */ + if (addr == NULL && addrsize != 0) { + pr_debug(" -> no reg passed in when needed !\n"); + return -EINVAL; + } + + /* Precalculate the match array - this simplifies match loop */ + for (i = 0; i < addrsize; i++) + initial_match_array[i] = addr[i]; + for (i = 0; i < intsize; i++) + initial_match_array[addrsize + i] = cpu_to_be32(out_irq->args[i]); + /* Now start the actual "proper" walk of the interrupt tree */ while (ipar != NULL) { /* Now check if cursor is an interrupt-controller and if it is @@ -154,11 +170,6 @@ int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec, if (of_get_property(ipar, "interrupt-controller", NULL) != NULL) { pr_debug(" -> got it !\n"); - for (i = 0; i < intsize; i++) - out_irq->args[i] = - of_read_number(intspec +i, 1); - out_irq->args_count = intsize; - out_irq->np = ipar; of_node_put(old); return 0; } @@ -175,34 +186,16 @@ int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec, /* Look for a mask */ imask = of_get_property(ipar, "interrupt-map-mask", NULL); - - /* If we were passed no "reg" property and we attempt to parse - * an interrupt-map, then #address-cells must be 0. - * Fail if it's not. - */ - if (addr == NULL && addrsize != 0) { - pr_debug(" -> no reg passed in when needed !\n"); - goto fail; - } + if (!imask) + imask = dummy_imask; /* Parse interrupt-map */ match = 0; while (imaplen > (addrsize + intsize + 1) && !match) { /* Compare specifiers */ match = 1; - for (i = 0; i < addrsize && match; ++i) { - __be32 mask = imask ? imask[i] - : cpu_to_be32(0xffffffffu); - match = ((addr[i] ^ imap[i]) & mask) == 0; - } - for (; i < (addrsize + intsize) && match; ++i) { - __be32 mask = imask ? imask[i] - : cpu_to_be32(0xffffffffu); - match = - ((intspec[i-addrsize] ^ imap[i]) & mask) == 0; - } - imap += addrsize + intsize; - imaplen -= addrsize + intsize; + for (i = 0; i < (addrsize + intsize); i++, imaplen--) + match = !((match_array[i] ^ *imap++) & imask[i]); pr_debug(" -> match=%d (imaplen=%d)\n", match, imaplen); @@ -247,12 +240,18 @@ int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec, if (!match) goto fail; - of_node_put(old); - old = of_node_get(newpar); + /* + * Successfully parsed an interrrupt-map translation; copy new + * interrupt specifier into the out_irq structure + */ + of_node_put(out_irq->np); + out_irq->np = of_node_get(newpar); + + match_array = imap - newaddrsize - newintsize; + for (i = 0; i < newintsize; i++) + out_irq->args[i] = be32_to_cpup(imap - newintsize + i); + out_irq->args_count = intsize = newintsize; addrsize = newaddrsize; - intsize = newintsize; - intspec = imap - intsize; - addr = intspec - addrsize; skiplevel: /* Iterate again with new parent */ @@ -263,7 +262,7 @@ int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec, } fail: of_node_put(ipar); - of_node_put(old); + of_node_put(out_irq->np); of_node_put(newpar); return -EINVAL; @@ -276,15 +275,16 @@ EXPORT_SYMBOL_GPL(of_irq_parse_raw); * @index: index of the interrupt to resolve * @out_irq: structure of_irq filled by this function * - * This function resolves an interrupt, walking the tree, for a given - * device-tree node. It's the high level pendant to of_irq_parse_raw(). + * This function resolves an interrupt for a node by walking the interrupt tree, + * finding which interrupt controller node it is attached to, and returning the + * interrupt specifier that can be used to retrieve a Linux IRQ number. */ int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_args *out_irq) { struct device_node *p; const __be32 *intspec, *tmp, *addr; u32 intsize, intlen; - int res = -EINVAL; + int i, res = -EINVAL; pr_debug("of_irq_parse_one: dev=%s, index=%d\n", of_node_full_name(device), index); @@ -320,9 +320,15 @@ int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_ar if ((index + 1) * intsize > intlen) goto out; - /* Get new specifier and map it */ - res = of_irq_parse_raw(p, intspec + index * intsize, intsize, - addr, out_irq); + /* Copy intspec into irq structure */ + intspec += index * intsize; + out_irq->np = p; + out_irq->args_count = intsize; + for (i = 0; i < intsize; i++) + out_irq->args[i] = be32_to_cpup(intspec++); + + /* Check if there are any interrupt-map translations to process */ + res = of_irq_parse_raw(addr, out_irq); out: of_node_put(p); return res; diff --git a/drivers/of/of_pci_irq.c b/drivers/of/of_pci_irq.c index ee3293d..303afeb 100644 --- a/drivers/of/of_pci_irq.c +++ b/drivers/of/of_pci_irq.c @@ -85,9 +85,12 @@ int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq pdev = ppdev; } + out_irq->np = ppnode; + out_irq->args_count = 1; + out_irq->args[0] = lspec; lspec_be = cpu_to_be32(lspec); laddr[0] = cpu_to_be32((pdev->bus->number << 16) | (pdev->devfn << 8)); - laddr[1] = laddr[2] = cpu_to_be32(0); - return of_irq_parse_raw(ppnode, &lspec_be, 1, laddr, out_irq); + laddr[1] = laddr[2] = cpu_to_be32(0); + return of_irq_parse_raw(laddr, out_irq); } EXPORT_SYMBOL_GPL(of_irq_parse_pci); diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h index 3bbba8d..c0d6dfe 100644 --- a/include/linux/of_irq.h +++ b/include/linux/of_irq.h @@ -31,10 +31,7 @@ static inline int of_irq_parse_oldworld(struct device_node *device, int index, } #endif /* CONFIG_PPC32 && CONFIG_PPC_PMAC */ - -extern int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec, - u32 ointsize, const __be32 *addr, - struct of_phandle_args *out_irq); +extern int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq); extern int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_args *out_irq); extern unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data); -- cgit v0.10.2 From 624cfca534f9b1ffb1326617b4e973a3d5ecff4a Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Fri, 11 Oct 2013 22:05:10 +0100 Subject: of: Add helper for printing an of_phandle_args structure It is sometimes useful for debug to get the contents of an of_phandle_args structure out into the kernel log. Signed-off-by: Grant Likely diff --git a/drivers/of/base.c b/drivers/of/base.c index 3ae106d..021db96 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1185,6 +1185,15 @@ int of_property_count_strings(struct device_node *np, const char *propname) } EXPORT_SYMBOL_GPL(of_property_count_strings); +void of_print_phandle_args(const char *msg, const struct of_phandle_args *args) +{ + int i; + printk("%s %s", msg, of_node_full_name(args->np)); + for (i = 0; i < args->args_count; i++) + printk(i ? ",%08x" : ":%08x", args->args[i]); + printk("\n"); +} + static int __of_parse_phandle_with_args(const struct device_node *np, const char *list_name, const char *cells_name, diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 0ed5ed4..ddea945 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -101,9 +101,9 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0; int imaplen, match, i; - pr_debug("of_irq_parse_raw: par=%s,intspec=[0x%08x 0x%08x...],ointsize=%d\n", - of_node_full_name(out_irq->np), out_irq->args[0], out_irq->args[1], - out_irq->args_count); +#ifdef DEBUG + of_print_phandle_args("of_irq_parse_raw: ", out_irq); +#endif ipar = of_node_get(out_irq->np); diff --git a/include/linux/of.h b/include/linux/of.h index f95aee3..374e035 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -275,6 +275,7 @@ extern int of_n_size_cells(struct device_node *np); extern const struct of_device_id *of_match_node( const struct of_device_id *matches, const struct device_node *node); extern int of_modalias_node(struct device_node *node, char *modalias, int len); +extern void of_print_phandle_args(const char *msg, const struct of_phandle_args *args); extern struct device_node *of_parse_phandle(const struct device_node *np, const char *phandle_name, int index); -- cgit v0.10.2 From a9f10ca76d784023fc45f01f025b54e9960f4ec1 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Fri, 11 Oct 2013 22:04:23 +0100 Subject: of: Add testcases for interrupt parsing This patch extends the DT selftest code with some test cases for the interrupt parsing functions. Signed-off-by: Grant Likely diff --git a/arch/arm/boot/dts/testcases/tests-interrupts.dtsi b/arch/arm/boot/dts/testcases/tests-interrupts.dtsi new file mode 100644 index 0000000..6ecda71 --- /dev/null +++ b/arch/arm/boot/dts/testcases/tests-interrupts.dtsi @@ -0,0 +1,41 @@ + +/ { + testcase-data { + interrupts { + #address-cells = <0>; + test_intc0: intc0 { + interrupt-controller; + #interrupt-cells = <1>; + }; + + test_intc1: intc1 { + interrupt-controller; + #interrupt-cells = <3>; + }; + + test_intc2: intc2 { + interrupt-controller; + #interrupt-cells = <2>; + }; + + test_intmap0: intmap0 { + #interrupt-cells = <1>; + #address-cells = <0>; + interrupt-map = <1 &test_intc0 9>, + <2 &test_intc1 10 11 12>, + <3 &test_intc2 13 14>, + <4 &test_intc2 15 16>; + }; + + interrupts0 { + interrupt-parent = <&test_intc0>; + interrupts = <1>, <2>, <3>, <4>; + }; + + interrupts1 { + interrupt-parent = <&test_intmap0>; + interrupts = <1>, <2>, <3>, <4>; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/testcases/tests.dtsi b/arch/arm/boot/dts/testcases/tests.dtsi index a7c5067..3f123ec 100644 --- a/arch/arm/boot/dts/testcases/tests.dtsi +++ b/arch/arm/boot/dts/testcases/tests.dtsi @@ -1 +1,2 @@ /include/ "tests-phandle.dtsi" +/include/ "tests-interrupts.dtsi" diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c index 0eb5c38..9c80f0b 100644 --- a/drivers/of/selftest.c +++ b/drivers/of/selftest.c @@ -9,18 +9,24 @@ #include #include #include +#include #include #include #include #include -static bool selftest_passed = true; +static struct selftest_results { + int passed; + int failed; +} selftest_results; + #define selftest(result, fmt, ...) { \ if (!(result)) { \ - pr_err("FAIL %s:%i " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \ - selftest_passed = false; \ + selftest_results.failed++; \ + pr_err("FAIL %s():%i " fmt, __func__, __LINE__, ##__VA_ARGS__); \ } else { \ - pr_info("pass %s:%i\n", __FILE__, __LINE__); \ + selftest_results.passed++; \ + pr_debug("pass %s():%i\n", __func__, __LINE__); \ } \ } @@ -131,7 +137,6 @@ static void __init of_selftest_property_match_string(void) struct device_node *np; int rc; - pr_info("start\n"); np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a"); if (!np) { pr_err("No testcase data in device tree\n"); @@ -154,6 +159,78 @@ static void __init of_selftest_property_match_string(void) selftest(rc == -EILSEQ, "unterminated string; rc=%i", rc); } +static void __init of_selftest_parse_interrupts(void) +{ + struct device_node *np; + struct of_phandle_args args; + int i, rc; + + np = of_find_node_by_path("/testcase-data/interrupts/interrupts0"); + if (!np) { + pr_err("missing testcase data\n"); + return; + } + + for (i = 0; i < 4; i++) { + bool passed = true; + args.args_count = 0; + rc = of_irq_parse_one(np, i, &args); + + passed &= !rc; + passed &= (args.args_count == 1); + passed &= (args.args[0] == (i + 1)); + + selftest(passed, "index %i - data error on node %s rc=%i\n", + i, args.np->full_name, rc); + } + of_node_put(np); + + np = of_find_node_by_path("/testcase-data/interrupts/interrupts1"); + if (!np) { + pr_err("missing testcase data\n"); + return; + } + + for (i = 0; i < 4; i++) { + bool passed = true; + args.args_count = 0; + rc = of_irq_parse_one(np, i, &args); + + /* Test the values from tests-phandle.dtsi */ + switch (i) { + case 0: + passed &= !rc; + passed &= (args.args_count == 1); + passed &= (args.args[0] == 9); + break; + case 1: + passed &= !rc; + passed &= (args.args_count == 3); + passed &= (args.args[0] == 10); + passed &= (args.args[1] == 11); + passed &= (args.args[2] == 12); + break; + case 2: + passed &= !rc; + passed &= (args.args_count == 2); + passed &= (args.args[0] == 13); + passed &= (args.args[1] == 14); + break; + case 3: + passed &= !rc; + passed &= (args.args_count == 2); + passed &= (args.args[0] == 15); + passed &= (args.args[1] == 16); + break; + default: + passed = false; + } + selftest(passed, "index %i - data error on node %s rc=%i\n", + i, args.np->full_name, rc); + } + of_node_put(np); +} + static int __init of_selftest(void) { struct device_node *np; @@ -168,7 +245,9 @@ static int __init of_selftest(void) pr_info("start of selftest - you will see error messages\n"); of_selftest_parse_phandle_with_args(); of_selftest_property_match_string(); - pr_info("end of selftest - %s\n", selftest_passed ? "PASS" : "FAIL"); + of_selftest_parse_interrupts(); + pr_info("end of selftest - %i passed, %i failed\n", + selftest_results.passed, selftest_results.failed); return 0; } late_initcall(of_selftest); -- cgit v0.10.2 From 3da5278727a895d49a601f67fd49dffa0b80f9a5 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 18 Sep 2013 15:24:43 +0200 Subject: of/irq: Rework of_irq_count() The of_irq_to_resource() helper that is used to implement of_irq_count() tries to resolve interrupts and in fact creates a mapping for resolved interrupts. That's pretty heavy lifting for something that claims to just return the number of interrupts requested by a given device node. Instead, use the more lightweight of_irq_map_one(), which, despite the name, doesn't create an actual mapping. Perhaps a better name would be of_irq_translate_one(). Signed-off-by: Thierry Reding Acked-by: Rob Herring [grant.likely: fixup s/of_irq_map_one/of_irq_parse_one/] Signed-off-by: Grant Likely diff --git a/drivers/of/irq.c b/drivers/of/irq.c index ddea945..7c4ff12 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -373,9 +373,10 @@ EXPORT_SYMBOL_GPL(of_irq_to_resource); */ int of_irq_count(struct device_node *dev) { + struct of_phandle_args irq; int nr = 0; - while (of_irq_to_resource(dev, nr, NULL)) + while (of_irq_parse_one(dev, nr, &irq) == 0) nr++; return nr; -- cgit v0.10.2 From f7578496a671a96e501f16a5104893275e32c33a Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 18 Sep 2013 15:24:44 +0200 Subject: of/irq: Use irq_of_parse_and_map() Replace some instances of of_irq_map_one()/irq_create_of_mapping() and of_irq_to_resource() by the simpler equivalent irq_of_parse_and_map(). Signed-off-by: Thierry Reding Acked-by: Rob Herring [grant.likely: resolved conflicts with core code renames] Signed-off-by: Grant Likely diff --git a/arch/arm/mach-u300/timer.c b/arch/arm/mach-u300/timer.c index b5db207..9a5f9fb 100644 --- a/arch/arm/mach-u300/timer.c +++ b/arch/arm/mach-u300/timer.c @@ -358,8 +358,7 @@ static struct delay_timer u300_delay_timer; */ static void __init u300_timer_init_of(struct device_node *np) { - struct resource irq_res; - int irq; + unsigned int irq; struct clk *clk; unsigned long rate; @@ -368,11 +367,11 @@ static void __init u300_timer_init_of(struct device_node *np) panic("could not ioremap system timer\n"); /* Get the IRQ for the GP1 timer */ - irq = of_irq_to_resource(np, 2, &irq_res); - if (irq <= 0) + irq = irq_of_parse_and_map(np, 2); + if (!irq) panic("no IRQ for system timer\n"); - pr_info("U300 GP1 timer @ base: %p, IRQ: %d\n", u300_timer_base, irq); + pr_info("U300 GP1 timer @ base: %p, IRQ: %u\n", u300_timer_base, irq); /* Clock the interrupt controller */ clk = of_clk_get(np, 0); diff --git a/arch/powerpc/platforms/cell/celleb_scc_pciex.c b/arch/powerpc/platforms/cell/celleb_scc_pciex.c index b3ea96d..4278acf 100644 --- a/arch/powerpc/platforms/cell/celleb_scc_pciex.c +++ b/arch/powerpc/platforms/cell/celleb_scc_pciex.c @@ -486,7 +486,6 @@ static __init int celleb_setup_pciex(struct device_node *node, struct pci_controller *phb) { struct resource r; - struct of_phandle_args oirq; int virq; /* SMMIO registers; used inside this file */ @@ -507,11 +506,11 @@ static __init int celleb_setup_pciex(struct device_node *node, phb->ops = &scc_pciex_pci_ops; /* internal interrupt handler */ - if (of_irq_parse_one(node, 1, &oirq)) { + virq = irq_of_parse_and_map(node, 1); + if (!virq) { pr_err("PCIEXC:Failed to map irq\n"); goto error; } - virq = irq_create_of_mapping(&oirq); if (request_irq(virq, pciex_handle_internal_irq, 0, "pciex", (void *)phb)) { pr_err("PCIEXC:Failed to request irq\n"); diff --git a/arch/powerpc/platforms/cell/spider-pic.c b/arch/powerpc/platforms/cell/spider-pic.c index d206804..1f72f4a 100644 --- a/arch/powerpc/platforms/cell/spider-pic.c +++ b/arch/powerpc/platforms/cell/spider-pic.c @@ -235,9 +235,9 @@ static unsigned int __init spider_find_cascade_and_node(struct spider_pic *pic) /* First, we check whether we have a real "interrupts" in the device * tree in case the device-tree is ever fixed */ - struct of_phandle_args oirq; - if (of_irq_parse_one(pic->host->of_node, 0, &oirq) == 0) - return irq_create_of_mapping(&oirq); + virq = irq_of_parse_and_map(pic->host->of_node, 0); + if (virq) + return virq; /* Now do the horrible hacks */ tmp = of_get_property(pic->host->of_node, "#interrupt-cells", NULL); diff --git a/arch/powerpc/sysdev/fsl_gtm.c b/arch/powerpc/sysdev/fsl_gtm.c index 0eb871c..dd0d5be 100644 --- a/arch/powerpc/sysdev/fsl_gtm.c +++ b/arch/powerpc/sysdev/fsl_gtm.c @@ -401,16 +401,15 @@ static int __init fsl_gtm_init(void) gtm->clock = *clock; for (i = 0; i < ARRAY_SIZE(gtm->timers); i++) { - int ret; - struct resource irq; + unsigned int irq; - ret = of_irq_to_resource(np, i, &irq); - if (ret == NO_IRQ) { + irq = irq_of_parse_and_map(np, i); + if (irq == NO_IRQ) { pr_err("%s: not enough interrupts specified\n", np->full_name); goto err; } - gtm->timers[i].irq = irq.start; + gtm->timers[i].irq = irq; gtm->timers[i].gtm = gtm; } diff --git a/arch/powerpc/sysdev/mpic_msgr.c b/arch/powerpc/sysdev/mpic_msgr.c index c753258..2c9b52a 100644 --- a/arch/powerpc/sysdev/mpic_msgr.c +++ b/arch/powerpc/sysdev/mpic_msgr.c @@ -237,15 +237,13 @@ static int mpic_msgr_probe(struct platform_device *dev) raw_spin_lock_init(&msgr->lock); if (receive_mask & (1 << i)) { - struct resource irq; - - if (of_irq_to_resource(np, irq_index, &irq) == NO_IRQ) { + msgr->irq = irq_of_parse_and_map(np, irq_index); + if (msgr->irq == NO_IRQ) { dev_err(&dev->dev, "Missing interrupt specifier"); kfree(msgr); return -EFAULT; } - msgr->irq = irq.start; irq_index += 1; } else { msgr->irq = NO_IRQ; diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c index b010d42..ae6e554 100644 --- a/drivers/crypto/caam/ctrl.c +++ b/drivers/crypto/caam/ctrl.c @@ -224,7 +224,7 @@ static int caam_probe(struct platform_device *pdev) topregs = (struct caam_full __iomem *)ctrl; /* Get the IRQ of the controller (for security violations only) */ - ctrlpriv->secvio_irq = of_irq_to_resource(nprop, 0, NULL); + ctrlpriv->secvio_irq = irq_of_parse_and_map(nprop, 0); /* * Enable DECO watchdogs and, if this is a PHYS_ADDR_T_64BIT kernel, diff --git a/drivers/crypto/caam/jr.c b/drivers/crypto/caam/jr.c index 105ba4d..517a16d 100644 --- a/drivers/crypto/caam/jr.c +++ b/drivers/crypto/caam/jr.c @@ -403,7 +403,7 @@ int caam_jr_probe(struct platform_device *pdev, struct device_node *np, dma_set_mask(jrdev, DMA_BIT_MASK(32)); /* Identify the interrupt */ - jrpriv->irq = of_irq_to_resource(np, 0, NULL); + jrpriv->irq = irq_of_parse_and_map(np, 0); /* Now do the platform independent part */ error = caam_jr_init(jrdev); /* now turn on hardware */ diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index 8bdde57..e28104b 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -1818,7 +1818,7 @@ static int omap_sham_get_res_of(struct omap_sham_dev *dd, goto err; } - dd->irq = of_irq_to_resource(node, 0, NULL); + dd->irq = irq_of_parse_and_map(node, 0); if (!dd->irq) { dev_err(dev, "can't translate OF irq value\n"); err = -EINVAL; diff --git a/drivers/i2c/busses/i2c-cpm.c b/drivers/i2c/busses/i2c-cpm.c index b2b8aa9..3e5ea2c 100644 --- a/drivers/i2c/busses/i2c-cpm.c +++ b/drivers/i2c/busses/i2c-cpm.c @@ -447,7 +447,7 @@ static int cpm_i2c_setup(struct cpm_i2c *cpm) init_waitqueue_head(&cpm->i2c_wait); - cpm->irq = of_irq_to_resource(ofdev->dev.of_node, 0, NULL); + cpm->irq = irq_of_parse_and_map(ofdev->dev.of_node, 0); if (!cpm->irq) return -EINVAL; diff --git a/drivers/input/serio/xilinx_ps2.c b/drivers/input/serio/xilinx_ps2.c index 4b7662a..36f7b95 100644 --- a/drivers/input/serio/xilinx_ps2.c +++ b/drivers/input/serio/xilinx_ps2.c @@ -235,12 +235,12 @@ static void sxps2_close(struct serio *pserio) */ static int xps2_of_probe(struct platform_device *ofdev) { - struct resource r_irq; /* Interrupt resources */ struct resource r_mem; /* IO mem resources */ struct xps2data *drvdata; struct serio *serio; struct device *dev = &ofdev->dev; resource_size_t remap_size, phys_addr; + unsigned int irq; int error; dev_info(dev, "Device Tree Probing \'%s\'\n", @@ -254,7 +254,8 @@ static int xps2_of_probe(struct platform_device *ofdev) } /* Get IRQ for the device */ - if (!of_irq_to_resource(ofdev->dev.of_node, 0, &r_irq)) { + irq = irq_of_parse_and_map(ofdev->dev.of_node, 0); + if (!irq) { dev_err(dev, "no IRQ found\n"); return -ENODEV; } @@ -267,7 +268,7 @@ static int xps2_of_probe(struct platform_device *ofdev) } spin_lock_init(&drvdata->lock); - drvdata->irq = r_irq.start; + drvdata->irq = irq; drvdata->serio = serio; drvdata->dev = dev; diff --git a/drivers/net/ethernet/arc/emac_main.c b/drivers/net/ethernet/arc/emac_main.c index 9e16014..d087852 100644 --- a/drivers/net/ethernet/arc/emac_main.c +++ b/drivers/net/ethernet/arc/emac_main.c @@ -628,12 +628,12 @@ static const struct net_device_ops arc_emac_netdev_ops = { static int arc_emac_probe(struct platform_device *pdev) { - struct resource res_regs, res_irq; + struct resource res_regs; struct device_node *phy_node; struct arc_emac_priv *priv; struct net_device *ndev; const char *mac_addr; - unsigned int id, clock_frequency; + unsigned int id, clock_frequency, irq; int err; if (!pdev->dev.of_node) @@ -661,8 +661,8 @@ static int arc_emac_probe(struct platform_device *pdev) } /* Get IRQ from device tree */ - err = of_irq_to_resource(pdev->dev.of_node, 0, &res_irq); - if (!err) { + irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + if (!irq) { dev_err(&pdev->dev, "failed to retrieve value from device tree\n"); return -ENODEV; } @@ -711,7 +711,7 @@ static int arc_emac_probe(struct platform_device *pdev) goto out; } - ndev->irq = res_irq.start; + ndev->irq = irq; dev_info(&pdev->dev, "IRQ is %d\n", ndev->irq); /* Register interrupt handler for device */ diff --git a/drivers/net/ethernet/freescale/fs_enet/mac-fcc.c b/drivers/net/ethernet/freescale/fs_enet/mac-fcc.c index 7583a95..10f781d 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mac-fcc.c +++ b/drivers/net/ethernet/freescale/fs_enet/mac-fcc.c @@ -88,7 +88,7 @@ static int do_pd_setup(struct fs_enet_private *fep) struct fs_platform_info *fpi = fep->fpi; int ret = -EINVAL; - fep->interrupt = of_irq_to_resource(ofdev->dev.of_node, 0, NULL); + fep->interrupt = irq_of_parse_and_map(ofdev->dev.of_node, 0); if (fep->interrupt == NO_IRQ) goto out; diff --git a/drivers/net/ethernet/freescale/fs_enet/mac-fec.c b/drivers/net/ethernet/freescale/fs_enet/mac-fec.c index 9ae6cdb..53a0c23 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mac-fec.c +++ b/drivers/net/ethernet/freescale/fs_enet/mac-fec.c @@ -98,7 +98,7 @@ static int do_pd_setup(struct fs_enet_private *fep) { struct platform_device *ofdev = to_platform_device(fep->dev); - fep->interrupt = of_irq_to_resource(ofdev->dev.of_node, 0, NULL); + fep->interrupt = irq_of_parse_and_map(ofdev->dev.of_node, 0); if (fep->interrupt == NO_IRQ) return -EINVAL; diff --git a/drivers/net/ethernet/freescale/fs_enet/mac-scc.c b/drivers/net/ethernet/freescale/fs_enet/mac-scc.c index 22a02a7..631f098 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mac-scc.c +++ b/drivers/net/ethernet/freescale/fs_enet/mac-scc.c @@ -98,7 +98,7 @@ static int do_pd_setup(struct fs_enet_private *fep) { struct platform_device *ofdev = to_platform_device(fep->dev); - fep->interrupt = of_irq_to_resource(ofdev->dev.of_node, 0, NULL); + fep->interrupt = irq_of_parse_and_map(ofdev->dev.of_node, 0); if (fep->interrupt == NO_IRQ) return -EINVAL; diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index b8f1103..3197d55 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -687,7 +687,7 @@ static int of_fsl_espi_probe(struct platform_device *ofdev) struct device_node *np = ofdev->dev.of_node; struct spi_master *master; struct resource mem; - struct resource irq; + unsigned int irq; int ret = -ENOMEM; ret = of_mpc8xxx_spi_probe(ofdev); @@ -702,13 +702,13 @@ static int of_fsl_espi_probe(struct platform_device *ofdev) if (ret) goto err; - ret = of_irq_to_resource(np, 0, &irq); + irq = irq_of_parse_and_map(np, 0); if (!ret) { ret = -EINVAL; goto err; } - master = fsl_espi_probe(dev, &mem, irq.start); + master = fsl_espi_probe(dev, &mem, irq); if (IS_ERR(master)) { ret = PTR_ERR(master); goto err; diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c index 1a535f7..6957f44 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c @@ -1207,7 +1207,7 @@ static int cpm_uart_init_port(struct device_node *np, pinfo->port.fifosize = pinfo->tx_nrfifos * pinfo->tx_fifosize; spin_lock_init(&pinfo->port.lock); - pinfo->port.irq = of_irq_to_resource(np, 0, NULL); + pinfo->port.irq = irq_of_parse_and_map(np, 0); if (pinfo->port.irq == NO_IRQ) { ret = -EINVAL; goto out_pram; -- cgit v0.10.2 From 16b84e5a505c790538e534ad8dfda9c288691e40 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 19 Sep 2013 16:44:55 -0500 Subject: of/irq: Create of_irq_parse_and_map_pci() to consolidate arch code. Several architectures open code effectively the same code block for finding and mapping PCI irqs. This patch consolidates it down to a single function. Signed-off-by: Grant Likely Acked-by: Michal Simek Cc: Russell King Cc: Ralf Baechle Cc: Benjamin Herrenschmidt diff --git a/arch/arm/mach-integrator/pci_v3.c b/arch/arm/mach-integrator/pci_v3.c index bb3aeb3..a87e510 100644 --- a/arch/arm/mach-integrator/pci_v3.c +++ b/arch/arm/mach-integrator/pci_v3.c @@ -835,21 +835,6 @@ static struct hw_pci pci_v3 __initdata = { #ifdef CONFIG_OF -static int __init pci_v3_map_irq_dt(const struct pci_dev *dev, u8 slot, u8 pin) -{ - struct of_phandle_args oirq; - int ret; - - ret = of_irq_parse_pci(dev, &oirq); - if (ret) { - dev_err(&dev->dev, "of_irq_parse_pci() %d\n", ret); - /* Proper return code 0 == NO_IRQ */ - return 0; - } - - return irq_create_of_mapping(&oirq); -} - static int __init pci_v3_dtprobe(struct platform_device *pdev, struct device_node *np) { @@ -918,7 +903,7 @@ static int __init pci_v3_dtprobe(struct platform_device *pdev, return -EINVAL; } - pci_v3.map_irq = pci_v3_map_irq_dt; + pci_v3.map_irq = of_irq_parse_and_map_pci; pci_common_init_dev(&pdev->dev, &pci_v3); return 0; diff --git a/arch/mips/pci/fixup-lantiq.c b/arch/mips/pci/fixup-lantiq.c index aef60e7..c2ce41e 100644 --- a/arch/mips/pci/fixup-lantiq.c +++ b/arch/mips/pci/fixup-lantiq.c @@ -25,15 +25,5 @@ int pcibios_plat_dev_init(struct pci_dev *dev) int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { - struct of_phandle_args dev_irq; - int irq; - - if (of_irq_parse_pci(dev, &dev_irq)) { - dev_err(&dev->dev, "trying to map irq for unknown slot:%d pin:%d\n", - slot, pin); - return 0; - } - irq = irq_create_of_mapping(&dev_irq); - dev_info(&dev->dev, "SLOT:%d PIN:%d IRQ:%d\n", slot, pin, irq); - return irq; + return of_irq_parse_and_map_pci(dev, slot, pin); } diff --git a/arch/mips/pci/pci-rt3883.c b/arch/mips/pci/pci-rt3883.c index eadc431..adeff2b 100644 --- a/arch/mips/pci/pci-rt3883.c +++ b/arch/mips/pci/pci-rt3883.c @@ -583,27 +583,7 @@ err_put_intc_node: int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { - struct of_phandle_args dev_irq; - int err; - int irq; - - err = of_irq_parse_pci(dev, &dev_irq); - if (err) { - pr_err("pci %s: unable to get irq map, err=%d\n", - pci_name((struct pci_dev *) dev), err); - return 0; - } - - irq = irq_create_of_mapping(&dev_irq); - - if (irq == 0) - pr_crit("pci %s: no irq found for pin %u\n", - pci_name((struct pci_dev *) dev), pin); - else - pr_info("pci %s: using irq %d for pin %u\n", - pci_name((struct pci_dev *) dev), irq, pin); - - return irq; + return of_irq_parse_and_map_pci(dev, slot, pin); } int pcibios_plat_dev_init(struct pci_dev *dev) diff --git a/arch/x86/kernel/devicetree.c b/arch/x86/kernel/devicetree.c index d39948f..69c826a 100644 --- a/arch/x86/kernel/devicetree.c +++ b/arch/x86/kernel/devicetree.c @@ -105,7 +105,6 @@ struct device_node *pcibios_get_phb_of_node(struct pci_bus *bus) static int x86_of_pci_irq_enable(struct pci_dev *dev) { - struct of_phandle_args oirq; u32 virq; int ret; u8 pin; @@ -116,11 +115,7 @@ static int x86_of_pci_irq_enable(struct pci_dev *dev) if (!pin) return 0; - ret = of_irq_parse_pci(dev, &oirq); - if (ret) - return ret; - - virq = irq_create_of_mapping(&oirq); + virq = of_irq_parse_and_map_pci(dev, 0, 0); if (virq == 0) return -EINVAL; dev->irq = virq; diff --git a/drivers/of/of_pci_irq.c b/drivers/of/of_pci_irq.c index 303afeb..8e92acd 100644 --- a/drivers/of/of_pci_irq.c +++ b/drivers/of/of_pci_irq.c @@ -94,3 +94,28 @@ int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq return of_irq_parse_raw(laddr, out_irq); } EXPORT_SYMBOL_GPL(of_irq_parse_pci); + +/** + * of_irq_parse_and_map_pci() - Decode a PCI irq from the device tree and map to a virq + * @dev: The pci device needing an irq + * @slot: PCI slot number; passed when used as map_irq callback. Unused + * @pin: PCI irq pin number; passed when used as map_irq callback. Unused + * + * @slot and @pin are unused, but included in the function so that this + * function can be used directly as the map_irq callback to pci_fixup_irqs(). + */ +int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin) +{ + struct of_phandle_args oirq; + int ret; + + ret = of_irq_parse_pci(dev, &oirq); + if (ret) { + dev_err(&dev->dev, "of_irq_parse_pci() failed with rc=%d\n", ret); + return 0; /* Proper return code 0 == NO_IRQ */ + } + + return irq_create_of_mapping(&oirq); +} +EXPORT_SYMBOL_GPL(of_irq_parse_and_map_pci); + diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index 3a8d01e..07ddb3a 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -645,18 +645,6 @@ static int __init mvebu_pcie_setup(int nr, struct pci_sys_data *sys) return 1; } -static int __init mvebu_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) -{ - struct of_phandle_args oirq; - int ret; - - ret = of_irq_parse_pci(dev, &oirq); - if (ret) - return ret; - - return irq_create_of_mapping(&oirq); -} - static struct pci_bus *mvebu_pcie_scan_bus(int nr, struct pci_sys_data *sys) { struct mvebu_pcie *pcie = sys_to_pcie(sys); @@ -705,7 +693,7 @@ static void __init mvebu_pcie_enable(struct mvebu_pcie *pcie) hw.private_data = (void **)&pcie; hw.setup = mvebu_pcie_setup; hw.scan = mvebu_pcie_scan_bus; - hw.map_irq = mvebu_pcie_map_irq; + hw.map_irq = of_irq_parse_and_map_pci; hw.ops = &mvebu_pcie_ops; hw.align_resource = mvebu_pcie_align_resource; diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h index f297237..1a1f5ff 100644 --- a/include/linux/of_pci.h +++ b/include/linux/of_pci.h @@ -7,6 +7,7 @@ struct pci_dev; struct of_phandle_args; int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq); +int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin); struct device_node; struct device_node *of_pci_find_child_device(struct device_node *parent, -- cgit v0.10.2 From f27446c3ad5b6d3b5b28ec0176e23d3ceca595d8 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 19 Sep 2013 23:34:26 -0500 Subject: microblaze/pci: Drop PowerPC-ism from irq parsing The Microblaze PCI code copied the PowerPC irq handling, but powerpc needs to handle broken device trees that are not present on Microblaze. This patch removes the powerpc special case and replaces it with a direct of_irq_parse_and_map_pci() call. Signed-off-by: Grant Likely Acked-by: Michal Simek diff --git a/arch/microblaze/include/asm/pci.h b/arch/microblaze/include/asm/pci.h index d52abb6..935f9be 100644 --- a/arch/microblaze/include/asm/pci.h +++ b/arch/microblaze/include/asm/pci.h @@ -127,8 +127,6 @@ extern void of_scan_pci_bridge(struct device_node *node, extern void of_scan_bus(struct device_node *node, struct pci_bus *bus); extern void of_rescan_bus(struct device_node *node, struct pci_bus *bus); -extern int pci_read_irq_line(struct pci_dev *dev); - extern int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap); diff --git a/arch/microblaze/pci/pci-common.c b/arch/microblaze/pci/pci-common.c index 60b386c..5060fc5 100644 --- a/arch/microblaze/pci/pci-common.c +++ b/arch/microblaze/pci/pci-common.c @@ -193,75 +193,6 @@ void pcibios_set_master(struct pci_dev *dev) } /* - * Reads the interrupt pin to determine if interrupt is use by card. - * If the interrupt is used, then gets the interrupt line from the - * openfirmware and sets it in the pci_dev and pci_config line. - */ -int pci_read_irq_line(struct pci_dev *pci_dev) -{ - struct of_phandle_args oirq; - unsigned int virq; - - /* The current device-tree that iSeries generates from the HV - * PCI informations doesn't contain proper interrupt routing, - * and all the fallback would do is print out crap, so we - * don't attempt to resolve the interrupts here at all, some - * iSeries specific fixup does it. - * - * In the long run, we will hopefully fix the generated device-tree - * instead. - */ - pr_debug("PCI: Try to map irq for %s...\n", pci_name(pci_dev)); - -#ifdef DEBUG - memset(&oirq, 0xff, sizeof(oirq)); -#endif - /* Try to get a mapping from the device-tree */ - if (of_irq_parse_pci(pci_dev, &oirq)) { - u8 line, pin; - - /* If that fails, lets fallback to what is in the config - * space and map that through the default controller. We - * also set the type to level low since that's what PCI - * interrupts are. If your platform does differently, then - * either provide a proper interrupt tree or don't use this - * function. - */ - if (pci_read_config_byte(pci_dev, PCI_INTERRUPT_PIN, &pin)) - return -1; - if (pin == 0) - return -1; - if (pci_read_config_byte(pci_dev, PCI_INTERRUPT_LINE, &line) || - line == 0xff || line == 0) { - return -1; - } - pr_debug(" No map ! Using line %d (pin %d) from PCI config\n", - line, pin); - - virq = irq_create_mapping(NULL, line); - if (virq) - irq_set_irq_type(virq, IRQ_TYPE_LEVEL_LOW); - } else { - pr_debug(" Got one, spec %d cells (0x%08x 0x%08x...) on %s\n", - oirq.args_count, oirq.args[0], oirq.args[1], - of_node_full_name(oirq.np)); - - virq = irq_create_of_mapping(&oirq); - } - if (!virq) { - pr_debug(" Failed to map !\n"); - return -1; - } - - pr_debug(" Mapped to linux irq %d\n", virq); - - pci_dev->irq = virq; - - return 0; -} -EXPORT_SYMBOL(pci_read_irq_line); - -/* * Platform support for /proc/bus/pci/X/Y mmap()s, * modelled on the sparc64 implementation by Dave Miller. * -- paulus. @@ -959,7 +890,7 @@ void pcibios_setup_bus_devices(struct pci_bus *bus) dev->dev.archdata.dma_data = (void *)PCI_DRAM_OFFSET; /* Read default IRQs and fixup if necessary */ - pci_read_irq_line(dev); + dev->irq = of_irq_parse_and_map_pci(dev, 0, 0); } } -- cgit v0.10.2 From 154bab12e1db824ad12ffd6df2d213dfb3d09878 Mon Sep 17 00:00:00 2001 From: Denis Carikli Date: Thu, 24 Oct 2013 14:13:48 +0200 Subject: ASoC: eukrea-tlv320: Use dev_err instead of pr_err. It also contains a minor style cleanup. Signed-off-by: Denis Carikli Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c index 9a4a0ca..5983740 100644 --- a/sound/soc/fsl/eukrea-tlv320.c +++ b/sound/soc/fsl/eukrea-tlv320.c @@ -42,7 +42,8 @@ static int eukrea_tlv320_hw_params(struct snd_pcm_substream *substream, SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); if (ret) { - pr_err("%s: failed set cpu dai format\n", __func__); + dev_err(cpu_dai->dev, + "Failed to set the cpu dai format.\n"); return ret; } @@ -50,14 +51,16 @@ static int eukrea_tlv320_hw_params(struct snd_pcm_substream *substream, SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); if (ret) { - pr_err("%s: failed set codec dai format\n", __func__); + dev_err(cpu_dai->dev, + "Failed to set the codec format.\n"); return ret; } ret = snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK, SND_SOC_CLOCK_OUT); if (ret) { - pr_err("%s: failed setting codec sysclk\n", __func__); + dev_err(cpu_dai->dev, + "Failed to set the codec sysclk.\n"); return ret; } snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffc, 0xffffffc, 2, 0); @@ -65,7 +68,8 @@ static int eukrea_tlv320_hw_params(struct snd_pcm_substream *substream, ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, SND_SOC_CLOCK_IN); if (ret) { - pr_err("can't set CPU system clock IMX_SSP_SYS_CLK\n"); + dev_err(cpu_dai->dev, + "Can't set the IMX_SSP_SYS_CLK CPU system clock.\n"); return ret; } @@ -155,7 +159,8 @@ static struct platform_driver eukrea_tlv320_driver = { .owner = THIS_MODULE, }, .probe = eukrea_tlv320_probe, - .remove = eukrea_tlv320_remove,}; + .remove = eukrea_tlv320_remove, +}; module_platform_driver(eukrea_tlv320_driver); -- cgit v0.10.2 From a5606f85611267047206d8ba055bc0e4ba166ad3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 24 Oct 2013 14:25:32 +0200 Subject: ALSA: Add ifdef CONFIG_GENERIC_ALLOCATOR for SNDRV_DMA_TYPE_IRAM code It turned out that we can't use gen_pool_*() functions on archs without CONFIG_GENERIC_ALLOCATOR (resulting in missing symbols), since linux/genalloc.h doesn't provide dummy functions for all. We'd be able to fix linux/genalloc.h size, but I take an easier path for now... Reported-by: Fengguang Wu Signed-off-by: Takashi Iwai diff --git a/include/sound/memalloc.h b/include/sound/memalloc.h index 510aec4..af99839 100644 --- a/include/sound/memalloc.h +++ b/include/sound/memalloc.h @@ -52,7 +52,11 @@ struct snd_dma_device { #else #define SNDRV_DMA_TYPE_DEV_SG SNDRV_DMA_TYPE_DEV /* no SG-buf support */ #endif +#ifdef CONFIG_GENERIC_ALLOCATOR #define SNDRV_DMA_TYPE_DEV_IRAM 4 /* generic device iram-buffer */ +#else +#define SNDRV_DMA_TYPE_DEV_IRAM SNDRV_DMA_TYPE_DEV +#endif /* * info for buffer allocation diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index 18c1d47..51a7921 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -238,6 +238,7 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, dmab->addr = 0; break; #ifdef CONFIG_HAS_DMA +#ifdef CONFIG_GENERIC_ALLOCATOR case SNDRV_DMA_TYPE_DEV_IRAM: snd_malloc_dev_iram(dmab, size); if (dmab->area) @@ -246,6 +247,7 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, * so if we fail to malloc, try to fetch memory traditionally. */ dmab->dev.type = SNDRV_DMA_TYPE_DEV; +#endif /* CONFIG_GENERIC_ALLOCATOR */ case SNDRV_DMA_TYPE_DEV: dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr); break; @@ -318,9 +320,11 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab) snd_free_pages(dmab->area, dmab->bytes); break; #ifdef CONFIG_HAS_DMA +#ifdef CONFIG_GENERIC_ALLOCATOR case SNDRV_DMA_TYPE_DEV_IRAM: snd_free_dev_iram(dmab); break; +#endif /* CONFIG_GENERIC_ALLOCATOR */ case SNDRV_DMA_TYPE_DEV: snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr); break; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 513f095..b71be57 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3199,12 +3199,14 @@ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *area) { area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; +#ifdef CONFIG_GENERIC_ALLOCATOR if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_IRAM) { area->vm_page_prot = pgprot_writecombine(area->vm_page_prot); return remap_pfn_range(area, area->vm_start, substream->dma_buffer.addr >> PAGE_SHIFT, area->vm_end - area->vm_start, area->vm_page_prot); } +#endif /* CONFIG_GENERIC_ALLOCATOR */ #ifdef ARCH_HAS_DMA_MMAP_COHERENT if (!substream->ops->page && substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) -- cgit v0.10.2 From 917f4b5cba78980a527098a910d94139d3e82c8d Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 24 Oct 2013 16:37:31 +0530 Subject: ALSA: compress: fix drain calls blocking other compress functions The drain and drain_notify callback were blocked by low level driver untill the draining was complete. Due to this being invoked with big fat mutex held, others ops like reading timestamp, calling pause, drop were blocked. So to fix this we add a new snd_compr_drain_notify() API. This would be required to be invoked by low level driver when drain or partial drain has been completed by the DSP. Thus we make the drain and partial_drain callback as non blocking and driver returns immediately after notifying DSP. The waiting is done while relasing the lock so that other ops can go ahead. Signed-off-by: Vinod Koul CC: stable@vger.kernel.org Signed-off-by: Takashi Iwai diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h index 9031a26..175ab32 100644 --- a/include/sound/compress_driver.h +++ b/include/sound/compress_driver.h @@ -48,6 +48,8 @@ struct snd_compr_ops; * the ring buffer * @total_bytes_transferred: cumulative bytes transferred by offload DSP * @sleep: poll sleep + * @wait: drain wait queue + * @drain_wake: condition for drain wake */ struct snd_compr_runtime { snd_pcm_state_t state; @@ -59,6 +61,8 @@ struct snd_compr_runtime { u64 total_bytes_available; u64 total_bytes_transferred; wait_queue_head_t sleep; + wait_queue_head_t wait; + unsigned int drain_wake; void *private_data; }; @@ -171,4 +175,12 @@ static inline void snd_compr_fragment_elapsed(struct snd_compr_stream *stream) wake_up(&stream->runtime->sleep); } +static inline void snd_compr_drain_notify(struct snd_compr_stream *stream) +{ + snd_BUG_ON(!stream); + + stream->runtime->drain_wake = 1; + wake_up(&stream->runtime->wait); +} + #endif diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index bea523a..3eb47d0 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -123,6 +123,7 @@ static int snd_compr_open(struct inode *inode, struct file *f) } runtime->state = SNDRV_PCM_STATE_OPEN; init_waitqueue_head(&runtime->sleep); + init_waitqueue_head(&runtime->wait); data->stream.runtime = runtime; f->private_data = (void *)data; mutex_lock(&compr->lock); @@ -682,12 +683,34 @@ static int snd_compr_stop(struct snd_compr_stream *stream) if (!retval) { stream->runtime->state = SNDRV_PCM_STATE_SETUP; wake_up(&stream->runtime->sleep); + snd_compr_drain_notify(stream); stream->runtime->total_bytes_available = 0; stream->runtime->total_bytes_transferred = 0; } return retval; } +static int snd_compress_wait_for_drain(struct snd_compr_stream *stream) +{ + /* + * We are called with lock held. So drop the lock while we wait for + * drain complete notfication from the driver + * + * It is expected that driver will notify the drain completion and then + * stream will be moved to SETUP state, even if draining resulted in an + * error. We can trigger next track after this. + */ + stream->runtime->state = SNDRV_PCM_STATE_DRAINING; + mutex_unlock(&stream->device->lock); + + wait_event(stream->runtime->wait, stream->runtime->drain_wake); + + wake_up(&stream->runtime->sleep); + mutex_lock(&stream->device->lock); + + return 0; +} + static int snd_compr_drain(struct snd_compr_stream *stream) { int retval; @@ -695,11 +718,17 @@ static int snd_compr_drain(struct snd_compr_stream *stream) if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || stream->runtime->state == SNDRV_PCM_STATE_SETUP) return -EPERM; + + stream->runtime->drain_wake = 0; retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN); - if (!retval) { - stream->runtime->state = SNDRV_PCM_STATE_DRAINING; + if (retval) { + pr_err("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval); wake_up(&stream->runtime->sleep); + return retval; } + + retval = snd_compress_wait_for_drain(stream); + stream->runtime->state = SNDRV_PCM_STATE_SETUP; return retval; } @@ -735,10 +764,16 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream) if (stream->next_track == false) return -EPERM; + stream->runtime->drain_wake = 0; retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN); + if (retval) { + pr_err("Partial drain returned failure\n"); + wake_up(&stream->runtime->sleep); + return retval; + } stream->next_track = false; - return retval; + return snd_compress_wait_for_drain(stream); } static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) -- cgit v0.10.2 From a52eaeb1898bc0589888ee62de291aa278379004 Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Thu, 24 Oct 2013 15:03:41 +0300 Subject: regmap: debugfs: Fix a boot time crash with early regmap init If called early enough, regmap_debugfs_init causes a crash, if the fs subsystem does not have its mount cache created yet. Even if this would work, the root node for the regmap debugfs is still missing, thus postpone the regmap_debugfs_init in this case until the root node is created. A special regmap_debugfs_early list is created for this purpose which is parsed later in the boot. Signed-off-by: Tero Kristo Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index de11eca..c5471cd 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -15,10 +15,19 @@ #include #include #include +#include #include "internal.h" +struct regmap_debugfs_node { + struct regmap *map; + const char *name; + struct list_head link; +}; + static struct dentry *regmap_debugfs_root; +static LIST_HEAD(regmap_debugfs_early_list); +static DEFINE_MUTEX(regmap_debugfs_early_lock); /* Calculate the length of a fixed format */ static size_t regmap_calc_reg_len(int max_val, char *buf, size_t buf_size) @@ -465,6 +474,20 @@ void regmap_debugfs_init(struct regmap *map, const char *name) struct rb_node *next; struct regmap_range_node *range_node; + /* If we don't have the debugfs root yet, postpone init */ + if (!regmap_debugfs_root) { + struct regmap_debugfs_node *node; + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + return; + node->map = map; + node->name = name; + mutex_lock(®map_debugfs_early_lock); + list_add(&node->link, ®map_debugfs_early_list); + mutex_unlock(®map_debugfs_early_lock); + return; + } + INIT_LIST_HEAD(&map->debugfs_off_cache); mutex_init(&map->cache_lock); @@ -519,18 +542,42 @@ void regmap_debugfs_init(struct regmap *map, const char *name) void regmap_debugfs_exit(struct regmap *map) { - debugfs_remove_recursive(map->debugfs); - mutex_lock(&map->cache_lock); - regmap_debugfs_free_dump_cache(map); - mutex_unlock(&map->cache_lock); - kfree(map->debugfs_name); + if (map->debugfs) { + debugfs_remove_recursive(map->debugfs); + mutex_lock(&map->cache_lock); + regmap_debugfs_free_dump_cache(map); + mutex_unlock(&map->cache_lock); + kfree(map->debugfs_name); + } else { + struct regmap_debugfs_node *node, *tmp; + + mutex_lock(®map_debugfs_early_lock); + list_for_each_entry_safe(node, tmp, ®map_debugfs_early_list, + link) { + if (node->map == map) { + list_del(&node->link); + kfree(node); + } + } + mutex_unlock(®map_debugfs_early_lock); + } } void regmap_debugfs_initcall(void) { + struct regmap_debugfs_node *node, *tmp; + regmap_debugfs_root = debugfs_create_dir("regmap", NULL); if (!regmap_debugfs_root) { pr_warn("regmap: Failed to create debugfs root\n"); return; } + + mutex_lock(®map_debugfs_early_lock); + list_for_each_entry_safe(node, tmp, ®map_debugfs_early_list, link) { + regmap_debugfs_init(node->map, node->name); + list_del(&node->link); + kfree(node); + } + mutex_unlock(®map_debugfs_early_lock); } -- cgit v0.10.2 From 88011c0911ee7a1b001393662b9884fb7f32eec6 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Thu, 24 Oct 2013 15:45:24 +0200 Subject: ALSA: hda/realtek - Raise the delay for alc283_shutup Some machine with 85ms delay might be happen pop noise when codec enter to D3. Raise up to 100ms delay will be match for more machine. Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 3016467c..dde858b 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2656,7 +2656,7 @@ static void alc283_shutup(struct hda_codec *codec) AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); if (hp_pin_sense) - msleep(85); + msleep(100); snd_hda_codec_write(codec, hp_pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); @@ -2665,7 +2665,7 @@ static void alc283_shutup(struct hda_codec *codec) alc_write_coef_idx(codec, 0x46, val | (3 << 12)); if (hp_pin_sense) - msleep(85); + msleep(100); snd_hda_shutup_pins(codec); alc_write_coef_idx(codec, 0x43, 0x9614); } -- cgit v0.10.2 From a861389a9f074d82081d0c7fbf30280a232c90d0 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 9 Oct 2013 16:12:37 +0300 Subject: ARM: OMAP4: use CLK_SET_RATE_PARENT for dss_dss_clk Set CLK_SET_RATE_PARENT flag for dss_dss_clk so that the DSS's fclk can be configured without the need to get the parent of the fclk. Signed-off-by: Tomi Valkeinen Signed-off-by: Paul Walmsley diff --git a/arch/arm/mach-omap2/cclock44xx_data.c b/arch/arm/mach-omap2/cclock44xx_data.c index 1d5b529..a27a0af 100644 --- a/arch/arm/mach-omap2/cclock44xx_data.c +++ b/arch/arm/mach-omap2/cclock44xx_data.c @@ -830,7 +830,8 @@ DEFINE_CLK_GATE(dss_tv_clk, "extalt_clkin_ck", &extalt_clkin_ck, 0x0, OMAP4430_CM_DSS_DSS_CLKCTRL, OMAP4430_OPTFCLKEN_TV_CLK_SHIFT, 0x0, NULL); -DEFINE_CLK_GATE(dss_dss_clk, "dpll_per_m5x2_ck", &dpll_per_m5x2_ck, 0x0, +DEFINE_CLK_GATE(dss_dss_clk, "dpll_per_m5x2_ck", &dpll_per_m5x2_ck, + CLK_SET_RATE_PARENT, OMAP4430_CM_DSS_DSS_CLKCTRL, OMAP4430_OPTFCLKEN_DSSCLK_SHIFT, 0x0, NULL); -- cgit v0.10.2 From 262c2c9d06585fb0ffc84da18e0f747836ce8baf Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 9 Oct 2013 16:12:38 +0300 Subject: ARM: OMAP3: use CLK_SET_RATE_PARENT for dss clocks Set CLK_SET_RATE_PARENT flag for dss1_alwon_fck_3430es2, dss1_alwon_fck_3430es1 and dpll4_m4x2_ck so that the DSS's fclk can be configured without the need to get the parent's parent of the fclk. Signed-off-by: Tomi Valkeinen Signed-off-by: Paul Walmsley diff --git a/arch/arm/mach-omap2/cclock3xxx_data.c b/arch/arm/mach-omap2/cclock3xxx_data.c index 334b767..a51dd75 100644 --- a/arch/arm/mach-omap2/cclock3xxx_data.c +++ b/arch/arm/mach-omap2/cclock3xxx_data.c @@ -869,7 +869,8 @@ static struct clk_hw_omap dpll4_m4x2_ck_hw = { .clkdm_name = "dpll4_clkdm", }; -DEFINE_STRUCT_CLK(dpll4_m4x2_ck, dpll4_m4x2_ck_parent_names, dpll4_m5x2_ck_ops); +DEFINE_STRUCT_CLK_FLAGS(dpll4_m4x2_ck, dpll4_m4x2_ck_parent_names, + dpll4_m5x2_ck_ops, CLK_SET_RATE_PARENT); static struct clk dpll4_m4x2_ck_3630 = { .name = "dpll4_m4x2_ck", @@ -877,6 +878,7 @@ static struct clk dpll4_m4x2_ck_3630 = { .parent_names = dpll4_m4x2_ck_parent_names, .num_parents = ARRAY_SIZE(dpll4_m4x2_ck_parent_names), .ops = &dpll4_m5x2_ck_3630_ops, + .flags = CLK_SET_RATE_PARENT, }; DEFINE_CLK_DIVIDER(dpll4_m6_ck, "dpll4_ck", &dpll4_ck, 0x0, @@ -968,8 +970,9 @@ static struct clk_hw_omap dss1_alwon_fck_3430es1_hw = { .clkdm_name = "dss_clkdm", }; -DEFINE_STRUCT_CLK(dss1_alwon_fck_3430es1, dss1_alwon_fck_3430es1_parent_names, - aes2_ick_ops); +DEFINE_STRUCT_CLK_FLAGS(dss1_alwon_fck_3430es1, + dss1_alwon_fck_3430es1_parent_names, aes2_ick_ops, + CLK_SET_RATE_PARENT); static struct clk dss1_alwon_fck_3430es2; @@ -983,8 +986,9 @@ static struct clk_hw_omap dss1_alwon_fck_3430es2_hw = { .clkdm_name = "dss_clkdm", }; -DEFINE_STRUCT_CLK(dss1_alwon_fck_3430es2, dss1_alwon_fck_3430es1_parent_names, - aes2_ick_ops); +DEFINE_STRUCT_CLK_FLAGS(dss1_alwon_fck_3430es2, + dss1_alwon_fck_3430es1_parent_names, aes2_ick_ops, + CLK_SET_RATE_PARENT); static struct clk dss2_alwon_fck; -- cgit v0.10.2 From 4ff7e3b65c8e1d8062365296b738fd262cfc2e9c Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 9 Oct 2013 16:12:39 +0300 Subject: ARM: OMAP3: fix dpll4_m3_ck and dpll4_m4_ck dividers dpll4_m3_ck and dpll4_m4_ck have divider bit fields which are 6 bits wide. However, only values from 1 to 32 are allowed. This means we have to add a divider tables and list the dividers explicitly. I believe the same issue is there for other dpll4_mx_ck clocks, but as I'm not familiar with them, I didn't touch them. Signed-off-by: Tomi Valkeinen Signed-off-by: Paul Walmsley diff --git a/arch/arm/mach-omap2/cclock3xxx_data.c b/arch/arm/mach-omap2/cclock3xxx_data.c index a51dd75..e94d635 100644 --- a/arch/arm/mach-omap2/cclock3xxx_data.c +++ b/arch/arm/mach-omap2/cclock3xxx_data.c @@ -381,6 +381,42 @@ static struct clk_hw_omap dpll4_ck_hw = { DEFINE_STRUCT_CLK(dpll4_ck, dpll3_ck_parent_names, dpll4_ck_ops); +static const struct clk_div_table dpll4_mx_ck_div_table[] = { + { .div = 1, .val = 1 }, + { .div = 2, .val = 2 }, + { .div = 3, .val = 3 }, + { .div = 4, .val = 4 }, + { .div = 5, .val = 5 }, + { .div = 6, .val = 6 }, + { .div = 7, .val = 7 }, + { .div = 8, .val = 8 }, + { .div = 9, .val = 9 }, + { .div = 10, .val = 10 }, + { .div = 11, .val = 11 }, + { .div = 12, .val = 12 }, + { .div = 13, .val = 13 }, + { .div = 14, .val = 14 }, + { .div = 15, .val = 15 }, + { .div = 16, .val = 16 }, + { .div = 17, .val = 17 }, + { .div = 18, .val = 18 }, + { .div = 19, .val = 19 }, + { .div = 20, .val = 20 }, + { .div = 21, .val = 21 }, + { .div = 22, .val = 22 }, + { .div = 23, .val = 23 }, + { .div = 24, .val = 24 }, + { .div = 25, .val = 25 }, + { .div = 26, .val = 26 }, + { .div = 27, .val = 27 }, + { .div = 28, .val = 28 }, + { .div = 29, .val = 29 }, + { .div = 30, .val = 30 }, + { .div = 31, .val = 31 }, + { .div = 32, .val = 32 }, + { .div = 0 }, +}; + DEFINE_CLK_DIVIDER(dpll4_m5_ck, "dpll4_ck", &dpll4_ck, 0x0, OMAP_CM_REGADDR(OMAP3430_CAM_MOD, CM_CLKSEL), OMAP3430_CLKSEL_CAM_SHIFT, OMAP3630_CLKSEL_CAM_WIDTH, @@ -524,10 +560,10 @@ static const struct clksel_rate clkout2_src_54m_rates[] = { { .div = 0 } }; -DEFINE_CLK_DIVIDER(dpll4_m3_ck, "dpll4_ck", &dpll4_ck, 0x0, +DEFINE_CLK_DIVIDER_TABLE(dpll4_m3_ck, "dpll4_ck", &dpll4_ck, 0x0, OMAP_CM_REGADDR(OMAP3430_DSS_MOD, CM_CLKSEL), OMAP3430_CLKSEL_TV_SHIFT, OMAP3630_CLKSEL_TV_WIDTH, - CLK_DIVIDER_ONE_BASED, NULL); + 0, dpll4_mx_ck_div_table, NULL); static struct clk dpll4_m3x2_ck; @@ -847,10 +883,10 @@ static struct clk dpll3_m3x2_ck_3630 = { DEFINE_CLK_FIXED_FACTOR(dpll3_x2_ck, "dpll3_ck", &dpll3_ck, 0x0, 2, 1); -DEFINE_CLK_DIVIDER(dpll4_m4_ck, "dpll4_ck", &dpll4_ck, 0x0, +DEFINE_CLK_DIVIDER_TABLE(dpll4_m4_ck, "dpll4_ck", &dpll4_ck, 0x0, OMAP_CM_REGADDR(OMAP3430_DSS_MOD, CM_CLKSEL), OMAP3430_CLKSEL_DSS1_SHIFT, OMAP3630_CLKSEL_DSS1_WIDTH, - CLK_DIVIDER_ONE_BASED, NULL); + 0, dpll4_mx_ck_div_table, NULL); static struct clk dpll4_m4x2_ck; -- cgit v0.10.2 From 307229d2ac5f60447cc655ebbce5d9c0baa18bbc Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Thu, 24 Oct 2013 21:10:34 +0300 Subject: ALSA: hda - hdmi: Allow HDA patches to customize more operations Upcoming AMD multichannel support requires many customized operations (channel mapping, ELD, HBR) but can otherwise share most of its code with the generic patch. Add a local struct hdmi_ops containing customizable HDMI-specific callbacks and move the current code to those callbacks. Functionality is unaltered. Signed-off-by: Anssi Hannula Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 0f9b103..acc00fe 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -82,6 +82,39 @@ struct hdmi_spec_per_pin { #endif }; +struct cea_channel_speaker_allocation; + +/* operations used by generic code that can be overridden by patches */ +struct hdmi_ops { + int (*pin_get_eld)(struct hda_codec *codec, hda_nid_t pin_nid, + unsigned char *buf, int *eld_size); + + /* get and set channel assigned to each HDMI ASP (audio sample packet) slot */ + int (*pin_get_slot_channel)(struct hda_codec *codec, hda_nid_t pin_nid, + int asp_slot); + int (*pin_set_slot_channel)(struct hda_codec *codec, hda_nid_t pin_nid, + int asp_slot, int channel); + + void (*pin_setup_infoframe)(struct hda_codec *codec, hda_nid_t pin_nid, + int ca, int active_channels, int conn_type); + + /* enable/disable HBR (HD passthrough) */ + int (*pin_hbr_setup)(struct hda_codec *codec, hda_nid_t pin_nid, bool hbr); + + int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid, + hda_nid_t pin_nid, u32 stream_tag, int format); + + /* Helpers for producing the channel map TLVs. These can be overridden + * for devices that have non-standard mapping requirements. */ + int (*chmap_cea_alloc_validate_get_type)(struct cea_channel_speaker_allocation *cap, + int channels); + void (*cea_alloc_to_tlv_chmap)(struct cea_channel_speaker_allocation *cap, + unsigned int *chmap, int channels); + + /* check that the user-given chmap is supported */ + int (*chmap_validate)(int ca, int channels, unsigned char *chmap); +}; + struct hdmi_spec { int num_cvts; struct snd_array cvts; /* struct hdmi_spec_per_cvt */ @@ -93,6 +126,7 @@ struct hdmi_spec { unsigned int channels_max; /* max over all cvts */ struct hdmi_eld temp_eld; + struct hdmi_ops ops; /* * Non-generic ATI/NVIDIA specific */ @@ -648,24 +682,24 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec, hda_nid_t pin_nid) { #ifdef CONFIG_SND_DEBUG_VERBOSE + struct hdmi_spec *spec = codec->spec; int i; - int slot; + int channel; for (i = 0; i < 8; i++) { - slot = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_CHAN_SLOT, i); + channel = spec->ops.pin_get_slot_channel(codec, pin_nid, i); printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n", - slot >> 4, slot & 0xf); + channel, i); } #endif } - static void hdmi_std_setup_channel_mapping(struct hda_codec *codec, hda_nid_t pin_nid, bool non_pcm, int ca) { + struct hdmi_spec *spec = codec->spec; struct cea_channel_speaker_allocation *ch_alloc; int i; int err; @@ -698,9 +732,10 @@ static void hdmi_std_setup_channel_mapping(struct hda_codec *codec, } for (i = 0; i < 8; i++) { - err = snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_HDMI_CHAN_SLOT, - non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]); + int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]; + int hdmi_slot = slotsetup & 0x0f; + int channel = (slotsetup & 0xf0) >> 4; + err = spec->ops.pin_set_slot_channel(codec, pin_nid, hdmi_slot, channel); if (err) { snd_printdd(KERN_NOTICE "HDMI: channel mapping failed\n"); @@ -810,6 +845,7 @@ static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec, int chs, unsigned char *map, int ca) { + struct hdmi_spec *spec = codec->spec; int ordered_ca = get_channel_allocation_order(ca); int alsa_pos, hdmi_slot; int assignments[8] = {[0 ... 7] = 0xf}; @@ -825,11 +861,10 @@ static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec, } for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) { - int val, err; + int err; - val = (assignments[hdmi_slot] << 4) | hdmi_slot; - err = snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_HDMI_CHAN_SLOT, val); + err = spec->ops.pin_set_slot_channel(codec, pin_nid, hdmi_slot, + assignments[hdmi_slot]); if (err) return -EINVAL; } @@ -865,6 +900,22 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec, hdmi_debug_channel_mapping(codec, pin_nid); } +static int hdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid, + int asp_slot, int channel) +{ + return snd_hda_codec_write(codec, pin_nid, 0, + AC_VERB_SET_HDMI_CHAN_SLOT, + (channel << 4) | asp_slot); +} + +static int hdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid, + int asp_slot) +{ + return (snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_HDMI_CHAN_SLOT, + asp_slot) & 0xf0) >> 4; +} + /* * Audio InfoFrame routines */ @@ -986,16 +1037,64 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, return true; } +static void hdmi_pin_setup_infoframe(struct hda_codec *codec, + hda_nid_t pin_nid, + int ca, int active_channels, + int conn_type) +{ + union audio_infoframe ai; + + if (conn_type == 0) { /* HDMI */ + struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi; + + hdmi_ai->type = 0x84; + hdmi_ai->ver = 0x01; + hdmi_ai->len = 0x0a; + hdmi_ai->CC02_CT47 = active_channels - 1; + hdmi_ai->CA = ca; + hdmi_checksum_audio_infoframe(hdmi_ai); + } else if (conn_type == 1) { /* DisplayPort */ + struct dp_audio_infoframe *dp_ai = &ai.dp; + + dp_ai->type = 0x84; + dp_ai->len = 0x1b; + dp_ai->ver = 0x11 << 2; + dp_ai->CC02_CT47 = active_channels - 1; + dp_ai->CA = ca; + } else { + snd_printd("HDMI: unknown connection type at pin %d\n", + pin_nid); + return; + } + + /* + * sizeof(ai) is used instead of sizeof(*hdmi_ai) or + * sizeof(*dp_ai) to avoid partial match/update problems when + * the user switches between HDMI/DP monitors. + */ + if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes, + sizeof(ai))) { + snd_printdd("hdmi_pin_setup_infoframe: " + "pin=%d channels=%d ca=0x%02x\n", + pin_nid, + active_channels, ca); + hdmi_stop_infoframe_trans(codec, pin_nid); + hdmi_fill_audio_infoframe(codec, pin_nid, + ai.bytes, sizeof(ai)); + hdmi_start_infoframe_trans(codec, pin_nid); + } +} + static void hdmi_setup_audio_infoframe(struct hda_codec *codec, struct hdmi_spec_per_pin *per_pin, bool non_pcm) { + struct hdmi_spec *spec = codec->spec; hda_nid_t pin_nid = per_pin->pin_nid; int channels = per_pin->channels; int active_channels; struct hdmi_eld *eld; int ca, ordered_ca; - union audio_infoframe ai; if (!channels) return; @@ -1021,30 +1120,6 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hdmi_set_channel_count(codec, per_pin->cvt_nid, active_channels); - memset(&ai, 0, sizeof(ai)); - if (eld->info.conn_type == 0) { /* HDMI */ - struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi; - - hdmi_ai->type = 0x84; - hdmi_ai->ver = 0x01; - hdmi_ai->len = 0x0a; - hdmi_ai->CC02_CT47 = active_channels - 1; - hdmi_ai->CA = ca; - hdmi_checksum_audio_infoframe(hdmi_ai); - } else if (eld->info.conn_type == 1) { /* DisplayPort */ - struct dp_audio_infoframe *dp_ai = &ai.dp; - - dp_ai->type = 0x84; - dp_ai->len = 0x1b; - dp_ai->ver = 0x11 << 2; - dp_ai->CC02_CT47 = active_channels - 1; - dp_ai->CA = ca; - } else { - snd_printd("HDMI: unknown connection type at pin %d\n", - pin_nid); - return; - } - /* * always configure channel mapping, it may have been changed by the * user in the meantime @@ -1053,27 +1128,12 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, channels, per_pin->chmap, per_pin->chmap_set); - /* - * sizeof(ai) is used instead of sizeof(*hdmi_ai) or - * sizeof(*dp_ai) to avoid partial match/update problems when - * the user switches between HDMI/DP monitors. - */ - if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes, - sizeof(ai))) { - snd_printdd("hdmi_setup_audio_infoframe: " - "pin=%d channels=%d ca=0x%02x\n", - pin_nid, - active_channels, ca); - hdmi_stop_infoframe_trans(codec, pin_nid); - hdmi_fill_audio_infoframe(codec, pin_nid, - ai.bytes, sizeof(ai)); - hdmi_start_infoframe_trans(codec, pin_nid); - } + spec->ops.pin_setup_infoframe(codec, pin_nid, ca, active_channels, + eld->info.conn_type); per_pin->non_pcm = non_pcm; } - /* * Unsolicited events */ @@ -1176,26 +1236,22 @@ static void haswell_verify_D0(struct hda_codec *codec, #define is_hbr_format(format) \ ((format & AC_FMT_TYPE_NON_PCM) && (format & AC_FMT_CHAN_MASK) == 7) -static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, - hda_nid_t pin_nid, u32 stream_tag, int format) +static int hdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid, + bool hbr) { - int pinctl; - int new_pinctl = 0; - - if (is_haswell(codec)) - haswell_verify_D0(codec, cvt_nid, pin_nid); + int pinctl, new_pinctl; if (snd_hda_query_pin_caps(codec, pin_nid) & AC_PINCAP_HBR) { pinctl = snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); new_pinctl = pinctl & ~AC_PINCTL_EPT; - if (is_hbr_format(format)) + if (hbr) new_pinctl |= AC_PINCTL_EPT_HBR; else new_pinctl |= AC_PINCTL_EPT_NATIVE; - snd_printdd("hdmi_setup_stream: " + snd_printdd("hdmi_pin_hbr_setup: " "NID=0x%x, %spinctl=0x%x\n", pin_nid, pinctl == new_pinctl ? "" : "new-", @@ -1205,11 +1261,26 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, new_pinctl); + } else if (hbr) + return -EINVAL; - } - if (is_hbr_format(format) && !new_pinctl) { + return 0; +} + +static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, + hda_nid_t pin_nid, u32 stream_tag, int format) +{ + struct hdmi_spec *spec = codec->spec; + int err; + + if (is_haswell(codec)) + haswell_verify_D0(codec, cvt_nid, pin_nid); + + err = spec->ops.pin_hbr_setup(codec, pin_nid, is_hbr_format(format)); + + if (err) { snd_printdd("hdmi_setup_stream: HBR is not supported\n"); - return -EINVAL; + return err; } snd_hda_codec_setup_stream(codec, cvt_nid, stream_tag, 0, format); @@ -1424,7 +1495,7 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) codec->addr, pin_nid, pin_eld->monitor_present, eld->eld_valid); if (eld->eld_valid) { - if (snd_hdmi_get_eld(codec, pin_nid, eld->eld_buffer, + if (spec->ops.pin_get_eld(codec, pin_nid, eld->eld_buffer, &eld->eld_size) < 0) eld->eld_valid = false; else { @@ -1654,7 +1725,7 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, hdmi_setup_audio_infoframe(codec, per_pin, non_pcm); mutex_unlock(&per_pin->lock); - return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); + return spec->ops.setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); } static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, @@ -1726,6 +1797,34 @@ static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol, return 0; } +static int hdmi_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap, + int channels) +{ + /* If the speaker allocation matches the channel count, it is OK.*/ + if (cap->channels != channels) + return -1; + + /* all channels are remappable freely */ + return SNDRV_CTL_TLVT_CHMAP_VAR; +} + +static void hdmi_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *cap, + unsigned int *chmap, int channels) +{ + int count = 0; + int c; + + for (c = 7; c >= 0; c--) { + int spk = cap->speakers[c]; + if (!spk) + continue; + + chmap[count++] = spk_to_chmap(spk); + } + + WARN_ON(count != channels); +} + static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *tlv) { @@ -1742,16 +1841,19 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, size -= 8; dst = tlv + 2; for (chs = 2; chs <= spec->channels_max; chs++) { - int i, c; + int i; struct cea_channel_speaker_allocation *cap; cap = channel_allocations; for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) { int chs_bytes = chs * 4; - if (cap->channels != chs) + int type = spec->ops.chmap_cea_alloc_validate_get_type(cap, chs); + unsigned int tlv_chmap[8]; + + if (type < 0) continue; if (size < 8) return -ENOMEM; - if (put_user(SNDRV_CTL_TLVT_CHMAP_VAR, dst) || + if (put_user(type, dst) || put_user(chs_bytes, dst + 1)) return -EFAULT; dst += 2; @@ -1761,14 +1863,10 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, return -ENOMEM; size -= chs_bytes; count += chs_bytes; - for (c = 7; c >= 0; c--) { - int spk = cap->speakers[c]; - if (!spk) - continue; - if (put_user(spk_to_chmap(spk), dst)) - return -EFAULT; - dst++; - } + spec->ops.cea_alloc_to_tlv_chmap(cap, tlv_chmap, chs); + if (copy_to_user(dst, tlv_chmap, chs_bytes)) + return -EFAULT; + dst += chs; } } if (put_user(count, tlv + 1)) @@ -1802,7 +1900,7 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, unsigned int ctl_idx; struct snd_pcm_substream *substream; unsigned char chmap[8]; - int i, ca, prepared = 0; + int i, err, ca, prepared = 0; ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); substream = snd_pcm_chmap_substream(info, ctl_idx); @@ -1826,6 +1924,11 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); if (ca < 0) return -EINVAL; + if (spec->ops.chmap_validate) { + err = spec->ops.chmap_validate(ca, ARRAY_SIZE(chmap), chmap); + if (err) + return err; + } mutex_lock(&per_pin->lock); per_pin->chmap_set = true; memcpy(per_pin->chmap, chmap, sizeof(chmap)); @@ -2032,6 +2135,17 @@ static const struct hda_codec_ops generic_hdmi_patch_ops = { #endif }; +static const struct hdmi_ops generic_standard_hdmi_ops = { + .pin_get_eld = snd_hdmi_get_eld, + .pin_get_slot_channel = hdmi_pin_get_slot_channel, + .pin_set_slot_channel = hdmi_pin_set_slot_channel, + .pin_setup_infoframe = hdmi_pin_setup_infoframe, + .pin_hbr_setup = hdmi_pin_hbr_setup, + .setup_stream = hdmi_setup_stream, + .chmap_cea_alloc_validate_get_type = hdmi_chmap_cea_alloc_validate_get_type, + .cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap, +}; + static void intel_haswell_fixup_connect_list(struct hda_codec *codec, hda_nid_t nid) @@ -2114,6 +2228,7 @@ static int patch_generic_hdmi(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + spec->ops = generic_standard_hdmi_ops; codec->spec = spec; hdmi_array_init(spec, 4); -- cgit v0.10.2 From 5a61358433b1f89b500f2c365746c73cb7a27e2f Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Thu, 24 Oct 2013 21:10:35 +0300 Subject: ALSA: hda - hdmi: Add ATI/AMD multi-channel audio support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ATI/AMD codecs do not support all the standard HDA HDMI/DP functions, instead various vendor-specific verbs are provided. This commit addresses these missing functions: - standard channel mapping support - standard infoframe configuration support ATI/AMD provides their own verbs that allow the following: - setting CA for infoframe - setting down-mix information for infoframe - channel pair remapping - individual channel remapping (revision ID 3+, 0x100300+) The documentation for the verbs has now been released by AMD: http://www.x.org/docs/AMD/AMD_HDA_verbs_v2.pdf Add support for the ATI/AMD specific verbs and use them instead of the generic methods on ATI/AMD codecs. This allows multi-channel PCM audio to work. Channel remapping is restricted to pairwise mapping on codecs with revision ID 2 (0x100200 as reported by procfs codec#X) or lower. This means cards up to Radeon HD7670 as far as I know. This will not affect standard multi-channel modes since these codecs support automatic FC-LFE swapping for HDMI. ATI/AMD codecs do not advertise all of their supported rates, formats and channel counts, therefore that information is forced accordingly so that all HDMI 1.x PCM parameters are marked as supported. Support for multiple ports is also added to patch_atihdmi so that 0x1002aa01 codecs with multiple ports will work properly when switched back to that patch. v2: splitted ELD emulation to a separate patch, tlv fixes v3: adapted to the new hdmi_ops infrastructure, fixed rev3+ vendor id Signed-off-by: Anssi Hannula Tested-by: Peter Frühberger # v2 Tested-by: Olivier Langlois # v2+rev3fix Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index acc00fe..1114be0 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -6,6 +6,7 @@ * Copyright (c) 2006 ATI Technologies Inc. * Copyright (c) 2008 NVIDIA Corp. All rights reserved. * Copyright (c) 2008 Wei Ni + * Copyright (c) 2013 Anssi Hannula * * Authors: * Wu Fengguang @@ -128,7 +129,7 @@ struct hdmi_spec { struct hdmi_eld temp_eld; struct hdmi_ops ops; /* - * Non-generic ATI/NVIDIA specific + * Non-generic VIA/NVIDIA specific */ struct hda_multi_out multiout; struct hda_pcm_stream pcm_playback; @@ -2784,49 +2785,292 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) } /* - * ATI-specific implementations - * - * FIXME: we may omit the whole this and use the generic code once after - * it's confirmed to work. + * ATI/AMD-specific implementations */ -#define ATIHDMI_CVT_NID 0x02 /* audio converter */ -#define ATIHDMI_PIN_NID 0x03 /* HDMI output pin */ +#define is_amdhdmi_rev3_or_later(codec) \ + ((codec)->vendor_id == 0x1002aa01 && ((codec)->revision_id & 0xff00) >= 0x0300) +#define has_amd_full_remap_support(codec) is_amdhdmi_rev3_or_later(codec) + +/* ATI/AMD specific HDA pin verbs, see the AMD HDA Verbs specification */ +#define ATI_VERB_SET_CHANNEL_ALLOCATION 0x771 +#define ATI_VERB_SET_DOWNMIX_INFO 0x772 +#define ATI_VERB_SET_MULTICHANNEL_01 0x777 +#define ATI_VERB_SET_MULTICHANNEL_23 0x778 +#define ATI_VERB_SET_MULTICHANNEL_45 0x779 +#define ATI_VERB_SET_MULTICHANNEL_67 0x77a +#define ATI_VERB_SET_MULTICHANNEL_1 0x785 +#define ATI_VERB_SET_MULTICHANNEL_3 0x786 +#define ATI_VERB_SET_MULTICHANNEL_5 0x787 +#define ATI_VERB_SET_MULTICHANNEL_7 0x788 +#define ATI_VERB_SET_MULTICHANNEL_MODE 0x789 +#define ATI_VERB_GET_CHANNEL_ALLOCATION 0xf71 +#define ATI_VERB_GET_DOWNMIX_INFO 0xf72 +#define ATI_VERB_GET_MULTICHANNEL_01 0xf77 +#define ATI_VERB_GET_MULTICHANNEL_23 0xf78 +#define ATI_VERB_GET_MULTICHANNEL_45 0xf79 +#define ATI_VERB_GET_MULTICHANNEL_67 0xf7a +#define ATI_VERB_GET_MULTICHANNEL_1 0xf85 +#define ATI_VERB_GET_MULTICHANNEL_3 0xf86 +#define ATI_VERB_GET_MULTICHANNEL_5 0xf87 +#define ATI_VERB_GET_MULTICHANNEL_7 0xf88 +#define ATI_VERB_GET_MULTICHANNEL_MODE 0xf89 + +#define ATI_OUT_ENABLE 0x1 + +#define ATI_MULTICHANNEL_MODE_PAIRED 0 +#define ATI_MULTICHANNEL_MODE_SINGLE 1 + +static void atihdmi_pin_setup_infoframe(struct hda_codec *codec, hda_nid_t pin_nid, int ca, + int active_channels, int conn_type) +{ + snd_hda_codec_write(codec, pin_nid, 0, ATI_VERB_SET_CHANNEL_ALLOCATION, ca); +} + +static int atihdmi_paired_swap_fc_lfe(int pos) +{ + /* + * ATI/AMD have automatic FC/LFE swap built-in + * when in pairwise mapping mode. + */ + + switch (pos) { + /* see channel_allocations[].speakers[] */ + case 2: return 3; + case 3: return 2; + default: break; + } + + return pos; +} + +static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map) +{ + struct cea_channel_speaker_allocation *cap; + int i, j; + + /* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */ + + cap = &channel_allocations[get_channel_allocation_order(ca)]; + for (i = 0; i < chs; ++i) { + int mask = to_spk_mask(map[i]); + bool ok = false; + bool companion_ok = false; + + if (!mask) + continue; + + for (j = 0 + i % 2; j < 8; j += 2) { + int chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j); + if (cap->speakers[chan_idx] == mask) { + /* channel is in a supported position */ + ok = true; + + if (i % 2 == 0 && i + 1 < chs) { + /* even channel, check the odd companion */ + int comp_chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j + 1); + int comp_mask_req = to_spk_mask(map[i+1]); + int comp_mask_act = cap->speakers[comp_chan_idx]; + + if (comp_mask_req == comp_mask_act) + companion_ok = true; + else + return -EINVAL; + } + break; + } + } + + if (!ok) + return -EINVAL; + + if (companion_ok) + i++; /* companion channel already checked */ + } + + return 0; +} + +static int atihdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid, + int hdmi_slot, int stream_channel) +{ + int verb; + int ati_channel_setup = 0; + + if (hdmi_slot > 7) + return -EINVAL; + + if (!has_amd_full_remap_support(codec)) { + hdmi_slot = atihdmi_paired_swap_fc_lfe(hdmi_slot); + + /* In case this is an odd slot but without stream channel, do not + * disable the slot since the corresponding even slot could have a + * channel. In case neither have a channel, the slot pair will be + * disabled when this function is called for the even slot. */ + if (hdmi_slot % 2 != 0 && stream_channel == 0xf) + return 0; + + hdmi_slot -= hdmi_slot % 2; + + if (stream_channel != 0xf) + stream_channel -= stream_channel % 2; + } + + verb = ATI_VERB_SET_MULTICHANNEL_01 + hdmi_slot/2 + (hdmi_slot % 2) * 0x00e; + + /* ati_channel_setup format: [7..4] = stream_channel_id, [1] = mute, [0] = enable */ + + if (stream_channel != 0xf) + ati_channel_setup = (stream_channel << 4) | ATI_OUT_ENABLE; + + return snd_hda_codec_write(codec, pin_nid, 0, verb, ati_channel_setup); +} + +static int atihdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid, + int asp_slot) +{ + bool was_odd = false; + int ati_asp_slot = asp_slot; + int verb; + int ati_channel_setup; + + if (asp_slot > 7) + return -EINVAL; + + if (!has_amd_full_remap_support(codec)) { + ati_asp_slot = atihdmi_paired_swap_fc_lfe(asp_slot); + if (ati_asp_slot % 2 != 0) { + ati_asp_slot -= 1; + was_odd = true; + } + } + + verb = ATI_VERB_GET_MULTICHANNEL_01 + ati_asp_slot/2 + (ati_asp_slot % 2) * 0x00e; + + ati_channel_setup = snd_hda_codec_read(codec, pin_nid, 0, verb, 0); + + if (!(ati_channel_setup & ATI_OUT_ENABLE)) + return 0xf; + + return ((ati_channel_setup & 0xf0) >> 4) + !!was_odd; +} -static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) +static int atihdmi_paired_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap, + int channels) +{ + int c; + + /* + * Pre-rev3 ATI/AMD codecs operate in a paired channel mode, so + * we need to take that into account (a single channel may take 2 + * channel slots if we need to carry a silent channel next to it). + * On Rev3+ AMD codecs this function is not used. + */ + int chanpairs = 0; + + /* We only produce even-numbered channel count TLVs */ + if ((channels % 2) != 0) + return -1; + + for (c = 0; c < 7; c += 2) { + if (cap->speakers[c] || cap->speakers[c+1]) + chanpairs++; + } + + if (chanpairs * 2 != channels) + return -1; + + return SNDRV_CTL_TLVT_CHMAP_PAIRED; +} + +static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *cap, + unsigned int *chmap, int channels) +{ + /* produce paired maps for pre-rev3 ATI/AMD codecs */ + int count = 0; + int c; + + for (c = 7; c >= 0; c--) { + int chan = 7 - atihdmi_paired_swap_fc_lfe(7 - c); + int spk = cap->speakers[chan]; + if (!spk) { + /* add N/A channel if the companion channel is occupied */ + if (cap->speakers[chan + (chan % 2 ? -1 : 1)]) + chmap[count++] = SNDRV_CHMAP_NA; + + continue; + } + + chmap[count++] = spk_to_chmap(spk); + } + + WARN_ON(count != channels); +} + +static int atihdmi_init(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_cvt *per_cvt = get_cvt(spec, 0); - int chans = substream->runtime->channels; - int i, err; + int pin_idx, err; - err = simple_playback_pcm_prepare(hinfo, codec, stream_tag, format, - substream); - if (err < 0) + err = generic_hdmi_init(codec); + + if (err) return err; - snd_hda_codec_write(codec, per_cvt->cvt_nid, 0, - AC_VERB_SET_CVT_CHAN_COUNT, chans - 1); - /* FIXME: XXX */ - for (i = 0; i < chans; i++) { - snd_hda_codec_write(codec, per_cvt->cvt_nid, 0, - AC_VERB_SET_HDMI_CHAN_SLOT, - (i << 4) | i); + + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); + + /* make sure downmix information in infoframe is zero */ + snd_hda_codec_write(codec, per_pin->pin_nid, 0, ATI_VERB_SET_DOWNMIX_INFO, 0); + + /* enable channel-wise remap mode if supported */ + if (has_amd_full_remap_support(codec)) + snd_hda_codec_write(codec, per_pin->pin_nid, 0, + ATI_VERB_SET_MULTICHANNEL_MODE, + ATI_MULTICHANNEL_MODE_SINGLE); } + return 0; } static int patch_atihdmi(struct hda_codec *codec) { struct hdmi_spec *spec; - int err = patch_simple_hdmi(codec, ATIHDMI_CVT_NID, ATIHDMI_PIN_NID); - if (err < 0) + struct hdmi_spec_per_cvt *per_cvt; + int err, cvt_idx; + + err = patch_generic_hdmi(codec); + + if (err) return err; + + codec->patch_ops.init = atihdmi_init; + spec = codec->spec; - spec->pcm_playback.ops.prepare = atihdmi_playback_pcm_prepare; + + spec->ops.pin_get_slot_channel = atihdmi_pin_get_slot_channel; + spec->ops.pin_set_slot_channel = atihdmi_pin_set_slot_channel; + spec->ops.pin_setup_infoframe = atihdmi_pin_setup_infoframe; + + if (!has_amd_full_remap_support(codec)) { + /* override to ATI/AMD-specific versions with pairwise mapping */ + spec->ops.chmap_cea_alloc_validate_get_type = + atihdmi_paired_chmap_cea_alloc_validate_get_type; + spec->ops.cea_alloc_to_tlv_chmap = atihdmi_paired_cea_alloc_to_tlv_chmap; + spec->ops.chmap_validate = atihdmi_paired_chmap_validate; + } + + /* ATI/AMD converters do not advertise all of their capabilities */ + for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) { + per_cvt = get_cvt(spec, cvt_idx); + per_cvt->channels_max = max(per_cvt->channels_max, 8u); + per_cvt->rates |= SUPPORTED_RATES; + per_cvt->formats |= SUPPORTED_FORMATS; + per_cvt->maxbps = max(per_cvt->maxbps, 24u); + } + + spec->channels_max = max(spec->channels_max, 8u); + return 0; } @@ -2846,7 +3090,7 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = { { .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi }, { .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi }, { .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi }, -{ .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_generic_hdmi }, +{ .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi }, { .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_generic_hdmi }, { .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_generic_hdmi }, { .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_generic_hdmi }, -- cgit v0.10.2 From 89250f84644676c1ed6a09acef1033e14596a402 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Thu, 24 Oct 2013 21:10:36 +0300 Subject: ALSA: hda - hdmi: Add ELD emulation for ATI/AMD codecs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ATI/AMD HDMI/DP codecs do not include standard HDA ELD (EDID-like data) support. In place of providing access to an ELD buffer, various vendor-specific verbs are provided to provide the relevant information. Revision ID 3 and later (0x100300 as reported by procfs codec#X) have support for providing more information than the previous revisions (but only if supported by the display driver). Generate ELD from the information provided by the vendor-specific verbs on ATI/AMD codecs. The specification is available at: http://www.x.org/docs/AMD/AMD_HDA_verbs_v2.pdf v2: moved code to hda_eld.c and cleaned it up v3: adapted to hdmi_ops infrastructure Signed-off-by: Anssi Hannula Tested-by: Peter Frühberger # v2 Tested-by: Olivier Langlois # v2 Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index f62356c..32d3e38 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -2,6 +2,7 @@ * Generic routines and proc interface for ELD(EDID Like Data) information * * Copyright(c) 2008 Intel Corporation. + * Copyright (c) 2013 Anssi Hannula * * Authors: * Wu Fengguang @@ -633,3 +634,153 @@ void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e, hinfo->maxbps = min(hinfo->maxbps, maxbps); hinfo->channels_max = min(hinfo->channels_max, channels_max); } + + +/* ATI/AMD specific stuff (ELD emulation) */ + +#define ATI_VERB_SET_AUDIO_DESCRIPTOR 0x776 +#define ATI_VERB_SET_SINK_INFO_INDEX 0x780 +#define ATI_VERB_GET_SPEAKER_ALLOCATION 0xf70 +#define ATI_VERB_GET_AUDIO_DESCRIPTOR 0xf76 +#define ATI_VERB_GET_AUDIO_VIDEO_DELAY 0xf7b +#define ATI_VERB_GET_SINK_INFO_INDEX 0xf80 +#define ATI_VERB_GET_SINK_INFO_DATA 0xf81 + +#define ATI_SPKALLOC_SPKALLOC 0x007f +#define ATI_SPKALLOC_TYPE_HDMI 0x0100 +#define ATI_SPKALLOC_TYPE_DISPLAYPORT 0x0200 + +/* first three bytes are just standard SAD */ +#define ATI_AUDIODESC_CHANNELS 0x00000007 +#define ATI_AUDIODESC_RATES 0x0000ff00 +#define ATI_AUDIODESC_LPCM_STEREO_RATES 0xff000000 + +/* in standard HDMI VSDB format */ +#define ATI_DELAY_VIDEO_LATENCY 0x000000ff +#define ATI_DELAY_AUDIO_LATENCY 0x0000ff00 + +enum ati_sink_info_idx { + ATI_INFO_IDX_MANUFACTURER_ID = 0, + ATI_INFO_IDX_PRODUCT_ID = 1, + ATI_INFO_IDX_SINK_DESC_LEN = 2, + ATI_INFO_IDX_PORT_ID_LOW = 3, + ATI_INFO_IDX_PORT_ID_HIGH = 4, + ATI_INFO_IDX_SINK_DESC_FIRST = 5, + ATI_INFO_IDX_SINK_DESC_LAST = 22, /* max len 18 bytes */ +}; + +int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid, + unsigned char *buf, int *eld_size, bool rev3_or_later) +{ + int spkalloc, ati_sad, aud_synch; + int sink_desc_len = 0; + int pos, i; + + /* ATI/AMD does not have ELD, emulate it */ + + spkalloc = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SPEAKER_ALLOCATION, 0); + + if (!spkalloc) { + snd_printd(KERN_INFO "HDMI ATI/AMD: no speaker allocation for ELD\n"); + return -EINVAL; + } + + memset(buf, 0, ELD_FIXED_BYTES + ELD_MAX_MNL + ELD_MAX_SAD * 3); + + /* version */ + buf[0] = ELD_VER_CEA_861D << 3; + + /* speaker allocation from EDID */ + buf[7] = spkalloc & ATI_SPKALLOC_SPKALLOC; + + /* is DisplayPort? */ + if (spkalloc & ATI_SPKALLOC_TYPE_DISPLAYPORT) + buf[5] |= 0x04; + + pos = ELD_FIXED_BYTES; + + if (rev3_or_later) { + int sink_info; + + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_LOW); + sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); + put_unaligned_le32(sink_info, buf + 8); + + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_HIGH); + sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); + put_unaligned_le32(sink_info, buf + 12); + + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_MANUFACTURER_ID); + sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); + put_unaligned_le16(sink_info, buf + 16); + + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PRODUCT_ID); + sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); + put_unaligned_le16(sink_info, buf + 18); + + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_LEN); + sink_desc_len = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); + + if (sink_desc_len > ELD_MAX_MNL) { + snd_printd(KERN_INFO "HDMI ATI/AMD: Truncating HDMI sink description with length %d\n", + sink_desc_len); + sink_desc_len = ELD_MAX_MNL; + } + + buf[4] |= sink_desc_len; + + for (i = 0; i < sink_desc_len; i++) { + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_FIRST + i); + buf[pos++] = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); + } + } + + for (i = AUDIO_CODING_TYPE_LPCM; i <= AUDIO_CODING_TYPE_WMAPRO; i++) { + if (i == AUDIO_CODING_TYPE_SACD || i == AUDIO_CODING_TYPE_DST) + continue; /* not handled by ATI/AMD */ + + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_AUDIO_DESCRIPTOR, i << 3); + ati_sad = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_DESCRIPTOR, 0); + + if (ati_sad & ATI_AUDIODESC_RATES) { + /* format is supported, copy SAD as-is */ + buf[pos++] = (ati_sad & 0x0000ff) >> 0; + buf[pos++] = (ati_sad & 0x00ff00) >> 8; + buf[pos++] = (ati_sad & 0xff0000) >> 16; + } + + if (i == AUDIO_CODING_TYPE_LPCM + && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES) + && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES) >> 16 != (ati_sad & ATI_AUDIODESC_RATES)) { + /* for PCM there is a separate stereo rate mask */ + buf[pos++] = ((ati_sad & 0x000000ff) & ~ATI_AUDIODESC_CHANNELS) | 0x1; + /* rates from the extra byte */ + buf[pos++] = (ati_sad & 0xff000000) >> 24; + buf[pos++] = (ati_sad & 0x00ff0000) >> 16; + } + } + + if (pos == ELD_FIXED_BYTES + sink_desc_len) { + snd_printd(KERN_INFO "HDMI ATI/AMD: no audio descriptors for ELD\n"); + return -EINVAL; + } + + aud_synch = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_VIDEO_DELAY, 0); + if ((aud_synch & ATI_DELAY_VIDEO_LATENCY) && (aud_synch & ATI_DELAY_AUDIO_LATENCY)) { + int video_latency = (aud_synch & ATI_DELAY_VIDEO_LATENCY) - 1; + int audio_latency = ((aud_synch & ATI_DELAY_AUDIO_LATENCY) >> 8) - 1; + + if (video_latency > audio_latency) + buf[6] = min(video_latency - audio_latency, 0xfa); + } + + /* Baseline length */ + buf[2] = pos - 4; + + /* SAD count */ + buf[5] |= ((pos - ELD_FIXED_BYTES - sink_desc_len) / 3) << 4; + + *eld_size = pos; + + return 0; +} diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 46cddd4..d398b64 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -763,6 +763,10 @@ void snd_hdmi_show_eld(struct parsed_hdmi_eld *e); void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e, struct hda_pcm_stream *hinfo); +int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid, + unsigned char *buf, int *eld_size, + bool rev3_or_later); + #ifdef CONFIG_PROC_FS void snd_hdmi_print_eld_info(struct hdmi_eld *eld, struct snd_info_buffer *buffer); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 1114be0..0307ae8 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2821,6 +2821,14 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) #define ATI_MULTICHANNEL_MODE_PAIRED 0 #define ATI_MULTICHANNEL_MODE_SINGLE 1 +static int atihdmi_pin_get_eld(struct hda_codec *codec, hda_nid_t nid, + unsigned char *buf, int *eld_size) +{ + /* call hda_eld.c ATI/AMD-specific function */ + return snd_hdmi_get_eld_ati(codec, nid, buf, eld_size, + is_amdhdmi_rev3_or_later(codec)); +} + static void atihdmi_pin_setup_infoframe(struct hda_codec *codec, hda_nid_t pin_nid, int ca, int active_channels, int conn_type) { @@ -3048,6 +3056,7 @@ static int patch_atihdmi(struct hda_codec *codec) spec = codec->spec; + spec->ops.pin_get_eld = atihdmi_pin_get_eld; spec->ops.pin_get_slot_channel = atihdmi_pin_get_slot_channel; spec->ops.pin_set_slot_channel = atihdmi_pin_set_slot_channel; spec->ops.pin_setup_infoframe = atihdmi_pin_setup_infoframe; -- cgit v0.10.2 From 461cf6b39dded3ddb15a2300a534aba039870e5f Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Thu, 24 Oct 2013 21:10:37 +0300 Subject: ALSA: hda - hdmi: Add HBR bitstreaming support for ATI/AMD HDMI codecs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ATI/AMD HDMI codecs do not include standard HDA HDMI HBR support (which is required for bitstreaming DTS-HD and Dolby TrueHD), instead they have custom verbs for checking and enabling it. Add support for the ATI/AMD HDMI HBR verbs. The specification is available at: http://www.x.org/docs/AMD/AMD_HDA_verbs_v2.pdf v2: adapted to hdmi_ops infrastructure Signed-off-by: Anssi Hannula Tested-by: Peter Frühberger # v1 Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 0307ae8..f6a9760 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2799,6 +2799,7 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) #define ATI_VERB_SET_MULTICHANNEL_23 0x778 #define ATI_VERB_SET_MULTICHANNEL_45 0x779 #define ATI_VERB_SET_MULTICHANNEL_67 0x77a +#define ATI_VERB_SET_HBR_CONTROL 0x77c #define ATI_VERB_SET_MULTICHANNEL_1 0x785 #define ATI_VERB_SET_MULTICHANNEL_3 0x786 #define ATI_VERB_SET_MULTICHANNEL_5 0x787 @@ -2810,6 +2811,7 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) #define ATI_VERB_GET_MULTICHANNEL_23 0xf78 #define ATI_VERB_GET_MULTICHANNEL_45 0xf79 #define ATI_VERB_GET_MULTICHANNEL_67 0xf7a +#define ATI_VERB_GET_HBR_CONTROL 0xf7c #define ATI_VERB_GET_MULTICHANNEL_1 0xf85 #define ATI_VERB_GET_MULTICHANNEL_3 0xf86 #define ATI_VERB_GET_MULTICHANNEL_5 0xf87 @@ -2821,6 +2823,9 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) #define ATI_MULTICHANNEL_MODE_PAIRED 0 #define ATI_MULTICHANNEL_MODE_SINGLE 1 +#define ATI_HBR_CAPABLE 0x01 +#define ATI_HBR_ENABLE 0x10 + static int atihdmi_pin_get_eld(struct hda_codec *codec, hda_nid_t nid, unsigned char *buf, int *eld_size) { @@ -3015,6 +3020,35 @@ static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_all WARN_ON(count != channels); } +static int atihdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid, + bool hbr) +{ + int hbr_ctl, hbr_ctl_new; + + hbr_ctl = snd_hda_codec_read(codec, pin_nid, 0, ATI_VERB_GET_HBR_CONTROL, 0); + if (hbr_ctl & ATI_HBR_CAPABLE) { + if (hbr) + hbr_ctl_new = hbr_ctl | ATI_HBR_ENABLE; + else + hbr_ctl_new = hbr_ctl & ~ATI_HBR_ENABLE; + + snd_printdd("atihdmi_pin_hbr_setup: " + "NID=0x%x, %shbr-ctl=0x%x\n", + pin_nid, + hbr_ctl == hbr_ctl_new ? "" : "new-", + hbr_ctl_new); + + if (hbr_ctl != hbr_ctl_new) + snd_hda_codec_write(codec, pin_nid, 0, + ATI_VERB_SET_HBR_CONTROL, + hbr_ctl_new); + + } else if (hbr) + return -EINVAL; + + return 0; +} + static int atihdmi_init(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; @@ -3060,6 +3094,7 @@ static int patch_atihdmi(struct hda_codec *codec) spec->ops.pin_get_slot_channel = atihdmi_pin_get_slot_channel; spec->ops.pin_set_slot_channel = atihdmi_pin_set_slot_channel; spec->ops.pin_setup_infoframe = atihdmi_pin_setup_infoframe; + spec->ops.pin_hbr_setup = atihdmi_pin_hbr_setup; if (!has_amd_full_remap_support(codec)) { /* override to ATI/AMD-specific versions with pairwise mapping */ -- cgit v0.10.2 From 84d69e790f48ff2e8472a8cf602243e43683eaf0 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Thu, 24 Oct 2013 21:10:38 +0300 Subject: ALSA: hda - hdmi: Disable ramp-up/down for non-PCM on AMD codecs Recent AMD HDMI codecs (revision ID 3 and later, 0x100300 as reported by procfs codec#0) have a configurable ramp-up/down functionality. The documentation ( http://www.x.org/docs/AMD/AMD_HDA_verbs_v2.pdf ) specifies that 180 ("180/256 =~ 0.7") is recommended for PCM and 0 for non-PCM. Apply the recommended values according to provided S/PDIF AES0 settings since ramp-up/down does not make sense for non-PCM. v2: adapted to hdmi_ops infrastructure * More note from Anssi: actually, re-reading mails reveals that Olivier didn't find the expected difference with this setting, except for "maybe slightly slower startup with AES0=6" (i.e. value 0, which is unexpected). So maybe a) it makes too unnoticiable a difference, or b) only affects certain hardware (card and/or sink), or c) ramp-up/down is only triggered with the MUTE bit of ATI_VERB_SET_MULTICHANNEL_xx which is also rev3+ specific, but is not presently used by the driver, or something else. So there's a significant chance setting ramp rate is useless for us ATM, but probably does not do actual harm either. Signed-off-by: Anssi Hannula Tested-by: Olivier Langlois # v1 Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index f6a9760..7bd89bf 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2818,6 +2818,10 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) #define ATI_VERB_GET_MULTICHANNEL_7 0xf88 #define ATI_VERB_GET_MULTICHANNEL_MODE 0xf89 +/* AMD specific HDA cvt verbs */ +#define ATI_VERB_SET_RAMP_RATE 0x770 +#define ATI_VERB_GET_RAMP_RATE 0xf70 + #define ATI_OUT_ENABLE 0x1 #define ATI_MULTICHANNEL_MODE_PAIRED 0 @@ -3049,6 +3053,23 @@ static int atihdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid, return 0; } +static int atihdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, + hda_nid_t pin_nid, u32 stream_tag, int format) +{ + + if (is_amdhdmi_rev3_or_later(codec)) { + int ramp_rate = 180; /* default as per AMD spec */ + /* disable ramp-up/down for non-pcm as per AMD spec */ + if (format & AC_FMT_TYPE_NON_PCM) + ramp_rate = 0; + + snd_hda_codec_write(codec, cvt_nid, 0, ATI_VERB_SET_RAMP_RATE, ramp_rate); + } + + return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); +} + + static int atihdmi_init(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; @@ -3095,6 +3116,7 @@ static int patch_atihdmi(struct hda_codec *codec) spec->ops.pin_set_slot_channel = atihdmi_pin_set_slot_channel; spec->ops.pin_setup_infoframe = atihdmi_pin_setup_infoframe; spec->ops.pin_hbr_setup = atihdmi_pin_hbr_setup; + spec->ops.setup_stream = atihdmi_setup_stream; if (!has_amd_full_remap_support(codec)) { /* override to ATI/AMD-specific versions with pairwise mapping */ -- cgit v0.10.2 From 7342017f4a0f129d277f78b8761f2732661ba30a Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Fri, 25 Oct 2013 01:45:18 +0300 Subject: ALSA: hda - hdmi: Re-setup pin and infoframe on plug-in on all codecs hdmi_setup_audio_infoframe() does not set up pin and infoframe if there is no connected sink. If a sink is connected while audio playback is already in progress, the pin and infoframe will not be properly set up, causing no audio or wrongly mapped audio. On Intel Haswell codecs the hdmi_setup_audio_infoframe() is already called again from hdmi_present_sense() when an ELD appears because transcoder:port mapping may have changed. Make the call non-Haswell-specific so that audio will be properly set up if the playback was started before a sink was connected. Tested on non-Haswell Intel HDMI codec by plugging sink in during playback. Signed-off-by: Anssi Hannula Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 7bd89bf..772827b 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1534,11 +1534,12 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) pin_eld->eld_size = eld->eld_size; pin_eld->info = eld->info; - /* Haswell-specific workaround: re-setup when the transcoder is - * changed during the stream playback + /* + * Re-setup pin and infoframe. This is needed e.g. when + * - sink is first plugged-in (infoframe is not set up if !monitor_present) + * - transcoder can change during stream playback on Haswell */ - if (is_haswell(codec) && - eld->eld_valid && !old_eld_valid && per_pin->setup) + if (eld->eld_valid && !old_eld_valid && per_pin->setup) hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); } -- cgit v0.10.2 From 99d8d3ba512810feb13e158042975dcda75cef31 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Thu, 24 Oct 2013 17:55:18 +0200 Subject: ASoC: kirkwood: Fix compile error due to patch 'add S/PDIF support' This patch fixes the compilation error of kirkwood-i2s.c introduced by the commit 75b9b65ee5a80e99e 'ASoC: kirkwood: add S/PDIF support'. Signed-off-by: Jean-Francois Moine Signed-off-by: Mark Brown diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c index 9ec38d1..d34d917 100644 --- a/sound/soc/kirkwood/kirkwood-i2s.c +++ b/sound/soc/kirkwood/kirkwood-i2s.c @@ -568,7 +568,7 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev) } else { dev_info(&pdev->dev, "found external clock\n"); clk_prepare_enable(priv->extclk); - soc_dai = &kirkwood_i2s_dai_extclk; + soc_dai = kirkwood_i2s_dai_extclk; } } -- cgit v0.10.2 From e5f7825cda366809153701e8bb89123bd973be00 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 24 Oct 2013 21:53:29 -0700 Subject: spi/hspi: add device tree support Support for loading the Renesas HSPI driver via devicetree. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/spi/sh-hspi.txt b/Documentation/devicetree/bindings/spi/sh-hspi.txt new file mode 100644 index 0000000..30b57b1 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/sh-hspi.txt @@ -0,0 +1,7 @@ +Renesas HSPI. + +Required properties: +- compatible : "renesas,hspi" +- reg : Offset and length of the register set for the device +- interrupts : interrupt line used by HSPI + diff --git a/drivers/spi/spi-sh-hspi.c b/drivers/spi/spi-sh-hspi.c index b9e7b5e..8a147d9 100644 --- a/drivers/spi/spi-sh-hspi.c +++ b/drivers/spi/spi-sh-hspi.c @@ -303,6 +303,7 @@ static int hspi_probe(struct platform_device *pdev) master->setup = hspi_setup; master->cleanup = hspi_cleanup; master->mode_bits = SPI_CPOL | SPI_CPHA; + master->dev.of_node = pdev->dev.of_node; master->auto_runtime_pm = true; master->transfer_one_message = hspi_transfer_one_message; ret = spi_register_master(master); @@ -333,12 +334,19 @@ static int hspi_remove(struct platform_device *pdev) return 0; } +static struct of_device_id hspi_of_match[] = { + { .compatible = "renesas,hspi", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, hspi_of_match); + static struct platform_driver hspi_driver = { .probe = hspi_probe, .remove = hspi_remove, .driver = { .name = "sh-hspi", .owner = THIS_MODULE, + .of_match_table = hspi_of_match, }, }; module_platform_driver(hspi_driver); -- cgit v0.10.2 From 29ca9c73e54131c9ad90c5381f368d9b09b5aca4 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 25 Oct 2013 17:06:24 +0800 Subject: ASoC: samsung: fix return value check in i2s_alloc_dai() In case of error, the function platform_device_alloc() returns NULL pointer not ERR_PTR(). The IS_ERR() test in the return value check should be replaced with NULL test. Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 32956df..2e031fa 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1073,7 +1073,7 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec) dev_set_drvdata(&i2s->pdev->dev, i2s); } else { /* Create a new platform_device for Secondary */ i2s->pdev = platform_device_alloc("samsung-i2s-sec", -1); - if (IS_ERR(i2s->pdev)) + if (!i2s->pdev) return NULL; i2s->pdev->dev.parent = &pdev->dev; -- cgit v0.10.2 From 6d9153bbce50d41802ad2e20ce52daf699a3e8ad Mon Sep 17 00:00:00 2001 From: Lan Tianyu Date: Thu, 24 Oct 2013 15:11:33 +0800 Subject: x86/reboot: Correct pr_info() log message in the set_bios/pci/kbd_reboot() commit: c767a54ba065 x86/debug: Add KERN_ to bare printks, convert printks to pr_ broke the log messages in the set_bios/pci/kbd_reboot() functions, by putting the reboot method string and quirk entry's ident string in the wrong order. This patch fixes it. Signed-off-by: Lan Tianyu Cc: holt@sgi.com Cc: davej@fedoraproject.org Cc: lenb@kernel.org Cc: rjw@rjwysocki.net Cc: awilliam@redhat.com Link: http://lkml.kernel.org/r/1382598693-29334-1-git-send-email-tianyu.lan@intel.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index 7692520..da1fe1c 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -61,7 +61,7 @@ static int __init set_bios_reboot(const struct dmi_system_id *d) if (reboot_type != BOOT_BIOS) { reboot_type = BOOT_BIOS; pr_info("%s series board detected. Selecting %s-method for reboots.\n", - "BIOS", d->ident); + d->ident, "BIOS"); } return 0; } @@ -117,7 +117,7 @@ static int __init set_pci_reboot(const struct dmi_system_id *d) if (reboot_type != BOOT_CF9) { reboot_type = BOOT_CF9; pr_info("%s series board detected. Selecting %s-method for reboots.\n", - "PCI", d->ident); + d->ident, "PCI"); } return 0; } @@ -127,7 +127,7 @@ static int __init set_kbd_reboot(const struct dmi_system_id *d) if (reboot_type != BOOT_KBD) { reboot_type = BOOT_KBD; pr_info("%s series board detected. Selecting %s-method for reboot.\n", - "KBD", d->ident); + d->ident, "KBD"); } return 0; } -- cgit v0.10.2 From 6dd17757927ba9d23c604fee6fe72b4755c7ea7f Mon Sep 17 00:00:00 2001 From: Brian Austin Date: Fri, 25 Oct 2013 10:01:14 -0500 Subject: ASoC: cs42l52: Add platform data for reset gpio This patch adds platform data support for a reset GPIO. Also uses reset_gpio to toggle reset of the CODEC Signed-off-by: Brian Austin Signed-off-by: Mark Brown diff --git a/include/sound/cs42l52.h b/include/sound/cs42l52.h index 4c68955..7c2be4a 100644 --- a/include/sound/cs42l52.h +++ b/include/sound/cs42l52.h @@ -31,6 +31,8 @@ struct cs42l52_platform_data { /* Charge Pump Freq. Check datasheet Pg73 */ unsigned int chgfreq; + /* Reset GPIO */ + unsigned int reset_gpio; }; #endif /* __CS42L52_H */ diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index be2ba1b..8367f3c 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -1205,6 +1206,7 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client, const struct i2c_device_id *id) { struct cs42l52_private *cs42l52; + struct cs42l52_platform_data *pdata = dev_get_platdata(&i2c_client->dev); int ret; unsigned int devid = 0; unsigned int reg; @@ -1222,11 +1224,22 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client, return ret; } - i2c_set_clientdata(i2c_client, cs42l52); + if (pdata) + cs42l52->pdata = *pdata; - if (dev_get_platdata(&i2c_client->dev)) - memcpy(&cs42l52->pdata, dev_get_platdata(&i2c_client->dev), - sizeof(cs42l52->pdata)); + if (cs42l52->pdata.reset_gpio) { + ret = gpio_request_one(cs42l52->pdata.reset_gpio, + GPIOF_OUT_INIT_HIGH, "CS42L52 /RST"); + if (ret < 0) { + dev_err(&i2c_client->dev, "Failed to request /RST %d: %d\n", + cs42l52->pdata.reset_gpio, ret); + return ret; + } + gpio_set_value_cansleep(cs42l52->pdata.reset_gpio, 0); + gpio_set_value_cansleep(cs42l52->pdata.reset_gpio, 1); + } + + i2c_set_clientdata(i2c_client, cs42l52); ret = regmap_register_patch(cs42l52->regmap, cs42l52_threshold_patch, ARRAY_SIZE(cs42l52_threshold_patch)); -- cgit v0.10.2 From 153723f6f1d13e7b9541b425ebdbaead4cc85346 Mon Sep 17 00:00:00 2001 From: Brian Austin Date: Fri, 25 Oct 2013 10:01:16 -0500 Subject: ASoC: cs42l52: convert pdata config to regmap_update_bits Moving platform data to bus probe and convert to regmap_update_bits. This will work nicer when converted to device tree instead of having it split into multiple probes Signed-off-by: Brian Austin Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index 8367f3c..56c5611 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -1117,40 +1117,6 @@ static int cs42l52_probe(struct snd_soc_codec *codec) cs42l52->sysclk = CS42L52_DEFAULT_CLK; cs42l52->config.format = CS42L52_DEFAULT_FORMAT; - /* Set Platform MICx CFG */ - snd_soc_update_bits(codec, CS42L52_MICA_CTL, - CS42L52_MIC_CTL_TYPE_MASK, - cs42l52->pdata.mica_cfg << - CS42L52_MIC_CTL_TYPE_SHIFT); - - snd_soc_update_bits(codec, CS42L52_MICB_CTL, - CS42L52_MIC_CTL_TYPE_MASK, - cs42l52->pdata.micb_cfg << - CS42L52_MIC_CTL_TYPE_SHIFT); - - /* if Single Ended, Get Mic_Select */ - if (cs42l52->pdata.mica_cfg) - snd_soc_update_bits(codec, CS42L52_MICA_CTL, - CS42L52_MIC_CTL_MIC_SEL_MASK, - cs42l52->pdata.mica_sel << - CS42L52_MIC_CTL_MIC_SEL_SHIFT); - if (cs42l52->pdata.micb_cfg) - snd_soc_update_bits(codec, CS42L52_MICB_CTL, - CS42L52_MIC_CTL_MIC_SEL_MASK, - cs42l52->pdata.micb_sel << - CS42L52_MIC_CTL_MIC_SEL_SHIFT); - - /* Set Platform Charge Pump Freq */ - snd_soc_update_bits(codec, CS42L52_CHARGE_PUMP, - CS42L52_CHARGE_PUMP_MASK, - cs42l52->pdata.chgfreq << - CS42L52_CHARGE_PUMP_SHIFT); - - /* Set Platform Bias Level */ - snd_soc_update_bits(codec, CS42L52_IFACE_CTL2, - CS42L52_IFACE_CTL2_BIAS_LVL, - cs42l52->pdata.micbias_lvl); - return ret; } @@ -1257,7 +1223,40 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client, return ret; } - regcache_cache_only(cs42l52->regmap, true); + /* Set Platform Data */ + if (cs42l52->pdata.mica_cfg) + regmap_update_bits(cs42l52->regmap, CS42L52_MICA_CTL, + CS42L52_MIC_CTL_TYPE_MASK, + cs42l52->pdata.mica_cfg << + CS42L52_MIC_CTL_TYPE_SHIFT); + + if (cs42l52->pdata.micb_cfg) + regmap_update_bits(cs42l52->regmap, CS42L52_MICB_CTL, + CS42L52_MIC_CTL_TYPE_MASK, + cs42l52->pdata.micb_cfg << + CS42L52_MIC_CTL_TYPE_SHIFT); + + if (cs42l52->pdata.mica_sel) + regmap_update_bits(cs42l52->regmap, CS42L52_MICA_CTL, + CS42L52_MIC_CTL_MIC_SEL_MASK, + cs42l52->pdata.mica_sel << + CS42L52_MIC_CTL_MIC_SEL_SHIFT); + if (cs42l52->pdata.micb_sel) + regmap_update_bits(cs42l52->regmap, CS42L52_MICB_CTL, + CS42L52_MIC_CTL_MIC_SEL_MASK, + cs42l52->pdata.micb_sel << + CS42L52_MIC_CTL_MIC_SEL_SHIFT); + + if (cs42l52->pdata.chgfreq) + regmap_update_bits(cs42l52->regmap, CS42L52_CHARGE_PUMP, + CS42L52_CHARGE_PUMP_MASK, + cs42l52->pdata.chgfreq << + CS42L52_CHARGE_PUMP_SHIFT); + + if (cs42l52->pdata.micbias_lvl) + regmap_update_bits(cs42l52->regmap, CS42L52_IFACE_CTL2, + CS42L52_IFACE_CTL2_BIAS_LVL, + cs42l52->pdata.micbias_lvl); ret = snd_soc_register_codec(&i2c_client->dev, &soc_codec_dev_cs42l52, &cs42l52_dai, 1); -- cgit v0.10.2 From e5f03af644c46b8713ddf5df545a32c1af8557ed Mon Sep 17 00:00:00 2001 From: Brian Austin Date: Fri, 25 Oct 2013 10:01:17 -0500 Subject: ASoC: cs42l52: Add chip rev id message This patch adds a print message at bootup for the CODEC Rev ID Signed-off-by: Brian Austin Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index 56c5611..8b427c9 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -1223,6 +1223,9 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client, return ret; } + dev_info(&i2c_client->dev, "Cirrus Logic CS42L52, Revision: %02X\n", + reg & 0xFF); + /* Set Platform Data */ if (cs42l52->pdata.mica_cfg) regmap_update_bits(cs42l52->regmap, CS42L52_MICA_CTL, -- cgit v0.10.2 From 954e04b9491adea99e4590bc73937fdd8774ab3c Mon Sep 17 00:00:00 2001 From: Bryan Wu Date: Tue, 24 Sep 2013 10:38:26 -0700 Subject: of: introduce of_get_available_child_count Some drivers keep counting available child by themselves. So introduce a new simple API like of_get_child_count() but for available childs. Cc: Josh Wu Signed-off-by: Bryan Wu Acked-by: Rob Herring diff --git a/include/linux/of.h b/include/linux/of.h index f95aee3..54c2560 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -226,6 +226,17 @@ static inline int of_get_child_count(const struct device_node *np) return num; } +static inline int of_get_available_child_count(const struct device_node *np) +{ + struct device_node *child; + int num = 0; + + for_each_available_child_of_node(np, child) + num++; + + return num; +} + extern struct device_node *of_find_node_with_property( struct device_node *from, const char *prop_name); #define for_each_node_with_property(dn, prop_name) \ @@ -376,6 +387,11 @@ static inline int of_get_child_count(const struct device_node *np) return 0; } +static inline int of_get_available_child_count(const struct device_node *np) +{ + return 0; +} + static inline int of_device_is_compatible(const struct device_node *device, const char *name) { -- cgit v0.10.2 From b0bb83df0a004ff6ef9b1a11784361c9eb63dbf9 Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Thu, 26 Sep 2013 04:27:56 -0700 Subject: leds-gpio: of: led should not be created if its status is disabled now the leds-gpio driver will create every child led node without checking the status is disabled or not. for example, if we have a led node like d3, and its status is disabled: leds { d3 { label = "d3"; gpios = <&pioE 24 0>; status = "disabled"; }; }; we except the d3 should not be created. And the gpios should not be request as well. But current driver will create d3 and request its gpio. This patch fix this by using for_each_available_child_of_node() and of_get_available_child_count() to enumerate all child nodes. So the disabled node will be inavailable. Signed-off-by: Josh Wu Signed-off-by: Bryan Wu diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 7ccafde..78b0e27 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -171,11 +171,11 @@ static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev) int count, ret; /* count LEDs in this device, so we know how much to allocate */ - count = of_get_child_count(np); + count = of_get_available_child_count(np); if (!count) return ERR_PTR(-ENODEV); - for_each_child_of_node(np, child) + for_each_available_child_of_node(np, child) if (of_get_gpio(child, 0) == -EPROBE_DEFER) return ERR_PTR(-EPROBE_DEFER); @@ -184,7 +184,7 @@ static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev) if (!priv) return ERR_PTR(-ENOMEM); - for_each_child_of_node(np, child) { + for_each_available_child_of_node(np, child) { struct gpio_led led = {}; enum of_gpio_flags flags; const char *state; -- cgit v0.10.2 From 30dae2f98612d7c8cd855861b9de205ebd9ef4fa Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 22 Oct 2013 11:02:56 -0700 Subject: leds: lp55xx: handle enable pin in driver This patch moves the handling of the chip's enable pin from the board code into the driver. It also updates all board-code files using the driver to incorporate this change. This is needed for device tree support of the enable pin. Signed-off-by: Sebastian Reichel Acked-by: Linus Walleij Acked-by: Tony Lindgren Signed-off-by: Bryan Wu diff --git a/Documentation/devicetree/bindings/leds/leds-lp55xx.txt b/Documentation/devicetree/bindings/leds/leds-lp55xx.txt index d221e75..c55b8c0 100644 --- a/Documentation/devicetree/bindings/leds/leds-lp55xx.txt +++ b/Documentation/devicetree/bindings/leds/leds-lp55xx.txt @@ -10,6 +10,7 @@ Each child has own specific current settings - max-cur: Maximun current at each led channel. Optional properties: +- enable-gpio: GPIO attached to the chip's enable pin - label: Used for naming LEDs - pwr-sel: LP8501 specific property. Power selection for output channels. 0: D1~9 are connected to VDD diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c index f6fe388..68dc998 100644 --- a/arch/arm/mach-omap2/board-rx51-peripherals.c +++ b/arch/arm/mach-omap2/board-rx51-peripherals.c @@ -211,29 +211,11 @@ static struct lp55xx_led_config rx51_lp5523_led_config[] = { } }; -static int rx51_lp5523_setup(void) -{ - return gpio_request_one(RX51_LP5523_CHIP_EN_GPIO, GPIOF_DIR_OUT, - "lp5523_enable"); -} - -static void rx51_lp5523_release(void) -{ - gpio_free(RX51_LP5523_CHIP_EN_GPIO); -} - -static void rx51_lp5523_enable(bool state) -{ - gpio_set_value(RX51_LP5523_CHIP_EN_GPIO, !!state); -} - static struct lp55xx_platform_data rx51_lp5523_platform_data = { .led_config = rx51_lp5523_led_config, .num_channels = ARRAY_SIZE(rx51_lp5523_led_config), .clock_mode = LP55XX_CLOCK_AUTO, - .setup_resources = rx51_lp5523_setup, - .release_resources = rx51_lp5523_release, - .enable = rx51_lp5523_enable, + .enable_gpio = RX51_LP5523_CHIP_EN_GPIO, }; #endif diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index ad0806e..703dec2 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -297,6 +297,7 @@ static struct lp55xx_platform_data __initdata lp5521_pri_data = { .led_config = &lp5521_pri_led[0], .num_channels = 3, .clock_mode = LP55XX_CLOCK_EXT, + .enable_gpio = -1, }; static struct lp55xx_led_config lp5521_sec_led[] = { @@ -322,6 +323,7 @@ static struct lp55xx_platform_data __initdata lp5521_sec_data = { .led_config = &lp5521_sec_led[0], .num_channels = 3, .clock_mode = LP55XX_CLOCK_EXT, + .enable_gpio = -1, }; /* I2C0 devices only available on the first HREF/MOP500 */ diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 075acf5..9acc6bb 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include "leds-lp55xx-common.h" @@ -407,18 +409,18 @@ int lp55xx_init_device(struct lp55xx_chip *chip) if (!pdata || !cfg) return -EINVAL; - if (pdata->setup_resources) { - ret = pdata->setup_resources(); + if (gpio_is_valid(pdata->enable_gpio)) { + ret = devm_gpio_request_one(dev, pdata->enable_gpio, + GPIOF_DIR_OUT, "lp5523_enable"); if (ret < 0) { - dev_err(dev, "setup resoure err: %d\n", ret); + dev_err(dev, "could not acquire enable gpio (err=%d)\n", + ret); goto err; } - } - if (pdata->enable) { - pdata->enable(0); + gpio_set_value(pdata->enable_gpio, 0); usleep_range(1000, 2000); /* Keep enable down at least 1ms */ - pdata->enable(1); + gpio_set_value(pdata->enable_gpio, 1); usleep_range(1000, 2000); /* 500us abs min. */ } @@ -459,11 +461,8 @@ void lp55xx_deinit_device(struct lp55xx_chip *chip) if (chip->clk) clk_disable_unprepare(chip->clk); - if (pdata->enable) - pdata->enable(0); - - if (pdata->release_resources) - pdata->release_resources(); + if (gpio_is_valid(pdata->enable_gpio)) + gpio_set_value(pdata->enable_gpio, 0); } EXPORT_SYMBOL_GPL(lp55xx_deinit_device); @@ -596,6 +595,8 @@ int lp55xx_of_populate_pdata(struct device *dev, struct device_node *np) of_property_read_string(np, "label", &pdata->label); of_property_read_u8(np, "clock-mode", &pdata->clock_mode); + pdata->enable_gpio = of_get_named_gpio(np, "enable-gpio", 0); + /* LP8501 specific */ of_property_read_u8(np, "pwr-sel", (u8 *)&pdata->pwr_sel); diff --git a/include/linux/platform_data/leds-lp55xx.h b/include/linux/platform_data/leds-lp55xx.h index c32de4d..624ff9e 100644 --- a/include/linux/platform_data/leds-lp55xx.h +++ b/include/linux/platform_data/leds-lp55xx.h @@ -67,10 +67,8 @@ struct lp55xx_platform_data { /* Clock configuration */ u8 clock_mode; - /* Platform specific functions */ - int (*setup_resources)(void); - void (*release_resources)(void); - void (*enable)(bool state); + /* optional enable GPIO */ + int enable_gpio; /* Predefined pattern data */ struct lp55xx_predef_pattern *patterns; -- cgit v0.10.2 From f82bf8e2c8be73ebd719807bf054721930371174 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 25 Oct 2013 18:06:09 +0200 Subject: ASoC: dmaengine: Use SNDRV_PCM_STREAM_LAST for array size ... to make the meaning more obvious. Signed-off-by: Takashi Iwai Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index 0c469cb..ee07903 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -25,7 +25,7 @@ #include struct dmaengine_pcm { - struct dma_chan *chan[SNDRV_PCM_STREAM_CAPTURE + 1]; + struct dma_chan *chan[SNDRV_PCM_STREAM_LAST + 1]; const struct snd_dmaengine_pcm_config *config; struct snd_soc_platform platform; unsigned int flags; -- cgit v0.10.2 From aab554ede931eddaca2e9b38c12489ae3f83fbe3 Mon Sep 17 00:00:00 2001 From: Brian Austin Date: Fri, 25 Oct 2013 10:01:15 -0500 Subject: ASoC: cs42l52: increase MAX_REGISTER for regmap_register_patch regmap_register_patch fails without the MAX_REGISTER set to highest register written to. Increase to register 0x47 Signed-off-by: Brian Austin Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/cs42l52.h b/sound/soc/codecs/cs42l52.h index 4277012..1a9412d 100644 --- a/sound/soc/codecs/cs42l52.h +++ b/sound/soc/codecs/cs42l52.h @@ -269,6 +269,6 @@ #define CS42L52_FIX_BITS1 0x3E #define CS42L52_FIX_BITS2 0x47 -#define CS42L52_MAX_REGISTER 0x34 +#define CS42L52_MAX_REGISTER 0x47 #endif -- cgit v0.10.2 From c6ce2b6bffe5740d572fdc5b5e690d5261abee51 Mon Sep 17 00:00:00 2001 From: Christian Ruppert Date: Tue, 8 Oct 2013 14:25:22 +0200 Subject: gpio: add TB10x GPIO driver The GPIO driver for the Abilis Systems TB10x series of SOCs based on ARC700 CPUs. It supports GPIO control and GPIO interrupt generation. This driver works in conjunction with the TB10x pinctrl driver. Signed-off-by: Sascha Leuenberger Signed-off-by: Christian Ruppert Acked-by: Kumar Gala Signed-off-by: Linus Walleij diff --git a/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt b/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt new file mode 100644 index 0000000..00611ac --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt @@ -0,0 +1,36 @@ +* Abilis TB10x GPIO controller + +Required Properties: +- compatible: Should be "abilis,tb10x-gpio" +- reg: Address and length of the register set for the device +- gpio-controller: Marks the device node as a gpio controller. +- #gpio-cells: Should be <2>. The first cell is the pin number and the + second cell is used to specify optional parameters: + - bit 0 specifies polarity (0 for normal, 1 for inverted). +- abilis,ngpio: the number of GPIO pins this driver controls. + +Optional Properties: +- interrupt-controller: Marks the device node as an interrupt controller. +- #interrupt-cells: Should be <1>. Interrupts are triggered on both edges. +- interrupts: Defines the interrupt line connecting this GPIO controller to + its parent interrupt controller. +- interrupt-parent: Defines the parent interrupt controller. + +GPIO ranges are specified as described in +Documentation/devicetree/bindings/gpio/gpio.txt + +Example: + + gpioa: gpio@FF140000 { + compatible = "abilis,tb10x-gpio"; + interrupt-controller; + #interrupt-cells = <1>; + interrupt-parent = <&tb10x_ictl>; + interrupts = <27 2>; + reg = <0xFF140000 0x1000>; + gpio-controller; + #gpio-cells = <2>; + abilis,ngpio = <3>; + gpio-ranges = <&iomux 0 0 0>; + gpio-ranges-group-names = "gpioa_pins"; + }; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b6ed304..661ca82 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -360,6 +360,10 @@ config GPIO_GRGPIO Select this to support Aeroflex Gaisler GRGPIO cores from the GRLIB VHDL IP core library. +config GPIO_TB10X + bool + select OF_GPIO + comment "I2C GPIO expanders:" config GPIO_ARIZONA diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 98e23eb..2931c99 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -71,6 +71,7 @@ obj-$(CONFIG_GPIO_STA2X11) += gpio-sta2x11.o obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o obj-$(CONFIG_GPIO_STP_XWAY) += gpio-stp-xway.o obj-$(CONFIG_GPIO_SX150X) += gpio-sx150x.o +obj-$(CONFIG_GPIO_TB10X) += gpio-tb10x.o obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o obj-$(CONFIG_ARCH_TEGRA) += gpio-tegra.o obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c new file mode 100644 index 0000000..833d0f4 --- /dev/null +++ b/drivers/gpio/gpio-tb10x.c @@ -0,0 +1,341 @@ +/* Abilis Systems MODULE DESCRIPTION + * + * Copyright (C) Abilis Systems 2013 + * + * Authors: Sascha Leuenberger + * Christian Ruppert + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TB10X_GPIO_DIR_IN (0x00000000) +#define TB10X_GPIO_DIR_OUT (0x00000001) +#define OFFSET_TO_REG_DDR (0x00) +#define OFFSET_TO_REG_DATA (0x04) +#define OFFSET_TO_REG_INT_EN (0x08) +#define OFFSET_TO_REG_CHANGE (0x0C) +#define OFFSET_TO_REG_WRMASK (0x10) +#define OFFSET_TO_REG_INT_TYPE (0x14) + + +/** + * @spinlock: used for atomic read/modify/write of registers + * @base: register base address + * @domain: IRQ domain of GPIO generated interrupts managed by this controller + * @irq: Interrupt line of parent interrupt controller + * @gc: gpio_chip structure associated to this GPIO controller + */ +struct tb10x_gpio { + spinlock_t spinlock; + void __iomem *base; + struct irq_domain *domain; + int irq; + struct gpio_chip gc; +}; + +static inline u32 tb10x_reg_read(struct tb10x_gpio *gpio, unsigned int offs) +{ + return ioread32(gpio->base + offs); +} + +static inline void tb10x_reg_write(struct tb10x_gpio *gpio, unsigned int offs, + u32 val) +{ + iowrite32(val, gpio->base + offs); +} + +static inline void tb10x_set_bits(struct tb10x_gpio *gpio, unsigned int offs, + u32 mask, u32 val) +{ + u32 r; + unsigned long flags; + + spin_lock_irqsave(&gpio->spinlock, flags); + + r = tb10x_reg_read(gpio, offs); + r = (r & ~mask) | (val & mask); + + tb10x_reg_write(gpio, offs, r); + + spin_unlock_irqrestore(&gpio->spinlock, flags); +} + +static inline struct tb10x_gpio *to_tb10x_gpio(struct gpio_chip *chip) +{ + return container_of(chip, struct tb10x_gpio, gc); +} + +static int tb10x_gpio_direction_in(struct gpio_chip *chip, unsigned offset) +{ + struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip); + int mask = BIT(offset); + int val = TB10X_GPIO_DIR_IN << offset; + + tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DDR, mask, val); + + return 0; +} + +static int tb10x_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip); + int val; + + val = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_DATA); + + if (val & BIT(offset)) + return 1; + else + return 0; +} + +static void tb10x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip); + int mask = BIT(offset); + int val = value << offset; + + tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DATA, mask, val); +} + +static int tb10x_gpio_direction_out(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip); + int mask = BIT(offset); + int val = TB10X_GPIO_DIR_OUT << offset; + + tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DDR, mask, val); + + return 0; +} + +static int tb10x_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + return pinctrl_request_gpio(chip->base + offset); +} + +static void tb10x_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + pinctrl_free_gpio(chip->base + offset); +} + +static int tb10x_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip); + + return irq_create_mapping(tb10x_gpio->domain, offset); +} + +static int tb10x_gpio_irq_set_type(struct irq_data *data, unsigned int type) +{ + if ((type & IRQF_TRIGGER_MASK) != IRQ_TYPE_EDGE_BOTH) { + pr_err("Only (both) edge triggered interrupts supported.\n"); + return -EINVAL; + } + + irqd_set_trigger_type(data, type); + + return IRQ_SET_MASK_OK; +} + +static irqreturn_t tb10x_gpio_irq_cascade(int irq, void *data) +{ + struct tb10x_gpio *tb10x_gpio = data; + u32 r = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_CHANGE); + u32 m = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_INT_EN); + const unsigned long bits = r & m; + int i; + + for_each_set_bit(i, &bits, 32) + generic_handle_irq(irq_find_mapping(tb10x_gpio->domain, i)); + + return IRQ_HANDLED; +} + +static int tb10x_gpio_probe(struct platform_device *pdev) +{ + struct tb10x_gpio *tb10x_gpio; + struct resource *mem; + struct device_node *dn = pdev->dev.of_node; + int ret = -EBUSY; + u32 ngpio; + + if (!dn) + return -EINVAL; + + if (of_property_read_u32(dn, "abilis,ngpio", &ngpio)) + return -EINVAL; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "No memory resource defined.\n"); + return -EINVAL; + } + + tb10x_gpio = devm_kzalloc(&pdev->dev, sizeof(*tb10x_gpio), GFP_KERNEL); + if (tb10x_gpio == NULL) + return -ENOMEM; + + spin_lock_init(&tb10x_gpio->spinlock); + + tb10x_gpio->base = devm_ioremap_resource(&pdev->dev, mem); + if (!tb10x_gpio->base) { + dev_err(&pdev->dev, "Could not remap reg space.\n"); + goto fail_ioremap; + } + + tb10x_gpio->gc.label = of_node_full_name(dn); + tb10x_gpio->gc.dev = &pdev->dev; + tb10x_gpio->gc.owner = THIS_MODULE; + tb10x_gpio->gc.direction_input = tb10x_gpio_direction_in; + tb10x_gpio->gc.get = tb10x_gpio_get; + tb10x_gpio->gc.direction_output = tb10x_gpio_direction_out; + tb10x_gpio->gc.set = tb10x_gpio_set; + tb10x_gpio->gc.request = tb10x_gpio_request; + tb10x_gpio->gc.free = tb10x_gpio_free; + tb10x_gpio->gc.base = -1; + tb10x_gpio->gc.ngpio = ngpio; + tb10x_gpio->gc.can_sleep = 0; + + + ret = gpiochip_add(&tb10x_gpio->gc); + if (ret < 0) { + dev_err(&pdev->dev, "Could not add gpiochip.\n"); + goto fail_gpiochip_registration; + } + + platform_set_drvdata(pdev, tb10x_gpio); + + if (of_find_property(dn, "interrupt-controller", NULL)) { + struct irq_chip_generic *gc; + + ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(&pdev->dev, "No interrupt specified.\n"); + goto fail_get_irq; + } + + tb10x_gpio->gc.to_irq = tb10x_gpio_to_irq; + tb10x_gpio->irq = ret; + + ret = devm_request_irq(&pdev->dev, ret, tb10x_gpio_irq_cascade, + IRQF_TRIGGER_NONE | IRQF_SHARED, + dev_name(&pdev->dev), tb10x_gpio); + if (ret != 0) + goto fail_request_irq; + + tb10x_gpio->domain = irq_domain_add_linear(dn, + tb10x_gpio->gc.ngpio, + &irq_generic_chip_ops, NULL); + if (!tb10x_gpio->domain) { + ret = -ENOMEM; + goto fail_irq_domain; + } + + ret = irq_alloc_domain_generic_chips(tb10x_gpio->domain, + tb10x_gpio->gc.ngpio, 1, tb10x_gpio->gc.label, + handle_edge_irq, IRQ_NOREQUEST, IRQ_NOPROBE, + IRQ_GC_INIT_MASK_CACHE); + if (ret) + goto fail_irq_domain; + + gc = tb10x_gpio->domain->gc->gc[0]; + gc->reg_base = tb10x_gpio->base; + gc->chip_types[0].type = IRQ_TYPE_EDGE_BOTH; + gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit; + gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit; + gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit; + gc->chip_types[0].chip.irq_set_type = tb10x_gpio_irq_set_type; + gc->chip_types[0].regs.ack = OFFSET_TO_REG_CHANGE; + gc->chip_types[0].regs.mask = OFFSET_TO_REG_INT_EN; + } + + return 0; + +fail_irq_domain: +fail_request_irq: +fail_get_irq: + gpiochip_remove(&tb10x_gpio->gc); +fail_gpiochip_registration: +fail_ioremap: + return ret; +} + +static int __exit tb10x_gpio_remove(struct platform_device *pdev) +{ + struct tb10x_gpio *tb10x_gpio = platform_get_drvdata(pdev); + int ret; + + if (tb10x_gpio->gc.to_irq) { + irq_remove_generic_chip(tb10x_gpio->domain->gc->gc[0], + BIT(tb10x_gpio->gc.ngpio) - 1, 0, 0); + kfree(tb10x_gpio->domain->gc); + irq_domain_remove(tb10x_gpio->domain); + free_irq(tb10x_gpio->irq, tb10x_gpio); + } + ret = gpiochip_remove(&tb10x_gpio->gc); + if (ret) + return ret; + + return 0; +} + +static const struct of_device_id tb10x_gpio_dt_ids[] = { + { .compatible = "abilis,tb10x-gpio" }, + { } +}; +MODULE_DEVICE_TABLE(of, tb10x_gpio_dt_ids); + +static struct platform_driver tb10x_gpio_driver = { + .probe = tb10x_gpio_probe, + .remove = tb10x_gpio_remove, + .driver = { + .name = "tb10x-gpio", + .of_match_table = of_match_ptr(tb10x_gpio_dt_ids), + .owner = THIS_MODULE, + } +}; + +static int __init ab_gpio_init(void) +{ + return platform_driver_register(&tb10x_gpio_driver); +} + +static void __exit ab_gpio_exit(void) +{ + platform_driver_unregister(&tb10x_gpio_driver); +} + +module_init(ab_gpio_init); +module_exit(ab_gpio_exit); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("tb10x gpio."); +MODULE_VERSION("0.0.1"); -- cgit v0.10.2 From afb5a7793144179bfd856e3c928b69c43e2a1d8a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 26 Oct 2013 00:33:58 +0200 Subject: ALSA: hda - Sync EAPD with vmaster on AD1984A Thinkpads As EAPD on NID 0x12 (speaker pin) is used as the master amp on Thinkpads with AD1984A codec, we can hook this to vmaster for saving a bit more power at master mute state. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 2aa2f57..87d2e03 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -973,8 +973,11 @@ static void ad1884_fixup_thinkpad(struct hda_codec *codec, { struct ad198x_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) + if (action == HDA_FIXUP_ACT_PRE_PROBE) { spec->gen.keep_eapd_on = 1; + spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook; + spec->eapd_nid = 0x12; + } } /* set magic COEFs for dmic */ -- cgit v0.10.2 From e9aa39bb7c4415ca26484239cc3a6686d549bf4f Mon Sep 17 00:00:00 2001 From: Li Bin Date: Mon, 21 Oct 2013 20:15:43 +0800 Subject: sched/rt: Fix task_tick_rt() comment This issue was introduced by 454c79999f7e ("sched/rt: Fix SCHED_RR across cgroups") that missed the word 'not'. Fix it. Signed-off-by: Li Bin Cc: Cc: Cc: Link: http://lkml.kernel.org/r/1382357743-54136-1-git-send-email-huawei.libin@huawei.com Signed-off-by: Ingo Molnar diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index a848f52..7d57275 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -1935,8 +1935,8 @@ static void task_tick_rt(struct rq *rq, struct task_struct *p, int queued) p->rt.time_slice = sched_rr_timeslice; /* - * Requeue to the end of queue if we (and all of our ancestors) are the - * only element on the queue + * Requeue to the end of queue if we (and all of our ancestors) are not + * the only element on the queue */ for_each_sched_rt_entity(rt_se) { if (rt_se->run_list.prev != rt_se->run_list.next) { -- cgit v0.10.2 From 3df7b41aa5e7797f391d0a41f8b0dce1fe366a09 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Mon, 21 Oct 2013 09:43:57 +0100 Subject: x86: Unify copy_from_user() size checking Commits 4a3127693001c61a21d1ce680db6340623f52e93 ("x86: Turn the copy_from_user check into an (optional) compile time warning") and 63312b6a6faae3f2e5577f2b001e3b504f10a2aa ("x86: Add a Kconfig option to turn the copy_from_user warnings into errors") touched only the 32-bit variant of copy_from_user(), whereas the original commit 9f0cf4adb6aa0bfccf675c938124e68f7f06349d ("x86: Use __builtin_object_size() to validate the buffer size for copy_from_user()") also added the same code to the 64-bit one. Further the earlier conversion from an inline WARN() to the call to copy_from_user_overflow() went a little too far: When the number of bytes to be copied is not a constant (e.g. [looking at 3.11] in drivers/net/tun.c:__tun_chr_ioctl() or drivers/pci/pcie/aer/aer_inject.c:aer_inject_write()), the compiler will always have to keep the funtion call, and hence there will always be a warning. By using __builtin_constant_p() we can avoid this. And then this slightly extends the effect of CONFIG_DEBUG_STRICT_USER_COPY_CHECKS in that apart from converting warnings to errors in the constant size case, it retains the (possibly wrong) warnings in the non-constant size case, such that if someone is prepared to get a few false positives, (s)he'll be able to recover the current behavior (except that these diagnostics now will never be converted to errors). Since the 32-bit variant (intentionally) didn't call might_fault(), the unification results in this being called twice now. Adding a suitable #ifdef would be the alternative if that's a problem. I'd like to point out though that with __compiletime_object_size() being restricted to gcc before 4.6, the whole construct is going to become more and more pointless going forward. I would question however that commit 2fb0815c9ee6b9ac50e15dd8360ec76d9fa46a2 ("gcc4: disable __compiletime_object_size for GCC 4.6+") was really necessary, and instead this should have been dealt with as is done here from the beginning. Signed-off-by: Jan Beulich Cc: Arjan van de Ven Cc: Guenter Roeck Cc: Linus Torvalds Cc: Andrew Morton Cc: Peter Zijlstra Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/5265056D02000078000FC4F3@nat28.tlf.novell.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h index 5838fa9..c9799ed 100644 --- a/arch/x86/include/asm/uaccess.h +++ b/arch/x86/include/asm/uaccess.h @@ -542,5 +542,73 @@ extern struct movsl_mask { # include #endif +unsigned long __must_check _copy_from_user(void *to, const void __user *from, + unsigned n); + +#ifdef CONFIG_DEBUG_STRICT_USER_COPY_CHECKS +# define copy_user_diag __compiletime_error +#else +# define copy_user_diag __compiletime_warning +#endif + +extern void copy_user_diag("copy_from_user() buffer size is too small") +copy_from_user_overflow(void); + +#undef copy_user_diag + +#ifdef CONFIG_DEBUG_STRICT_USER_COPY_CHECKS + +extern void +__compiletime_warning("copy_from_user() buffer size is not provably correct") +__copy_from_user_overflow(void) __asm__("copy_from_user_overflow"); +#define __copy_from_user_overflow(size, count) __copy_from_user_overflow() + +#else + +static inline void +__copy_from_user_overflow(int size, unsigned long count) +{ + WARN(1, "Buffer overflow detected (%d < %lu)!\n", size, count); +} + +#endif + +static inline unsigned long __must_check +copy_from_user(void *to, const void __user *from, unsigned long n) +{ + int sz = __compiletime_object_size(to); + + might_fault(); + + /* + * While we would like to have the compiler do the checking for us + * even in the non-constant size case, any false positives there are + * a problem (especially when DEBUG_STRICT_USER_COPY_CHECKS, but even + * without - the [hopefully] dangerous looking nature of the warning + * would make people go look at the respecitive call sites over and + * over again just to find that there's no problem). + * + * And there are cases where it's just not realistic for the compiler + * to prove the count to be in range. For example when multiple call + * sites of a helper function - perhaps in different source files - + * all doing proper range checking, yet the helper function not doing + * so again. + * + * Therefore limit the compile time checking to the constant size + * case, and do only runtime checking for non-constant sizes. + */ + + if (likely(sz < 0 || sz >= n)) + n = _copy_from_user(to, from, n); + else if(__builtin_constant_p(n)) + copy_from_user_overflow(); + else + __copy_from_user_overflow(sz, n); + + return n; +} + +#undef __copy_from_user_overflow + #endif /* _ASM_X86_UACCESS_H */ diff --git a/arch/x86/include/asm/uaccess_32.h b/arch/x86/include/asm/uaccess_32.h index 7f760a9..c348c87 100644 --- a/arch/x86/include/asm/uaccess_32.h +++ b/arch/x86/include/asm/uaccess_32.h @@ -186,31 +186,5 @@ __copy_from_user_inatomic_nocache(void *to, const void __user *from, unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n); -unsigned long __must_check _copy_from_user(void *to, - const void __user *from, - unsigned long n); - - -extern void copy_from_user_overflow(void) -#ifdef CONFIG_DEBUG_STRICT_USER_COPY_CHECKS - __compiletime_error("copy_from_user() buffer size is not provably correct") -#else - __compiletime_warning("copy_from_user() buffer size is not provably correct") -#endif -; - -static inline unsigned long __must_check copy_from_user(void *to, - const void __user *from, - unsigned long n) -{ - int sz = __compiletime_object_size(to); - - if (likely(sz == -1 || sz >= n)) - n = _copy_from_user(to, from, n); - else - copy_from_user_overflow(); - - return n; -} #endif /* _ASM_X86_UACCESS_32_H */ diff --git a/arch/x86/include/asm/uaccess_64.h b/arch/x86/include/asm/uaccess_64.h index 4f7923d..4df93c4 100644 --- a/arch/x86/include/asm/uaccess_64.h +++ b/arch/x86/include/asm/uaccess_64.h @@ -48,26 +48,8 @@ copy_user_generic(void *to, const void *from, unsigned len) __must_check unsigned long _copy_to_user(void __user *to, const void *from, unsigned len); __must_check unsigned long -_copy_from_user(void *to, const void __user *from, unsigned len); -__must_check unsigned long copy_in_user(void __user *to, const void __user *from, unsigned len); -static inline unsigned long __must_check copy_from_user(void *to, - const void __user *from, - unsigned long n) -{ - int sz = __compiletime_object_size(to); - - might_fault(); - if (likely(sz == -1 || sz >= n)) - n = _copy_from_user(to, from, n); -#ifdef CONFIG_DEBUG_VM - else - WARN(1, "Buffer overflow detected!\n"); -#endif - return n; -} - static __always_inline __must_check int copy_to_user(void __user *dst, const void *src, unsigned size) { diff --git a/arch/x86/lib/usercopy_32.c b/arch/x86/lib/usercopy_32.c index 3eb18ac..c408d02 100644 --- a/arch/x86/lib/usercopy_32.c +++ b/arch/x86/lib/usercopy_32.c @@ -679,8 +679,7 @@ EXPORT_SYMBOL(copy_to_user); * If some data could not be copied, this function will pad the copied * data to the requested size using zero bytes. */ -unsigned long -_copy_from_user(void *to, const void __user *from, unsigned long n) +unsigned long _copy_from_user(void *to, const void __user *from, unsigned n) { if (access_ok(VERIFY_READ, from, n)) n = __copy_from_user(to, from, n); -- cgit v0.10.2 From 7a3d9b0f3abbea957b829cdfff8169872c575642 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Mon, 21 Oct 2013 09:44:37 +0100 Subject: x86: Unify copy_to_user() and add size checking to it Similarly to copy_from_user(), where the range check is to protect against kernel memory corruption, copy_to_user() can benefit from such checking too: Here it protects against kernel information leaks. Signed-off-by: Jan Beulich Cc: Cc: Linus Torvalds Cc: Andrew Morton Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/5265059502000078000FC4F6@nat28.tlf.novell.com Signed-off-by: Ingo Molnar Cc: Arjan van de Ven diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h index c9799ed..8ec57c0 100644 --- a/arch/x86/include/asm/uaccess.h +++ b/arch/x86/include/asm/uaccess.h @@ -544,6 +544,8 @@ extern struct movsl_mask { unsigned long __must_check _copy_from_user(void *to, const void __user *from, unsigned n); +unsigned long __must_check _copy_to_user(void __user *to, const void *from, + unsigned n); #ifdef CONFIG_DEBUG_STRICT_USER_COPY_CHECKS # define copy_user_diag __compiletime_error @@ -553,6 +555,8 @@ unsigned long __must_check _copy_from_user(void *to, const void __user *from, extern void copy_user_diag("copy_from_user() buffer size is too small") copy_from_user_overflow(void); +extern void copy_user_diag("copy_to_user() buffer size is too small") +copy_to_user_overflow(void) __asm__("copy_from_user_overflow"); #undef copy_user_diag @@ -563,6 +567,11 @@ __compiletime_warning("copy_from_user() buffer size is not provably correct") __copy_from_user_overflow(void) __asm__("copy_from_user_overflow"); #define __copy_from_user_overflow(size, count) __copy_from_user_overflow() +extern void +__compiletime_warning("copy_to_user() buffer size is not provably correct") +__copy_to_user_overflow(void) __asm__("copy_from_user_overflow"); +#define __copy_to_user_overflow(size, count) __copy_to_user_overflow() + #else static inline void @@ -571,6 +580,8 @@ __copy_from_user_overflow(int size, unsigned long count) WARN(1, "Buffer overflow detected (%d < %lu)!\n", size, count); } +#define __copy_to_user_overflow __copy_from_user_overflow + #endif static inline unsigned long __must_check @@ -608,7 +619,26 @@ copy_from_user(void *to, const void __user *from, unsigned long n) return n; } +static inline unsigned long __must_check +copy_to_user(void __user *to, const void *from, unsigned long n) +{ + int sz = __compiletime_object_size(from); + + might_fault(); + + /* See the comment in copy_from_user() above. */ + if (likely(sz < 0 || sz >= n)) + n = _copy_to_user(to, from, n); + else if(__builtin_constant_p(n)) + copy_to_user_overflow(); + else + __copy_to_user_overflow(sz, n); + + return n; +} + #undef __copy_from_user_overflow +#undef __copy_to_user_overflow #endif /* _ASM_X86_UACCESS_H */ diff --git a/arch/x86/include/asm/uaccess_32.h b/arch/x86/include/asm/uaccess_32.h index c348c87..3c03a5d 100644 --- a/arch/x86/include/asm/uaccess_32.h +++ b/arch/x86/include/asm/uaccess_32.h @@ -184,7 +184,4 @@ __copy_from_user_inatomic_nocache(void *to, const void __user *from, return __copy_from_user_ll_nocache_nozero(to, from, n); } -unsigned long __must_check copy_to_user(void __user *to, - const void *from, unsigned long n); - #endif /* _ASM_X86_UACCESS_32_H */ diff --git a/arch/x86/include/asm/uaccess_64.h b/arch/x86/include/asm/uaccess_64.h index 4df93c4..0acae71 100644 --- a/arch/x86/include/asm/uaccess_64.h +++ b/arch/x86/include/asm/uaccess_64.h @@ -46,19 +46,9 @@ copy_user_generic(void *to, const void *from, unsigned len) } __must_check unsigned long -_copy_to_user(void __user *to, const void *from, unsigned len); -__must_check unsigned long copy_in_user(void __user *to, const void __user *from, unsigned len); static __always_inline __must_check -int copy_to_user(void __user *dst, const void *src, unsigned size) -{ - might_fault(); - - return _copy_to_user(dst, src, size); -} - -static __always_inline __must_check int __copy_from_user(void *dst, const void __user *src, unsigned size) { int ret = 0; diff --git a/arch/x86/lib/usercopy_32.c b/arch/x86/lib/usercopy_32.c index c408d02..e2f5e21 100644 --- a/arch/x86/lib/usercopy_32.c +++ b/arch/x86/lib/usercopy_32.c @@ -654,14 +654,13 @@ EXPORT_SYMBOL(__copy_from_user_ll_nocache_nozero); * Returns number of bytes that could not be copied. * On success, this will be zero. */ -unsigned long -copy_to_user(void __user *to, const void *from, unsigned long n) +unsigned long _copy_to_user(void __user *to, const void *from, unsigned n) { if (access_ok(VERIFY_WRITE, to, n)) n = __copy_to_user(to, from, n); return n; } -EXPORT_SYMBOL(copy_to_user); +EXPORT_SYMBOL(_copy_to_user); /** * copy_from_user: - Copy a block of data from user space. -- cgit v0.10.2 From 09dc68d958c67c76cf672ec78b7391af453010f8 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Mon, 21 Oct 2013 09:35:20 +0100 Subject: x86/cpu: Track legacy CPU model data only on 32-bit kernels struct cpu_dev's c_models is only ever set inside CONFIG_X86_32 conditionals (or code that's being built for 32-bit only), so there's no use of reserving the (empty) space for the model names in a 64-bit kernel. Similarly, c_size_cache is only used in the #else of a CONFIG_X86_64 conditional, so reserving space for (and in one case even initializing) that field is pointless for 64-bit kernels too. While moving both fields to the end of the structure, I also noticed that: - the c_models array size was one too small, potentially causing table_lookup_model() to return garbage on Intel CPUs (intel.c's instance was lacking the sentinel with family being zero), so the patch bumps that by one, - c_models' vendor sub-field was unused (and anyway redundant with the base structure's c_x86_vendor field), so the patch deletes it. Also rename the legacy fields so that their legacy nature stands out and comment their declarations. Signed-off-by: Jan Beulich Link: http://lkml.kernel.org/r/5265036802000078000FC4DB@nat28.tlf.novell.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 903a264..3daece7 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -823,8 +823,8 @@ static const struct cpu_dev amd_cpu_dev = { .c_vendor = "AMD", .c_ident = { "AuthenticAMD" }, #ifdef CONFIG_X86_32 - .c_models = { - { .vendor = X86_VENDOR_AMD, .family = 4, .model_names = + .legacy_models = { + { .family = 4, .model_names = { [3] = "486 DX/2", [7] = "486 DX/2-WB", @@ -835,7 +835,7 @@ static const struct cpu_dev amd_cpu_dev = { } }, }, - .c_size_cache = amd_size_cache, + .legacy_cache_size = amd_size_cache, #endif .c_early_init = early_init_amd, .c_detect_tlb = cpu_detect_tlb_amd, diff --git a/arch/x86/kernel/cpu/centaur.c b/arch/x86/kernel/cpu/centaur.c index fbf6c3b..8d5652d 100644 --- a/arch/x86/kernel/cpu/centaur.c +++ b/arch/x86/kernel/cpu/centaur.c @@ -468,10 +468,10 @@ static void init_centaur(struct cpuinfo_x86 *c) #endif } +#ifdef CONFIG_X86_32 static unsigned int centaur_size_cache(struct cpuinfo_x86 *c, unsigned int size) { -#ifdef CONFIG_X86_32 /* VIA C3 CPUs (670-68F) need further shifting. */ if ((c->x86 == 6) && ((c->x86_model == 7) || (c->x86_model == 8))) size >>= 8; @@ -484,16 +484,18 @@ centaur_size_cache(struct cpuinfo_x86 *c, unsigned int size) if ((c->x86 == 6) && (c->x86_model == 9) && (c->x86_mask == 1) && (size == 65)) size -= 1; -#endif return size; } +#endif static const struct cpu_dev centaur_cpu_dev = { .c_vendor = "Centaur", .c_ident = { "CentaurHauls" }, .c_early_init = early_init_centaur, .c_init = init_centaur, - .c_size_cache = centaur_size_cache, +#ifdef CONFIG_X86_32 + .legacy_cache_size = centaur_size_cache, +#endif .c_x86_vendor = X86_VENDOR_CENTAUR, }; diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 2793d1f..9ada0b3 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -346,7 +346,8 @@ static void filter_cpuid_features(struct cpuinfo_x86 *c, bool warn) /* Look up CPU names by table lookup. */ static const char *table_lookup_model(struct cpuinfo_x86 *c) { - const struct cpu_model_info *info; +#ifdef CONFIG_X86_32 + const struct legacy_cpu_model_info *info; if (c->x86_model >= 16) return NULL; /* Range check */ @@ -354,13 +355,14 @@ static const char *table_lookup_model(struct cpuinfo_x86 *c) if (!this_cpu) return NULL; - info = this_cpu->c_models; + info = this_cpu->legacy_models; - while (info && info->family) { + while (info->family) { if (info->family == c->x86) return info->model_names[c->x86_model]; info++; } +#endif return NULL; /* Not found */ } @@ -450,8 +452,8 @@ void cpu_detect_cache_sizes(struct cpuinfo_x86 *c) c->x86_tlbsize += ((ebx >> 16) & 0xfff) + (ebx & 0xfff); #else /* do processor-specific cache resizing */ - if (this_cpu->c_size_cache) - l2size = this_cpu->c_size_cache(c, l2size); + if (this_cpu->legacy_cache_size) + l2size = this_cpu->legacy_cache_size(c, l2size); /* Allow user to override all this if necessary. */ if (cachesize_override != -1) diff --git a/arch/x86/kernel/cpu/cpu.h b/arch/x86/kernel/cpu/cpu.h index 4041c24..c37dc37 100644 --- a/arch/x86/kernel/cpu/cpu.h +++ b/arch/x86/kernel/cpu/cpu.h @@ -1,12 +1,6 @@ #ifndef ARCH_X86_CPU_H #define ARCH_X86_CPU_H -struct cpu_model_info { - int vendor; - int family; - const char *model_names[16]; -}; - /* attempt to consolidate cpu attributes */ struct cpu_dev { const char *c_vendor; @@ -14,15 +8,23 @@ struct cpu_dev { /* some have two possibilities for cpuid string */ const char *c_ident[2]; - struct cpu_model_info c_models[4]; - void (*c_early_init)(struct cpuinfo_x86 *); void (*c_bsp_init)(struct cpuinfo_x86 *); void (*c_init)(struct cpuinfo_x86 *); void (*c_identify)(struct cpuinfo_x86 *); void (*c_detect_tlb)(struct cpuinfo_x86 *); - unsigned int (*c_size_cache)(struct cpuinfo_x86 *, unsigned int); int c_x86_vendor; +#ifdef CONFIG_X86_32 + /* Optional vendor specific routine to obtain the cache size. */ + unsigned int (*legacy_cache_size)(struct cpuinfo_x86 *, + unsigned int); + + /* Family/stepping-based lookup table for model names. */ + struct legacy_cpu_model_info { + int family; + const char *model_names[16]; + } legacy_models[5]; +#endif }; struct _tlb_table { diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c index ec72995..dc1ec0d 100644 --- a/arch/x86/kernel/cpu/intel.c +++ b/arch/x86/kernel/cpu/intel.c @@ -665,8 +665,8 @@ static const struct cpu_dev intel_cpu_dev = { .c_vendor = "Intel", .c_ident = { "GenuineIntel" }, #ifdef CONFIG_X86_32 - .c_models = { - { .vendor = X86_VENDOR_INTEL, .family = 4, .model_names = + .legacy_models = { + { .family = 4, .model_names = { [0] = "486 DX-25/33", [1] = "486 DX-50", @@ -679,7 +679,7 @@ static const struct cpu_dev intel_cpu_dev = { [9] = "486 DX/4-WB" } }, - { .vendor = X86_VENDOR_INTEL, .family = 5, .model_names = + { .family = 5, .model_names = { [0] = "Pentium 60/66 A-step", [1] = "Pentium 60/66", @@ -690,7 +690,7 @@ static const struct cpu_dev intel_cpu_dev = { [8] = "Mobile Pentium MMX" } }, - { .vendor = X86_VENDOR_INTEL, .family = 6, .model_names = + { .family = 6, .model_names = { [0] = "Pentium Pro A-step", [1] = "Pentium Pro", @@ -704,7 +704,7 @@ static const struct cpu_dev intel_cpu_dev = { [11] = "Pentium III (Tualatin)", } }, - { .vendor = X86_VENDOR_INTEL, .family = 15, .model_names = + { .family = 15, .model_names = { [0] = "Pentium 4 (Unknown)", [1] = "Pentium 4 (Willamette)", @@ -714,7 +714,7 @@ static const struct cpu_dev intel_cpu_dev = { } }, }, - .c_size_cache = intel_size_cache, + .legacy_cache_size = intel_size_cache, #endif .c_detect_tlb = intel_detect_tlb, .c_early_init = early_init_intel, diff --git a/arch/x86/kernel/cpu/umc.c b/arch/x86/kernel/cpu/umc.c index 202759a..75c5ad5 100644 --- a/arch/x86/kernel/cpu/umc.c +++ b/arch/x86/kernel/cpu/umc.c @@ -11,8 +11,8 @@ static const struct cpu_dev umc_cpu_dev = { .c_vendor = "UMC", .c_ident = { "UMC UMC UMC" }, - .c_models = { - { .vendor = X86_VENDOR_UMC, .family = 4, .model_names = + .legacy_models = { + { .family = 4, .model_names = { [1] = "U5D", [2] = "U5S", -- cgit v0.10.2 From ee5872befc9324fa4c2583c24d7ee7120314a2b7 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Mon, 21 Oct 2013 09:31:57 +0100 Subject: x86/time: Honor ACPI FADT flag indicating absence of a CMOS RTC Even though the omission was found only during code review (originally in the Xen hypervisor, looking through ACPI v5 flags and their meanings and uses), we shouldn't be creating a corresponding platform device in that case. Signed-off-by: Jan Beulich Cc: John Stultz Link: http://lkml.kernel.org/r/5265029D02000078000FC4D2@nat28.tlf.novell.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/rtc.c b/arch/x86/kernel/rtc.c index 0aa2939..5b9dd44 100644 --- a/arch/x86/kernel/rtc.c +++ b/arch/x86/kernel/rtc.c @@ -192,6 +192,14 @@ static __init int add_rtc_cmos(void) if (mrst_identify_cpu()) return -ENODEV; +#ifdef CONFIG_ACPI + if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC) { + /* This warning can likely go away again in a year or two. */ + pr_info("ACPI: not registering RTC platform device\n"); + return -ENODEV; + } +#endif + platform_device_register(&rtc_device); dev_info(&rtc_device.dev, "registered platform RTC device (no PNP device found)\n"); -- cgit v0.10.2 From a9c9cafdde46d06a28f92e3a68b5534fa268e92d Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Sat, 26 Oct 2013 15:31:26 +0100 Subject: ASoC: wm5110: Add missing routes for AEC Loopback Reported-by: Nariman Poushin Signed-off-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index bbd6438..8c91be5 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -983,24 +983,36 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = { ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"), ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"), + { "AEC Loopback", "HPOUT1L", "OUT1L" }, + { "AEC Loopback", "HPOUT1R", "OUT1R" }, { "HPOUT1L", NULL, "OUT1L" }, { "HPOUT1R", NULL, "OUT1R" }, + { "AEC Loopback", "HPOUT2L", "OUT2L" }, + { "AEC Loopback", "HPOUT2R", "OUT2R" }, { "HPOUT2L", NULL, "OUT2L" }, { "HPOUT2R", NULL, "OUT2R" }, + { "AEC Loopback", "HPOUT3L", "OUT3L" }, + { "AEC Loopback", "HPOUT3R", "OUT3R" }, { "HPOUT3L", NULL, "OUT3L" }, { "HPOUT3R", NULL, "OUT3L" }, + { "AEC Loopback", "SPKOUTL", "OUT4L" }, { "SPKOUTLN", NULL, "OUT4L" }, { "SPKOUTLP", NULL, "OUT4L" }, + { "AEC Loopback", "SPKOUTR", "OUT4R" }, { "SPKOUTRN", NULL, "OUT4R" }, { "SPKOUTRP", NULL, "OUT4R" }, + { "AEC Loopback", "SPKDAT1L", "OUT5L" }, + { "AEC Loopback", "SPKDAT1R", "OUT5R" }, { "SPKDAT1L", NULL, "OUT5L" }, { "SPKDAT1R", NULL, "OUT5R" }, + { "AEC Loopback", "SPKDAT2L", "OUT6L" }, + { "AEC Loopback", "SPKDAT2R", "OUT6R" }, { "SPKDAT2L", NULL, "OUT6L" }, { "SPKDAT2R", NULL, "OUT6R" }, -- cgit v0.10.2 From 3d8f7318f929f3b84571ffac2ef7bf8bddfb6d41 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 25 Oct 2013 17:29:25 +0800 Subject: ASoC: fsl_spdif: fix return value check in fsl_spdif_probe() In case of error, the function platform_get_resource() returns NULL pointer not ERR_PTR(). The IS_ERR() test in the return value check should be replaced with NULL test. Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index c0fea02..e1bf5ef 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -1107,9 +1107,9 @@ static int fsl_spdif_probe(struct platform_device *pdev) /* Get the addresses and IRQ */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (IS_ERR(res)) { + if (!res) { dev_err(&pdev->dev, "could not determine device resources\n"); - return PTR_ERR(res); + return -ENXIO; } regs = devm_ioremap_resource(&pdev->dev, res); -- cgit v0.10.2 From 24799c22aea81a8890a66e101cd5a3b175980777 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Sat, 28 Sep 2013 03:07:21 +0400 Subject: sh-pfc: r8a7778: Add CAN pin groups Add CAN data and clock pin groups to R8A7778 PFC driver. Signed-off-by: Sergei Shtylyov Signed-off-by: Laurent Pinchart diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7778.c b/drivers/pinctrl/sh-pfc/pfc-r8a7778.c index 20b1d0d..8b1881c 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7778.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7778.c @@ -1304,6 +1304,33 @@ AUDIO_PFC_DAT(audio_clkout_a, AUDIO_CLKOUT_A); AUDIO_PFC_PIN(audio_clkout_b, RCAR_GP_PIN(1, 16)); AUDIO_PFC_DAT(audio_clkout_b, AUDIO_CLKOUT_B); +/* - CAN macro --------_----------------------------------------------------- */ +#define CAN_PFC_PINS(name, args...) SH_PFC_PINS(name, args) +#define CAN_PFC_DATA(name, tx, rx) SH_PFC_MUX2(name, tx, rx) +#define CAN_PFC_CLK(name, clk) SH_PFC_MUX1(name, clk) + +/* - CAN0 ------------------------------------------------------------------- */ +CAN_PFC_PINS(can0_data_a, RCAR_GP_PIN(1, 30), RCAR_GP_PIN(1, 31)); +CAN_PFC_DATA(can0_data_a, CAN0_TX_A, CAN0_RX_A); +CAN_PFC_PINS(can0_data_b, RCAR_GP_PIN(2, 26), RCAR_GP_PIN(2, 27)); +CAN_PFC_DATA(can0_data_b, CAN0_TX_B, CAN0_RX_B); + +/* - CAN1 ------------------------------------------------------------------- */ +CAN_PFC_PINS(can1_data_a, RCAR_GP_PIN(4, 20), RCAR_GP_PIN(4, 19)); +CAN_PFC_DATA(can1_data_a, CAN1_TX_A, CAN1_RX_A); +CAN_PFC_PINS(can1_data_b, RCAR_GP_PIN(2, 28), RCAR_GP_PIN(2, 29)); +CAN_PFC_DATA(can1_data_b, CAN1_TX_B, CAN1_RX_B); + +/* - CAN_CLK --------------------------------------------------------------- */ +CAN_PFC_PINS(can_clk_a, RCAR_GP_PIN(3, 24)); +CAN_PFC_CLK(can_clk_a, CAN_CLK_A); +CAN_PFC_PINS(can_clk_b, RCAR_GP_PIN(1, 16)); +CAN_PFC_CLK(can_clk_b, CAN_CLK_B); +CAN_PFC_PINS(can_clk_c, RCAR_GP_PIN(4, 24)); +CAN_PFC_CLK(can_clk_c, CAN_CLK_C); +CAN_PFC_PINS(can_clk_d, RCAR_GP_PIN(2, 25)); +CAN_PFC_CLK(can_clk_d, CAN_CLK_D); + /* - Ether ------------------------------------------------------------------ */ SH_PFC_PINS(ether_rmii, RCAR_GP_PIN(4, 10), RCAR_GP_PIN(4, 11), RCAR_GP_PIN(4, 13), RCAR_GP_PIN(4, 9), @@ -1698,6 +1725,14 @@ static const struct sh_pfc_pin_group pinmux_groups[] = { SH_PFC_PIN_GROUP(audio_clk_c), SH_PFC_PIN_GROUP(audio_clkout_a), SH_PFC_PIN_GROUP(audio_clkout_b), + SH_PFC_PIN_GROUP(can0_data_a), + SH_PFC_PIN_GROUP(can0_data_b), + SH_PFC_PIN_GROUP(can1_data_a), + SH_PFC_PIN_GROUP(can1_data_b), + SH_PFC_PIN_GROUP(can_clk_a), + SH_PFC_PIN_GROUP(can_clk_b), + SH_PFC_PIN_GROUP(can_clk_c), + SH_PFC_PIN_GROUP(can_clk_d), SH_PFC_PIN_GROUP(ether_rmii), SH_PFC_PIN_GROUP(ether_link), SH_PFC_PIN_GROUP(ether_magic), @@ -1826,6 +1861,24 @@ static const char * const audio_clk_groups[] = { "audio_clkout_b", }; +static const char * const can0_groups[] = { + "can0_data_a", + "can0_data_b", + "can_clk_a", + "can_clk_b", + "can_clk_c", + "can_clk_d", +}; + +static const char * const can1_groups[] = { + "can1_data_a", + "can1_data_b", + "can_clk_a", + "can_clk_b", + "can_clk_c", + "can_clk_d", +}; + static const char * const ether_groups[] = { "ether_rmii", "ether_link", @@ -2022,6 +2075,8 @@ static const char * const vin1_groups[] = { static const struct sh_pfc_function pinmux_functions[] = { SH_PFC_FUNCTION(audio_clk), + SH_PFC_FUNCTION(can0), + SH_PFC_FUNCTION(can1), SH_PFC_FUNCTION(ether), SH_PFC_FUNCTION(hscif0), SH_PFC_FUNCTION(hscif1), -- cgit v0.10.2 From 5088451962389924b9f05e22e6956f5c1a515d1a Mon Sep 17 00:00:00 2001 From: Hisashi Nakamura Date: Thu, 17 Oct 2013 06:46:05 +0900 Subject: pinctrl: sh-pfc: r8a7791 PFC support Add PFC support for the r8a7791 SoC V2 including pin groups for on-chip devices such as MSIOF, SCIF, USB, MMC, SDHI, DU. Signed-off-by: Hisashi Nakamura Signed-off-by: Kunihito Higashiyama Signed-off-by: Yoshikazu Fujikawa Signed-off-by: Nobuyuki HIRAI Signed-off-by: Shinobu Uehara Signed-off-by: Koji Matsuoka Signed-off-by: Ryo Kataoka [damm@opensource.se: Forward ported to upstream, minor fixes] Signed-off-by: Magnus Damm Signed-off-by: Laurent Pinchart diff --git a/drivers/pinctrl/sh-pfc/Kconfig b/drivers/pinctrl/sh-pfc/Kconfig index 636a882..26187aa 100644 --- a/drivers/pinctrl/sh-pfc/Kconfig +++ b/drivers/pinctrl/sh-pfc/Kconfig @@ -45,6 +45,11 @@ config PINCTRL_PFC_R8A7790 depends on ARCH_R8A7790 select PINCTRL_SH_PFC +config PINCTRL_PFC_R8A7791 + def_bool y + depends on ARCH_R8A7791 + select PINCTRL_SH_PFC + config PINCTRL_PFC_SH7203 def_bool y depends on CPU_SUBTYPE_SH7203 diff --git a/drivers/pinctrl/sh-pfc/Makefile b/drivers/pinctrl/sh-pfc/Makefile index 5e0c222..ad8f4cf 100644 --- a/drivers/pinctrl/sh-pfc/Makefile +++ b/drivers/pinctrl/sh-pfc/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_PINCTRL_PFC_R8A7740) += pfc-r8a7740.o obj-$(CONFIG_PINCTRL_PFC_R8A7778) += pfc-r8a7778.o obj-$(CONFIG_PINCTRL_PFC_R8A7779) += pfc-r8a7779.o obj-$(CONFIG_PINCTRL_PFC_R8A7790) += pfc-r8a7790.o +obj-$(CONFIG_PINCTRL_PFC_R8A7791) += pfc-r8a7791.o obj-$(CONFIG_PINCTRL_PFC_SH7203) += pfc-sh7203.o obj-$(CONFIG_PINCTRL_PFC_SH7264) += pfc-sh7264.o obj-$(CONFIG_PINCTRL_PFC_SH7269) += pfc-sh7269.o diff --git a/drivers/pinctrl/sh-pfc/core.c b/drivers/pinctrl/sh-pfc/core.c index 738f14f..d77ece5 100644 --- a/drivers/pinctrl/sh-pfc/core.c +++ b/drivers/pinctrl/sh-pfc/core.c @@ -431,6 +431,12 @@ static const struct of_device_id sh_pfc_of_table[] = { .data = &r8a7790_pinmux_info, }, #endif +#ifdef CONFIG_PINCTRL_PFC_R8A7791 + { + .compatible = "renesas,pfc-r8a7791", + .data = &r8a7791_pinmux_info, + }, +#endif #ifdef CONFIG_PINCTRL_PFC_SH7372 { .compatible = "renesas,pfc-sh7372", @@ -558,6 +564,9 @@ static const struct platform_device_id sh_pfc_id_table[] = { #ifdef CONFIG_PINCTRL_PFC_R8A7790 { "pfc-r8a7790", (kernel_ulong_t)&r8a7790_pinmux_info }, #endif +#ifdef CONFIG_PINCTRL_PFC_R8A7791 + { "pfc-r8a7791", (kernel_ulong_t)&r8a7791_pinmux_info }, +#endif #ifdef CONFIG_PINCTRL_PFC_SH7203 { "pfc-sh7203", (kernel_ulong_t)&sh7203_pinmux_info }, #endif diff --git a/drivers/pinctrl/sh-pfc/core.h b/drivers/pinctrl/sh-pfc/core.h index a1b2376..11ea872 100644 --- a/drivers/pinctrl/sh-pfc/core.h +++ b/drivers/pinctrl/sh-pfc/core.h @@ -69,6 +69,7 @@ extern const struct sh_pfc_soc_info r8a7740_pinmux_info; extern const struct sh_pfc_soc_info r8a7778_pinmux_info; extern const struct sh_pfc_soc_info r8a7779_pinmux_info; extern const struct sh_pfc_soc_info r8a7790_pinmux_info; +extern const struct sh_pfc_soc_info r8a7791_pinmux_info; extern const struct sh_pfc_soc_info sh7203_pinmux_info; extern const struct sh_pfc_soc_info sh7264_pinmux_info; extern const struct sh_pfc_soc_info sh7269_pinmux_info; diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7791.c b/drivers/pinctrl/sh-pfc/pfc-r8a7791.c new file mode 100644 index 0000000..bf76a65 --- /dev/null +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7791.c @@ -0,0 +1,4214 @@ +/* + * r8a7791 processor support - PFC hardware block. + * + * Copyright (C) 2013 Renesas Electronics Corporation + * + * 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. + */ + +#include +#include + +#include "core.h" +#include "sh_pfc.h" + +#define CPU_ALL_PORT(fn, sfx) \ + PORT_GP_32(0, fn, sfx), \ + PORT_GP_32(1, fn, sfx), \ + PORT_GP_32(2, fn, sfx), \ + PORT_GP_32(3, fn, sfx), \ + PORT_GP_32(4, fn, sfx), \ + PORT_GP_32(5, fn, sfx), \ + PORT_GP_32(6, fn, sfx), \ + PORT_GP_32(7, fn, sfx) + +enum { + PINMUX_RESERVED = 0, + + PINMUX_DATA_BEGIN, + GP_ALL(DATA), + PINMUX_DATA_END, + + PINMUX_FUNCTION_BEGIN, + GP_ALL(FN), + + /* GPSR0 */ + FN_IP0_0, FN_IP0_1, FN_IP0_2, FN_IP0_3, FN_IP0_4, FN_IP0_5, + FN_IP0_6, FN_IP0_7, FN_IP0_8, FN_IP0_9, FN_IP0_10, FN_IP0_11, + FN_IP0_12, FN_IP0_13, FN_IP0_14, FN_IP0_15, FN_IP0_18_16, FN_IP0_20_19, + FN_IP0_22_21, FN_IP0_24_23, FN_IP0_26_25, FN_IP0_28_27, FN_IP0_30_29, + FN_IP1_1_0, FN_IP1_3_2, FN_IP1_5_4, FN_IP1_7_6, FN_IP1_10_8, + FN_IP1_13_11, FN_IP1_16_14, FN_IP1_19_17, FN_IP1_22_20, + + /* GPSR1 */ + FN_IP1_25_23, FN_IP1_28_26, FN_IP1_31_29, FN_IP2_2_0, FN_IP2_4_3, + FN_IP2_6_5, FN_IP2_9_7, FN_IP2_12_10, FN_IP2_15_13, FN_IP2_18_16, + FN_IP2_20_19, FN_IP2_22_21, FN_EX_CS0_N, FN_IP2_24_23, FN_IP2_26_25, + FN_IP2_29_27, FN_IP3_2_0, FN_IP3_5_3, FN_IP3_8_6, FN_RD_N, + FN_IP3_11_9, FN_IP3_13_12, FN_IP3_15_14 , FN_IP3_17_16 , FN_IP3_19_18, + FN_IP3_21_20, + + /* GPSR2 */ + FN_IP3_27_25, FN_IP3_30_28, FN_IP4_1_0, FN_IP4_4_2, FN_IP4_7_5, + FN_IP4_9_8, FN_IP4_12_10, FN_IP4_15_13, FN_IP4_18_16, FN_IP4_19, + FN_IP4_20, FN_IP4_21, FN_IP4_23_22, FN_IP4_25_24, FN_IP4_27_26, + FN_IP4_30_28, FN_IP5_2_0, FN_IP5_5_3, FN_IP5_8_6, FN_IP5_11_9, + FN_IP5_14_12, FN_IP5_16_15, FN_IP5_19_17, FN_IP5_21_20, FN_IP5_23_22, + FN_IP5_25_24, FN_IP5_28_26, FN_IP5_31_29, FN_AUDIO_CLKA, FN_IP6_2_0, + FN_IP6_5_3, FN_IP6_7_6, + + /* GPSR3 */ + FN_IP7_5_3, FN_IP7_8_6, FN_IP7_10_9, FN_IP7_12_11, FN_IP7_14_13, + FN_IP7_16_15, FN_IP7_18_17, FN_IP7_20_19, FN_IP7_23_21, FN_IP7_26_24, + FN_IP7_29_27, FN_IP8_2_0, FN_IP8_5_3, FN_IP8_8_6, FN_IP8_11_9, + FN_IP8_14_12, FN_IP8_17_15, FN_IP8_20_18, FN_IP8_23_21, FN_IP8_25_24, + FN_IP8_27_26, FN_IP8_30_28, FN_IP9_2_0, FN_IP9_5_3, FN_IP9_6, FN_IP9_7, + FN_IP9_10_8, FN_IP9_11, FN_IP9_12, FN_IP9_15_13, FN_IP9_16, + FN_IP9_18_17, + + /* GPSR4 */ + FN_VI0_CLK, FN_IP9_20_19, FN_IP9_22_21, FN_IP9_24_23, FN_IP9_26_25, + FN_VI0_DATA0_VI0_B0, FN_VI0_DATA1_VI0_B1, FN_VI0_DATA2_VI0_B2, + FN_IP9_28_27, FN_VI0_DATA4_VI0_B4, FN_VI0_DATA5_VI0_B5, + FN_VI0_DATA6_VI0_B6, FN_VI0_DATA7_VI0_B7, FN_IP9_31_29, FN_IP10_2_0, + FN_IP10_5_3, FN_IP10_8_6, FN_IP10_11_9, FN_IP10_14_12, FN_IP10_16_15, + FN_IP10_18_17, FN_IP10_21_19, FN_IP10_24_22, FN_IP10_26_25, + FN_IP10_28_27, FN_IP10_31_29, FN_IP11_2_0, FN_IP11_5_3, FN_IP11_8_6, + FN_IP15_1_0, FN_IP15_3_2, FN_IP15_5_4, + + /* GPSR5 */ + FN_IP11_11_9, FN_IP11_14_12, FN_IP11_16_15, FN_IP11_18_17, FN_IP11_19, + FN_IP11_20, FN_IP11_21, FN_IP11_22, FN_IP11_23, FN_IP11_24, + FN_IP11_25, FN_IP11_26, FN_IP11_27, FN_IP11_29_28, FN_IP11_31_30, + FN_IP12_1_0, FN_IP12_3_2, FN_IP12_6_4, FN_IP12_9_7, FN_IP12_12_10, + FN_IP12_15_13, FN_IP12_17_16, FN_IP12_19_18, FN_IP12_21_20, + FN_IP12_23_22, FN_IP12_26_24, FN_IP12_29_27, FN_IP13_2_0, FN_IP13_4_3, + FN_IP13_6_5, FN_IP13_9_7, FN_IP3_24_22, + + /* GPSR6 */ + FN_IP13_10, FN_IP13_11, FN_IP13_12, FN_IP13_13, FN_IP13_14, + FN_IP13_15, FN_IP13_18_16, FN_IP13_21_19, FN_IP13_22, FN_IP13_24_23, + FN_IP13_25, FN_IP13_26, FN_IP13_27, FN_IP13_30_28, FN_IP14_1_0, + FN_IP14_2, FN_IP14_3, FN_IP14_4, FN_IP14_5, FN_IP14_6, FN_IP14_7, + FN_IP14_10_8, FN_IP14_13_11, FN_IP14_16_14, FN_IP14_19_17, + FN_IP14_22_20, FN_IP14_25_23, FN_IP14_28_26, FN_IP14_31_29, + FN_USB1_OVC, FN_DU0_DOTCLKIN, + + /* GPSR7 */ + FN_IP15_17_15, FN_IP15_20_18, FN_IP15_23_21, FN_IP15_26_24, + FN_IP15_29_27, FN_IP16_2_0, FN_IP16_5_3, FN_IP16_7_6, FN_IP16_9_8, + FN_IP16_11_10, FN_IP6_9_8, FN_IP6_11_10, FN_IP6_13_12, FN_IP6_15_14, + FN_IP6_18_16, FN_IP6_20_19, FN_IP6_23_21, FN_IP6_26_24, FN_IP6_29_27, + FN_IP7_2_0, FN_IP15_8_6, FN_IP15_11_9, FN_IP15_14_12, + FN_USB0_PWEN, FN_USB0_OVC, FN_USB1_PWEN, + + /* IPSR0 */ + FN_D0, FN_D1, FN_D2, FN_D3, FN_D4, FN_D5, FN_D6, FN_D7, FN_D8, + FN_D9, FN_D10, FN_D11, FN_D12, FN_D13, FN_D14, FN_D15, + FN_A0, FN_ATAWR0_N_C, FN_MSIOF0_SCK_B, FN_SCL0_C, FN_PWM2_B, + FN_A1, FN_MSIOF0_SYNC_B, FN_A2, FN_MSIOF0_SS1_B, + FN_A3, FN_MSIOF0_SS2_B, FN_A4, FN_MSIOF0_TXD_B, + FN_A5, FN_MSIOF0_RXD_B, FN_A6, FN_MSIOF1_SCK, + + /* IPSR1 */ + FN_A7, FN_MSIOF1_SYNC, FN_A8, FN_MSIOF1_SS1, FN_SCL0, + FN_A9, FN_MSIOF1_SS2, FN_SDA0, + FN_A10, FN_MSIOF1_TXD, FN_MSIOF1_TXD_D, + FN_A11, FN_MSIOF1_RXD, FN_SCL3_D, FN_MSIOF1_RXD_D, + FN_A12, FN_FMCLK, FN_SDA3_D, FN_MSIOF1_SCK_D, + FN_A13, FN_ATAG0_N_C, FN_BPFCLK, FN_MSIOF1_SS1_D, + FN_A14, FN_ATADIR0_N_C, FN_FMIN, FN_FMIN_C, FN_MSIOF1_SYNC_D, + FN_A15, FN_BPFCLK_C, + FN_A16, FN_DREQ2_B, FN_FMCLK_C, FN_SCIFA1_SCK_B, + FN_A17, FN_DACK2_B, FN_SDA0_C, + FN_A18, FN_DREQ1, FN_SCIFA1_RXD_C, FN_SCIFB1_RXD_C, + + /* IPSR2 */ + FN_A19, FN_DACK1, FN_SCIFA1_TXD_C, FN_SCIFB1_TXD_C, FN_SCIFB1_SCK_B, + FN_A20, FN_SPCLK, + FN_A21, FN_ATAWR0_N_B, FN_MOSI_IO0, + FN_A22, FN_MISO_IO1, FN_FMCLK_B, FN_TX0, FN_SCIFA0_TXD, + FN_A23, FN_IO2, FN_BPFCLK_B, FN_RX0, FN_SCIFA0_RXD, + FN_A24, FN_DREQ2, FN_IO3, FN_TX1, FN_SCIFA1_TXD, + FN_A25, FN_DACK2, FN_SSL, FN_DREQ1_C, FN_RX1, FN_SCIFA1_RXD, + FN_CS0_N, FN_ATAG0_N_B, FN_SCL1, + FN_CS1_N_A26, FN_ATADIR0_N_B, FN_SDA1, + FN_EX_CS1_N, FN_MSIOF2_SCK, + FN_EX_CS2_N, FN_ATAWR0_N, FN_MSIOF2_SYNC, + FN_EX_CS3_N, FN_ATADIR0_N, FN_MSIOF2_TXD, FN_ATAG0_N, FN_EX_WAIT1, + + /* IPSR3 */ + FN_EX_CS4_N, FN_ATARD0_N, FN_MSIOF2_RXD, FN_EX_WAIT2, + FN_EX_CS5_N, FN_ATACS00_N, FN_MSIOF2_SS1, FN_HRX1_B, + FN_SCIFB1_RXD_B, FN_PWM1, FN_TPU_TO1, + FN_BS_N, FN_ATACS10_N, FN_MSIOF2_SS2, FN_HTX1_B, + FN_SCIFB1_TXD_B, FN_PWM2, FN_TPU_TO2, + FN_RD_WR_N, FN_HRX2_B, FN_FMIN_B, FN_SCIFB0_RXD_B, FN_DREQ1_D, + FN_WE0_N, FN_HCTS2_N_B, FN_SCIFB0_TXD_B, + FN_WE1_N, FN_ATARD0_N_B, FN_HTX2_B, FN_SCIFB0_RTS_N_B, + FN_EX_WAIT0, FN_HRTS2_N_B, FN_SCIFB0_CTS_N_B, + FN_DREQ0, FN_PWM3, FN_TPU_TO3, + FN_DACK0, FN_DRACK0, FN_REMOCON, + FN_SPEEDIN, FN_HSCK0_C, FN_HSCK2_C, FN_SCIFB0_SCK_B, + FN_SCIFB2_SCK_B, FN_DREQ2_C, FN_HTX2_D, + FN_SSI_SCK0129, FN_HRX0_C, FN_HRX2_C, FN_SCIFB0_RXD_C, FN_SCIFB2_RXD_C, + FN_SSI_WS0129, FN_HTX0_C, FN_HTX2_C, FN_SCIFB0_TXD_C, FN_SCIFB2_TXD_C, + + /* IPSR4 */ + FN_SSI_SDATA0, FN_SCL0_B, FN_SCL7_B, FN_MSIOF2_SCK_C, + FN_SSI_SCK1, FN_SDA0_B, FN_SDA7_B, FN_MSIOF2_SYNC_C, FN_GLO_I0_D, + FN_SSI_WS1, FN_SCL1_B, FN_SCL8_B, FN_MSIOF2_TXD_C, FN_GLO_I1_D, + FN_SSI_SDATA1, FN_SDA1_B, FN_SDA8_B, FN_MSIOF2_RXD_C, + FN_SSI_SCK2, FN_SCL2, FN_GPS_CLK_B, FN_GLO_Q0_D, FN_HSCK1_E, + FN_SSI_WS2, FN_SDA2, FN_GPS_SIGN_B, FN_RX2_E, + FN_GLO_Q1_D, FN_HCTS1_N_E, + FN_SSI_SDATA2, FN_GPS_MAG_B, FN_TX2_E, FN_HRTS1_N_E, + FN_SSI_SCK34, FN_SSI_WS34, FN_SSI_SDATA3, + FN_SSI_SCK4, FN_GLO_SS_D, + FN_SSI_WS4, FN_GLO_RFON_D, + FN_SSI_SDATA4, FN_MSIOF2_SCK_D, + FN_SSI_SCK5, FN_MSIOF1_SCK_C, FN_TS_SDATA0, FN_GLO_I0, + FN_MSIOF2_SYNC_D, FN_VI1_R2_B, + + /* IPSR5 */ + FN_SSI_WS5, FN_MSIOF1_SYNC_C, FN_TS_SCK0, FN_GLO_I1, + FN_MSIOF2_TXD_D, FN_VI1_R3_B, + FN_SSI_SDATA5, FN_MSIOF1_TXD_C, FN_TS_SDEN0, FN_GLO_Q0, + FN_MSIOF2_SS1_D, FN_VI1_R4_B, + FN_SSI_SCK6, FN_MSIOF1_RXD_C, FN_TS_SPSYNC0, FN_GLO_Q1, + FN_MSIOF2_RXD_D, FN_VI1_R5_B, + FN_SSI_WS6, FN_GLO_SCLK, FN_MSIOF2_SS2_D, FN_VI1_R6_B, + FN_SSI_SDATA6, FN_STP_IVCXO27_0_B, FN_GLO_SDATA, FN_VI1_R7_B, + FN_SSI_SCK78, FN_STP_ISCLK_0_B, FN_GLO_SS, + FN_SSI_WS78, FN_TX0_D, FN_STP_ISD_0_B, FN_GLO_RFON, + FN_SSI_SDATA7, FN_RX0_D, FN_STP_ISEN_0_B, + FN_SSI_SDATA8, FN_TX1_D, FN_STP_ISSYNC_0_B, + FN_SSI_SCK9, FN_RX1_D, FN_GLO_SCLK_D, + FN_SSI_WS9, FN_TX3_D, FN_CAN0_TX_D, FN_GLO_SDATA_D, + FN_SSI_SDATA9, FN_RX3_D, FN_CAN0_RX_D, + + /* IPSR6 */ + FN_AUDIO_CLKB, FN_STP_OPWM_0_B, FN_MSIOF1_SCK_B, + FN_SCIF_CLK, FN_BPFCLK_E, + FN_AUDIO_CLKC, FN_SCIFB0_SCK_C, FN_MSIOF1_SYNC_B, FN_RX2, + FN_SCIFA2_RXD, FN_FMIN_E, + FN_AUDIO_CLKOUT, FN_MSIOF1_SS1_B, FN_TX2, FN_SCIFA2_TXD, + FN_IRQ0, FN_SCIFB1_RXD_D, FN_INTC_IRQ0_N, + FN_IRQ1, FN_SCIFB1_SCK_C, FN_INTC_IRQ1_N, + FN_IRQ2, FN_SCIFB1_TXD_D, FN_INTC_IRQ2_N, + FN_IRQ3, FN_SCL4_C, FN_MSIOF2_TXD_E, FN_INTC_IRQ3_N, + FN_IRQ4, FN_HRX1_C, FN_SDA4_C, FN_MSIOF2_RXD_E, FN_INTC_IRQ4_N, + FN_IRQ5, FN_HTX1_C, FN_SCL1_E, FN_MSIOF2_SCK_E, + FN_IRQ6, FN_HSCK1_C, FN_MSIOF1_SS2_B, FN_SDA1_E, FN_MSIOF2_SYNC_E, + FN_IRQ7, FN_HCTS1_N_C, FN_MSIOF1_TXD_B, FN_GPS_CLK_C, FN_GPS_CLK_D, + FN_IRQ8, FN_HRTS1_N_C, FN_MSIOF1_RXD_B, FN_GPS_SIGN_C, FN_GPS_SIGN_D, + + /* IPSR7 */ + FN_IRQ9, FN_DU1_DOTCLKIN_B, FN_CAN_CLK_D, FN_GPS_MAG_C, + FN_SCIF_CLK_B, FN_GPS_MAG_D, + FN_DU1_DR0, FN_LCDOUT0, FN_VI1_DATA0_B, FN_TX0_B, + FN_SCIFA0_TXD_B, FN_MSIOF2_SCK_B, + FN_DU1_DR1, FN_LCDOUT1, FN_VI1_DATA1_B, FN_RX0_B, + FN_SCIFA0_RXD_B, FN_MSIOF2_SYNC_B, + FN_DU1_DR2, FN_LCDOUT2, FN_SSI_SCK0129_B, + FN_DU1_DR3, FN_LCDOUT3, FN_SSI_WS0129_B, + FN_DU1_DR4, FN_LCDOUT4, FN_SSI_SDATA0_B, + FN_DU1_DR5, FN_LCDOUT5, FN_SSI_SCK1_B, + FN_DU1_DR6, FN_LCDOUT6, FN_SSI_WS1_B, + FN_DU1_DR7, FN_LCDOUT7, FN_SSI_SDATA1_B, + FN_DU1_DG0, FN_LCDOUT8, FN_VI1_DATA2_B, FN_TX1_B, + FN_SCIFA1_TXD_B, FN_MSIOF2_SS1_B, + FN_DU1_DG1, FN_LCDOUT9, FN_VI1_DATA3_B, FN_RX1_B, + FN_SCIFA1_RXD_B, FN_MSIOF2_SS2_B, + FN_DU1_DG2, FN_LCDOUT10, FN_VI1_DATA4_B, FN_SCIF1_SCK_B, + FN_SCIFA1_SCK, FN_SSI_SCK78_B, + + /* IPSR8 */ + FN_DU1_DG3, FN_LCDOUT11, FN_VI1_DATA5_B, FN_SSI_WS78_B, + FN_DU1_DG4, FN_LCDOUT12, FN_VI1_DATA6_B, FN_HRX0_B, + FN_SCIFB2_RXD_B, FN_SSI_SDATA7_B, + FN_DU1_DG5, FN_LCDOUT13, FN_VI1_DATA7_B, FN_HCTS0_N_B, + FN_SCIFB2_TXD_B, FN_SSI_SDATA8_B, + FN_DU1_DG6, FN_LCDOUT14, FN_HRTS0_N_B, + FN_SCIFB2_CTS_N_B, FN_SSI_SCK9_B, + FN_DU1_DG7, FN_LCDOUT15, FN_HTX0_B, FN_SCIFB2_RTS_N_B, FN_SSI_WS9_B, + FN_DU1_DB0, FN_LCDOUT16, FN_VI1_CLK_B, FN_TX2_B, + FN_SCIFA2_TXD_B, FN_MSIOF2_TXD_B, + FN_DU1_DB1, FN_LCDOUT17, FN_VI1_HSYNC_N_B, FN_RX2_B, + FN_SCIFA2_RXD_B, FN_MSIOF2_RXD_B, + FN_DU1_DB2, FN_LCDOUT18, FN_VI1_VSYNC_N_B, FN_SCIF2_SCK_B, + FN_SCIFA2_SCK, FN_SSI_SDATA9_B, + FN_DU1_DB3, FN_LCDOUT19, FN_VI1_CLKENB_B, + FN_DU1_DB4, FN_LCDOUT20, FN_VI1_FIELD_B, FN_CAN1_RX, + FN_DU1_DB5, FN_LCDOUT21, FN_TX3, FN_SCIFA3_TXD, FN_CAN1_TX, + + /* IPSR9 */ + FN_DU1_DB6, FN_LCDOUT22, FN_SCL3_C, FN_RX3, FN_SCIFA3_RXD, + FN_DU1_DB7, FN_LCDOUT23, FN_SDA3_C, FN_SCIF3_SCK, FN_SCIFA3_SCK, + FN_DU1_DOTCLKIN, FN_QSTVA_QVS, + FN_DU1_DOTCLKOUT0, FN_QCLK, + FN_DU1_DOTCLKOUT1, FN_QSTVB_QVE, FN_CAN0_TX, + FN_TX3_B, FN_SCL2_B, FN_PWM4, + FN_DU1_EXHSYNC_DU1_HSYNC, FN_QSTH_QHS, + FN_DU1_EXVSYNC_DU1_VSYNC, FN_QSTB_QHE, + FN_DU1_EXODDF_DU1_ODDF_DISP_CDE, FN_QCPV_QDE, + FN_CAN0_RX, FN_RX3_B, FN_SDA2_B, + FN_DU1_DISP, FN_QPOLA, + FN_DU1_CDE, FN_QPOLB, FN_PWM4_B, + FN_VI0_CLKENB, FN_TX4, FN_SCIFA4_TXD, FN_TS_SDATA0_D, + FN_VI0_FIELD, FN_RX4, FN_SCIFA4_RXD, FN_TS_SCK0_D, + FN_VI0_HSYNC_N, FN_TX5, FN_SCIFA5_TXD, FN_TS_SDEN0_D, + FN_VI0_VSYNC_N, FN_RX5, FN_SCIFA5_RXD, FN_TS_SPSYNC0_D, + FN_VI0_DATA3_VI0_B3, FN_SCIF3_SCK_B, FN_SCIFA3_SCK_B, + FN_VI0_G0, FN_SCL8, FN_STP_IVCXO27_0_C, FN_SCL4, + FN_HCTS2_N, FN_SCIFB2_CTS_N, FN_ATAWR1_N, + + /* IPSR10 */ + FN_VI0_G1, FN_SDA8, FN_STP_ISCLK_0_C, FN_SDA4, + FN_HRTS2_N, FN_SCIFB2_RTS_N, FN_ATADIR1_N, + FN_VI0_G2, FN_VI2_HSYNC_N, FN_STP_ISD_0_C, FN_SCL3_B, + FN_HSCK2, FN_SCIFB2_SCK, FN_ATARD1_N, + FN_VI0_G3, FN_VI2_VSYNC_N, FN_STP_ISEN_0_C, FN_SDA3_B, + FN_HRX2, FN_SCIFB2_RXD, FN_ATACS01_N, + FN_VI0_G4, FN_VI2_CLKENB, FN_STP_ISSYNC_0_C, + FN_HTX2, FN_SCIFB2_TXD, FN_SCIFB0_SCK_D, + FN_VI0_G5, FN_VI2_FIELD, FN_STP_OPWM_0_C, FN_FMCLK_D, + FN_CAN0_TX_E, FN_HTX1_D, FN_SCIFB0_TXD_D, + FN_VI0_G6, FN_VI2_CLK, FN_BPFCLK_D, + FN_VI0_G7, FN_VI2_DATA0, FN_FMIN_D, + FN_VI0_R0, FN_VI2_DATA1, FN_GLO_I0_B, + FN_TS_SDATA0_C, FN_ATACS11_N, + FN_VI0_R1, FN_VI2_DATA2, FN_GLO_I1_B, + FN_TS_SCK0_C, FN_ATAG1_N, + FN_VI0_R2, FN_VI2_DATA3, FN_GLO_Q0_B, FN_TS_SDEN0_C, + FN_VI0_R3, FN_VI2_DATA4, FN_GLO_Q1_B, FN_TS_SPSYNC0_C, + FN_VI0_R4, FN_VI2_DATA5, FN_GLO_SCLK_B, FN_TX0_C, FN_SCL1_D, + + /* IPSR11 */ + FN_VI0_R5, FN_VI2_DATA6, FN_GLO_SDATA_B, FN_RX0_C, FN_SDA1_D, + FN_VI0_R6, FN_VI2_DATA7, FN_GLO_SS_B, FN_TX1_C, FN_SCL4_B, + FN_VI0_R7, FN_GLO_RFON_B, FN_RX1_C, FN_CAN0_RX_E, + FN_SDA4_B, FN_HRX1_D, FN_SCIFB0_RXD_D, + FN_VI1_HSYNC_N, FN_AVB_RXD0, FN_TS_SDATA0_B, FN_TX4_B, FN_SCIFA4_TXD_B, + FN_VI1_VSYNC_N, FN_AVB_RXD1, FN_TS_SCK0_B, FN_RX4_B, FN_SCIFA4_RXD_B, + FN_VI1_CLKENB, FN_AVB_RXD2, FN_TS_SDEN0_B, + FN_VI1_FIELD, FN_AVB_RXD3, FN_TS_SPSYNC0_B, + FN_VI1_CLK, FN_AVB_RXD4, FN_VI1_DATA0, FN_AVB_RXD5, + FN_VI1_DATA1, FN_AVB_RXD6, FN_VI1_DATA2, FN_AVB_RXD7, + FN_VI1_DATA3, FN_AVB_RX_ER, FN_VI1_DATA4, FN_AVB_MDIO, + FN_VI1_DATA5, FN_AVB_RX_DV, FN_VI1_DATA6, FN_AVB_MAGIC, + FN_VI1_DATA7, FN_AVB_MDC, + FN_ETH_MDIO, FN_AVB_RX_CLK, FN_SCL2_C, + FN_ETH_CRS_DV, FN_AVB_LINK, FN_SDA2_C, + + /* IPSR12 */ + FN_ETH_RX_ER, FN_AVB_CRS, FN_SCL3, FN_SCL7, + FN_ETH_RXD0, FN_AVB_PHY_INT, FN_SDA3, FN_SDA7, + FN_ETH_RXD1, FN_AVB_GTXREFCLK, FN_CAN0_TX_C, + FN_SCL2_D, FN_MSIOF1_RXD_E, + FN_ETH_LINK, FN_AVB_TXD0, FN_CAN0_RX_C, FN_SDA2_D, FN_MSIOF1_SCK_E, + FN_ETH_REFCLK, FN_AVB_TXD1, FN_SCIFA3_RXD_B, + FN_CAN1_RX_C, FN_MSIOF1_SYNC_E, + FN_ETH_TXD1, FN_AVB_TXD2, FN_SCIFA3_TXD_B, + FN_CAN1_TX_C, FN_MSIOF1_TXD_E, + FN_ETH_TX_EN, FN_AVB_TXD3, FN_TCLK1_B, FN_CAN_CLK_B, + FN_ETH_MAGIC, FN_AVB_TXD4, FN_IETX_C, + FN_ETH_TXD0, FN_AVB_TXD5, FN_IECLK_C, + FN_ETH_MDC, FN_AVB_TXD6, FN_IERX_C, + FN_STP_IVCXO27_0, FN_AVB_TXD7, FN_SCIFB2_TXD_D, + FN_ADIDATA_B, FN_MSIOF0_SYNC_C, + FN_STP_ISCLK_0, FN_AVB_TX_EN, FN_SCIFB2_RXD_D, + FN_ADICS_SAMP_B, FN_MSIOF0_SCK_C, + + /* IPSR13 */ + FN_STP_ISD_0, FN_AVB_TX_ER, FN_SCIFB2_SCK_C, + FN_ADICLK_B, FN_MSIOF0_SS1_C, + FN_STP_ISEN_0, FN_AVB_TX_CLK, FN_ADICHS0_B, FN_MSIOF0_SS2_C, + FN_STP_ISSYNC_0, FN_AVB_COL, FN_ADICHS1_B, FN_MSIOF0_RXD_C, + FN_STP_OPWM_0, FN_AVB_GTX_CLK, FN_PWM0_B, + FN_ADICHS2_B, FN_MSIOF0_TXD_C, + FN_SD0_CLK, FN_SPCLK_B, FN_SD0_CMD, FN_MOSI_IO0_B, + FN_SD0_DATA0, FN_MISO_IO1_B, FN_SD0_DATA1, FN_IO2_B, + FN_SD0_DATA2, FN_IO3_B, FN_SD0_DATA3, FN_SSL_B, + FN_SD0_CD, FN_MMC_D6_B, FN_SIM0_RST_B, FN_CAN0_RX_F, + FN_SCIFA5_TXD_B, FN_TX3_C, + FN_SD0_WP, FN_MMC_D7_B, FN_SIM0_D_B, FN_CAN0_TX_F, + FN_SCIFA5_RXD_B, FN_RX3_C, + FN_SD1_CMD, FN_REMOCON_B, FN_SD1_DATA0, FN_SPEEDIN_B, + FN_SD1_DATA1, FN_IETX_B, FN_SD1_DATA2, FN_IECLK_B, + FN_SD1_DATA3, FN_IERX_B, + FN_SD1_CD, FN_PWM0, FN_TPU_TO0, FN_SCL1_C, + + /* IPSR14 */ + FN_SD1_WP, FN_PWM1_B, FN_SDA1_C, + FN_SD2_CLK, FN_MMC_CLK, FN_SD2_CMD, FN_MMC_CMD, + FN_SD2_DATA0, FN_MMC_D0, FN_SD2_DATA1, FN_MMC_D1, + FN_SD2_DATA2, FN_MMC_D2, FN_SD2_DATA3, FN_MMC_D3, + FN_SD2_CD, FN_MMC_D4, FN_SCL8_C, FN_TX5_B, FN_SCIFA5_TXD_C, + FN_SD2_WP, FN_MMC_D5, FN_SDA8_C, FN_RX5_B, FN_SCIFA5_RXD_C, + FN_MSIOF0_SCK, FN_RX2_C, FN_ADIDATA, FN_VI1_CLK_C, FN_VI1_G0_B, + FN_MSIOF0_SYNC, FN_TX2_C, FN_ADICS_SAMP, FN_VI1_CLKENB_C, FN_VI1_G1_B, + FN_MSIOF0_TXD, FN_ADICLK, FN_VI1_FIELD_C, FN_VI1_G2_B, + FN_MSIOF0_RXD, FN_ADICHS0, FN_VI1_DATA0_C, FN_VI1_G3_B, + FN_MSIOF0_SS1, FN_MMC_D6, FN_ADICHS1, FN_TX0_E, + FN_VI1_HSYNC_N_C, FN_SCL7_C, FN_VI1_G4_B, + FN_MSIOF0_SS2, FN_MMC_D7, FN_ADICHS2, FN_RX0_E, + FN_VI1_VSYNC_N_C, FN_SDA7_C, FN_VI1_G5_B, + + /* IPSR15 */ + FN_SIM0_RST, FN_IETX, FN_CAN1_TX_D, + FN_SIM0_CLK, FN_IECLK, FN_CAN_CLK_C, + FN_SIM0_D, FN_IERX, FN_CAN1_RX_D, + FN_GPS_CLK, FN_DU1_DOTCLKIN_C, FN_AUDIO_CLKB_B, + FN_PWM5_B, FN_SCIFA3_TXD_C, + FN_GPS_SIGN, FN_TX4_C, FN_SCIFA4_TXD_C, FN_PWM5, + FN_VI1_G6_B, FN_SCIFA3_RXD_C, + FN_GPS_MAG, FN_RX4_C, FN_SCIFA4_RXD_C, FN_PWM6, + FN_VI1_G7_B, FN_SCIFA3_SCK_C, + FN_HCTS0_N, FN_SCIFB0_CTS_N, FN_GLO_I0_C, FN_TCLK1, FN_VI1_DATA1_C, + FN_HRTS0_N, FN_SCIFB0_RTS_N, FN_GLO_I1_C, FN_VI1_DATA2_C, + FN_HSCK0, FN_SCIFB0_SCK, FN_GLO_Q0_C, FN_CAN_CLK, + FN_TCLK2, FN_VI1_DATA3_C, + FN_HRX0, FN_SCIFB0_RXD, FN_GLO_Q1_C, FN_CAN0_RX_B, FN_VI1_DATA4_C, + FN_HTX0, FN_SCIFB0_TXD, FN_GLO_SCLK_C, FN_CAN0_TX_B, FN_VI1_DATA5_C, + + /* IPSR16 */ + FN_HRX1, FN_SCIFB1_RXD, FN_VI1_R0_B, FN_GLO_SDATA_C, FN_VI1_DATA6_C, + FN_HTX1, FN_SCIFB1_TXD, FN_VI1_R1_B, FN_GLO_SS_C, FN_VI1_DATA7_C, + FN_HSCK1, FN_SCIFB1_SCK, FN_MLB_CK, FN_GLO_RFON_C, + FN_HCTS1_N, FN_SCIFB1_CTS_N, FN_MLB_SIG, FN_CAN1_TX_B, + FN_HRTS1_N, FN_SCIFB1_RTS_N, FN_MLB_DAT, FN_CAN1_RX_B, + + /* MOD_SEL */ + FN_SEL_SCIF1_0, FN_SEL_SCIF1_1, FN_SEL_SCIF1_2, FN_SEL_SCIF1_3, + FN_SEL_SCIFB_0, FN_SEL_SCIFB_1, FN_SEL_SCIFB_2, FN_SEL_SCIFB_3, + FN_SEL_SCIFB2_0, FN_SEL_SCIFB2_1, FN_SEL_SCIFB2_2, FN_SEL_SCIFB2_3, + FN_SEL_SCIFB1_0, FN_SEL_SCIFB1_1, FN_SEL_SCIFB1_2, FN_SEL_SCIFB1_3, + FN_SEL_SCIFA1_0, FN_SEL_SCIFA1_1, FN_SEL_SCIFA1_2, + FN_SEL_SSI9_0, FN_SEL_SSI9_1, + FN_SEL_SCFA_0, FN_SEL_SCFA_1, + FN_SEL_QSP_0, FN_SEL_QSP_1, + FN_SEL_SSI7_0, FN_SEL_SSI7_1, + FN_SEL_HSCIF1_0, FN_SEL_HSCIF1_1, FN_SEL_HSCIF1_2, FN_SEL_HSCIF1_3, + FN_SEL_HSCIF1_4, + FN_SEL_VI1_0, FN_SEL_VI1_1, FN_SEL_VI1_2, + FN_SEL_TMU1_0, FN_SEL_TMU1_1, + FN_SEL_LBS_0, FN_SEL_LBS_1, FN_SEL_LBS_2, FN_SEL_LBS_3, + FN_SEL_TSIF0_0, FN_SEL_TSIF0_1, FN_SEL_TSIF0_2, FN_SEL_TSIF0_3, + FN_SEL_SOF0_0, FN_SEL_SOF0_1, FN_SEL_SOF0_2, + + /* MOD_SEL2 */ + FN_SEL_SCIF0_0, FN_SEL_SCIF0_1, FN_SEL_SCIF0_2, FN_SEL_SCIF0_3, + FN_SEL_SCIF0_4, + FN_SEL_SCIF_0, FN_SEL_SCIF_1, + FN_SEL_CAN0_0, FN_SEL_CAN0_1, FN_SEL_CAN0_2, FN_SEL_CAN0_3, + FN_SEL_CAN0_4, FN_SEL_CAN0_5, + FN_SEL_CAN1_0, FN_SEL_CAN1_1, FN_SEL_CAN1_2, FN_SEL_CAN1_3, + FN_SEL_SCIFA2_0, FN_SEL_SCIFA2_1, + FN_SEL_SCIF4_0, FN_SEL_SCIF4_1, FN_SEL_SCIF4_2, + FN_SEL_ADG_0, FN_SEL_ADG_1, + FN_SEL_FM_0, FN_SEL_FM_1, FN_SEL_FM_2, FN_SEL_FM_3, FN_SEL_FM_4, + FN_SEL_SCIFA5_0, FN_SEL_SCIFA5_1, FN_SEL_SCIFA5_2, + FN_SEL_GPS_0, FN_SEL_GPS_1, FN_SEL_GPS_2, FN_SEL_GPS_3, + FN_SEL_SCIFA4_0, FN_SEL_SCIFA4_1, FN_SEL_SCIFA4_2, + FN_SEL_SCIFA3_0, FN_SEL_SCIFA3_1, FN_SEL_SCIFA3_2, + FN_SEL_SIM_0, FN_SEL_SIM_1, + FN_SEL_SSI8_0, FN_SEL_SSI8_1, + + /* MOD_SEL3 */ + FN_SEL_HSCIF2_0, FN_SEL_HSCIF2_1, FN_SEL_HSCIF2_2, FN_SEL_HSCIF2_3, + FN_SEL_CANCLK_0, FN_SEL_CANCLK_1, FN_SEL_CANCLK_2, FN_SEL_CANCLK_3, + FN_SEL_IIC8_0, FN_SEL_IIC8_1, FN_SEL_IIC8_2, + FN_SEL_IIC7_0, FN_SEL_IIC7_1, FN_SEL_IIC7_2, + FN_SEL_IIC4_0, FN_SEL_IIC4_1, FN_SEL_IIC4_2, + FN_SEL_IIC3_0, FN_SEL_IIC3_1, FN_SEL_IIC3_2, FN_SEL_IIC3_3, + FN_SEL_SCIF3_0, FN_SEL_SCIF3_1, FN_SEL_SCIF3_2, FN_SEL_SCIF3_3, + FN_SEL_IEB_0, FN_SEL_IEB_1, FN_SEL_IEB_2, + FN_SEL_MMC_0, FN_SEL_MMC_1, + FN_SEL_SCIF5_0, FN_SEL_SCIF5_1, + FN_SEL_IIC2_0, FN_SEL_IIC2_1, FN_SEL_IIC2_2, FN_SEL_IIC2_3, + FN_SEL_IIC1_0, FN_SEL_IIC1_1, FN_SEL_IIC1_2, FN_SEL_IIC1_3, + FN_SEL_IIC1_4, + FN_SEL_IIC0_0, FN_SEL_IIC0_1, FN_SEL_IIC0_2, + + /* MOD_SEL4 */ + FN_SEL_SOF1_0, FN_SEL_SOF1_1, FN_SEL_SOF1_2, FN_SEL_SOF1_3, + FN_SEL_SOF1_4, + FN_SEL_HSCIF0_0, FN_SEL_HSCIF0_1, FN_SEL_HSCIF0_2, + FN_SEL_DIS_0, FN_SEL_DIS_1, FN_SEL_DIS_2, + FN_SEL_RAD_0, FN_SEL_RAD_1, + FN_SEL_RCN_0, FN_SEL_RCN_1, + FN_SEL_RSP_0, FN_SEL_RSP_1, + FN_SEL_SCIF2_0, FN_SEL_SCIF2_1, FN_SEL_SCIF2_2, FN_SEL_SCIF2_3, + FN_SEL_SCIF2_4, + FN_SEL_SOF2_0, FN_SEL_SOF2_1, FN_SEL_SOF2_2, FN_SEL_SOF2_3, + FN_SEL_SOF2_4, + FN_SEL_SSI1_0, FN_SEL_SSI1_1, + FN_SEL_SSI0_0, FN_SEL_SSI0_1, + FN_SEL_SSP_0, FN_SEL_SSP_1, FN_SEL_SSP_2, + PINMUX_FUNCTION_END, + + PINMUX_MARK_BEGIN, + + EX_CS0_N_MARK, RD_N_MARK, + + AUDIO_CLKA_MARK, + + VI0_CLK_MARK, VI0_DATA0_VI0_B0_MARK, VI0_DATA1_VI0_B1_MARK, + VI0_DATA2_VI0_B2_MARK, VI0_DATA4_VI0_B4_MARK, VI0_DATA5_VI0_B5_MARK, + VI0_DATA6_VI0_B6_MARK, VI0_DATA7_VI0_B7_MARK, + + SD1_CLK_MARK, + + USB0_PWEN_MARK, USB0_OVC_MARK, USB1_PWEN_MARK, USB1_OVC_MARK, + DU0_DOTCLKIN_MARK, + + /* IPSR0 */ + D0_MARK, D1_MARK, D2_MARK, D3_MARK, D4_MARK, D5_MARK, + D6_MARK, D7_MARK, D8_MARK, + D9_MARK, D10_MARK, D11_MARK, D12_MARK, D13_MARK, D14_MARK, D15_MARK, + A0_MARK, ATAWR0_N_C_MARK, MSIOF0_SCK_B_MARK, SCL0_C_MARK, PWM2_B_MARK, + A1_MARK, MSIOF0_SYNC_B_MARK, A2_MARK, MSIOF0_SS1_B_MARK, + A3_MARK, MSIOF0_SS2_B_MARK, A4_MARK, MSIOF0_TXD_B_MARK, + A5_MARK, MSIOF0_RXD_B_MARK, A6_MARK, MSIOF1_SCK_MARK, + + /* IPSR1 */ + A7_MARK, MSIOF1_SYNC_MARK, A8_MARK, MSIOF1_SS1_MARK, SCL0_MARK, + A9_MARK, MSIOF1_SS2_MARK, SDA0_MARK, + A10_MARK, MSIOF1_TXD_MARK, MSIOF1_TXD_D_MARK, + A11_MARK, MSIOF1_RXD_MARK, SCL3_D_MARK, MSIOF1_RXD_D_MARK, + A12_MARK, FMCLK_MARK, SDA3_D_MARK, MSIOF1_SCK_D_MARK, + A13_MARK, ATAG0_N_C_MARK, BPFCLK_MARK, MSIOF1_SS1_D_MARK, + A14_MARK, ATADIR0_N_C_MARK, FMIN_MARK, FMIN_C_MARK, MSIOF1_SYNC_D_MARK, + A15_MARK, BPFCLK_C_MARK, + A16_MARK, DREQ2_B_MARK, FMCLK_C_MARK, SCIFA1_SCK_B_MARK, + A17_MARK, DACK2_B_MARK, SDA0_C_MARK, + A18_MARK, DREQ1_MARK, SCIFA1_RXD_C_MARK, SCIFB1_RXD_C_MARK, + + /* IPSR2 */ + A19_MARK, DACK1_MARK, SCIFA1_TXD_C_MARK, + SCIFB1_TXD_C_MARK, SCIFB1_SCK_B_MARK, + A20_MARK, SPCLK_MARK, + A21_MARK, ATAWR0_N_B_MARK, MOSI_IO0_MARK, + A22_MARK, MISO_IO1_MARK, FMCLK_B_MARK, TX0_MARK, SCIFA0_TXD_MARK, + A23_MARK, IO2_MARK, BPFCLK_B_MARK, RX0_MARK, SCIFA0_RXD_MARK, + A24_MARK, DREQ2_MARK, IO3_MARK, TX1_MARK, SCIFA1_TXD_MARK, + A25_MARK, DACK2_MARK, SSL_MARK, DREQ1_C_MARK, + RX1_MARK, SCIFA1_RXD_MARK, + CS0_N_MARK, ATAG0_N_B_MARK, SCL1_MARK, + CS1_N_A26_MARK, ATADIR0_N_B_MARK, SDA1_MARK, + EX_CS1_N_MARK, MSIOF2_SCK_MARK, + EX_CS2_N_MARK, ATAWR0_N_MARK, MSIOF2_SYNC_MARK, + EX_CS3_N_MARK, ATADIR0_N_MARK, MSIOF2_TXD_MARK, + ATAG0_N_MARK, EX_WAIT1_MARK, + + /* IPSR3 */ + EX_CS4_N_MARK, ATARD0_N_MARK, MSIOF2_RXD_MARK, EX_WAIT2_MARK, + EX_CS5_N_MARK, ATACS00_N_MARK, MSIOF2_SS1_MARK, HRX1_B_MARK, + SCIFB1_RXD_B_MARK, PWM1_MARK, TPU_TO1_MARK, + BS_N_MARK, ATACS10_N_MARK, MSIOF2_SS2_MARK, HTX1_B_MARK, + SCIFB1_TXD_B_MARK, PWM2_MARK, TPU_TO2_MARK, + RD_WR_N_MARK, HRX2_B_MARK, FMIN_B_MARK, + SCIFB0_RXD_B_MARK, DREQ1_D_MARK, + WE0_N_MARK, HCTS2_N_B_MARK, SCIFB0_TXD_B_MARK, + WE1_N_MARK, ATARD0_N_B_MARK, HTX2_B_MARK, SCIFB0_RTS_N_B_MARK, + EX_WAIT0_MARK, HRTS2_N_B_MARK, SCIFB0_CTS_N_B_MARK, + DREQ0_MARK, PWM3_MARK, TPU_TO3_MARK, + DACK0_MARK, DRACK0_MARK, REMOCON_MARK, + SPEEDIN_MARK, HSCK0_C_MARK, HSCK2_C_MARK, SCIFB0_SCK_B_MARK, + SCIFB2_SCK_B_MARK, DREQ2_C_MARK, HTX2_D_MARK, + SSI_SCK0129_MARK, HRX0_C_MARK, HRX2_C_MARK, + SCIFB0_RXD_C_MARK, SCIFB2_RXD_C_MARK, + SSI_WS0129_MARK, HTX0_C_MARK, HTX2_C_MARK, + SCIFB0_TXD_C_MARK, SCIFB2_TXD_C_MARK, + + /* IPSR4 */ + SSI_SDATA0_MARK, SCL0_B_MARK, SCL7_B_MARK, MSIOF2_SCK_C_MARK, + SSI_SCK1_MARK, SDA0_B_MARK, SDA7_B_MARK, + MSIOF2_SYNC_C_MARK, GLO_I0_D_MARK, + SSI_WS1_MARK, SCL1_B_MARK, SCL8_B_MARK, + MSIOF2_TXD_C_MARK, GLO_I1_D_MARK, + SSI_SDATA1_MARK, SDA1_B_MARK, SDA8_B_MARK, MSIOF2_RXD_C_MARK, + SSI_SCK2_MARK, SCL2_MARK, GPS_CLK_B_MARK, GLO_Q0_D_MARK, HSCK1_E_MARK, + SSI_WS2_MARK, SDA2_MARK, GPS_SIGN_B_MARK, RX2_E_MARK, + GLO_Q1_D_MARK, HCTS1_N_E_MARK, + SSI_SDATA2_MARK, GPS_MAG_B_MARK, TX2_E_MARK, HRTS1_N_E_MARK, + SSI_SCK34_MARK, SSI_WS34_MARK, SSI_SDATA3_MARK, + SSI_SCK4_MARK, GLO_SS_D_MARK, + SSI_WS4_MARK, GLO_RFON_D_MARK, + SSI_SDATA4_MARK, MSIOF2_SCK_D_MARK, + SSI_SCK5_MARK, MSIOF1_SCK_C_MARK, TS_SDATA0_MARK, GLO_I0_MARK, + MSIOF2_SYNC_D_MARK, VI1_R2_B_MARK, + + /* IPSR5 */ + SSI_WS5_MARK, MSIOF1_SYNC_C_MARK, TS_SCK0_MARK, GLO_I1_MARK, + MSIOF2_TXD_D_MARK, VI1_R3_B_MARK, + SSI_SDATA5_MARK, MSIOF1_TXD_C_MARK, TS_SDEN0_MARK, GLO_Q0_MARK, + MSIOF2_SS1_D_MARK, VI1_R4_B_MARK, + SSI_SCK6_MARK, MSIOF1_RXD_C_MARK, TS_SPSYNC0_MARK, GLO_Q1_MARK, + MSIOF2_RXD_D_MARK, VI1_R5_B_MARK, + SSI_WS6_MARK, GLO_SCLK_MARK, MSIOF2_SS2_D_MARK, VI1_R6_B_MARK, + SSI_SDATA6_MARK, STP_IVCXO27_0_B_MARK, GLO_SDATA_MARK, VI1_R7_B_MARK, + SSI_SCK78_MARK, STP_ISCLK_0_B_MARK, GLO_SS_MARK, + SSI_WS78_MARK, TX0_D_MARK, STP_ISD_0_B_MARK, GLO_RFON_MARK, + SSI_SDATA7_MARK, RX0_D_MARK, STP_ISEN_0_B_MARK, + SSI_SDATA8_MARK, TX1_D_MARK, STP_ISSYNC_0_B_MARK, + SSI_SCK9_MARK, RX1_D_MARK, GLO_SCLK_D_MARK, + SSI_WS9_MARK, TX3_D_MARK, CAN0_TX_D_MARK, GLO_SDATA_D_MARK, + SSI_SDATA9_MARK, RX3_D_MARK, CAN0_RX_D_MARK, + + /* IPSR6 */ + AUDIO_CLKB_MARK, STP_OPWM_0_B_MARK, MSIOF1_SCK_B_MARK, + SCIF_CLK_MARK, BPFCLK_E_MARK, + AUDIO_CLKC_MARK, SCIFB0_SCK_C_MARK, MSIOF1_SYNC_B_MARK, RX2_MARK, + SCIFA2_RXD_MARK, FMIN_E_MARK, + AUDIO_CLKOUT_MARK, MSIOF1_SS1_B_MARK, TX2_MARK, SCIFA2_TXD_MARK, + IRQ0_MARK, SCIFB1_RXD_D_MARK, INTC_IRQ0_N_MARK, + IRQ1_MARK, SCIFB1_SCK_C_MARK, INTC_IRQ1_N_MARK, + IRQ2_MARK, SCIFB1_TXD_D_MARK, INTC_IRQ2_N_MARK, + IRQ3_MARK, SCL4_C_MARK, MSIOF2_TXD_E_MARK, INTC_IRQ3_N_MARK, + IRQ4_MARK, HRX1_C_MARK, SDA4_C_MARK, + MSIOF2_RXD_E_MARK, INTC_IRQ4_N_MARK, + IRQ5_MARK, HTX1_C_MARK, SCL1_E_MARK, MSIOF2_SCK_E_MARK, + IRQ6_MARK, HSCK1_C_MARK, MSIOF1_SS2_B_MARK, + SDA1_E_MARK, MSIOF2_SYNC_E_MARK, + IRQ7_MARK, HCTS1_N_C_MARK, MSIOF1_TXD_B_MARK, + GPS_CLK_C_MARK, GPS_CLK_D_MARK, + IRQ8_MARK, HRTS1_N_C_MARK, MSIOF1_RXD_B_MARK, + GPS_SIGN_C_MARK, GPS_SIGN_D_MARK, + + /* IPSR7 */ + IRQ9_MARK, DU1_DOTCLKIN_B_MARK, CAN_CLK_D_MARK, GPS_MAG_C_MARK, + SCIF_CLK_B_MARK, GPS_MAG_D_MARK, + DU1_DR0_MARK, LCDOUT0_MARK, VI1_DATA0_B_MARK, TX0_B_MARK, + SCIFA0_TXD_B_MARK, MSIOF2_SCK_B_MARK, + DU1_DR1_MARK, LCDOUT1_MARK, VI1_DATA1_B_MARK, RX0_B_MARK, + SCIFA0_RXD_B_MARK, MSIOF2_SYNC_B_MARK, + DU1_DR2_MARK, LCDOUT2_MARK, SSI_SCK0129_B_MARK, + DU1_DR3_MARK, LCDOUT3_MARK, SSI_WS0129_B_MARK, + DU1_DR4_MARK, LCDOUT4_MARK, SSI_SDATA0_B_MARK, + DU1_DR5_MARK, LCDOUT5_MARK, SSI_SCK1_B_MARK, + DU1_DR6_MARK, LCDOUT6_MARK, SSI_WS1_B_MARK, + DU1_DR7_MARK, LCDOUT7_MARK, SSI_SDATA1_B_MARK, + DU1_DG0_MARK, LCDOUT8_MARK, VI1_DATA2_B_MARK, TX1_B_MARK, + SCIFA1_TXD_B_MARK, MSIOF2_SS1_B_MARK, + DU1_DG1_MARK, LCDOUT9_MARK, VI1_DATA3_B_MARK, RX1_B_MARK, + SCIFA1_RXD_B_MARK, MSIOF2_SS2_B_MARK, + DU1_DG2_MARK, LCDOUT10_MARK, VI1_DATA4_B_MARK, SCIF1_SCK_B_MARK, + SCIFA1_SCK_MARK, SSI_SCK78_B_MARK, + + /* IPSR8 */ + DU1_DG3_MARK, LCDOUT11_MARK, VI1_DATA5_B_MARK, SSI_WS78_B_MARK, + DU1_DG4_MARK, LCDOUT12_MARK, VI1_DATA6_B_MARK, HRX0_B_MARK, + SCIFB2_RXD_B_MARK, SSI_SDATA7_B_MARK, + DU1_DG5_MARK, LCDOUT13_MARK, VI1_DATA7_B_MARK, HCTS0_N_B_MARK, + SCIFB2_TXD_B_MARK, SSI_SDATA8_B_MARK, + DU1_DG6_MARK, LCDOUT14_MARK, HRTS0_N_B_MARK, + SCIFB2_CTS_N_B_MARK, SSI_SCK9_B_MARK, + DU1_DG7_MARK, LCDOUT15_MARK, HTX0_B_MARK, + SCIFB2_RTS_N_B_MARK, SSI_WS9_B_MARK, + DU1_DB0_MARK, LCDOUT16_MARK, VI1_CLK_B_MARK, TX2_B_MARK, + SCIFA2_TXD_B_MARK, MSIOF2_TXD_B_MARK, + DU1_DB1_MARK, LCDOUT17_MARK, VI1_HSYNC_N_B_MARK, RX2_B_MARK, + SCIFA2_RXD_B_MARK, MSIOF2_RXD_B_MARK, + DU1_DB2_MARK, LCDOUT18_MARK, VI1_VSYNC_N_B_MARK, SCIF2_SCK_B_MARK, + SCIFA2_SCK_MARK, SSI_SDATA9_B_MARK, + DU1_DB3_MARK, LCDOUT19_MARK, VI1_CLKENB_B_MARK, + DU1_DB4_MARK, LCDOUT20_MARK, VI1_FIELD_B_MARK, CAN1_RX_MARK, + DU1_DB5_MARK, LCDOUT21_MARK, TX3_MARK, SCIFA3_TXD_MARK, CAN1_TX_MARK, + + /* IPSR9 */ + DU1_DB6_MARK, LCDOUT22_MARK, SCL3_C_MARK, RX3_MARK, SCIFA3_RXD_MARK, + DU1_DB7_MARK, LCDOUT23_MARK, SDA3_C_MARK, + SCIF3_SCK_MARK, SCIFA3_SCK_MARK, + DU1_DOTCLKIN_MARK, QSTVA_QVS_MARK, + DU1_DOTCLKOUT0_MARK, QCLK_MARK, + DU1_DOTCLKOUT1_MARK, QSTVB_QVE_MARK, CAN0_TX_MARK, + TX3_B_MARK, SCL2_B_MARK, PWM4_MARK, + DU1_EXHSYNC_DU1_HSYNC_MARK, QSTH_QHS_MARK, + DU1_EXVSYNC_DU1_VSYNC_MARK, QSTB_QHE_MARK, + DU1_EXODDF_DU1_ODDF_DISP_CDE_MARK, QCPV_QDE_MARK, + CAN0_RX_MARK, RX3_B_MARK, SDA2_B_MARK, + DU1_DISP_MARK, QPOLA_MARK, + DU1_CDE_MARK, QPOLB_MARK, PWM4_B_MARK, + VI0_CLKENB_MARK, TX4_MARK, SCIFA4_TXD_MARK, TS_SDATA0_D_MARK, + VI0_FIELD_MARK, RX4_MARK, SCIFA4_RXD_MARK, TS_SCK0_D_MARK, + VI0_HSYNC_N_MARK, TX5_MARK, SCIFA5_TXD_MARK, TS_SDEN0_D_MARK, + VI0_VSYNC_N_MARK, RX5_MARK, SCIFA5_RXD_MARK, TS_SPSYNC0_D_MARK, + VI0_DATA3_VI0_B3_MARK, SCIF3_SCK_B_MARK, SCIFA3_SCK_B_MARK, + VI0_G0_MARK, SCL8_MARK, STP_IVCXO27_0_C_MARK, SCL4_MARK, + HCTS2_N_MARK, SCIFB2_CTS_N_MARK, ATAWR1_N_MARK, + + /* IPSR10 */ + VI0_G1_MARK, SDA8_MARK, STP_ISCLK_0_C_MARK, SDA4_MARK, + HRTS2_N_MARK, SCIFB2_RTS_N_MARK, ATADIR1_N_MARK, + VI0_G2_MARK, VI2_HSYNC_N_MARK, STP_ISD_0_C_MARK, SCL3_B_MARK, + HSCK2_MARK, SCIFB2_SCK_MARK, ATARD1_N_MARK, + VI0_G3_MARK, VI2_VSYNC_N_MARK, STP_ISEN_0_C_MARK, SDA3_B_MARK, + HRX2_MARK, SCIFB2_RXD_MARK, ATACS01_N_MARK, + VI0_G4_MARK, VI2_CLKENB_MARK, STP_ISSYNC_0_C_MARK, + HTX2_MARK, SCIFB2_TXD_MARK, SCIFB0_SCK_D_MARK, + VI0_G5_MARK, VI2_FIELD_MARK, STP_OPWM_0_C_MARK, FMCLK_D_MARK, + CAN0_TX_E_MARK, HTX1_D_MARK, SCIFB0_TXD_D_MARK, + VI0_G6_MARK, VI2_CLK_MARK, BPFCLK_D_MARK, + VI0_G7_MARK, VI2_DATA0_MARK, FMIN_D_MARK, + VI0_R0_MARK, VI2_DATA1_MARK, GLO_I0_B_MARK, + TS_SDATA0_C_MARK, ATACS11_N_MARK, + VI0_R1_MARK, VI2_DATA2_MARK, GLO_I1_B_MARK, + TS_SCK0_C_MARK, ATAG1_N_MARK, + VI0_R2_MARK, VI2_DATA3_MARK, GLO_Q0_B_MARK, TS_SDEN0_C_MARK, + VI0_R3_MARK, VI2_DATA4_MARK, GLO_Q1_B_MARK, TS_SPSYNC0_C_MARK, + VI0_R4_MARK, VI2_DATA5_MARK, GLO_SCLK_B_MARK, TX0_C_MARK, SCL1_D_MARK, + + /* IPSR11 */ + VI0_R5_MARK, VI2_DATA6_MARK, GLO_SDATA_B_MARK, RX0_C_MARK, SDA1_D_MARK, + VI0_R6_MARK, VI2_DATA7_MARK, GLO_SS_B_MARK, TX1_C_MARK, SCL4_B_MARK, + VI0_R7_MARK, GLO_RFON_B_MARK, RX1_C_MARK, CAN0_RX_E_MARK, + SDA4_B_MARK, HRX1_D_MARK, SCIFB0_RXD_D_MARK, + VI1_HSYNC_N_MARK, AVB_RXD0_MARK, TS_SDATA0_B_MARK, + TX4_B_MARK, SCIFA4_TXD_B_MARK, + VI1_VSYNC_N_MARK, AVB_RXD1_MARK, TS_SCK0_B_MARK, + RX4_B_MARK, SCIFA4_RXD_B_MARK, + VI1_CLKENB_MARK, AVB_RXD2_MARK, TS_SDEN0_B_MARK, + VI1_FIELD_MARK, AVB_RXD3_MARK, TS_SPSYNC0_B_MARK, + VI1_CLK_MARK, AVB_RXD4_MARK, VI1_DATA0_MARK, AVB_RXD5_MARK, + VI1_DATA1_MARK, AVB_RXD6_MARK, VI1_DATA2_MARK, AVB_RXD7_MARK, + VI1_DATA3_MARK, AVB_RX_ER_MARK, VI1_DATA4_MARK, AVB_MDIO_MARK, + VI1_DATA5_MARK, AVB_RX_DV_MARK, VI1_DATA6_MARK, AVB_MAGIC_MARK, + VI1_DATA7_MARK, AVB_MDC_MARK, + ETH_MDIO_MARK, AVB_RX_CLK_MARK, SCL2_C_MARK, + ETH_CRS_DV_MARK, AVB_LINK_MARK, SDA2_C_MARK, + + /* IPSR12 */ + ETH_RX_ER_MARK, AVB_CRS_MARK, SCL3_MARK, SCL7_MARK, + ETH_RXD0_MARK, AVB_PHY_INT_MARK, SDA3_MARK, SDA7_MARK, + ETH_RXD1_MARK, AVB_GTXREFCLK_MARK, CAN0_TX_C_MARK, + SCL2_D_MARK, MSIOF1_RXD_E_MARK, + ETH_LINK_MARK, AVB_TXD0_MARK, CAN0_RX_C_MARK, + SDA2_D_MARK, MSIOF1_SCK_E_MARK, + ETH_REFCLK_MARK, AVB_TXD1_MARK, SCIFA3_RXD_B_MARK, + CAN1_RX_C_MARK, MSIOF1_SYNC_E_MARK, + ETH_TXD1_MARK, AVB_TXD2_MARK, SCIFA3_TXD_B_MARK, + CAN1_TX_C_MARK, MSIOF1_TXD_E_MARK, + ETH_TX_EN_MARK, AVB_TXD3_MARK, TCLK1_B_MARK, CAN_CLK_B_MARK, + ETH_MAGIC_MARK, AVB_TXD4_MARK, IETX_C_MARK, + ETH_TXD0_MARK, AVB_TXD5_MARK, IECLK_C_MARK, + ETH_MDC_MARK, AVB_TXD6_MARK, IERX_C_MARK, + STP_IVCXO27_0_MARK, AVB_TXD7_MARK, SCIFB2_TXD_D_MARK, + ADIDATA_B_MARK, MSIOF0_SYNC_C_MARK, + STP_ISCLK_0_MARK, AVB_TX_EN_MARK, SCIFB2_RXD_D_MARK, + ADICS_SAMP_B_MARK, MSIOF0_SCK_C_MARK, + + /* IPSR13 */ + STP_ISD_0_MARK, AVB_TX_ER_MARK, SCIFB2_SCK_C_MARK, + ADICLK_B_MARK, MSIOF0_SS1_C_MARK, + STP_ISEN_0_MARK, AVB_TX_CLK_MARK, ADICHS0_B_MARK, MSIOF0_SS2_C_MARK, + STP_ISSYNC_0_MARK, AVB_COL_MARK, ADICHS1_B_MARK, MSIOF0_RXD_C_MARK, + STP_OPWM_0_MARK, AVB_GTX_CLK_MARK, PWM0_B_MARK, + ADICHS2_B_MARK, MSIOF0_TXD_C_MARK, + SD0_CLK_MARK, SPCLK_B_MARK, SD0_CMD_MARK, MOSI_IO0_B_MARK, + SD0_DATA0_MARK, MISO_IO1_B_MARK, SD0_DATA1_MARK, IO2_B_MARK, + SD0_DATA2_MARK, IO3_B_MARK, SD0_DATA3_MARK, SSL_B_MARK, + SD0_CD_MARK, MMC_D6_B_MARK, SIM0_RST_B_MARK, CAN0_RX_F_MARK, + SCIFA5_TXD_B_MARK, TX3_C_MARK, + SD0_WP_MARK, MMC_D7_B_MARK, SIM0_D_B_MARK, CAN0_TX_F_MARK, + SCIFA5_RXD_B_MARK, RX3_C_MARK, + SD1_CMD_MARK, REMOCON_B_MARK, SD1_DATA0_MARK, SPEEDIN_B_MARK, + SD1_DATA1_MARK, IETX_B_MARK, SD1_DATA2_MARK, IECLK_B_MARK, + SD1_DATA3_MARK, IERX_B_MARK, + SD1_CD_MARK, PWM0_MARK, TPU_TO0_MARK, SCL1_C_MARK, + + /* IPSR14 */ + SD1_WP_MARK, PWM1_B_MARK, SDA1_C_MARK, + SD2_CLK_MARK, MMC_CLK_MARK, SD2_CMD_MARK, MMC_CMD_MARK, + SD2_DATA0_MARK, MMC_D0_MARK, SD2_DATA1_MARK, MMC_D1_MARK, + SD2_DATA2_MARK, MMC_D2_MARK, SD2_DATA3_MARK, MMC_D3_MARK, + SD2_CD_MARK, MMC_D4_MARK, SCL8_C_MARK, TX5_B_MARK, SCIFA5_TXD_C_MARK, + SD2_WP_MARK, MMC_D5_MARK, SDA8_C_MARK, RX5_B_MARK, SCIFA5_RXD_C_MARK, + MSIOF0_SCK_MARK, RX2_C_MARK, ADIDATA_MARK, + VI1_CLK_C_MARK, VI1_G0_B_MARK, + MSIOF0_SYNC_MARK, TX2_C_MARK, ADICS_SAMP_MARK, + VI1_CLKENB_C_MARK, VI1_G1_B_MARK, + MSIOF0_TXD_MARK, ADICLK_MARK, VI1_FIELD_C_MARK, VI1_G2_B_MARK, + MSIOF0_RXD_MARK, ADICHS0_MARK, VI1_DATA0_C_MARK, VI1_G3_B_MARK, + MSIOF0_SS1_MARK, MMC_D6_MARK, ADICHS1_MARK, TX0_E_MARK, + VI1_HSYNC_N_C_MARK, SCL7_C_MARK, VI1_G4_B_MARK, + MSIOF0_SS2_MARK, MMC_D7_MARK, ADICHS2_MARK, RX0_E_MARK, + VI1_VSYNC_N_C_MARK, SDA7_C_MARK, VI1_G5_B_MARK, + + /* IPSR15 */ + SIM0_RST_MARK, IETX_MARK, CAN1_TX_D_MARK, + SIM0_CLK_MARK, IECLK_MARK, CAN_CLK_C_MARK, + SIM0_D_MARK, IERX_MARK, CAN1_RX_D_MARK, + GPS_CLK_MARK, DU1_DOTCLKIN_C_MARK, AUDIO_CLKB_B_MARK, + PWM5_B_MARK, SCIFA3_TXD_C_MARK, + GPS_SIGN_MARK, TX4_C_MARK, SCIFA4_TXD_C_MARK, PWM5_MARK, + VI1_G6_B_MARK, SCIFA3_RXD_C_MARK, + GPS_MAG_MARK, RX4_C_MARK, SCIFA4_RXD_C_MARK, PWM6_MARK, + VI1_G7_B_MARK, SCIFA3_SCK_C_MARK, + HCTS0_N_MARK, SCIFB0_CTS_N_MARK, GLO_I0_C_MARK, + TCLK1_MARK, VI1_DATA1_C_MARK, + HRTS0_N_MARK, SCIFB0_RTS_N_MARK, GLO_I1_C_MARK, VI1_DATA2_C_MARK, + HSCK0_MARK, SCIFB0_SCK_MARK, GLO_Q0_C_MARK, CAN_CLK_MARK, + TCLK2_MARK, VI1_DATA3_C_MARK, + HRX0_MARK, SCIFB0_RXD_MARK, GLO_Q1_C_MARK, + CAN0_RX_B_MARK, VI1_DATA4_C_MARK, + HTX0_MARK, SCIFB0_TXD_MARK, GLO_SCLK_C_MARK, + CAN0_TX_B_MARK, VI1_DATA5_C_MARK, + + /* IPSR16 */ + HRX1_MARK, SCIFB1_RXD_MARK, VI1_R0_B_MARK, + GLO_SDATA_C_MARK, VI1_DATA6_C_MARK, + HTX1_MARK, SCIFB1_TXD_MARK, VI1_R1_B_MARK, + GLO_SS_C_MARK, VI1_DATA7_C_MARK, + HSCK1_MARK, SCIFB1_SCK_MARK, MLB_CK_MARK, GLO_RFON_C_MARK, + HCTS1_N_MARK, SCIFB1_CTS_N_MARK, MLB_SIG_MARK, CAN1_TX_B_MARK, + HRTS1_N_MARK, SCIFB1_RTS_N_MARK, MLB_DAT_MARK, CAN1_RX_B_MARK, + PINMUX_MARK_END, +}; + +static const u16 pinmux_data[] = { + PINMUX_DATA_GP_ALL(), /* PINMUX_DATA(GP_M_N_DATA, GP_M_N_FN...), */ + + PINMUX_DATA(EX_CS0_N_MARK, FN_EX_CS0_N), + PINMUX_DATA(RD_N_MARK, FN_RD_N), + PINMUX_DATA(AUDIO_CLKA_MARK, FN_AUDIO_CLKA), + PINMUX_DATA(VI0_CLK_MARK, FN_VI0_CLK), + PINMUX_DATA(VI0_DATA0_VI0_B0_MARK, FN_VI0_DATA0_VI0_B0), + PINMUX_DATA(VI0_DATA1_VI0_B1_MARK, FN_VI0_DATA1_VI0_B1), + PINMUX_DATA(VI0_DATA2_VI0_B2_MARK, FN_VI0_DATA2_VI0_B2), + PINMUX_DATA(VI0_DATA4_VI0_B4_MARK, FN_VI0_DATA4_VI0_B4), + PINMUX_DATA(VI0_DATA5_VI0_B5_MARK, FN_VI0_DATA5_VI0_B5), + PINMUX_DATA(VI0_DATA6_VI0_B6_MARK, FN_VI0_DATA6_VI0_B6), + PINMUX_DATA(VI0_DATA7_VI0_B7_MARK, FN_VI0_DATA7_VI0_B7), + PINMUX_DATA(USB0_PWEN_MARK, FN_USB0_PWEN), + PINMUX_DATA(USB0_OVC_MARK, FN_USB0_OVC), + PINMUX_DATA(USB1_PWEN_MARK, FN_USB1_PWEN), + PINMUX_DATA(USB1_OVC_MARK, FN_USB1_OVC), + PINMUX_DATA(DU0_DOTCLKIN_MARK, FN_DU0_DOTCLKIN), + + /* IPSR0 */ + PINMUX_IPSR_DATA(IP0_0, D0), + PINMUX_IPSR_DATA(IP0_1, D1), + PINMUX_IPSR_DATA(IP0_2, D2), + PINMUX_IPSR_DATA(IP0_3, D3), + PINMUX_IPSR_DATA(IP0_4, D4), + PINMUX_IPSR_DATA(IP0_5, D5), + PINMUX_IPSR_DATA(IP0_6, D6), + PINMUX_IPSR_DATA(IP0_7, D7), + PINMUX_IPSR_DATA(IP0_8, D8), + PINMUX_IPSR_DATA(IP0_9, D9), + PINMUX_IPSR_DATA(IP0_10, D10), + PINMUX_IPSR_DATA(IP0_11, D11), + PINMUX_IPSR_DATA(IP0_12, D12), + PINMUX_IPSR_DATA(IP0_13, D13), + PINMUX_IPSR_DATA(IP0_14, D14), + PINMUX_IPSR_DATA(IP0_15, D15), + PINMUX_IPSR_DATA(IP0_18_16, A0), + PINMUX_IPSR_MODSEL_DATA(IP0_18_16, ATAWR0_N_C, SEL_LBS_2), + PINMUX_IPSR_MODSEL_DATA(IP0_18_16, MSIOF0_SCK_B, SEL_SOF0_1), + PINMUX_IPSR_MODSEL_DATA(IP0_18_16, SCL0_C, SEL_IIC0_2), + PINMUX_IPSR_DATA(IP0_18_16, PWM2_B), + PINMUX_IPSR_DATA(IP0_20_19, A1), + PINMUX_IPSR_MODSEL_DATA(IP0_20_19, MSIOF0_SYNC_B, SEL_SOF0_1), + PINMUX_IPSR_DATA(IP0_22_21, A2), + PINMUX_IPSR_MODSEL_DATA(IP0_22_21, MSIOF0_SS1_B, SEL_SOF0_1), + PINMUX_IPSR_DATA(IP0_24_23, A3), + PINMUX_IPSR_MODSEL_DATA(IP0_24_23, MSIOF0_SS2_B, SEL_SOF0_1), + PINMUX_IPSR_DATA(IP0_26_25, A4), + PINMUX_IPSR_MODSEL_DATA(IP0_26_25, MSIOF0_TXD_B, SEL_SOF0_1), + PINMUX_IPSR_DATA(IP0_28_27, A5), + PINMUX_IPSR_MODSEL_DATA(IP0_28_27, MSIOF0_RXD_B, SEL_SOF0_1), + PINMUX_IPSR_DATA(IP0_30_29, A6), + PINMUX_IPSR_MODSEL_DATA(IP0_30_29, MSIOF1_SCK, SEL_SOF1_0), + + /* IPSR1 */ + PINMUX_IPSR_DATA(IP1_1_0, A7), + PINMUX_IPSR_MODSEL_DATA(IP1_1_0, MSIOF1_SYNC, SEL_SOF1_0), + PINMUX_IPSR_DATA(IP1_3_2, A8), + PINMUX_IPSR_MODSEL_DATA(IP1_3_2, MSIOF1_SS1, SEL_SOF1_0), + PINMUX_IPSR_MODSEL_DATA(IP1_3_2, SCL0, SEL_IIC0_0), + PINMUX_IPSR_DATA(IP1_5_4, A9), + PINMUX_IPSR_MODSEL_DATA(IP1_5_4, MSIOF1_SS2, SEL_SOF1_0), + PINMUX_IPSR_MODSEL_DATA(IP1_5_4, SDA0, SEL_IIC0_0), + PINMUX_IPSR_DATA(IP1_7_6, A10), + PINMUX_IPSR_MODSEL_DATA(IP1_7_6, MSIOF1_TXD, SEL_SOF1_0), + PINMUX_IPSR_MODSEL_DATA(IP1_7_6, MSIOF1_TXD_D, SEL_SOF1_3), + PINMUX_IPSR_DATA(IP1_10_8, A11), + PINMUX_IPSR_MODSEL_DATA(IP1_10_8, MSIOF1_RXD, SEL_SOF1_0), + PINMUX_IPSR_MODSEL_DATA(IP1_10_8, SCL3_D, SEL_IIC3_3), + PINMUX_IPSR_MODSEL_DATA(IP1_10_8, MSIOF1_RXD_D, SEL_SOF1_3), + PINMUX_IPSR_DATA(IP1_13_11, A12), + PINMUX_IPSR_MODSEL_DATA(IP1_13_11, FMCLK, SEL_FM_0), + PINMUX_IPSR_MODSEL_DATA(IP1_13_11, SDA3_D, SEL_IIC3_3), + PINMUX_IPSR_MODSEL_DATA(IP1_13_11, MSIOF1_SCK_D, SEL_SOF1_3), + PINMUX_IPSR_DATA(IP1_16_14, A13), + PINMUX_IPSR_MODSEL_DATA(IP1_16_14, ATAG0_N_C, SEL_LBS_2), + PINMUX_IPSR_MODSEL_DATA(IP1_16_14, BPFCLK, SEL_FM_0), + PINMUX_IPSR_MODSEL_DATA(IP1_16_14, MSIOF1_SS1_D, SEL_SOF1_3), + PINMUX_IPSR_DATA(IP1_19_17, A14), + PINMUX_IPSR_MODSEL_DATA(IP1_19_17, ATADIR0_N_C, SEL_LBS_2), + PINMUX_IPSR_MODSEL_DATA(IP1_19_17, FMIN, SEL_FM_0), + PINMUX_IPSR_MODSEL_DATA(IP1_19_17, FMIN_C, SEL_FM_2), + PINMUX_IPSR_MODSEL_DATA(IP1_19_17, MSIOF1_SYNC_D, SEL_SOF1_3), + PINMUX_IPSR_DATA(IP1_22_20, A15), + PINMUX_IPSR_MODSEL_DATA(IP1_22_20, BPFCLK_C, SEL_FM_2), + PINMUX_IPSR_DATA(IP1_25_23, A16), + PINMUX_IPSR_MODSEL_DATA(IP1_25_23, DREQ2_B, SEL_LBS_1), + PINMUX_IPSR_MODSEL_DATA(IP1_25_23, FMCLK_C, SEL_FM_2), + PINMUX_IPSR_MODSEL_DATA(IP1_25_23, SCIFA1_SCK_B, SEL_SCIFA1_1), + PINMUX_IPSR_DATA(IP1_28_26, A17), + PINMUX_IPSR_MODSEL_DATA(IP1_28_26, DACK2_B, SEL_LBS_1), + PINMUX_IPSR_MODSEL_DATA(IP1_28_26, SDA0_C, SEL_IIC0_2), + PINMUX_IPSR_DATA(IP1_31_29, A18), + PINMUX_IPSR_MODSEL_DATA(IP1_31_29, DREQ1, SEL_LBS_0), + PINMUX_IPSR_MODSEL_DATA(IP1_31_29, SCIFA1_RXD_C, SEL_SCIFA1_2), + PINMUX_IPSR_MODSEL_DATA(IP1_31_29, SCIFB1_RXD_C, SEL_SCIFB1_2), + + /* IPSR2 */ + PINMUX_IPSR_DATA(IP2_2_0, A19), + PINMUX_IPSR_DATA(IP2_2_0, DACK1), + PINMUX_IPSR_MODSEL_DATA(IP2_2_0, SCIFA1_TXD_C, SEL_SCIFA1_2), + PINMUX_IPSR_MODSEL_DATA(IP2_2_0, SCIFB1_TXD_C, SEL_SCIFB1_2), + PINMUX_IPSR_MODSEL_DATA(IP2_2_0, SCIFB1_SCK_B, SEL_SCIFB1_0), + PINMUX_IPSR_DATA(IP2_2_0, A20), + PINMUX_IPSR_MODSEL_DATA(IP2_4_3, SPCLK, SEL_QSP_0), + PINMUX_IPSR_DATA(IP2_6_5, A21), + PINMUX_IPSR_MODSEL_DATA(IP2_6_5, ATAWR0_N_B, SEL_LBS_1), + PINMUX_IPSR_MODSEL_DATA(IP2_6_5, MOSI_IO0, SEL_QSP_0), + PINMUX_IPSR_DATA(IP2_9_7, A22), + PINMUX_IPSR_MODSEL_DATA(IP2_9_7, MISO_IO1, SEL_QSP_0), + PINMUX_IPSR_MODSEL_DATA(IP2_9_7, FMCLK_B, SEL_FM_1), + PINMUX_IPSR_MODSEL_DATA(IP2_9_7, TX0, SEL_SCIF0_0), + PINMUX_IPSR_MODSEL_DATA(IP2_9_7, SCIFA0_TXD, SEL_SCFA_0), + PINMUX_IPSR_DATA(IP2_12_10, A23), + PINMUX_IPSR_MODSEL_DATA(IP2_12_10, IO2, SEL_QSP_0), + PINMUX_IPSR_MODSEL_DATA(IP2_12_10, BPFCLK_B, SEL_FM_1), + PINMUX_IPSR_MODSEL_DATA(IP2_12_10, RX0, SEL_SCIF0_0), + PINMUX_IPSR_MODSEL_DATA(IP2_12_10, SCIFA0_RXD, SEL_SCFA_0), + PINMUX_IPSR_DATA(IP2_15_13, A24), + PINMUX_IPSR_MODSEL_DATA(IP2_15_13, DREQ2, SEL_LBS_0), + PINMUX_IPSR_MODSEL_DATA(IP2_15_13, IO3, SEL_QSP_0), + PINMUX_IPSR_MODSEL_DATA(IP2_15_13, TX1, SEL_SCIF1_0), + PINMUX_IPSR_MODSEL_DATA(IP2_15_13, SCIFA1_TXD, SEL_SCIFA1_0), + PINMUX_IPSR_DATA(IP2_18_16, A25), + PINMUX_IPSR_MODSEL_DATA(IP2_18_16, DACK2, SEL_LBS_0), + PINMUX_IPSR_MODSEL_DATA(IP2_18_16, SSL, SEL_QSP_0), + PINMUX_IPSR_MODSEL_DATA(IP2_18_16, DREQ1_C, SEL_LBS_2), + PINMUX_IPSR_MODSEL_DATA(IP2_18_16, RX1, SEL_SCIF1_0), + PINMUX_IPSR_MODSEL_DATA(IP2_18_16, SCIFA1_RXD, SEL_SCIFA1_0), + PINMUX_IPSR_DATA(IP2_20_19, CS0_N), + PINMUX_IPSR_MODSEL_DATA(IP2_20_19, ATAG0_N_B, SEL_LBS_1), + PINMUX_IPSR_MODSEL_DATA(IP2_20_19, SCL1, SEL_IIC1_0), + PINMUX_IPSR_DATA(IP2_22_21, CS1_N_A26), + PINMUX_IPSR_MODSEL_DATA(IP2_22_21, ATADIR0_N_B, SEL_LBS_1), + PINMUX_IPSR_MODSEL_DATA(IP2_22_21, SDA1, SEL_IIC1_0), + PINMUX_IPSR_DATA(IP2_24_23, EX_CS1_N), + PINMUX_IPSR_MODSEL_DATA(IP2_24_23, MSIOF2_SCK, SEL_SOF2_0), + PINMUX_IPSR_DATA(IP2_26_25, EX_CS2_N), + PINMUX_IPSR_MODSEL_DATA(IP2_26_25, ATAWR0_N, SEL_LBS_0), + PINMUX_IPSR_MODSEL_DATA(IP2_26_25, MSIOF2_SYNC, SEL_SOF2_0), + PINMUX_IPSR_DATA(IP2_29_27, EX_CS3_N), + PINMUX_IPSR_MODSEL_DATA(IP2_29_27, ATADIR0_N, SEL_LBS_0), + PINMUX_IPSR_MODSEL_DATA(IP2_29_27, MSIOF2_TXD, SEL_SOF2_0), + PINMUX_IPSR_MODSEL_DATA(IP2_29_27, ATAG0_N, SEL_LBS_0), + PINMUX_IPSR_DATA(IP2_29_27, EX_WAIT1), + + /* IPSR3 */ + PINMUX_IPSR_DATA(IP3_2_0, EX_CS4_N), + PINMUX_IPSR_MODSEL_DATA(IP3_2_0, ATARD0_N, SEL_LBS_0), + PINMUX_IPSR_MODSEL_DATA(IP3_2_0, MSIOF2_RXD, SEL_SOF2_0), + PINMUX_IPSR_DATA(IP3_2_0, EX_WAIT2), + PINMUX_IPSR_DATA(IP3_5_3, EX_CS5_N), + PINMUX_IPSR_DATA(IP3_5_3, ATACS00_N), + PINMUX_IPSR_MODSEL_DATA(IP3_5_3, MSIOF2_SS1, SEL_SOF2_0), + PINMUX_IPSR_MODSEL_DATA(IP3_5_3, HRX1_B, SEL_HSCIF1_1), + PINMUX_IPSR_MODSEL_DATA(IP3_5_3, SCIFB1_RXD_B, SEL_SCIFB1_1), + PINMUX_IPSR_DATA(IP3_5_3, PWM1), + PINMUX_IPSR_DATA(IP3_5_3, TPU_TO1), + PINMUX_IPSR_DATA(IP3_8_6, BS_N), + PINMUX_IPSR_DATA(IP3_8_6, ATACS10_N), + PINMUX_IPSR_MODSEL_DATA(IP3_8_6, MSIOF2_SS2, SEL_SOF2_0), + PINMUX_IPSR_MODSEL_DATA(IP3_8_6, HTX1_B, SEL_HSCIF1_1), + PINMUX_IPSR_MODSEL_DATA(IP3_8_6, SCIFB1_TXD_B, SEL_SCIFB1_1), + PINMUX_IPSR_DATA(IP3_8_6, PWM2), + PINMUX_IPSR_DATA(IP3_8_6, TPU_TO2), + PINMUX_IPSR_DATA(IP3_11_9, RD_WR_N), + PINMUX_IPSR_MODSEL_DATA(IP3_11_9, HRX2_B, SEL_HSCIF2_1), + PINMUX_IPSR_MODSEL_DATA(IP3_11_9, FMIN_B, SEL_FM_1), + PINMUX_IPSR_MODSEL_DATA(IP3_11_9, SCIFB0_RXD_B, SEL_SCIFB_1), + PINMUX_IPSR_MODSEL_DATA(IP3_11_9, DREQ1_D, SEL_LBS_1), + PINMUX_IPSR_DATA(IP3_13_12, WE0_N), + PINMUX_IPSR_MODSEL_DATA(IP3_13_12, HCTS2_N_B, SEL_HSCIF2_1), + PINMUX_IPSR_MODSEL_DATA(IP3_13_12, SCIFB0_TXD_B, SEL_SCIFB_1), + PINMUX_IPSR_DATA(IP3_15_14, WE1_N), + PINMUX_IPSR_MODSEL_DATA(IP3_15_14, ATARD0_N_B, SEL_LBS_1), + PINMUX_IPSR_MODSEL_DATA(IP3_15_14, HTX2_B, SEL_HSCIF2_1), + PINMUX_IPSR_MODSEL_DATA(IP3_15_14, SCIFB0_RTS_N_B, SEL_SCIFB_1), + PINMUX_IPSR_DATA(IP3_17_16, EX_WAIT0), + PINMUX_IPSR_MODSEL_DATA(IP3_17_16, HRTS2_N_B, SEL_HSCIF2_1), + PINMUX_IPSR_MODSEL_DATA(IP3_17_16, SCIFB0_CTS_N_B, SEL_SCIFB_1), + PINMUX_IPSR_DATA(IP3_19_18, DREQ0), + PINMUX_IPSR_DATA(IP3_19_18, PWM3), + PINMUX_IPSR_DATA(IP3_19_18, TPU_TO3), + PINMUX_IPSR_DATA(IP3_21_20, DACK0), + PINMUX_IPSR_DATA(IP3_21_20, DRACK0), + PINMUX_IPSR_MODSEL_DATA(IP3_21_20, REMOCON, SEL_RCN_0), + PINMUX_IPSR_MODSEL_DATA(IP3_24_22, SPEEDIN, SEL_RSP_0), + PINMUX_IPSR_MODSEL_DATA(IP3_24_22, HSCK0_C, SEL_HSCIF0_2), + PINMUX_IPSR_MODSEL_DATA(IP3_24_22, HSCK2_C, SEL_HSCIF2_2), + PINMUX_IPSR_MODSEL_DATA(IP3_24_22, SCIFB0_SCK_B, SEL_SCIFB_1), + PINMUX_IPSR_MODSEL_DATA(IP3_24_22, SCIFB2_SCK_B, SEL_SCIFB2_1), + PINMUX_IPSR_MODSEL_DATA(IP3_24_22, DREQ2_C, SEL_LBS_2), + PINMUX_IPSR_MODSEL_DATA(IP3_30_28, HTX2_C, SEL_HSCIF2_2), + PINMUX_IPSR_MODSEL_DATA(IP3_27_25, SSI_SCK0129, SEL_SSI0_0), + PINMUX_IPSR_MODSEL_DATA(IP3_27_25, HRX0_C, SEL_HSCIF0_2), + PINMUX_IPSR_MODSEL_DATA(IP3_27_25, HRX2_C, SEL_HSCIF2_2), + PINMUX_IPSR_MODSEL_DATA(IP3_27_25, SCIFB0_RXD_C, SEL_SCIFB_2), + PINMUX_IPSR_MODSEL_DATA(IP3_27_25, SCIFB2_RXD_C, SEL_SCIFB2_2), + PINMUX_IPSR_MODSEL_DATA(IP3_30_28, SSI_WS0129, SEL_SSI0_0), + PINMUX_IPSR_MODSEL_DATA(IP3_30_28, HTX0_C, SEL_HSCIF0_2), + PINMUX_IPSR_MODSEL_DATA(IP3_30_28, HTX2_C, SEL_HSCIF2_2), + PINMUX_IPSR_MODSEL_DATA(IP3_30_28, SCIFB0_TXD_C, SEL_SCIFB_2), + PINMUX_IPSR_MODSEL_DATA(IP3_30_28, SCIFB2_TXD_C, SEL_SCIFB2_2), + + /* IPSR4 */ + PINMUX_IPSR_MODSEL_DATA(IP4_1_0, SSI_SDATA0, SEL_SSI0_0), + PINMUX_IPSR_MODSEL_DATA(IP4_1_0, SCL0_B, SEL_IIC0_1), + PINMUX_IPSR_MODSEL_DATA(IP4_1_0, SCL7_B, SEL_IIC7_1), + PINMUX_IPSR_MODSEL_DATA(IP4_1_0, MSIOF2_SCK_C, SEL_SOF2_2), + PINMUX_IPSR_MODSEL_DATA(IP4_4_2, SSI_SCK1, SEL_SSI1_0), + PINMUX_IPSR_MODSEL_DATA(IP4_4_2, SDA0_B, SEL_IIC0_1), + PINMUX_IPSR_MODSEL_DATA(IP4_4_2, SDA7_B, SEL_IIC7_1), + PINMUX_IPSR_MODSEL_DATA(IP4_4_2, MSIOF2_SYNC_C, SEL_SOF2_2), + PINMUX_IPSR_MODSEL_DATA(IP4_4_2, GLO_I0_D, SEL_GPS_3), + PINMUX_IPSR_MODSEL_DATA(IP4_7_5, SSI_WS1, SEL_SSI1_0), + PINMUX_IPSR_MODSEL_DATA(IP4_7_5, SCL1_B, SEL_IIC1_1), + PINMUX_IPSR_MODSEL_DATA(IP4_7_5, SCL8_B, SEL_IIC8_1), + PINMUX_IPSR_MODSEL_DATA(IP4_7_5, MSIOF2_TXD_C, SEL_SOF2_2), + PINMUX_IPSR_MODSEL_DATA(IP4_7_5, GLO_I1_D, SEL_GPS_3), + PINMUX_IPSR_MODSEL_DATA(IP4_9_8, SSI_SDATA1, SEL_SSI1_0), + PINMUX_IPSR_MODSEL_DATA(IP4_9_8, SDA1_B, SEL_IIC1_1), + PINMUX_IPSR_MODSEL_DATA(IP4_9_8, SDA8_B, SEL_IIC8_1), + PINMUX_IPSR_MODSEL_DATA(IP4_9_8, MSIOF2_RXD_C, SEL_SOF2_2), + PINMUX_IPSR_DATA(IP4_12_10, SSI_SCK2), + PINMUX_IPSR_MODSEL_DATA(IP4_12_10, SCL2, SEL_IIC2_0), + PINMUX_IPSR_MODSEL_DATA(IP4_12_10, GPS_CLK_B, SEL_GPS_1), + PINMUX_IPSR_MODSEL_DATA(IP4_12_10, GLO_Q0_D, SEL_GPS_3), + PINMUX_IPSR_DATA(IP4_15_13, SSI_WS2), + PINMUX_IPSR_MODSEL_DATA(IP4_15_13, SDA2, SEL_IIC2_0), + PINMUX_IPSR_MODSEL_DATA(IP4_15_13, GPS_SIGN_B, SEL_GPS_1), + PINMUX_IPSR_MODSEL_DATA(IP4_15_13, RX2_E, SEL_SCIF2_4), + PINMUX_IPSR_MODSEL_DATA(IP4_15_13, GLO_Q1_D, SEL_GPS_3), + PINMUX_IPSR_DATA(IP4_18_16, SSI_SDATA2), + PINMUX_IPSR_MODSEL_DATA(IP4_18_16, GPS_MAG_B, SEL_GPS_1), + PINMUX_IPSR_MODSEL_DATA(IP4_18_16, TX2_E, SEL_SCIF2_4), + PINMUX_IPSR_DATA(IP4_19, SSI_SCK34), + PINMUX_IPSR_DATA(IP4_20, SSI_WS34), + PINMUX_IPSR_DATA(IP4_21, SSI_SDATA3), + PINMUX_IPSR_DATA(IP4_23_22, SSI_SCK4), + PINMUX_IPSR_MODSEL_DATA(IP4_23_22, GLO_SS_D, SEL_GPS_3), + PINMUX_IPSR_DATA(IP4_25_24, SSI_WS4), + PINMUX_IPSR_MODSEL_DATA(IP4_25_24, GLO_RFON_D, SEL_GPS_3), + PINMUX_IPSR_DATA(IP4_27_26, SSI_SDATA4), + PINMUX_IPSR_MODSEL_DATA(IP4_27_26, MSIOF2_SCK_D, SEL_SOF2_3), + PINMUX_IPSR_DATA(IP4_30_28, SSI_SCK5), + PINMUX_IPSR_MODSEL_DATA(IP4_30_28, MSIOF1_SCK_C, SEL_SOF1_2), + PINMUX_IPSR_MODSEL_DATA(IP4_30_28, TS_SDATA0, SEL_TSIF0_0), + PINMUX_IPSR_MODSEL_DATA(IP4_30_28, GLO_I0, SEL_GPS_0), + PINMUX_IPSR_MODSEL_DATA(IP4_30_28, MSIOF2_SYNC_D, SEL_SOF2_3), + PINMUX_IPSR_DATA(IP4_30_28, VI1_R2_B), + + /* IPSR5 */ + PINMUX_IPSR_DATA(IP5_2_0, SSI_WS5), + PINMUX_IPSR_MODSEL_DATA(IP5_2_0, MSIOF1_SYNC_C, SEL_SOF1_2), + PINMUX_IPSR_MODSEL_DATA(IP5_2_0, TS_SCK0, SEL_TSIF0_0), + PINMUX_IPSR_MODSEL_DATA(IP5_2_0, GLO_I1, SEL_GPS_0), + PINMUX_IPSR_MODSEL_DATA(IP5_2_0, MSIOF2_TXD_D, SEL_SOF2_3), + PINMUX_IPSR_DATA(IP5_2_0, VI1_R3_B), + PINMUX_IPSR_DATA(IP5_5_3, SSI_SDATA5), + PINMUX_IPSR_MODSEL_DATA(IP5_5_3, MSIOF1_TXD_C, SEL_SOF1_2), + PINMUX_IPSR_MODSEL_DATA(IP5_5_3, TS_SDEN0, SEL_TSIF0_0), + PINMUX_IPSR_MODSEL_DATA(IP5_5_3, GLO_Q0, SEL_GPS_0), + PINMUX_IPSR_MODSEL_DATA(IP5_5_3, MSIOF2_SS1_D, SEL_SOF2_3), + PINMUX_IPSR_DATA(IP5_5_3, VI1_R4_B), + PINMUX_IPSR_DATA(IP5_8_6, SSI_SCK6), + PINMUX_IPSR_MODSEL_DATA(IP5_8_6, MSIOF1_RXD_C, SEL_SOF1_2), + PINMUX_IPSR_MODSEL_DATA(IP5_8_6, TS_SPSYNC0, SEL_TSIF0_0), + PINMUX_IPSR_MODSEL_DATA(IP5_8_6, GLO_Q1, SEL_GPS_0), + PINMUX_IPSR_MODSEL_DATA(IP5_8_6, MSIOF2_RXD_D, SEL_SOF2_3), + PINMUX_IPSR_DATA(IP5_8_6, VI1_R5_B), + PINMUX_IPSR_DATA(IP5_11_9, SSI_WS6), + PINMUX_IPSR_MODSEL_DATA(IP5_11_9, GLO_SCLK, SEL_GPS_0), + PINMUX_IPSR_MODSEL_DATA(IP5_11_9, MSIOF2_SS2_D, SEL_SOF2_3), + PINMUX_IPSR_DATA(IP5_11_9, VI1_R6_B), + PINMUX_IPSR_DATA(IP5_14_12, SSI_SDATA6), + PINMUX_IPSR_MODSEL_DATA(IP5_14_12, STP_IVCXO27_0_B, SEL_SSP_1), + PINMUX_IPSR_MODSEL_DATA(IP5_14_12, GLO_SDATA, SEL_GPS_0), + PINMUX_IPSR_DATA(IP5_14_12, VI1_R7_B), + PINMUX_IPSR_MODSEL_DATA(IP5_16_15, SSI_SCK78, SEL_SSI7_0), + PINMUX_IPSR_MODSEL_DATA(IP5_16_15, STP_ISCLK_0_B, SEL_SSP_1), + PINMUX_IPSR_MODSEL_DATA(IP5_16_15, GLO_SS, SEL_GPS_0), + PINMUX_IPSR_MODSEL_DATA(IP5_19_17, SSI_WS78, SEL_SSI7_0), + PINMUX_IPSR_MODSEL_DATA(IP5_19_17, TX0_D, SEL_SCIF0_3), + PINMUX_IPSR_MODSEL_DATA(IP5_19_17, STP_ISD_0_B, SEL_SSP_1), + PINMUX_IPSR_MODSEL_DATA(IP5_19_17, GLO_RFON, SEL_GPS_0), + PINMUX_IPSR_MODSEL_DATA(IP5_21_20, SSI_SDATA7, SEL_SSI7_0), + PINMUX_IPSR_MODSEL_DATA(IP5_21_20, RX0_D, SEL_SCIF0_3), + PINMUX_IPSR_MODSEL_DATA(IP5_21_20, STP_ISEN_0_B, SEL_SSP_1), + PINMUX_IPSR_MODSEL_DATA(IP5_23_22, SSI_SDATA8, SEL_SSI8_0), + PINMUX_IPSR_MODSEL_DATA(IP5_23_22, TX1_D, SEL_SCIF1_3), + PINMUX_IPSR_MODSEL_DATA(IP5_23_22, STP_ISSYNC_0_B, SEL_SSP_1), + PINMUX_IPSR_MODSEL_DATA(IP5_25_24, SSI_SCK9, SEL_SSI9_0), + PINMUX_IPSR_MODSEL_DATA(IP5_25_24, RX1_D, SEL_SCIF1_3), + PINMUX_IPSR_MODSEL_DATA(IP5_25_24, GLO_SCLK_D, SEL_GPS_3), + PINMUX_IPSR_MODSEL_DATA(IP5_28_26, SSI_WS9, SEL_SSI9_0), + PINMUX_IPSR_MODSEL_DATA(IP5_28_26, TX3_D, SEL_SCIF3_3), + PINMUX_IPSR_MODSEL_DATA(IP5_28_26, CAN0_TX_D, SEL_CAN0_3), + PINMUX_IPSR_MODSEL_DATA(IP5_28_26, GLO_SDATA_D, SEL_GPS_3), + PINMUX_IPSR_MODSEL_DATA(IP5_31_29, SSI_SDATA9, SEL_SSI9_0), + PINMUX_IPSR_MODSEL_DATA(IP5_31_29, RX3_D, SEL_SCIF3_3), + PINMUX_IPSR_MODSEL_DATA(IP5_31_29, CAN0_RX_D, SEL_CAN0_3), + + /* IPSR6 */ + PINMUX_IPSR_MODSEL_DATA(IP6_2_0, AUDIO_CLKB, SEL_ADG_0), + PINMUX_IPSR_MODSEL_DATA(IP6_2_0, STP_OPWM_0_B, SEL_SSP_1), + PINMUX_IPSR_MODSEL_DATA(IP6_2_0, MSIOF1_SCK_B, SEL_SOF1_1), + PINMUX_IPSR_MODSEL_DATA(IP6_2_0, SCIF_CLK, SEL_SCIF_0), + PINMUX_IPSR_MODSEL_DATA(IP6_2_0, BPFCLK_E, SEL_FM_4), + PINMUX_IPSR_DATA(IP6_5_3, AUDIO_CLKC), + PINMUX_IPSR_MODSEL_DATA(IP6_5_3, SCIFB0_SCK_C, SEL_SCIFB_2), + PINMUX_IPSR_MODSEL_DATA(IP6_5_3, MSIOF1_SYNC_B, SEL_SOF1_1), + PINMUX_IPSR_MODSEL_DATA(IP6_5_3, RX2, SEL_SCIF2_0), + PINMUX_IPSR_MODSEL_DATA(IP6_5_3, SCIFA2_RXD, SEL_SCIFA2_0), + PINMUX_IPSR_MODSEL_DATA(IP6_5_3, FMIN_E, SEL_FM_4), + PINMUX_IPSR_DATA(IP6_7_6, AUDIO_CLKOUT), + PINMUX_IPSR_MODSEL_DATA(IP6_7_6, MSIOF1_SS1_B, SEL_SOF1_1), + PINMUX_IPSR_MODSEL_DATA(IP6_5_3, TX2, SEL_SCIF2_0), + PINMUX_IPSR_MODSEL_DATA(IP6_7_6, SCIFA2_TXD, SEL_SCIFA2_0), + PINMUX_IPSR_DATA(IP6_9_8, IRQ0), + PINMUX_IPSR_MODSEL_DATA(IP6_9_8, SCIFB1_RXD_D, SEL_SCIFB1_3), + PINMUX_IPSR_DATA(IP6_9_8, INTC_IRQ0_N), + PINMUX_IPSR_DATA(IP6_11_10, IRQ1), + PINMUX_IPSR_MODSEL_DATA(IP6_11_10, SCIFB1_SCK_C, SEL_SCIFB1_2), + PINMUX_IPSR_DATA(IP6_11_10, INTC_IRQ1_N), + PINMUX_IPSR_DATA(IP6_13_12, IRQ2), + PINMUX_IPSR_MODSEL_DATA(IP6_13_12, SCIFB1_TXD_D, SEL_SCIFB1_3), + PINMUX_IPSR_DATA(IP6_13_12, INTC_IRQ2_N), + PINMUX_IPSR_DATA(IP6_15_14, IRQ3), + PINMUX_IPSR_MODSEL_DATA(IP6_15_14, SCL4_C, SEL_IIC4_2), + PINMUX_IPSR_MODSEL_DATA(IP6_15_14, MSIOF2_TXD_E, SEL_SOF2_4), + PINMUX_IPSR_DATA(IP6_15_14, INTC_IRQ4_N), + PINMUX_IPSR_DATA(IP6_18_16, IRQ4), + PINMUX_IPSR_MODSEL_DATA(IP6_18_16, HRX1_C, SEL_HSCIF1_2), + PINMUX_IPSR_MODSEL_DATA(IP6_18_16, SDA4_C, SEL_IIC4_2), + PINMUX_IPSR_MODSEL_DATA(IP6_18_16, MSIOF2_RXD_E, SEL_SOF2_4), + PINMUX_IPSR_DATA(IP6_18_16, INTC_IRQ4_N), + PINMUX_IPSR_DATA(IP6_20_19, IRQ5), + PINMUX_IPSR_MODSEL_DATA(IP6_20_19, HTX1_C, SEL_HSCIF1_2), + PINMUX_IPSR_MODSEL_DATA(IP6_20_19, SCL1_E, SEL_IIC1_4), + PINMUX_IPSR_MODSEL_DATA(IP6_20_19, MSIOF2_SCK_E, SEL_SOF2_4), + PINMUX_IPSR_DATA(IP6_23_21, IRQ6), + PINMUX_IPSR_MODSEL_DATA(IP6_23_21, HSCK1_C, SEL_HSCIF1_2), + PINMUX_IPSR_MODSEL_DATA(IP6_23_21, MSIOF1_SS2_B, SEL_SOF1_1), + PINMUX_IPSR_MODSEL_DATA(IP6_23_21, SDA1_E, SEL_IIC1_4), + PINMUX_IPSR_MODSEL_DATA(IP6_23_21, MSIOF2_SYNC_E, SEL_SOF2_4), + PINMUX_IPSR_DATA(IP6_26_24, IRQ7), + PINMUX_IPSR_MODSEL_DATA(IP6_26_24, HCTS1_N_C, SEL_HSCIF1_2), + PINMUX_IPSR_MODSEL_DATA(IP6_26_24, MSIOF1_TXD_B, SEL_SOF1_1), + PINMUX_IPSR_MODSEL_DATA(IP6_26_24, GPS_CLK_C, SEL_GPS_2), + PINMUX_IPSR_MODSEL_DATA(IP6_26_24, GPS_CLK_D, SEL_GPS_3), + PINMUX_IPSR_DATA(IP6_29_27, IRQ8), + PINMUX_IPSR_MODSEL_DATA(IP6_29_27, HRTS1_N_C, SEL_HSCIF1_2), + PINMUX_IPSR_MODSEL_DATA(IP6_29_27, MSIOF1_RXD_B, SEL_SOF1_1), + PINMUX_IPSR_MODSEL_DATA(IP6_29_27, GPS_SIGN_C, SEL_GPS_2), + PINMUX_IPSR_MODSEL_DATA(IP6_29_27, GPS_SIGN_D, SEL_GPS_3), + + /* IPSR7 */ + PINMUX_IPSR_DATA(IP7_2_0, IRQ9), + PINMUX_IPSR_MODSEL_DATA(IP7_2_0, DU1_DOTCLKIN_B, SEL_DIS_1), + PINMUX_IPSR_MODSEL_DATA(IP7_2_0, CAN_CLK_D, SEL_CANCLK_3), + PINMUX_IPSR_MODSEL_DATA(IP7_2_0, GPS_MAG_C, SEL_GPS_2), + PINMUX_IPSR_MODSEL_DATA(IP7_2_0, SCIF_CLK_B, SEL_SCIF_1), + PINMUX_IPSR_MODSEL_DATA(IP7_2_0, GPS_MAG_D, SEL_GPS_3), + PINMUX_IPSR_DATA(IP7_5_3, DU1_DR0), + PINMUX_IPSR_DATA(IP7_5_3, LCDOUT0), + PINMUX_IPSR_MODSEL_DATA(IP7_5_3, VI1_DATA0_B, SEL_VI1_1), + PINMUX_IPSR_MODSEL_DATA(IP7_5_3, TX0_B, SEL_SCIF0_1), + PINMUX_IPSR_MODSEL_DATA(IP7_5_3, SCIFA0_TXD_B, SEL_SCFA_1), + PINMUX_IPSR_MODSEL_DATA(IP7_5_3, MSIOF2_SCK_B, SEL_SOF2_1), + PINMUX_IPSR_DATA(IP7_8_6, DU1_DR1), + PINMUX_IPSR_DATA(IP7_8_6, LCDOUT1), + PINMUX_IPSR_MODSEL_DATA(IP7_8_6, VI1_DATA1_B, SEL_VI1_1), + PINMUX_IPSR_MODSEL_DATA(IP7_8_6, RX0_B, SEL_SCIF0_1), + PINMUX_IPSR_MODSEL_DATA(IP7_8_6, SCIFA0_RXD_B, SEL_SCFA_1), + PINMUX_IPSR_MODSEL_DATA(IP7_8_6, MSIOF2_SYNC_B, SEL_SOF2_1), + PINMUX_IPSR_DATA(IP7_10_9, DU1_DR2), + PINMUX_IPSR_DATA(IP7_10_9, LCDOUT2), + PINMUX_IPSR_MODSEL_DATA(IP7_10_9, SSI_SCK0129_B, SEL_SSI0_1), + PINMUX_IPSR_DATA(IP7_12_11, DU1_DR3), + PINMUX_IPSR_DATA(IP7_12_11, LCDOUT3), + PINMUX_IPSR_MODSEL_DATA(IP7_12_11, SSI_WS0129_B, SEL_SSI0_1), + PINMUX_IPSR_DATA(IP7_14_13, DU1_DR4), + PINMUX_IPSR_DATA(IP7_14_13, LCDOUT4), + PINMUX_IPSR_MODSEL_DATA(IP7_14_13, SSI_SDATA0_B, SEL_SSI0_1), + PINMUX_IPSR_DATA(IP7_16_15, DU1_DR5), + PINMUX_IPSR_DATA(IP7_16_15, LCDOUT5), + PINMUX_IPSR_MODSEL_DATA(IP7_16_15, SSI_SCK1_B, SEL_SSI1_1), + PINMUX_IPSR_DATA(IP7_18_17, DU1_DR6), + PINMUX_IPSR_DATA(IP7_18_17, LCDOUT6), + PINMUX_IPSR_MODSEL_DATA(IP7_18_17, SSI_WS1_B, SEL_SSI1_1), + PINMUX_IPSR_DATA(IP7_20_19, DU1_DR7), + PINMUX_IPSR_DATA(IP7_20_19, LCDOUT7), + PINMUX_IPSR_MODSEL_DATA(IP7_20_19, SSI_SDATA1_B, SEL_SSI1_1), + PINMUX_IPSR_DATA(IP7_23_21, DU1_DG0), + PINMUX_IPSR_DATA(IP7_23_21, LCDOUT8), + PINMUX_IPSR_MODSEL_DATA(IP7_23_21, VI1_DATA2_B, SEL_VI1_1), + PINMUX_IPSR_MODSEL_DATA(IP7_23_21, TX1_B, SEL_SCIF1_1), + PINMUX_IPSR_MODSEL_DATA(IP7_23_21, SCIFA1_TXD_B, SEL_SCIFA1_1), + PINMUX_IPSR_MODSEL_DATA(IP7_23_21, MSIOF2_SS1_B, SEL_SOF2_1), + PINMUX_IPSR_DATA(IP7_26_24, DU1_DG1), + PINMUX_IPSR_DATA(IP7_26_24, LCDOUT9), + PINMUX_IPSR_MODSEL_DATA(IP7_26_24, VI1_DATA3_B, SEL_VI1_1), + PINMUX_IPSR_MODSEL_DATA(IP7_26_24, RX1_B, SEL_SCIF1_1), + PINMUX_IPSR_MODSEL_DATA(IP7_26_24, SCIFA1_RXD_B, SEL_SCIFA1_1), + PINMUX_IPSR_MODSEL_DATA(IP7_26_24, MSIOF2_SS2_B, SEL_SOF2_1), + PINMUX_IPSR_DATA(IP7_29_27, DU1_DG2), + PINMUX_IPSR_DATA(IP7_29_27, LCDOUT10), + PINMUX_IPSR_MODSEL_DATA(IP7_29_27, VI1_DATA4_B, SEL_VI1_1), + PINMUX_IPSR_DATA(IP7_29_27, SCIF1_SCK_B), + PINMUX_IPSR_MODSEL_DATA(IP7_29_27, SCIFA1_SCK, SEL_SCIFA1_0), + PINMUX_IPSR_MODSEL_DATA(IP7_29_27, SSI_SCK78_B, SEL_SSI7_1), + + /* IPSR8 */ + PINMUX_IPSR_DATA(IP8_2_0, DU1_DG3), + PINMUX_IPSR_DATA(IP8_2_0, LCDOUT11), + PINMUX_IPSR_MODSEL_DATA(IP8_2_0, VI1_DATA5_B, SEL_VI1_1), + PINMUX_IPSR_MODSEL_DATA(IP8_2_0, SSI_WS78_B, SEL_SSI7_1), + PINMUX_IPSR_DATA(IP8_5_3, DU1_DG4), + PINMUX_IPSR_DATA(IP8_5_3, LCDOUT12), + PINMUX_IPSR_MODSEL_DATA(IP8_5_3, VI1_DATA6_B, SEL_VI1_1), + PINMUX_IPSR_MODSEL_DATA(IP8_5_3, HRX0_B, SEL_HSCIF0_1), + PINMUX_IPSR_MODSEL_DATA(IP8_5_3, SCIFB2_RXD_B, SEL_SCIFB2_1), + PINMUX_IPSR_MODSEL_DATA(IP8_5_3, SSI_SDATA7_B, SEL_SSI7_1), + PINMUX_IPSR_DATA(IP8_8_6, DU1_DG5), + PINMUX_IPSR_DATA(IP8_8_6, LCDOUT13), + PINMUX_IPSR_MODSEL_DATA(IP8_8_6, VI1_DATA7_B, SEL_VI1_1), + PINMUX_IPSR_MODSEL_DATA(IP8_8_6, HCTS0_N_B, SEL_HSCIF0_1), + PINMUX_IPSR_MODSEL_DATA(IP8_8_6, SCIFB2_TXD_B, SEL_SCIFB2_1), + PINMUX_IPSR_MODSEL_DATA(IP8_8_6, SSI_SDATA8_B, SEL_SSI8_1), + PINMUX_IPSR_DATA(IP8_11_9, DU1_DG6), + PINMUX_IPSR_DATA(IP8_11_9, LCDOUT14), + PINMUX_IPSR_MODSEL_DATA(IP8_11_9, HRTS0_N_B, SEL_HSCIF0_1), + PINMUX_IPSR_MODSEL_DATA(IP8_11_9, SCIFB2_CTS_N_B, SEL_SCIFB2_1), + PINMUX_IPSR_MODSEL_DATA(IP8_11_9, SSI_SCK9_B, SEL_SSI9_1), + PINMUX_IPSR_DATA(IP8_14_12, DU1_DG7), + PINMUX_IPSR_DATA(IP8_14_12, LCDOUT15), + PINMUX_IPSR_MODSEL_DATA(IP8_14_12, HTX0_B, SEL_HSCIF0_1), + PINMUX_IPSR_MODSEL_DATA(IP8_14_12, SCIFB2_RTS_N_B, SEL_SCIFB2_1), + PINMUX_IPSR_MODSEL_DATA(IP8_14_12, SSI_WS9_B, SEL_SSI9_1), + PINMUX_IPSR_DATA(IP8_17_15, DU1_DB0), + PINMUX_IPSR_DATA(IP8_17_15, LCDOUT16), + PINMUX_IPSR_MODSEL_DATA(IP8_17_15, VI1_CLK_B, SEL_VI1_1), + PINMUX_IPSR_MODSEL_DATA(IP8_17_15, TX2_B, SEL_SCIF2_1), + PINMUX_IPSR_MODSEL_DATA(IP8_17_15, SCIFA2_TXD_B, SEL_SCIFA2_1), + PINMUX_IPSR_MODSEL_DATA(IP8_17_15, MSIOF2_TXD_B, SEL_SOF2_1), + PINMUX_IPSR_DATA(IP8_20_18, DU1_DB1), + PINMUX_IPSR_DATA(IP8_20_18, LCDOUT17), + PINMUX_IPSR_MODSEL_DATA(IP8_20_18, VI1_HSYNC_N_B, SEL_VI1_1), + PINMUX_IPSR_MODSEL_DATA(IP8_20_18, RX2_B, SEL_SCIF2_1), + PINMUX_IPSR_MODSEL_DATA(IP8_20_18, SCIFA2_RXD_B, SEL_SCIFA2_1), + PINMUX_IPSR_MODSEL_DATA(IP8_20_18, MSIOF2_RXD_B, SEL_SOF2_1), + PINMUX_IPSR_DATA(IP8_23_21, DU1_DB2), + PINMUX_IPSR_DATA(IP8_23_21, LCDOUT18), + PINMUX_IPSR_MODSEL_DATA(IP8_23_21, VI1_VSYNC_N_B, SEL_VI1_1), + PINMUX_IPSR_DATA(IP8_23_21, SCIF2_SCK_B), + PINMUX_IPSR_MODSEL_DATA(IP8_23_21, SCIFA2_SCK, SEL_SCIFA2_1), + PINMUX_IPSR_MODSEL_DATA(IP8_23_21, SSI_SDATA9_B, SEL_SSI9_1), + PINMUX_IPSR_DATA(IP8_25_24, DU1_DB3), + PINMUX_IPSR_DATA(IP8_25_24, LCDOUT19), + PINMUX_IPSR_MODSEL_DATA(IP8_25_24, VI1_CLKENB_B, SEL_VI1_1), + PINMUX_IPSR_DATA(IP8_27_26, DU1_DB4), + PINMUX_IPSR_DATA(IP8_27_26, LCDOUT20), + PINMUX_IPSR_MODSEL_DATA(IP8_27_26, VI1_FIELD_B, SEL_VI1_1), + PINMUX_IPSR_MODSEL_DATA(IP8_27_26, CAN1_RX, SEL_CAN1_0), + PINMUX_IPSR_DATA(IP8_30_28, DU1_DB5), + PINMUX_IPSR_DATA(IP8_30_28, LCDOUT21), + PINMUX_IPSR_MODSEL_DATA(IP8_30_28, TX3, SEL_SCIF3_0), + PINMUX_IPSR_MODSEL_DATA(IP8_30_28, SCIFA3_TXD, SEL_SCIFA3_0), + PINMUX_IPSR_MODSEL_DATA(IP8_30_28, CAN1_TX, SEL_CAN1_0), + + /* IPSR9 */ + PINMUX_IPSR_DATA(IP9_2_0, DU1_DB6), + PINMUX_IPSR_DATA(IP9_2_0, LCDOUT22), + PINMUX_IPSR_MODSEL_DATA(IP9_2_0, SCL3_C, SEL_IIC3_2), + PINMUX_IPSR_MODSEL_DATA(IP9_2_0, RX3, SEL_SCIF3_0), + PINMUX_IPSR_MODSEL_DATA(IP9_2_0, SCIFA3_RXD, SEL_SCIFA3_0), + PINMUX_IPSR_DATA(IP9_5_3, DU1_DB7), + PINMUX_IPSR_DATA(IP9_5_3, LCDOUT23), + PINMUX_IPSR_MODSEL_DATA(IP9_5_3, SDA3_C, SEL_IIC3_2), + PINMUX_IPSR_MODSEL_DATA(IP9_5_3, SCIF3_SCK, SEL_SCIF3_0), + PINMUX_IPSR_MODSEL_DATA(IP9_5_3, SCIFA3_SCK, SEL_SCIFA3_0), + PINMUX_IPSR_MODSEL_DATA(IP9_6, DU1_DOTCLKIN, SEL_DIS_0), + PINMUX_IPSR_DATA(IP9_6, QSTVA_QVS), + PINMUX_IPSR_DATA(IP9_7, DU1_DOTCLKOUT0), + PINMUX_IPSR_DATA(IP9_7, QCLK), + PINMUX_IPSR_DATA(IP9_10_8, DU1_DOTCLKOUT1), + PINMUX_IPSR_DATA(IP9_10_8, QSTVB_QVE), + PINMUX_IPSR_MODSEL_DATA(IP9_10_8, CAN0_TX, SEL_CAN0_0), + PINMUX_IPSR_MODSEL_DATA(IP9_10_8, TX3_B, SEL_SCIF3_1), + PINMUX_IPSR_MODSEL_DATA(IP9_10_8, SCL2_B, SEL_IIC2_1), + PINMUX_IPSR_DATA(IP9_10_8, PWM4), + PINMUX_IPSR_DATA(IP9_11, DU1_EXHSYNC_DU1_HSYNC), + PINMUX_IPSR_DATA(IP9_11, QSTH_QHS), + PINMUX_IPSR_DATA(IP9_12, DU1_EXVSYNC_DU1_VSYNC), + PINMUX_IPSR_DATA(IP9_12, QSTB_QHE), + PINMUX_IPSR_DATA(IP9_15_13, DU1_EXODDF_DU1_ODDF_DISP_CDE), + PINMUX_IPSR_DATA(IP9_15_13, QCPV_QDE), + PINMUX_IPSR_MODSEL_DATA(IP9_15_13, CAN0_RX, SEL_CAN0_0), + PINMUX_IPSR_MODSEL_DATA(IP9_15_13, RX3_B, SEL_SCIF3_1), + PINMUX_IPSR_MODSEL_DATA(IP9_15_13, SDA2_B, SEL_IIC2_1), + PINMUX_IPSR_DATA(IP9_16, DU1_DISP), + PINMUX_IPSR_DATA(IP9_16, QPOLA), + PINMUX_IPSR_DATA(IP9_18_17, DU1_CDE), + PINMUX_IPSR_DATA(IP9_18_17, QPOLB), + PINMUX_IPSR_DATA(IP9_18_17, PWM4_B), + PINMUX_IPSR_DATA(IP9_20_19, VI0_CLKENB), + PINMUX_IPSR_MODSEL_DATA(IP9_20_19, TX4, SEL_SCIF4_0), + PINMUX_IPSR_MODSEL_DATA(IP9_20_19, SCIFA4_TXD, SEL_SCIFA4_0), + PINMUX_IPSR_MODSEL_DATA(IP9_20_19, TS_SDATA0_D, SEL_TSIF0_3), + PINMUX_IPSR_DATA(IP9_22_21, VI0_FIELD), + PINMUX_IPSR_MODSEL_DATA(IP9_22_21, RX4, SEL_SCIF4_0), + PINMUX_IPSR_MODSEL_DATA(IP9_22_21, SCIFA4_RXD, SEL_SCIFA4_0), + PINMUX_IPSR_MODSEL_DATA(IP9_22_21, TS_SCK0_D, SEL_TSIF0_3), + PINMUX_IPSR_DATA(IP9_24_23, VI0_HSYNC_N), + PINMUX_IPSR_MODSEL_DATA(IP9_24_23, TX5, SEL_SCIF5_0), + PINMUX_IPSR_MODSEL_DATA(IP9_24_23, SCIFA5_TXD, SEL_SCIFA5_0), + PINMUX_IPSR_MODSEL_DATA(IP9_24_23, TS_SDEN0_D, SEL_TSIF0_3), + PINMUX_IPSR_DATA(IP9_26_25, VI0_VSYNC_N), + PINMUX_IPSR_MODSEL_DATA(IP9_26_25, RX5, SEL_SCIF5_0), + PINMUX_IPSR_MODSEL_DATA(IP9_26_25, SCIFA5_RXD, SEL_SCIFA5_0), + PINMUX_IPSR_MODSEL_DATA(IP9_26_25, TS_SPSYNC0_D, SEL_TSIF0_3), + PINMUX_IPSR_DATA(IP9_28_27, VI0_DATA3_VI0_B3), + PINMUX_IPSR_MODSEL_DATA(IP9_28_27, SCIF3_SCK_B, SEL_SCIF3_1), + PINMUX_IPSR_MODSEL_DATA(IP9_28_27, SCIFA3_SCK_B, SEL_SCIFA3_1), + PINMUX_IPSR_DATA(IP9_31_29, VI0_G0), + PINMUX_IPSR_MODSEL_DATA(IP9_31_29, SCL8, SEL_IIC8_0), + PINMUX_IPSR_MODSEL_DATA(IP9_31_29, STP_IVCXO27_0_C, SEL_SSP_2), + PINMUX_IPSR_MODSEL_DATA(IP9_31_29, SCL4, SEL_IIC4_0), + PINMUX_IPSR_MODSEL_DATA(IP9_31_29, HCTS2_N, SEL_HSCIF2_0), + PINMUX_IPSR_MODSEL_DATA(IP9_31_29, SCIFB2_CTS_N, SEL_SCIFB2_0), + PINMUX_IPSR_DATA(IP9_31_29, ATAWR1_N), + + /* IPSR10 */ + PINMUX_IPSR_DATA(IP10_2_0, VI0_G1), + PINMUX_IPSR_MODSEL_DATA(IP10_2_0, SDA8, SEL_IIC8_0), + PINMUX_IPSR_MODSEL_DATA(IP10_2_0, STP_ISCLK_0_C, SEL_SSP_2), + PINMUX_IPSR_MODSEL_DATA(IP10_2_0, SDA4, SEL_IIC4_0), + PINMUX_IPSR_MODSEL_DATA(IP10_2_0, HRTS2_N, SEL_HSCIF2_0), + PINMUX_IPSR_MODSEL_DATA(IP10_2_0, SCIFB2_RTS_N, SEL_SCIFB2_0), + PINMUX_IPSR_DATA(IP10_2_0, ATADIR1_N), + PINMUX_IPSR_DATA(IP10_5_3, VI0_G2), + PINMUX_IPSR_DATA(IP10_5_3, VI2_HSYNC_N), + PINMUX_IPSR_MODSEL_DATA(IP10_5_3, STP_ISD_0_C, SEL_SSP_2), + PINMUX_IPSR_MODSEL_DATA(IP10_5_3, SCL3_B, SEL_IIC3_1), + PINMUX_IPSR_MODSEL_DATA(IP10_5_3, HSCK2, SEL_HSCIF2_0), + PINMUX_IPSR_MODSEL_DATA(IP10_5_3, SCIFB2_SCK, SEL_SCIFB2_0), + PINMUX_IPSR_DATA(IP10_5_3, ATARD1_N), + PINMUX_IPSR_DATA(IP10_8_6, VI0_G3), + PINMUX_IPSR_DATA(IP10_8_6, VI2_VSYNC_N), + PINMUX_IPSR_MODSEL_DATA(IP10_8_6, STP_ISEN_0_C, SEL_SSP_2), + PINMUX_IPSR_MODSEL_DATA(IP10_8_6, SDA3_B, SEL_IIC3_1), + PINMUX_IPSR_MODSEL_DATA(IP10_8_6, HRX2, SEL_HSCIF2_0), + PINMUX_IPSR_MODSEL_DATA(IP10_8_6, SCIFB2_RXD, SEL_SCIFB2_0), + PINMUX_IPSR_DATA(IP10_8_6, ATACS01_N), + PINMUX_IPSR_DATA(IP10_11_9, VI0_G4), + PINMUX_IPSR_DATA(IP10_11_9, VI2_CLKENB), + PINMUX_IPSR_MODSEL_DATA(IP10_11_9, STP_ISSYNC_0_C, SEL_SSP_2), + PINMUX_IPSR_MODSEL_DATA(IP10_11_9, HTX2, SEL_HSCIF2_0), + PINMUX_IPSR_MODSEL_DATA(IP10_11_9, SCIFB2_TXD, SEL_SCIFB2_0), + PINMUX_IPSR_MODSEL_DATA(IP10_11_9, SCIFB0_SCK_D, SEL_SCIFB_3), + PINMUX_IPSR_DATA(IP10_14_12, VI0_G5), + PINMUX_IPSR_DATA(IP10_14_12, VI2_FIELD), + PINMUX_IPSR_MODSEL_DATA(IP10_14_12, STP_OPWM_0_C, SEL_SSP_2), + PINMUX_IPSR_MODSEL_DATA(IP10_14_12, FMCLK_D, SEL_FM_3), + PINMUX_IPSR_MODSEL_DATA(IP10_14_12, CAN0_TX_E, SEL_CAN0_4), + PINMUX_IPSR_MODSEL_DATA(IP10_14_12, HTX1_D, SEL_HSCIF1_3), + PINMUX_IPSR_MODSEL_DATA(IP10_14_12, SCIFB0_TXD_D, SEL_SCIFB_3), + PINMUX_IPSR_DATA(IP10_16_15, VI0_G6), + PINMUX_IPSR_DATA(IP10_16_15, VI2_CLK), + PINMUX_IPSR_MODSEL_DATA(IP10_16_15, BPFCLK_D, SEL_FM_3), + PINMUX_IPSR_DATA(IP10_18_17, VI0_G7), + PINMUX_IPSR_DATA(IP10_18_17, VI2_DATA0), + PINMUX_IPSR_MODSEL_DATA(IP10_18_17, FMIN_D, SEL_FM_3), + PINMUX_IPSR_DATA(IP10_21_19, VI0_R0), + PINMUX_IPSR_DATA(IP10_21_19, VI2_DATA1), + PINMUX_IPSR_MODSEL_DATA(IP10_21_19, GLO_I0_B, SEL_GPS_1), + PINMUX_IPSR_MODSEL_DATA(IP10_21_19, TS_SDATA0_C, SEL_TSIF0_2), + PINMUX_IPSR_DATA(IP10_21_19, ATACS11_N), + PINMUX_IPSR_DATA(IP10_24_22, VI0_R1), + PINMUX_IPSR_DATA(IP10_24_22, VI2_DATA2), + PINMUX_IPSR_MODSEL_DATA(IP10_24_22, GLO_I1_B, SEL_GPS_1), + PINMUX_IPSR_MODSEL_DATA(IP10_24_22, TS_SCK0_C, SEL_TSIF0_2), + PINMUX_IPSR_DATA(IP10_24_22, ATAG1_N), + PINMUX_IPSR_DATA(IP10_26_25, VI0_R2), + PINMUX_IPSR_DATA(IP10_26_25, VI2_DATA3), + PINMUX_IPSR_MODSEL_DATA(IP10_26_25, GLO_Q0_B, SEL_GPS_1), + PINMUX_IPSR_MODSEL_DATA(IP10_26_25, TS_SDEN0_C, SEL_TSIF0_2), + PINMUX_IPSR_DATA(IP10_28_27, VI0_R3), + PINMUX_IPSR_DATA(IP10_28_27, VI2_DATA4), + PINMUX_IPSR_MODSEL_DATA(IP10_28_27, GLO_Q1_B, SEL_GPS_1), + PINMUX_IPSR_MODSEL_DATA(IP10_28_27, TS_SPSYNC0_C, SEL_TSIF0_2), + PINMUX_IPSR_DATA(IP10_31_29, VI0_R4), + PINMUX_IPSR_DATA(IP10_31_29, VI2_DATA5), + PINMUX_IPSR_MODSEL_DATA(IP10_31_29, GLO_SCLK_B, SEL_GPS_1), + PINMUX_IPSR_MODSEL_DATA(IP10_31_29, TX0_C, SEL_SCIF0_2), + PINMUX_IPSR_MODSEL_DATA(IP10_31_29, SCL1_D, SEL_IIC1_3), + + /* IPSR11 */ + PINMUX_IPSR_DATA(IP11_2_0, VI0_R5), + PINMUX_IPSR_DATA(IP11_2_0, VI2_DATA6), + PINMUX_IPSR_MODSEL_DATA(IP11_2_0, GLO_SDATA_B, SEL_GPS_1), + PINMUX_IPSR_MODSEL_DATA(IP11_2_0, RX0_C, SEL_SCIF0_2), + PINMUX_IPSR_MODSEL_DATA(IP11_2_0, SDA1_D, SEL_IIC1_3), + PINMUX_IPSR_DATA(IP11_5_3, VI0_R6), + PINMUX_IPSR_DATA(IP11_5_3, VI2_DATA7), + PINMUX_IPSR_MODSEL_DATA(IP11_5_3, GLO_SS_B, SEL_GPS_1), + PINMUX_IPSR_MODSEL_DATA(IP11_5_3, TX1_C, SEL_SCIF1_2), + PINMUX_IPSR_MODSEL_DATA(IP11_5_3, SCL4_B, SEL_IIC4_1), + PINMUX_IPSR_DATA(IP11_8_6, VI0_R7), + PINMUX_IPSR_MODSEL_DATA(IP11_8_6, GLO_RFON_B, SEL_GPS_1), + PINMUX_IPSR_MODSEL_DATA(IP11_8_6, RX1_C, SEL_SCIF1_2), + PINMUX_IPSR_MODSEL_DATA(IP11_8_6, CAN0_RX_E, SEL_CAN0_4), + PINMUX_IPSR_MODSEL_DATA(IP11_8_6, SDA4_B, SEL_IIC4_1), + PINMUX_IPSR_MODSEL_DATA(IP11_8_6, HRX1_D, SEL_HSCIF1_3), + PINMUX_IPSR_MODSEL_DATA(IP11_8_6, SCIFB0_RXD_D, SEL_SCIFB_3), + PINMUX_IPSR_MODSEL_DATA(IP11_11_9, VI1_HSYNC_N, SEL_VI1_0), + PINMUX_IPSR_DATA(IP11_11_9, AVB_RXD0), + PINMUX_IPSR_MODSEL_DATA(IP11_11_9, TS_SDATA0_B, SEL_TSIF0_1), + PINMUX_IPSR_MODSEL_DATA(IP11_11_9, TX4_B, SEL_SCIF4_1), + PINMUX_IPSR_MODSEL_DATA(IP11_11_9, SCIFA4_TXD_B, SEL_SCIFA4_1), + PINMUX_IPSR_MODSEL_DATA(IP11_14_12, VI1_VSYNC_N, SEL_VI1_0), + PINMUX_IPSR_DATA(IP11_14_12, AVB_RXD1), + PINMUX_IPSR_MODSEL_DATA(IP11_14_12, TS_SCK0_B, SEL_TSIF0_1), + PINMUX_IPSR_MODSEL_DATA(IP11_14_12, RX4_B, SEL_SCIF4_1), + PINMUX_IPSR_MODSEL_DATA(IP11_14_12, SCIFA4_RXD_B, SEL_SCIFA4_1), + PINMUX_IPSR_MODSEL_DATA(IP11_16_15, VI1_CLKENB, SEL_VI1_0), + PINMUX_IPSR_DATA(IP11_16_15, AVB_RXD2), + PINMUX_IPSR_MODSEL_DATA(IP11_16_15, TS_SDEN0_B, SEL_TSIF0_1), + PINMUX_IPSR_MODSEL_DATA(IP11_18_17, VI1_FIELD, SEL_VI1_0), + PINMUX_IPSR_DATA(IP11_18_17, AVB_RXD3), + PINMUX_IPSR_MODSEL_DATA(IP11_18_17, TS_SPSYNC0_B, SEL_TSIF0_1), + PINMUX_IPSR_MODSEL_DATA(IP11_19, VI1_CLK, SEL_VI1_0), + PINMUX_IPSR_DATA(IP11_19, AVB_RXD4), + PINMUX_IPSR_MODSEL_DATA(IP11_20, VI1_DATA0, SEL_VI1_0), + PINMUX_IPSR_DATA(IP11_20, AVB_RXD5), + PINMUX_IPSR_MODSEL_DATA(IP11_21, VI1_DATA1, SEL_VI1_0), + PINMUX_IPSR_DATA(IP11_21, AVB_RXD6), + PINMUX_IPSR_MODSEL_DATA(IP11_22, VI1_DATA2, SEL_VI1_0), + PINMUX_IPSR_DATA(IP11_22, AVB_RXD7), + PINMUX_IPSR_MODSEL_DATA(IP11_23, VI1_DATA3, SEL_VI1_0), + PINMUX_IPSR_DATA(IP11_23, AVB_RX_ER), + PINMUX_IPSR_MODSEL_DATA(IP11_24, VI1_DATA4, SEL_VI1_0), + PINMUX_IPSR_DATA(IP11_24, AVB_MDIO), + PINMUX_IPSR_MODSEL_DATA(IP11_25, VI1_DATA5, SEL_VI1_0), + PINMUX_IPSR_DATA(IP11_25, AVB_RX_DV), + PINMUX_IPSR_MODSEL_DATA(IP11_26, VI1_DATA6, SEL_VI1_0), + PINMUX_IPSR_DATA(IP11_26, AVB_MAGIC), + PINMUX_IPSR_MODSEL_DATA(IP11_27, VI1_DATA7, SEL_VI1_0), + PINMUX_IPSR_DATA(IP11_27, AVB_MDC), + PINMUX_IPSR_DATA(IP11_29_28, ETH_MDIO), + PINMUX_IPSR_DATA(IP11_29_28, AVB_RX_CLK), + PINMUX_IPSR_MODSEL_DATA(IP11_29_28, SCL2_C, SEL_IIC2_2), + PINMUX_IPSR_DATA(IP11_31_30, ETH_CRS_DV), + PINMUX_IPSR_DATA(IP11_31_30, AVB_LINK), + PINMUX_IPSR_MODSEL_DATA(IP11_31_30, SDA2_C, SEL_IIC2_2), + + /* IPSR12 */ + PINMUX_IPSR_DATA(IP12_1_0, ETH_RX_ER), + PINMUX_IPSR_DATA(IP12_1_0, AVB_CRS), + PINMUX_IPSR_MODSEL_DATA(IP12_1_0, SCL3, SEL_IIC3_0), + PINMUX_IPSR_MODSEL_DATA(IP12_1_0, SCL7, SEL_IIC7_0), + PINMUX_IPSR_DATA(IP12_3_2, ETH_RXD0), + PINMUX_IPSR_DATA(IP12_3_2, AVB_PHY_INT), + PINMUX_IPSR_MODSEL_DATA(IP12_3_2, SDA3, SEL_IIC3_0), + PINMUX_IPSR_MODSEL_DATA(IP12_3_2, SDA7, SEL_IIC7_0), + PINMUX_IPSR_DATA(IP12_6_4, ETH_RXD1), + PINMUX_IPSR_DATA(IP12_6_4, AVB_GTXREFCLK), + PINMUX_IPSR_MODSEL_DATA(IP12_6_4, CAN0_TX_C, SEL_CAN0_2), + PINMUX_IPSR_MODSEL_DATA(IP12_6_4, SCL2_D, SEL_IIC2_3), + PINMUX_IPSR_MODSEL_DATA(IP12_6_4, MSIOF1_RXD_E, SEL_SOF1_4), + PINMUX_IPSR_DATA(IP12_9_7, ETH_LINK), + PINMUX_IPSR_DATA(IP12_9_7, AVB_TXD0), + PINMUX_IPSR_MODSEL_DATA(IP12_9_7, CAN0_RX_C, SEL_CAN0_2), + PINMUX_IPSR_MODSEL_DATA(IP12_9_7, SDA2_D, SEL_IIC2_3), + PINMUX_IPSR_MODSEL_DATA(IP12_9_7, MSIOF1_SCK_E, SEL_SOF1_4), + PINMUX_IPSR_DATA(IP12_12_10, ETH_REFCLK), + PINMUX_IPSR_DATA(IP12_12_10, AVB_TXD1), + PINMUX_IPSR_MODSEL_DATA(IP12_12_10, SCIFA3_RXD_B, SEL_SCIFA3_1), + PINMUX_IPSR_MODSEL_DATA(IP12_12_10, CAN1_RX_C, SEL_CAN1_2), + PINMUX_IPSR_MODSEL_DATA(IP12_12_10, MSIOF1_SYNC_E, SEL_SOF1_4), + PINMUX_IPSR_DATA(IP12_15_13, ETH_TXD1), + PINMUX_IPSR_DATA(IP12_15_13, AVB_TXD2), + PINMUX_IPSR_MODSEL_DATA(IP12_15_13, SCIFA3_TXD_B, SEL_SCIFA3_1), + PINMUX_IPSR_MODSEL_DATA(IP12_15_13, CAN1_TX_C, SEL_CAN1_2), + PINMUX_IPSR_MODSEL_DATA(IP12_15_13, MSIOF1_TXD_E, SEL_SOF1_4), + PINMUX_IPSR_DATA(IP12_17_16, ETH_TX_EN), + PINMUX_IPSR_DATA(IP12_17_16, AVB_TXD3), + PINMUX_IPSR_MODSEL_DATA(IP12_17_16, TCLK1_B, SEL_TMU1_0), + PINMUX_IPSR_MODSEL_DATA(IP12_17_16, CAN_CLK_B, SEL_CANCLK_1), + PINMUX_IPSR_DATA(IP12_19_18, ETH_MAGIC), + PINMUX_IPSR_DATA(IP12_19_18, AVB_TXD4), + PINMUX_IPSR_MODSEL_DATA(IP12_19_18, IETX_C, SEL_IEB_2), + PINMUX_IPSR_DATA(IP12_21_20, ETH_TXD0), + PINMUX_IPSR_DATA(IP12_21_20, AVB_TXD5), + PINMUX_IPSR_MODSEL_DATA(IP12_21_20, IECLK_C, SEL_IEB_2), + PINMUX_IPSR_DATA(IP12_23_22, ETH_MDC), + PINMUX_IPSR_DATA(IP12_23_22, AVB_TXD6), + PINMUX_IPSR_MODSEL_DATA(IP12_23_22, IERX_C, SEL_IEB_2), + PINMUX_IPSR_MODSEL_DATA(IP12_26_24, STP_IVCXO27_0, SEL_SSP_0), + PINMUX_IPSR_DATA(IP12_26_24, AVB_TXD7), + PINMUX_IPSR_MODSEL_DATA(IP12_26_24, SCIFB2_TXD_D, SEL_SCIFB2_3), + PINMUX_IPSR_MODSEL_DATA(IP12_26_24, ADIDATA_B, SEL_RAD_1), + PINMUX_IPSR_MODSEL_DATA(IP12_26_24, MSIOF0_SYNC_C, SEL_SOF0_2), + PINMUX_IPSR_MODSEL_DATA(IP12_29_27, STP_ISCLK_0, SEL_SSP_0), + PINMUX_IPSR_DATA(IP12_29_27, AVB_TX_EN), + PINMUX_IPSR_MODSEL_DATA(IP12_29_27, SCIFB2_RXD_D, SEL_SCIFB2_3), + PINMUX_IPSR_MODSEL_DATA(IP12_29_27, ADICS_SAMP_B, SEL_RAD_1), + PINMUX_IPSR_MODSEL_DATA(IP12_29_27, MSIOF0_SCK_C, SEL_SOF0_2), + + /* IPSR13 */ + PINMUX_IPSR_MODSEL_DATA(IP13_2_0, STP_ISD_0, SEL_SSP_0), + PINMUX_IPSR_DATA(IP13_2_0, AVB_TX_ER), + PINMUX_IPSR_MODSEL_DATA(IP13_2_0, SCIFB2_SCK_C, SEL_SCIFB2_2), + PINMUX_IPSR_MODSEL_DATA(IP13_2_0, ADICLK_B, SEL_RAD_1), + PINMUX_IPSR_MODSEL_DATA(IP13_2_0, MSIOF0_SS1_C, SEL_SOF0_2), + PINMUX_IPSR_MODSEL_DATA(IP13_4_3, STP_ISEN_0, SEL_SSP_0), + PINMUX_IPSR_DATA(IP13_4_3, AVB_TX_CLK), + PINMUX_IPSR_MODSEL_DATA(IP13_4_3, ADICHS0_B, SEL_RAD_1), + PINMUX_IPSR_MODSEL_DATA(IP13_4_3, MSIOF0_SS2_C, SEL_SOF0_2), + PINMUX_IPSR_MODSEL_DATA(IP13_6_5, STP_ISSYNC_0, SEL_SSP_0), + PINMUX_IPSR_DATA(IP13_6_5, AVB_COL), + PINMUX_IPSR_MODSEL_DATA(IP13_6_5, ADICHS1_B, SEL_RAD_1), + PINMUX_IPSR_MODSEL_DATA(IP13_6_5, MSIOF0_RXD_C, SEL_SOF0_2), + PINMUX_IPSR_MODSEL_DATA(IP13_9_7, STP_OPWM_0, SEL_SSP_0), + PINMUX_IPSR_DATA(IP13_9_7, AVB_GTX_CLK), + PINMUX_IPSR_DATA(IP13_9_7, PWM0_B), + PINMUX_IPSR_MODSEL_DATA(IP13_9_7, ADICHS2_B, SEL_RAD_1), + PINMUX_IPSR_MODSEL_DATA(IP13_9_7, MSIOF0_TXD_C, SEL_SOF0_2), + PINMUX_IPSR_DATA(IP13_10, SD0_CLK), + PINMUX_IPSR_MODSEL_DATA(IP13_10, SPCLK_B, SEL_QSP_1), + PINMUX_IPSR_DATA(IP13_11, SD0_CMD), + PINMUX_IPSR_MODSEL_DATA(IP13_11, MOSI_IO0_B, SEL_QSP_1), + PINMUX_IPSR_DATA(IP13_12, SD0_DATA0), + PINMUX_IPSR_MODSEL_DATA(IP13_12, MISO_IO1_B, SEL_QSP_1), + PINMUX_IPSR_DATA(IP13_13, SD0_DATA1), + PINMUX_IPSR_MODSEL_DATA(IP13_13, IO2_B, SEL_QSP_1), + PINMUX_IPSR_DATA(IP13_14, SD0_DATA2), + PINMUX_IPSR_MODSEL_DATA(IP13_14, IO3_B, SEL_QSP_1), + PINMUX_IPSR_DATA(IP13_15, SD0_DATA3), + PINMUX_IPSR_MODSEL_DATA(IP13_15, SSL_B, SEL_QSP_1), + PINMUX_IPSR_DATA(IP13_18_16, SD0_CD), + PINMUX_IPSR_MODSEL_DATA(IP13_18_16, MMC_D6_B, SEL_MMC_1), + PINMUX_IPSR_MODSEL_DATA(IP13_18_16, SIM0_RST_B, SEL_SIM_1), + PINMUX_IPSR_MODSEL_DATA(IP13_18_16, CAN0_RX_F, SEL_CAN0_5), + PINMUX_IPSR_MODSEL_DATA(IP13_18_16, SCIFA5_TXD_B, SEL_SCIFA5_1), + PINMUX_IPSR_MODSEL_DATA(IP13_18_16, TX3_C, SEL_SCIF3_2), + PINMUX_IPSR_DATA(IP13_21_19, SD0_WP), + PINMUX_IPSR_MODSEL_DATA(IP13_21_19, MMC_D7_B, SEL_MMC_1), + PINMUX_IPSR_MODSEL_DATA(IP13_21_19, SIM0_D_B, SEL_SIM_1), + PINMUX_IPSR_MODSEL_DATA(IP13_21_19, CAN0_TX_F, SEL_CAN0_5), + PINMUX_IPSR_MODSEL_DATA(IP13_21_19, SCIFA5_RXD_B, SEL_SCIFA5_1), + PINMUX_IPSR_MODSEL_DATA(IP13_21_19, RX3_C, SEL_SCIF3_2), + PINMUX_IPSR_DATA(IP13_22, SD1_CMD), + PINMUX_IPSR_MODSEL_DATA(IP13_22, REMOCON_B, SEL_RCN_1), + PINMUX_IPSR_DATA(IP13_24_23, SD1_DATA0), + PINMUX_IPSR_MODSEL_DATA(IP13_24_23, SPEEDIN_B, SEL_RSP_1), + PINMUX_IPSR_DATA(IP13_25, SD1_DATA1), + PINMUX_IPSR_MODSEL_DATA(IP13_25, IETX_B, SEL_IEB_1), + PINMUX_IPSR_DATA(IP13_26, SD1_DATA2), + PINMUX_IPSR_MODSEL_DATA(IP13_26, IECLK_B, SEL_IEB_1), + PINMUX_IPSR_DATA(IP13_27, SD1_DATA3), + PINMUX_IPSR_MODSEL_DATA(IP13_27, IERX_B, SEL_IEB_1), + PINMUX_IPSR_DATA(IP13_30_28, SD1_CD), + PINMUX_IPSR_DATA(IP13_30_28, PWM0), + PINMUX_IPSR_DATA(IP13_30_28, TPU_TO0), + PINMUX_IPSR_MODSEL_DATA(IP13_30_28, SCL1_C, SEL_IIC1_2), + + /* IPSR14 */ + PINMUX_IPSR_DATA(IP14_1_0, SD1_WP), + PINMUX_IPSR_DATA(IP14_1_0, PWM1_B), + PINMUX_IPSR_MODSEL_DATA(IP14_1_0, SDA1_C, SEL_IIC1_2), + PINMUX_IPSR_DATA(IP14_2, SD2_CLK), + PINMUX_IPSR_DATA(IP14_2, MMC_CLK), + PINMUX_IPSR_DATA(IP14_3, SD2_CMD), + PINMUX_IPSR_DATA(IP14_3, MMC_CMD), + PINMUX_IPSR_DATA(IP14_4, SD2_DATA0), + PINMUX_IPSR_DATA(IP14_4, MMC_D0), + PINMUX_IPSR_DATA(IP14_5, SD2_DATA1), + PINMUX_IPSR_DATA(IP14_5, MMC_D1), + PINMUX_IPSR_DATA(IP14_6, SD2_DATA2), + PINMUX_IPSR_DATA(IP14_6, MMC_D2), + PINMUX_IPSR_DATA(IP14_7, SD2_DATA3), + PINMUX_IPSR_DATA(IP14_7, MMC_D3), + PINMUX_IPSR_DATA(IP14_10_8, SD2_CD), + PINMUX_IPSR_DATA(IP14_10_8, MMC_D4), + PINMUX_IPSR_MODSEL_DATA(IP14_10_8, SCL8_C, SEL_IIC8_2), + PINMUX_IPSR_MODSEL_DATA(IP14_10_8, TX5_B, SEL_SCIF5_1), + PINMUX_IPSR_MODSEL_DATA(IP14_10_8, SCIFA5_TXD_C, SEL_SCIFA5_2), + PINMUX_IPSR_DATA(IP14_13_11, SD2_WP), + PINMUX_IPSR_DATA(IP14_13_11, MMC_D5), + PINMUX_IPSR_MODSEL_DATA(IP14_13_11, SDA8_C, SEL_IIC8_2), + PINMUX_IPSR_MODSEL_DATA(IP14_13_11, RX5_B, SEL_SCIF5_1), + PINMUX_IPSR_MODSEL_DATA(IP14_13_11, SCIFA5_RXD_C, SEL_SCIFA5_2), + PINMUX_IPSR_MODSEL_DATA(IP14_16_14, MSIOF0_SCK, SEL_SOF0_0), + PINMUX_IPSR_MODSEL_DATA(IP14_16_14, RX2_C, SEL_SCIF2_2), + PINMUX_IPSR_MODSEL_DATA(IP14_16_14, ADIDATA, SEL_RAD_0), + PINMUX_IPSR_MODSEL_DATA(IP14_16_14, VI1_CLK_C, SEL_VI1_2), + PINMUX_IPSR_DATA(IP14_16_14, VI1_G0_B), + PINMUX_IPSR_MODSEL_DATA(IP14_19_17, MSIOF0_SYNC, SEL_SOF0_0), + PINMUX_IPSR_MODSEL_DATA(IP14_19_17, TX2_C, SEL_SCIF2_2), + PINMUX_IPSR_MODSEL_DATA(IP14_19_17, ADICS_SAMP, SEL_RAD_0), + PINMUX_IPSR_MODSEL_DATA(IP14_19_17, VI1_CLKENB_C, SEL_VI1_2), + PINMUX_IPSR_DATA(IP14_19_17, VI1_G1_B), + PINMUX_IPSR_MODSEL_DATA(IP14_22_20, MSIOF0_TXD, SEL_SOF0_0), + PINMUX_IPSR_MODSEL_DATA(IP14_22_20, ADICLK, SEL_RAD_0), + PINMUX_IPSR_MODSEL_DATA(IP14_22_20, VI1_FIELD_C, SEL_VI1_2), + PINMUX_IPSR_DATA(IP14_22_20, VI1_G2_B), + PINMUX_IPSR_MODSEL_DATA(IP14_25_23, MSIOF0_RXD, SEL_SOF0_0), + PINMUX_IPSR_MODSEL_DATA(IP14_25_23, ADICHS0, SEL_RAD_0), + PINMUX_IPSR_MODSEL_DATA(IP14_25_23, VI1_DATA0_C, SEL_VI1_2), + PINMUX_IPSR_DATA(IP14_25_23, VI1_G3_B), + PINMUX_IPSR_MODSEL_DATA(IP14_28_26, MSIOF0_SS1, SEL_SOF0_0), + PINMUX_IPSR_MODSEL_DATA(IP14_28_26, MMC_D6, SEL_MMC_0), + PINMUX_IPSR_MODSEL_DATA(IP14_28_26, ADICHS1, SEL_RAD_0), + PINMUX_IPSR_MODSEL_DATA(IP14_28_26, TX0_E, SEL_SCIF0_4), + PINMUX_IPSR_MODSEL_DATA(IP14_28_26, VI1_HSYNC_N_C, SEL_VI1_2), + PINMUX_IPSR_MODSEL_DATA(IP14_28_26, SCL7_C, SEL_IIC7_2), + PINMUX_IPSR_DATA(IP14_28_26, VI1_G4_B), + PINMUX_IPSR_MODSEL_DATA(IP14_31_29, MSIOF0_SS2, SEL_SOF0_0), + PINMUX_IPSR_MODSEL_DATA(IP14_31_29, MMC_D7, SEL_MMC_0), + PINMUX_IPSR_MODSEL_DATA(IP14_31_29, ADICHS2, SEL_RAD_0), + PINMUX_IPSR_MODSEL_DATA(IP14_31_29, RX0_E, SEL_SCIF0_4), + PINMUX_IPSR_MODSEL_DATA(IP14_31_29, VI1_VSYNC_N_C, SEL_VI1_2), + PINMUX_IPSR_MODSEL_DATA(IP14_31_29, SDA7_C, SEL_IIC7_2), + PINMUX_IPSR_DATA(IP14_31_29, VI1_G5_B), + + /* IPSR15 */ + PINMUX_IPSR_MODSEL_DATA(IP15_1_0, SIM0_RST, SEL_SIM_0), + PINMUX_IPSR_MODSEL_DATA(IP15_1_0, IETX, SEL_IEB_0), + PINMUX_IPSR_MODSEL_DATA(IP15_1_0, CAN1_TX_D, SEL_CAN1_3), + PINMUX_IPSR_DATA(IP15_3_2, SIM0_CLK), + PINMUX_IPSR_MODSEL_DATA(IP15_3_2, IECLK, SEL_IEB_0), + PINMUX_IPSR_MODSEL_DATA(IP15_3_2, CAN_CLK_C, SEL_CANCLK_2), + PINMUX_IPSR_MODSEL_DATA(IP15_5_4, SIM0_D, SEL_SIM_0), + PINMUX_IPSR_MODSEL_DATA(IP15_5_4, IERX, SEL_IEB_0), + PINMUX_IPSR_MODSEL_DATA(IP15_5_4, CAN1_RX_D, SEL_CAN1_3), + PINMUX_IPSR_MODSEL_DATA(IP15_8_6, GPS_CLK, SEL_GPS_0), + PINMUX_IPSR_MODSEL_DATA(IP15_8_6, DU1_DOTCLKIN_C, SEL_DIS_2), + PINMUX_IPSR_MODSEL_DATA(IP15_8_6, AUDIO_CLKB_B, SEL_ADG_1), + PINMUX_IPSR_DATA(IP15_8_6, PWM5_B), + PINMUX_IPSR_MODSEL_DATA(IP15_8_6, SCIFA3_TXD_C, SEL_SCIFA3_2), + PINMUX_IPSR_MODSEL_DATA(IP15_11_9, GPS_SIGN, SEL_GPS_0), + PINMUX_IPSR_MODSEL_DATA(IP15_11_9, TX4_C, SEL_SCIF4_2), + PINMUX_IPSR_MODSEL_DATA(IP15_11_9, SCIFA4_TXD_C, SEL_SCIFA4_2), + PINMUX_IPSR_DATA(IP15_11_9, PWM5), + PINMUX_IPSR_DATA(IP15_11_9, VI1_G6_B), + PINMUX_IPSR_MODSEL_DATA(IP15_11_9, SCIFA3_RXD_C, SEL_SCIFA3_2), + PINMUX_IPSR_MODSEL_DATA(IP15_14_12, GPS_MAG, SEL_GPS_0), + PINMUX_IPSR_MODSEL_DATA(IP15_14_12, RX4_C, SEL_SCIF4_2), + PINMUX_IPSR_MODSEL_DATA(IP15_14_12, SCIFA4_RXD_C, SEL_SCIFA4_2), + PINMUX_IPSR_DATA(IP15_14_12, PWM6), + PINMUX_IPSR_DATA(IP15_14_12, VI1_G7_B), + PINMUX_IPSR_MODSEL_DATA(IP15_14_12, SCIFA3_SCK_C, SEL_SCIFA3_2), + PINMUX_IPSR_MODSEL_DATA(IP15_17_15, HCTS0_N, SEL_HSCIF0_0), + PINMUX_IPSR_MODSEL_DATA(IP15_17_15, SCIFB0_CTS_N, SEL_SCIFB_0), + PINMUX_IPSR_MODSEL_DATA(IP15_17_15, GLO_I0_C, SEL_GPS_2), + PINMUX_IPSR_MODSEL_DATA(IP15_17_15, TCLK1, SEL_TMU1_0), + PINMUX_IPSR_MODSEL_DATA(IP15_17_15, VI1_DATA1_C, SEL_VI1_2), + PINMUX_IPSR_MODSEL_DATA(IP15_20_18, HRTS0_N, SEL_HSCIF0_0), + PINMUX_IPSR_MODSEL_DATA(IP15_20_18, SCIFB0_RTS_N, SEL_SCIFB_0), + PINMUX_IPSR_MODSEL_DATA(IP15_20_18, GLO_I1_C, SEL_GPS_2), + PINMUX_IPSR_MODSEL_DATA(IP15_20_18, VI1_DATA2_C, SEL_VI1_2), + PINMUX_IPSR_MODSEL_DATA(IP15_23_21, HSCK0, SEL_HSCIF0_0), + PINMUX_IPSR_MODSEL_DATA(IP15_23_21, SCIFB0_SCK, SEL_SCIFB_0), + PINMUX_IPSR_MODSEL_DATA(IP15_23_21, GLO_Q0_C, SEL_GPS_2), + PINMUX_IPSR_MODSEL_DATA(IP15_23_21, CAN_CLK, SEL_CANCLK_0), + PINMUX_IPSR_DATA(IP15_23_21, TCLK2), + PINMUX_IPSR_MODSEL_DATA(IP15_23_21, VI1_DATA3_C, SEL_VI1_2), + PINMUX_IPSR_MODSEL_DATA(IP15_26_24, HRX0, SEL_HSCIF0_0), + PINMUX_IPSR_MODSEL_DATA(IP15_26_24, SCIFB0_RXD, SEL_SCIFB_0), + PINMUX_IPSR_MODSEL_DATA(IP15_26_24, GLO_Q1_C, SEL_GPS_2), + PINMUX_IPSR_MODSEL_DATA(IP15_26_24, CAN0_RX_B, SEL_CAN0_1), + PINMUX_IPSR_MODSEL_DATA(IP15_26_24, VI1_DATA4_C, SEL_VI1_2), + PINMUX_IPSR_MODSEL_DATA(IP15_29_27, HTX0, SEL_HSCIF0_0), + PINMUX_IPSR_MODSEL_DATA(IP15_29_27, SCIFB0_TXD, SEL_SCIFB_0), + PINMUX_IPSR_MODSEL_DATA(IP15_29_27, GLO_SCLK_C, SEL_GPS_2), + PINMUX_IPSR_MODSEL_DATA(IP15_29_27, CAN0_TX_B, SEL_CAN0_1), + PINMUX_IPSR_MODSEL_DATA(IP15_29_27, VI1_DATA5_C, SEL_VI1_2), + + /* IPSR16 */ + PINMUX_IPSR_MODSEL_DATA(IP16_2_0, HRX1, SEL_HSCIF1_0), + PINMUX_IPSR_MODSEL_DATA(IP16_2_0, SCIFB1_RXD, SEL_SCIFB1_0), + PINMUX_IPSR_DATA(IP16_2_0, VI1_R0_B), + PINMUX_IPSR_MODSEL_DATA(IP16_2_0, GLO_SDATA_C, SEL_GPS_2), + PINMUX_IPSR_MODSEL_DATA(IP16_2_0, VI1_DATA6_C, SEL_VI1_2), + PINMUX_IPSR_MODSEL_DATA(IP16_5_3, HTX1, SEL_HSCIF1_0), + PINMUX_IPSR_MODSEL_DATA(IP16_5_3, SCIFB1_TXD, SEL_SCIFB1_0), + PINMUX_IPSR_DATA(IP16_5_3, VI1_R1_B), + PINMUX_IPSR_MODSEL_DATA(IP16_5_3, GLO_SS_C, SEL_GPS_2), + PINMUX_IPSR_MODSEL_DATA(IP16_5_3, VI1_DATA7_C, SEL_VI1_2), + PINMUX_IPSR_MODSEL_DATA(IP16_7_6, HSCK1, SEL_HSCIF1_0), + PINMUX_IPSR_MODSEL_DATA(IP16_7_6, SCIFB1_SCK, SEL_SCIFB1_0), + PINMUX_IPSR_DATA(IP16_7_6, MLB_CK), + PINMUX_IPSR_MODSEL_DATA(IP16_7_6, GLO_RFON_C, SEL_GPS_2), + PINMUX_IPSR_MODSEL_DATA(IP16_9_8, HCTS1_N, SEL_HSCIF1_0), + PINMUX_IPSR_DATA(IP16_9_8, SCIFB1_CTS_N), + PINMUX_IPSR_DATA(IP16_9_8, MLB_SIG), + PINMUX_IPSR_MODSEL_DATA(IP16_9_8, CAN1_TX_B, SEL_CAN1_1), + PINMUX_IPSR_MODSEL_DATA(IP16_11_10, HRTS1_N, SEL_HSCIF1_0), + PINMUX_IPSR_DATA(IP16_11_10, SCIFB1_RTS_N), + PINMUX_IPSR_DATA(IP16_11_10, MLB_DAT), + PINMUX_IPSR_MODSEL_DATA(IP16_11_10, CAN1_RX_B, SEL_CAN1_1), +}; + +static struct sh_pfc_pin pinmux_pins[] = { + PINMUX_GPIO_GP_ALL(), +}; + +/* - DU --------------------------------------------------------------------- */ +static const unsigned int du_rgb666_pins[] = { + /* R[7:2], G[7:2], B[7:2] */ + RCAR_GP_PIN(3, 7), RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 5), + RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 3), RCAR_GP_PIN(3, 2), + RCAR_GP_PIN(3, 15), RCAR_GP_PIN(3, 14), RCAR_GP_PIN(3, 13), + RCAR_GP_PIN(3, 12), RCAR_GP_PIN(3, 11), RCAR_GP_PIN(3, 10), + RCAR_GP_PIN(3, 23), RCAR_GP_PIN(3, 22), RCAR_GP_PIN(3, 21), + RCAR_GP_PIN(3, 20), RCAR_GP_PIN(3, 19), RCAR_GP_PIN(3, 18), +}; +static const unsigned int du_rgb666_mux[] = { + DU1_DR7_MARK, DU1_DR6_MARK, DU1_DR5_MARK, DU1_DR4_MARK, + DU1_DR3_MARK, DU1_DR2_MARK, + DU1_DG7_MARK, DU1_DG6_MARK, DU1_DG5_MARK, DU1_DG4_MARK, + DU1_DG3_MARK, DU1_DG2_MARK, + DU1_DB7_MARK, DU1_DB6_MARK, DU1_DB5_MARK, DU1_DB4_MARK, + DU1_DB3_MARK, DU1_DB2_MARK, +}; +static const unsigned int du_rgb888_pins[] = { + /* R[7:0], G[7:0], B[7:0] */ + RCAR_GP_PIN(3, 7), RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 5), + RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 3), RCAR_GP_PIN(3, 2), + RCAR_GP_PIN(3, 1), RCAR_GP_PIN(3, 0), + RCAR_GP_PIN(3, 15), RCAR_GP_PIN(3, 14), RCAR_GP_PIN(3, 13), + RCAR_GP_PIN(3, 12), RCAR_GP_PIN(3, 11), RCAR_GP_PIN(3, 10), + RCAR_GP_PIN(3, 9), RCAR_GP_PIN(3, 8), + RCAR_GP_PIN(3, 23), RCAR_GP_PIN(3, 22), RCAR_GP_PIN(3, 21), + RCAR_GP_PIN(3, 20), RCAR_GP_PIN(3, 19), RCAR_GP_PIN(3, 18), + RCAR_GP_PIN(3, 17), RCAR_GP_PIN(3, 16), +}; +static const unsigned int du_rgb888_mux[] = { + DU1_DR7_MARK, DU1_DR6_MARK, DU1_DR5_MARK, DU1_DR4_MARK, + DU1_DR3_MARK, DU1_DR2_MARK, DU1_DR1_MARK, DU1_DR0_MARK, + DU1_DG7_MARK, DU1_DG6_MARK, DU1_DG5_MARK, DU1_DG4_MARK, + DU1_DG3_MARK, DU1_DG2_MARK, DU1_DG1_MARK, DU1_DG0_MARK, + DU1_DB7_MARK, DU1_DB6_MARK, DU1_DB5_MARK, DU1_DB4_MARK, + DU1_DB3_MARK, DU1_DB2_MARK, DU1_DB1_MARK, DU1_DB0_MARK, +}; +static const unsigned int du_clk_out_0_pins[] = { + /* CLKOUT */ + RCAR_GP_PIN(3, 25), +}; +static const unsigned int du_clk_out_0_mux[] = { + DU1_DOTCLKOUT0_MARK +}; +static const unsigned int du_clk_out_1_pins[] = { + /* CLKOUT */ + RCAR_GP_PIN(3, 26), +}; +static const unsigned int du_clk_out_1_mux[] = { + DU1_DOTCLKOUT1_MARK +}; +static const unsigned int du_sync_1_pins[] = { + /* EXVSYNC/VSYNC, EXHSYNC/HSYNC, EXDISP/EXODDF/EXCDE */ + RCAR_GP_PIN(3, 29), RCAR_GP_PIN(3, 28), RCAR_GP_PIN(3, 27), +}; +static const unsigned int du_sync_1_mux[] = { + DU1_EXODDF_DU1_ODDF_DISP_CDE_MARK, + DU1_EXVSYNC_DU1_VSYNC_MARK, DU1_EXHSYNC_DU1_HSYNC_MARK +}; +static const unsigned int du_cde_disp_pins[] = { + /* CDE DISP */ + RCAR_GP_PIN(3, 31), RCAR_GP_PIN(3, 30), +}; +static const unsigned int du0_clk_in_pins[] = { + /* CLKIN */ + RCAR_GP_PIN(6, 31), +}; +static const unsigned int du0_clk_in_mux[] = { + DU0_DOTCLKIN_MARK +}; +static const unsigned int du_cde_disp_mux[] = { + DU1_CDE_MARK, DU1_DISP_MARK +}; +static const unsigned int du1_clk_in_pins[] = { + /* CLKIN */ + RCAR_GP_PIN(7, 20), RCAR_GP_PIN(7, 19), RCAR_GP_PIN(3, 24), +}; +static const unsigned int du1_clk_in_mux[] = { + DU1_DOTCLKIN_C_MARK, DU1_DOTCLKIN_B_MARK, DU1_DOTCLKIN_MARK +}; +/* - ETH -------------------------------------------------------------------- */ +static const unsigned int eth_link_pins[] = { + /* LINK */ + RCAR_GP_PIN(5, 18), +}; +static const unsigned int eth_link_mux[] = { + ETH_LINK_MARK, +}; +static const unsigned int eth_magic_pins[] = { + /* MAGIC */ + RCAR_GP_PIN(5, 22), +}; +static const unsigned int eth_magic_mux[] = { + ETH_MAGIC_MARK, +}; +static const unsigned int eth_mdio_pins[] = { + /* MDC, MDIO */ + RCAR_GP_PIN(5, 24), RCAR_GP_PIN(5, 13), +}; +static const unsigned int eth_mdio_mux[] = { + ETH_MDC_MARK, ETH_MDIO_MARK, +}; +static const unsigned int eth_rmii_pins[] = { + /* RXD[0:1], RX_ER, CRS_DV, TXD[0:1], TX_EN, REF_CLK */ + RCAR_GP_PIN(5, 16), RCAR_GP_PIN(5, 17), RCAR_GP_PIN(5, 15), + RCAR_GP_PIN(5, 14), RCAR_GP_PIN(5, 23), RCAR_GP_PIN(5, 20), + RCAR_GP_PIN(5, 21), RCAR_GP_PIN(5, 19), +}; +static const unsigned int eth_rmii_mux[] = { + ETH_RXD0_MARK, ETH_RXD1_MARK, ETH_RX_ER_MARK, ETH_CRS_DV_MARK, + ETH_TXD0_MARK, ETH_TXD1_MARK, ETH_TX_EN_MARK, ETH_REFCLK_MARK, +}; +/* - INTC ------------------------------------------------------------------- */ +static const unsigned int intc_irq0_pins[] = { + /* IRQ */ + RCAR_GP_PIN(7, 10), +}; +static const unsigned int intc_irq0_mux[] = { + IRQ0_MARK, +}; +static const unsigned int intc_irq1_pins[] = { + /* IRQ */ + RCAR_GP_PIN(7, 11), +}; +static const unsigned int intc_irq1_mux[] = { + IRQ1_MARK, +}; +static const unsigned int intc_irq2_pins[] = { + /* IRQ */ + RCAR_GP_PIN(7, 12), +}; +static const unsigned int intc_irq2_mux[] = { + IRQ2_MARK, +}; +static const unsigned int intc_irq3_pins[] = { + /* IRQ */ + RCAR_GP_PIN(7, 13), +}; +static const unsigned int intc_irq3_mux[] = { + IRQ3_MARK, +}; +/* - MMCIF ------------------------------------------------------------------ */ +static const unsigned int mmc_data1_pins[] = { + /* D[0] */ + RCAR_GP_PIN(6, 18), +}; +static const unsigned int mmc_data1_mux[] = { + MMC_D0_MARK, +}; +static const unsigned int mmc_data4_pins[] = { + /* D[0:3] */ + RCAR_GP_PIN(6, 18), RCAR_GP_PIN(6, 19), + RCAR_GP_PIN(6, 20), RCAR_GP_PIN(6, 21), +}; +static const unsigned int mmc_data4_mux[] = { + MMC_D0_MARK, MMC_D1_MARK, MMC_D2_MARK, MMC_D3_MARK, +}; +static const unsigned int mmc_data8_pins[] = { + /* D[0:7] */ + RCAR_GP_PIN(6, 18), RCAR_GP_PIN(6, 19), + RCAR_GP_PIN(6, 20), RCAR_GP_PIN(6, 21), + RCAR_GP_PIN(6, 22), RCAR_GP_PIN(6, 23), + RCAR_GP_PIN(6, 28), RCAR_GP_PIN(6, 29), +}; +static const unsigned int mmc_data8_mux[] = { + MMC_D0_MARK, MMC_D1_MARK, MMC_D2_MARK, MMC_D3_MARK, + MMC_D4_MARK, MMC_D5_MARK, MMC_D6_MARK, MMC_D7_MARK, +}; +static const unsigned int mmc_ctrl_pins[] = { + /* CLK, CMD */ + RCAR_GP_PIN(6, 16), RCAR_GP_PIN(6, 17), +}; +static const unsigned int mmc_ctrl_mux[] = { + MMC_CLK_MARK, MMC_CMD_MARK, +}; +/* - MSIOF0 ----------------------------------------------------------------- */ +static const unsigned int msiof0_clk_pins[] = { + /* SCK */ + RCAR_GP_PIN(6, 24), +}; +static const unsigned int msiof0_clk_mux[] = { + MSIOF0_SCK_MARK, +}; +static const unsigned int msiof0_sync_pins[] = { + /* SYNC */ + RCAR_GP_PIN(6, 25), +}; +static const unsigned int msiof0_sync_mux[] = { + MSIOF0_SYNC_MARK, +}; +static const unsigned int msiof0_ss1_pins[] = { + /* SS1 */ + RCAR_GP_PIN(6, 28), +}; +static const unsigned int msiof0_ss1_mux[] = { + MSIOF0_SS1_MARK, +}; +static const unsigned int msiof0_ss2_pins[] = { + /* SS2 */ + RCAR_GP_PIN(6, 29), +}; +static const unsigned int msiof0_ss2_mux[] = { + MSIOF0_SS2_MARK, +}; +static const unsigned int msiof0_rx_pins[] = { + /* RXD */ + RCAR_GP_PIN(6, 27), +}; +static const unsigned int msiof0_rx_mux[] = { + MSIOF0_RXD_MARK, +}; +static const unsigned int msiof0_tx_pins[] = { + /* TXD */ + RCAR_GP_PIN(6, 26), +}; +static const unsigned int msiof0_tx_mux[] = { + MSIOF0_TXD_MARK, +}; +/* - MSIOF1 ----------------------------------------------------------------- */ +static const unsigned int msiof1_clk_pins[] = { + /* SCK */ + RCAR_GP_PIN(0, 22), +}; +static const unsigned int msiof1_clk_mux[] = { + MSIOF1_SCK_MARK, +}; +static const unsigned int msiof1_sync_pins[] = { + /* SYNC */ + RCAR_GP_PIN(0, 23), +}; +static const unsigned int msiof1_sync_mux[] = { + MSIOF1_SYNC_MARK, +}; +static const unsigned int msiof1_ss1_pins[] = { + /* SS1 */ + RCAR_GP_PIN(0, 24), +}; +static const unsigned int msiof1_ss1_mux[] = { + MSIOF1_SS1_MARK, +}; +static const unsigned int msiof1_ss2_pins[] = { + /* SS2 */ + RCAR_GP_PIN(0, 25), +}; +static const unsigned int msiof1_ss2_mux[] = { + MSIOF1_SS2_MARK, +}; +static const unsigned int msiof1_rx_pins[] = { + /* RXD */ + RCAR_GP_PIN(0, 27), +}; +static const unsigned int msiof1_rx_mux[] = { + MSIOF1_RXD_MARK, +}; +static const unsigned int msiof1_tx_pins[] = { + /* TXD */ + RCAR_GP_PIN(0, 26), +}; +static const unsigned int msiof1_tx_mux[] = { + MSIOF1_TXD_MARK, +}; +/* - MSIOF2 ----------------------------------------------------------------- */ +static const unsigned int msiof2_clk_pins[] = { + /* SCK */ + RCAR_GP_PIN(1, 13), +}; +static const unsigned int msiof2_clk_mux[] = { + MSIOF2_SCK_MARK, +}; +static const unsigned int msiof2_sync_pins[] = { + /* SYNC */ + RCAR_GP_PIN(1, 14), +}; +static const unsigned int msiof2_sync_mux[] = { + MSIOF2_SYNC_MARK, +}; +static const unsigned int msiof2_ss1_pins[] = { + /* SS1 */ + RCAR_GP_PIN(1, 17), +}; +static const unsigned int msiof2_ss1_mux[] = { + MSIOF2_SS1_MARK, +}; +static const unsigned int msiof2_ss2_pins[] = { + /* SS2 */ + RCAR_GP_PIN(1, 18), +}; +static const unsigned int msiof2_ss2_mux[] = { + MSIOF2_SS2_MARK, +}; +static const unsigned int msiof2_rx_pins[] = { + /* RXD */ + RCAR_GP_PIN(1, 16), +}; +static const unsigned int msiof2_rx_mux[] = { + MSIOF2_RXD_MARK, +}; +static const unsigned int msiof2_tx_pins[] = { + /* TXD */ + RCAR_GP_PIN(1, 15), +}; +static const unsigned int msiof2_tx_mux[] = { + MSIOF2_TXD_MARK, +}; +/* - SCIF0 ------------------------------------------------------------------ */ +static const unsigned int scif0_data_pins[] = { + /* RX, TX */ + RCAR_GP_PIN(1, 7), RCAR_GP_PIN(1, 6), +}; +static const unsigned int scif0_data_mux[] = { + RX0_MARK, TX0_MARK, +}; +static const unsigned int scif0_data_b_pins[] = { + /* RX, TX */ + RCAR_GP_PIN(3, 1), RCAR_GP_PIN(3, 0), +}; +static const unsigned int scif0_data_b_mux[] = { + RX0_B_MARK, TX0_B_MARK, +}; +static const unsigned int scif0_data_c_pins[] = { + /* RX, TX */ + RCAR_GP_PIN(4, 26), RCAR_GP_PIN(4, 25), +}; +static const unsigned int scif0_data_c_mux[] = { + RX0_C_MARK, TX0_C_MARK, +}; +static const unsigned int scif0_data_d_pins[] = { + /* RX, TX */ + RCAR_GP_PIN(2, 23), RCAR_GP_PIN(2, 22), +}; +static const unsigned int scif0_data_d_mux[] = { + RX0_D_MARK, TX0_D_MARK, +}; +static const unsigned int scif0_data_e_pins[] = { + /* RX, TX */ + RCAR_GP_PIN(6, 29), RCAR_GP_PIN(6, 28), +}; +static const unsigned int scif0_data_e_mux[] = { + RX0_E_MARK, TX0_E_MARK, +}; +/* - SCIF1 ------------------------------------------------------------------ */ +static const unsigned int scif1_data_pins[] = { + /* RX, TX */ + RCAR_GP_PIN(1, 9), RCAR_GP_PIN(1, 8), +}; +static const unsigned int scif1_data_mux[] = { + RX1_MARK, TX1_MARK, +}; +static const unsigned int scif1_data_b_pins[] = { + /* RX, TX */ + RCAR_GP_PIN(3, 9), RCAR_GP_PIN(3, 8), +}; +static const unsigned int scif1_data_b_mux[] = { + RX1_B_MARK, TX1_B_MARK, +}; +static const unsigned int scif1_clk_b_pins[] = { + /* SCK */ + RCAR_GP_PIN(3, 10), +}; +static const unsigned int scif1_clk_b_mux[] = { + SCIF1_SCK_B_MARK, +}; +static const unsigned int scif1_data_c_pins[] = { + /* RX, TX */ + RCAR_GP_PIN(4, 28), RCAR_GP_PIN(4, 27), +}; +static const unsigned int scif1_data_c_mux[] = { + RX1_C_MARK, TX1_C_MARK, +}; +static const unsigned int scif1_data_d_pins[] = { + /* RX, TX */ + RCAR_GP_PIN(2, 25), RCAR_GP_PIN(2, 24), +}; +static const unsigned int scif1_data_d_mux[] = { + RX1_D_MARK, TX1_D_MARK, +}; +/* - SCIF2 ------------------------------------------------------------------ */ +static const unsigned int scif2_data_pins[] = { + /* RX, TX */ + RCAR_GP_PIN(2, 30), RCAR_GP_PIN(2, 31), +}; +static const unsigned int scif2_data_mux[] = { + RX2_MARK, TX2_MARK, +}; +static const unsigned int scif2_data_b_pins[] = { + /* RX, TX */ + RCAR_GP_PIN(3, 17), RCAR_GP_PIN(3, 16), +}; +static const unsigned int scif2_data_b_mux[] = { + RX2_B_MARK, TX2_B_MARK, +}; +static const unsigned int scif2_clk_b_pins[] = { + /* SCK */ + RCAR_GP_PIN(3, 18), +}; +static const unsigned int scif2_clk_b_mux[] = { + SCIF2_SCK_B_MARK, +}; +static const unsigned int scif2_data_c_pins[] = { + /* RX, TX */ + RCAR_GP_PIN(6, 24), RCAR_GP_PIN(6, 25), +}; +static const unsigned int scif2_data_c_mux[] = { + RX2_C_MARK, TX2_C_MARK, +}; +static const unsigned int scif2_data_e_pins[] = { + /* RX, TX */ + RCAR_GP_PIN(2, 7), RCAR_GP_PIN(2, 8), +}; +static const unsigned int scif2_data_e_mux[] = { + RX2_E_MARK, TX2_E_MARK, +}; +/* - SCIF3 ------------------------------------------------------------------ */ +static const unsigned int scif3_data_pins[] = { + /* RX, TX */ + RCAR_GP_PIN(3, 22), RCAR_GP_PIN(3, 21), +}; +static const unsigned int scif3_data_mux[] = { + RX3_MARK, TX3_MARK, +}; +static const unsigned int scif3_clk_pins[] = { + /* SCK */ + RCAR_GP_PIN(3, 23), +}; +static const unsigned int scif3_clk_mux[] = { + SCIF3_SCK_MARK, +}; +static const unsigned int scif3_data_b_pins[] = { + /* RX, TX */ + RCAR_GP_PIN(3, 29), RCAR_GP_PIN(3, 26), +}; +static const unsigned int scif3_data_b_mux[] = { + RX3_B_MARK, TX3_B_MARK, +}; +static const unsigned int scif3_clk_b_pins[] = { + /* SCK */ + RCAR_GP_PIN(4, 8), +}; +static const unsigned int scif3_clk_b_mux[] = { + SCIF3_SCK_B_MARK, +}; +static const unsigned int scif3_data_c_pins[] = { + /* RX, TX */ + RCAR_GP_PIN(6, 7), RCAR_GP_PIN(6, 6), +}; +static const unsigned int scif3_data_c_mux[] = { + RX3_C_MARK, TX3_C_MARK, +}; +static const unsigned int scif3_data_d_pins[] = { + /* RX, TX */ + RCAR_GP_PIN(2, 27), RCAR_GP_PIN(2, 26), +}; +static const unsigned int scif3_data_d_mux[] = { + RX3_D_MARK, TX3_D_MARK, +}; +/* - SCIF4 ------------------------------------------------------------------ */ +static const unsigned int scif4_data_pins[] = { + /* RX, TX */ + RCAR_GP_PIN(4, 2), RCAR_GP_PIN(4, 1), +}; +static const unsigned int scif4_data_mux[] = { + RX4_MARK, TX4_MARK, +}; +static const unsigned int scif4_data_b_pins[] = { + /* RX, TX */ + RCAR_GP_PIN(5, 1), RCAR_GP_PIN(5, 0), +}; +static const unsigned int scif4_data_b_mux[] = { + RX4_B_MARK, TX4_B_MARK, +}; +static const unsigned int scif4_data_c_pins[] = { + /* RX, TX */ + RCAR_GP_PIN(7, 22), RCAR_GP_PIN(7, 21), +}; +static const unsigned int scif4_data_c_mux[] = { + RX4_C_MARK, TX4_C_MARK, +}; +/* - SCIF5 ------------------------------------------------------------------ */ +static const unsigned int scif5_data_pins[] = { + /* RX, TX */ + RCAR_GP_PIN(4, 4), RCAR_GP_PIN(4, 3), +}; +static const unsigned int scif5_data_mux[] = { + RX5_MARK, TX5_MARK, +}; +static const unsigned int scif5_data_b_pins[] = { + /* RX, TX */ + RCAR_GP_PIN(6, 23), RCAR_GP_PIN(6, 22), +}; +static const unsigned int scif5_data_b_mux[] = { + RX5_B_MARK, TX5_B_MARK, +}; +/* - SCIFA0 ----------------------------------------------------------------- */ +static const unsigned int scifa0_data_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(1, 7), RCAR_GP_PIN(1, 6), +}; +static const unsigned int scifa0_data_mux[] = { + SCIFA0_RXD_MARK, SCIFA0_TXD_MARK, +}; +static const unsigned int scifa0_data_b_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(3, 1), RCAR_GP_PIN(3, 0), +}; +static const unsigned int scifa0_data_b_mux[] = { + SCIFA0_RXD_B_MARK, SCIFA0_TXD_B_MARK +}; +/* - SCIFA1 ----------------------------------------------------------------- */ +static const unsigned int scifa1_data_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(1, 9), RCAR_GP_PIN(1, 8), +}; +static const unsigned int scifa1_data_mux[] = { + SCIFA1_RXD_MARK, SCIFA1_TXD_MARK, +}; +static const unsigned int scifa1_clk_pins[] = { + /* SCK */ + RCAR_GP_PIN(3, 10), +}; +static const unsigned int scifa1_clk_mux[] = { + SCIFA1_SCK_MARK, +}; +static const unsigned int scifa1_data_b_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(3, 9), RCAR_GP_PIN(3, 8), +}; +static const unsigned int scifa1_data_b_mux[] = { + SCIFA1_RXD_B_MARK, SCIFA1_TXD_B_MARK, +}; +static const unsigned int scifa1_clk_b_pins[] = { + /* SCK */ + RCAR_GP_PIN(1, 0), +}; +static const unsigned int scifa1_clk_b_mux[] = { + SCIFA1_SCK_B_MARK, +}; +static const unsigned int scifa1_data_c_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(1, 2), RCAR_GP_PIN(1, 3), +}; +static const unsigned int scifa1_data_c_mux[] = { + SCIFA1_RXD_C_MARK, SCIFA1_TXD_C_MARK, +}; +/* - SCIFA2 ----------------------------------------------------------------- */ +static const unsigned int scifa2_data_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(2, 30), RCAR_GP_PIN(2, 31), +}; +static const unsigned int scifa2_data_mux[] = { + SCIFA2_RXD_MARK, SCIFA2_TXD_MARK, +}; +static const unsigned int scifa2_clk_pins[] = { + /* SCK */ + RCAR_GP_PIN(3, 18), +}; +static const unsigned int scifa2_clk_mux[] = { + SCIFA2_SCK_MARK, +}; +static const unsigned int scifa2_data_b_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(3, 17), RCAR_GP_PIN(3, 16), +}; +static const unsigned int scifa2_data_b_mux[] = { + SCIFA2_RXD_B_MARK, SCIFA2_TXD_B_MARK, +}; +/* - SCIFA3 ----------------------------------------------------------------- */ +static const unsigned int scifa3_data_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(3, 22), RCAR_GP_PIN(3, 21), +}; +static const unsigned int scifa3_data_mux[] = { + SCIFA3_RXD_MARK, SCIFA3_TXD_MARK, +}; +static const unsigned int scifa3_clk_pins[] = { + /* SCK */ + RCAR_GP_PIN(3, 23), +}; +static const unsigned int scifa3_clk_mux[] = { + SCIFA3_SCK_MARK, +}; +static const unsigned int scifa3_data_b_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(5, 19), RCAR_GP_PIN(5, 20), +}; +static const unsigned int scifa3_data_b_mux[] = { + SCIFA3_RXD_B_MARK, SCIFA3_TXD_B_MARK, +}; +static const unsigned int scifa3_clk_b_pins[] = { + /* SCK */ + RCAR_GP_PIN(4, 8), +}; +static const unsigned int scifa3_clk_b_mux[] = { + SCIFA3_SCK_B_MARK, +}; +static const unsigned int scifa3_data_c_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(7, 21), RCAR_GP_PIN(7, 20), +}; +static const unsigned int scifa3_data_c_mux[] = { + SCIFA3_RXD_C_MARK, SCIFA3_TXD_C_MARK, +}; +static const unsigned int scifa3_clk_c_pins[] = { + /* SCK */ + RCAR_GP_PIN(7, 22), +}; +static const unsigned int scifa3_clk_c_mux[] = { + SCIFA3_SCK_C_MARK, +}; +/* - SCIFA4 ----------------------------------------------------------------- */ +static const unsigned int scifa4_data_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(4, 2), RCAR_GP_PIN(4, 1), +}; +static const unsigned int scifa4_data_mux[] = { + SCIFA4_RXD_MARK, SCIFA4_TXD_MARK, +}; +static const unsigned int scifa4_data_b_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(5, 1), RCAR_GP_PIN(5, 0), +}; +static const unsigned int scifa4_data_b_mux[] = { + SCIFA4_RXD_B_MARK, SCIFA4_TXD_B_MARK, +}; +static const unsigned int scifa4_data_c_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(7, 22), RCAR_GP_PIN(7, 21), +}; +static const unsigned int scifa4_data_c_mux[] = { + SCIFA4_RXD_C_MARK, SCIFA4_TXD_C_MARK, +}; +/* - SCIFA5 ----------------------------------------------------------------- */ +static const unsigned int scifa5_data_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(4, 4), RCAR_GP_PIN(4, 3), +}; +static const unsigned int scifa5_data_mux[] = { + SCIFA5_RXD_MARK, SCIFA5_TXD_MARK, +}; +static const unsigned int scifa5_data_b_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(6, 7), RCAR_GP_PIN(6, 6), +}; +static const unsigned int scifa5_data_b_mux[] = { + SCIFA5_RXD_B_MARK, SCIFA5_TXD_B_MARK, +}; +static const unsigned int scifa5_data_c_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(6, 23), RCAR_GP_PIN(6, 22), +}; +static const unsigned int scifa5_data_c_mux[] = { + SCIFA5_RXD_C_MARK, SCIFA5_TXD_C_MARK, +}; +/* - SCIFB0 ----------------------------------------------------------------- */ +static const unsigned int scifb0_data_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(7, 3), RCAR_GP_PIN(7, 4), +}; +static const unsigned int scifb0_data_mux[] = { + SCIFB0_RXD_MARK, SCIFB0_TXD_MARK, +}; +static const unsigned int scifb0_clk_pins[] = { + /* SCK */ + RCAR_GP_PIN(7, 2), +}; +static const unsigned int scifb0_clk_mux[] = { + SCIFB0_SCK_MARK, +}; +static const unsigned int scifb0_ctrl_pins[] = { + /* RTS, CTS */ + RCAR_GP_PIN(7, 1), RCAR_GP_PIN(7, 0), +}; +static const unsigned int scifb0_ctrl_mux[] = { + SCIFB0_RTS_N_MARK, SCIFB0_CTS_N_MARK, +}; +static const unsigned int scifb0_data_b_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(1, 20), RCAR_GP_PIN(1, 21), +}; +static const unsigned int scifb0_data_b_mux[] = { + SCIFB0_RXD_B_MARK, SCIFB0_TXD_B_MARK, +}; +static const unsigned int scifb0_clk_b_pins[] = { + /* SCK */ + RCAR_GP_PIN(5, 31), +}; +static const unsigned int scifb0_clk_b_mux[] = { + SCIFB0_SCK_B_MARK, +}; +static const unsigned int scifb0_ctrl_b_pins[] = { + /* RTS, CTS */ + RCAR_GP_PIN(1, 22), RCAR_GP_PIN(1, 23), +}; +static const unsigned int scifb0_ctrl_b_mux[] = { + SCIFB0_RTS_N_B_MARK, SCIFB0_CTS_N_B_MARK, +}; +static const unsigned int scifb0_data_c_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(2, 0), RCAR_GP_PIN(2, 1), +}; +static const unsigned int scifb0_data_c_mux[] = { + SCIFB0_RXD_C_MARK, SCIFB0_TXD_C_MARK, +}; +static const unsigned int scifb0_clk_c_pins[] = { + /* SCK */ + RCAR_GP_PIN(2, 30), +}; +static const unsigned int scifb0_clk_c_mux[] = { + SCIFB0_SCK_C_MARK, +}; +static const unsigned int scifb0_data_d_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(4, 28), RCAR_GP_PIN(4, 18), +}; +static const unsigned int scifb0_data_d_mux[] = { + SCIFB0_RXD_D_MARK, SCIFB0_TXD_D_MARK, +}; +static const unsigned int scifb0_clk_d_pins[] = { + /* SCK */ + RCAR_GP_PIN(4, 17), +}; +static const unsigned int scifb0_clk_d_mux[] = { + SCIFB0_SCK_D_MARK, +}; +/* - SCIFB1 ----------------------------------------------------------------- */ +static const unsigned int scifb1_data_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(7, 5), RCAR_GP_PIN(7, 6), +}; +static const unsigned int scifb1_data_mux[] = { + SCIFB1_RXD_MARK, SCIFB1_TXD_MARK, +}; +static const unsigned int scifb1_clk_pins[] = { + /* SCK */ + RCAR_GP_PIN(7, 7), +}; +static const unsigned int scifb1_clk_mux[] = { + SCIFB1_SCK_MARK, +}; +static const unsigned int scifb1_ctrl_pins[] = { + /* RTS, CTS */ + RCAR_GP_PIN(7, 9), RCAR_GP_PIN(7, 8), +}; +static const unsigned int scifb1_ctrl_mux[] = { + SCIFB1_RTS_N_MARK, SCIFB1_CTS_N_MARK, +}; +static const unsigned int scifb1_data_b_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(1, 17), RCAR_GP_PIN(1, 18), +}; +static const unsigned int scifb1_data_b_mux[] = { + SCIFB1_RXD_B_MARK, SCIFB1_TXD_B_MARK, +}; +static const unsigned int scifb1_clk_b_pins[] = { + /* SCK */ + RCAR_GP_PIN(1, 3), +}; +static const unsigned int scifb1_clk_b_mux[] = { + SCIFB1_SCK_B_MARK, +}; +static const unsigned int scifb1_data_c_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(1, 2), RCAR_GP_PIN(1, 3), +}; +static const unsigned int scifb1_data_c_mux[] = { + SCIFB1_RXD_C_MARK, SCIFB1_TXD_C_MARK, +}; +static const unsigned int scifb1_clk_c_pins[] = { + /* SCK */ + RCAR_GP_PIN(7, 11), +}; +static const unsigned int scifb1_clk_c_mux[] = { + SCIFB1_SCK_C_MARK, +}; +static const unsigned int scifb1_data_d_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(7, 10), RCAR_GP_PIN(7, 12), +}; +static const unsigned int scifb1_data_d_mux[] = { + SCIFB1_RXD_D_MARK, SCIFB1_TXD_D_MARK, +}; +/* - SCIFB2 ----------------------------------------------------------------- */ +static const unsigned int scifb2_data_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(4, 16), RCAR_GP_PIN(4, 17), +}; +static const unsigned int scifb2_data_mux[] = { + SCIFB2_RXD_MARK, SCIFB2_TXD_MARK, +}; +static const unsigned int scifb2_clk_pins[] = { + /* SCK */ + RCAR_GP_PIN(4, 15), +}; +static const unsigned int scifb2_clk_mux[] = { + SCIFB2_SCK_MARK, +}; +static const unsigned int scifb2_ctrl_pins[] = { + /* RTS, CTS */ + RCAR_GP_PIN(4, 14), RCAR_GP_PIN(4, 13), +}; +static const unsigned int scifb2_ctrl_mux[] = { + SCIFB2_RTS_N_MARK, SCIFB2_CTS_N_MARK, +}; +static const unsigned int scifb2_data_b_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(3, 12), RCAR_GP_PIN(3, 13), +}; +static const unsigned int scifb2_data_b_mux[] = { + SCIFB2_RXD_B_MARK, SCIFB2_TXD_B_MARK, +}; +static const unsigned int scifb2_clk_b_pins[] = { + /* SCK */ + RCAR_GP_PIN(5, 31), +}; +static const unsigned int scifb2_clk_b_mux[] = { + SCIFB2_SCK_B_MARK, +}; +static const unsigned int scifb2_ctrl_b_pins[] = { + /* RTS, CTS */ + RCAR_GP_PIN(3, 15), RCAR_GP_PIN(3, 14), +}; +static const unsigned int scifb2_ctrl_b_mux[] = { + SCIFB2_RTS_N_B_MARK, SCIFB2_CTS_N_B_MARK, +}; +static const unsigned int scifb2_data_c_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(2, 0), RCAR_GP_PIN(2, 1), +}; +static const unsigned int scifb2_data_c_mux[] = { + SCIFB2_RXD_C_MARK, SCIFB2_TXD_C_MARK, +}; +static const unsigned int scifb2_clk_c_pins[] = { + /* SCK */ + RCAR_GP_PIN(5, 27), +}; +static const unsigned int scifb2_clk_c_mux[] = { + SCIFB2_SCK_C_MARK, +}; +static const unsigned int scifb2_data_d_pins[] = { + /* RXD, TXD */ + RCAR_GP_PIN(5, 26), RCAR_GP_PIN(5, 25), +}; +static const unsigned int scifb2_data_d_mux[] = { + SCIFB2_RXD_D_MARK, SCIFB2_TXD_D_MARK, +}; +/* - SDHI0 ------------------------------------------------------------------ */ +static const unsigned int sdhi0_data1_pins[] = { + /* D0 */ + RCAR_GP_PIN(6, 2), +}; +static const unsigned int sdhi0_data1_mux[] = { + SD0_DATA0_MARK, +}; +static const unsigned int sdhi0_data4_pins[] = { + /* D[0:3] */ + RCAR_GP_PIN(6, 2), RCAR_GP_PIN(6, 3), + RCAR_GP_PIN(6, 4), RCAR_GP_PIN(6, 5), +}; +static const unsigned int sdhi0_data4_mux[] = { + SD0_DATA0_MARK, SD0_DATA1_MARK, SD0_DATA2_MARK, SD0_DATA3_MARK, +}; +static const unsigned int sdhi0_ctrl_pins[] = { + /* CLK, CMD */ + RCAR_GP_PIN(6, 0), RCAR_GP_PIN(6, 1), +}; +static const unsigned int sdhi0_ctrl_mux[] = { + SD0_CLK_MARK, SD0_CMD_MARK, +}; +static const unsigned int sdhi0_cd_pins[] = { + /* CD */ + RCAR_GP_PIN(6, 6), +}; +static const unsigned int sdhi0_cd_mux[] = { + SD0_CD_MARK, +}; +static const unsigned int sdhi0_wp_pins[] = { + /* WP */ + RCAR_GP_PIN(6, 7), +}; +static const unsigned int sdhi0_wp_mux[] = { + SD0_WP_MARK, +}; +/* - SDHI1 ------------------------------------------------------------------ */ +static const unsigned int sdhi1_data1_pins[] = { + /* D0 */ + RCAR_GP_PIN(6, 10), +}; +static const unsigned int sdhi1_data1_mux[] = { + SD1_DATA0_MARK, +}; +static const unsigned int sdhi1_data4_pins[] = { + /* D[0:3] */ + RCAR_GP_PIN(6, 10), RCAR_GP_PIN(6, 11), + RCAR_GP_PIN(6, 12), RCAR_GP_PIN(6, 13), +}; +static const unsigned int sdhi1_data4_mux[] = { + SD1_DATA0_MARK, SD1_DATA1_MARK, SD1_DATA2_MARK, SD1_DATA3_MARK, +}; +static const unsigned int sdhi1_ctrl_pins[] = { + /* CLK, CMD */ + RCAR_GP_PIN(6, 8), RCAR_GP_PIN(6, 9), +}; +static const unsigned int sdhi1_ctrl_mux[] = { + SD1_CLK_MARK, SD1_CMD_MARK, +}; +static const unsigned int sdhi1_cd_pins[] = { + /* CD */ + RCAR_GP_PIN(6, 14), +}; +static const unsigned int sdhi1_cd_mux[] = { + SD1_CD_MARK, +}; +static const unsigned int sdhi1_wp_pins[] = { + /* WP */ + RCAR_GP_PIN(6, 15), +}; +static const unsigned int sdhi1_wp_mux[] = { + SD1_WP_MARK, +}; +/* - SDHI2 ------------------------------------------------------------------ */ +static const unsigned int sdhi2_data1_pins[] = { + /* D0 */ + RCAR_GP_PIN(6, 18), +}; +static const unsigned int sdhi2_data1_mux[] = { + SD2_DATA0_MARK, +}; +static const unsigned int sdhi2_data4_pins[] = { + /* D[0:3] */ + RCAR_GP_PIN(6, 18), RCAR_GP_PIN(6, 19), + RCAR_GP_PIN(6, 20), RCAR_GP_PIN(6, 21), +}; +static const unsigned int sdhi2_data4_mux[] = { + SD2_DATA0_MARK, SD2_DATA1_MARK, SD2_DATA2_MARK, SD2_DATA3_MARK, +}; +static const unsigned int sdhi2_ctrl_pins[] = { + /* CLK, CMD */ + RCAR_GP_PIN(6, 16), RCAR_GP_PIN(6, 17), +}; +static const unsigned int sdhi2_ctrl_mux[] = { + SD2_CLK_MARK, SD2_CMD_MARK, +}; +static const unsigned int sdhi2_cd_pins[] = { + /* CD */ + RCAR_GP_PIN(6, 22), +}; +static const unsigned int sdhi2_cd_mux[] = { + SD2_CD_MARK, +}; +static const unsigned int sdhi2_wp_pins[] = { + /* WP */ + RCAR_GP_PIN(6, 23), +}; +static const unsigned int sdhi2_wp_mux[] = { + SD2_WP_MARK, +}; +/* - USB0 ------------------------------------------------------------------- */ +static const unsigned int usb0_pwen_pins[] = { + /* PWEN */ + RCAR_GP_PIN(7, 23), +}; +static const unsigned int usb0_pwen_mux[] = { + USB0_PWEN_MARK, +}; +static const unsigned int usb0_ovc_pins[] = { + /* OVC */ + RCAR_GP_PIN(7, 24), +}; +static const unsigned int usb0_ovc_mux[] = { + USB0_OVC_MARK, +}; +/* - USB1 ------------------------------------------------------------------- */ +static const unsigned int usb1_pwen_pins[] = { + /* PWEN */ + RCAR_GP_PIN(7, 25), +}; +static const unsigned int usb1_pwen_mux[] = { + USB1_PWEN_MARK, +}; +static const unsigned int usb1_ovc_pins[] = { + /* OVC */ + RCAR_GP_PIN(6, 30), +}; +static const unsigned int usb1_ovc_mux[] = { + USB1_OVC_MARK, +}; + +static const struct sh_pfc_pin_group pinmux_groups[] = { + SH_PFC_PIN_GROUP(du_rgb666), + SH_PFC_PIN_GROUP(du_rgb888), + SH_PFC_PIN_GROUP(du_clk_out_0), + SH_PFC_PIN_GROUP(du_clk_out_1), + SH_PFC_PIN_GROUP(du_sync_1), + SH_PFC_PIN_GROUP(du_cde_disp), + SH_PFC_PIN_GROUP(du0_clk_in), + SH_PFC_PIN_GROUP(du1_clk_in), + SH_PFC_PIN_GROUP(eth_link), + SH_PFC_PIN_GROUP(eth_magic), + SH_PFC_PIN_GROUP(eth_mdio), + SH_PFC_PIN_GROUP(eth_rmii), + SH_PFC_PIN_GROUP(intc_irq0), + SH_PFC_PIN_GROUP(intc_irq1), + SH_PFC_PIN_GROUP(intc_irq2), + SH_PFC_PIN_GROUP(intc_irq3), + SH_PFC_PIN_GROUP(mmc_data1), + SH_PFC_PIN_GROUP(mmc_data4), + SH_PFC_PIN_GROUP(mmc_data8), + SH_PFC_PIN_GROUP(mmc_ctrl), + SH_PFC_PIN_GROUP(msiof0_clk), + SH_PFC_PIN_GROUP(msiof0_sync), + SH_PFC_PIN_GROUP(msiof0_ss1), + SH_PFC_PIN_GROUP(msiof0_ss2), + SH_PFC_PIN_GROUP(msiof0_rx), + SH_PFC_PIN_GROUP(msiof0_tx), + SH_PFC_PIN_GROUP(msiof1_clk), + SH_PFC_PIN_GROUP(msiof1_sync), + SH_PFC_PIN_GROUP(msiof1_ss1), + SH_PFC_PIN_GROUP(msiof1_ss2), + SH_PFC_PIN_GROUP(msiof1_rx), + SH_PFC_PIN_GROUP(msiof1_tx), + SH_PFC_PIN_GROUP(msiof2_clk), + SH_PFC_PIN_GROUP(msiof2_sync), + SH_PFC_PIN_GROUP(msiof2_ss1), + SH_PFC_PIN_GROUP(msiof2_ss2), + SH_PFC_PIN_GROUP(msiof2_rx), + SH_PFC_PIN_GROUP(msiof2_tx), + SH_PFC_PIN_GROUP(scif0_data), + SH_PFC_PIN_GROUP(scif0_data_b), + SH_PFC_PIN_GROUP(scif0_data_c), + SH_PFC_PIN_GROUP(scif0_data_d), + SH_PFC_PIN_GROUP(scif0_data_e), + SH_PFC_PIN_GROUP(scif1_data), + SH_PFC_PIN_GROUP(scif1_data_b), + SH_PFC_PIN_GROUP(scif1_clk_b), + SH_PFC_PIN_GROUP(scif1_data_c), + SH_PFC_PIN_GROUP(scif1_data_d), + SH_PFC_PIN_GROUP(scif2_data), + SH_PFC_PIN_GROUP(scif2_data_b), + SH_PFC_PIN_GROUP(scif2_clk_b), + SH_PFC_PIN_GROUP(scif2_data_c), + SH_PFC_PIN_GROUP(scif2_data_e), + SH_PFC_PIN_GROUP(scif3_data), + SH_PFC_PIN_GROUP(scif3_clk), + SH_PFC_PIN_GROUP(scif3_data_b), + SH_PFC_PIN_GROUP(scif3_clk_b), + SH_PFC_PIN_GROUP(scif3_data_c), + SH_PFC_PIN_GROUP(scif3_data_d), + SH_PFC_PIN_GROUP(scif4_data), + SH_PFC_PIN_GROUP(scif4_data_b), + SH_PFC_PIN_GROUP(scif4_data_c), + SH_PFC_PIN_GROUP(scif5_data), + SH_PFC_PIN_GROUP(scif5_data_b), + SH_PFC_PIN_GROUP(scifa0_data), + SH_PFC_PIN_GROUP(scifa0_data_b), + SH_PFC_PIN_GROUP(scifa1_data), + SH_PFC_PIN_GROUP(scifa1_clk), + SH_PFC_PIN_GROUP(scifa1_data_b), + SH_PFC_PIN_GROUP(scifa1_clk_b), + SH_PFC_PIN_GROUP(scifa1_data_c), + SH_PFC_PIN_GROUP(scifa2_data), + SH_PFC_PIN_GROUP(scifa2_clk), + SH_PFC_PIN_GROUP(scifa2_data_b), + SH_PFC_PIN_GROUP(scifa3_data), + SH_PFC_PIN_GROUP(scifa3_clk), + SH_PFC_PIN_GROUP(scifa3_data_b), + SH_PFC_PIN_GROUP(scifa3_clk_b), + SH_PFC_PIN_GROUP(scifa3_data_c), + SH_PFC_PIN_GROUP(scifa3_clk_c), + SH_PFC_PIN_GROUP(scifa4_data), + SH_PFC_PIN_GROUP(scifa4_data_b), + SH_PFC_PIN_GROUP(scifa4_data_c), + SH_PFC_PIN_GROUP(scifa5_data), + SH_PFC_PIN_GROUP(scifa5_data_b), + SH_PFC_PIN_GROUP(scifa5_data_c), + SH_PFC_PIN_GROUP(scifb0_data), + SH_PFC_PIN_GROUP(scifb0_clk), + SH_PFC_PIN_GROUP(scifb0_ctrl), + SH_PFC_PIN_GROUP(scifb0_data_b), + SH_PFC_PIN_GROUP(scifb0_clk_b), + SH_PFC_PIN_GROUP(scifb0_ctrl_b), + SH_PFC_PIN_GROUP(scifb0_data_c), + SH_PFC_PIN_GROUP(scifb0_clk_c), + SH_PFC_PIN_GROUP(scifb0_data_d), + SH_PFC_PIN_GROUP(scifb0_clk_d), + SH_PFC_PIN_GROUP(scifb1_data), + SH_PFC_PIN_GROUP(scifb1_clk), + SH_PFC_PIN_GROUP(scifb1_ctrl), + SH_PFC_PIN_GROUP(scifb1_data_b), + SH_PFC_PIN_GROUP(scifb1_clk_b), + SH_PFC_PIN_GROUP(scifb1_data_c), + SH_PFC_PIN_GROUP(scifb1_clk_c), + SH_PFC_PIN_GROUP(scifb1_data_d), + SH_PFC_PIN_GROUP(scifb2_data), + SH_PFC_PIN_GROUP(scifb2_clk), + SH_PFC_PIN_GROUP(scifb2_ctrl), + SH_PFC_PIN_GROUP(scifb2_data_b), + SH_PFC_PIN_GROUP(scifb2_clk_b), + SH_PFC_PIN_GROUP(scifb2_ctrl_b), + SH_PFC_PIN_GROUP(scifb2_data_c), + SH_PFC_PIN_GROUP(scifb2_clk_c), + SH_PFC_PIN_GROUP(scifb2_data_d), + SH_PFC_PIN_GROUP(sdhi0_data1), + SH_PFC_PIN_GROUP(sdhi0_data4), + SH_PFC_PIN_GROUP(sdhi0_ctrl), + SH_PFC_PIN_GROUP(sdhi0_cd), + SH_PFC_PIN_GROUP(sdhi0_wp), + SH_PFC_PIN_GROUP(sdhi1_data1), + SH_PFC_PIN_GROUP(sdhi1_data4), + SH_PFC_PIN_GROUP(sdhi1_ctrl), + SH_PFC_PIN_GROUP(sdhi1_cd), + SH_PFC_PIN_GROUP(sdhi1_wp), + SH_PFC_PIN_GROUP(sdhi2_data1), + SH_PFC_PIN_GROUP(sdhi2_data4), + SH_PFC_PIN_GROUP(sdhi2_ctrl), + SH_PFC_PIN_GROUP(sdhi2_cd), + SH_PFC_PIN_GROUP(sdhi2_wp), + SH_PFC_PIN_GROUP(usb0_pwen), + SH_PFC_PIN_GROUP(usb0_ovc), + SH_PFC_PIN_GROUP(usb1_pwen), + SH_PFC_PIN_GROUP(usb1_ovc), +}; + +static const char * const du_groups[] = { + "du_rgb666", + "du_rgb888", + "du_clk_out_0", + "du_clk_out_1", + "du_sync_1", + "du_cde_disp", +}; + +static const char * const du0_groups[] = { + "du0_clk_in", +}; + +static const char * const du1_groups[] = { + "du1_clk_in", +}; + +static const char * const eth_groups[] = { + "eth_link", + "eth_magic", + "eth_mdio", + "eth_rmii", +}; + +static const char * const intc_groups[] = { + "intc_irq0", + "intc_irq1", + "intc_irq2", + "intc_irq3", +}; + +static const char * const mmc_groups[] = { + "mmc_data1", + "mmc_data4", + "mmc_data8", + "mmc_ctrl", +}; + +static const char * const msiof0_groups[] = { + "msiof0_clk", + "msiof0_ctrl", + "msiof0_data", +}; + +static const char * const msiof1_groups[] = { + "msiof1_clk", + "msiof1_ctrl", + "msiof1_data", +}; + +static const char * const msiof2_groups[] = { + "msiof2_clk", + "msiof2_ctrl", + "msiof2_data", +}; + +static const char * const scif0_groups[] = { + "scif0_data", + "scif0_data_b", + "scif0_data_c", + "scif0_data_d", + "scif0_data_e", +}; + +static const char * const scif1_groups[] = { + "scif1_data", + "scif1_data_b", + "scif1_clk_b", + "scif1_data_c", + "scif1_data_d", +}; + +static const char * const scif2_groups[] = { + "scif2_data", + "scif2_data_b", + "scif2_clk_b", + "scif2_data_c", + "scif2_data_e", +}; +static const char * const scif3_groups[] = { + "scif3_data", + "scif3_clk", + "scif3_data_b", + "scif3_clk_b", + "scif3_data_c", + "scif3_data_d", +}; +static const char * const scif4_groups[] = { + "scif4_data", + "scif4_data_b", + "scif4_data_c", +}; +static const char * const scif5_groups[] = { + "scif5_data", + "scif5_data_b", +}; +static const char * const scifa0_groups[] = { + "scifa0_data", + "scifa0_data_b", +}; +static const char * const scifa1_groups[] = { + "scifa1_data", + "scifa1_clk", + "scifa1_data_b", + "scifa1_clk_b", + "scifa1_data_c", +}; +static const char * const scifa2_groups[] = { + "scifa2_data", + "scifa2_clk", + "scifa2_data_b", +}; +static const char * const scifa3_groups[] = { + "scifa3_data", + "scifa3_clk", + "scifa3_data_b", + "scifa3_clk_b", + "scifa3_data_c", + "scifa3_clk_c", +}; +static const char * const scifa4_groups[] = { + "scifa4_data", + "scifa4_data_b", + "scifa4_data_c", +}; +static const char * const scifa5_groups[] = { + "scifa5_data", + "scifa5_data_b", + "scifa5_data_c", +}; +static const char * const scifb0_groups[] = { + "scifb0_data", + "scifb0_clk", + "scifb0_ctrl", + "scifb0_data_b", + "scifb0_clk_b", + "scifb0_ctrl_b", + "scifb0_data_c", + "scifb0_clk_c", + "scifb0_data_d", + "scifb0_clk_d", +}; +static const char * const scifb1_groups[] = { + "scifb1_data", + "scifb1_clk", + "scifb1_ctrl", + "scifb1_data_b", + "scifb1_clk_b", + "scifb1_data_c", + "scifb1_clk_c", + "scifb1_data_d", +}; +static const char * const scifb2_groups[] = { + "scifb2_data", + "scifb2_clk", + "scifb2_ctrl", + "scifb2_data_b", + "scifb2_clk_b", + "scifb2_ctrl_b", + "scifb0_data_c", + "scifb2_clk_c", + "scifb2_data_d", +}; + +static const char * const sdhi0_groups[] = { + "sdhi0_data1", + "sdhi0_data4", + "sdhi0_ctrl", + "sdhi0_cd", + "sdhi0_wp", +}; + +static const char * const sdhi1_groups[] = { + "sdhi1_data1", + "sdhi1_data4", + "sdhi1_ctrl", + "sdhi1_cd", + "sdhi1_wp", +}; + +static const char * const sdhi2_groups[] = { + "sdhi2_data1", + "sdhi2_data4", + "sdhi2_ctrl", + "sdhi2_cd", + "sdhi2_wp", +}; + +static const char * const usb0_groups[] = { + "usb0_pwen", + "usb0_ovc", +}; +static const char * const usb1_groups[] = { + "usb1_pwen", + "usb1_ovc", +}; + +static const struct sh_pfc_function pinmux_functions[] = { + SH_PFC_FUNCTION(du), + SH_PFC_FUNCTION(du0), + SH_PFC_FUNCTION(du1), + SH_PFC_FUNCTION(eth), + SH_PFC_FUNCTION(intc), + SH_PFC_FUNCTION(mmc), + SH_PFC_FUNCTION(msiof0), + SH_PFC_FUNCTION(msiof1), + SH_PFC_FUNCTION(msiof2), + SH_PFC_FUNCTION(scif0), + SH_PFC_FUNCTION(scif1), + SH_PFC_FUNCTION(scif2), + SH_PFC_FUNCTION(scif3), + SH_PFC_FUNCTION(scif4), + SH_PFC_FUNCTION(scif5), + SH_PFC_FUNCTION(scifa0), + SH_PFC_FUNCTION(scifa1), + SH_PFC_FUNCTION(scifa2), + SH_PFC_FUNCTION(scifa3), + SH_PFC_FUNCTION(scifa4), + SH_PFC_FUNCTION(scifa5), + SH_PFC_FUNCTION(scifb0), + SH_PFC_FUNCTION(scifb1), + SH_PFC_FUNCTION(scifb2), + SH_PFC_FUNCTION(sdhi0), + SH_PFC_FUNCTION(sdhi1), + SH_PFC_FUNCTION(sdhi2), + SH_PFC_FUNCTION(usb0), + SH_PFC_FUNCTION(usb1), +}; + +static struct pinmux_cfg_reg pinmux_config_regs[] = { + { PINMUX_CFG_REG("GPSR0", 0xE6060004, 32, 1) { + GP_0_31_FN, FN_IP1_22_20, + GP_0_30_FN, FN_IP1_19_17, + GP_0_29_FN, FN_IP1_16_14, + GP_0_28_FN, FN_IP1_13_11, + GP_0_27_FN, FN_IP1_10_8, + GP_0_26_FN, FN_IP1_7_6, + GP_0_25_FN, FN_IP1_5_4, + GP_0_24_FN, FN_IP1_3_2, + GP_0_23_FN, FN_IP1_1_0, + GP_0_22_FN, FN_IP0_30_29, + GP_0_21_FN, FN_IP0_28_27, + GP_0_20_FN, FN_IP0_26_25, + GP_0_19_FN, FN_IP0_24_23, + GP_0_18_FN, FN_IP0_22_21, + GP_0_17_FN, FN_IP0_20_19, + GP_0_16_FN, FN_IP0_18_16, + GP_0_15_FN, FN_IP0_15, + GP_0_14_FN, FN_IP0_14, + GP_0_13_FN, FN_IP0_13, + GP_0_12_FN, FN_IP0_12, + GP_0_11_FN, FN_IP0_11, + GP_0_10_FN, FN_IP0_10, + GP_0_9_FN, FN_IP0_9, + GP_0_8_FN, FN_IP0_8, + GP_0_7_FN, FN_IP0_7, + GP_0_6_FN, FN_IP0_6, + GP_0_5_FN, FN_IP0_5, + GP_0_4_FN, FN_IP0_4, + GP_0_3_FN, FN_IP0_3, + GP_0_2_FN, FN_IP0_2, + GP_0_1_FN, FN_IP0_1, + GP_0_0_FN, FN_IP0_0, } + }, + { PINMUX_CFG_REG("GPSR1", 0xE6060008, 32, 1) { + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + GP_1_25_FN, FN_IP3_21_20, + GP_1_24_FN, FN_IP3_19_18, + GP_1_23_FN, FN_IP3_17_16, + GP_1_22_FN, FN_IP3_15_14, + GP_1_21_FN, FN_IP3_13_12, + GP_1_20_FN, FN_IP3_11_9, + GP_1_19_FN, FN_RD_N, + GP_1_18_FN, FN_IP3_8_6, + GP_1_17_FN, FN_IP3_5_3, + GP_1_16_FN, FN_IP3_2_0, + GP_1_15_FN, FN_IP2_29_27, + GP_1_14_FN, FN_IP2_26_25, + GP_1_13_FN, FN_IP2_24_23, + GP_1_12_FN, FN_EX_CS0_N, + GP_1_11_FN, FN_IP2_22_21, + GP_1_10_FN, FN_IP2_20_19, + GP_1_9_FN, FN_IP2_18_16, + GP_1_8_FN, FN_IP2_15_13, + GP_1_7_FN, FN_IP2_12_10, + GP_1_6_FN, FN_IP2_9_7, + GP_1_5_FN, FN_IP2_6_5, + GP_1_4_FN, FN_IP2_4_3, + GP_1_3_FN, FN_IP2_2_0, + GP_1_2_FN, FN_IP1_31_29, + GP_1_1_FN, FN_IP1_28_26, + GP_1_0_FN, FN_IP1_25_23, } + }, + { PINMUX_CFG_REG("GPSR2", 0xE606000C, 32, 1) { + GP_2_31_FN, FN_IP6_7_6, + GP_2_30_FN, FN_IP6_5_3, + GP_2_29_FN, FN_IP6_2_0, + GP_2_28_FN, FN_AUDIO_CLKA, + GP_2_27_FN, FN_IP5_31_29, + GP_2_26_FN, FN_IP5_28_26, + GP_2_25_FN, FN_IP5_25_24, + GP_2_24_FN, FN_IP5_23_22, + GP_2_23_FN, FN_IP5_21_20, + GP_2_22_FN, FN_IP5_19_17, + GP_2_21_FN, FN_IP5_16_15, + GP_2_20_FN, FN_IP5_14_12, + GP_2_19_FN, FN_IP5_11_9, + GP_2_18_FN, FN_IP5_8_6, + GP_2_17_FN, FN_IP5_5_3, + GP_2_16_FN, FN_IP5_2_0, + GP_2_15_FN, FN_IP4_30_28, + GP_2_14_FN, FN_IP4_27_26, + GP_2_13_FN, FN_IP4_25_24, + GP_2_12_FN, FN_IP4_23_22, + GP_2_11_FN, FN_IP4_21, + GP_2_10_FN, FN_IP4_20, + GP_2_9_FN, FN_IP4_19, + GP_2_8_FN, FN_IP4_18_16, + GP_2_7_FN, FN_IP4_15_13, + GP_2_6_FN, FN_IP4_12_10, + GP_2_5_FN, FN_IP4_9_8, + GP_2_4_FN, FN_IP4_7_5, + GP_2_3_FN, FN_IP4_4_2, + GP_2_2_FN, FN_IP4_1_0, + GP_2_1_FN, FN_IP3_30_28, + GP_2_0_FN, FN_IP3_27_25 } + }, + { PINMUX_CFG_REG("GPSR3", 0xE6060010, 32, 1) { + GP_3_31_FN, FN_IP9_18_17, + GP_3_30_FN, FN_IP9_16, + GP_3_29_FN, FN_IP9_15_13, + GP_3_28_FN, FN_IP9_12, + GP_3_27_FN, FN_IP9_11, + GP_3_26_FN, FN_IP9_10_8, + GP_3_25_FN, FN_IP9_7, + GP_3_24_FN, FN_IP9_6, + GP_3_23_FN, FN_IP9_5_3, + GP_3_22_FN, FN_IP9_2_0, + GP_3_21_FN, FN_IP8_30_28, + GP_3_20_FN, FN_IP8_27_26, + GP_3_19_FN, FN_IP8_25_24, + GP_3_18_FN, FN_IP8_23_21, + GP_3_17_FN, FN_IP8_20_18, + GP_3_16_FN, FN_IP8_17_15, + GP_3_15_FN, FN_IP8_14_12, + GP_3_14_FN, FN_IP8_11_9, + GP_3_13_FN, FN_IP8_8_6, + GP_3_12_FN, FN_IP8_5_3, + GP_3_11_FN, FN_IP8_2_0, + GP_3_10_FN, FN_IP7_29_27, + GP_3_9_FN, FN_IP7_26_24, + GP_3_8_FN, FN_IP7_23_21, + GP_3_7_FN, FN_IP7_20_19, + GP_3_6_FN, FN_IP7_18_17, + GP_3_5_FN, FN_IP7_16_15, + GP_3_4_FN, FN_IP7_14_13, + GP_3_3_FN, FN_IP7_12_11, + GP_3_2_FN, FN_IP7_10_9, + GP_3_1_FN, FN_IP7_8_6, + GP_3_0_FN, FN_IP7_5_3 } + }, + { PINMUX_CFG_REG("GPSR4", 0xE6060014, 32, 1) { + GP_4_31_FN, FN_IP15_5_4, + GP_4_30_FN, FN_IP15_3_2, + GP_4_29_FN, FN_IP15_1_0, + GP_4_28_FN, FN_IP11_8_6, + GP_4_27_FN, FN_IP11_5_3, + GP_4_26_FN, FN_IP11_2_0, + GP_4_25_FN, FN_IP10_31_29, + GP_4_24_FN, FN_IP10_28_27, + GP_4_23_FN, FN_IP10_26_25, + GP_4_22_FN, FN_IP10_24_22, + GP_4_21_FN, FN_IP10_21_19, + GP_4_20_FN, FN_IP10_18_17, + GP_4_19_FN, FN_IP10_16_15, + GP_4_18_FN, FN_IP10_14_12, + GP_4_17_FN, FN_IP10_11_9, + GP_4_16_FN, FN_IP10_8_6, + GP_4_15_FN, FN_IP10_5_3, + GP_4_14_FN, FN_IP10_2_0, + GP_4_13_FN, FN_IP9_31_29, + GP_4_12_FN, FN_VI0_DATA7_VI0_B7, + GP_4_11_FN, FN_VI0_DATA6_VI0_B6, + GP_4_10_FN, FN_VI0_DATA5_VI0_B5, + GP_4_9_FN, FN_VI0_DATA4_VI0_B4, + GP_4_8_FN, FN_IP9_28_27, + GP_4_7_FN, FN_VI0_DATA2_VI0_B2, + GP_4_6_FN, FN_VI0_DATA1_VI0_B1, + GP_4_5_FN, FN_VI0_DATA0_VI0_B0, + GP_4_4_FN, FN_IP9_26_25, + GP_4_3_FN, FN_IP9_24_23, + GP_4_2_FN, FN_IP9_22_21, + GP_4_1_FN, FN_IP9_20_19, + GP_4_0_FN, FN_VI0_CLK } + }, + { PINMUX_CFG_REG("GPSR5", 0xE6060018, 32, 1) { + GP_5_31_FN, FN_IP3_24_22, + GP_5_30_FN, FN_IP13_9_7, + GP_5_29_FN, FN_IP13_6_5, + GP_5_28_FN, FN_IP13_4_3, + GP_5_27_FN, FN_IP13_2_0, + GP_5_26_FN, FN_IP12_29_27, + GP_5_25_FN, FN_IP12_26_24, + GP_5_24_FN, FN_IP12_23_22, + GP_5_23_FN, FN_IP12_21_20, + GP_5_22_FN, FN_IP12_19_18, + GP_5_21_FN, FN_IP12_17_16, + GP_5_20_FN, FN_IP12_15_13, + GP_5_19_FN, FN_IP12_12_10, + GP_5_18_FN, FN_IP12_9_7, + GP_5_17_FN, FN_IP12_6_4, + GP_5_16_FN, FN_IP12_3_2, + GP_5_15_FN, FN_IP12_1_0, + GP_5_14_FN, FN_IP11_31_30, + GP_5_13_FN, FN_IP11_29_28, + GP_5_12_FN, FN_IP11_27, + GP_5_11_FN, FN_IP11_26, + GP_5_10_FN, FN_IP11_25, + GP_5_9_FN, FN_IP11_24, + GP_5_8_FN, FN_IP11_23, + GP_5_7_FN, FN_IP11_22, + GP_5_6_FN, FN_IP11_21, + GP_5_5_FN, FN_IP11_20, + GP_5_4_FN, FN_IP11_19, + GP_5_3_FN, FN_IP11_18_17, + GP_5_2_FN, FN_IP11_16_15, + GP_5_1_FN, FN_IP11_14_12, + GP_5_0_FN, FN_IP11_11_9 } + }, + { PINMUX_CFG_REG("GPSR6", 0xE606001C, 32, 1) { + GP_6_31_FN, FN_DU0_DOTCLKIN, + GP_6_30_FN, FN_USB1_OVC, + GP_6_29_FN, FN_IP14_31_29, + GP_6_28_FN, FN_IP14_28_26, + GP_6_27_FN, FN_IP14_25_23, + GP_6_26_FN, FN_IP14_22_20, + GP_6_25_FN, FN_IP14_19_17, + GP_6_24_FN, FN_IP14_16_14, + GP_6_23_FN, FN_IP14_13_11, + GP_6_22_FN, FN_IP14_10_8, + GP_6_21_FN, FN_IP14_7, + GP_6_20_FN, FN_IP14_6, + GP_6_19_FN, FN_IP14_5, + GP_6_18_FN, FN_IP14_4, + GP_6_17_FN, FN_IP14_3, + GP_6_16_FN, FN_IP14_2, + GP_6_15_FN, FN_IP14_1_0, + GP_6_14_FN, FN_IP13_30_28, + GP_6_13_FN, FN_IP13_27, + GP_6_12_FN, FN_IP13_26, + GP_6_11_FN, FN_IP13_25, + GP_6_10_FN, FN_IP13_24_23, + GP_6_9_FN, FN_IP13_22, + 0, 0, + GP_6_7_FN, FN_IP13_21_19, + GP_6_6_FN, FN_IP13_18_16, + GP_6_5_FN, FN_IP13_15, + GP_6_4_FN, FN_IP13_14, + GP_6_3_FN, FN_IP13_13, + GP_6_2_FN, FN_IP13_12, + GP_6_1_FN, FN_IP13_11, + GP_6_0_FN, FN_IP13_10 } + }, + { PINMUX_CFG_REG("GPSR7", 0xE6060074, 32, 1) { + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + GP_7_25_FN, FN_USB1_PWEN, + GP_7_24_FN, FN_USB0_OVC, + GP_7_23_FN, FN_USB0_PWEN, + GP_7_22_FN, FN_IP15_14_12, + GP_7_21_FN, FN_IP15_11_9, + GP_7_20_FN, FN_IP15_8_6, + GP_7_19_FN, FN_IP7_2_0, + GP_7_18_FN, FN_IP6_29_27, + GP_7_17_FN, FN_IP6_26_24, + GP_7_16_FN, FN_IP6_23_21, + GP_7_15_FN, FN_IP6_20_19, + GP_7_14_FN, FN_IP6_18_16, + GP_7_13_FN, FN_IP6_15_14, + GP_7_12_FN, FN_IP6_13_12, + GP_7_11_FN, FN_IP6_11_10, + GP_7_10_FN, FN_IP6_9_8, + GP_7_9_FN, FN_IP16_11_10, + GP_7_8_FN, FN_IP16_9_8, + GP_7_7_FN, FN_IP16_7_6, + GP_7_6_FN, FN_IP16_5_3, + GP_7_5_FN, FN_IP16_2_0, + GP_7_4_FN, FN_IP15_29_27, + GP_7_3_FN, FN_IP15_26_24, + GP_7_2_FN, FN_IP15_23_21, + GP_7_1_FN, FN_IP15_20_18, + GP_7_0_FN, FN_IP15_17_15 } + }, + { PINMUX_CFG_REG_VAR("IPSR0", 0xE6060020, 32, + 1, 2, 2, 2, 2, 2, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1) { + /* IP0_31 [1] */ + 0, 0, + /* IP0_30_29 [2] */ + FN_A6, FN_MSIOF1_SCK, + 0, 0, + /* IP0_28_27 [2] */ + FN_A5, FN_MSIOF0_RXD_B, + 0, 0, + /* IP0_26_25 [2] */ + FN_A4, FN_MSIOF0_TXD_B, + 0, 0, + /* IP0_24_23 [2] */ + FN_A3, FN_MSIOF0_SS2_B, + 0, 0, + /* IP0_22_21 [2] */ + FN_A2, FN_MSIOF0_SS1_B, + 0, 0, + /* IP0_20_19 [2] */ + FN_A1, FN_MSIOF0_SYNC_B, + 0, 0, + /* IP0_18_16 [3] */ + FN_A0, FN_ATAWR0_N_C, FN_MSIOF0_SCK_B, FN_SCL0_C, FN_PWM2_B, + 0, 0, 0, + /* IP0_15 [1] */ + FN_D15, 0, + /* IP0_14 [1] */ + FN_D14, 0, + /* IP0_13 [1] */ + FN_D13, 0, + /* IP0_12 [1] */ + FN_D12, 0, + /* IP0_11 [1] */ + FN_D11, 0, + /* IP0_10 [1] */ + FN_D10, 0, + /* IP0_9 [1] */ + FN_D9, 0, + /* IP0_8 [1] */ + FN_D8, 0, + /* IP0_7 [1] */ + FN_D7, 0, + /* IP0_6 [1] */ + FN_D6, 0, + /* IP0_5 [1] */ + FN_D5, 0, + /* IP0_4 [1] */ + FN_D4, 0, + /* IP0_3 [1] */ + FN_D3, 0, + /* IP0_2 [1] */ + FN_D2, 0, + /* IP0_1 [1] */ + FN_D1, 0, + /* IP0_0 [1] */ + FN_D0, 0, } + }, + { PINMUX_CFG_REG_VAR("IPSR1", 0xE6060024, 32, + 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2) { + /* IP1_31_29 [3] */ + FN_A18, FN_DREQ1, FN_SCIFA1_RXD_C, 0, FN_SCIFB1_RXD_C, + 0, 0, 0, + /* IP1_28_26 [3] */ + FN_A17, FN_DACK2_B, 0, FN_SDA0_C, + 0, 0, 0, 0, + /* IP1_25_23 [3] */ + FN_A16, FN_DREQ2_B, FN_FMCLK_C, 0, FN_SCIFA1_SCK_B, + 0, 0, 0, + /* IP1_22_20 [3] */ + FN_A15, FN_BPFCLK_C, + 0, 0, 0, 0, 0, 0, + /* IP1_19_17 [3] */ + FN_A14, FN_ATADIR0_N_C, FN_FMIN, FN_FMIN_C, FN_MSIOF1_SYNC_D, + 0, 0, 0, + /* IP1_16_14 [3] */ + FN_A13, FN_ATAG0_N_C, FN_BPFCLK, FN_MSIOF1_SS1_D, + 0, 0, 0, 0, + /* IP1_13_11 [3] */ + FN_A12, FN_FMCLK, FN_SDA3_D, FN_MSIOF1_SCK_D, + 0, 0, 0, 0, + /* IP1_10_8 [3] */ + FN_A11, FN_MSIOF1_RXD, FN_SCL3_D, FN_MSIOF1_RXD_D, + 0, 0, 0, 0, + /* IP1_7_6 [2] */ + FN_A10, FN_MSIOF1_TXD, 0, FN_MSIOF1_TXD_D, + /* IP1_5_4 [2] */ + FN_A9, FN_MSIOF1_SS2, FN_SDA0, 0, + /* IP1_3_2 [2] */ + FN_A8, FN_MSIOF1_SS1, FN_SCL0, 0, + /* IP1_1_0 [2] */ + FN_A7, FN_MSIOF1_SYNC, + 0, 0, } + }, + { PINMUX_CFG_REG_VAR("IPSR2", 0xE6060028, 32, + 2, 3, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 3) { + /* IP2_31_20 [2] */ + 0, 0, 0, 0, + /* IP2_29_27 [3] */ + FN_EX_CS3_N, FN_ATADIR0_N, FN_MSIOF2_TXD, + FN_ATAG0_N, 0, FN_EX_WAIT1, + 0, 0, + /* IP2_26_25 [2] */ + FN_EX_CS2_N, FN_ATAWR0_N, FN_MSIOF2_SYNC, 0, + /* IP2_24_23 [2] */ + FN_EX_CS1_N, FN_MSIOF2_SCK, 0, 0, + /* IP2_22_21 [2] */ + FN_CS1_N_A26, FN_ATADIR0_N_B, FN_SDA1, 0, + /* IP2_20_19 [2] */ + FN_CS0_N, FN_ATAG0_N_B, FN_SCL1, 0, + /* IP2_18_16 [3] */ + FN_A25, FN_DACK2, FN_SSL, FN_DREQ1_C, FN_RX1, FN_SCIFA1_RXD, + 0, 0, + /* IP2_15_13 [3] */ + FN_A24, FN_DREQ2, FN_IO3, FN_TX1, FN_SCIFA1_TXD, + 0, 0, 0, + /* IP2_12_0 [3] */ + FN_A23, FN_IO2, FN_BPFCLK_B, FN_RX0, FN_SCIFA0_RXD, + 0, 0, 0, + /* IP2_9_7 [3] */ + FN_A22, FN_MISO_IO1, FN_FMCLK_B, FN_TX0, FN_SCIFA0_TXD, + 0, 0, 0, + /* IP2_6_5 [2] */ + FN_A21, FN_ATAWR0_N_B, FN_MOSI_IO0, 0, + /* IP2_4_3 [2] */ + FN_A20, FN_SPCLK, 0, 0, + /* IP2_2_0 [3] */ + FN_A19, FN_DACK1, FN_SCIFA1_TXD_C, 0, + FN_SCIFB1_TXD_C, 0, FN_SCIFB1_SCK_B, 0, } + }, + { PINMUX_CFG_REG_VAR("IPSR3", 0xE606002C, 32, + 1, 3, 3, 3, 2, 2, 2, 2, 2, 3, 3, 3, 3) { + /* IP3_31 [1] */ + 0, 0, + /* IP3_30_28 [3] */ + FN_SSI_WS0129, FN_HTX0_C, FN_HTX2_C, + FN_SCIFB0_TXD_C, FN_SCIFB2_TXD_C, + 0, 0, 0, + /* IP3_27_25 [3] */ + FN_SSI_SCK0129, FN_HRX0_C, FN_HRX2_C, + FN_SCIFB0_RXD_C, FN_SCIFB2_RXD_C, + 0, 0, 0, + /* IP3_24_22 [3] */ + FN_SPEEDIN, 0, FN_HSCK0_C, FN_HSCK2_C, FN_SCIFB0_SCK_B, + FN_SCIFB2_SCK_B, FN_DREQ2_C, FN_HTX2_D, + /* IP3_21_20 [2] */ + FN_DACK0, FN_DRACK0, FN_REMOCON, 0, + /* IP3_19_18 [2] */ + FN_DREQ0, FN_PWM3, FN_TPU_TO3, 0, + /* IP3_17_16 [2] */ + FN_EX_WAIT0, FN_HRTS2_N_B, FN_SCIFB0_CTS_N_B, 0, + /* IP3_15_14 [2] */ + FN_WE1_N, FN_ATARD0_N_B, FN_HTX2_B, FN_SCIFB0_RTS_N_B, + /* IP3_13_12 [2] */ + FN_WE0_N, FN_HCTS2_N_B, FN_SCIFB0_TXD_B, 0, + /* IP3_11_9 [3] */ + FN_RD_WR_N, FN_HRX2_B, FN_FMIN_B, FN_SCIFB0_RXD_B, FN_DREQ1_D, + 0, 0, 0, + /* IP3_8_6 [3] */ + FN_BS_N, FN_ATACS10_N, FN_MSIOF2_SS2, FN_HTX1_B, + FN_SCIFB1_TXD_B, FN_PWM2, FN_TPU_TO2, 0, + /* IP3_5_3 [3] */ + FN_EX_CS5_N, FN_ATACS00_N, FN_MSIOF2_SS1, FN_HRX1_B, + FN_SCIFB1_RXD_B, FN_PWM1, FN_TPU_TO1, 0, + /* IP3_2_0 [3] */ + FN_EX_CS4_N, FN_ATARD0_N, FN_MSIOF2_RXD, 0, FN_EX_WAIT2, + 0, 0, 0, } + }, + { PINMUX_CFG_REG_VAR("IPSR4", 0xE6060030, 32, + 1, 3, 2, 2, 2, 1, 1, 1, 3, 3, 3, 2, 3, 3, 2) { + /* IP4_31 [1] */ + 0, 0, + /* IP4_30_28 [3] */ + FN_SSI_SCK5, FN_MSIOF1_SCK_C, FN_TS_SDATA0, FN_GLO_I0, + FN_MSIOF2_SYNC_D, FN_VI1_R2_B, + 0, 0, + /* IP4_27_26 [2] */ + FN_SSI_SDATA4, FN_MSIOF2_SCK_D, 0, 0, + /* IP4_25_24 [2] */ + FN_SSI_WS4, FN_GLO_RFON_D, 0, 0, + /* IP4_23_22 [2] */ + FN_SSI_SCK4, FN_GLO_SS_D, 0, 0, + /* IP4_21 [1] */ + FN_SSI_SDATA3, 0, + /* IP4_20 [1] */ + FN_SSI_WS34, 0, + /* IP4_19 [1] */ + FN_SSI_SCK34, 0, + /* IP4_18_16 [3] */ + FN_SSI_SDATA2, FN_GPS_MAG_B, FN_TX2_E, FN_HRTS1_N_E, + 0, 0, 0, 0, + /* IP4_15_13 [3] */ + FN_SSI_WS2, FN_SDA2, FN_GPS_SIGN_B, FN_RX2_E, + FN_GLO_Q1_D, FN_HCTS1_N_E, + 0, 0, + /* IP4_12_10 [3] */ + FN_SSI_SCK2, FN_SCL2, FN_GPS_CLK_B, FN_GLO_Q0_D, FN_HSCK1_E, + 0, 0, 0, + /* IP4_9_8 [2] */ + FN_SSI_SDATA1, FN_SDA1_B, FN_SDA8_B, FN_MSIOF2_RXD_C, + /* IP4_7_5 [3] */ + FN_SSI_WS1, FN_SCL1_B, FN_SCL8_B, FN_MSIOF2_TXD_C, FN_GLO_I1_D, + 0, 0, 0, + /* IP4_4_2 [3] */ + FN_SSI_SCK1, FN_SDA0_B, FN_SDA7_B, + FN_MSIOF2_SYNC_C, FN_GLO_I0_D, + 0, 0, 0, + /* IP4_1_0 [2] */ + FN_SSI_SDATA0, FN_SCL0_B, FN_SCL7_B, FN_MSIOF2_SCK_C, } + }, + { PINMUX_CFG_REG_VAR("IPSR5", 0xE6060034, 32, + 3, 3, 2, 2, 2, 3, 2, 3, 3, 3, 3, 3) { + /* IP5_31_29 [3] */ + FN_SSI_SDATA9, FN_RX3_D, FN_CAN0_RX_D, + 0, 0, 0, 0, 0, + /* IP5_28_26 [3] */ + FN_SSI_WS9, FN_TX3_D, FN_CAN0_TX_D, FN_GLO_SDATA_D, + 0, 0, 0, 0, + /* IP5_25_24 [2] */ + FN_SSI_SCK9, FN_RX1_D, FN_GLO_SCLK_D, 0, + /* IP5_23_22 [2] */ + FN_SSI_SDATA8, FN_TX1_D, FN_STP_ISSYNC_0_B, 0, + /* IP5_21_20 [2] */ + FN_SSI_SDATA7, FN_RX0_D, FN_STP_ISEN_0_B, 0, + /* IP5_19_17 [3] */ + FN_SSI_WS78, FN_TX0_D, FN_STP_ISD_0_B, FN_GLO_RFON, + 0, 0, 0, 0, + /* IP5_16_15 [2] */ + FN_SSI_SCK78, FN_STP_ISCLK_0_B, FN_GLO_SS, 0, + /* IP5_14_12 [3] */ + FN_SSI_SDATA6, FN_STP_IVCXO27_0_B, FN_GLO_SDATA, FN_VI1_R7_B, + 0, 0, 0, 0, + /* IP5_11_9 [3] */ + FN_SSI_WS6, FN_GLO_SCLK, FN_MSIOF2_SS2_D, FN_VI1_R6_B, + 0, 0, 0, 0, + /* IP5_8_6 [3] */ + FN_SSI_SCK6, FN_MSIOF1_RXD_C, FN_TS_SPSYNC0, FN_GLO_Q1, + FN_MSIOF2_RXD_D, FN_VI1_R5_B, + 0, 0, + /* IP5_5_3 [3] */ + FN_SSI_SDATA5, FN_MSIOF1_TXD_C, FN_TS_SDEN0, FN_GLO_Q0, + FN_MSIOF2_SS1_D, FN_VI1_R4_B, + 0, 0, + /* IP5_2_0 [3] */ + FN_SSI_WS5, FN_MSIOF1_SYNC_C, FN_TS_SCK0, FN_GLO_I1, + FN_MSIOF2_TXD_D, FN_VI1_R3_B, + 0, 0, } + }, + { PINMUX_CFG_REG_VAR("IPSR6", 0xE6060038, 32, + 2, 3, 3, 3, 2, 3, 2, 2, 2, 2, 2, 3, 3) { + /* IP6_31_30 [2] */ + 0, 0, 0, 0, + /* IP6_29_27 [3] */ + FN_IRQ8, FN_HRTS1_N_C, FN_MSIOF1_RXD_B, + FN_GPS_SIGN_C, FN_GPS_SIGN_D, + 0, 0, 0, + /* IP6_26_24 [3] */ + FN_IRQ7, FN_HCTS1_N_C, FN_MSIOF1_TXD_B, + FN_GPS_CLK_C, FN_GPS_CLK_D, + 0, 0, 0, + /* IP6_23_21 [3] */ + FN_IRQ6, FN_HSCK1_C, FN_MSIOF1_SS2_B, + FN_SDA1_E, FN_MSIOF2_SYNC_E, + 0, 0, 0, + /* IP6_20_19 [2] */ + FN_IRQ5, FN_HTX1_C, FN_SCL1_E, FN_MSIOF2_SCK_E, + /* IP6_18_16 [3] */ + FN_IRQ4, FN_HRX1_C, FN_SDA4_C, FN_MSIOF2_RXD_E, FN_INTC_IRQ4_N, + 0, 0, 0, + /* IP6_15_14 [2] */ + FN_IRQ3, FN_SCL4_C, FN_MSIOF2_TXD_E, FN_INTC_IRQ3_N, + /* IP6_13_12 [2] */ + FN_IRQ2, FN_SCIFB1_TXD_D, FN_INTC_IRQ2_N, 0, + /* IP6_11_10 [2] */ + FN_IRQ1, FN_SCIFB1_SCK_C, FN_INTC_IRQ1_N, 0, + /* IP6_9_8 [2] */ + FN_IRQ0, FN_SCIFB1_RXD_D, FN_INTC_IRQ0_N, 0, + /* IP6_7_6 [2] */ + FN_AUDIO_CLKOUT, FN_MSIOF1_SS1_B, FN_TX2, FN_SCIFA2_TXD, + /* IP6_5_3 [3] */ + FN_AUDIO_CLKC, FN_SCIFB0_SCK_C, FN_MSIOF1_SYNC_B, FN_RX2, + FN_SCIFA2_RXD, FN_FMIN_E, + 0, 0, + /* IP6_2_0 [3] */ + FN_AUDIO_CLKB, FN_STP_OPWM_0_B, FN_MSIOF1_SCK_B, + FN_SCIF_CLK, 0, FN_BPFCLK_E, + 0, 0, } + }, + { PINMUX_CFG_REG_VAR("IPSR7", 0xE606003C, 32, + 2, 3, 3, 3, 2, 2, 2, 2, 2, 2, 3, 3, 3) { + /* IP7_31_30 [2] */ + 0, 0, 0, 0, + /* IP7_29_27 [3] */ + FN_DU1_DG2, FN_LCDOUT10, FN_VI1_DATA4_B, FN_SCIF1_SCK_B, + FN_SCIFA1_SCK, FN_SSI_SCK78_B, + 0, 0, + /* IP7_26_24 [3] */ + FN_DU1_DG1, FN_LCDOUT9, FN_VI1_DATA3_B, FN_RX1_B, + FN_SCIFA1_RXD_B, FN_MSIOF2_SS2_B, + 0, 0, + /* IP7_23_21 [3] */ + FN_DU1_DG0, FN_LCDOUT8, FN_VI1_DATA2_B, FN_TX1_B, + FN_SCIFA1_TXD_B, FN_MSIOF2_SS1_B, + 0, 0, + /* IP7_20_19 [2] */ + FN_DU1_DR7, FN_LCDOUT7, FN_SSI_SDATA1_B, 0, + /* IP7_18_17 [2] */ + FN_DU1_DR6, FN_LCDOUT6, FN_SSI_WS1_B, 0, + /* IP7_16_15 [2] */ + FN_DU1_DR5, FN_LCDOUT5, FN_SSI_SCK1_B, 0, + /* IP7_14_13 [2] */ + FN_DU1_DR4, FN_LCDOUT4, FN_SSI_SDATA0_B, 0, + /* IP7_12_11 [2] */ + FN_DU1_DR3, FN_LCDOUT3, FN_SSI_WS0129_B, 0, + /* IP7_10_9 [2] */ + FN_DU1_DR2, FN_LCDOUT2, FN_SSI_SCK0129_B, 0, + /* IP7_8_6 [3] */ + FN_DU1_DR1, FN_LCDOUT1, FN_VI1_DATA1_B, FN_RX0_B, + FN_SCIFA0_RXD_B, FN_MSIOF2_SYNC_B, + 0, 0, + /* IP7_5_3 [3] */ + FN_DU1_DR0, FN_LCDOUT0, FN_VI1_DATA0_B, FN_TX0_B, + FN_SCIFA0_TXD_B, FN_MSIOF2_SCK_B, + 0, 0, + /* IP7_2_0 [3] */ + FN_IRQ9, FN_DU1_DOTCLKIN_B, FN_CAN_CLK_D, FN_GPS_MAG_C, + FN_SCIF_CLK_B, FN_GPS_MAG_D, + 0, 0, } + }, + { PINMUX_CFG_REG_VAR("IPSR8", 0xE6060040, 32, + 1, 3, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3) { + /* IP8_31 [1] */ + 0, 0, + /* IP8_30_28 [3] */ + FN_DU1_DB5, FN_LCDOUT21, FN_TX3, FN_SCIFA3_TXD, FN_CAN1_TX, + 0, 0, 0, + /* IP8_27_26 [2] */ + FN_DU1_DB4, FN_LCDOUT20, FN_VI1_FIELD_B, FN_CAN1_RX, + /* IP8_25_24 [2] */ + FN_DU1_DB3, FN_LCDOUT19, FN_VI1_CLKENB_B, 0, + /* IP8_23_21 [3] */ + FN_DU1_DB2, FN_LCDOUT18, FN_VI1_VSYNC_N_B, FN_SCIF2_SCK_B, + FN_SCIFA2_SCK, FN_SSI_SDATA9_B, + 0, 0, + /* IP8_20_18 [3] */ + FN_DU1_DB1, FN_LCDOUT17, FN_VI1_HSYNC_N_B, FN_RX2_B, + FN_SCIFA2_RXD_B, FN_MSIOF2_RXD_B, + 0, 0, + /* IP8_17_15 [3] */ + FN_DU1_DB0, FN_LCDOUT16, FN_VI1_CLK_B, FN_TX2_B, + FN_SCIFA2_TXD_B, FN_MSIOF2_TXD_B, + 0, 0, + /* IP8_14_12 [3] */ + FN_DU1_DG7, FN_LCDOUT15, FN_HTX0_B, + FN_SCIFB2_RTS_N_B, FN_SSI_WS9_B, + 0, 0, 0, + /* IP8_11_9 [3] */ + FN_DU1_DG6, FN_LCDOUT14, FN_HRTS0_N_B, + FN_SCIFB2_CTS_N_B, FN_SSI_SCK9_B, + 0, 0, 0, + /* IP8_8_6 [3] */ + FN_DU1_DG5, FN_LCDOUT13, FN_VI1_DATA7_B, FN_HCTS0_N_B, + FN_SCIFB2_TXD_B, FN_SSI_SDATA8_B, + 0, 0, + /* IP8_5_3 [3] */ + FN_DU1_DG4, FN_LCDOUT12, FN_VI1_DATA6_B, FN_HRX0_B, + FN_SCIFB2_RXD_B, FN_SSI_SDATA7_B, + 0, 0, + /* IP8_2_0 [3] */ + FN_DU1_DG3, FN_LCDOUT11, FN_VI1_DATA5_B, 0, FN_SSI_WS78_B, + 0, 0, 0, } + }, + { PINMUX_CFG_REG_VAR("IPSR9", 0xE6060044, 32, + 3, 2, 2, 2, 2, 2, 2, 1, 3, 1, 1, 3, 1, 1, 3, 3) { + /* IP9_31_29 [3] */ + FN_VI0_G0, FN_SCL8, FN_STP_IVCXO27_0_C, FN_SCL4, + FN_HCTS2_N, FN_SCIFB2_CTS_N, FN_ATAWR1_N, 0, + /* IP9_28_27 [2] */ + FN_VI0_DATA3_VI0_B3, FN_SCIF3_SCK_B, FN_SCIFA3_SCK_B, 0, + /* IP9_26_25 [2] */ + FN_VI0_VSYNC_N, FN_RX5, FN_SCIFA5_RXD, FN_TS_SPSYNC0_D, + /* IP9_24_23 [2] */ + FN_VI0_HSYNC_N, FN_TX5, FN_SCIFA5_TXD, FN_TS_SDEN0_D, + /* IP9_22_21 [2] */ + FN_VI0_FIELD, FN_RX4, FN_SCIFA4_RXD, FN_TS_SCK0_D, + /* IP9_20_19 [2] */ + FN_VI0_CLKENB, FN_TX4, FN_SCIFA4_TXD, FN_TS_SDATA0_D, + /* IP9_18_17 [2] */ + FN_DU1_CDE, FN_QPOLB, FN_PWM4_B, 0, + /* IP9_16 [1] */ + FN_DU1_DISP, FN_QPOLA, + /* IP9_15_13 [3] */ + FN_DU1_EXODDF_DU1_ODDF_DISP_CDE, FN_QCPV_QDE, + FN_CAN0_RX, FN_RX3_B, FN_SDA2_B, + 0, 0, 0, + /* IP9_12 [1] */ + FN_DU1_EXVSYNC_DU1_VSYNC, FN_QSTB_QHE, + /* IP9_11 [1] */ + FN_DU1_EXHSYNC_DU1_HSYNC, FN_QSTH_QHS, + /* IP9_10_8 [3] */ + FN_DU1_DOTCLKOUT1, FN_QSTVB_QVE, FN_CAN0_TX, + FN_TX3_B, FN_SCL2_B, FN_PWM4, + 0, 0, + /* IP9_7 [1] */ + FN_DU1_DOTCLKOUT0, FN_QCLK, + /* IP9_6 [1] */ + FN_DU1_DOTCLKIN, FN_QSTVA_QVS, + /* IP9_5_3 [3] */ + FN_DU1_DB7, FN_LCDOUT23, FN_SDA3_C, + FN_SCIF3_SCK, FN_SCIFA3_SCK, + 0, 0, 0, + /* IP9_2_0 [3] */ + FN_DU1_DB6, FN_LCDOUT22, FN_SCL3_C, FN_RX3, FN_SCIFA3_RXD, + 0, 0, 0, } + }, + { PINMUX_CFG_REG_VAR("IPSR10", 0xE6060048, 32, + 3, 2, 2, 3, 3, 2, 2, 3, 3, 3, 3, 3) { + /* IP10_31_29 [3] */ + FN_VI0_R4, FN_VI2_DATA5, FN_GLO_SCLK_B, FN_TX0_C, FN_SCL1_D, + 0, 0, 0, + /* IP10_28_27 [2] */ + FN_VI0_R3, FN_VI2_DATA4, FN_GLO_Q1_B, FN_TS_SPSYNC0_C, + /* IP10_26_25 [2] */ + FN_VI0_R2, FN_VI2_DATA3, FN_GLO_Q0_B, FN_TS_SDEN0_C, + /* IP10_24_22 [3] */ + FN_VI0_R1, FN_VI2_DATA2, FN_GLO_I1_B, FN_TS_SCK0_C, FN_ATAG1_N, + 0, 0, 0, + /* IP10_21_29 [3] */ + FN_VI0_R0, FN_VI2_DATA1, FN_GLO_I0_B, + FN_TS_SDATA0_C, FN_ATACS11_N, + 0, 0, 0, + /* IP10_18_17 [2] */ + FN_VI0_G7, FN_VI2_DATA0, FN_FMIN_D, 0, + /* IP10_16_15 [2] */ + FN_VI0_G6, FN_VI2_CLK, FN_BPFCLK_D, 0, + /* IP10_14_12 [3] */ + FN_VI0_G5, FN_VI2_FIELD, FN_STP_OPWM_0_C, FN_FMCLK_D, + FN_CAN0_TX_E, FN_HTX1_D, FN_SCIFB0_TXD_D, 0, + /* IP10_11_9 [3] */ + FN_VI0_G4, FN_VI2_CLKENB, FN_STP_ISSYNC_0_C, + FN_HTX2, FN_SCIFB2_TXD, FN_SCIFB0_SCK_D, + 0, 0, + /* IP10_8_6 [3] */ + FN_VI0_G3, FN_VI2_VSYNC_N, FN_STP_ISEN_0_C, FN_SDA3_B, + FN_HRX2, FN_SCIFB2_RXD, FN_ATACS01_N, 0, + /* IP10_5_3 [3] */ + FN_VI0_G2, FN_VI2_HSYNC_N, FN_STP_ISD_0_C, FN_SCL3_B, + FN_HSCK2, FN_SCIFB2_SCK, FN_ATARD1_N, 0, + /* IP10_2_0 [3] */ + FN_VI0_G1, FN_SDA8, FN_STP_ISCLK_0_C, FN_SDA4, + FN_HRTS2_N, FN_SCIFB2_RTS_N, FN_ATADIR1_N, 0, } + }, + { PINMUX_CFG_REG_VAR("IPSR11", 0xE606004C, 32, + 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, + 3, 3, 3, 3, 3) { + /* IP11_31_30 [2] */ + FN_ETH_CRS_DV, FN_AVB_LINK, FN_SDA2_C, 0, + /* IP11_29_28 [2] */ + FN_ETH_MDIO, FN_AVB_RX_CLK, FN_SCL2_C, 0, + /* IP11_27 [1] */ + FN_VI1_DATA7, FN_AVB_MDC, + /* IP11_26 [1] */ + FN_VI1_DATA6, FN_AVB_MAGIC, + /* IP11_25 [1] */ + FN_VI1_DATA5, FN_AVB_RX_DV, + /* IP11_24 [1] */ + FN_VI1_DATA4, FN_AVB_MDIO, + /* IP11_23 [1] */ + FN_VI1_DATA3, FN_AVB_RX_ER, + /* IP11_22 [1] */ + FN_VI1_DATA2, FN_AVB_RXD7, + /* IP11_21 [1] */ + FN_VI1_DATA1, FN_AVB_RXD6, + /* IP11_20 [1] */ + FN_VI1_DATA0, FN_AVB_RXD5, + /* IP11_19 [1] */ + FN_VI1_CLK, FN_AVB_RXD4, + /* IP11_18_17 [2] */ + FN_VI1_FIELD, FN_AVB_RXD3, FN_TS_SPSYNC0_B, 0, + /* IP11_16_15 [2] */ + FN_VI1_CLKENB, FN_AVB_RXD2, FN_TS_SDEN0_B, 0, + /* IP11_14_12 [3] */ + FN_VI1_VSYNC_N, FN_AVB_RXD1, FN_TS_SCK0_B, + FN_RX4_B, FN_SCIFA4_RXD_B, + 0, 0, 0, + /* IP11_11_9 [3] */ + FN_VI1_HSYNC_N, FN_AVB_RXD0, FN_TS_SDATA0_B, + FN_TX4_B, FN_SCIFA4_TXD_B, + 0, 0, 0, + /* IP11_8_6 [3] */ + FN_VI0_R7, FN_GLO_RFON_B, FN_RX1_C, FN_CAN0_RX_E, + FN_SDA4_B, FN_HRX1_D, FN_SCIFB0_RXD_D, 0, + /* IP11_5_3 [3] */ + FN_VI0_R6, FN_VI2_DATA7, FN_GLO_SS_B, FN_TX1_C, FN_SCL4_B, + 0, 0, 0, + /* IP11_2_0 [3] */ + FN_VI0_R5, FN_VI2_DATA6, FN_GLO_SDATA_B, FN_RX0_C, FN_SDA1_D, + 0, 0, 0, } + }, + { PINMUX_CFG_REG_VAR("IPSR12", 0xE6060050, 32, + 2, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2) { + /* IP12_31_30 [2] */ + 0, 0, 0, 0, + /* IP12_29_27 [3] */ + FN_STP_ISCLK_0, FN_AVB_TX_EN, FN_SCIFB2_RXD_D, + FN_ADICS_SAMP_B, FN_MSIOF0_SCK_C, + 0, 0, 0, + /* IP12_26_24 [3] */ + FN_STP_IVCXO27_0, FN_AVB_TXD7, FN_SCIFB2_TXD_D, + FN_ADIDATA_B, FN_MSIOF0_SYNC_C, + 0, 0, 0, + /* IP12_23_22 [2] */ + FN_ETH_MDC, FN_AVB_TXD6, FN_IERX_C, 0, + /* IP12_21_20 [2] */ + FN_ETH_TXD0, FN_AVB_TXD5, FN_IECLK_C, 0, + /* IP12_19_18 [2] */ + FN_ETH_MAGIC, FN_AVB_TXD4, FN_IETX_C, 0, + /* IP12_17_16 [2] */ + FN_ETH_TX_EN, FN_AVB_TXD3, FN_TCLK1_B, FN_CAN_CLK_B, + /* IP12_15_13 [3] */ + FN_ETH_TXD1, FN_AVB_TXD2, FN_SCIFA3_TXD_B, + FN_CAN1_TX_C, FN_MSIOF1_TXD_E, + 0, 0, 0, + /* IP12_12_10 [3] */ + FN_ETH_REFCLK, FN_AVB_TXD1, FN_SCIFA3_RXD_B, + FN_CAN1_RX_C, FN_MSIOF1_SYNC_E, + 0, 0, 0, + /* IP12_9_7 [3] */ + FN_ETH_LINK, FN_AVB_TXD0, FN_CAN0_RX_C, + FN_SDA2_D, FN_MSIOF1_SCK_E, + 0, 0, 0, + /* IP12_6_4 [3] */ + FN_ETH_RXD1, FN_AVB_GTXREFCLK, FN_CAN0_TX_C, + FN_SCL2_D, FN_MSIOF1_RXD_E, + 0, 0, 0, + /* IP12_3_2 [2] */ + FN_ETH_RXD0, FN_AVB_PHY_INT, FN_SDA3, FN_SDA7, + /* IP12_1_0 [2] */ + FN_ETH_RX_ER, FN_AVB_CRS, FN_SCL3, FN_SCL7, } + }, + { PINMUX_CFG_REG_VAR("IPSR13", 0xE6060054, 32, + 1, 3, 1, 1, 1, 2, 1, 3, 3, 1, 1, 1, 1, 1, 1, + 3, 2, 2, 3) { + /* IP13_31 [1] */ + 0, 0, + /* IP13_30_28 [3] */ + FN_SD1_CD, FN_PWM0, FN_TPU_TO0, FN_SCL1_C, + 0, 0, 0, 0, + /* IP13_27 [1] */ + FN_SD1_DATA3, FN_IERX_B, + /* IP13_26 [1] */ + FN_SD1_DATA2, FN_IECLK_B, + /* IP13_25 [1] */ + FN_SD1_DATA1, FN_IETX_B, + /* IP13_24_23 [2] */ + FN_SD1_DATA0, FN_SPEEDIN_B, 0, 0, + /* IP13_22 [1] */ + FN_SD1_CMD, FN_REMOCON_B, + /* IP13_21_19 [3] */ + FN_SD0_WP, FN_MMC_D7_B, FN_SIM0_D_B, FN_CAN0_TX_F, + FN_SCIFA5_RXD_B, FN_RX3_C, + 0, 0, + /* IP13_18_16 [3] */ + FN_SD0_CD, FN_MMC_D6_B, FN_SIM0_RST_B, FN_CAN0_RX_F, + FN_SCIFA5_TXD_B, FN_TX3_C, + 0, 0, + /* IP13_15 [1] */ + FN_SD0_DATA3, FN_SSL_B, + /* IP13_14 [1] */ + FN_SD0_DATA2, FN_IO3_B, + /* IP13_13 [1] */ + FN_SD0_DATA1, FN_IO2_B, + /* IP13_12 [1] */ + FN_SD0_DATA0, FN_MISO_IO1_B, + /* IP13_11 [1] */ + FN_SD0_CMD, FN_MOSI_IO0_B, + /* IP13_10 [1] */ + FN_SD0_CLK, FN_SPCLK_B, + /* IP13_9_7 [3] */ + FN_STP_OPWM_0, FN_AVB_GTX_CLK, FN_PWM0_B, + FN_ADICHS2_B, FN_MSIOF0_TXD_C, + 0, 0, 0, + /* IP13_6_5 [2] */ + FN_STP_ISSYNC_0, FN_AVB_COL, FN_ADICHS1_B, FN_MSIOF0_RXD_C, + /* IP13_4_3 [2] */ + FN_STP_ISEN_0, FN_AVB_TX_CLK, FN_ADICHS0_B, FN_MSIOF0_SS2_C, + /* IP13_2_0 [3] */ + FN_STP_ISD_0, FN_AVB_TX_ER, FN_SCIFB2_SCK_C, + FN_ADICLK_B, FN_MSIOF0_SS1_C, + 0, 0, 0, } + }, + { PINMUX_CFG_REG_VAR("IPSR14", 0xE6060058, 32, + 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 2) { + /* IP14_31_29 [3] */ + FN_MSIOF0_SS2, FN_MMC_D7, FN_ADICHS2, FN_RX0_E, + FN_VI1_VSYNC_N_C, FN_SDA7_C, FN_VI1_G5_B, 0, + /* IP14_28_26 [3] */ + FN_MSIOF0_SS1, FN_MMC_D6, FN_ADICHS1, FN_TX0_E, + FN_VI1_HSYNC_N_C, FN_SCL7_C, FN_VI1_G4_B, 0, + /* IP14_25_23 [3] */ + FN_MSIOF0_RXD, FN_ADICHS0, 0, FN_VI1_DATA0_C, FN_VI1_G3_B, + 0, 0, 0, + /* IP14_22_20 [3] */ + FN_MSIOF0_TXD, FN_ADICLK, 0, FN_VI1_FIELD_C, FN_VI1_G2_B, + 0, 0, 0, + /* IP14_19_17 [3] */ + FN_MSIOF0_SYNC, FN_TX2_C, FN_ADICS_SAMP, 0, + FN_VI1_CLKENB_C, FN_VI1_G1_B, + 0, 0, + /* IP14_16_14 [3] */ + FN_MSIOF0_SCK, FN_RX2_C, FN_ADIDATA, 0, + FN_VI1_CLK_C, FN_VI1_G0_B, + 0, 0, + /* IP14_13_11 [3] */ + FN_SD2_WP, FN_MMC_D5, FN_SDA8_C, FN_RX5_B, FN_SCIFA5_RXD_C, + 0, 0, 0, + /* IP14_10_8 [3] */ + FN_SD2_CD, FN_MMC_D4, FN_SCL8_C, FN_TX5_B, FN_SCIFA5_TXD_C, + 0, 0, 0, + /* IP14_7 [1] */ + FN_SD2_DATA3, FN_MMC_D3, + /* IP14_6 [1] */ + FN_SD2_DATA2, FN_MMC_D2, + /* IP14_5 [1] */ + FN_SD2_DATA1, FN_MMC_D1, + /* IP14_4 [1] */ + FN_SD2_DATA0, FN_MMC_D0, + /* IP14_3 [1] */ + FN_SD2_CMD, FN_MMC_CMD, + /* IP14_2 [1] */ + FN_SD2_CLK, FN_MMC_CLK, + /* IP14_1_0 [2] */ + FN_SD1_WP, FN_PWM1_B, FN_SDA1_C, 0, } + }, + { PINMUX_CFG_REG_VAR("IPSR15", 0xE606005C, 32, + 2, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2) { + /* IP15_31_30 [2] */ + 0, 0, 0, 0, + /* IP15_29_27 [3] */ + FN_HTX0, FN_SCIFB0_TXD, 0, FN_GLO_SCLK_C, + FN_CAN0_TX_B, FN_VI1_DATA5_C, + 0, 0, + /* IP15_26_24 [3] */ + FN_HRX0, FN_SCIFB0_RXD, 0, FN_GLO_Q1_C, + FN_CAN0_RX_B, FN_VI1_DATA4_C, + 0, 0, + /* IP15_23_21 [3] */ + FN_HSCK0, FN_SCIFB0_SCK, 0, FN_GLO_Q0_C, FN_CAN_CLK, + FN_TCLK2, FN_VI1_DATA3_C, 0, + /* IP15_20_18 [3] */ + FN_HRTS0_N, FN_SCIFB0_RTS_N, 0, FN_GLO_I1_C, FN_VI1_DATA2_C, + 0, 0, 0, + /* IP15_17_15 [3] */ + FN_HCTS0_N, FN_SCIFB0_CTS_N, 0, FN_GLO_I0_C, + FN_TCLK1, FN_VI1_DATA1_C, + 0, 0, + /* IP15_14_12 [3] */ + FN_GPS_MAG, FN_RX4_C, FN_SCIFA4_RXD_C, FN_PWM6, + FN_VI1_G7_B, FN_SCIFA3_SCK_C, + 0, 0, + /* IP15_11_9 [3] */ + FN_GPS_SIGN, FN_TX4_C, FN_SCIFA4_TXD_C, FN_PWM5, + FN_VI1_G6_B, FN_SCIFA3_RXD_C, + 0, 0, + /* IP15_8_6 [3] */ + FN_GPS_CLK, FN_DU1_DOTCLKIN_C, FN_AUDIO_CLKB_B, + FN_PWM5_B, FN_SCIFA3_TXD_C, + 0, 0, 0, + /* IP15_5_4 [2] */ + FN_SIM0_D, FN_IERX, FN_CAN1_RX_D, 0, + /* IP15_3_2 [2] */ + FN_SIM0_CLK, FN_IECLK, FN_CAN_CLK_C, 0, + /* IP15_1_0 [2] */ + FN_SIM0_RST, FN_IETX, FN_CAN1_TX_D, 0, } + }, + { PINMUX_CFG_REG_VAR("IPSR16", 0xE6060160, 32, + 4, 4, 4, 4, 4, 2, 2, 2, 3, 3) { + /* IP16_31_28 [4] */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + /* IP16_27_24 [4] */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + /* IP16_23_20 [4] */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + /* IP16_19_16 [4] */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + /* IP16_15_12 [4] */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + /* IP16_11_10 [2] */ + FN_HRTS1_N, FN_SCIFB1_RTS_N, FN_MLB_DAT, FN_CAN1_RX_B, + /* IP16_9_8 [2] */ + FN_HCTS1_N, FN_SCIFB1_CTS_N, FN_MLB_SIG, FN_CAN1_TX_B, + /* IP16_7_6 [2] */ + FN_HSCK1, FN_SCIFB1_SCK, FN_MLB_CK, FN_GLO_RFON_C, + /* IP16_5_3 [3] */ + FN_HTX1, FN_SCIFB1_TXD, FN_VI1_R1_B, + FN_GLO_SS_C, FN_VI1_DATA7_C, + 0, 0, 0, + /* IP16_2_0 [3] */ + FN_HRX1, FN_SCIFB1_RXD, FN_VI1_R0_B, + FN_GLO_SDATA_C, FN_VI1_DATA6_C, + 0, 0, 0, } + }, + { PINMUX_CFG_REG_VAR("MOD_SEL", 0xE6060090, 32, + 1, 2, 2, 2, 3, 2, 1, 1, 1, 1, + 3, 2, 2, 2, 1, 2, 2, 2) { + /* RESEVED [1] */ + 0, 0, + /* SEL_SCIF1 [2] */ + FN_SEL_SCIF1_0, FN_SEL_SCIF1_1, FN_SEL_SCIF1_2, FN_SEL_SCIF1_3, + /* SEL_SCIFB [2] */ + FN_SEL_SCIFB_0, FN_SEL_SCIFB_1, FN_SEL_SCIFB_2, FN_SEL_SCIFB_3, + /* SEL_SCIFB2 [2] */ + FN_SEL_SCIFB2_0, FN_SEL_SCIFB2_1, + FN_SEL_SCIFB2_2, FN_SEL_SCIFB2_3, + /* SEL_SCIFB1 [3] */ + FN_SEL_SCIFB1_0, FN_SEL_SCIFB1_1, + FN_SEL_SCIFB1_2, FN_SEL_SCIFB1_3, + 0, 0, 0, 0, + /* SEL_SCIFA1 [2] */ + FN_SEL_SCIFA1_0, FN_SEL_SCIFA1_1, FN_SEL_SCIFA1_2, 0, + /* SEL_SSI9 [1] */ + FN_SEL_SSI9_0, FN_SEL_SSI9_1, + /* SEL_SCFA [1] */ + FN_SEL_SCFA_0, FN_SEL_SCFA_1, + /* SEL_QSP [1] */ + FN_SEL_QSP_0, FN_SEL_QSP_1, + /* SEL_SSI7 [1] */ + FN_SEL_SSI7_0, FN_SEL_SSI7_1, + /* SEL_HSCIF1 [3] */ + FN_SEL_HSCIF1_0, FN_SEL_HSCIF1_1, FN_SEL_HSCIF1_2, + FN_SEL_HSCIF1_3, FN_SEL_HSCIF1_4, + 0, 0, 0, + /* RESEVED [2] */ + 0, 0, 0, 0, + /* SEL_VI1 [2] */ + FN_SEL_VI1_0, FN_SEL_VI1_1, FN_SEL_VI1_2, 0, + /* RESEVED [2] */ + 0, 0, 0, 0, + /* SEL_TMU [1] */ + FN_SEL_TMU1_0, FN_SEL_TMU1_1, + /* SEL_LBS [2] */ + FN_SEL_LBS_0, FN_SEL_LBS_1, FN_SEL_LBS_2, FN_SEL_LBS_3, + /* SEL_TSIF0 [2] */ + FN_SEL_TSIF0_0, FN_SEL_TSIF0_1, FN_SEL_TSIF0_2, FN_SEL_TSIF0_3, + /* SEL_SOF0 [2] */ + FN_SEL_SOF0_0, FN_SEL_SOF0_1, FN_SEL_SOF0_2, 0, } + }, + { PINMUX_CFG_REG_VAR("MOD_SEL2", 0xE6060094, 32, + 3, 1, 1, 3, 2, 1, 1, 2, 2, + 1, 3, 2, 1, 2, 2, 2, 1, 1, 1) { + /* SEL_SCIF0 [3] */ + FN_SEL_SCIF0_0, FN_SEL_SCIF0_1, FN_SEL_SCIF0_2, + FN_SEL_SCIF0_3, FN_SEL_SCIF0_4, + 0, 0, 0, + /* RESEVED [1] */ + 0, 0, + /* SEL_SCIF [1] */ + FN_SEL_SCIF_0, FN_SEL_SCIF_1, + /* SEL_CAN0 [3] */ + FN_SEL_CAN0_0, FN_SEL_CAN0_1, FN_SEL_CAN0_2, FN_SEL_CAN0_3, + FN_SEL_CAN0_4, FN_SEL_CAN0_5, + 0, 0, + /* SEL_CAN1 [2] */ + FN_SEL_CAN1_0, FN_SEL_CAN1_1, FN_SEL_CAN1_2, FN_SEL_CAN1_3, + /* RESEVED [1] */ + 0, 0, + /* SEL_SCIFA2 [1] */ + FN_SEL_SCIFA2_0, FN_SEL_SCIFA2_1, + /* SEL_SCIF4 [2] */ + FN_SEL_SCIF4_0, FN_SEL_SCIF4_1, FN_SEL_SCIF4_2, 0, + /* RESEVED [2] */ + 0, 0, 0, 0, + /* SEL_ADG [1] */ + FN_SEL_ADG_0, FN_SEL_ADG_1, + /* SEL_FM [3] */ + FN_SEL_FM_0, FN_SEL_FM_1, FN_SEL_FM_2, + FN_SEL_FM_3, FN_SEL_FM_4, + 0, 0, 0, + /* SEL_SCIFA5 [2] */ + FN_SEL_SCIFA5_0, FN_SEL_SCIFA5_1, FN_SEL_SCIFA5_2, 0, + /* RESEVED [1] */ + 0, 0, + /* SEL_GPS [2] */ + FN_SEL_GPS_0, FN_SEL_GPS_1, FN_SEL_GPS_2, FN_SEL_GPS_3, + /* SEL_SCIFA4 [2] */ + FN_SEL_SCIFA4_0, FN_SEL_SCIFA4_1, FN_SEL_SCIFA4_2, 0, + /* SEL_SCIFA3 [2] */ + FN_SEL_SCIFA3_0, FN_SEL_SCIFA3_1, FN_SEL_SCIFA3_2, 0, + /* SEL_SIM [1] */ + FN_SEL_SIM_0, FN_SEL_SIM_1, + /* RESEVED [1] */ + 0, 0, + /* SEL_SSI8 [1] */ + FN_SEL_SSI8_0, FN_SEL_SSI8_1, } + }, + { PINMUX_CFG_REG_VAR("MOD_SEL3", 0xE6060098, 32, + 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 2, 2, 3, 2, 2, 2, 1) { + /* SEL_HSCIF2 [2] */ + FN_SEL_HSCIF2_0, FN_SEL_HSCIF2_1, + FN_SEL_HSCIF2_2, FN_SEL_HSCIF2_3, + /* SEL_CANCLK [2] */ + FN_SEL_CANCLK_0, FN_SEL_CANCLK_1, + FN_SEL_CANCLK_2, FN_SEL_CANCLK_3, + /* SEL_IIC8 [2] */ + FN_SEL_IIC8_0, FN_SEL_IIC8_1, FN_SEL_IIC8_2, 0, + /* SEL_IIC7 [2] */ + FN_SEL_IIC7_0, FN_SEL_IIC7_1, FN_SEL_IIC7_2, 0, + /* SEL_IIC4 [2] */ + FN_SEL_IIC4_0, FN_SEL_IIC4_1, FN_SEL_IIC4_2, 0, + /* SEL_IIC3 [2] */ + FN_SEL_IIC3_0, FN_SEL_IIC3_1, FN_SEL_IIC3_2, FN_SEL_IIC3_3, + /* SEL_SCIF3 [2] */ + FN_SEL_SCIF3_0, FN_SEL_SCIF3_1, FN_SEL_SCIF3_2, FN_SEL_SCIF3_3, + /* SEL_IEB [2] */ + FN_SEL_IEB_0, FN_SEL_IEB_1, FN_SEL_IEB_2, + /* SEL_MMC [1] */ + FN_SEL_MMC_0, FN_SEL_MMC_1, + /* SEL_SCIF5 [1] */ + FN_SEL_SCIF5_0, FN_SEL_SCIF5_1, + /* RESEVED [2] */ + 0, 0, 0, 0, + /* SEL_IIC2 [2] */ + FN_SEL_IIC2_0, FN_SEL_IIC2_1, FN_SEL_IIC2_2, FN_SEL_IIC2_3, + /* SEL_IIC1 [3] */ + FN_SEL_IIC1_0, FN_SEL_IIC1_1, FN_SEL_IIC1_2, FN_SEL_IIC1_3, + FN_SEL_IIC1_4, + 0, 0, 0, + /* SEL_IIC0 [2] */ + FN_SEL_IIC0_0, FN_SEL_IIC0_1, FN_SEL_IIC0_2, 0, + /* RESEVED [2] */ + 0, 0, 0, 0, + /* RESEVED [2] */ + 0, 0, 0, 0, + /* RESEVED [1] */ + 0, 0, } + }, + { PINMUX_CFG_REG_VAR("MOD_SEL4", 0xE606009C, 32, + 3, 2, 2, 1, 1, 1, 1, 3, 2, + 2, 3, 1, 1, 1, 2, 2, 2, 2) { + /* SEL_SOF1 [3] */ + FN_SEL_SOF1_0, FN_SEL_SOF1_1, FN_SEL_SOF1_2, FN_SEL_SOF1_3, + FN_SEL_SOF1_4, + 0, 0, 0, + /* SEL_HSCIF0 [2] */ + FN_SEL_HSCIF0_0, FN_SEL_HSCIF0_1, FN_SEL_HSCIF0_2, 0, + /* SEL_DIS [2] */ + FN_SEL_DIS_0, FN_SEL_DIS_1, FN_SEL_DIS_2, 0, + /* RESEVED [1] */ + 0, 0, + /* SEL_RAD [1] */ + FN_SEL_RAD_0, FN_SEL_RAD_1, + /* SEL_RCN [1] */ + FN_SEL_RCN_0, FN_SEL_RCN_1, + /* SEL_RSP [1] */ + FN_SEL_RSP_0, FN_SEL_RSP_1, + /* SEL_SCIF2 [3] */ + FN_SEL_SCIF2_0, FN_SEL_SCIF2_1, FN_SEL_SCIF2_2, + FN_SEL_SCIF2_3, FN_SEL_SCIF2_4, + 0, 0, 0, + /* RESEVED [2] */ + 0, 0, 0, 0, + /* RESEVED [2] */ + 0, 0, 0, 0, + /* SEL_SOF2 [3] */ + FN_SEL_SOF2_0, FN_SEL_SOF2_1, FN_SEL_SOF2_2, + FN_SEL_SOF2_3, FN_SEL_SOF2_4, + 0, 0, 0, + /* RESEVED [1] */ + 0, 0, + /* SEL_SSI1 [1] */ + FN_SEL_SSI1_0, FN_SEL_SSI1_1, + /* SEL_SSI0 [1] */ + FN_SEL_SSI0_0, FN_SEL_SSI0_1, + /* SEL_SSP [2] */ + FN_SEL_SSP_0, FN_SEL_SSP_1, FN_SEL_SSP_2, 0, + /* RESEVED [2] */ + 0, 0, 0, 0, + /* RESEVED [2] */ + 0, 0, 0, 0, + /* RESEVED [2] */ + 0, 0, 0, 0, } + }, + { }, +}; + +const struct sh_pfc_soc_info r8a7791_pinmux_info = { + .name = "r8a77910_pfc", + .unlock_reg = 0xe6060000, /* PMMR */ + + .function = { PINMUX_FUNCTION_BEGIN, PINMUX_FUNCTION_END }, + + .pins = pinmux_pins, + .nr_pins = ARRAY_SIZE(pinmux_pins), + .groups = pinmux_groups, + .nr_groups = ARRAY_SIZE(pinmux_groups), + .functions = pinmux_functions, + .nr_functions = ARRAY_SIZE(pinmux_functions), + + .cfg_regs = pinmux_config_regs, + + .gpio_data = pinmux_data, + .gpio_data_size = ARRAY_SIZE(pinmux_data), +}; -- cgit v0.10.2 From bbfe65c219c638e19f1da5adab1005b2d68ca810 Mon Sep 17 00:00:00 2001 From: Thomas Pfaff Date: Fri, 11 Oct 2013 13:00:40 +0200 Subject: genirq: Set the irq thread policy without checking CAP_SYS_NICE In commit ee23871389 ("genirq: Set irq thread to RT priority on creation") we moved the assigment of the thread's priority from the thread's function into __setup_irq(). That function may run in user context for instance if the user opens an UART node and then driver calls requests in the ->open() callback. That user may not have CAP_SYS_NICE and so the irq thread won't run with the SCHED_OTHER policy. This patch uses sched_setscheduler_nocheck() so we omit the CAP_SYS_NICE check which is otherwise required for the SCHED_OTHER policy. [bigeasy: Rewrite the changelog] Signed-off-by: Thomas Pfaff Cc: Ivo Sieben Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/r/1381489240-29626-1-git-send-email-bigeasy@linutronix.de Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Thomas Gleixner diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 514bcfd..3e59f95 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -956,7 +956,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) goto out_mput; } - sched_setscheduler(t, SCHED_FIFO, ¶m); + sched_setscheduler_nocheck(t, SCHED_FIFO, ¶m); /* * We keep the reference to the task struct even if -- cgit v0.10.2 From a655f75c7502e5000c8745f458d3dbb9777f4aca Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 25 Oct 2013 16:33:12 +0200 Subject: ALSA: pcm_dmaengine: Remove hardcoded PCM formats Use the standard PCM helper function to figure out the sample bytes instead of hardcodec PCM format checks in snd_hwparams_to_dma_slave_config(). The patch also extends the format check for 8 bytes formats although no one should match so far. Acked-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c index aa924d9..94d0873 100644 --- a/sound/core/pcm_dmaengine.c +++ b/sound/core/pcm_dmaengine.c @@ -63,23 +63,19 @@ int snd_hwparams_to_dma_slave_config(const struct snd_pcm_substream *substream, struct dma_slave_config *slave_config) { enum dma_slave_buswidth buswidth; + int bits; - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S8: + bits = snd_pcm_format_physical_width(params_format(params)); + if (bits < 8 || bits > 64) + return -EINVAL; + else if (bits == 8) buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE; - break; - case SNDRV_PCM_FORMAT_S16_LE: + else if (bits == 16) buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; - break; - case SNDRV_PCM_FORMAT_S18_3LE: - case SNDRV_PCM_FORMAT_S20_3LE: - case SNDRV_PCM_FORMAT_S24_LE: - case SNDRV_PCM_FORMAT_S32_LE: + else if (bits <= 32) buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; - break; - default: - return -EINVAL; - } + else + buswidth = DMA_SLAVE_BUSWIDTH_8_BYTES; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { slave_config->direction = DMA_MEM_TO_DEV; -- cgit v0.10.2 From ac9ff7997b6f2b31949dcd2495ac671fd9ddc990 Mon Sep 17 00:00:00 2001 From: Michael wang Date: Mon, 28 Oct 2013 10:50:22 +0800 Subject: sched: Remove extra put_online_cpus() inside sched_setaffinity() Commit 6acce3ef8: sched: Remove get_online_cpus() usage has left one extra put_online_cpus() inside sched_setaffinity(), remove it to fix the WARN: ------------[ cut here ]------------ WARNING: CPU: 0 PID: 3166 at kernel/cpu.c:84 put_online_cpus+0x43/0x70() ... [] put_online_cpus+0x43/0x70 [ [] sched_setaffinity+0x7d/0x1f9 [ ... Reported-by: Fengguang Wu Tested-by: Fengguang Wu Signed-off-by: Michael Wang Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/526DD0EE.1090309@linux.vnet.ibm.com Signed-off-by: Ingo Molnar diff --git a/kernel/sched/core.c b/kernel/sched/core.c index c06b8d3..7c61f31 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -3716,7 +3716,6 @@ long sched_setaffinity(pid_t pid, const struct cpumask *in_mask) p = find_process_by_pid(pid); if (!p) { rcu_read_unlock(); - put_online_cpus(); return -ESRCH; } -- cgit v0.10.2 From 63437313daaf90b372d5b383a8cc6ec8dce185fa Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 28 Oct 2013 16:08:27 +0100 Subject: ALSA: memalloc: Yet another ifdef CONFIG_GENERIC_ALLOCATOR protection I obviously forgot to merge the right version... Reported-by: Stephen Rothwell Signed-off-by: Takashi Iwai diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index 51a7921..809fd79 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -159,6 +159,7 @@ static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr, dma_free_coherent(dev, PAGE_SIZE << pg, ptr, dma); } +#ifdef CONFIG_GENERIC_ALLOCATOR /** * snd_malloc_dev_iram - allocate memory from on-chip internal ram * @dmab: buffer allocation record to store the allocated data @@ -198,6 +199,7 @@ void snd_free_dev_iram(struct snd_dma_buffer *dmab) if (pool && dmab->area) gen_pool_free(pool, (unsigned long)dmab->area, dmab->bytes); } +#endif /* CONFIG_GENERIC_ALLOCATOR */ #endif /* CONFIG_HAS_DMA */ /* -- cgit v0.10.2 From 72548e836b0c4abbb652e791dee9c91203a9a4c6 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Fri, 4 Oct 2013 09:36:56 +0100 Subject: x86/efi: Add EFI framebuffer earlyprintk support It's incredibly difficult to diagnose early EFI boot issues without special hardware because earlyprintk=vga doesn't work on EFI systems. Add support for writing to the EFI framebuffer, via earlyprintk=efi, which will actually give users a chance of providing debug output. Cc: H. Peter Anvin Acked-by: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Jones Signed-off-by: Matt Fleming diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 7f9d4f5..7a0f202 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -792,6 +792,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted. earlyprintk= [X86,SH,BLACKFIN,ARM] earlyprintk=vga + earlyprintk=efi earlyprintk=xen earlyprintk=serial[,ttySn[,baudrate]] earlyprintk=serial[,0x...[,baudrate]] @@ -805,7 +806,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted. Append ",keep" to not disable it when the real console takes over. - Only vga or serial or usb debug port at a time. + Only one of vga, efi, serial, or usb debug port can + be used at a time. Currently only ttyS0 and ttyS1 may be specified by name. Other I/O ports may be explicitly specified @@ -819,8 +821,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted. Interaction with the standard serial driver is not very good. - The VGA output is eventually overwritten by the real - console. + The VGA and EFI output is eventually overwritten by + the real console. The xen output can only be used by Xen PV guests. diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug index 78d91af..0f3621e 100644 --- a/arch/x86/Kconfig.debug +++ b/arch/x86/Kconfig.debug @@ -59,6 +59,16 @@ config EARLY_PRINTK_DBGP with klogd/syslogd or the X server. You should normally N here, unless you want to debug such a crash. You need usb debug device. +config EARLY_PRINTK_EFI + bool "Early printk via the EFI framebuffer" + depends on EFI && EARLY_PRINTK + select FONT_SUPPORT + ---help--- + Write kernel log output directly into the EFI framebuffer. + + This is useful for kernel debugging when your machine crashes very + early before the console code is initialized. + config X86_PTDUMP bool "Export kernel pagetable layout to userspace via debugfs" depends on DEBUG_KERNEL diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index 0062a01..65c6e6e 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -109,6 +109,8 @@ static inline bool efi_is_native(void) return IS_ENABLED(CONFIG_X86_64) == efi_enabled(EFI_64BIT); } +extern struct console early_efi_console; + #else /* * IF EFI is not configured, have the EFI calls return -ENOSYS. diff --git a/arch/x86/kernel/early_printk.c b/arch/x86/kernel/early_printk.c index d15f575..6d3d200 100644 --- a/arch/x86/kernel/early_printk.c +++ b/arch/x86/kernel/early_printk.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include /* Simple VGA output */ #define VGABASE (__ISA_IO_base + 0xb8000) @@ -234,6 +236,11 @@ static int __init setup_early_printk(char *buf) early_console_register(&early_hsu_console, keep); } #endif +#ifdef CONFIG_EARLY_PRINTK_EFI + if (!strncmp(buf, "efi", 3)) + early_console_register(&early_efi_console, keep); +#endif + buf++; } return 0; diff --git a/arch/x86/platform/efi/Makefile b/arch/x86/platform/efi/Makefile index 6db1cc4..b7b0b35 100644 --- a/arch/x86/platform/efi/Makefile +++ b/arch/x86/platform/efi/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_EFI) += efi.o efi_$(BITS).o efi_stub_$(BITS).o obj-$(CONFIG_ACPI_BGRT) += efi-bgrt.o +obj-$(CONFIG_EARLY_PRINTK_EFI) += early_printk.o diff --git a/arch/x86/platform/efi/early_printk.c b/arch/x86/platform/efi/early_printk.c new file mode 100644 index 0000000..6599a00 --- /dev/null +++ b/arch/x86/platform/efi/early_printk.c @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2013 Intel Corporation; author Matt Fleming + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include +#include + +static const struct font_desc *font; +static u32 efi_x, efi_y; + +static __init void early_efi_clear_scanline(unsigned int y) +{ + unsigned long base, *dst; + u16 len; + + base = boot_params.screen_info.lfb_base; + len = boot_params.screen_info.lfb_linelength; + + dst = early_ioremap(base + y*len, len); + if (!dst) + return; + + memset(dst, 0, len); + early_iounmap(dst, len); +} + +static __init void early_efi_scroll_up(void) +{ + unsigned long base, *dst, *src; + u16 len; + u32 i, height; + + base = boot_params.screen_info.lfb_base; + len = boot_params.screen_info.lfb_linelength; + height = boot_params.screen_info.lfb_height; + + for (i = 0; i < height - font->height; i++) { + dst = early_ioremap(base + i*len, len); + if (!dst) + return; + + src = early_ioremap(base + (i + font->height) * len, len); + if (!src) { + early_iounmap(dst, len); + return; + } + + memmove(dst, src, len); + + early_iounmap(src, len); + early_iounmap(dst, len); + } +} + +static void early_efi_write_char(u32 *dst, unsigned char c, unsigned int h) +{ + const u32 color_black = 0x00000000; + const u32 color_white = 0x00ffffff; + const u8 *src; + u8 s8; + int m; + + src = font->data + c * font->height; + s8 = *(src + h); + + for (m = 0; m < 8; m++) { + if ((s8 >> (7 - m)) & 1) + *dst = color_white; + else + *dst = color_black; + dst++; + } +} + +static __init void +early_efi_write(struct console *con, const char *str, unsigned int num) +{ + struct screen_info *si; + unsigned long base; + unsigned int len; + const char *s; + void *dst; + + base = boot_params.screen_info.lfb_base; + si = &boot_params.screen_info; + len = si->lfb_linelength; + + while (num) { + unsigned int linemax; + unsigned int h, count = 0; + + for (s = str; *s && *s != '\n'; s++) { + if (count == num) + break; + count++; + } + + linemax = (si->lfb_width - efi_x) / font->width; + if (count > linemax) + count = linemax; + + for (h = 0; h < font->height; h++) { + unsigned int n, x; + + dst = early_ioremap(base + (efi_y + h) * len, len); + if (!dst) + return; + + s = str; + n = count; + x = efi_x; + + while (n-- > 0) { + early_efi_write_char(dst + x*4, *s, h); + x += font->width; + s++; + } + + early_iounmap(dst, len); + } + + num -= count; + efi_x += count * font->width; + str += count; + + if (num > 0 && *s == '\n') { + efi_x = 0; + efi_y += font->height; + str++; + num--; + } + + if (efi_x >= si->lfb_width) { + efi_x = 0; + efi_y += font->height; + } + + if (efi_y + font->height >= si->lfb_height) { + u32 i; + + efi_y -= font->height; + early_efi_scroll_up(); + + for (i = 0; i < font->height; i++) + early_efi_clear_scanline(efi_y + i); + } + } +} + +static __init int early_efi_setup(struct console *con, char *options) +{ + struct screen_info *si; + u16 xres, yres; + u32 i; + + si = &boot_params.screen_info; + xres = si->lfb_width; + yres = si->lfb_height; + + /* + * early_efi_write_char() implicitly assumes a framebuffer with + * 32-bits per pixel. + */ + if (si->lfb_depth != 32) + return -ENODEV; + + font = get_default_font(xres, yres, -1, -1); + if (!font) + return -ENODEV; + + efi_y = rounddown(yres, font->height) - font->height; + for (i = 0; i < (yres - efi_y) / font->height; i++) + early_efi_scroll_up(); + + return 0; +} + +struct console early_efi_console = { + .name = "earlyefi", + .write = early_efi_write, + .setup = early_efi_setup, + .flags = CON_PRINTBUFFER, + .index = -1, +}; -- cgit v0.10.2 From a01779f89fc8a2225cb82dca0fc7b8451851cb7b Mon Sep 17 00:00:00 2001 From: Josh Cartwright Date: Mon, 28 Oct 2013 13:12:35 -0500 Subject: regmap: add SPMI support Add basic support for the System Power Management Interface (SPMI) bus. This is a simple implementation which only implements register accesses via the Extended Register Read/Write Long commands. Signed-off-by: Josh Cartwright Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig index f0d3054..4251570 100644 --- a/drivers/base/regmap/Kconfig +++ b/drivers/base/regmap/Kconfig @@ -3,7 +3,7 @@ # subsystems should select the appropriate symbols. config REGMAP - default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_MMIO || REGMAP_IRQ) + default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_MMIO || REGMAP_IRQ) select LZO_COMPRESS select LZO_DECOMPRESS select IRQ_DOMAIN if REGMAP_IRQ @@ -15,6 +15,9 @@ config REGMAP_I2C config REGMAP_SPI tristate +config REGMAP_SPMI + tristate + config REGMAP_MMIO tristate diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile index cf12998..a7c670b 100644 --- a/drivers/base/regmap/Makefile +++ b/drivers/base/regmap/Makefile @@ -3,5 +3,6 @@ obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o regcache-flat.o obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o +obj-$(CONFIG_REGMAP_SPMI) += regmap-spmi.o obj-$(CONFIG_REGMAP_MMIO) += regmap-mmio.o obj-$(CONFIG_REGMAP_IRQ) += regmap-irq.o diff --git a/drivers/base/regmap/regmap-spmi.c b/drivers/base/regmap/regmap-spmi.c new file mode 100644 index 0000000..ac23910 --- /dev/null +++ b/drivers/base/regmap/regmap-spmi.c @@ -0,0 +1,90 @@ +/* + * Register map access API - SPMI support + * + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * + * Based on regmap-i2c.c: + * Copyright 2011 Wolfson Microelectronics plc + * Author: Mark Brown + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include + +static int regmap_spmi_read(void *context, + const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + BUG_ON(reg_size != 2); + return spmi_ext_register_readl(context, *(u16 *)reg, + val, val_size); +} + +static int regmap_spmi_gather_write(void *context, + const void *reg, size_t reg_size, + const void *val, size_t val_size) +{ + BUG_ON(reg_size != 2); + return spmi_ext_register_writel(context, *(u16 *)reg, val, val_size); +} + +static int regmap_spmi_write(void *context, const void *data, + size_t count) +{ + BUG_ON(count < 2); + return regmap_spmi_gather_write(context, data, 2, data + 2, count - 2); +} + +static struct regmap_bus regmap_spmi = { + .read = regmap_spmi_read, + .write = regmap_spmi_write, + .gather_write = regmap_spmi_gather_write, + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, + .val_format_endian_default = REGMAP_ENDIAN_NATIVE, +}; + +/** + * regmap_init_spmi(): Initialize register map + * + * @sdev: Device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer to + * a struct regmap. + */ +struct regmap *regmap_init_spmi(struct spmi_device *sdev, + const struct regmap_config *config) +{ + return regmap_init(&sdev->dev, ®map_spmi, sdev, config); +} +EXPORT_SYMBOL_GPL(regmap_init_spmi); + +/** + * devm_regmap_init_spmi(): Initialise managed register map + * + * @sdev: Device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. The regmap will be automatically freed by the + * device management code. + */ +struct regmap *devm_regmap_init_spmi(struct spmi_device *sdev, + const struct regmap_config *config) +{ + return devm_regmap_init(&sdev->dev, ®map_spmi, sdev, config); +} +EXPORT_SYMBOL_GPL(devm_regmap_init_spmi); + +MODULE_LICENSE("GPL"); diff --git a/include/linux/regmap.h b/include/linux/regmap.h index a10380b..3f5abc8 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -23,6 +23,7 @@ struct device; struct i2c_client; struct irq_domain; struct spi_device; +struct spmi_device; struct regmap; struct regmap_range_cfg; struct regmap_field; @@ -318,6 +319,8 @@ struct regmap *regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config); struct regmap *regmap_init_spi(struct spi_device *dev, const struct regmap_config *config); +struct regmap *regmap_init_spmi(struct spmi_device *dev, + const struct regmap_config *config); struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id, void __iomem *regs, const struct regmap_config *config); @@ -330,6 +333,8 @@ struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config); struct regmap *devm_regmap_init_spi(struct spi_device *dev, const struct regmap_config *config); +struct regmap *devm_regmap_init_spmi(struct spmi_device *dev, + const struct regmap_config *config); struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id, void __iomem *regs, const struct regmap_config *config); -- cgit v0.10.2 From 79d9701559a9f3e9b2021fbd292f5e70ad75f686 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 19 Sep 2013 16:47:37 -0500 Subject: of/irq: create interrupts-extended property The standard interrupts property in device tree can only handle interrupts coming from a single interrupt parent. If a device is wired to multiple interrupt controllers, then it needs to be attached to a node with an interrupt-map property to demux the interrupt specifiers which is confusing. It would be a lot easier if there was a form of the interrupts property that allows for a separate interrupt phandle for each interrupt specifier. This patch does exactly that by creating a new interrupts-extended property which reuses the phandle+arguments pattern used by GPIOs and other core bindings. Signed-off-by: Grant Likely Acked-by: Tony Lindgren Acked-by: Kumar Gala [grant.likely: removed versatile platform hunks into separate patch] Cc: Rob Herring diff --git a/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt b/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt index 72a06c0..1486497 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt @@ -4,16 +4,33 @@ Specifying interrupt information for devices 1) Interrupt client nodes ------------------------- -Nodes that describe devices which generate interrupts must contain an -"interrupts" property. This property must contain a list of interrupt -specifiers, one per output interrupt. The format of the interrupt specifier is -determined by the interrupt controller to which the interrupts are routed; see -section 2 below for details. +Nodes that describe devices which generate interrupts must contain an either an +"interrupts" property or an "interrupts-extended" property. These properties +contain a list of interrupt specifiers, one per output interrupt. The format of +the interrupt specifier is determined by the interrupt controller to which the +interrupts are routed; see section 2 below for details. + + Example: + interrupt-parent = <&intc1>; + interrupts = <5 0>, <6 0>; The "interrupt-parent" property is used to specify the controller to which interrupts are routed and contains a single phandle referring to the interrupt controller node. This property is inherited, so it may be specified in an -interrupt client node or in any of its parent nodes. +interrupt client node or in any of its parent nodes. Interrupts listed in the +"interrupts" property are always in reference to the node's interrupt parent. + +The "interrupts-extended" property is a special form for use when a node needs +to reference multiple interrupt parents. Each entry in this property contains +both the parent phandle and the interrupt specifier. "interrupts-extended" +should only be used when a device has multiple interrupt parents. + + Example: + interrupts-extended = <&intc1 5 1>, <&intc2 1 0>; + +A device node may contain either "interrupts" or "interrupts-extended", but not +both. If both properties are present, then the operating system should log an +error and use only the data in "interrupts". 2) Interrupt controller nodes ----------------------------- diff --git a/arch/arm/boot/dts/testcases/tests-interrupts.dtsi b/arch/arm/boot/dts/testcases/tests-interrupts.dtsi index 6ecda71..560d6bf 100644 --- a/arch/arm/boot/dts/testcases/tests-interrupts.dtsi +++ b/arch/arm/boot/dts/testcases/tests-interrupts.dtsi @@ -27,6 +27,12 @@ <4 &test_intc2 15 16>; }; + test_intmap1: intmap1 { + #interrupt-cells = <2>; + #address-cells = <0>; + interrupt-map = <1 2 &test_intc0 15>; + }; + interrupts0 { interrupt-parent = <&test_intc0>; interrupts = <1>, <2>, <3>, <4>; @@ -36,6 +42,16 @@ interrupt-parent = <&test_intmap0>; interrupts = <1>, <2>, <3>, <4>; }; + + interrupts-extended0 { + interrupts-extended = <&test_intc0 1>, + <&test_intc1 2 3 4>, + <&test_intc2 5 6>, + <&test_intmap0 1>, + <&test_intmap0 2>, + <&test_intmap0 3>, + <&test_intmap1 1 2>; + }; }; }; }; diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 7c4ff12..8cc62b4 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -292,17 +292,23 @@ int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_ar if (of_irq_workarounds & OF_IMAP_OLDWORLD_MAC) return of_irq_parse_oldworld(device, index, out_irq); + /* Get the reg property (if any) */ + addr = of_get_property(device, "reg", NULL); + /* Get the interrupts property */ intspec = of_get_property(device, "interrupts", &intlen); - if (intspec == NULL) - return -EINVAL; + if (intspec == NULL) { + /* Try the new-style interrupts-extended */ + res = of_parse_phandle_with_args(device, "interrupts-extended", + "#interrupt-cells", index, out_irq); + if (res) + return -EINVAL; + return of_irq_parse_raw(addr, out_irq); + } intlen /= sizeof(*intspec); pr_debug(" intspec=%d intlen=%d\n", be32_to_cpup(intspec), intlen); - /* Get the reg property (if any) */ - addr = of_get_property(device, "reg", NULL); - /* Look for the interrupt parent. */ p = of_irq_find_parent(device); if (p == NULL) diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c index 9c80f0b..e21012b 100644 --- a/drivers/of/selftest.c +++ b/drivers/of/selftest.c @@ -231,6 +231,75 @@ static void __init of_selftest_parse_interrupts(void) of_node_put(np); } +static void __init of_selftest_parse_interrupts_extended(void) +{ + struct device_node *np; + struct of_phandle_args args; + int i, rc; + + np = of_find_node_by_path("/testcase-data/interrupts/interrupts-extended0"); + if (!np) { + pr_err("missing testcase data\n"); + return; + } + + for (i = 0; i < 7; i++) { + bool passed = true; + rc = of_irq_parse_one(np, i, &args); + + /* Test the values from tests-phandle.dtsi */ + switch (i) { + case 0: + passed &= !rc; + passed &= (args.args_count == 1); + passed &= (args.args[0] == 1); + break; + case 1: + passed &= !rc; + passed &= (args.args_count == 3); + passed &= (args.args[0] == 2); + passed &= (args.args[1] == 3); + passed &= (args.args[2] == 4); + break; + case 2: + passed &= !rc; + passed &= (args.args_count == 2); + passed &= (args.args[0] == 5); + passed &= (args.args[1] == 6); + break; + case 3: + passed &= !rc; + passed &= (args.args_count == 1); + passed &= (args.args[0] == 9); + break; + case 4: + passed &= !rc; + passed &= (args.args_count == 3); + passed &= (args.args[0] == 10); + passed &= (args.args[1] == 11); + passed &= (args.args[2] == 12); + break; + case 5: + passed &= !rc; + passed &= (args.args_count == 2); + passed &= (args.args[0] == 13); + passed &= (args.args[1] == 14); + break; + case 6: + passed &= !rc; + passed &= (args.args_count == 1); + passed &= (args.args[0] == 15); + break; + default: + passed = false; + } + + selftest(passed, "index %i - data error on node %s rc=%i\n", + i, args.np->full_name, rc); + } + of_node_put(np); +} + static int __init of_selftest(void) { struct device_node *np; @@ -246,6 +315,7 @@ static int __init of_selftest(void) of_selftest_parse_phandle_with_args(); of_selftest_property_match_string(); of_selftest_parse_interrupts(); + of_selftest_parse_interrupts_extended(); pr_info("end of selftest - %i passed, %i failed\n", selftest_results.passed, selftest_results.failed); return 0; -- cgit v0.10.2 From 0976c946a610d06e907335b7a3afa6db046f8e1b Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Mon, 28 Oct 2013 16:50:11 -0700 Subject: arm/versatile: Fix versatile irq specifications. Two of the versatile irq definitions are incorrect, mostly because two devices have connections to more than one interrupt controller. Fix them by using the new interrupts-extended property to fan out without using an awful interrupt-map nexus node. Signed-off-by: Grant Likely diff --git a/arch/arm/boot/dts/versatile-ab.dts b/arch/arm/boot/dts/versatile-ab.dts index dde75ae..e01e5a0 100644 --- a/arch/arm/boot/dts/versatile-ab.dts +++ b/arch/arm/boot/dts/versatile-ab.dts @@ -185,7 +185,7 @@ mmc@5000 { compatible = "arm,primecell"; reg = < 0x5000 0x1000>; - interrupts = <22 34>; + interrupts-extended = <&vic 22 &sic 2>; }; kmi@6000 { compatible = "arm,pl050", "arm,primecell"; diff --git a/arch/arm/boot/dts/versatile-pb.dts b/arch/arm/boot/dts/versatile-pb.dts index 7e81752..f43907c 100644 --- a/arch/arm/boot/dts/versatile-pb.dts +++ b/arch/arm/boot/dts/versatile-pb.dts @@ -41,7 +41,7 @@ mmc@b000 { compatible = "arm,primecell"; reg = <0xb000 0x1000>; - interrupts = <23 34>; + interrupts-extended = <&vic 23 &sic 2>; }; }; }; -- cgit v0.10.2 From 0e3d4373b8a7757a8f5187f5cabafb6aceff469b Mon Sep 17 00:00:00 2001 From: Minghuan Lian Date: Wed, 31 Jul 2013 10:59:07 +0800 Subject: powerpc/dts: fix sRIO error interrupt for b4860 For B4 platform, MPIC EISR register is in reversed bitmap order, instead of "Error interrupt source 0-31. Bit 0 represents SRC0." the correct ordering is "Error interrupt source 0-31. Bit 0 represents SRC31." This patch is to fix sRIO EISR bit value of error interrupt in dts node. Signed-off-by: Minghuan Lian Signed-off-by: Scott Wood diff --git a/arch/powerpc/boot/dts/fsl/b4860si-post.dtsi b/arch/powerpc/boot/dts/fsl/b4860si-post.dtsi index e5cf6c8..9813975 100644 --- a/arch/powerpc/boot/dts/fsl/b4860si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/b4860si-post.dtsi @@ -41,7 +41,7 @@ &rio { compatible = "fsl,srio"; - interrupts = <16 2 1 11>; + interrupts = <16 2 1 20>; #address-cells = <2>; #size-cells = <2>; fsl,iommu-parent = <&pamu0>; -- cgit v0.10.2 From bbd234b146a158ae59da0592c1598336db63b0fc Mon Sep 17 00:00:00 2001 From: Chunhe Lan Date: Fri, 2 Aug 2013 16:46:25 +0800 Subject: powerpc/pci: Change the DECLARE_PCI_FIXUP_{HEADER => EARLY} macro of pci quirk Freescale platform has class code = 0x0b2000, when it boots. This makes kernel PCI bus code to setup these devices resulting into the following notice information when trying to enable them: pci 0000:00:00.0: ignoring class 0x0b2000 (doesn't match header type 01) The above information is outputted by judging value of dev->class before pci_setup_device() function, and the DECLARE_PCI_FIXUP_HEADER quirk runs after pci_setup_device() function. But the DECLARE_PCI_FIXUP_EARLY quirk runs before judging value of dev->class and pci_setup_device() function. So we use the DECLARE_PCI_FIXUP_EARLY macro to fix this issue. Signed-off-by: Chunhe Lan Cc: Benjamin Herrenschmidt Cc: Bjorn Helgaas Cc: Paul Mackerras Signed-off-by: Scott Wood diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c index ccfb50d..2103963 100644 --- a/arch/powerpc/sysdev/fsl_pci.c +++ b/arch/powerpc/sysdev/fsl_pci.c @@ -40,7 +40,7 @@ static int fsl_pcie_bus_fixup, is_mpc83xx_pci; -static void quirk_fsl_pcie_header(struct pci_dev *dev) +static void quirk_fsl_pcie_early(struct pci_dev *dev) { u8 hdr_type; @@ -562,7 +562,8 @@ no_bridge: } #endif /* CONFIG_FSL_SOC_BOOKE || CONFIG_PPC_86xx */ -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_FREESCALE, PCI_ANY_ID, quirk_fsl_pcie_header); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_FREESCALE, PCI_ANY_ID, + quirk_fsl_pcie_early); #if defined(CONFIG_PPC_83xx) || defined(CONFIG_PPC_MPC512x) struct mpc83xx_pcie_priv { -- cgit v0.10.2 From 4f6d45d7db7e4622a74b9fd8c99b13c68129d70d Mon Sep 17 00:00:00 2001 From: Lijun Pan Date: Thu, 24 Oct 2013 16:11:13 -0500 Subject: powerpc/e6500: Include Power ISA properties b4420 and b4860 device trees do not have these properties. Signed-off-by: Lijun Pan Signed-off-by: Scott Wood diff --git a/arch/powerpc/boot/dts/fsl/b4420si-pre.dtsi b/arch/powerpc/boot/dts/fsl/b4420si-pre.dtsi index 7b4426e..c6e451a 100644 --- a/arch/powerpc/boot/dts/fsl/b4420si-pre.dtsi +++ b/arch/powerpc/boot/dts/fsl/b4420si-pre.dtsi @@ -34,6 +34,8 @@ /dts-v1/; +/include/ "e6500_power_isa.dtsi" + / { compatible = "fsl,B4420"; #address-cells = <2>; diff --git a/arch/powerpc/boot/dts/fsl/b4860si-pre.dtsi b/arch/powerpc/boot/dts/fsl/b4860si-pre.dtsi index 5263fa4..9bc26b1 100644 --- a/arch/powerpc/boot/dts/fsl/b4860si-pre.dtsi +++ b/arch/powerpc/boot/dts/fsl/b4860si-pre.dtsi @@ -34,6 +34,8 @@ /dts-v1/; +/include/ "e6500_power_isa.dtsi" + / { compatible = "fsl,B4860"; #address-cells = <2>; -- cgit v0.10.2 From f4b123886d2cdd196a0e3482de48d921443790c7 Mon Sep 17 00:00:00 2001 From: Lijun Pan Date: Thu, 24 Oct 2013 16:03:25 -0500 Subject: powerpc/e500v2: Include Power ISA properties bsc9131 device tree does not have these properties. Signed-off-by: Lijun Pan Signed-off-by: Scott Wood diff --git a/arch/powerpc/boot/dts/fsl/bsc9131si-pre.dtsi b/arch/powerpc/boot/dts/fsl/bsc9131si-pre.dtsi index 743e4ae..f6ec4a6 100644 --- a/arch/powerpc/boot/dts/fsl/bsc9131si-pre.dtsi +++ b/arch/powerpc/boot/dts/fsl/bsc9131si-pre.dtsi @@ -33,6 +33,9 @@ */ /dts-v1/; + +/include/ "e500v2_power_isa.dtsi" + / { compatible = "fsl,BSC9131"; #address-cells = <2>; -- cgit v0.10.2 From a0b38b4e7858c76e7e53510f24d6bf22cab144a8 Mon Sep 17 00:00:00 2001 From: Suzuki Poulose Date: Tue, 27 Aug 2013 13:22:14 +0530 Subject: powerpc: Set the NOTE type for SPE regset The regset defintion for SPE doesn't have the core_note_type set, which prevents it from being dumped. Add the note type NT_PPC_SPE for SPE regset. Signed-off-by: Suzuki K Poulose Cc: Roland McGrath Signed-off-by: Scott Wood diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index 01be88b..75fb404 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c @@ -658,7 +658,7 @@ static const struct user_regset native_regsets[] = { #endif #ifdef CONFIG_SPE [REGSET_SPE] = { - .n = 35, + .core_note_type = NT_PPC_SPE, .n = 35, .size = sizeof(u32), .align = sizeof(u32), .active = evr_active, .get = evr_get, .set = evr_set }, -- cgit v0.10.2 From 1eb2819d69c818784cab0d4a89a35e36bd753e57 Mon Sep 17 00:00:00 2001 From: LEROY Christophe Date: Wed, 28 Aug 2013 16:19:17 +0200 Subject: powerpc/mpc8xx: Clearer Oops message for Software Emulation Exception This patch modifies the Oops message in case of Software Emulation Exception. The existing message is quite confusing because it refers to FPU Emulation while most often the issue is due to either a non supported instruction (not necessarily FPU related) or a stale instruction due to HW issues. The new message tries to be more generic in order to make the user understand that the Oops is due to something wrong with an instruction, not necessarily due to an FPU instruction. Signed-off-by: Christophe Leroy Signed-off-by: Scott Wood diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index f686686..ad20dcf 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -1468,7 +1468,8 @@ void SoftwareEmulation(struct pt_regs *regs) if (!user_mode(regs)) { debugger(regs); - die("Kernel Mode Software FPU Emulation", regs, SIGFPE); + die("Kernel Mode Unimplemented Instruction or SW FPU Emulation", + regs, SIGFPE); } if (!emulate_math(regs)) -- cgit v0.10.2 From 0f06ad70921eb00fa48f73754059fd8854f32572 Mon Sep 17 00:00:00 2001 From: "Haijun.Zhang" Date: Thu, 29 Aug 2013 11:31:28 +0800 Subject: powerpc/eSDCH: Specify voltage for T4240QDS Freescale T4240QDS reference board has extra voltage shifters added to allow 3.3V operation, so add 3.3v voltage support for T4240QDS. 1.8v and 3.3v is recommand for eMMC and SDHC card. Signed-off-by: Haijun Zhang Signed-off-by: Scott Wood diff --git a/arch/powerpc/boot/dts/t4240qds.dts b/arch/powerpc/boot/dts/t4240qds.dts index 0555976..eb3c3de 100644 --- a/arch/powerpc/boot/dts/t4240qds.dts +++ b/arch/powerpc/boot/dts/t4240qds.dts @@ -148,6 +148,10 @@ interrupts = <0x1 0x1 0 0>; }; }; + + sdhc@114000 { + voltage-ranges = <1800 1800 3300 3300>; + }; }; pci0: pcie@ffe240000 { -- cgit v0.10.2 From 3169ab00e0727ea2891483e636bd8e6aa11521a1 Mon Sep 17 00:00:00 2001 From: "Haijun.Zhang" Date: Mon, 2 Sep 2013 18:37:02 +0800 Subject: powerpc/dts: Correct sdhci quirk for bsc9131 We use property "sdhci,auto-cmd12" instead of "fsl,sdhci-auto-cmd12" to distinguish if the sdhc host has quirk SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12. Signed-off-by: Haijun Zhang Signed-off-by: Scott Wood diff --git a/arch/powerpc/boot/dts/fsl/bsc9131si-post.dtsi b/arch/powerpc/boot/dts/fsl/bsc9131si-post.dtsi index 5180d9d..0c0efa9 100644 --- a/arch/powerpc/boot/dts/fsl/bsc9131si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/bsc9131si-post.dtsi @@ -130,7 +130,7 @@ usb@22000 { /include/ "pq3-esdhc-0.dtsi" sdhc@2e000 { - fsl,sdhci-auto-cmd12; + sdhci,auto-cmd12; interrupts = <41 0x2 0 0>; }; -- cgit v0.10.2 From 788d399d3c3dd1e02ab316272e1dc1881182e16d Mon Sep 17 00:00:00 2001 From: Shengzhou Liu Date: Tue, 3 Sep 2013 16:28:26 +0800 Subject: powerpc/fsl/defconfig: enable CONFIG_AT803X_PHY Enable CONFIG_AT803X_PHY to support AR8030/8033/8035 PHY. Signed-off-by: Shengzhou Liu Signed-off-by: Scott Wood diff --git a/arch/powerpc/configs/corenet32_smp_defconfig b/arch/powerpc/configs/corenet32_smp_defconfig index 3dfab4c..71bc43a 100644 --- a/arch/powerpc/configs/corenet32_smp_defconfig +++ b/arch/powerpc/configs/corenet32_smp_defconfig @@ -104,6 +104,7 @@ CONFIG_FSL_PQ_MDIO=y CONFIG_E1000=y CONFIG_E1000E=y CONFIG_VITESSE_PHY=y +CONFIG_AT803X_PHY=y CONFIG_FIXED_PHY=y # CONFIG_INPUT_MOUSEDEV is not set # CONFIG_INPUT_KEYBOARD is not set diff --git a/arch/powerpc/configs/mpc85xx_defconfig b/arch/powerpc/configs/mpc85xx_defconfig index dc098d9..d2e0fab 100644 --- a/arch/powerpc/configs/mpc85xx_defconfig +++ b/arch/powerpc/configs/mpc85xx_defconfig @@ -138,6 +138,7 @@ CONFIG_MARVELL_PHY=y CONFIG_DAVICOM_PHY=y CONFIG_CICADA_PHY=y CONFIG_VITESSE_PHY=y +CONFIG_AT803X_PHY=y CONFIG_FIXED_PHY=y CONFIG_INPUT_FF_MEMLESS=m # CONFIG_INPUT_MOUSEDEV is not set diff --git a/arch/powerpc/configs/mpc85xx_smp_defconfig b/arch/powerpc/configs/mpc85xx_smp_defconfig index 5bca601..4cb7b59 100644 --- a/arch/powerpc/configs/mpc85xx_smp_defconfig +++ b/arch/powerpc/configs/mpc85xx_smp_defconfig @@ -138,6 +138,7 @@ CONFIG_MARVELL_PHY=y CONFIG_DAVICOM_PHY=y CONFIG_CICADA_PHY=y CONFIG_VITESSE_PHY=y +CONFIG_AT803X_PHY=y CONFIG_FIXED_PHY=y CONFIG_INPUT_FF_MEMLESS=m # CONFIG_INPUT_MOUSEDEV is not set -- cgit v0.10.2 From afc4b47372ace24c630c1d79b3a0ed32bf1d19fd Mon Sep 17 00:00:00 2001 From: Hongtao Jia Date: Mon, 9 Sep 2013 18:03:33 +0800 Subject: powerpc: Add I2C bus multiplexer node for B4 and T4240QDS In both B4 and T4240QDS platform PCA9547 I2C bus multiplexer is used. The sub-nodes are also reorganized according to right I2C topology. Signed-off-by: Jia Hongtao Signed-off-by: Scott Wood diff --git a/arch/powerpc/boot/dts/b4qds.dtsi b/arch/powerpc/boot/dts/b4qds.dtsi index e6d2f8f..8b47edc 100644 --- a/arch/powerpc/boot/dts/b4qds.dtsi +++ b/arch/powerpc/boot/dts/b4qds.dtsi @@ -120,25 +120,38 @@ }; i2c@118000 { - eeprom@50 { - compatible = "at24,24c64"; - reg = <0x50>; - }; - eeprom@51 { - compatible = "at24,24c256"; - reg = <0x51>; - }; - eeprom@53 { - compatible = "at24,24c256"; - reg = <0x53>; - }; - eeprom@57 { - compatible = "at24,24c256"; - reg = <0x57>; - }; - rtc@68 { - compatible = "dallas,ds3232"; - reg = <0x68>; + mux@77 { + compatible = "nxp,pca9547"; + reg = <0x77>; + #address-cells = <1>; + #size-cells = <0>; + + i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + eeprom@50 { + compatible = "at24,24c64"; + reg = <0x50>; + }; + eeprom@51 { + compatible = "at24,24c256"; + reg = <0x51>; + }; + eeprom@53 { + compatible = "at24,24c256"; + reg = <0x53>; + }; + eeprom@57 { + compatible = "at24,24c256"; + reg = <0x57>; + }; + rtc@68 { + compatible = "dallas,ds3232"; + reg = <0x68>; + }; + }; }; }; diff --git a/arch/powerpc/boot/dts/t4240qds.dts b/arch/powerpc/boot/dts/t4240qds.dts index eb3c3de..63e81b0 100644 --- a/arch/powerpc/boot/dts/t4240qds.dts +++ b/arch/powerpc/boot/dts/t4240qds.dts @@ -118,34 +118,47 @@ }; i2c@118000 { - eeprom@51 { - compatible = "at24,24c256"; - reg = <0x51>; - }; - eeprom@52 { - compatible = "at24,24c256"; - reg = <0x52>; - }; - eeprom@53 { - compatible = "at24,24c256"; - reg = <0x53>; - }; - eeprom@54 { - compatible = "at24,24c256"; - reg = <0x54>; - }; - eeprom@55 { - compatible = "at24,24c256"; - reg = <0x55>; - }; - eeprom@56 { - compatible = "at24,24c256"; - reg = <0x56>; - }; - rtc@68 { - compatible = "dallas,ds3232"; - reg = <0x68>; - interrupts = <0x1 0x1 0 0>; + mux@77 { + compatible = "nxp,pca9547"; + reg = <0x77>; + #address-cells = <1>; + #size-cells = <0>; + + i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + eeprom@51 { + compatible = "at24,24c256"; + reg = <0x51>; + }; + eeprom@52 { + compatible = "at24,24c256"; + reg = <0x52>; + }; + eeprom@53 { + compatible = "at24,24c256"; + reg = <0x53>; + }; + eeprom@54 { + compatible = "at24,24c256"; + reg = <0x54>; + }; + eeprom@55 { + compatible = "at24,24c256"; + reg = <0x55>; + }; + eeprom@56 { + compatible = "at24,24c256"; + reg = <0x56>; + }; + rtc@68 { + compatible = "dallas,ds3232"; + reg = <0x68>; + interrupts = <0x1 0x1 0 0>; + }; + }; }; }; -- cgit v0.10.2 From 79df1b374ba681f1322a0efd9a88bb85f1462796 Mon Sep 17 00:00:00 2001 From: LEROY Christophe Date: Wed, 11 Sep 2013 18:44:44 +0200 Subject: powerpc/8xx: Revert commit e0908085fc2391c85b85fb814ae1df377c8e0dcb The commit e0908085fc2391c85b85fb814ae1df377c8e0dcb ("powerpc/8xx: Fix regression introduced by cache coherency rewrite") is not needed anymore. The issue was because dcbst wrongly sets the store bit when causing a DTLB error, but this is now fixed by commit 0a2ab51ffb8dfdf51402dcfb446629648c96bc78 ("powerpc/8xx: Fixup DAR from buggy dcbX instructions.") which handles the buggy dcbx instructions on data page faults on the 8xx. Signed-off-by: Christophe Leroy [scottwood@freescale.com: fix commit message] Signed-off-by: Scott Wood diff --git a/arch/powerpc/mm/pgtable.c b/arch/powerpc/mm/pgtable.c index edda589..841e0d0 100644 --- a/arch/powerpc/mm/pgtable.c +++ b/arch/powerpc/mm/pgtable.c @@ -32,8 +32,6 @@ #include #include -#include "mmu_decl.h" - static inline int is_exec_fault(void) { return current->thread.regs && TRAP(current->thread.regs) == 0x400; @@ -72,7 +70,7 @@ struct page * maybe_pte_to_page(pte_t pte) * support falls into the same category. */ -static pte_t set_pte_filter(pte_t pte, unsigned long addr) +static pte_t set_pte_filter(pte_t pte) { pte = __pte(pte_val(pte) & ~_PAGE_HPTEFLAGS); if (pte_looks_normal(pte) && !(cpu_has_feature(CPU_FTR_COHERENT_ICACHE) || @@ -81,17 +79,6 @@ static pte_t set_pte_filter(pte_t pte, unsigned long addr) if (!pg) return pte; if (!test_bit(PG_arch_1, &pg->flags)) { -#ifdef CONFIG_8xx - /* On 8xx, cache control instructions (particularly - * "dcbst" from flush_dcache_icache) fault as write - * operation if there is an unpopulated TLB entry - * for the address in question. To workaround that, - * we invalidate the TLB here, thus avoiding dcbst - * misbehaviour. - */ - /* 8xx doesn't care about PID, size or ind args */ - _tlbil_va(addr, 0, 0, 0); -#endif /* CONFIG_8xx */ flush_dcache_icache_page(pg); set_bit(PG_arch_1, &pg->flags); } @@ -111,7 +98,7 @@ static pte_t set_access_flags_filter(pte_t pte, struct vm_area_struct *vma, * as we don't have two bits to spare for _PAGE_EXEC and _PAGE_HWEXEC so * instead we "filter out" the exec permission for non clean pages. */ -static pte_t set_pte_filter(pte_t pte, unsigned long addr) +static pte_t set_pte_filter(pte_t pte) { struct page *pg; @@ -193,7 +180,7 @@ void set_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep, * this context might not have been activated yet when this * is called. */ - pte = set_pte_filter(pte, addr); + pte = set_pte_filter(pte); /* Perform the setting of the PTE */ __set_pte_at(mm, addr, ptep, pte, 0); -- cgit v0.10.2 From 7371fcb7f71854a0add525e6024cd44b81a31dde Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Wed, 11 Sep 2013 18:36:52 -0500 Subject: powerpc/b4qds: enable coreint Commit 9837b43c5f3514e5d28f65f1513f4dc6759d2810 ("powerpc/85xx: enable coreint for all the 64bit boards") removed the ifdef that avoided coreint on 64-bit, but it missed b4_qds.c. Signed-off-by: Scott Wood Cc: Kevin Hao Cc: Shaveta Leekha diff --git a/arch/powerpc/platforms/85xx/b4_qds.c b/arch/powerpc/platforms/85xx/b4_qds.c index 0c6702f..0f18663 100644 --- a/arch/powerpc/platforms/85xx/b4_qds.c +++ b/arch/powerpc/platforms/85xx/b4_qds.c @@ -79,12 +79,7 @@ define_machine(b4_qds) { #ifdef CONFIG_PCI .pcibios_fixup_bus = fsl_pcibios_fixup_bus, #endif -/* coreint doesn't play nice with lazy EE, use legacy mpic for now */ -#ifdef CONFIG_PPC64 - .get_irq = mpic_get_irq, -#else .get_irq = mpic_get_coreint_irq, -#endif .restart = fsl_rstcr_restart, .calibrate_decr = generic_calibrate_decr, .progress = udbg_progress, -- cgit v0.10.2 From 8f4af7df86c793fe3333e0cd2c769beef912d21c Mon Sep 17 00:00:00 2001 From: York Sun Date: Mon, 16 Sep 2013 16:38:06 -0700 Subject: powerpc/t4240emu: Add device tree file for t4240emu T4240EMU is an emulator target with minimum peripherals. It is based on T4240QDS and trimmed down most peripherals due to either not modeled or lack of board level connections. The main purpose of this minimum dts is to speed up booting on emulator. Signed-off-by: York Sun [scottwood@freescale.com: whitespace fixes] Signed-off-by: Scott Wood diff --git a/arch/powerpc/boot/dts/t4240emu.dts b/arch/powerpc/boot/dts/t4240emu.dts new file mode 100644 index 0000000..ee24ab3 --- /dev/null +++ b/arch/powerpc/boot/dts/t4240emu.dts @@ -0,0 +1,268 @@ +/* + * T4240 emulator Device Tree Source + * + * Copyright 2013 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/dts-v1/; + +/include/ "fsl/e6500_power_isa.dtsi" +/ { + compatible = "fsl,T4240"; + #address-cells = <2>; + #size-cells = <2>; + interrupt-parent = <&mpic>; + + aliases { + ccsr = &soc; + + serial0 = &serial0; + serial1 = &serial1; + serial2 = &serial2; + serial3 = &serial3; + dma0 = &dma0; + dma1 = &dma1; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu0: PowerPC,e6500@0 { + device_type = "cpu"; + reg = <0 1>; + next-level-cache = <&L2_1>; + }; + cpu1: PowerPC,e6500@2 { + device_type = "cpu"; + reg = <2 3>; + next-level-cache = <&L2_1>; + }; + cpu2: PowerPC,e6500@4 { + device_type = "cpu"; + reg = <4 5>; + next-level-cache = <&L2_1>; + }; + cpu3: PowerPC,e6500@6 { + device_type = "cpu"; + reg = <6 7>; + next-level-cache = <&L2_1>; + }; + + cpu4: PowerPC,e6500@8 { + device_type = "cpu"; + reg = <8 9>; + next-level-cache = <&L2_2>; + }; + cpu5: PowerPC,e6500@10 { + device_type = "cpu"; + reg = <10 11>; + next-level-cache = <&L2_2>; + }; + cpu6: PowerPC,e6500@12 { + device_type = "cpu"; + reg = <12 13>; + next-level-cache = <&L2_2>; + }; + cpu7: PowerPC,e6500@14 { + device_type = "cpu"; + reg = <14 15>; + next-level-cache = <&L2_2>; + }; + + cpu8: PowerPC,e6500@16 { + device_type = "cpu"; + reg = <16 17>; + next-level-cache = <&L2_3>; + }; + cpu9: PowerPC,e6500@18 { + device_type = "cpu"; + reg = <18 19>; + next-level-cache = <&L2_3>; + }; + cpu10: PowerPC,e6500@20 { + device_type = "cpu"; + reg = <20 21>; + next-level-cache = <&L2_3>; + }; + cpu11: PowerPC,e6500@22 { + device_type = "cpu"; + reg = <22 23>; + next-level-cache = <&L2_3>; + }; + }; +}; + +/ { + model = "fsl,T4240QDS"; + compatible = "fsl,T4240EMU", "fsl,T4240QDS"; + #address-cells = <2>; + #size-cells = <2>; + interrupt-parent = <&mpic>; + + ifc: localbus@ffe124000 { + reg = <0xf 0xfe124000 0 0x2000>; + ranges = <0 0 0xf 0xe8000000 0x08000000 + 2 0 0xf 0xff800000 0x00010000 + 3 0 0xf 0xffdf0000 0x00008000>; + + nor@0,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "cfi-flash"; + reg = <0x0 0x0 0x8000000>; + + bank-width = <2>; + device-width = <1>; + }; + + }; + + memory { + device_type = "memory"; + }; + + soc: soc@ffe000000 { + ranges = <0x00000000 0xf 0xfe000000 0x1000000>; + reg = <0xf 0xfe000000 0 0x00001000>; + + }; +}; + +&ifc { + #address-cells = <2>; + #size-cells = <1>; + compatible = "fsl,ifc", "simple-bus"; + interrupts = <25 2 0 0>; +}; + +&soc { + #address-cells = <1>; + #size-cells = <1>; + device_type = "soc"; + compatible = "simple-bus"; + + soc-sram-error { + compatible = "fsl,soc-sram-error"; + interrupts = <16 2 1 29>; + }; + + corenet-law@0 { + compatible = "fsl,corenet-law"; + reg = <0x0 0x1000>; + fsl,num-laws = <32>; + }; + + ddr1: memory-controller@8000 { + compatible = "fsl,qoriq-memory-controller-v4.7", + "fsl,qoriq-memory-controller"; + reg = <0x8000 0x1000>; + interrupts = <16 2 1 23>; + }; + + ddr2: memory-controller@9000 { + compatible = "fsl,qoriq-memory-controller-v4.7", + "fsl,qoriq-memory-controller"; + reg = <0x9000 0x1000>; + interrupts = <16 2 1 22>; + }; + + ddr3: memory-controller@a000 { + compatible = "fsl,qoriq-memory-controller-v4.7", + "fsl,qoriq-memory-controller"; + reg = <0xa000 0x1000>; + interrupts = <16 2 1 21>; + }; + + cpc: l3-cache-controller@10000 { + compatible = "fsl,t4240-l3-cache-controller", "cache"; + reg = <0x10000 0x1000 + 0x11000 0x1000 + 0x12000 0x1000>; + interrupts = <16 2 1 27 + 16 2 1 26 + 16 2 1 25>; + }; + + corenet-cf@18000 { + compatible = "fsl,corenet-cf"; + reg = <0x18000 0x1000>; + interrupts = <16 2 1 31>; + fsl,ccf-num-csdids = <32>; + fsl,ccf-num-snoopids = <32>; + }; + + iommu@20000 { + compatible = "fsl,pamu-v1.0", "fsl,pamu"; + reg = <0x20000 0x6000>; + interrupts = < + 24 2 0 0 + 16 2 1 30>; + }; + +/include/ "fsl/qoriq-mpic.dtsi" + + guts: global-utilities@e0000 { + compatible = "fsl,t4240-device-config", "fsl,qoriq-device-config-2.0"; + reg = <0xe0000 0xe00>; + fsl,has-rstcr; + fsl,liodn-bits = <12>; + }; + + clockgen: global-utilities@e1000 { + compatible = "fsl,t4240-clockgen", "fsl,qoriq-clockgen-2.0"; + reg = <0xe1000 0x1000>; + }; + +/include/ "fsl/qoriq-dma-0.dtsi" +/include/ "fsl/qoriq-dma-1.dtsi" + +/include/ "fsl/qoriq-i2c-0.dtsi" +/include/ "fsl/qoriq-i2c-1.dtsi" +/include/ "fsl/qoriq-duart-0.dtsi" +/include/ "fsl/qoriq-duart-1.dtsi" + + L2_1: l2-cache-controller@c20000 { + compatible = "fsl,t4240-l2-cache-controller"; + reg = <0xc20000 0x40000>; + next-level-cache = <&cpc>; + }; + L2_2: l2-cache-controller@c60000 { + compatible = "fsl,t4240-l2-cache-controller"; + reg = <0xc60000 0x40000>; + next-level-cache = <&cpc>; + }; + L2_3: l2-cache-controller@ca0000 { + compatible = "fsl,t4240-l2-cache-controller"; + reg = <0xca0000 0x40000>; + next-level-cache = <&cpc>; + }; +}; -- cgit v0.10.2 From 312325031d0da8a7c4b2857e79a23fb89df2b887 Mon Sep 17 00:00:00 2001 From: York Sun Date: Mon, 16 Sep 2013 16:38:07 -0700 Subject: powerpc/b4860emu: Add device tree file for b4860emu B4860EMU is a emualtor target with minimum peripherals. It is based on B4860QDS and trimmed down most peripherals due to either not modeled or lack of board level connections. The main purpose of this minimum dts is to speed up booting on emulator. Signed-off-by: York Sun [scottwood@freescale.com: whitespace fix] Signed-off-by: Scott Wood diff --git a/arch/powerpc/boot/dts/b4860emu.dts b/arch/powerpc/boot/dts/b4860emu.dts new file mode 100644 index 0000000..7290021 --- /dev/null +++ b/arch/powerpc/boot/dts/b4860emu.dts @@ -0,0 +1,218 @@ +/* + * B4860 emulator Device Tree Source + * + * Copyright 2013 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * This software is provided by Freescale Semiconductor "as is" and any + * express or implied warranties, including, but not limited to, the implied + * warranties of merchantability and fitness for a particular purpose are + * disclaimed. In no event shall Freescale Semiconductor be liable for any + * direct, indirect, incidental, special, exemplary, or consequential damages + * (including, but not limited to, procurement of substitute goods or services; + * loss of use, data, or profits; or business interruption) however caused and + * on any theory of liability, whether in contract, strict liability, or tort + * (including negligence or otherwise) arising in any way out of the use of + * this software, even if advised of the possibility of such damage. + */ + +/dts-v1/; + +/include/ "fsl/e6500_power_isa.dtsi" + +/ { + compatible = "fsl,B4860"; + #address-cells = <2>; + #size-cells = <2>; + interrupt-parent = <&mpic>; + + aliases { + ccsr = &soc; + + serial0 = &serial0; + serial1 = &serial1; + serial2 = &serial2; + serial3 = &serial3; + dma0 = &dma0; + dma1 = &dma1; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu0: PowerPC,e6500@0 { + device_type = "cpu"; + reg = <0 1>; + next-level-cache = <&L2>; + }; + cpu1: PowerPC,e6500@2 { + device_type = "cpu"; + reg = <2 3>; + next-level-cache = <&L2>; + }; + cpu2: PowerPC,e6500@4 { + device_type = "cpu"; + reg = <4 5>; + next-level-cache = <&L2>; + }; + cpu3: PowerPC,e6500@6 { + device_type = "cpu"; + reg = <6 7>; + next-level-cache = <&L2>; + }; + }; +}; + +/ { + model = "fsl,B4860QDS"; + compatible = "fsl,B4860EMU", "fsl,B4860QDS"; + #address-cells = <2>; + #size-cells = <2>; + interrupt-parent = <&mpic>; + + ifc: localbus@ffe124000 { + reg = <0xf 0xfe124000 0 0x2000>; + ranges = <0 0 0xf 0xe8000000 0x08000000 + 2 0 0xf 0xff800000 0x00010000 + 3 0 0xf 0xffdf0000 0x00008000>; + + nor@0,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "cfi-flash"; + reg = <0x0 0x0 0x8000000>; + bank-width = <2>; + device-width = <1>; + }; + }; + + memory { + device_type = "memory"; + }; + + soc: soc@ffe000000 { + ranges = <0x00000000 0xf 0xfe000000 0x1000000>; + reg = <0xf 0xfe000000 0 0x00001000>; + }; +}; + +&ifc { + #address-cells = <2>; + #size-cells = <1>; + compatible = "fsl,ifc", "simple-bus"; + interrupts = <25 2 0 0>; +}; + +&soc { + #address-cells = <1>; + #size-cells = <1>; + device_type = "soc"; + compatible = "simple-bus"; + + soc-sram-error { + compatible = "fsl,soc-sram-error"; + interrupts = <16 2 1 2>; + }; + + corenet-law@0 { + compatible = "fsl,corenet-law"; + reg = <0x0 0x1000>; + fsl,num-laws = <32>; + }; + + ddr1: memory-controller@8000 { + compatible = "fsl,qoriq-memory-controller-v4.5", "fsl,qoriq-memory-controller"; + reg = <0x8000 0x1000>; + interrupts = <16 2 1 8>; + }; + + ddr2: memory-controller@9000 { + compatible = "fsl,qoriq-memory-controller-v4.5","fsl,qoriq-memory-controller"; + reg = <0x9000 0x1000>; + interrupts = <16 2 1 9>; + }; + + cpc: l3-cache-controller@10000 { + compatible = "fsl,b4-l3-cache-controller", "cache"; + reg = <0x10000 0x1000 + 0x11000 0x1000>; + interrupts = <16 2 1 4>; + }; + + corenet-cf@18000 { + compatible = "fsl,b4-corenet-cf"; + reg = <0x18000 0x1000>; + interrupts = <16 2 1 0>; + fsl,ccf-num-csdids = <32>; + fsl,ccf-num-snoopids = <32>; + }; + + iommu@20000 { + compatible = "fsl,pamu-v1.0", "fsl,pamu"; + reg = <0x20000 0x4000>; + #address-cells = <1>; + #size-cells = <1>; + interrupts = < + 24 2 0 0 + 16 2 1 1>; + pamu0: pamu@0 { + reg = <0 0x1000>; + fsl,primary-cache-geometry = <8 1>; + fsl,secondary-cache-geometry = <32 2>; + }; + }; + +/include/ "fsl/qoriq-mpic.dtsi" + + guts: global-utilities@e0000 { + compatible = "fsl,b4-device-config"; + reg = <0xe0000 0xe00>; + fsl,has-rstcr; + fsl,liodn-bits = <12>; + }; + + clockgen: global-utilities@e1000 { + compatible = "fsl,b4-clockgen", "fsl,qoriq-clockgen-2.0"; + reg = <0xe1000 0x1000>; + }; + +/include/ "fsl/qoriq-dma-0.dtsi" + dma@100300 { + fsl,iommu-parent = <&pamu0>; + fsl,liodn-reg = <&guts 0x580>; /* DMA1LIODNR */ + }; + +/include/ "fsl/qoriq-dma-1.dtsi" + dma@101300 { + fsl,iommu-parent = <&pamu0>; + fsl,liodn-reg = <&guts 0x584>; /* DMA2LIODNR */ + }; + +/include/ "fsl/qoriq-i2c-0.dtsi" +/include/ "fsl/qoriq-i2c-1.dtsi" +/include/ "fsl/qoriq-duart-0.dtsi" +/include/ "fsl/qoriq-duart-1.dtsi" + + L2: l2-cache-controller@c20000 { + compatible = "fsl,b4-l2-cache-controller"; + reg = <0xc20000 0x1000>; + next-level-cache = <&cpc>; + }; +}; -- cgit v0.10.2 From 8f4a9e525a91d5903165167f1a7dbb22e97edcd0 Mon Sep 17 00:00:00 2001 From: Prabhakar Kushwaha Date: Tue, 24 Sep 2013 16:20:32 +0530 Subject: powerpc/dts/c293pcie: Add range field for IFC NAND C290PCIe has NAND flash present on IFC Chip Select(CS) 1. So Add "ranges" field for NAND flash on CS1. Signed-off-by: Prabhakar Kushwaha Signed-off-by: Scott Wood diff --git a/arch/powerpc/boot/dts/c293pcie.dts b/arch/powerpc/boot/dts/c293pcie.dts index 1238bda..6681cc2 100644 --- a/arch/powerpc/boot/dts/c293pcie.dts +++ b/arch/powerpc/boot/dts/c293pcie.dts @@ -45,6 +45,7 @@ ifc: ifc@fffe1e000 { reg = <0xf 0xffe1e000 0 0x2000>; ranges = <0x0 0x0 0xf 0xec000000 0x04000000 + 0x1 0x0 0xf 0xff800000 0x00010000 0x2 0x0 0xf 0xffdf0000 0x00010000>; }; -- cgit v0.10.2 From 512e267f3594b5a3b4937a00666241cb994ef55a Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Thu, 26 Sep 2013 09:42:26 +0800 Subject: powerpc/85xx: introduce corenet_generic machine In the current kernel, the board files for p2041rdb, p3041ds, p4080ds, p5020ds, p5040ds, t4240qds and b4qds are almost the same except the machine name. So this introduces a cornet_generic machine to support all these boards to avoid the code duplication. With these changes the file corenet_ds.h becomes useless. Just delete it. Signed-off-by: Kevin Hao Signed-off-by: Scott Wood diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig index de2eb93..3bee943 100644 --- a/arch/powerpc/platforms/85xx/Kconfig +++ b/arch/powerpc/platforms/85xx/Kconfig @@ -228,6 +228,7 @@ config P2041_RDB select GPIO_MPC8XXX select HAS_RAPIDIO select PPC_EPAPR_HV_PIC + select CORENET_GENERIC help This option enables support for the P2041 RDB board @@ -241,6 +242,7 @@ config P3041_DS select GPIO_MPC8XXX select HAS_RAPIDIO select PPC_EPAPR_HV_PIC + select CORENET_GENERIC help This option enables support for the P3041 DS board @@ -254,6 +256,7 @@ config P4080_DS select GPIO_MPC8XXX select HAS_RAPIDIO select PPC_EPAPR_HV_PIC + select CORENET_GENERIC help This option enables support for the P4080 DS board @@ -278,6 +281,7 @@ config P5020_DS select GPIO_MPC8XXX select HAS_RAPIDIO select PPC_EPAPR_HV_PIC + select CORENET_GENERIC help This option enables support for the P5020 DS board @@ -292,6 +296,7 @@ config P5040_DS select GPIO_MPC8XXX select HAS_RAPIDIO select PPC_EPAPR_HV_PIC + select CORENET_GENERIC help This option enables support for the P5040 DS board @@ -323,6 +328,7 @@ config T4240_QDS select GPIO_MPC8XXX select HAS_RAPIDIO select PPC_EPAPR_HV_PIC + select CORENET_GENERIC help This option enables support for the T4240 QDS board @@ -337,6 +343,7 @@ config B4_QDS select ARCH_REQUIRE_GPIOLIB select HAS_RAPIDIO select PPC_EPAPR_HV_PIC + select CORENET_GENERIC help This option enables support for the B4 QDS board The B4 application development system B4 QDS is a complete @@ -348,3 +355,6 @@ endif # FSL_SOC_BOOKE config TQM85xx bool + +config CORENET_GENERIC + bool diff --git a/arch/powerpc/platforms/85xx/Makefile b/arch/powerpc/platforms/85xx/Makefile index 53c9f75..a6c281d 100644 --- a/arch/powerpc/platforms/85xx/Makefile +++ b/arch/powerpc/platforms/85xx/Makefile @@ -18,13 +18,7 @@ obj-$(CONFIG_P1010_RDB) += p1010rdb.o obj-$(CONFIG_P1022_DS) += p1022_ds.o obj-$(CONFIG_P1022_RDK) += p1022_rdk.o obj-$(CONFIG_P1023_RDS) += p1023_rds.o -obj-$(CONFIG_P2041_RDB) += p2041_rdb.o corenet_ds.o -obj-$(CONFIG_P3041_DS) += p3041_ds.o corenet_ds.o -obj-$(CONFIG_P4080_DS) += p4080_ds.o corenet_ds.o -obj-$(CONFIG_P5020_DS) += p5020_ds.o corenet_ds.o -obj-$(CONFIG_P5040_DS) += p5040_ds.o corenet_ds.o -obj-$(CONFIG_T4240_QDS) += t4240_qds.o corenet_ds.o -obj-$(CONFIG_B4_QDS) += b4_qds.o corenet_ds.o +obj-$(CONFIG_CORENET_GENERIC) += corenet_ds.o obj-$(CONFIG_STX_GP3) += stx_gp3.o obj-$(CONFIG_TQM85xx) += tqm85xx.o obj-$(CONFIG_SBC8548) += sbc8548.o diff --git a/arch/powerpc/platforms/85xx/b4_qds.c b/arch/powerpc/platforms/85xx/b4_qds.c deleted file mode 100644 index 0f18663..0000000 --- a/arch/powerpc/platforms/85xx/b4_qds.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * B4 QDS Setup - * Should apply for QDS platform of B4860 and it's personalities. - * viz B4860/B4420/B4220QDS - * - * Copyright 2012 Freescale Semiconductor Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "corenet_ds.h" - -/* - * Called very early, device-tree isn't unflattened - */ -static int __init b4_qds_probe(void) -{ - unsigned long root = of_get_flat_dt_root(); -#ifdef CONFIG_SMP - extern struct smp_ops_t smp_85xx_ops; -#endif - - if ((of_flat_dt_is_compatible(root, "fsl,B4860QDS")) || - (of_flat_dt_is_compatible(root, "fsl,B4420QDS")) || - (of_flat_dt_is_compatible(root, "fsl,B4220QDS"))) - return 1; - - /* Check if we're running under the Freescale hypervisor */ - if ((of_flat_dt_is_compatible(root, "fsl,B4860QDS-hv")) || - (of_flat_dt_is_compatible(root, "fsl,B4420QDS-hv")) || - (of_flat_dt_is_compatible(root, "fsl,B4220QDS-hv"))) { - ppc_md.init_IRQ = ehv_pic_init; - ppc_md.get_irq = ehv_pic_get_irq; - ppc_md.restart = fsl_hv_restart; - ppc_md.power_off = fsl_hv_halt; - ppc_md.halt = fsl_hv_halt; -#ifdef CONFIG_SMP - /* - * Disable the timebase sync operations because we can't write - * to the timebase registers under the hypervisor. - */ - smp_85xx_ops.give_timebase = NULL; - smp_85xx_ops.take_timebase = NULL; -#endif - return 1; - } - - return 0; -} - -define_machine(b4_qds) { - .name = "B4 QDS", - .probe = b4_qds_probe, - .setup_arch = corenet_ds_setup_arch, - .init_IRQ = corenet_ds_pic_init, -#ifdef CONFIG_PCI - .pcibios_fixup_bus = fsl_pcibios_fixup_bus, -#endif - .get_irq = mpic_get_coreint_irq, - .restart = fsl_rstcr_restart, - .calibrate_decr = generic_calibrate_decr, - .progress = udbg_progress, -#ifdef CONFIG_PPC64 - .power_save = book3e_idle, -#else - .power_save = e500_idle, -#endif -}; - -machine_arch_initcall(b4_qds, corenet_ds_publish_devices); - -#ifdef CONFIG_SWIOTLB -machine_arch_initcall(b4_qds, swiotlb_setup_bus_notifier); -#endif diff --git a/arch/powerpc/platforms/85xx/corenet_ds.c b/arch/powerpc/platforms/85xx/corenet_ds.c index aa3690b..8e0285a 100644 --- a/arch/powerpc/platforms/85xx/corenet_ds.c +++ b/arch/powerpc/platforms/85xx/corenet_ds.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -94,3 +95,88 @@ int __init corenet_ds_publish_devices(void) { return of_platform_bus_probe(NULL, of_device_ids, NULL); } + +static const char * const boards[] __initconst = { + "fsl,P2041RDB", + "fsl,P3041DS", + "fsl,P4080DS", + "fsl,P5020DS", + "fsl,P5040DS", + "fsl,T4240QDS", + "fsl,B4860QDS", + "fsl,B4420QDS", + "fsl,B4220QDS", + NULL +}; + +static const char * const hv_boards[] __initconst = { + "fsl,P2041RDB-hv", + "fsl,P3041DS-hv", + "fsl,P4080DS-hv", + "fsl,P5020DS-hv", + "fsl,P5040DS-hv", + "fsl,T4240QDS-hv", + "fsl,B4860QDS-hv", + "fsl,B4420QDS-hv", + "fsl,B4220QDS-hv", + NULL +}; + +/* + * Called very early, device-tree isn't unflattened + */ +static int __init corenet_generic_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); +#ifdef CONFIG_SMP + extern struct smp_ops_t smp_85xx_ops; +#endif + + if (of_flat_dt_match(root, boards)) + return 1; + + /* Check if we're running under the Freescale hypervisor */ + if (of_flat_dt_match(root, hv_boards)) { + ppc_md.init_IRQ = ehv_pic_init; + ppc_md.get_irq = ehv_pic_get_irq; + ppc_md.restart = fsl_hv_restart; + ppc_md.power_off = fsl_hv_halt; + ppc_md.halt = fsl_hv_halt; +#ifdef CONFIG_SMP + /* + * Disable the timebase sync operations because we can't write + * to the timebase registers under the hypervisor. + */ + smp_85xx_ops.give_timebase = NULL; + smp_85xx_ops.take_timebase = NULL; +#endif + return 1; + } + + return 0; +} + +define_machine(corenet_generic) { + .name = "CoreNet Generic", + .probe = corenet_generic_probe, + .setup_arch = corenet_ds_setup_arch, + .init_IRQ = corenet_ds_pic_init, +#ifdef CONFIG_PCI + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#endif + .get_irq = mpic_get_coreint_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +#ifdef CONFIG_PPC64 + .power_save = book3e_idle, +#else + .power_save = e500_idle, +#endif +}; + +machine_arch_initcall(corenet_generic, corenet_ds_publish_devices); + +#ifdef CONFIG_SWIOTLB +machine_arch_initcall(corenet_generic, swiotlb_setup_bus_notifier); +#endif diff --git a/arch/powerpc/platforms/85xx/corenet_ds.h b/arch/powerpc/platforms/85xx/corenet_ds.h deleted file mode 100644 index ddd700b..0000000 --- a/arch/powerpc/platforms/85xx/corenet_ds.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Corenet based SoC DS Setup - * - * Copyright 2009 Freescale Semiconductor Inc. - * - * 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 CORENET_DS_H -#define CORENET_DS_H - -extern void __init corenet_ds_pic_init(void); -extern void __init corenet_ds_setup_arch(void); -extern int __init corenet_ds_publish_devices(void); - -#endif diff --git a/arch/powerpc/platforms/85xx/p2041_rdb.c b/arch/powerpc/platforms/85xx/p2041_rdb.c deleted file mode 100644 index 000c089..0000000 --- a/arch/powerpc/platforms/85xx/p2041_rdb.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * P2041 RDB Setup - * - * Copyright 2011 Freescale Semiconductor Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "corenet_ds.h" - -/* - * Called very early, device-tree isn't unflattened - */ -static int __init p2041_rdb_probe(void) -{ - unsigned long root = of_get_flat_dt_root(); -#ifdef CONFIG_SMP - extern struct smp_ops_t smp_85xx_ops; -#endif - - if (of_flat_dt_is_compatible(root, "fsl,P2041RDB")) - return 1; - - /* Check if we're running under the Freescale hypervisor */ - if (of_flat_dt_is_compatible(root, "fsl,P2041RDB-hv")) { - ppc_md.init_IRQ = ehv_pic_init; - ppc_md.get_irq = ehv_pic_get_irq; - ppc_md.restart = fsl_hv_restart; - ppc_md.power_off = fsl_hv_halt; - ppc_md.halt = fsl_hv_halt; -#ifdef CONFIG_SMP - /* - * Disable the timebase sync operations because we can't write - * to the timebase registers under the hypervisor. - */ - smp_85xx_ops.give_timebase = NULL; - smp_85xx_ops.take_timebase = NULL; -#endif - return 1; - } - - return 0; -} - -define_machine(p2041_rdb) { - .name = "P2041 RDB", - .probe = p2041_rdb_probe, - .setup_arch = corenet_ds_setup_arch, - .init_IRQ = corenet_ds_pic_init, -#ifdef CONFIG_PCI - .pcibios_fixup_bus = fsl_pcibios_fixup_bus, -#endif - .get_irq = mpic_get_coreint_irq, - .restart = fsl_rstcr_restart, - .calibrate_decr = generic_calibrate_decr, - .progress = udbg_progress, - .power_save = e500_idle, -}; - -machine_arch_initcall(p2041_rdb, corenet_ds_publish_devices); - -#ifdef CONFIG_SWIOTLB -machine_arch_initcall(p2041_rdb, swiotlb_setup_bus_notifier); -#endif diff --git a/arch/powerpc/platforms/85xx/p3041_ds.c b/arch/powerpc/platforms/85xx/p3041_ds.c deleted file mode 100644 index b3edc20..0000000 --- a/arch/powerpc/platforms/85xx/p3041_ds.c +++ /dev/null @@ -1,89 +0,0 @@ -/* - * P3041 DS Setup - * - * Maintained by Kumar Gala (see MAINTAINERS for contact information) - * - * Copyright 2009-2010 Freescale Semiconductor Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "corenet_ds.h" - -/* - * Called very early, device-tree isn't unflattened - */ -static int __init p3041_ds_probe(void) -{ - unsigned long root = of_get_flat_dt_root(); -#ifdef CONFIG_SMP - extern struct smp_ops_t smp_85xx_ops; -#endif - - if (of_flat_dt_is_compatible(root, "fsl,P3041DS")) - return 1; - - /* Check if we're running under the Freescale hypervisor */ - if (of_flat_dt_is_compatible(root, "fsl,P3041DS-hv")) { - ppc_md.init_IRQ = ehv_pic_init; - ppc_md.get_irq = ehv_pic_get_irq; - ppc_md.restart = fsl_hv_restart; - ppc_md.power_off = fsl_hv_halt; - ppc_md.halt = fsl_hv_halt; -#ifdef CONFIG_SMP - /* - * Disable the timebase sync operations because we can't write - * to the timebase registers under the hypervisor. - */ - smp_85xx_ops.give_timebase = NULL; - smp_85xx_ops.take_timebase = NULL; -#endif - return 1; - } - - return 0; -} - -define_machine(p3041_ds) { - .name = "P3041 DS", - .probe = p3041_ds_probe, - .setup_arch = corenet_ds_setup_arch, - .init_IRQ = corenet_ds_pic_init, -#ifdef CONFIG_PCI - .pcibios_fixup_bus = fsl_pcibios_fixup_bus, -#endif - .get_irq = mpic_get_coreint_irq, - .restart = fsl_rstcr_restart, - .calibrate_decr = generic_calibrate_decr, - .progress = udbg_progress, - .power_save = e500_idle, -}; - -machine_arch_initcall(p3041_ds, corenet_ds_publish_devices); - -#ifdef CONFIG_SWIOTLB -machine_arch_initcall(p3041_ds, swiotlb_setup_bus_notifier); -#endif diff --git a/arch/powerpc/platforms/85xx/p4080_ds.c b/arch/powerpc/platforms/85xx/p4080_ds.c deleted file mode 100644 index 54df106..0000000 --- a/arch/powerpc/platforms/85xx/p4080_ds.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * P4080 DS Setup - * - * Maintained by Kumar Gala (see MAINTAINERS for contact information) - * - * Copyright 2009 Freescale Semiconductor Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "corenet_ds.h" - -/* - * Called very early, device-tree isn't unflattened - */ -static int __init p4080_ds_probe(void) -{ - unsigned long root = of_get_flat_dt_root(); -#ifdef CONFIG_SMP - extern struct smp_ops_t smp_85xx_ops; -#endif - - if (of_flat_dt_is_compatible(root, "fsl,P4080DS")) - return 1; - - /* Check if we're running under the Freescale hypervisor */ - if (of_flat_dt_is_compatible(root, "fsl,P4080DS-hv")) { - ppc_md.init_IRQ = ehv_pic_init; - ppc_md.get_irq = ehv_pic_get_irq; - ppc_md.restart = fsl_hv_restart; - ppc_md.power_off = fsl_hv_halt; - ppc_md.halt = fsl_hv_halt; -#ifdef CONFIG_SMP - /* - * Disable the timebase sync operations because we can't write - * to the timebase registers under the hypervisor. - */ - smp_85xx_ops.give_timebase = NULL; - smp_85xx_ops.take_timebase = NULL; -#endif - return 1; - } - - return 0; -} - -define_machine(p4080_ds) { - .name = "P4080 DS", - .probe = p4080_ds_probe, - .setup_arch = corenet_ds_setup_arch, - .init_IRQ = corenet_ds_pic_init, -#ifdef CONFIG_PCI - .pcibios_fixup_bus = fsl_pcibios_fixup_bus, -#endif - .get_irq = mpic_get_coreint_irq, - .restart = fsl_rstcr_restart, - .calibrate_decr = generic_calibrate_decr, - .progress = udbg_progress, - .power_save = e500_idle, -}; - -machine_arch_initcall(p4080_ds, corenet_ds_publish_devices); -#ifdef CONFIG_SWIOTLB -machine_arch_initcall(p4080_ds, swiotlb_setup_bus_notifier); -#endif diff --git a/arch/powerpc/platforms/85xx/p5020_ds.c b/arch/powerpc/platforms/85xx/p5020_ds.c deleted file mode 100644 index 39cfa40..0000000 --- a/arch/powerpc/platforms/85xx/p5020_ds.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * P5020 DS Setup - * - * Maintained by Kumar Gala (see MAINTAINERS for contact information) - * - * Copyright 2009-2010 Freescale Semiconductor Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "corenet_ds.h" - -/* - * Called very early, device-tree isn't unflattened - */ -static int __init p5020_ds_probe(void) -{ - unsigned long root = of_get_flat_dt_root(); -#ifdef CONFIG_SMP - extern struct smp_ops_t smp_85xx_ops; -#endif - - if (of_flat_dt_is_compatible(root, "fsl,P5020DS")) - return 1; - - /* Check if we're running under the Freescale hypervisor */ - if (of_flat_dt_is_compatible(root, "fsl,P5020DS-hv")) { - ppc_md.init_IRQ = ehv_pic_init; - ppc_md.get_irq = ehv_pic_get_irq; - ppc_md.restart = fsl_hv_restart; - ppc_md.power_off = fsl_hv_halt; - ppc_md.halt = fsl_hv_halt; -#ifdef CONFIG_SMP - /* - * Disable the timebase sync operations because we can't write - * to the timebase registers under the hypervisor. - */ - smp_85xx_ops.give_timebase = NULL; - smp_85xx_ops.take_timebase = NULL; -#endif - return 1; - } - - return 0; -} - -define_machine(p5020_ds) { - .name = "P5020 DS", - .probe = p5020_ds_probe, - .setup_arch = corenet_ds_setup_arch, - .init_IRQ = corenet_ds_pic_init, -#ifdef CONFIG_PCI - .pcibios_fixup_bus = fsl_pcibios_fixup_bus, -#endif - .get_irq = mpic_get_coreint_irq, - .restart = fsl_rstcr_restart, - .calibrate_decr = generic_calibrate_decr, - .progress = udbg_progress, -#ifdef CONFIG_PPC64 - .power_save = book3e_idle, -#else - .power_save = e500_idle, -#endif -}; - -machine_arch_initcall(p5020_ds, corenet_ds_publish_devices); - -#ifdef CONFIG_SWIOTLB -machine_arch_initcall(p5020_ds, swiotlb_setup_bus_notifier); -#endif diff --git a/arch/powerpc/platforms/85xx/p5040_ds.c b/arch/powerpc/platforms/85xx/p5040_ds.c deleted file mode 100644 index f70e74c..0000000 --- a/arch/powerpc/platforms/85xx/p5040_ds.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * P5040 DS Setup - * - * Copyright 2009-2010 Freescale Semiconductor Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include - -#include "corenet_ds.h" - -/* - * Called very early, device-tree isn't unflattened - */ -static int __init p5040_ds_probe(void) -{ - unsigned long root = of_get_flat_dt_root(); -#ifdef CONFIG_SMP - extern struct smp_ops_t smp_85xx_ops; -#endif - - if (of_flat_dt_is_compatible(root, "fsl,P5040DS")) - return 1; - - /* Check if we're running under the Freescale hypervisor */ - if (of_flat_dt_is_compatible(root, "fsl,P5040DS-hv")) { - ppc_md.init_IRQ = ehv_pic_init; - ppc_md.get_irq = ehv_pic_get_irq; - ppc_md.restart = fsl_hv_restart; - ppc_md.power_off = fsl_hv_halt; - ppc_md.halt = fsl_hv_halt; -#ifdef CONFIG_SMP - /* - * Disable the timebase sync operations because we can't write - * to the timebase registers under the hypervisor. - */ - smp_85xx_ops.give_timebase = NULL; - smp_85xx_ops.take_timebase = NULL; -#endif - return 1; - } - - return 0; -} - -define_machine(p5040_ds) { - .name = "P5040 DS", - .probe = p5040_ds_probe, - .setup_arch = corenet_ds_setup_arch, - .init_IRQ = corenet_ds_pic_init, -#ifdef CONFIG_PCI - .pcibios_fixup_bus = fsl_pcibios_fixup_bus, -#endif - .get_irq = mpic_get_coreint_irq, - .restart = fsl_rstcr_restart, - .calibrate_decr = generic_calibrate_decr, - .progress = udbg_progress, -#ifdef CONFIG_PPC64 - .power_save = book3e_idle, -#else - .power_save = e500_idle, -#endif -}; - -machine_arch_initcall(p5040_ds, corenet_ds_publish_devices); - -#ifdef CONFIG_SWIOTLB -machine_arch_initcall(p5040_ds, swiotlb_setup_bus_notifier); -#endif diff --git a/arch/powerpc/platforms/85xx/t4240_qds.c b/arch/powerpc/platforms/85xx/t4240_qds.c deleted file mode 100644 index 91ead6b..0000000 --- a/arch/powerpc/platforms/85xx/t4240_qds.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * T4240 QDS Setup - * - * Maintained by Kumar Gala (see MAINTAINERS for contact information) - * - * Copyright 2012 Freescale Semiconductor Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "corenet_ds.h" - -/* - * Called very early, device-tree isn't unflattened - */ -static int __init t4240_qds_probe(void) -{ - unsigned long root = of_get_flat_dt_root(); -#ifdef CONFIG_SMP - extern struct smp_ops_t smp_85xx_ops; -#endif - - if (of_flat_dt_is_compatible(root, "fsl,T4240QDS")) - return 1; - - /* Check if we're running under the Freescale hypervisor */ - if (of_flat_dt_is_compatible(root, "fsl,T4240QDS-hv")) { - ppc_md.init_IRQ = ehv_pic_init; - ppc_md.get_irq = ehv_pic_get_irq; - ppc_md.restart = fsl_hv_restart; - ppc_md.power_off = fsl_hv_halt; - ppc_md.halt = fsl_hv_halt; -#ifdef CONFIG_SMP - /* - * Disable the timebase sync operations because we can't write - * to the timebase registers under the hypervisor. - */ - smp_85xx_ops.give_timebase = NULL; - smp_85xx_ops.take_timebase = NULL; -#endif - return 1; - } - - return 0; -} - -define_machine(t4240_qds) { - .name = "T4240 QDS", - .probe = t4240_qds_probe, - .setup_arch = corenet_ds_setup_arch, - .init_IRQ = corenet_ds_pic_init, -#ifdef CONFIG_PCI - .pcibios_fixup_bus = fsl_pcibios_fixup_bus, -#endif - .get_irq = mpic_get_coreint_irq, - .restart = fsl_rstcr_restart, - .calibrate_decr = generic_calibrate_decr, - .progress = udbg_progress, -#ifdef CONFIG_PPC64 - .power_save = book3e_idle, -#else - .power_save = e500_idle, -#endif -}; - -machine_arch_initcall(t4240_qds, corenet_ds_publish_devices); - -#ifdef CONFIG_SWIOTLB -machine_arch_initcall(t4240_qds, swiotlb_setup_bus_notifier); -#endif -- cgit v0.10.2 From befe7c123ee4546316bb9aa9952c0e2f7c0d9c48 Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Thu, 26 Sep 2013 09:42:27 +0800 Subject: powerpc/85xx: rename the corenet_ds.c to corenet_generic.c This file is also used by some RDB and QDS boards. So the name seems not so accurate. Rename it to corenet_generic.c. Also update the function names in this file according to the change. Signed-off-by: Kevin Hao diff --git a/arch/powerpc/platforms/85xx/Makefile b/arch/powerpc/platforms/85xx/Makefile index a6c281d..dd4c0b5 100644 --- a/arch/powerpc/platforms/85xx/Makefile +++ b/arch/powerpc/platforms/85xx/Makefile @@ -18,7 +18,7 @@ obj-$(CONFIG_P1010_RDB) += p1010rdb.o obj-$(CONFIG_P1022_DS) += p1022_ds.o obj-$(CONFIG_P1022_RDK) += p1022_rdk.o obj-$(CONFIG_P1023_RDS) += p1023_rds.o -obj-$(CONFIG_CORENET_GENERIC) += corenet_ds.o +obj-$(CONFIG_CORENET_GENERIC) += corenet_generic.o obj-$(CONFIG_STX_GP3) += stx_gp3.o obj-$(CONFIG_TQM85xx) += tqm85xx.o obj-$(CONFIG_SBC8548) += sbc8548.o diff --git a/arch/powerpc/platforms/85xx/corenet_ds.c b/arch/powerpc/platforms/85xx/corenet_ds.c deleted file mode 100644 index 8e0285a..0000000 --- a/arch/powerpc/platforms/85xx/corenet_ds.c +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Corenet based SoC DS Setup - * - * Maintained by Kumar Gala (see MAINTAINERS for contact information) - * - * Copyright 2009-2011 Freescale Semiconductor Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include "smp.h" - -void __init corenet_ds_pic_init(void) -{ - struct mpic *mpic; - unsigned int flags = MPIC_BIG_ENDIAN | MPIC_SINGLE_DEST_CPU | - MPIC_NO_RESET; - - if (ppc_md.get_irq == mpic_get_coreint_irq) - flags |= MPIC_ENABLE_COREINT; - - mpic = mpic_alloc(NULL, 0, flags, 0, 512, " OpenPIC "); - BUG_ON(mpic == NULL); - - mpic_init(mpic); -} - -/* - * Setup the architecture - */ -void __init corenet_ds_setup_arch(void) -{ - mpc85xx_smp_init(); - - swiotlb_detect_4g(); - - pr_info("%s board from Freescale Semiconductor\n", ppc_md.name); -} - -static const struct of_device_id of_device_ids[] = { - { - .compatible = "simple-bus" - }, - { - .compatible = "fsl,srio", - }, - { - .compatible = "fsl,p4080-pcie", - }, - { - .compatible = "fsl,qoriq-pcie-v2.2", - }, - { - .compatible = "fsl,qoriq-pcie-v2.3", - }, - { - .compatible = "fsl,qoriq-pcie-v2.4", - }, - { - .compatible = "fsl,qoriq-pcie-v3.0", - }, - /* The following two are for the Freescale hypervisor */ - { - .name = "hypervisor", - }, - { - .name = "handles", - }, - {} -}; - -int __init corenet_ds_publish_devices(void) -{ - return of_platform_bus_probe(NULL, of_device_ids, NULL); -} - -static const char * const boards[] __initconst = { - "fsl,P2041RDB", - "fsl,P3041DS", - "fsl,P4080DS", - "fsl,P5020DS", - "fsl,P5040DS", - "fsl,T4240QDS", - "fsl,B4860QDS", - "fsl,B4420QDS", - "fsl,B4220QDS", - NULL -}; - -static const char * const hv_boards[] __initconst = { - "fsl,P2041RDB-hv", - "fsl,P3041DS-hv", - "fsl,P4080DS-hv", - "fsl,P5020DS-hv", - "fsl,P5040DS-hv", - "fsl,T4240QDS-hv", - "fsl,B4860QDS-hv", - "fsl,B4420QDS-hv", - "fsl,B4220QDS-hv", - NULL -}; - -/* - * Called very early, device-tree isn't unflattened - */ -static int __init corenet_generic_probe(void) -{ - unsigned long root = of_get_flat_dt_root(); -#ifdef CONFIG_SMP - extern struct smp_ops_t smp_85xx_ops; -#endif - - if (of_flat_dt_match(root, boards)) - return 1; - - /* Check if we're running under the Freescale hypervisor */ - if (of_flat_dt_match(root, hv_boards)) { - ppc_md.init_IRQ = ehv_pic_init; - ppc_md.get_irq = ehv_pic_get_irq; - ppc_md.restart = fsl_hv_restart; - ppc_md.power_off = fsl_hv_halt; - ppc_md.halt = fsl_hv_halt; -#ifdef CONFIG_SMP - /* - * Disable the timebase sync operations because we can't write - * to the timebase registers under the hypervisor. - */ - smp_85xx_ops.give_timebase = NULL; - smp_85xx_ops.take_timebase = NULL; -#endif - return 1; - } - - return 0; -} - -define_machine(corenet_generic) { - .name = "CoreNet Generic", - .probe = corenet_generic_probe, - .setup_arch = corenet_ds_setup_arch, - .init_IRQ = corenet_ds_pic_init, -#ifdef CONFIG_PCI - .pcibios_fixup_bus = fsl_pcibios_fixup_bus, -#endif - .get_irq = mpic_get_coreint_irq, - .restart = fsl_rstcr_restart, - .calibrate_decr = generic_calibrate_decr, - .progress = udbg_progress, -#ifdef CONFIG_PPC64 - .power_save = book3e_idle, -#else - .power_save = e500_idle, -#endif -}; - -machine_arch_initcall(corenet_generic, corenet_ds_publish_devices); - -#ifdef CONFIG_SWIOTLB -machine_arch_initcall(corenet_generic, swiotlb_setup_bus_notifier); -#endif diff --git a/arch/powerpc/platforms/85xx/corenet_generic.c b/arch/powerpc/platforms/85xx/corenet_generic.c new file mode 100644 index 0000000..fbd871e --- /dev/null +++ b/arch/powerpc/platforms/85xx/corenet_generic.c @@ -0,0 +1,182 @@ +/* + * Corenet based SoC DS Setup + * + * Maintained by Kumar Gala (see MAINTAINERS for contact information) + * + * Copyright 2009-2011 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "smp.h" + +void __init corenet_gen_pic_init(void) +{ + struct mpic *mpic; + unsigned int flags = MPIC_BIG_ENDIAN | MPIC_SINGLE_DEST_CPU | + MPIC_NO_RESET; + + if (ppc_md.get_irq == mpic_get_coreint_irq) + flags |= MPIC_ENABLE_COREINT; + + mpic = mpic_alloc(NULL, 0, flags, 0, 512, " OpenPIC "); + BUG_ON(mpic == NULL); + + mpic_init(mpic); +} + +/* + * Setup the architecture + */ +void __init corenet_gen_setup_arch(void) +{ + mpc85xx_smp_init(); + + swiotlb_detect_4g(); + + pr_info("%s board from Freescale Semiconductor\n", ppc_md.name); +} + +static const struct of_device_id of_device_ids[] = { + { + .compatible = "simple-bus" + }, + { + .compatible = "fsl,srio", + }, + { + .compatible = "fsl,p4080-pcie", + }, + { + .compatible = "fsl,qoriq-pcie-v2.2", + }, + { + .compatible = "fsl,qoriq-pcie-v2.3", + }, + { + .compatible = "fsl,qoriq-pcie-v2.4", + }, + { + .compatible = "fsl,qoriq-pcie-v3.0", + }, + /* The following two are for the Freescale hypervisor */ + { + .name = "hypervisor", + }, + { + .name = "handles", + }, + {} +}; + +int __init corenet_gen_publish_devices(void) +{ + return of_platform_bus_probe(NULL, of_device_ids, NULL); +} + +static const char * const boards[] __initconst = { + "fsl,P2041RDB", + "fsl,P3041DS", + "fsl,P4080DS", + "fsl,P5020DS", + "fsl,P5040DS", + "fsl,T4240QDS", + "fsl,B4860QDS", + "fsl,B4420QDS", + "fsl,B4220QDS", + NULL +}; + +static const char * const hv_boards[] __initconst = { + "fsl,P2041RDB-hv", + "fsl,P3041DS-hv", + "fsl,P4080DS-hv", + "fsl,P5020DS-hv", + "fsl,P5040DS-hv", + "fsl,T4240QDS-hv", + "fsl,B4860QDS-hv", + "fsl,B4420QDS-hv", + "fsl,B4220QDS-hv", + NULL +}; + +/* + * Called very early, device-tree isn't unflattened + */ +static int __init corenet_generic_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); +#ifdef CONFIG_SMP + extern struct smp_ops_t smp_85xx_ops; +#endif + + if (of_flat_dt_match(root, boards)) + return 1; + + /* Check if we're running under the Freescale hypervisor */ + if (of_flat_dt_match(root, hv_boards)) { + ppc_md.init_IRQ = ehv_pic_init; + ppc_md.get_irq = ehv_pic_get_irq; + ppc_md.restart = fsl_hv_restart; + ppc_md.power_off = fsl_hv_halt; + ppc_md.halt = fsl_hv_halt; +#ifdef CONFIG_SMP + /* + * Disable the timebase sync operations because we can't write + * to the timebase registers under the hypervisor. + */ + smp_85xx_ops.give_timebase = NULL; + smp_85xx_ops.take_timebase = NULL; +#endif + return 1; + } + + return 0; +} + +define_machine(corenet_generic) { + .name = "CoreNet Generic", + .probe = corenet_generic_probe, + .setup_arch = corenet_gen_setup_arch, + .init_IRQ = corenet_gen_pic_init, +#ifdef CONFIG_PCI + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#endif + .get_irq = mpic_get_coreint_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +#ifdef CONFIG_PPC64 + .power_save = book3e_idle, +#else + .power_save = e500_idle, +#endif +}; + +machine_arch_initcall(corenet_generic, corenet_gen_publish_devices); + +#ifdef CONFIG_SWIOTLB +machine_arch_initcall(corenet_generic, swiotlb_setup_bus_notifier); +#endif -- cgit v0.10.2 From 9e0967572e5a0e8c887b2d71192bdad342e8a3dd Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Thu, 26 Sep 2013 09:42:28 +0800 Subject: powerpc/85xx: use one kernel option for all the CoreNet_Generic boards Currently all these boards use the same machine struct and also select the same kernel options, so it seems a bit of redundant to keep one separate kernel option for each board. Also update the defconfigs according to this change. Signed-off-by: Kevin Hao Signed-off-by: Scott Wood diff --git a/arch/powerpc/configs/corenet32_smp_defconfig b/arch/powerpc/configs/corenet32_smp_defconfig index 71bc43a..bbd794d 100644 --- a/arch/powerpc/configs/corenet32_smp_defconfig +++ b/arch/powerpc/configs/corenet32_smp_defconfig @@ -23,11 +23,7 @@ CONFIG_MODVERSIONS=y # CONFIG_BLK_DEV_BSG is not set CONFIG_PARTITION_ADVANCED=y CONFIG_MAC_PARTITION=y -CONFIG_P2041_RDB=y -CONFIG_P3041_DS=y -CONFIG_P4080_DS=y -CONFIG_P5020_DS=y -CONFIG_P5040_DS=y +CONFIG_CORENET_GENERIC=y CONFIG_HIGHMEM=y # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set CONFIG_BINFMT_MISC=m diff --git a/arch/powerpc/configs/corenet64_smp_defconfig b/arch/powerpc/configs/corenet64_smp_defconfig index fa94fb3..63508dd 100644 --- a/arch/powerpc/configs/corenet64_smp_defconfig +++ b/arch/powerpc/configs/corenet64_smp_defconfig @@ -21,10 +21,7 @@ CONFIG_MODVERSIONS=y # CONFIG_BLK_DEV_BSG is not set CONFIG_PARTITION_ADVANCED=y CONFIG_MAC_PARTITION=y -CONFIG_B4_QDS=y -CONFIG_P5020_DS=y -CONFIG_P5040_DS=y -CONFIG_T4240_QDS=y +CONFIG_CORENET_GENERIC=y # CONFIG_PPC_OF_BOOT_TRAMPOLINE is not set CONFIG_BINFMT_MISC=m CONFIG_MATH_EMULATION=y diff --git a/arch/powerpc/configs/ppc64e_defconfig b/arch/powerpc/configs/ppc64e_defconfig index 0085dc4..0a6be6d 100644 --- a/arch/powerpc/configs/ppc64e_defconfig +++ b/arch/powerpc/configs/ppc64e_defconfig @@ -23,7 +23,7 @@ CONFIG_MODULE_SRCVERSION_ALL=y CONFIG_PARTITION_ADVANCED=y CONFIG_MAC_PARTITION=y CONFIG_EFI_PARTITION=y -CONFIG_P5020_DS=y +CONFIG_CORENET_GENERIC=y CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig index 3bee943..4d46349 100644 --- a/arch/powerpc/platforms/85xx/Kconfig +++ b/arch/powerpc/platforms/85xx/Kconfig @@ -218,88 +218,16 @@ config GE_IMP3A This board is a 3U CompactPCI Single Board Computer with a Freescale P2020 processor. -config P2041_RDB - bool "Freescale P2041 RDB" - select DEFAULT_UIMAGE - select PPC_E500MC - select PHYS_64BIT - select SWIOTLB - select ARCH_REQUIRE_GPIOLIB - select GPIO_MPC8XXX - select HAS_RAPIDIO - select PPC_EPAPR_HV_PIC - select CORENET_GENERIC - help - This option enables support for the P2041 RDB board - -config P3041_DS - bool "Freescale P3041 DS" - select DEFAULT_UIMAGE - select PPC_E500MC - select PHYS_64BIT - select SWIOTLB - select ARCH_REQUIRE_GPIOLIB - select GPIO_MPC8XXX - select HAS_RAPIDIO - select PPC_EPAPR_HV_PIC - select CORENET_GENERIC - help - This option enables support for the P3041 DS board - -config P4080_DS - bool "Freescale P4080 DS" - select DEFAULT_UIMAGE - select PPC_E500MC - select PHYS_64BIT - select SWIOTLB - select ARCH_REQUIRE_GPIOLIB - select GPIO_MPC8XXX - select HAS_RAPIDIO - select PPC_EPAPR_HV_PIC - select CORENET_GENERIC - help - This option enables support for the P4080 DS board - config SGY_CTS1000 tristate "Servergy CTS-1000 support" select GPIOLIB select OF_GPIO - depends on P4080_DS + depends on CORENET_GENERIC help Enable this to support functionality in Servergy's CTS-1000 systems. endif # PPC32 -config P5020_DS - bool "Freescale P5020 DS" - select DEFAULT_UIMAGE - select E500 - select PPC_E500MC - select PHYS_64BIT - select SWIOTLB - select ARCH_REQUIRE_GPIOLIB - select GPIO_MPC8XXX - select HAS_RAPIDIO - select PPC_EPAPR_HV_PIC - select CORENET_GENERIC - help - This option enables support for the P5020 DS board - -config P5040_DS - bool "Freescale P5040 DS" - select DEFAULT_UIMAGE - select E500 - select PPC_E500MC - select PHYS_64BIT - select SWIOTLB - select ARCH_REQUIRE_GPIOLIB - select GPIO_MPC8XXX - select HAS_RAPIDIO - select PPC_EPAPR_HV_PIC - select CORENET_GENERIC - help - This option enables support for the P5040 DS board - config PPC_QEMU_E500 bool "QEMU generic e500 platform" select DEFAULT_UIMAGE @@ -315,10 +243,8 @@ config PPC_QEMU_E500 unset based on the emulated CPU (or actual host CPU in the case of KVM). -if PPC64 - -config T4240_QDS - bool "Freescale T4240 QDS" +config CORENET_GENERIC + bool "Freescale CoreNet Generic" select DEFAULT_UIMAGE select E500 select PPC_E500MC @@ -328,33 +254,16 @@ config T4240_QDS select GPIO_MPC8XXX select HAS_RAPIDIO select PPC_EPAPR_HV_PIC - select CORENET_GENERIC help - This option enables support for the T4240 QDS board + This option enables support for the FSL CoreNet based boards. + For 32bit kernel, the following boards are supported: + P2041 RDB, P3041 DS and P4080 DS + For 64bit kernel, the following boards are supported: + T4240 QDS and B4 QDS + The following boards are supported for both 32bit and 64bit kernel: + P5020 DS and P5040 DS -config B4_QDS - bool "Freescale B4 QDS" - select DEFAULT_UIMAGE - select E500 - select PPC_E500MC - select PHYS_64BIT - select SWIOTLB - select GPIOLIB - select ARCH_REQUIRE_GPIOLIB - select HAS_RAPIDIO - select PPC_EPAPR_HV_PIC - select CORENET_GENERIC - help - This option enables support for the B4 QDS board - The B4 application development system B4 QDS is a complete - debugging environment intended for engineers developing - applications for the B4. - -endif endif # FSL_SOC_BOOKE config TQM85xx bool - -config CORENET_GENERIC - bool -- cgit v0.10.2 From 4e591f3c0a618b8230a37bfff64b59e76374f2e5 Mon Sep 17 00:00:00 2001 From: LEROY Christophe Date: Tue, 24 Sep 2013 10:18:39 +0200 Subject: powerpc/8xx: Fixing issue with CONFIG_PIN_TLB Activating CONFIG_PIN_TLB is supposed to pin the IMMR and the first three 8Mbytes pages. But the setting of MD_CTR to a pinnable entry was missing before the pinning of the third 8Mb page. As the index is decremented module 28 (MD_RSV4D is set) after every DTLB update, the third 8Mbytes page was not pinned. Signed-off-by: Christophe Leroy Signed-off-by: Scott Wood diff --git a/arch/powerpc/kernel/head_8xx.S b/arch/powerpc/kernel/head_8xx.S index 1b92a97..7ee876d 100644 --- a/arch/powerpc/kernel/head_8xx.S +++ b/arch/powerpc/kernel/head_8xx.S @@ -858,6 +858,9 @@ initial_mmu: addis r11, r11, 0x0080 /* Add 8M */ mtspr SPRN_MD_RPN, r11 + addi r10, r10, 0x0100 + mtspr SPRN_MD_CTR, r10 + addis r8, r8, 0x0080 /* Add 8M */ mtspr SPRN_MD_EPN, r8 mtspr SPRN_MD_TWC, r9 -- cgit v0.10.2 From 5f2da0f1e8fb074482118f04d0db0614951c3bb8 Mon Sep 17 00:00:00 2001 From: LEROY Christophe Date: Fri, 11 Oct 2013 14:56:40 +0200 Subject: powerpc/8xx: Fixing memory init issue with CONFIG_PIN_TLB Activating CONFIG_PIN_TLB allows access to the 24 first Mbytes of memory at bootup instead of 8. It is needed for "big" kernels for instance when activating CONFIG_LOCKDEP_SUPPORT. This needs to be taken into account in init_32 too, otherwise memory allocation soon fails after startup. Signed-off-by: Christophe Leroy Acked-by: Joakim Tjernlund Signed-off-by: Scott Wood diff --git a/arch/powerpc/mm/init_32.c b/arch/powerpc/mm/init_32.c index d47d3da..cff59f1 100644 --- a/arch/powerpc/mm/init_32.c +++ b/arch/powerpc/mm/init_32.c @@ -213,7 +213,12 @@ void setup_initial_memory_limit(phys_addr_t first_memblock_base, */ BUG_ON(first_memblock_base != 0); +#ifdef CONFIG_PIN_TLB + /* 8xx can only access 24MB at the moment */ + memblock_set_current_limit(min_t(u64, first_memblock_size, 0x01800000)); +#else /* 8xx can only access 8MB at the moment */ memblock_set_current_limit(min_t(u64, first_memblock_size, 0x00800000)); +#endif } #endif /* CONFIG_8xx */ -- cgit v0.10.2 From 8d7c0b527fba4891e5d8c74d78e80cac177bce5e Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 12 Oct 2013 15:13:19 +0800 Subject: powerpc/6xx: add missing iounmap() on error in hlwd_pic_init() Add the missing iounmap() before return from hlwd_pic_init() in the error handling case. Signed-off-by: Wei Yongjun Signed-off-by: Scott Wood diff --git a/arch/powerpc/platforms/embedded6xx/hlwd-pic.c b/arch/powerpc/platforms/embedded6xx/hlwd-pic.c index 3006b51..6f61e21 100644 --- a/arch/powerpc/platforms/embedded6xx/hlwd-pic.c +++ b/arch/powerpc/platforms/embedded6xx/hlwd-pic.c @@ -181,6 +181,7 @@ struct irq_domain *hlwd_pic_init(struct device_node *np) &hlwd_irq_domain_ops, io_base); if (!irq_domain) { pr_err("failed to allocate irq_domain\n"); + iounmap(io_base); return NULL; } -- cgit v0.10.2 From d82341b08b8105965267252b51c41fb35fc81156 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 13 Oct 2013 18:05:38 +0200 Subject: arch/powerpc/platforms/83xx: Remove obsolete cleanup for clientdata A few new i2c-drivers came into the kernel which clear the clientdata-pointer on exit or error. This is obsolete meanwhile, the core will do it. Signed-off-by: Wolfram Sang Signed-off-by: Scott Wood diff --git a/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c b/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c index 7bc3158..fd71cfd 100644 --- a/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c +++ b/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c @@ -204,7 +204,6 @@ static int mcu_remove(struct i2c_client *client) ret = mcu_gpiochip_remove(mcu); if (ret) return ret; - i2c_set_clientdata(client, NULL); kfree(mcu); return 0; } -- cgit v0.10.2 From fd1348d098b5d0f633c869b50a74cfcbeb9834f5 Mon Sep 17 00:00:00 2001 From: Zhao Qiang Date: Mon, 14 Oct 2013 14:46:13 +0800 Subject: powerpc/p1010rdb: add P1010RDB-PB platform support The P1010RDB-PB is similar to P1010RDB(P1010RDB-PA). So, P1010RDB-PB use the same platform file as P1010RDB. Then Add support for P1010RDB-PB platform. Signed-off-by: Zhao Qiang Signed-off-by: Scott Wood diff --git a/arch/powerpc/platforms/85xx/p1010rdb.c b/arch/powerpc/platforms/85xx/p1010rdb.c index 0252961..d6a3dd3 100644 --- a/arch/powerpc/platforms/85xx/p1010rdb.c +++ b/arch/powerpc/platforms/85xx/p1010rdb.c @@ -66,6 +66,8 @@ static int __init p1010_rdb_probe(void) if (of_flat_dt_is_compatible(root, "fsl,P1010RDB")) return 1; + if (of_flat_dt_is_compatible(root, "fsl,P1010RDB-PB")) + return 1; return 0; } -- cgit v0.10.2 From dde8e3ee6b37fa33690604302bebfe7b576c3802 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 25 Oct 2013 17:30:24 +0800 Subject: powerpc/mv643xx_eth: fix return check in mv64x60_eth_register_shared_pdev() In case of error, the function platform_device_register_simple() returns RR_PTR() and never returns NULL. The NULL test in the return value check should be replaced with IS_ERR(). Signed-off-by: Wei Yongjun Signed-off-by: Scott Wood diff --git a/arch/powerpc/sysdev/mv64x60_dev.c b/arch/powerpc/sysdev/mv64x60_dev.c index 4a25c26..a3a8fad 100644 --- a/arch/powerpc/sysdev/mv64x60_dev.c +++ b/arch/powerpc/sysdev/mv64x60_dev.c @@ -228,7 +228,7 @@ static struct platform_device * __init mv64x60_eth_register_shared_pdev( if (id == 0) { pdev = platform_device_register_simple("orion-mdio", -1, &r[1], 1); - if (!pdev) + if (IS_ERR(pdev)) return pdev; } -- cgit v0.10.2 From b60c5a7a82cdfec2221263ce259faa4a36696163 Mon Sep 17 00:00:00 2001 From: Christian Kujau Date: Mon, 28 Oct 2013 05:51:36 -0700 Subject: powerpc/6xx: CONFIG_MCU_MPC8349EMITX cannot be a module During "make ppc6xx_defconfig" the following happens: HOSTCC scripts/basic/fixdep GEN /usr/local/src/tmp/lnx/Makefile HOSTCC scripts/kconfig/conf.o HOSTCC scripts/kconfig/zconf.tab.o HOSTLD scripts/kconfig/conf arch/powerpc/configs/ppc6xx_defconfig:74:warning: symbol value 'm' invalid for MCU_MPC8349EMITX Setting CONFIG_MCU_MPC8349EMITX=y in ppc6xx_defconfig makes the warning go away. This too has been reported by Geert Uytterhoeven a long time ago: https://lkml.org/lkml/2011/11/13/11 - I only came across this because I needed a "clean" defconfig for this Powerbook G5. Signed-off-by: Christian Kujau [scottwood@freescale.com: cleaned up commit message slightly] Signed-off-by: Scott Wood diff --git a/arch/powerpc/configs/ppc6xx_defconfig b/arch/powerpc/configs/ppc6xx_defconfig index 20ebfaf..c2353bf 100644 --- a/arch/powerpc/configs/ppc6xx_defconfig +++ b/arch/powerpc/configs/ppc6xx_defconfig @@ -71,7 +71,7 @@ CONFIG_QUICC_ENGINE=y CONFIG_QE_GPIO=y CONFIG_PPC_BESTCOMM=y CONFIG_GPIO_MPC8XXX=y -CONFIG_MCU_MPC8349EMITX=m +CONFIG_MCU_MPC8349EMITX=y CONFIG_HIGHMEM=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y -- cgit v0.10.2 From a3821b2af185b64e3382c45fbdaa2cbc91ce14b8 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Mon, 28 Oct 2013 22:07:59 -0500 Subject: powerpc: Fix PPC_EMULATED_STATS build break with sync patch Commit 9863c28a2af90a56c088f5f6288d7f6d2c923c14 ("powerpc: Emulate sync instruction variants") introduced a build breakage with CONFIG_PPC_EMULATED_STATS enabled. Signed-off-by: Scott Wood Cc: Kumar Gala Cc: James Yang --- diff --git a/arch/powerpc/include/asm/emulated_ops.h b/arch/powerpc/include/asm/emulated_ops.h index 5a8b82a..4358e30 100644 --- a/arch/powerpc/include/asm/emulated_ops.h +++ b/arch/powerpc/include/asm/emulated_ops.h @@ -43,6 +43,7 @@ extern struct ppc_emulated { struct ppc_emulated_entry popcntb; struct ppc_emulated_entry spe; struct ppc_emulated_entry string; + struct ppc_emulated_entry sync; struct ppc_emulated_entry unaligned; #ifdef CONFIG_MATH_EMULATION struct ppc_emulated_entry math; diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index ad20dcf..62c3dd8 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -1820,6 +1820,7 @@ struct ppc_emulated ppc_emulated = { WARN_EMULATED_SETUP(popcntb), WARN_EMULATED_SETUP(spe), WARN_EMULATED_SETUP(string), + WARN_EMULATED_SETUP(sync), WARN_EMULATED_SETUP(unaligned), #ifdef CONFIG_MATH_EMULATION WARN_EMULATED_SETUP(math), -- cgit v0.10.2 From 9b389a8a022110b4bc055a19b888283544d9eba6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 28 Oct 2013 11:24:23 +0100 Subject: ALSA: 6fire: Fix probe of multiple cards The probe code of snd-usb-6fire driver overrides the devices[] pointer wrongly without checking whether it's already occupied or not. This would screw up the device disconnection later. Spotted by coverity CID 141423. Cc: Signed-off-by: Takashi Iwai diff --git a/sound/usb/6fire/chip.c b/sound/usb/6fire/chip.c index c39c779..66edc4a 100644 --- a/sound/usb/6fire/chip.c +++ b/sound/usb/6fire/chip.c @@ -101,7 +101,7 @@ static int usb6fire_chip_probe(struct usb_interface *intf, usb_set_intfdata(intf, chips[i]); mutex_unlock(®ister_mutex); return 0; - } else if (regidx < 0) + } else if (!devices[i] && regidx < 0) regidx = i; } if (regidx < 0) { -- cgit v0.10.2 From 728deecdd4e9b4a58faf518638169c743c57cee2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 28 Oct 2013 11:39:23 +0100 Subject: ALSA: hda - Fix possible NULL dereference in snd_hda_get_pin_label() Fix a possible NULL access of indexp in fill_audio_out_name() called from snd_hda_get_pin_label(). Spotted by coverity CID 402035. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 48a9d00..853c6a6 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -638,7 +638,7 @@ static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, /* don't add channel suffix for Headphone controls */ int idx = get_hp_label_index(codec, nid, cfg->hp_pins, cfg->hp_outs); - if (idx >= 0) + if (idx >= 0 && indexp) *indexp = idx; sfx = ""; } -- cgit v0.10.2 From 57d8ff617f871eee11bb2f648505592c2e859d0f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 28 Oct 2013 11:42:56 +0100 Subject: ALSA: hda - Add a fallthru comment ... to improve the readability. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index 20d065a..98bce98 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -110,6 +110,7 @@ static int snd_hda_beep_event(struct input_dev *dev, unsigned int type, case SND_BELL: if (hz) hz = 1000; + /* fallthru */ case SND_TONE: if (beep->linear_tone) beep->tone = beep_linear_tone(beep, hz); -- cgit v0.10.2 From ca16ec02e1887cc88e9eb3504ee8ea6d8112d05d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 28 Oct 2013 12:00:35 +0100 Subject: ALSA: hda - Remove locally dead codes Reported by coverity. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 0faab3b..de1a767 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2634,8 +2634,7 @@ static int map_slaves(struct hda_codec *codec, const char * const *slaves, items = codec->mixers.list; for (i = 0; i < codec->mixers.used; i++) { struct snd_kcontrol *sctl = items[i].kctl; - if (!sctl || !sctl->id.name || - sctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER) + if (!sctl || sctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER) continue; for (s = slaves; *s; s++) { char tmpname[sizeof(sctl->id.name)]; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 1f0a040..8947035 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -554,8 +554,6 @@ do_sku: nid = portd; else if (tmp == 3) nid = porti; - else - return 1; if (found_in_nid_list(nid, spec->gen.autocfg.line_out_pins, spec->gen.autocfg.line_outs)) return 1; -- cgit v0.10.2 From 1f96153bc613c526f5ea39a7f366b3bdf70249be Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 28 Oct 2013 12:40:46 +0100 Subject: ALSA: pcm: Add fallthru comments Just to improve readability. Spotted by coverity CID 115002 and 115003. Signed-off-by: Takashi Iwai diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index b71be57..01a5e05 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -2428,6 +2428,7 @@ static int snd_pcm_hwsync(struct snd_pcm_substream *substream) case SNDRV_PCM_STATE_DRAINING: if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) goto __badfd; + /* Fall through */ case SNDRV_PCM_STATE_RUNNING: if ((err = snd_pcm_update_hw_ptr(substream)) < 0) break; @@ -2460,6 +2461,7 @@ static int snd_pcm_delay(struct snd_pcm_substream *substream, case SNDRV_PCM_STATE_DRAINING: if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) goto __badfd; + /* Fall through */ case SNDRV_PCM_STATE_RUNNING: if ((err = snd_pcm_update_hw_ptr(substream)) < 0) break; -- cgit v0.10.2 From 60f6fef877d52525b8887d27cfacf74a279e3e12 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 28 Oct 2013 12:54:52 +0100 Subject: ALSA: Optimize module name check module->name is a fixed array, so we can check the empty contents straightforwardly in module_slot_match(). Spotted by coverity CID 1056786. Signed-off-by: Takashi Iwai diff --git a/sound/core/init.c b/sound/core/init.c index 6b90871..01a8938 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -66,7 +66,7 @@ static int module_slot_match(struct module *module, int idx) #ifdef MODULE const char *s1, *s2; - if (!module || !module->name || !slots[idx]) + if (!module || !*module->name || !slots[idx]) return 0; s1 = module->name; -- cgit v0.10.2 From b327d25c1c3d475d1d1217be520801283e8bdf29 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Tue, 29 Oct 2013 12:05:02 +0900 Subject: ALSA: Fix typo in documentation/alsa Correct spelling typo in documentation/alsa Signed-off-by: Masanari Iida Signed-off-by: Takashi Iwai diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index 95731a0..b8dd0df 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -616,7 +616,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. As default, snd-dummy drivers doesn't allocate the real buffers but either ignores read/write or mmap a single dummy page to all - buffer pages, in order to save the resouces. If your apps need + buffer pages, in order to save the resources. If your apps need the read/ written buffer data to be consistent, pass fake_buffer=0 option. diff --git a/Documentation/sound/alsa/Audiophile-Usb.txt b/Documentation/sound/alsa/Audiophile-Usb.txt index 654dd3b..e7a5ed4 100644 --- a/Documentation/sound/alsa/Audiophile-Usb.txt +++ b/Documentation/sound/alsa/Audiophile-Usb.txt @@ -232,7 +232,7 @@ The parameter can be given: # modprobe snd-usb-audio index=1 device_setup=0x09 * Or while configuring the modules options in your modules configuration file - (tipically a .conf file in /etc/modprobe.d/ directory: + (typically a .conf file in /etc/modprobe.d/ directory: alias snd-card-1 snd-usb-audio options snd-usb-audio index=1 device_setup=0x09 diff --git a/Documentation/sound/alsa/CMIPCI.txt b/Documentation/sound/alsa/CMIPCI.txt index 16935c8..4e36e6e 100644 --- a/Documentation/sound/alsa/CMIPCI.txt +++ b/Documentation/sound/alsa/CMIPCI.txt @@ -87,7 +87,7 @@ with 4 channels, and use the interleaved 4 channel data. -There are some control switchs affecting to the speaker connections: +There are some control switches affecting to the speaker connections: "Line-In Mode" - an enum control to change the behavior of line-in jack. Either "Line-In", "Rear Output" or "Bass Output" can diff --git a/Documentation/sound/alsa/compress_offload.txt b/Documentation/sound/alsa/compress_offload.txt index fd74ff2..630c492 100644 --- a/Documentation/sound/alsa/compress_offload.txt +++ b/Documentation/sound/alsa/compress_offload.txt @@ -217,12 +217,12 @@ Not supported: would be enabled with ALSA kcontrols. - Audio policy/resource management. This API does not provide any - hooks to query the utilization of the audio DSP, nor any premption + hooks to query the utilization of the audio DSP, nor any preemption mechanisms. -- No notion of underun/overrun. Since the bytes written are compressed +- No notion of underrun/overrun. Since the bytes written are compressed in nature and data written/read doesn't translate directly to - rendered output in time, this does not deal with underrun/overun and + rendered output in time, this does not deal with underrun/overrun and maybe dealt in user-library Credits: diff --git a/Documentation/sound/alsa/soc/DPCM.txt b/Documentation/sound/alsa/soc/DPCM.txt index aa8546f..0110180 100644 --- a/Documentation/sound/alsa/soc/DPCM.txt +++ b/Documentation/sound/alsa/soc/DPCM.txt @@ -192,7 +192,7 @@ This BE DAI link connects DAI0 to the codec (in this case RT5460 AIF1). It sets the "no_pcm" flag to mark it has a BE and sets flags for supported stream directions using "dpcm_playback" and "dpcm_capture" above. -The BE has also flags set for ignoreing suspend and PM down time. This allows +The BE has also flags set for ignoring suspend and PM down time. This allows the BE to work in a hostless mode where the host CPU is not transferring data like a BT phone call :- @@ -328,7 +328,7 @@ The host can control the hostless link either by :- between both DAIs. 2) Hostless FE. This FE has a virtual connection to the BE DAI links on the DAPM - graph. Control is then carried out by the FE as regualar PCM operations. + graph. Control is then carried out by the FE as regular PCM operations. This method gives more control over the DAI links, but requires much more userspace code to control the link. Its recommended to use CODEC<->CODEC unless your HW needs more fine grained sequencing of the PCM ops. diff --git a/Documentation/sound/alsa/soc/dapm.txt b/Documentation/sound/alsa/soc/dapm.txt index 7dfd88c..6faab48 100644 --- a/Documentation/sound/alsa/soc/dapm.txt +++ b/Documentation/sound/alsa/soc/dapm.txt @@ -30,7 +30,7 @@ There are 4 power domains within DAPM machine driver and responds to asynchronous events e.g when HP are inserted - 3. Path domain - audio susbsystem signal paths + 3. Path domain - audio subsystem signal paths Automatically set when mixer and mux settings are changed by the user. e.g. alsamixer, amixer. @@ -64,7 +64,7 @@ Audio DAPM widgets fall into a number of types:- o Speaker - Speaker o Supply - Power or clock supply widget used by other widgets. o Regulator - External regulator that supplies power to audio components. - o Clock - External clock that supplies clock to audio componnents. + o Clock - External clock that supplies clock to audio components. o AIF IN - Audio Interface Input (with TDM slot mask). o AIF OUT - Audio Interface Output (with TDM slot mask). o Siggen - Signal Generator. -- cgit v0.10.2 From 9f694bc7936a7e4e9c9efac2900cddaf71303c0a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 29 Oct 2013 11:56:21 +0100 Subject: ALSA: memalloc: Make snd_{malloc|free}_dev_iram() static These are used only locally. Signed-off-by: Takashi Iwai diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index 809fd79..278248b 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -167,7 +167,7 @@ static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr, * * This function requires iram phandle provided via of_node */ -void snd_malloc_dev_iram(struct snd_dma_buffer *dmab, size_t size) +static void snd_malloc_dev_iram(struct snd_dma_buffer *dmab, size_t size) { struct device *dev = dmab->dev.dev; struct gen_pool *pool = NULL; @@ -192,7 +192,7 @@ void snd_malloc_dev_iram(struct snd_dma_buffer *dmab, size_t size) * snd_free_dev_iram - free allocated specific memory from on-chip internal ram * @dmab: buffer allocation record to store the allocated data */ -void snd_free_dev_iram(struct snd_dma_buffer *dmab) +static void snd_free_dev_iram(struct snd_dma_buffer *dmab) { struct gen_pool *pool = dmab->private_data; -- cgit v0.10.2 From a40a3937222c728be925f2d78650cfe9b20be3f5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 29 Oct 2013 11:59:31 +0100 Subject: ALSA: memalloc: NULL-initialize in snd_malloc_dev_iram() dmab->area and addr fields should be cleared at the head of snd_malloc_dev_iram() as especially dmab->area is used to indicate the allocation failure / fallback. Signed-off-by: Takashi Iwai diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index 278248b..9d93f02 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -172,6 +172,9 @@ static void snd_malloc_dev_iram(struct snd_dma_buffer *dmab, size_t size) struct device *dev = dmab->dev.dev; struct gen_pool *pool = NULL; + dmab->area = NULL; + dmab->addr = 0; + if (dev->of_node) pool = of_get_named_gen_pool(dev->of_node, "iram", 0); -- cgit v0.10.2 From 1ee14e6c8cddeeb8a490d7b54cd9016e4bb900b4 Mon Sep 17 00:00:00 2001 From: Ben Segall Date: Wed, 16 Oct 2013 11:16:12 -0700 Subject: sched: Fix race on toggling cfs_bandwidth_used When we transition cfs_bandwidth_used to false, any currently throttled groups will incorrectly return false from cfs_rq_throttled. While tg_set_cfs_bandwidth will unthrottle them eventually, currently running code (including at least dequeue_task_fair and distribute_cfs_runtime) will cause errors. Fix this by turning off cfs_bandwidth_used only after unthrottling all cfs_rqs. Tested: toggle bandwidth back and forth on a loaded cgroup. Caused crashes in minutes without the patch, hasn't crashed with it. Signed-off-by: Ben Segall Signed-off-by: Peter Zijlstra Cc: pjt@google.com Link: http://lkml.kernel.org/r/20131016181611.22647.80365.stgit@sword-of-the-dawn.mtv.corp.google.com Signed-off-by: Ingo Molnar diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 7c61f31..450a34b 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -7436,7 +7436,12 @@ static int tg_set_cfs_bandwidth(struct task_group *tg, u64 period, u64 quota) runtime_enabled = quota != RUNTIME_INF; runtime_was_enabled = cfs_b->quota != RUNTIME_INF; - account_cfs_bandwidth_used(runtime_enabled, runtime_was_enabled); + /* + * If we need to toggle cfs_bandwidth_used, off->on must occur + * before making related changes, and on->off must occur afterwards + */ + if (runtime_enabled && !runtime_was_enabled) + cfs_bandwidth_usage_inc(); raw_spin_lock_irq(&cfs_b->lock); cfs_b->period = ns_to_ktime(period); cfs_b->quota = quota; @@ -7462,6 +7467,8 @@ static int tg_set_cfs_bandwidth(struct task_group *tg, u64 period, u64 quota) unthrottle_cfs_rq(cfs_rq); raw_spin_unlock_irq(&rq->lock); } + if (runtime_was_enabled && !runtime_enabled) + cfs_bandwidth_usage_dec(); out_unlock: mutex_unlock(&cfs_constraints_mutex); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 813dd61..ebd187f 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -2845,13 +2845,14 @@ static inline bool cfs_bandwidth_used(void) return static_key_false(&__cfs_bandwidth_used); } -void account_cfs_bandwidth_used(int enabled, int was_enabled) +void cfs_bandwidth_usage_inc(void) { - /* only need to count groups transitioning between enabled/!enabled */ - if (enabled && !was_enabled) - static_key_slow_inc(&__cfs_bandwidth_used); - else if (!enabled && was_enabled) - static_key_slow_dec(&__cfs_bandwidth_used); + static_key_slow_inc(&__cfs_bandwidth_used); +} + +void cfs_bandwidth_usage_dec(void) +{ + static_key_slow_dec(&__cfs_bandwidth_used); } #else /* HAVE_JUMP_LABEL */ static bool cfs_bandwidth_used(void) @@ -2859,7 +2860,8 @@ static bool cfs_bandwidth_used(void) return true; } -void account_cfs_bandwidth_used(int enabled, int was_enabled) {} +void cfs_bandwidth_usage_inc(void) {} +void cfs_bandwidth_usage_dec(void) {} #endif /* HAVE_JUMP_LABEL */ /* diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index ffc7087..4e650ac 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1352,7 +1352,8 @@ extern void print_rt_stats(struct seq_file *m, int cpu); extern void init_cfs_rq(struct cfs_rq *cfs_rq); extern void init_rt_rq(struct rt_rq *rt_rq, struct rq *rq); -extern void account_cfs_bandwidth_used(int enabled, int was_enabled); +extern void cfs_bandwidth_usage_inc(void); +extern void cfs_bandwidth_usage_dec(void); #ifdef CONFIG_NO_HZ_COMMON enum rq_nohz_flag_bits { -- cgit v0.10.2 From db06e78cc13d70f10877e0557becc88ab3ad2be8 Mon Sep 17 00:00:00 2001 From: Ben Segall Date: Wed, 16 Oct 2013 11:16:17 -0700 Subject: sched: Fix cfs_bandwidth misuse of hrtimer_expires_remaining hrtimer_expires_remaining does not take internal hrtimer locks and thus must be guarded against concurrent __hrtimer_start_range_ns (but returning HRTIMER_RESTART is safe). Use cfs_b->lock to make it safe. Signed-off-by: Ben Segall Signed-off-by: Peter Zijlstra Cc: pjt@google.com Link: http://lkml.kernel.org/r/20131016181617.22647.73829.stgit@sword-of-the-dawn.mtv.corp.google.com Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index ebd187f..897d977 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -3285,7 +3285,13 @@ static const u64 min_bandwidth_expiration = 2 * NSEC_PER_MSEC; /* how long we wait to gather additional slack before distributing */ static const u64 cfs_bandwidth_slack_period = 5 * NSEC_PER_MSEC; -/* are we near the end of the current quota period? */ +/* + * Are we near the end of the current quota period? + * + * Requires cfs_b->lock for hrtimer_expires_remaining to be safe against the + * hrtimer base being cleared by __hrtimer_start_range_ns. In the case of + * migrate_hrtimers, base is never cleared, so we are fine. + */ static int runtime_refresh_within(struct cfs_bandwidth *cfs_b, u64 min_expire) { struct hrtimer *refresh_timer = &cfs_b->period_timer; @@ -3361,10 +3367,12 @@ static void do_sched_cfs_slack_timer(struct cfs_bandwidth *cfs_b) u64 expires; /* confirm we're still not at a refresh boundary */ - if (runtime_refresh_within(cfs_b, min_bandwidth_expiration)) + raw_spin_lock(&cfs_b->lock); + if (runtime_refresh_within(cfs_b, min_bandwidth_expiration)) { + raw_spin_unlock(&cfs_b->lock); return; + } - raw_spin_lock(&cfs_b->lock); if (cfs_b->quota != RUNTIME_INF && cfs_b->runtime > slice) { runtime = cfs_b->runtime; cfs_b->runtime = 0; -- cgit v0.10.2 From 927b54fccbf04207ec92f669dce6806848cbec7d Mon Sep 17 00:00:00 2001 From: Ben Segall Date: Wed, 16 Oct 2013 11:16:22 -0700 Subject: sched: Fix hrtimer_cancel()/rq->lock deadlock __start_cfs_bandwidth calls hrtimer_cancel while holding rq->lock, waiting for the hrtimer to finish. However, if sched_cfs_period_timer runs for another loop iteration, the hrtimer can attempt to take rq->lock, resulting in deadlock. Fix this by ensuring that cfs_b->timer_active is cleared only if the _latest_ call to do_sched_cfs_period_timer is returning as idle. Then __start_cfs_bandwidth can just call hrtimer_try_to_cancel and wait for that to succeed or timer_active == 1. Signed-off-by: Ben Segall Signed-off-by: Peter Zijlstra Cc: pjt@google.com Link: http://lkml.kernel.org/r/20131016181622.22647.16643.stgit@sword-of-the-dawn.mtv.corp.google.com Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 897d977..f6308cb 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -3225,6 +3225,13 @@ static int do_sched_cfs_period_timer(struct cfs_bandwidth *cfs_b, int overrun) if (idle) goto out_unlock; + /* + * if we have relooped after returning idle once, we need to update our + * status as actually running, so that other cpus doing + * __start_cfs_bandwidth will stop trying to cancel us. + */ + cfs_b->timer_active = 1; + __refill_cfs_bandwidth_runtime(cfs_b); if (!throttled) { @@ -3493,11 +3500,11 @@ void __start_cfs_bandwidth(struct cfs_bandwidth *cfs_b) * (timer_active==0 becomes visible before the hrtimer call-back * terminates). In either case we ensure that it's re-programmed */ - while (unlikely(hrtimer_active(&cfs_b->period_timer))) { + while (unlikely(hrtimer_active(&cfs_b->period_timer)) && + hrtimer_try_to_cancel(&cfs_b->period_timer) < 0) { + /* bounce the lock to allow do_sched_cfs_period_timer to run */ raw_spin_unlock(&cfs_b->lock); - /* ensure cfs_b->lock is available while we wait */ - hrtimer_cancel(&cfs_b->period_timer); - + cpu_relax(); raw_spin_lock(&cfs_b->lock); /* if someone else restarted the timer then we're done */ if (cfs_b->timer_active) -- cgit v0.10.2 From 0ac9b1c21874d2490331233b3242085f8151e166 Mon Sep 17 00:00:00 2001 From: Paul Turner Date: Wed, 16 Oct 2013 11:16:27 -0700 Subject: sched: Guarantee new group-entities always have weight Currently, group entity load-weights are initialized to zero. This admits some races with respect to the first time they are re-weighted in earlty use. ( Let g[x] denote the se for "g" on cpu "x". ) Suppose that we have root->a and that a enters a throttled state, immediately followed by a[0]->t1 (the only task running on cpu[0]) blocking: put_prev_task(group_cfs_rq(a[0]), t1) put_prev_entity(..., t1) check_cfs_rq_runtime(group_cfs_rq(a[0])) throttle_cfs_rq(group_cfs_rq(a[0])) Then, before unthrottling occurs, let a[0]->b[0]->t2 wake for the first time: enqueue_task_fair(rq[0], t2) enqueue_entity(group_cfs_rq(b[0]), t2) enqueue_entity_load_avg(group_cfs_rq(b[0]), t2) account_entity_enqueue(group_cfs_ra(b[0]), t2) update_cfs_shares(group_cfs_rq(b[0])) < skipped because b is part of a throttled hierarchy > enqueue_entity(group_cfs_rq(a[0]), b[0]) ... We now have b[0] enqueued, yet group_cfs_rq(a[0])->load.weight == 0 which violates invariants in several code-paths. Eliminate the possibility of this by initializing group entity weight. Signed-off-by: Paul Turner Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20131016181627.22647.47543.stgit@sword-of-the-dawn.mtv.corp.google.com Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index f6308cb..0923ab2 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -7198,7 +7198,8 @@ void init_tg_cfs_entry(struct task_group *tg, struct cfs_rq *cfs_rq, se->cfs_rq = parent->my_q; se->my_q = cfs_rq; - update_load_set(&se->load, 0); + /* guarantee group entities always have weight */ + update_load_set(&se->load, NICE_0_LOAD); se->parent = parent; } -- cgit v0.10.2 From f9f9ffc237dd924f048204e8799da74f9ecf40cf Mon Sep 17 00:00:00 2001 From: Ben Segall Date: Wed, 16 Oct 2013 11:16:32 -0700 Subject: sched: Avoid throttle_cfs_rq() racing with period_timer stopping throttle_cfs_rq() doesn't check to make sure that period_timer is running, and while update_curr/assign_cfs_runtime does, a concurrently running period_timer on another cpu could cancel itself between this cpu's update_curr and throttle_cfs_rq(). If there are no other cfs_rqs running in the tg to restart the timer, this causes the cfs_rq to be stranded forever. Fix this by calling __start_cfs_bandwidth() in throttle if the timer is inactive. (Also add some sched_debug lines for cfs_bandwidth.) Tested: make a run/sleep task in a cgroup, loop switching the cgroup between 1ms/100ms quota and unlimited, checking for timer_active=0 and throttled=1 as a failure. With the throttle_cfs_rq() change commented out this fails, with the full patch it passes. Signed-off-by: Ben Segall Signed-off-by: Peter Zijlstra Cc: pjt@google.com Link: http://lkml.kernel.org/r/20131016181632.22647.84174.stgit@sword-of-the-dawn.mtv.corp.google.com Signed-off-by: Ingo Molnar diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c index e6ba5e3..5c34d18 100644 --- a/kernel/sched/debug.c +++ b/kernel/sched/debug.c @@ -229,6 +229,14 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq) atomic_read(&cfs_rq->tg->runnable_avg)); #endif #endif +#ifdef CONFIG_CFS_BANDWIDTH + SEQ_printf(m, " .%-30s: %d\n", "tg->cfs_bandwidth.timer_active", + cfs_rq->tg->cfs_bandwidth.timer_active); + SEQ_printf(m, " .%-30s: %d\n", "throttled", + cfs_rq->throttled); + SEQ_printf(m, " .%-30s: %d\n", "throttle_count", + cfs_rq->throttle_count); +#endif #ifdef CONFIG_FAIR_GROUP_SCHED print_cfs_group_stats(m, cpu, cfs_rq->tg); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 0923ab2..41c02b6 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -3112,6 +3112,8 @@ static void throttle_cfs_rq(struct cfs_rq *cfs_rq) cfs_rq->throttled_clock = rq_clock(rq); raw_spin_lock(&cfs_b->lock); list_add_tail_rcu(&cfs_rq->throttled_list, &cfs_b->throttled_cfs_rq); + if (!cfs_b->timer_active) + __start_cfs_bandwidth(cfs_b); raw_spin_unlock(&cfs_b->lock); } -- cgit v0.10.2 From d9494cb4299da66541a3f3ab82c552889bee0606 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 17 Oct 2013 15:36:19 +0200 Subject: perf: Remove useless atomic_t There's nothing atomic about atomic_set vs atomic_read; so remove the atomic_t usage. Also, make running_sample_length static as it really is (and should be) local to this translation unit. Signed-off-by: Peter Zijlstra Cc: eranian@google.com Cc: Don Zickus Cc: jmario@redhat.com Cc: acme@infradead.org Cc: dave.hansen@linux.intel.com Link: http://lkml.kernel.org/n/tip-vw9lg588x1ic248whybjon0c@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/kernel/events/core.c b/kernel/events/core.c index 5bd7fe4..028dad9 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -175,8 +175,8 @@ int sysctl_perf_event_sample_rate __read_mostly = DEFAULT_MAX_SAMPLE_RATE; static int max_samples_per_tick __read_mostly = DIV_ROUND_UP(DEFAULT_MAX_SAMPLE_RATE, HZ); static int perf_sample_period_ns __read_mostly = DEFAULT_SAMPLE_PERIOD_NS; -static atomic_t perf_sample_allowed_ns __read_mostly = - ATOMIC_INIT( DEFAULT_SAMPLE_PERIOD_NS * DEFAULT_CPU_TIME_MAX_PERCENT / 100); +static int perf_sample_allowed_ns __read_mostly = + DEFAULT_SAMPLE_PERIOD_NS * DEFAULT_CPU_TIME_MAX_PERCENT / 100; void update_perf_cpu_limits(void) { @@ -184,7 +184,7 @@ void update_perf_cpu_limits(void) tmp *= sysctl_perf_cpu_time_max_percent; do_div(tmp, 100); - atomic_set(&perf_sample_allowed_ns, tmp); + ACCESS_ONCE(perf_sample_allowed_ns) = tmp; } static int perf_rotate_context(struct perf_cpu_context *cpuctx); @@ -228,14 +228,15 @@ int perf_cpu_time_max_percent_handler(struct ctl_table *table, int write, * we detect that events are taking too long. */ #define NR_ACCUMULATED_SAMPLES 128 -DEFINE_PER_CPU(u64, running_sample_length); +static DEFINE_PER_CPU(u64, running_sample_length); void perf_sample_event_took(u64 sample_len_ns) { u64 avg_local_sample_len; u64 local_samples_len; + u64 allowed_ns = ACCESS_ONCE(perf_sample_allowed_ns); - if (atomic_read(&perf_sample_allowed_ns) == 0) + if (allowed_ns == 0) return; /* decay the counter by 1 average sample */ @@ -251,7 +252,7 @@ void perf_sample_event_took(u64 sample_len_ns) */ avg_local_sample_len = local_samples_len/NR_ACCUMULATED_SAMPLES; - if (avg_local_sample_len <= atomic_read(&perf_sample_allowed_ns)) + if (avg_local_sample_len <= allowed_ns) return; if (max_samples_per_tick <= 1) @@ -262,10 +263,9 @@ void perf_sample_event_took(u64 sample_len_ns) perf_sample_period_ns = NSEC_PER_SEC / sysctl_perf_event_sample_rate; printk_ratelimited(KERN_WARNING - "perf samples too long (%lld > %d), lowering " + "perf samples too long (%lld > %lld), lowering " "kernel.perf_event_max_sample_rate to %d\n", - avg_local_sample_len, - atomic_read(&perf_sample_allowed_ns), + avg_local_sample_len, allowed_ns, sysctl_perf_event_sample_rate); update_perf_cpu_limits(); -- cgit v0.10.2 From 32c5fb7e7d18b4fd37c5e29dea731151e9d66866 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 16 Oct 2013 22:09:45 +0200 Subject: perf: Kill the dead !vma->vm_mm code in perf_event_mmap_event() 1. perf_event_mmap(vma) is never called with a gate_vma-like arg, remove the "if (!vma->vm_mm)" code. 2. arch_vma_name() can use the chached value of mmap_event->vma. 3. Change the code to not call arch_vma_name() twice. 4. Purely cosmetic, but since we use "goto got_name" all the time remove "else" from "[stack]" branch just for symmetry. Signed-off-by: Oleg Nesterov Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20131016200945.GB23214@redhat.com Signed-off-by: Ingo Molnar diff --git a/kernel/events/core.c b/kernel/events/core.c index 028dad9..3ea5605 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -5136,21 +5136,19 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event) min = MINOR(dev); } else { - if (arch_vma_name(mmap_event->vma)) { - name = strncpy(tmp, arch_vma_name(mmap_event->vma), - sizeof(tmp) - 1); + name = arch_vma_name(vma); + if (name) { + name = strncpy(tmp, name, sizeof(tmp) - 1); tmp[sizeof(tmp) - 1] = '\0'; goto got_name; } - if (!vma->vm_mm) { - name = strncpy(tmp, "[vdso]", sizeof(tmp)); - goto got_name; - } else if (vma->vm_start <= vma->vm_mm->start_brk && + if (vma->vm_start <= vma->vm_mm->start_brk && vma->vm_end >= vma->vm_mm->brk) { name = strncpy(tmp, "[heap]", sizeof(tmp)); goto got_name; - } else if (vma->vm_start <= vma->vm_mm->start_stack && + } + if (vma->vm_start <= vma->vm_mm->start_stack && vma->vm_end >= vma->vm_mm->start_stack) { name = strncpy(tmp, "[stack]", sizeof(tmp)); goto got_name; -- cgit v0.10.2 From 3ea2f2b96f9e636f49eb10962e96db3e19cab157 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 16 Oct 2013 22:10:04 +0200 Subject: perf: Do not waste PAGE_SIZE bytes for ALIGN(8) in perf_event_mmap_event() perf_event_mmap_event() does kzalloc(PATH_MAX + sizeof(u64)) to ensure we can align the size later. However this means that we actually allocate PAGE_SIZE * 2 buffer, seems too much. Change this code to allocate PATH_MAX==PAGE_SIZE bytes, but tell d_path() to not use the last sizeof(u64) bytes. Note: it is not clear why do we need __GFP_ZERO, see the next patch. Signed-off-by: Oleg Nesterov Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20131016201004.GC23214@redhat.com Signed-off-by: Ingo Molnar diff --git a/kernel/events/core.c b/kernel/events/core.c index 3ea5605..b409e75 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -5113,17 +5113,18 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event) if (file) { struct inode *inode; dev_t dev; - /* - * d_path works from the end of the rb backwards, so we - * need to add enough zero bytes after the string to handle - * the 64bit alignment we do later. - */ - buf = kzalloc(PATH_MAX + sizeof(u64), GFP_KERNEL); + + buf = kzalloc(PATH_MAX, GFP_KERNEL); if (!buf) { name = strncpy(tmp, "//enomem", sizeof(tmp)); goto got_name; } - name = d_path(&file->f_path, buf, PATH_MAX); + /* + * d_path() works from the end of the rb backwards, so we + * need to add enough zero bytes after the string to handle + * the 64bit alignment we do later. + */ + name = d_path(&file->f_path, buf, PATH_MAX - sizeof(u64)); if (IS_ERR(name)) { name = strncpy(tmp, "//toolong", sizeof(tmp)); goto got_name; -- cgit v0.10.2 From 2c42cfbfe10872929c2ba1f8130e31063ff59b94 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 17 Oct 2013 00:06:46 +0200 Subject: perf: Change zero-padding of strings in perf_event_mmap_event() Oleg complained about the excessive 0-ing in perf_event_mmap_event(), so try and be smarter about it while keeping it fairly fool proof and avoid leaking random bits out to userspace. Suggested-by: Oleg Nesterov Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-8jirlm99m6if2z13wd6rbyu6@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/kernel/events/core.c b/kernel/events/core.c index b409e75..85a8bbd 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -5106,15 +5106,13 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event) unsigned int size; char tmp[16]; char *buf = NULL; - const char *name; - - memset(tmp, 0, sizeof(tmp)); + char *name; if (file) { struct inode *inode; dev_t dev; - buf = kzalloc(PATH_MAX, GFP_KERNEL); + buf = kmalloc(PATH_MAX, GFP_KERNEL); if (!buf) { name = strncpy(tmp, "//enomem", sizeof(tmp)); goto got_name; @@ -5137,7 +5135,7 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event) min = MINOR(dev); } else { - name = arch_vma_name(vma); + name = (char *)arch_vma_name(vma); if (name) { name = strncpy(tmp, name, sizeof(tmp) - 1); tmp[sizeof(tmp) - 1] = '\0'; @@ -5160,7 +5158,14 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event) } got_name: - size = ALIGN(strlen(name)+1, sizeof(u64)); + /* + * Since our buffer works in 8 byte units we need to align our string + * size to a multiple of 8. However, we must guarantee the tail end is + * zero'd out to avoid leaking random bits to userspace. + */ + size = strlen(name)+1; + while (!IS_ALIGNED(size, sizeof(u64))) + name[size++] = '\0'; mmap_event->file_name = name; mmap_event->file_size = size; -- cgit v0.10.2 From e00b12e64be9a34ef071de7b6052ca9ea29dd460 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 24 Oct 2013 12:52:06 +0200 Subject: perf/x86: Further optimize copy_from_user_nmi() Now that we can deal with nested NMI due to IRET re-enabling NMIs and can deal with faults from NMI by making sure we preserve CR2 over NMIs we can in fact simply access user-space memory from NMI context. So rewrite copy_from_user_nmi() to use __copy_from_user_inatomic() and rework the fault path to do the minimal required work before taking the in_atomic() fault handler. In particular avoid perf_sw_event() which would make perf recurse on itself (it should be harmless as our recursion protections should be able to deal with this -- but why tempt fate). Also rename notify_page_fault() to kprobes_fault() as that is a much better name; there is no notifier in it and its specific to kprobes. Don measured that his worst case NMI path shrunk from ~300K cycles to ~150K cycles. Cc: Stephane Eranian Cc: jmario@redhat.com Cc: Arnaldo Carvalho de Melo Cc: Linus Torvalds Cc: Andi Kleen Cc: dave.hansen@linux.intel.com Tested-by: Don Zickus Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20131024105206.GM2490@laptop.programming.kicks-ass.net Signed-off-by: Ingo Molnar diff --git a/arch/x86/lib/usercopy.c b/arch/x86/lib/usercopy.c index 4f74d94..5465b86 100644 --- a/arch/x86/lib/usercopy.c +++ b/arch/x86/lib/usercopy.c @@ -11,39 +11,26 @@ #include /* - * best effort, GUP based copy_from_user() that is NMI-safe + * We rely on the nested NMI work to allow atomic faults from the NMI path; the + * nested NMI paths are careful to preserve CR2. */ unsigned long copy_from_user_nmi(void *to, const void __user *from, unsigned long n) { - unsigned long offset, addr = (unsigned long)from; - unsigned long size, len = 0; - struct page *page; - void *map; - int ret; + unsigned long ret; if (__range_not_ok(from, n, TASK_SIZE)) - return len; - - do { - ret = __get_user_pages_fast(addr, 1, 0, &page); - if (!ret) - break; - - offset = addr & (PAGE_SIZE - 1); - size = min(PAGE_SIZE - offset, n - len); - - map = kmap_atomic(page); - memcpy(to, map+offset, size); - kunmap_atomic(map); - put_page(page); - - len += size; - to += size; - addr += size; - - } while (len < n); - - return len; + return 0; + + /* + * Even though this function is typically called from NMI/IRQ context + * disable pagefaults so that its behaviour is consistent even when + * called form other contexts. + */ + pagefault_disable(); + ret = __copy_from_user_inatomic(to, from, n); + pagefault_enable(); + + return n - ret; } EXPORT_SYMBOL_GPL(copy_from_user_nmi); diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 3aaeffc..7a517bb 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -51,7 +51,7 @@ kmmio_fault(struct pt_regs *regs, unsigned long addr) return 0; } -static inline int __kprobes notify_page_fault(struct pt_regs *regs) +static inline int __kprobes kprobes_fault(struct pt_regs *regs) { int ret = 0; @@ -1048,7 +1048,7 @@ __do_page_fault(struct pt_regs *regs, unsigned long error_code) return; /* kprobes don't want to hook the spurious faults: */ - if (notify_page_fault(regs)) + if (kprobes_fault(regs)) return; /* * Don't take the mm semaphore here. If we fixup a prefetch @@ -1060,23 +1060,8 @@ __do_page_fault(struct pt_regs *regs, unsigned long error_code) } /* kprobes don't want to hook the spurious faults: */ - if (unlikely(notify_page_fault(regs))) + if (unlikely(kprobes_fault(regs))) return; - /* - * It's safe to allow irq's after cr2 has been saved and the - * vmalloc fault has been handled. - * - * User-mode registers count as a user access even for any - * potential system fault or CPU buglet: - */ - if (user_mode_vm(regs)) { - local_irq_enable(); - error_code |= PF_USER; - flags |= FAULT_FLAG_USER; - } else { - if (regs->flags & X86_EFLAGS_IF) - local_irq_enable(); - } if (unlikely(error_code & PF_RSVD)) pgtable_bad(regs, error_code, address); @@ -1088,8 +1073,6 @@ __do_page_fault(struct pt_regs *regs, unsigned long error_code) } } - perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); - /* * If we're in an interrupt, have no user context or are running * in an atomic region then we must not take the fault: @@ -1099,6 +1082,24 @@ __do_page_fault(struct pt_regs *regs, unsigned long error_code) return; } + /* + * It's safe to allow irq's after cr2 has been saved and the + * vmalloc fault has been handled. + * + * User-mode registers count as a user access even for any + * potential system fault or CPU buglet: + */ + if (user_mode_vm(regs)) { + local_irq_enable(); + error_code |= PF_USER; + flags |= FAULT_FLAG_USER; + } else { + if (regs->flags & X86_EFLAGS_IF) + local_irq_enable(); + } + + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); + if (error_code & PF_WRITE) flags |= FAULT_FLAG_WRITE; -- cgit v0.10.2 From 5a3126d4fe7c311fe12f98fef0470f6cb582d1ef Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 7 Oct 2013 17:12:48 +0200 Subject: perf: Fix the perf context switch optimization Currently we only optimize the context switch between two contexts that have the same parent; this forgoes the optimization between parent and child context, even though these contexts could be equivalent too. Signed-off-by: Peter Zijlstra Cc: Frederic Weisbecker Cc: Adrian Hunter Cc: Shishkin, Alexander Link: http://lkml.kernel.org/r/20131007164257.GH3081@twins.programming.kicks-ass.net Signed-off-by: Ingo Molnar diff --git a/kernel/events/core.c b/kernel/events/core.c index 85a8bbd..17b3c6c 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -899,6 +899,7 @@ static void unclone_ctx(struct perf_event_context *ctx) put_ctx(ctx->parent_ctx); ctx->parent_ctx = NULL; } + ctx->generation++; } static u32 perf_event_pid(struct perf_event *event, struct task_struct *p) @@ -1136,6 +1137,8 @@ list_add_event(struct perf_event *event, struct perf_event_context *ctx) ctx->nr_events++; if (event->attr.inherit_stat) ctx->nr_stat++; + + ctx->generation++; } /* @@ -1313,6 +1316,8 @@ list_del_event(struct perf_event *event, struct perf_event_context *ctx) */ if (event->state > PERF_EVENT_STATE_OFF) event->state = PERF_EVENT_STATE_OFF; + + ctx->generation++; } static void perf_group_detach(struct perf_event *event) @@ -2149,22 +2154,38 @@ static void ctx_sched_out(struct perf_event_context *ctx, } /* - * Test whether two contexts are equivalent, i.e. whether they - * have both been cloned from the same version of the same context - * and they both have the same number of enabled events. - * If the number of enabled events is the same, then the set - * of enabled events should be the same, because these are both - * inherited contexts, therefore we can't access individual events - * in them directly with an fd; we can only enable/disable all - * events via prctl, or enable/disable all events in a family - * via ioctl, which will have the same effect on both contexts. + * Test whether two contexts are equivalent, i.e. whether they have both been + * cloned from the same version of the same context. + * + * Equivalence is measured using a generation number in the context that is + * incremented on each modification to it; see unclone_ctx(), list_add_event() + * and list_del_event(). */ static int context_equiv(struct perf_event_context *ctx1, struct perf_event_context *ctx2) { - return ctx1->parent_ctx && ctx1->parent_ctx == ctx2->parent_ctx - && ctx1->parent_gen == ctx2->parent_gen - && !ctx1->pin_count && !ctx2->pin_count; + /* Pinning disables the swap optimization */ + if (ctx1->pin_count || ctx2->pin_count) + return 0; + + /* If ctx1 is the parent of ctx2 */ + if (ctx1 == ctx2->parent_ctx && ctx1->generation == ctx2->parent_gen) + return 1; + + /* If ctx2 is the parent of ctx1 */ + if (ctx1->parent_ctx == ctx2 && ctx1->parent_gen == ctx2->generation) + return 1; + + /* + * If ctx1 and ctx2 have the same parent; we flatten the parent + * hierarchy, see perf_event_init_context(). + */ + if (ctx1->parent_ctx && ctx1->parent_ctx == ctx2->parent_ctx && + ctx1->parent_gen == ctx2->parent_gen) + return 1; + + /* Unmatched */ + return 0; } static void __perf_event_sync_stat(struct perf_event *event, @@ -2247,7 +2268,7 @@ static void perf_event_context_sched_out(struct task_struct *task, int ctxn, { struct perf_event_context *ctx = task->perf_event_ctxp[ctxn]; struct perf_event_context *next_ctx; - struct perf_event_context *parent; + struct perf_event_context *parent, *next_parent; struct perf_cpu_context *cpuctx; int do_switch = 1; @@ -2259,10 +2280,18 @@ static void perf_event_context_sched_out(struct task_struct *task, int ctxn, return; rcu_read_lock(); - parent = rcu_dereference(ctx->parent_ctx); next_ctx = next->perf_event_ctxp[ctxn]; - if (parent && next_ctx && - rcu_dereference(next_ctx->parent_ctx) == parent) { + if (!next_ctx) + goto unlock; + + parent = rcu_dereference(ctx->parent_ctx); + next_parent = rcu_dereference(next_ctx->parent_ctx); + + /* If neither context have a parent context; they cannot be clones. */ + if (!parent && !next_parent) + goto unlock; + + if (next_parent == ctx || next_ctx == parent || next_parent == parent) { /* * Looks like the two contexts are clones, so we might be * able to optimize the context switch. We lock both @@ -2290,6 +2319,7 @@ static void perf_event_context_sched_out(struct task_struct *task, int ctxn, raw_spin_unlock(&next_ctx->lock); raw_spin_unlock(&ctx->lock); } +unlock: rcu_read_unlock(); if (do_switch) { @@ -7136,7 +7166,6 @@ SYSCALL_DEFINE5(perf_event_open, } perf_install_in_context(ctx, event, event->cpu); - ++ctx->generation; perf_unpin_context(ctx); mutex_unlock(&ctx->mutex); @@ -7219,7 +7248,6 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu, WARN_ON_ONCE(ctx->parent_ctx); mutex_lock(&ctx->mutex); perf_install_in_context(ctx, event, cpu); - ++ctx->generation; perf_unpin_context(ctx); mutex_unlock(&ctx->mutex); -- cgit v0.10.2 From 30612cd90005d8c4a3c53c7acadcf934a46c13df Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Mon, 28 Oct 2013 10:00:36 +0100 Subject: pinctrl: imx1 core driver Core driver for register formats of imx1/imx21/imx27 processors. The pins of those processors are grouped into ports. Each port has 32 pins. The pins mux configuration is controlled by registers with 1 or 2 bit per pin, depending on the specific control register. Signed-off-by: Markus Pargmann Acked-by: Sascha Hauer Acked-by: Shawn Guo Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index e283c49..fcc748f 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -109,6 +109,11 @@ config PINCTRL_IMX select PINMUX select PINCONF +config PINCTRL_IMX1_CORE + bool + select PINMUX + select PINCONF + config PINCTRL_IMX35 bool "IMX35 pinctrl driver" depends on OF diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 8476cd9..8b334e3 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o obj-$(CONFIG_PINCTRL_BCM2835) += pinctrl-bcm2835.o obj-$(CONFIG_PINCTRL_BAYTRAIL) += pinctrl-baytrail.o obj-$(CONFIG_PINCTRL_IMX) += pinctrl-imx.o +obj-$(CONFIG_PINCTRL_IMX1_CORE) += pinctrl-imx1-core.o obj-$(CONFIG_PINCTRL_IMX35) += pinctrl-imx35.o obj-$(CONFIG_PINCTRL_IMX51) += pinctrl-imx51.o obj-$(CONFIG_PINCTRL_IMX53) += pinctrl-imx53.o diff --git a/drivers/pinctrl/pinctrl-imx1-core.c b/drivers/pinctrl/pinctrl-imx1-core.c new file mode 100644 index 0000000..4d5a9e7 --- /dev/null +++ b/drivers/pinctrl/pinctrl-imx1-core.c @@ -0,0 +1,653 @@ +/* + * Core driver for the imx pin controller in imx1/21/27 + * + * Copyright (C) 2013 Pengutronix + * Author: Markus Pargmann + * + * Based on pinctrl-imx.c: + * Author: Dong Aisheng + * Copyright (C) 2012 Freescale Semiconductor, Inc. + * Copyright (C) 2012 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "pinctrl-imx1.h" + +struct imx1_pinctrl { + struct device *dev; + struct pinctrl_dev *pctl; + void __iomem *base; + const struct imx1_pinctrl_soc_info *info; +}; + +/* + * MX1 register offsets + */ + +#define MX1_DDIR 0x00 +#define MX1_OCR 0x04 +#define MX1_ICONFA 0x0c +#define MX1_ICONFB 0x10 +#define MX1_GIUS 0x20 +#define MX1_GPR 0x38 +#define MX1_PUEN 0x40 + +#define MX1_PORT_STRIDE 0x100 + + +/* + * MUX_ID format defines + */ +#define MX1_MUX_FUNCTION(val) (BIT(0) & val) +#define MX1_MUX_GPIO(val) ((BIT(1) & val) >> 1) +#define MX1_MUX_DIR(val) ((BIT(2) & val) >> 2) +#define MX1_MUX_OCONF(val) (((BIT(4) | BIT(5)) & val) >> 4) +#define MX1_MUX_ICONFA(val) (((BIT(8) | BIT(9)) & val) >> 8) +#define MX1_MUX_ICONFB(val) (((BIT(10) | BIT(11)) & val) >> 10) + + +/* + * IMX1 IOMUXC manages the pins based on ports. Each port has 32 pins. IOMUX + * control register are seperated into function, output configuration, input + * configuration A, input configuration B, GPIO in use and data direction. + * + * Those controls that are represented by 1 bit have a direct mapping between + * bit position and pin id. If they are represented by 2 bit, the lower 16 pins + * are in the first register and the upper 16 pins in the second (next) + * register. pin_id is stored in bit (pin_id%16)*2 and the bit above. + */ + +/* + * Calculates the register offset from a pin_id + */ +static void __iomem *imx1_mem(struct imx1_pinctrl *ipctl, unsigned int pin_id) +{ + unsigned int port = pin_id / 32; + return ipctl->base + port * MX1_PORT_STRIDE; +} + +/* + * Write to a register with 2 bits per pin. The function will automatically + * use the next register if the pin is managed in the second register. + */ +static void imx1_write_2bit(struct imx1_pinctrl *ipctl, unsigned int pin_id, + u32 value, u32 reg_offset) +{ + void __iomem *reg = imx1_mem(ipctl, pin_id) + reg_offset; + int offset = (pin_id % 16) * 2; /* offset, regardless of register used */ + int mask = ~(0x3 << offset); /* Mask for 2 bits at offset */ + u32 old_val; + u32 new_val; + + dev_dbg(ipctl->dev, "write: register 0x%p offset %d value 0x%x\n", + reg, offset, value); + + /* Use the next register if the pin's port pin number is >=16 */ + if (pin_id % 32 >= 16) + reg += 0x04; + + /* Get current state of pins */ + old_val = readl(reg); + old_val &= mask; + + new_val = value & 0x3; /* Make sure value is really 2 bit */ + new_val <<= offset; + new_val |= old_val;/* Set new state for pin_id */ + + writel(new_val, reg); +} + +static void imx1_write_bit(struct imx1_pinctrl *ipctl, unsigned int pin_id, + u32 value, u32 reg_offset) +{ + void __iomem *reg = imx1_mem(ipctl, pin_id) + reg_offset; + int offset = pin_id % 32; + int mask = ~BIT_MASK(offset); + u32 old_val; + u32 new_val; + + /* Get current state of pins */ + old_val = readl(reg); + old_val &= mask; + + new_val = value & 0x1; /* Make sure value is really 1 bit */ + new_val <<= offset; + new_val |= old_val;/* Set new state for pin_id */ + + writel(new_val, reg); +} + +static int imx1_read_2bit(struct imx1_pinctrl *ipctl, unsigned int pin_id, + u32 reg_offset) +{ + void __iomem *reg = imx1_mem(ipctl, pin_id) + reg_offset; + int offset = pin_id % 16; + + /* Use the next register if the pin's port pin number is >=16 */ + if (pin_id % 32 >= 16) + reg += 0x04; + + return (readl(reg) & (BIT(offset) | BIT(offset+1))) >> offset; +} + +static int imx1_read_bit(struct imx1_pinctrl *ipctl, unsigned int pin_id, + u32 reg_offset) +{ + void __iomem *reg = imx1_mem(ipctl, pin_id) + reg_offset; + int offset = pin_id % 32; + + return !!(readl(reg) & BIT(offset)); +} + +static const inline struct imx1_pin_group *imx1_pinctrl_find_group_by_name( + const struct imx1_pinctrl_soc_info *info, + const char *name) +{ + const struct imx1_pin_group *grp = NULL; + int i; + + for (i = 0; i < info->ngroups; i++) { + if (!strcmp(info->groups[i].name, name)) { + grp = &info->groups[i]; + break; + } + } + + return grp; +} + +static int imx1_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct imx1_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); + const struct imx1_pinctrl_soc_info *info = ipctl->info; + + return info->ngroups; +} + +static const char *imx1_get_group_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct imx1_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); + const struct imx1_pinctrl_soc_info *info = ipctl->info; + + return info->groups[selector].name; +} + +static int imx1_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector, + const unsigned int **pins, + unsigned *npins) +{ + struct imx1_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); + const struct imx1_pinctrl_soc_info *info = ipctl->info; + + if (selector >= info->ngroups) + return -EINVAL; + + *pins = info->groups[selector].pin_ids; + *npins = info->groups[selector].npins; + + return 0; +} + +static void imx1_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, + unsigned offset) +{ + struct imx1_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); + + seq_printf(s, "GPIO %d, function %d, direction %d, oconf %d, iconfa %d, iconfb %d", + imx1_read_bit(ipctl, offset, MX1_GIUS), + imx1_read_bit(ipctl, offset, MX1_GPR), + imx1_read_bit(ipctl, offset, MX1_DDIR), + imx1_read_2bit(ipctl, offset, MX1_OCR), + imx1_read_2bit(ipctl, offset, MX1_ICONFA), + imx1_read_2bit(ipctl, offset, MX1_ICONFB)); +} + +static int imx1_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, unsigned *num_maps) +{ + struct imx1_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); + const struct imx1_pinctrl_soc_info *info = ipctl->info; + const struct imx1_pin_group *grp; + struct pinctrl_map *new_map; + struct device_node *parent; + int map_num = 1; + int i, j; + + /* + * first find the group of this node and check if we need create + * config maps for pins + */ + grp = imx1_pinctrl_find_group_by_name(info, np->name); + if (!grp) { + dev_err(info->dev, "unable to find group for node %s\n", + np->name); + return -EINVAL; + } + + for (i = 0; i < grp->npins; i++) + map_num++; + + new_map = kmalloc(sizeof(struct pinctrl_map) * map_num, GFP_KERNEL); + if (!new_map) + return -ENOMEM; + + *map = new_map; + *num_maps = map_num; + + /* create mux map */ + parent = of_get_parent(np); + if (!parent) { + kfree(new_map); + return -EINVAL; + } + new_map[0].type = PIN_MAP_TYPE_MUX_GROUP; + new_map[0].data.mux.function = parent->name; + new_map[0].data.mux.group = np->name; + of_node_put(parent); + + /* create config map */ + new_map++; + for (i = j = 0; i < grp->npins; i++) { + new_map[j].type = PIN_MAP_TYPE_CONFIGS_PIN; + new_map[j].data.configs.group_or_pin = + pin_get_name(pctldev, grp->pins[i].pin_id); + new_map[j].data.configs.configs = &grp->pins[i].config; + new_map[j].data.configs.num_configs = 1; + j++; + } + + dev_dbg(pctldev->dev, "maps: function %s group %s num %d\n", + (*map)->data.mux.function, (*map)->data.mux.group, map_num); + + return 0; +} + +static void imx1_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, unsigned num_maps) +{ + kfree(map); +} + +static const struct pinctrl_ops imx1_pctrl_ops = { + .get_groups_count = imx1_get_groups_count, + .get_group_name = imx1_get_group_name, + .get_group_pins = imx1_get_group_pins, + .pin_dbg_show = imx1_pin_dbg_show, + .dt_node_to_map = imx1_dt_node_to_map, + .dt_free_map = imx1_dt_free_map, + +}; + +static int imx1_pmx_enable(struct pinctrl_dev *pctldev, unsigned selector, + unsigned group) +{ + struct imx1_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); + const struct imx1_pinctrl_soc_info *info = ipctl->info; + const struct imx1_pin *pins; + unsigned int npins; + int i; + + /* + * Configure the mux mode for each pin in the group for a specific + * function. + */ + pins = info->groups[group].pins; + npins = info->groups[group].npins; + + WARN_ON(!pins || !npins); + + dev_dbg(ipctl->dev, "enable function %s group %s\n", + info->functions[selector].name, info->groups[group].name); + + for (i = 0; i < npins; i++) { + unsigned int mux = pins[i].mux_id; + unsigned int pin_id = pins[i].pin_id; + unsigned int afunction = MX1_MUX_FUNCTION(mux); + unsigned int gpio_in_use = MX1_MUX_GPIO(mux); + unsigned int direction = MX1_MUX_DIR(mux); + unsigned int gpio_oconf = MX1_MUX_OCONF(mux); + unsigned int gpio_iconfa = MX1_MUX_ICONFA(mux); + unsigned int gpio_iconfb = MX1_MUX_ICONFB(mux); + + dev_dbg(pctldev->dev, "%s, pin 0x%x, function %d, gpio %d, direction %d, oconf %d, iconfa %d, iconfb %d\n", + __func__, pin_id, afunction, gpio_in_use, + direction, gpio_oconf, gpio_iconfa, + gpio_iconfb); + + imx1_write_bit(ipctl, pin_id, gpio_in_use, MX1_GIUS); + imx1_write_bit(ipctl, pin_id, direction, MX1_DDIR); + + if (gpio_in_use) { + imx1_write_2bit(ipctl, pin_id, gpio_oconf, MX1_OCR); + imx1_write_2bit(ipctl, pin_id, gpio_iconfa, + MX1_ICONFA); + imx1_write_2bit(ipctl, pin_id, gpio_iconfb, + MX1_ICONFB); + } else { + imx1_write_bit(ipctl, pin_id, afunction, MX1_GPR); + } + } + + return 0; +} + +static int imx1_pmx_get_funcs_count(struct pinctrl_dev *pctldev) +{ + struct imx1_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); + const struct imx1_pinctrl_soc_info *info = ipctl->info; + + return info->nfunctions; +} + +static const char *imx1_pmx_get_func_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct imx1_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); + const struct imx1_pinctrl_soc_info *info = ipctl->info; + + return info->functions[selector].name; +} + +static int imx1_pmx_get_groups(struct pinctrl_dev *pctldev, unsigned selector, + const char * const **groups, + unsigned * const num_groups) +{ + struct imx1_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); + const struct imx1_pinctrl_soc_info *info = ipctl->info; + + *groups = info->functions[selector].groups; + *num_groups = info->functions[selector].num_groups; + + return 0; +} + +static const struct pinmux_ops imx1_pmx_ops = { + .get_functions_count = imx1_pmx_get_funcs_count, + .get_function_name = imx1_pmx_get_func_name, + .get_function_groups = imx1_pmx_get_groups, + .enable = imx1_pmx_enable, +}; + +static int imx1_pinconf_get(struct pinctrl_dev *pctldev, + unsigned pin_id, unsigned long *config) +{ + struct imx1_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); + + *config = imx1_read_bit(ipctl, pin_id, MX1_PUEN); + + return 0; +} + +static int imx1_pinconf_set(struct pinctrl_dev *pctldev, + unsigned pin_id, unsigned long *configs, + unsigned num_configs) +{ + struct imx1_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); + const struct imx1_pinctrl_soc_info *info = ipctl->info; + int i; + + for (i = 0; i != num_configs; ++i) { + imx1_write_bit(ipctl, pin_id, configs[i] & 0x01, MX1_PUEN); + + dev_dbg(ipctl->dev, "pinconf set pullup pin %s\n", + info->pins[pin_id].name); + } + + return 0; +} + +static void imx1_pinconf_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned pin_id) +{ + unsigned long config; + + imx1_pinconf_get(pctldev, pin_id, &config); + seq_printf(s, "0x%lx", config); +} + +static void imx1_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned group) +{ + struct imx1_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); + const struct imx1_pinctrl_soc_info *info = ipctl->info; + struct imx1_pin_group *grp; + unsigned long config; + const char *name; + int i, ret; + + if (group > info->ngroups) + return; + + seq_puts(s, "\n"); + grp = &info->groups[group]; + for (i = 0; i < grp->npins; i++) { + name = pin_get_name(pctldev, grp->pins[i].pin_id); + ret = imx1_pinconf_get(pctldev, grp->pins[i].pin_id, &config); + if (ret) + return; + seq_printf(s, "%s: 0x%lx", name, config); + } +} + +static const struct pinconf_ops imx1_pinconf_ops = { + .pin_config_get = imx1_pinconf_get, + .pin_config_set = imx1_pinconf_set, + .pin_config_dbg_show = imx1_pinconf_dbg_show, + .pin_config_group_dbg_show = imx1_pinconf_group_dbg_show, +}; + +static struct pinctrl_desc imx1_pinctrl_desc = { + .pctlops = &imx1_pctrl_ops, + .pmxops = &imx1_pmx_ops, + .confops = &imx1_pinconf_ops, + .owner = THIS_MODULE, +}; + +static int imx1_pinctrl_parse_groups(struct device_node *np, + struct imx1_pin_group *grp, + struct imx1_pinctrl_soc_info *info, + u32 index) +{ + int size; + const __be32 *list; + int i; + + dev_dbg(info->dev, "group(%d): %s\n", index, np->name); + + /* Initialise group */ + grp->name = np->name; + + /* + * the binding format is fsl,pins = + */ + list = of_get_property(np, "fsl,pins", &size); + /* we do not check return since it's safe node passed down */ + if (!size || size % 12) { + dev_notice(info->dev, "Not a valid fsl,pins property (%s)\n", + np->name); + return -EINVAL; + } + + grp->npins = size / 12; + grp->pins = devm_kzalloc(info->dev, + grp->npins * sizeof(struct imx1_pin), GFP_KERNEL); + grp->pin_ids = devm_kzalloc(info->dev, + grp->npins * sizeof(unsigned int), GFP_KERNEL); + + if (!grp->pins || !grp->pin_ids) + return -ENOMEM; + + for (i = 0; i < grp->npins; i++) { + grp->pins[i].pin_id = be32_to_cpu(*list++); + grp->pins[i].mux_id = be32_to_cpu(*list++); + grp->pins[i].config = be32_to_cpu(*list++); + + grp->pin_ids[i] = grp->pins[i].pin_id; + } + + return 0; +} + +static int imx1_pinctrl_parse_functions(struct device_node *np, + struct imx1_pinctrl_soc_info *info, + u32 index) +{ + struct device_node *child; + struct imx1_pmx_func *func; + struct imx1_pin_group *grp; + int ret; + static u32 grp_index; + u32 i = 0; + + dev_dbg(info->dev, "parse function(%d): %s\n", index, np->name); + + func = &info->functions[index]; + + /* Initialise function */ + func->name = np->name; + func->num_groups = of_get_child_count(np); + if (func->num_groups <= 0) + return -EINVAL; + + func->groups = devm_kzalloc(info->dev, + func->num_groups * sizeof(char *), GFP_KERNEL); + + if (!func->groups) + return -ENOMEM; + + for_each_child_of_node(np, child) { + func->groups[i] = child->name; + grp = &info->groups[grp_index++]; + ret = imx1_pinctrl_parse_groups(child, grp, info, i++); + if (ret == -ENOMEM) + return ret; + } + + return 0; +} + +static int imx1_pinctrl_parse_dt(struct platform_device *pdev, + struct imx1_pinctrl *pctl, struct imx1_pinctrl_soc_info *info) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *child; + int ret; + u32 nfuncs = 0; + u32 ngroups = 0; + u32 ifunc = 0; + + if (!np) + return -ENODEV; + + for_each_child_of_node(np, child) { + ++nfuncs; + ngroups += of_get_child_count(child); + } + + if (!nfuncs) { + dev_err(&pdev->dev, "No pin functions defined\n"); + return -EINVAL; + } + + info->nfunctions = nfuncs; + info->functions = devm_kzalloc(&pdev->dev, + nfuncs * sizeof(struct imx1_pmx_func), GFP_KERNEL); + + info->ngroups = ngroups; + info->groups = devm_kzalloc(&pdev->dev, + ngroups * sizeof(struct imx1_pin_group), GFP_KERNEL); + + + if (!info->functions || !info->groups) + return -ENOMEM; + + for_each_child_of_node(np, child) { + ret = imx1_pinctrl_parse_functions(child, info, ifunc++); + if (ret == -ENOMEM) + return -ENOMEM; + } + + return 0; +} + +int imx1_pinctrl_core_probe(struct platform_device *pdev, + struct imx1_pinctrl_soc_info *info) +{ + struct imx1_pinctrl *ipctl; + struct resource *res; + struct pinctrl_desc *pctl_desc; + int ret; + + if (!info || !info->pins || !info->npins) { + dev_err(&pdev->dev, "wrong pinctrl info\n"); + return -EINVAL; + } + info->dev = &pdev->dev; + + /* Create state holders etc for this driver */ + ipctl = devm_kzalloc(&pdev->dev, sizeof(*ipctl), GFP_KERNEL); + if (!ipctl) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOENT; + + ipctl->base = devm_ioremap_nocache(&pdev->dev, res->start, + resource_size(res)); + if (IS_ERR(ipctl->base)) + return PTR_ERR(ipctl->base); + + pctl_desc = &imx1_pinctrl_desc; + pctl_desc->name = dev_name(&pdev->dev); + pctl_desc->pins = info->pins; + pctl_desc->npins = info->npins; + + ret = imx1_pinctrl_parse_dt(pdev, ipctl, info); + if (ret) { + dev_err(&pdev->dev, "fail to probe dt properties\n"); + return ret; + } + + ipctl->info = info; + ipctl->dev = info->dev; + platform_set_drvdata(pdev, ipctl); + ipctl->pctl = pinctrl_register(pctl_desc, &pdev->dev, ipctl); + if (!ipctl->pctl) { + dev_err(&pdev->dev, "could not register IMX pinctrl driver\n"); + return -EINVAL; + } + + dev_info(&pdev->dev, "initialized IMX pinctrl driver\n"); + + return 0; +} + +int imx1_pinctrl_core_remove(struct platform_device *pdev) +{ + struct imx1_pinctrl *ipctl = platform_get_drvdata(pdev); + + pinctrl_unregister(ipctl->pctl); + + return 0; +} diff --git a/drivers/pinctrl/pinctrl-imx1.h b/drivers/pinctrl/pinctrl-imx1.h new file mode 100644 index 0000000..692a54c --- /dev/null +++ b/drivers/pinctrl/pinctrl-imx1.h @@ -0,0 +1,73 @@ +/* + * IMX pinmux core definitions + * + * Copyright (C) 2012 Freescale Semiconductor, Inc. + * Copyright (C) 2012 Linaro Ltd. + * + * Author: Dong Aisheng + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __DRIVERS_PINCTRL_IMX1_H +#define __DRIVERS_PINCTRL_IMX1_H + +struct platform_device; + +/** + * struct imx1_pin - describes an IMX1/21/27 pin. + * @pin_id: ID of the described pin. + * @mux_id: ID of the mux setup. + * @config: Configuration of the pin (currently only pullup-enable). + */ +struct imx1_pin { + unsigned int pin_id; + unsigned int mux_id; + unsigned long config; +}; + +/** + * struct imx1_pin_group - describes an IMX pin group + * @name: the name of this specific pin group + * @pins: an array of imx1_pin structs used in this group + * @npins: the number of pins in this group array, i.e. the number of + * elements in .pins so we can iterate over that array + */ +struct imx1_pin_group { + const char *name; + unsigned int *pin_ids; + struct imx1_pin *pins; + unsigned npins; +}; + +/** + * struct imx1_pmx_func - describes IMX pinmux functions + * @name: the name of this specific function + * @groups: corresponding pin groups + * @num_groups: the number of groups + */ +struct imx1_pmx_func { + const char *name; + const char **groups; + unsigned num_groups; +}; + +struct imx1_pinctrl_soc_info { + struct device *dev; + const struct pinctrl_pin_desc *pins; + unsigned int npins; + struct imx1_pin_group *groups; + unsigned int ngroups; + struct imx1_pmx_func *functions; + unsigned int nfunctions; +}; + +#define IMX_PINCTRL_PIN(pin) PINCTRL_PIN(pin, #pin) + +int imx1_pinctrl_core_probe(struct platform_device *pdev, + struct imx1_pinctrl_soc_info *info); +int imx1_pinctrl_core_remove(struct platform_device *pdev); +#endif /* __DRIVERS_PINCTRL_IMX1_H */ -- cgit v0.10.2 From a8d09e3a6dfc44fd2246f3c57d2cd00e1a56e2bd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 29 Oct 2013 15:07:19 +0100 Subject: ALSA: opl3: Fix possible negative array index access Spotted by coverity CID 115196. Signed-off-by: Takashi Iwai diff --git a/sound/drivers/opl3/opl3_midi.c b/sound/drivers/opl3/opl3_midi.c index 0c796bc..6c6d09a 100644 --- a/sound/drivers/opl3/opl3_midi.c +++ b/sound/drivers/opl3/opl3_midi.c @@ -390,6 +390,11 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan) voice = snd_opl3_oss_map[chan->number]; } + if (voice < 0) { + spin_unlock_irqrestore(&opl3->voice_lock, flags); + return; + } + if (voice < MAX_OPL2_VOICES) { /* Left register block for voices 0 .. 8 */ reg_side = OPL3_LEFT; -- cgit v0.10.2 From 75415df8ffbac2ace9463ceffb3f21299af2548a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 29 Oct 2013 15:15:20 +0100 Subject: ALSA: pcsp: Fix initialization with nopcm=1 When nopcm=1 is set, some initializations based on hrtimer resolution might be bogus because the driver checks the resolution only when nopcm=0. Simply get the resolution always at first for fixing the bug. Spotted by coverity CID 139740. Signed-off-by: Takashi Iwai diff --git a/sound/drivers/pcsp/pcsp.c b/sound/drivers/pcsp/pcsp.c index 1c19cd7..f664bae 100644 --- a/sound/drivers/pcsp/pcsp.c +++ b/sound/drivers/pcsp/pcsp.c @@ -46,8 +46,9 @@ static int snd_pcsp_create(struct snd_card *card) int err; int div, min_div, order; + hrtimer_get_res(CLOCK_MONOTONIC, &tp); + if (!nopcm) { - hrtimer_get_res(CLOCK_MONOTONIC, &tp); if (tp.tv_sec || tp.tv_nsec > PCSP_MAX_PERIOD_NS) { printk(KERN_ERR "PCSP: Timer resolution is not sufficient " "(%linS)\n", tp.tv_nsec); -- cgit v0.10.2 From 97f44f56ca94709f45bc348f5d2c88696eae5f9b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 29 Oct 2013 15:20:06 +0100 Subject: ALSA: Limit the fallback card id string size When no proper id string is given, the driver tries to fall back to copy the proc_root name string via strcpy(), but this might overflow the fixed string size. Let's use strlcpy(). Spotted by coverity CID 139008. Signed-off-by: Takashi Iwai diff --git a/sound/core/init.c b/sound/core/init.c index 01a8938..1351f22 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -597,7 +597,7 @@ static void snd_card_set_id_no_lock(struct snd_card *card, const char *src, /* last resort... */ snd_printk(KERN_ERR "unable to set card id (%s)\n", id); if (card->proc_root->name) - strcpy(card->id, card->proc_root->name); + strlcpy(card->id, card->proc_root->name, sizeof(card->id)); } /** -- cgit v0.10.2 From 57a4451d26eef4ccbf3b32fd116295f001c18cb4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 29 Oct 2013 15:26:12 +0100 Subject: ALSA: Use strlcpy() instead of strncpy() We tend to make stupid mistakes with strncpy(). Let's take a safer one, strlcpy(). Signed-off-by: Takashi Iwai diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c index 5066a37..9a2ac1e 100644 --- a/sound/arm/pxa2xx-ac97.c +++ b/sound/arm/pxa2xx-ac97.c @@ -185,7 +185,7 @@ static int pxa2xx_ac97_probe(struct platform_device *dev) goto err; card->dev = &dev->dev; - strncpy(card->driver, dev->dev.driver->name, sizeof(card->driver)); + strlcpy(card->driver, dev->dev.driver->name, sizeof(card->driver)); ret = pxa2xx_pcm_new(card, &pxa2xx_ac97_pcm_client, &pxa2xx_ac97_pcm); if (ret) diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c index ed726d1..f3735e6 100644 --- a/sound/i2c/other/ak4xxx-adda.c +++ b/sound/i2c/other/ak4xxx-adda.c @@ -583,7 +583,7 @@ static int ak4xxx_capture_source_info(struct snd_kcontrol *kcontrol, if (idx >= num_names) return -EINVAL; input_names = ak->adc_info[mixer_ch].input_names; - strncpy(uinfo->value.enumerated.name, input_names[idx], + strlcpy(uinfo->value.enumerated.name, input_names[idx], sizeof(uinfo->value.enumerated.name)); return 0; } diff --git a/sound/pci/cs5535audio/cs5535audio_olpc.c b/sound/pci/cs5535audio/cs5535audio_olpc.c index da1cb9c..e6a4450 100644 --- a/sound/pci/cs5535audio/cs5535audio_olpc.c +++ b/sound/pci/cs5535audio/cs5535audio_olpc.c @@ -161,13 +161,13 @@ int olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97) /* drop the original AD1888 HPF control */ memset(&elem, 0, sizeof(elem)); elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strncpy(elem.name, "High Pass Filter Enable", sizeof(elem.name)); + strlcpy(elem.name, "High Pass Filter Enable", sizeof(elem.name)); snd_ctl_remove_id(card, &elem); /* drop the original V_REFOUT control */ memset(&elem, 0, sizeof(elem)); elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strncpy(elem.name, "V_REFOUT Enable", sizeof(elem.name)); + strlcpy(elem.name, "V_REFOUT Enable", sizeof(elem.name)); snd_ctl_remove_id(card, &elem); /* add the OLPC-specific controls */ diff --git a/sound/pci/ice1712/psc724.c b/sound/pci/ice1712/psc724.c index 302ac6d..4019cf2 100644 --- a/sound/pci/ice1712/psc724.c +++ b/sound/pci/ice1712/psc724.c @@ -203,12 +203,12 @@ static void psc724_set_jack_state(struct snd_ice1712 *ice, bool hp_connected) /* notify about master speaker mute change */ memset(&elem_id, 0, sizeof(elem_id)); elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strncpy(elem_id.name, "Master Speakers Playback Switch", + strlcpy(elem_id.name, "Master Speakers Playback Switch", sizeof(elem_id.name)); kctl = snd_ctl_find_id(ice->card, &elem_id); snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); /* and headphone mute change */ - strncpy(elem_id.name, spec->wm8776.ctl[WM8776_CTL_HP_SW].name, + strlcpy(elem_id.name, spec->wm8776.ctl[WM8776_CTL_HP_SW].name, sizeof(elem_id.name)); kctl = snd_ctl_find_id(ice->card, &elem_id); snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); diff --git a/sound/pci/ice1712/wm8776.c b/sound/pci/ice1712/wm8776.c index a3c05fe..5227cb0 100644 --- a/sound/pci/ice1712/wm8776.c +++ b/sound/pci/ice1712/wm8776.c @@ -52,7 +52,7 @@ static void snd_wm8776_activate_ctl(struct snd_wm8776 *wm, unsigned int index_offset; memset(&elem_id, 0, sizeof(elem_id)); - strncpy(elem_id.name, ctl_name, sizeof(elem_id.name)); + strlcpy(elem_id.name, ctl_name, sizeof(elem_id.name)); elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; kctl = snd_ctl_find_id(card, &elem_id); if (!kctl) diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 2907e68..e98dc00 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -6400,7 +6400,7 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, memset(&hdspm_version, 0, sizeof(hdspm_version)); hdspm_version.card_type = hdspm->io_type; - strncpy(hdspm_version.cardname, hdspm->card_name, + strlcpy(hdspm_version.cardname, hdspm->card_name, sizeof(hdspm_version.cardname)); hdspm_version.serial = hdspm->serial; hdspm_version.firmware_rev = hdspm->firmware_rev; -- cgit v0.10.2 From 9bd0f5c042e218c0573e730d181287abecd6b18b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 29 Oct 2013 15:39:37 +0100 Subject: ALSA: ad1889: Fix right attenuation proc output The right attenuation bits aren't needed to be shifted. Spotted by coverity CID 11427. Signed-off-by: Takashi Iwai diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c index d2b9d61..b680d03 100644 --- a/sound/pci/ad1889.c +++ b/sound/pci/ad1889.c @@ -739,7 +739,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe reg = ad1889_readw(chip, AD_DS_WADA); snd_iprintf(buffer, "Right: %s, -%d dB\n", (reg & AD_DS_WADA_RWAM) ? "mute" : "unmute", - ((reg & AD_DS_WADA_RWAA) >> 8) * 3); + (reg & AD_DS_WADA_RWAA) * 3); reg = ad1889_readw(chip, AD_DS_WAS); snd_iprintf(buffer, "Wave samplerate: %u Hz\n", reg); -- cgit v0.10.2 From 329bbd9ba738b85675151b82260cd08bdd765de2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 29 Oct 2013 15:42:29 +0100 Subject: ALSA: ali5451: Drop unused variable The variable runtime is never used, and this might be even a source of NULL-dereference. Nothing better than killing it. Spotted by coverity CID 100862. Signed-off-by: Takashi Iwai diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index 3dfa12b..c6835a3 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -855,7 +855,6 @@ static void snd_ali_disable_spdif_out(struct snd_ali *codec) static void snd_ali_update_ptr(struct snd_ali *codec, int channel) { struct snd_ali_voice *pvoice; - struct snd_pcm_runtime *runtime; struct snd_ali_channel_control *pchregs; unsigned int old, mask; #ifdef ALI_DEBUG @@ -872,7 +871,6 @@ static void snd_ali_update_ptr(struct snd_ali *codec, int channel) return; pvoice = &codec->synth.voices[channel]; - runtime = pvoice->substream->runtime; udelay(100); spin_lock(&codec->reg_lock); -- cgit v0.10.2 From 2026d24ef2ea8caad5e87662a58075e930ccab63 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 29 Oct 2013 16:04:37 +0100 Subject: ALSA: rme96: Return error code in PCM copy ops Just pass the error code returned from copy_from_user_toio() and copy_to_user_fromio() helpers. Spotted by coverity CID 114119. Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index bb9ebc5..0236363 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -350,9 +350,8 @@ snd_rme96_playback_copy(struct snd_pcm_substream *substream, struct rme96 *rme96 = snd_pcm_substream_chip(substream); count <<= rme96->playback_frlog; pos <<= rme96->playback_frlog; - copy_from_user_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, src, - count); - return 0; + return copy_from_user_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, src, + count); } static int @@ -365,9 +364,8 @@ snd_rme96_capture_copy(struct snd_pcm_substream *substream, struct rme96 *rme96 = snd_pcm_substream_chip(substream); count <<= rme96->capture_frlog; pos <<= rme96->capture_frlog; - copy_to_user_fromio(dst, rme96->iobase + RME96_IO_REC_BUFFER + pos, - count); - return 0; + return copy_to_user_fromio(dst, rme96->iobase + RME96_IO_REC_BUFFER + pos, + count); } /* -- cgit v0.10.2 From e12483e0f3dbc32dad8fa1dc97efac22b6aee94f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 29 Oct 2013 16:37:11 +0100 Subject: ALSA: ak4114: Fix wrong register array size The size of the register cache array is actually 6 instead of 7, as it caches up to AK4114_REG_INT1_MASK. This resulted in unexpected access out of array range, although most of them aren't so serious (just reading one more byte on the stack at snd_ak4114_create()). Also, the check of cache size was wrongly done by checking with sizeof() instead of ARRAY_SIZE(). Fixed this together. (And yes, hardcoded numbers are bad, but I keep the coding style as is for making it clear what this patch actually does.) Spotted by coverity among several CIDs, e.g. 711621. Signed-off-by: Takashi Iwai diff --git a/include/sound/ak4114.h b/include/sound/ak4114.h index 3ce69fd..52f02a6 100644 --- a/include/sound/ak4114.h +++ b/include/sound/ak4114.h @@ -170,7 +170,7 @@ struct ak4114 { void * private_data; unsigned int init: 1; spinlock_t lock; - unsigned char regmap[7]; + unsigned char regmap[6]; unsigned char txcsb[5]; struct snd_kcontrol *kctls[AK4114_CONTROLS]; struct snd_pcm_substream *playback_substream; @@ -189,7 +189,7 @@ struct ak4114 { int snd_ak4114_create(struct snd_card *card, ak4114_read_t *read, ak4114_write_t *write, - const unsigned char pgm[7], const unsigned char txcsb[5], + const unsigned char pgm[6], const unsigned char txcsb[5], void *private_data, struct ak4114 **r_ak4114); void snd_ak4114_reg_write(struct ak4114 *ak4114, unsigned char reg, unsigned char mask, unsigned char val); void snd_ak4114_reinit(struct ak4114 *ak4114); diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c index 5bf4fca..15ae025 100644 --- a/sound/i2c/other/ak4114.c +++ b/sound/i2c/other/ak4114.c @@ -60,7 +60,7 @@ static void reg_dump(struct ak4114 *ak4114) printk(KERN_DEBUG "AK4114 REG DUMP:\n"); for (i = 0; i < 0x20; i++) - printk(KERN_DEBUG "reg[%02x] = %02x (%02x)\n", i, reg_read(ak4114, i), i < sizeof(ak4114->regmap) ? ak4114->regmap[i] : 0); + printk(KERN_DEBUG "reg[%02x] = %02x (%02x)\n", i, reg_read(ak4114, i), i < ARRAY_SIZE(ak4114->regmap) ? ak4114->regmap[i] : 0); } #endif @@ -81,7 +81,7 @@ static int snd_ak4114_dev_free(struct snd_device *device) int snd_ak4114_create(struct snd_card *card, ak4114_read_t *read, ak4114_write_t *write, - const unsigned char pgm[7], const unsigned char txcsb[5], + const unsigned char pgm[6], const unsigned char txcsb[5], void *private_data, struct ak4114 **r_ak4114) { struct ak4114 *chip; @@ -101,7 +101,7 @@ int snd_ak4114_create(struct snd_card *card, chip->private_data = private_data; INIT_DELAYED_WORK(&chip->work, ak4114_stats); - for (reg = 0; reg < 7; reg++) + for (reg = 0; reg < 6; reg++) chip->regmap[reg] = pgm[reg]; for (reg = 0; reg < 5; reg++) chip->txcsb[reg] = txcsb[reg]; @@ -142,7 +142,7 @@ static void ak4114_init_regs(struct ak4114 *chip) /* release reset, but leave powerdown */ reg_write(chip, AK4114_REG_PWRDN, (old | AK4114_RST) & ~AK4114_PWN); udelay(200); - for (reg = 1; reg < 7; reg++) + for (reg = 1; reg < 6; reg++) reg_write(chip, reg, chip->regmap[reg]); for (reg = 0; reg < 5; reg++) reg_write(chip, reg + AK4114_REG_TXCSB0, chip->txcsb[reg]); -- cgit v0.10.2 From 4c88b7f287e329698727049641ecdf50c8c66af5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 29 Oct 2013 16:45:34 +0100 Subject: ALSA: ice1724: Fix uninitialized variable access Spotted by coverity CIDs 751505 and 751506. Signed-off-by: Takashi Iwai diff --git a/sound/pci/ice1712/wm8766.c b/sound/pci/ice1712/wm8766.c index e473f8a..21b373b 100644 --- a/sound/pci/ice1712/wm8766.c +++ b/sound/pci/ice1712/wm8766.c @@ -253,7 +253,8 @@ static int snd_wm8766_ctl_get(struct snd_kcontrol *kcontrol, } if (wm->ctl[n].flags & WM8766_FLAG_INVERT) { val1 = wm->ctl[n].max - (val1 - wm->ctl[n].min); - val2 = wm->ctl[n].max - (val2 - wm->ctl[n].min); + if (wm->ctl[n].flags & WM8766_FLAG_STEREO) + val2 = wm->ctl[n].max - (val2 - wm->ctl[n].min); } ucontrol->value.integer.value[0] = val1; if (wm->ctl[n].flags & WM8766_FLAG_STEREO) diff --git a/sound/pci/ice1712/wm8776.c b/sound/pci/ice1712/wm8776.c index 5227cb0..e66c0da 100644 --- a/sound/pci/ice1712/wm8776.c +++ b/sound/pci/ice1712/wm8776.c @@ -526,7 +526,8 @@ static int snd_wm8776_ctl_get(struct snd_kcontrol *kcontrol, } if (wm->ctl[n].flags & WM8776_FLAG_INVERT) { val1 = wm->ctl[n].max - (val1 - wm->ctl[n].min); - val2 = wm->ctl[n].max - (val2 - wm->ctl[n].min); + if (wm->ctl[n].flags & WM8776_FLAG_STEREO) + val2 = wm->ctl[n].max - (val2 - wm->ctl[n].min); } ucontrol->value.integer.value[0] = val1; if (wm->ctl[n].flags & WM8776_FLAG_STEREO) -- cgit v0.10.2 From 9cd5ab9c3c32767e5c5e76bed9b0e2b84512f936 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 29 Oct 2013 16:51:36 +0100 Subject: ALSA: lola: Fix uninitialized variable access in error message The FUNCTION_TYPE parameter isn't associated with any NID, thus showing the uninitialized nid in the error message is simply nonsense. Spotted by coverity CID 145068. Signed-off-by: Takashi Iwai diff --git a/sound/pci/lola/lola.c b/sound/pci/lola/lola.c index 7307d97..0568540 100644 --- a/sound/pci/lola/lola.c +++ b/sound/pci/lola/lola.c @@ -463,7 +463,7 @@ static int lola_parse_tree(struct lola *chip) err = lola_read_param(chip, 1, LOLA_PAR_FUNCTION_TYPE, &val); if (err < 0) { - printk(KERN_ERR SFX "Can't read FUNCTION_TYPE for 0x%x\n", nid); + printk(KERN_ERR SFX "Can't read FUNCTION_TYPE\n"); return err; } if (val != 1) { -- cgit v0.10.2 From e16dbf6011137343f51685c1e0c5be36a68fc501 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Tue, 29 Oct 2013 15:32:19 +0100 Subject: pinctrl: imx27: imx27 pincontrol driver imx27 pincontrol driver using the imx1 core driver. The DT bindings are similar to other imx pincontrol drivers. Signed-off-by: Markus Pargmann Acked-by: Sascha Hauer Acked-by: Shawn Guo Signed-off-by: Linus Walleij diff --git a/Documentation/devicetree/bindings/pinctrl/fsl,imx27-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/fsl,imx27-pinctrl.txt new file mode 100644 index 0000000..353eca0 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/fsl,imx27-pinctrl.txt @@ -0,0 +1,99 @@ +* Freescale IMX27 IOMUX Controller + +Required properties: +- compatible: "fsl,imx27-iomuxc" + +The iomuxc driver node should define subnodes containing of pinctrl configuration subnodes. + +Required properties for pin configuration node: +- fsl,pins: three integers array, represents a group of pins mux and config + setting. The format is fsl,pins = . + + PIN is an integer between 0 and 0xbf. imx27 has 6 ports with 32 configurable + configurable pins each. PIN is PORT * 32 + PORT_PIN, PORT_PIN is the pin + number on the specific port (between 0 and 31). + + MUX_ID is + function + (direction << 2) + (gpio_oconf << 4) + (gpio_iconfa << 8) + (gpio_iconfb << 10) + + function value is used to select the pin function. + Possible values: + 0 - Primary function + 1 - Alternate function + 2 - GPIO + Registers: GIUS (GPIO In Use), GPR (General Purpose Register) + + direction defines the data direction of the pin. + Possible values: + 0 - Input + 1 - Output + Register: DDIR + + gpio_oconf configures the gpio submodule output signal. This does not + have any effect unless GPIO function is selected. A/B/C_IN are output + signals of function blocks A,B and C. Specific function blocks are + described in the reference manual. + Possible values: + 0 - A_IN + 1 - B_IN + 2 - C_IN + 3 - Data Register + Registers: OCR1, OCR2 + + gpio_iconfa/b configures the gpio submodule input to functionblocks A and + B. GPIO function should be selected if this is configured. + Possible values: + 0 - GPIO_IN + 1 - Interrupt Status Register + 2 - Pulldown + 3 - Pullup + Registers ICONFA1, ICONFA2, ICONFB1 and ICONFB2 + + CONFIG can be 0 or 1, meaning Pullup disable/enable. + + + +Example: + +iomuxc: iomuxc@10015000 { + compatible = "fsl,imx27-iomuxc"; + reg = <0x10015000 0x600>; + + uart { + pinctrl_uart1: uart-1 { + fsl,pins = < + 0x8c 0x004 0x0 /* UART1_TXD__UART1_TXD */ + 0x8d 0x000 0x0 /* UART1_RXD__UART1_RXD */ + 0x8e 0x004 0x0 /* UART1_CTS__UART1_CTS */ + 0x8f 0x000 0x0 /* UART1_RTS__UART1_RTS */ + >; + }; + + ... + }; +}; + + +For convenience there are macros defined in imx27-pinfunc.h which provide PIN +and MUX_ID. They are structured as MX27_PAD___. The names +are defined in the i.MX27 reference manual. + +The above example using macros: + +iomuxc: iomuxc@10015000 { + compatible = "fsl,imx27-iomuxc"; + reg = <0x10015000 0x600>; + + uart { + pinctrl_uart1: uart-1 { + fsl,pins = < + MX27_PAD_UART1_TXD__UART1_TXD 0x0 + MX27_PAD_UART1_RXD__UART1_RXD 0x0 + MX27_PAD_UART1_CTS__UART1_CTS 0x0 + MX27_PAD_UART1_RTS__UART1_RTS 0x0 + >; + }; + + ... + }; +}; diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index fcc748f..db1ddca 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -114,6 +114,14 @@ config PINCTRL_IMX1_CORE select PINMUX select PINCONF +config PINCTRL_IMX27 + bool "IMX27 pinctrl driver" + depends on OF + depends on SOC_IMX27 + select PINCTRL_IMX1_CORE + help + Say Y here to enable the imx27 pinctrl driver + config PINCTRL_IMX35 bool "IMX35 pinctrl driver" depends on OF diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 8b334e3..915d19c 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_PINCTRL_BCM2835) += pinctrl-bcm2835.o obj-$(CONFIG_PINCTRL_BAYTRAIL) += pinctrl-baytrail.o obj-$(CONFIG_PINCTRL_IMX) += pinctrl-imx.o obj-$(CONFIG_PINCTRL_IMX1_CORE) += pinctrl-imx1-core.o +obj-$(CONFIG_PINCTRL_IMX27) += pinctrl-imx27.o obj-$(CONFIG_PINCTRL_IMX35) += pinctrl-imx35.o obj-$(CONFIG_PINCTRL_IMX51) += pinctrl-imx51.o obj-$(CONFIG_PINCTRL_IMX53) += pinctrl-imx53.o diff --git a/drivers/pinctrl/pinctrl-imx27.c b/drivers/pinctrl/pinctrl-imx27.c new file mode 100644 index 0000000..417c992 --- /dev/null +++ b/drivers/pinctrl/pinctrl-imx27.c @@ -0,0 +1,477 @@ +/* + * imx27 pinctrl driver based on imx pinmux core + * + * Copyright (C) 2013 Pengutronix + * + * Author: Markus Pargmann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "pinctrl-imx1.h" + +#define PAD_ID(port, pin) (port*32 + pin) +#define PA 0 +#define PB 1 +#define PC 2 +#define PD 3 +#define PE 4 +#define PF 5 + +enum imx27_pads { + MX27_PAD_USBH2_CLK = PAD_ID(PA, 0), + MX27_PAD_USBH2_DIR = PAD_ID(PA, 1), + MX27_PAD_USBH2_DATA7 = PAD_ID(PA, 2), + MX27_PAD_USBH2_NXT = PAD_ID(PA, 3), + MX27_PAD_USBH2_STP = PAD_ID(PA, 4), + MX27_PAD_LSCLK = PAD_ID(PA, 5), + MX27_PAD_LD0 = PAD_ID(PA, 6), + MX27_PAD_LD1 = PAD_ID(PA, 7), + MX27_PAD_LD2 = PAD_ID(PA, 8), + MX27_PAD_LD3 = PAD_ID(PA, 9), + MX27_PAD_LD4 = PAD_ID(PA, 10), + MX27_PAD_LD5 = PAD_ID(PA, 11), + MX27_PAD_LD6 = PAD_ID(PA, 12), + MX27_PAD_LD7 = PAD_ID(PA, 13), + MX27_PAD_LD8 = PAD_ID(PA, 14), + MX27_PAD_LD9 = PAD_ID(PA, 15), + MX27_PAD_LD10 = PAD_ID(PA, 16), + MX27_PAD_LD11 = PAD_ID(PA, 17), + MX27_PAD_LD12 = PAD_ID(PA, 18), + MX27_PAD_LD13 = PAD_ID(PA, 19), + MX27_PAD_LD14 = PAD_ID(PA, 20), + MX27_PAD_LD15 = PAD_ID(PA, 21), + MX27_PAD_LD16 = PAD_ID(PA, 22), + MX27_PAD_LD17 = PAD_ID(PA, 23), + MX27_PAD_REV = PAD_ID(PA, 24), + MX27_PAD_CLS = PAD_ID(PA, 25), + MX27_PAD_PS = PAD_ID(PA, 26), + MX27_PAD_SPL_SPR = PAD_ID(PA, 27), + MX27_PAD_HSYNC = PAD_ID(PA, 28), + MX27_PAD_VSYNC = PAD_ID(PA, 29), + MX27_PAD_CONTRAST = PAD_ID(PA, 30), + MX27_PAD_OE_ACD = PAD_ID(PA, 31), + + MX27_PAD_UNUSED0 = PAD_ID(PB, 0), + MX27_PAD_UNUSED1 = PAD_ID(PB, 1), + MX27_PAD_UNUSED2 = PAD_ID(PB, 2), + MX27_PAD_UNUSED3 = PAD_ID(PB, 3), + MX27_PAD_SD2_D0 = PAD_ID(PB, 4), + MX27_PAD_SD2_D1 = PAD_ID(PB, 5), + MX27_PAD_SD2_D2 = PAD_ID(PB, 6), + MX27_PAD_SD2_D3 = PAD_ID(PB, 7), + MX27_PAD_SD2_CMD = PAD_ID(PB, 8), + MX27_PAD_SD2_CLK = PAD_ID(PB, 9), + MX27_PAD_CSI_D0 = PAD_ID(PB, 10), + MX27_PAD_CSI_D1 = PAD_ID(PB, 11), + MX27_PAD_CSI_D2 = PAD_ID(PB, 12), + MX27_PAD_CSI_D3 = PAD_ID(PB, 13), + MX27_PAD_CSI_D4 = PAD_ID(PB, 14), + MX27_PAD_CSI_MCLK = PAD_ID(PB, 15), + MX27_PAD_CSI_PIXCLK = PAD_ID(PB, 16), + MX27_PAD_CSI_D5 = PAD_ID(PB, 17), + MX27_PAD_CSI_D6 = PAD_ID(PB, 18), + MX27_PAD_CSI_D7 = PAD_ID(PB, 19), + MX27_PAD_CSI_VSYNC = PAD_ID(PB, 20), + MX27_PAD_CSI_HSYNC = PAD_ID(PB, 21), + MX27_PAD_USBH1_SUSP = PAD_ID(PB, 22), + MX27_PAD_USB_PWR = PAD_ID(PB, 23), + MX27_PAD_USB_OC_B = PAD_ID(PB, 24), + MX27_PAD_USBH1_RCV = PAD_ID(PB, 25), + MX27_PAD_USBH1_FS = PAD_ID(PB, 26), + MX27_PAD_USBH1_OE_B = PAD_ID(PB, 27), + MX27_PAD_USBH1_TXDM = PAD_ID(PB, 28), + MX27_PAD_USBH1_TXDP = PAD_ID(PB, 29), + MX27_PAD_USBH1_RXDM = PAD_ID(PB, 30), + MX27_PAD_USBH1_RXDP = PAD_ID(PB, 31), + + MX27_PAD_UNUSED4 = PAD_ID(PC, 0), + MX27_PAD_UNUSED5 = PAD_ID(PC, 1), + MX27_PAD_UNUSED6 = PAD_ID(PC, 2), + MX27_PAD_UNUSED7 = PAD_ID(PC, 3), + MX27_PAD_UNUSED8 = PAD_ID(PC, 4), + MX27_PAD_I2C2_SDA = PAD_ID(PC, 5), + MX27_PAD_I2C2_SCL = PAD_ID(PC, 6), + MX27_PAD_USBOTG_DATA5 = PAD_ID(PC, 7), + MX27_PAD_USBOTG_DATA6 = PAD_ID(PC, 8), + MX27_PAD_USBOTG_DATA0 = PAD_ID(PC, 9), + MX27_PAD_USBOTG_DATA2 = PAD_ID(PC, 10), + MX27_PAD_USBOTG_DATA1 = PAD_ID(PC, 11), + MX27_PAD_USBOTG_DATA4 = PAD_ID(PC, 12), + MX27_PAD_USBOTG_DATA3 = PAD_ID(PC, 13), + MX27_PAD_TOUT = PAD_ID(PC, 14), + MX27_PAD_TIN = PAD_ID(PC, 15), + MX27_PAD_SSI4_FS = PAD_ID(PC, 16), + MX27_PAD_SSI4_RXDAT = PAD_ID(PC, 17), + MX27_PAD_SSI4_TXDAT = PAD_ID(PC, 18), + MX27_PAD_SSI4_CLK = PAD_ID(PC, 19), + MX27_PAD_SSI1_FS = PAD_ID(PC, 20), + MX27_PAD_SSI1_RXDAT = PAD_ID(PC, 21), + MX27_PAD_SSI1_TXDAT = PAD_ID(PC, 22), + MX27_PAD_SSI1_CLK = PAD_ID(PC, 23), + MX27_PAD_SSI2_FS = PAD_ID(PC, 24), + MX27_PAD_SSI2_RXDAT = PAD_ID(PC, 25), + MX27_PAD_SSI2_TXDAT = PAD_ID(PC, 26), + MX27_PAD_SSI2_CLK = PAD_ID(PC, 27), + MX27_PAD_SSI3_FS = PAD_ID(PC, 28), + MX27_PAD_SSI3_RXDAT = PAD_ID(PC, 29), + MX27_PAD_SSI3_TXDAT = PAD_ID(PC, 30), + MX27_PAD_SSI3_CLK = PAD_ID(PC, 31), + + MX27_PAD_SD3_CMD = PAD_ID(PD, 0), + MX27_PAD_SD3_CLK = PAD_ID(PD, 1), + MX27_PAD_ATA_DATA0 = PAD_ID(PD, 2), + MX27_PAD_ATA_DATA1 = PAD_ID(PD, 3), + MX27_PAD_ATA_DATA2 = PAD_ID(PD, 4), + MX27_PAD_ATA_DATA3 = PAD_ID(PD, 5), + MX27_PAD_ATA_DATA4 = PAD_ID(PD, 6), + MX27_PAD_ATA_DATA5 = PAD_ID(PD, 7), + MX27_PAD_ATA_DATA6 = PAD_ID(PD, 8), + MX27_PAD_ATA_DATA7 = PAD_ID(PD, 9), + MX27_PAD_ATA_DATA8 = PAD_ID(PD, 10), + MX27_PAD_ATA_DATA9 = PAD_ID(PD, 11), + MX27_PAD_ATA_DATA10 = PAD_ID(PD, 12), + MX27_PAD_ATA_DATA11 = PAD_ID(PD, 13), + MX27_PAD_ATA_DATA12 = PAD_ID(PD, 14), + MX27_PAD_ATA_DATA13 = PAD_ID(PD, 15), + MX27_PAD_ATA_DATA14 = PAD_ID(PD, 16), + MX27_PAD_I2C_DATA = PAD_ID(PD, 17), + MX27_PAD_I2C_CLK = PAD_ID(PD, 18), + MX27_PAD_CSPI2_SS2 = PAD_ID(PD, 19), + MX27_PAD_CSPI2_SS1 = PAD_ID(PD, 20), + MX27_PAD_CSPI2_SS0 = PAD_ID(PD, 21), + MX27_PAD_CSPI2_SCLK = PAD_ID(PD, 22), + MX27_PAD_CSPI2_MISO = PAD_ID(PD, 23), + MX27_PAD_CSPI2_MOSI = PAD_ID(PD, 24), + MX27_PAD_CSPI1_RDY = PAD_ID(PD, 25), + MX27_PAD_CSPI1_SS2 = PAD_ID(PD, 26), + MX27_PAD_CSPI1_SS1 = PAD_ID(PD, 27), + MX27_PAD_CSPI1_SS0 = PAD_ID(PD, 28), + MX27_PAD_CSPI1_SCLK = PAD_ID(PD, 29), + MX27_PAD_CSPI1_MISO = PAD_ID(PD, 30), + MX27_PAD_CSPI1_MOSI = PAD_ID(PD, 31), + + MX27_PAD_USBOTG_NXT = PAD_ID(PE, 0), + MX27_PAD_USBOTG_STP = PAD_ID(PE, 1), + MX27_PAD_USBOTG_DIR = PAD_ID(PE, 2), + MX27_PAD_UART2_CTS = PAD_ID(PE, 3), + MX27_PAD_UART2_RTS = PAD_ID(PE, 4), + MX27_PAD_PWMO = PAD_ID(PE, 5), + MX27_PAD_UART2_TXD = PAD_ID(PE, 6), + MX27_PAD_UART2_RXD = PAD_ID(PE, 7), + MX27_PAD_UART3_TXD = PAD_ID(PE, 8), + MX27_PAD_UART3_RXD = PAD_ID(PE, 9), + MX27_PAD_UART3_CTS = PAD_ID(PE, 10), + MX27_PAD_UART3_RTS = PAD_ID(PE, 11), + MX27_PAD_UART1_TXD = PAD_ID(PE, 12), + MX27_PAD_UART1_RXD = PAD_ID(PE, 13), + MX27_PAD_UART1_CTS = PAD_ID(PE, 14), + MX27_PAD_UART1_RTS = PAD_ID(PE, 15), + MX27_PAD_RTCK = PAD_ID(PE, 16), + MX27_PAD_RESET_OUT_B = PAD_ID(PE, 17), + MX27_PAD_SD1_D0 = PAD_ID(PE, 18), + MX27_PAD_SD1_D1 = PAD_ID(PE, 19), + MX27_PAD_SD1_D2 = PAD_ID(PE, 20), + MX27_PAD_SD1_D3 = PAD_ID(PE, 21), + MX27_PAD_SD1_CMD = PAD_ID(PE, 22), + MX27_PAD_SD1_CLK = PAD_ID(PE, 23), + MX27_PAD_USBOTG_CLK = PAD_ID(PE, 24), + MX27_PAD_USBOTG_DATA7 = PAD_ID(PE, 25), + MX27_PAD_UNUSED9 = PAD_ID(PE, 26), + MX27_PAD_UNUSED10 = PAD_ID(PE, 27), + MX27_PAD_UNUSED11 = PAD_ID(PE, 28), + MX27_PAD_UNUSED12 = PAD_ID(PE, 29), + MX27_PAD_UNUSED13 = PAD_ID(PE, 30), + MX27_PAD_UNUSED14 = PAD_ID(PE, 31), + + MX27_PAD_NFRB = PAD_ID(PF, 0), + MX27_PAD_NFCLE = PAD_ID(PF, 1), + MX27_PAD_NFWP_B = PAD_ID(PF, 2), + MX27_PAD_NFCE_B = PAD_ID(PF, 3), + MX27_PAD_NFALE = PAD_ID(PF, 4), + MX27_PAD_NFRE_B = PAD_ID(PF, 5), + MX27_PAD_NFWE_B = PAD_ID(PF, 6), + MX27_PAD_PC_POE = PAD_ID(PF, 7), + MX27_PAD_PC_RW_B = PAD_ID(PF, 8), + MX27_PAD_IOIS16 = PAD_ID(PF, 9), + MX27_PAD_PC_RST = PAD_ID(PF, 10), + MX27_PAD_PC_BVD2 = PAD_ID(PF, 11), + MX27_PAD_PC_BVD1 = PAD_ID(PF, 12), + MX27_PAD_PC_VS2 = PAD_ID(PF, 13), + MX27_PAD_PC_VS1 = PAD_ID(PF, 14), + MX27_PAD_CLKO = PAD_ID(PF, 15), + MX27_PAD_PC_PWRON = PAD_ID(PF, 16), + MX27_PAD_PC_READY = PAD_ID(PF, 17), + MX27_PAD_PC_WAIT_B = PAD_ID(PF, 18), + MX27_PAD_PC_CD2_B = PAD_ID(PF, 19), + MX27_PAD_PC_CD1_B = PAD_ID(PF, 20), + MX27_PAD_CS4_B = PAD_ID(PF, 21), + MX27_PAD_CS5_B = PAD_ID(PF, 22), + MX27_PAD_ATA_DATA15 = PAD_ID(PF, 23), + MX27_PAD_UNUSED15 = PAD_ID(PF, 24), + MX27_PAD_UNUSED16 = PAD_ID(PF, 25), + MX27_PAD_UNUSED17 = PAD_ID(PF, 26), + MX27_PAD_UNUSED18 = PAD_ID(PF, 27), + MX27_PAD_UNUSED19 = PAD_ID(PF, 28), + MX27_PAD_UNUSED20 = PAD_ID(PF, 29), + MX27_PAD_UNUSED21 = PAD_ID(PF, 30), + MX27_PAD_UNUSED22 = PAD_ID(PF, 31), +}; + +/* Pad names for the pinmux subsystem */ +static const struct pinctrl_pin_desc imx27_pinctrl_pads[] = { + IMX_PINCTRL_PIN(MX27_PAD_USBH2_CLK), + IMX_PINCTRL_PIN(MX27_PAD_USBH2_DIR), + IMX_PINCTRL_PIN(MX27_PAD_USBH2_DATA7), + IMX_PINCTRL_PIN(MX27_PAD_USBH2_NXT), + IMX_PINCTRL_PIN(MX27_PAD_USBH2_STP), + IMX_PINCTRL_PIN(MX27_PAD_LSCLK), + IMX_PINCTRL_PIN(MX27_PAD_LD0), + IMX_PINCTRL_PIN(MX27_PAD_LD1), + IMX_PINCTRL_PIN(MX27_PAD_LD2), + IMX_PINCTRL_PIN(MX27_PAD_LD3), + IMX_PINCTRL_PIN(MX27_PAD_LD4), + IMX_PINCTRL_PIN(MX27_PAD_LD5), + IMX_PINCTRL_PIN(MX27_PAD_LD6), + IMX_PINCTRL_PIN(MX27_PAD_LD7), + IMX_PINCTRL_PIN(MX27_PAD_LD8), + IMX_PINCTRL_PIN(MX27_PAD_LD9), + IMX_PINCTRL_PIN(MX27_PAD_LD10), + IMX_PINCTRL_PIN(MX27_PAD_LD11), + IMX_PINCTRL_PIN(MX27_PAD_LD12), + IMX_PINCTRL_PIN(MX27_PAD_LD13), + IMX_PINCTRL_PIN(MX27_PAD_LD14), + IMX_PINCTRL_PIN(MX27_PAD_LD15), + IMX_PINCTRL_PIN(MX27_PAD_LD16), + IMX_PINCTRL_PIN(MX27_PAD_LD17), + IMX_PINCTRL_PIN(MX27_PAD_REV), + IMX_PINCTRL_PIN(MX27_PAD_CLS), + IMX_PINCTRL_PIN(MX27_PAD_PS), + IMX_PINCTRL_PIN(MX27_PAD_SPL_SPR), + IMX_PINCTRL_PIN(MX27_PAD_HSYNC), + IMX_PINCTRL_PIN(MX27_PAD_VSYNC), + IMX_PINCTRL_PIN(MX27_PAD_CONTRAST), + IMX_PINCTRL_PIN(MX27_PAD_OE_ACD), + + IMX_PINCTRL_PIN(MX27_PAD_UNUSED0), + IMX_PINCTRL_PIN(MX27_PAD_UNUSED1), + IMX_PINCTRL_PIN(MX27_PAD_UNUSED2), + IMX_PINCTRL_PIN(MX27_PAD_UNUSED3), + IMX_PINCTRL_PIN(MX27_PAD_SD2_D0), + IMX_PINCTRL_PIN(MX27_PAD_SD2_D1), + IMX_PINCTRL_PIN(MX27_PAD_SD2_D2), + IMX_PINCTRL_PIN(MX27_PAD_SD2_D3), + IMX_PINCTRL_PIN(MX27_PAD_SD2_CMD), + IMX_PINCTRL_PIN(MX27_PAD_SD2_CLK), + IMX_PINCTRL_PIN(MX27_PAD_CSI_D0), + IMX_PINCTRL_PIN(MX27_PAD_CSI_D1), + IMX_PINCTRL_PIN(MX27_PAD_CSI_D2), + IMX_PINCTRL_PIN(MX27_PAD_CSI_D3), + IMX_PINCTRL_PIN(MX27_PAD_CSI_D4), + IMX_PINCTRL_PIN(MX27_PAD_CSI_MCLK), + IMX_PINCTRL_PIN(MX27_PAD_CSI_PIXCLK), + IMX_PINCTRL_PIN(MX27_PAD_CSI_D5), + IMX_PINCTRL_PIN(MX27_PAD_CSI_D6), + IMX_PINCTRL_PIN(MX27_PAD_CSI_D7), + IMX_PINCTRL_PIN(MX27_PAD_CSI_VSYNC), + IMX_PINCTRL_PIN(MX27_PAD_CSI_HSYNC), + IMX_PINCTRL_PIN(MX27_PAD_USBH1_SUSP), + IMX_PINCTRL_PIN(MX27_PAD_USB_PWR), + IMX_PINCTRL_PIN(MX27_PAD_USB_OC_B), + IMX_PINCTRL_PIN(MX27_PAD_USBH1_RCV), + IMX_PINCTRL_PIN(MX27_PAD_USBH1_FS), + IMX_PINCTRL_PIN(MX27_PAD_USBH1_OE_B), + IMX_PINCTRL_PIN(MX27_PAD_USBH1_TXDM), + IMX_PINCTRL_PIN(MX27_PAD_USBH1_TXDP), + IMX_PINCTRL_PIN(MX27_PAD_USBH1_RXDM), + IMX_PINCTRL_PIN(MX27_PAD_USBH1_RXDP), + + IMX_PINCTRL_PIN(MX27_PAD_UNUSED4), + IMX_PINCTRL_PIN(MX27_PAD_UNUSED5), + IMX_PINCTRL_PIN(MX27_PAD_UNUSED6), + IMX_PINCTRL_PIN(MX27_PAD_UNUSED7), + IMX_PINCTRL_PIN(MX27_PAD_UNUSED8), + IMX_PINCTRL_PIN(MX27_PAD_I2C2_SDA), + IMX_PINCTRL_PIN(MX27_PAD_I2C2_SCL), + IMX_PINCTRL_PIN(MX27_PAD_USBOTG_DATA5), + IMX_PINCTRL_PIN(MX27_PAD_USBOTG_DATA6), + IMX_PINCTRL_PIN(MX27_PAD_USBOTG_DATA0), + IMX_PINCTRL_PIN(MX27_PAD_USBOTG_DATA2), + IMX_PINCTRL_PIN(MX27_PAD_USBOTG_DATA1), + IMX_PINCTRL_PIN(MX27_PAD_USBOTG_DATA4), + IMX_PINCTRL_PIN(MX27_PAD_USBOTG_DATA3), + IMX_PINCTRL_PIN(MX27_PAD_TOUT), + IMX_PINCTRL_PIN(MX27_PAD_TIN), + IMX_PINCTRL_PIN(MX27_PAD_SSI4_FS), + IMX_PINCTRL_PIN(MX27_PAD_SSI4_RXDAT), + IMX_PINCTRL_PIN(MX27_PAD_SSI4_TXDAT), + IMX_PINCTRL_PIN(MX27_PAD_SSI4_CLK), + IMX_PINCTRL_PIN(MX27_PAD_SSI1_FS), + IMX_PINCTRL_PIN(MX27_PAD_SSI1_RXDAT), + IMX_PINCTRL_PIN(MX27_PAD_SSI1_TXDAT), + IMX_PINCTRL_PIN(MX27_PAD_SSI1_CLK), + IMX_PINCTRL_PIN(MX27_PAD_SSI2_FS), + IMX_PINCTRL_PIN(MX27_PAD_SSI2_RXDAT), + IMX_PINCTRL_PIN(MX27_PAD_SSI2_TXDAT), + IMX_PINCTRL_PIN(MX27_PAD_SSI2_CLK), + IMX_PINCTRL_PIN(MX27_PAD_SSI3_FS), + IMX_PINCTRL_PIN(MX27_PAD_SSI3_RXDAT), + IMX_PINCTRL_PIN(MX27_PAD_SSI3_TXDAT), + IMX_PINCTRL_PIN(MX27_PAD_SSI3_CLK), + + IMX_PINCTRL_PIN(MX27_PAD_SD3_CMD), + IMX_PINCTRL_PIN(MX27_PAD_SD3_CLK), + IMX_PINCTRL_PIN(MX27_PAD_ATA_DATA0), + IMX_PINCTRL_PIN(MX27_PAD_ATA_DATA1), + IMX_PINCTRL_PIN(MX27_PAD_ATA_DATA2), + IMX_PINCTRL_PIN(MX27_PAD_ATA_DATA3), + IMX_PINCTRL_PIN(MX27_PAD_ATA_DATA4), + IMX_PINCTRL_PIN(MX27_PAD_ATA_DATA5), + IMX_PINCTRL_PIN(MX27_PAD_ATA_DATA6), + IMX_PINCTRL_PIN(MX27_PAD_ATA_DATA7), + IMX_PINCTRL_PIN(MX27_PAD_ATA_DATA8), + IMX_PINCTRL_PIN(MX27_PAD_ATA_DATA9), + IMX_PINCTRL_PIN(MX27_PAD_ATA_DATA10), + IMX_PINCTRL_PIN(MX27_PAD_ATA_DATA11), + IMX_PINCTRL_PIN(MX27_PAD_ATA_DATA12), + IMX_PINCTRL_PIN(MX27_PAD_ATA_DATA13), + IMX_PINCTRL_PIN(MX27_PAD_ATA_DATA14), + IMX_PINCTRL_PIN(MX27_PAD_I2C_DATA), + IMX_PINCTRL_PIN(MX27_PAD_I2C_CLK), + IMX_PINCTRL_PIN(MX27_PAD_CSPI2_SS2), + IMX_PINCTRL_PIN(MX27_PAD_CSPI2_SS1), + IMX_PINCTRL_PIN(MX27_PAD_CSPI2_SS0), + IMX_PINCTRL_PIN(MX27_PAD_CSPI2_SCLK), + IMX_PINCTRL_PIN(MX27_PAD_CSPI2_MISO), + IMX_PINCTRL_PIN(MX27_PAD_CSPI2_MOSI), + IMX_PINCTRL_PIN(MX27_PAD_CSPI1_RDY), + IMX_PINCTRL_PIN(MX27_PAD_CSPI1_SS2), + IMX_PINCTRL_PIN(MX27_PAD_CSPI1_SS1), + IMX_PINCTRL_PIN(MX27_PAD_CSPI1_SS0), + IMX_PINCTRL_PIN(MX27_PAD_CSPI1_SCLK), + IMX_PINCTRL_PIN(MX27_PAD_CSPI1_MISO), + IMX_PINCTRL_PIN(MX27_PAD_CSPI1_MOSI), + + IMX_PINCTRL_PIN(MX27_PAD_USBOTG_NXT), + IMX_PINCTRL_PIN(MX27_PAD_USBOTG_STP), + IMX_PINCTRL_PIN(MX27_PAD_USBOTG_DIR), + IMX_PINCTRL_PIN(MX27_PAD_UART2_CTS), + IMX_PINCTRL_PIN(MX27_PAD_UART2_RTS), + IMX_PINCTRL_PIN(MX27_PAD_PWMO), + IMX_PINCTRL_PIN(MX27_PAD_UART2_TXD), + IMX_PINCTRL_PIN(MX27_PAD_UART2_RXD), + IMX_PINCTRL_PIN(MX27_PAD_UART3_TXD), + IMX_PINCTRL_PIN(MX27_PAD_UART3_RXD), + IMX_PINCTRL_PIN(MX27_PAD_UART3_CTS), + IMX_PINCTRL_PIN(MX27_PAD_UART3_RTS), + IMX_PINCTRL_PIN(MX27_PAD_UART1_TXD), + IMX_PINCTRL_PIN(MX27_PAD_UART1_RXD), + IMX_PINCTRL_PIN(MX27_PAD_UART1_CTS), + IMX_PINCTRL_PIN(MX27_PAD_UART1_RTS), + IMX_PINCTRL_PIN(MX27_PAD_RTCK), + IMX_PINCTRL_PIN(MX27_PAD_RESET_OUT_B), + IMX_PINCTRL_PIN(MX27_PAD_SD1_D0), + IMX_PINCTRL_PIN(MX27_PAD_SD1_D1), + IMX_PINCTRL_PIN(MX27_PAD_SD1_D2), + IMX_PINCTRL_PIN(MX27_PAD_SD1_D3), + IMX_PINCTRL_PIN(MX27_PAD_SD1_CMD), + IMX_PINCTRL_PIN(MX27_PAD_SD1_CLK), + IMX_PINCTRL_PIN(MX27_PAD_USBOTG_CLK), + IMX_PINCTRL_PIN(MX27_PAD_USBOTG_DATA7), + IMX_PINCTRL_PIN(MX27_PAD_UNUSED9), + IMX_PINCTRL_PIN(MX27_PAD_UNUSED10), + IMX_PINCTRL_PIN(MX27_PAD_UNUSED11), + IMX_PINCTRL_PIN(MX27_PAD_UNUSED12), + IMX_PINCTRL_PIN(MX27_PAD_UNUSED13), + IMX_PINCTRL_PIN(MX27_PAD_UNUSED14), + + IMX_PINCTRL_PIN(MX27_PAD_NFRB), + IMX_PINCTRL_PIN(MX27_PAD_NFCLE), + IMX_PINCTRL_PIN(MX27_PAD_NFWP_B), + IMX_PINCTRL_PIN(MX27_PAD_NFCE_B), + IMX_PINCTRL_PIN(MX27_PAD_NFALE), + IMX_PINCTRL_PIN(MX27_PAD_NFRE_B), + IMX_PINCTRL_PIN(MX27_PAD_NFWE_B), + IMX_PINCTRL_PIN(MX27_PAD_PC_POE), + IMX_PINCTRL_PIN(MX27_PAD_PC_RW_B), + IMX_PINCTRL_PIN(MX27_PAD_IOIS16), + IMX_PINCTRL_PIN(MX27_PAD_PC_RST), + IMX_PINCTRL_PIN(MX27_PAD_PC_BVD2), + IMX_PINCTRL_PIN(MX27_PAD_PC_BVD1), + IMX_PINCTRL_PIN(MX27_PAD_PC_VS2), + IMX_PINCTRL_PIN(MX27_PAD_PC_VS1), + IMX_PINCTRL_PIN(MX27_PAD_CLKO), + IMX_PINCTRL_PIN(MX27_PAD_PC_PWRON), + IMX_PINCTRL_PIN(MX27_PAD_PC_READY), + IMX_PINCTRL_PIN(MX27_PAD_PC_WAIT_B), + IMX_PINCTRL_PIN(MX27_PAD_PC_CD2_B), + IMX_PINCTRL_PIN(MX27_PAD_PC_CD1_B), + IMX_PINCTRL_PIN(MX27_PAD_CS4_B), + IMX_PINCTRL_PIN(MX27_PAD_CS5_B), + IMX_PINCTRL_PIN(MX27_PAD_ATA_DATA15), + IMX_PINCTRL_PIN(MX27_PAD_UNUSED15), + IMX_PINCTRL_PIN(MX27_PAD_UNUSED16), + IMX_PINCTRL_PIN(MX27_PAD_UNUSED17), + IMX_PINCTRL_PIN(MX27_PAD_UNUSED18), + IMX_PINCTRL_PIN(MX27_PAD_UNUSED19), + IMX_PINCTRL_PIN(MX27_PAD_UNUSED20), + IMX_PINCTRL_PIN(MX27_PAD_UNUSED21), + IMX_PINCTRL_PIN(MX27_PAD_UNUSED22), +}; + +static struct imx1_pinctrl_soc_info imx27_pinctrl_info = { + .pins = imx27_pinctrl_pads, + .npins = ARRAY_SIZE(imx27_pinctrl_pads), +}; + +static struct of_device_id imx27_pinctrl_of_match[] = { + { .compatible = "fsl,imx27-iomuxc", }, + { /* sentinel */ } +}; + +struct imx27_pinctrl_private { + int num_gpio_childs; + struct platform_device **gpio_dev; + struct mxc_gpio_platform_data *gpio_pdata; +}; + +static int imx27_pinctrl_probe(struct platform_device *pdev) +{ + return imx1_pinctrl_core_probe(pdev, &imx27_pinctrl_info); +} + +static struct platform_driver imx27_pinctrl_driver = { + .driver = { + .name = "imx27-pinctrl", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(imx27_pinctrl_of_match), + }, + .probe = imx27_pinctrl_probe, + .remove = imx1_pinctrl_core_remove, +}; + +static int __init imx27_pinctrl_init(void) +{ + return platform_driver_register(&imx27_pinctrl_driver); +} +arch_initcall(imx27_pinctrl_init); + +static void __exit imx27_pinctrl_exit(void) +{ + platform_driver_unregister(&imx27_pinctrl_driver); +} +module_exit(imx27_pinctrl_exit); +MODULE_AUTHOR("Markus Pargmann "); +MODULE_DESCRIPTION("Freescale IMX27 pinctrl driver"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From b5ef3f2a8074af7aef9a32f4535c57f986364a60 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Tue, 29 Oct 2013 17:06:27 +0800 Subject: ASoC: wm8962: Fix null pointer pdata access in I2C probe() When using DT binding to pass private data, there would be Kernel panic occuring due to NULL pointer access in wm8962_i2c_probe(). Thus fix it. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 2bf9ee7..7dd79c4 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3616,28 +3616,28 @@ static int wm8962_i2c_probe(struct i2c_client *i2c, 0); /* Apply static configuration for GPIOs */ - for (i = 0; i < ARRAY_SIZE(pdata->gpio_init); i++) - if (pdata->gpio_init[i]) { + for (i = 0; i < ARRAY_SIZE(wm8962->pdata.gpio_init); i++) + if (wm8962->pdata.gpio_init[i]) { wm8962_set_gpio_mode(wm8962, i + 1); regmap_write(wm8962->regmap, 0x200 + i, - pdata->gpio_init[i] & 0xffff); + wm8962->pdata.gpio_init[i] & 0xffff); } /* Put the speakers into mono mode? */ - if (pdata->spk_mono) + if (wm8962->pdata.spk_mono) regmap_update_bits(wm8962->regmap, WM8962_CLASS_D_CONTROL_2, WM8962_SPK_MONO_MASK, WM8962_SPK_MONO); /* Micbias setup, detection enable and detection * threasholds. */ - if (pdata->mic_cfg) + if (wm8962->pdata.mic_cfg) regmap_update_bits(wm8962->regmap, WM8962_ADDITIONAL_CONTROL_4, WM8962_MICDET_ENA | WM8962_MICDET_THR_MASK | WM8962_MICSHORT_THR_MASK | WM8962_MICBIAS_LVL, - pdata->mic_cfg); + wm8962->pdata.mic_cfg); /* Latch volume update bits */ regmap_update_bits(wm8962->regmap, WM8962_LEFT_INPUT_VOLUME, @@ -3682,7 +3682,7 @@ static int wm8962_i2c_probe(struct i2c_client *i2c, } if (wm8962->irq) { - if (pdata->irq_active_low) { + if (wm8962->pdata.irq_active_low) { trigger = IRQF_TRIGGER_LOW; irq_pol = WM8962_IRQ_POL; } else { -- cgit v0.10.2 From c2d3f25dda016d9697c5416810d4528770f0a281 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 9 Oct 2013 14:08:09 +0200 Subject: uprobes: Remove the wrong __weak attribute linux/uprobes.h declares arch_uprobe_skip_sstep() as a weak function. But as there is no definition of generic version so when trying to build uprobes for an architecture that doesn't yet have a arch_uprobe_skip_sstep() implementation, the vmlinux will try to call arch_uprobe_skip_sstep() somehwere in Stupidhistan leading to a system crash. We rather want a proper link error so remove arch_uprobe_skip_sstep(). Signed-off-by: Ralf Baechle Signed-off-by: Oleg Nesterov diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index 06f28be..e6fba62 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -123,7 +123,7 @@ extern int uprobe_post_sstep_notifier(struct pt_regs *regs); extern int uprobe_pre_sstep_notifier(struct pt_regs *regs); extern void uprobe_notify_resume(struct pt_regs *regs); extern bool uprobe_deny_signal(void); -extern bool __weak arch_uprobe_skip_sstep(struct arch_uprobe *aup, struct pt_regs *regs); +extern bool arch_uprobe_skip_sstep(struct arch_uprobe *aup, struct pt_regs *regs); extern void uprobe_clear_state(struct mm_struct *mm); #else /* !CONFIG_UPROBES */ struct uprobes_state { -- cgit v0.10.2 From b68e0749100e1b901bf11330f149b321c082178e Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Sun, 13 Oct 2013 21:18:31 +0200 Subject: uprobes: Change the callsite of uprobe_copy_process() Preparation for the next patches. Move the callsite of uprobe_copy_process() in copy_process() down to the succesfull return. We do not care if copy_process() fails, uprobe_free_utask() won't be called in this case so the wrong ->utask != NULL doesn't matter. OTOH, with this change we know that copy_process() can't fail when uprobe_copy_process() is called, the new task should either return to user-mode or call do_exit(). This way uprobe_copy_process() can: 1. setup p->utask != NULL if necessary 2. setup uprobes_state.xol_area 3. use task_work_add(p) Also, move the definition of uprobe_copy_process() down so that it can see get_utask(). Signed-off-by: Oleg Nesterov Acked-by: Srikar Dronamraju diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index ad8e1bd..db7a1dc 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -1345,14 +1345,6 @@ void uprobe_free_utask(struct task_struct *t) } /* - * Called in context of a new clone/fork from copy_process. - */ -void uprobe_copy_process(struct task_struct *t) -{ - t->utask = NULL; -} - -/* * Allocate a uprobe_task object for the task if if necessary. * Called when the thread hits a breakpoint. * @@ -1368,6 +1360,14 @@ static struct uprobe_task *get_utask(void) } /* + * Called in context of a new clone/fork from copy_process. + */ +void uprobe_copy_process(struct task_struct *t) +{ + t->utask = NULL; +} + +/* * Current area->vaddr notion assume the trampoline address is always * equal area->vaddr. * diff --git a/kernel/fork.c b/kernel/fork.c index 086fe73..d3603b8 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1373,7 +1373,6 @@ static struct task_struct *copy_process(unsigned long clone_flags, INIT_LIST_HEAD(&p->pi_state_list); p->pi_state_cache = NULL; #endif - uprobe_copy_process(p); /* * sigaltstack should be cleared when sharing the same VM */ @@ -1490,6 +1489,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, perf_event_fork(p); trace_task_newtask(p, clone_flags); + uprobe_copy_process(p); return p; -- cgit v0.10.2 From 6441ec8b7c108b72789d120562b9f1d976e4aaaf Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Sun, 13 Oct 2013 21:18:35 +0200 Subject: uprobes: Introduce __create_xol_area() No functional changes, preparation. Extract the code which actually allocates/installs the new area into the new helper, __create_xol_area(). While at it remove the unnecessary "ret = ENOMEM" and "ret = 0" in xol_add_vma(), they both have no effect. Signed-off-by: Oleg Nesterov Acked-by: Srikar Dronamraju diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index db7a1dc..ad17d81 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -1096,16 +1096,14 @@ void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned lon } /* Slot allocation for XOL */ -static int xol_add_vma(struct xol_area *area) +static int xol_add_vma(struct mm_struct *mm, struct xol_area *area) { - struct mm_struct *mm = current->mm; int ret = -EALREADY; down_write(&mm->mmap_sem); if (mm->uprobes_state.xol_area) goto fail; - ret = -ENOMEM; /* Try to map as high as possible, this is only a hint. */ area->vaddr = get_unmapped_area(NULL, TASK_SIZE - PAGE_SIZE, PAGE_SIZE, 0, 0); if (area->vaddr & ~PAGE_MASK) { @@ -1120,28 +1118,17 @@ static int xol_add_vma(struct xol_area *area) smp_wmb(); /* pairs with get_xol_area() */ mm->uprobes_state.xol_area = area; - ret = 0; fail: up_write(&mm->mmap_sem); return ret; } -/* - * get_xol_area - Allocate process's xol_area if necessary. - * This area will be used for storing instructions for execution out of line. - * - * Returns the allocated area or NULL. - */ -static struct xol_area *get_xol_area(void) +static struct xol_area *__create_xol_area(void) { struct mm_struct *mm = current->mm; - struct xol_area *area; uprobe_opcode_t insn = UPROBE_SWBP_INSN; - - area = mm->uprobes_state.xol_area; - if (area) - goto ret; + struct xol_area *area; area = kzalloc(sizeof(*area), GFP_KERNEL); if (unlikely(!area)) @@ -1155,13 +1142,13 @@ static struct xol_area *get_xol_area(void) if (!area->page) goto free_bitmap; - /* allocate first slot of task's xol_area for the return probes */ + init_waitqueue_head(&area->wq); + /* Reserve the 1st slot for get_trampoline_vaddr() */ set_bit(0, area->bitmap); - copy_to_page(area->page, 0, &insn, UPROBE_SWBP_INSN_SIZE); atomic_set(&area->slot_count, 1); - init_waitqueue_head(&area->wq); + copy_to_page(area->page, 0, &insn, UPROBE_SWBP_INSN_SIZE); - if (!xol_add_vma(area)) + if (!xol_add_vma(mm, area)) return area; __free_page(area->page); @@ -1170,9 +1157,25 @@ static struct xol_area *get_xol_area(void) free_area: kfree(area); out: + return NULL; +} + +/* + * get_xol_area - Allocate process's xol_area if necessary. + * This area will be used for storing instructions for execution out of line. + * + * Returns the allocated area or NULL. + */ +static struct xol_area *get_xol_area(void) +{ + struct mm_struct *mm = current->mm; + struct xol_area *area; + + if (!mm->uprobes_state.xol_area) + __create_xol_area(); + area = mm->uprobes_state.xol_area; - ret: - smp_read_barrier_depends(); /* pairs with wmb in xol_add_vma() */ + smp_read_barrier_depends(); /* pairs with wmb in xol_add_vma() */ return area; } -- cgit v0.10.2 From af0d95af79773f7637107cd3871aaabcb425f15a Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Sun, 13 Oct 2013 21:18:38 +0200 Subject: uprobes: Teach __create_xol_area() to accept the predefined vaddr Currently xol_add_vma() uses get_unmapped_area() for area->vaddr, but the next patches need to use the fixed address. So this patch adds the new "vaddr" argument to __create_xol_area() which should be used as area->vaddr if it is nonzero. xol_add_vma() doesn't bother to verify that the predefined addr is not used, insert_vm_struct() should fail if find_vma_links() detects the overlap with the existing vma. Also, __create_xol_area() doesn't need __GFP_ZERO to allocate area. Signed-off-by: Oleg Nesterov Acked-by: Srikar Dronamraju diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index ad17d81..7d12a45 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -1104,11 +1104,14 @@ static int xol_add_vma(struct mm_struct *mm, struct xol_area *area) if (mm->uprobes_state.xol_area) goto fail; - /* Try to map as high as possible, this is only a hint. */ - area->vaddr = get_unmapped_area(NULL, TASK_SIZE - PAGE_SIZE, PAGE_SIZE, 0, 0); - if (area->vaddr & ~PAGE_MASK) { - ret = area->vaddr; - goto fail; + if (!area->vaddr) { + /* Try to map as high as possible, this is only a hint. */ + area->vaddr = get_unmapped_area(NULL, TASK_SIZE - PAGE_SIZE, + PAGE_SIZE, 0, 0); + if (area->vaddr & ~PAGE_MASK) { + ret = area->vaddr; + goto fail; + } } ret = install_special_mapping(mm, area->vaddr, PAGE_SIZE, @@ -1124,13 +1127,13 @@ static int xol_add_vma(struct mm_struct *mm, struct xol_area *area) return ret; } -static struct xol_area *__create_xol_area(void) +static struct xol_area *__create_xol_area(unsigned long vaddr) { struct mm_struct *mm = current->mm; uprobe_opcode_t insn = UPROBE_SWBP_INSN; struct xol_area *area; - area = kzalloc(sizeof(*area), GFP_KERNEL); + area = kmalloc(sizeof(*area), GFP_KERNEL); if (unlikely(!area)) goto out; @@ -1142,6 +1145,7 @@ static struct xol_area *__create_xol_area(void) if (!area->page) goto free_bitmap; + area->vaddr = vaddr; init_waitqueue_head(&area->wq); /* Reserve the 1st slot for get_trampoline_vaddr() */ set_bit(0, area->bitmap); @@ -1172,7 +1176,7 @@ static struct xol_area *get_xol_area(void) struct xol_area *area; if (!mm->uprobes_state.xol_area) - __create_xol_area(); + __create_xol_area(0); area = mm->uprobes_state.xol_area; smp_read_barrier_depends(); /* pairs with wmb in xol_add_vma() */ -- cgit v0.10.2 From 248d3a7b2f100078c5f6878351177859380582e9 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Sun, 13 Oct 2013 21:18:41 +0200 Subject: uprobes: Change uprobe_copy_process() to dup return_instances uprobe_copy_process() assumes that the new child doesn't need ->utask, it should be allocated by demand. But this is not true if the forking task has the pending ret- probes, the child should report them as well and thus it needs the copy of parent's ->return_instances chain. Otherwise the child crashes when it returns from the probed function. Alternatively we could cleanup the child's stack, but this needs per-arch changes and this is not what we want. At least systemtap expects a .return in the child too. Note: this change alone doesn't fix the problem, see the next change. Reported-by: Martin Cermak Reported-by: David Smith Signed-off-by: Oleg Nesterov Acked-by: Srikar Dronamraju diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 7d12a45..1c6cda6 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -1366,12 +1366,55 @@ static struct uprobe_task *get_utask(void) return current->utask; } +static int dup_utask(struct task_struct *t, struct uprobe_task *o_utask) +{ + struct uprobe_task *n_utask; + struct return_instance **p, *o, *n; + + n_utask = kzalloc(sizeof(struct uprobe_task), GFP_KERNEL); + if (!n_utask) + return -ENOMEM; + t->utask = n_utask; + + p = &n_utask->return_instances; + for (o = o_utask->return_instances; o; o = o->next) { + n = kmalloc(sizeof(struct return_instance), GFP_KERNEL); + if (!n) + return -ENOMEM; + + *n = *o; + atomic_inc(&n->uprobe->ref); + n->next = NULL; + + *p = n; + p = &n->next; + n_utask->depth++; + } + + return 0; +} + +static void uprobe_warn(struct task_struct *t, const char *msg) +{ + pr_warn("uprobe: %s:%d failed to %s\n", + current->comm, current->pid, msg); +} + /* * Called in context of a new clone/fork from copy_process. */ void uprobe_copy_process(struct task_struct *t) { + struct uprobe_task *utask = current->utask; + struct mm_struct *mm = current->mm; + t->utask = NULL; + + if (mm == t->mm || !utask || !utask->return_instances) + return; + + if (dup_utask(t, utask)) + return uprobe_warn(t, "dup ret instances"); } /* -- cgit v0.10.2 From aa59c53fd4599c91ccf9629af0c2777b89929076 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Sun, 13 Oct 2013 21:18:44 +0200 Subject: uprobes: Change uprobe_copy_process() to dup xol_area This finally fixes the serious bug in uretprobes: a forked child crashes if the parent called fork() with the pending ret probe. Trivial test-case: # perf probe -x /lib/libc.so.6 __fork%return # perf record -e probe_libc:__fork perl -le 'fork || print "OK"' (the child doesn't print "OK", it is killed by SIGSEGV) If the child returns from the probed function it actually returns to trampoline_vaddr, because it got the copy of parent's stack mangled by prepare_uretprobe() when the parent entered this func. It crashes because a) this address is not mapped and b) until the previous change it doesn't have the proper->return_instances info. This means that uprobe_copy_process() has to create xol_area which has the trampoline slot, and its vaddr should be equal to parent's xol_area->vaddr. Unfortunately, uprobe_copy_process() can not simply do __create_xol_area(child, xol_area->vaddr). This could actually work but perf_event_mmap() doesn't expect the usage of foreign ->mm. So we offload this to task_work_run(), and pass the argument via not yet used utask->vaddr. We know that this vaddr is fine for install_special_mapping(), the necessary hole was recently "created" by dup_mmap() which skips the parent's VM_DONTCOPY area, and nobody else could use the new mm. Unfortunately, this also means that we can not handle the errors properly, we obviously can not abort the already completed fork(). So we simply print the warning if GFP_KERNEL allocation (the only possible reason) fails. Reported-by: Martin Cermak Reported-by: David Smith Signed-off-by: Oleg Nesterov Acked-by: Srikar Dronamraju diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 1c6cda6..9f282e1 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -35,6 +35,7 @@ #include /* notifier mechanism */ #include "../../mm/internal.h" /* munlock_vma_page */ #include +#include #include @@ -1400,6 +1401,17 @@ static void uprobe_warn(struct task_struct *t, const char *msg) current->comm, current->pid, msg); } +static void dup_xol_work(struct callback_head *work) +{ + kfree(work); + + if (current->flags & PF_EXITING) + return; + + if (!__create_xol_area(current->utask->vaddr)) + uprobe_warn(current, "dup xol area"); +} + /* * Called in context of a new clone/fork from copy_process. */ @@ -1407,6 +1419,8 @@ void uprobe_copy_process(struct task_struct *t) { struct uprobe_task *utask = current->utask; struct mm_struct *mm = current->mm; + struct callback_head *work; + struct xol_area *area; t->utask = NULL; @@ -1415,6 +1429,20 @@ void uprobe_copy_process(struct task_struct *t) if (dup_utask(t, utask)) return uprobe_warn(t, "dup ret instances"); + + /* The task can fork() after dup_xol_work() fails */ + area = mm->uprobes_state.xol_area; + if (!area) + return uprobe_warn(t, "dup xol area"); + + /* TODO: move it into the union in uprobe_task */ + work = kmalloc(sizeof(*work), GFP_KERNEL); + if (!work) + return uprobe_warn(t, "dup xol area"); + + utask->vaddr = area->vaddr; + init_task_work(work, dup_xol_work); + task_work_add(t, work, true); } /* -- cgit v0.10.2 From 3ab679661721b1ec2aaad99a801870ed59ab1110 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 16 Oct 2013 19:39:37 +0200 Subject: uprobes: Teach uprobe_copy_process() to handle CLONE_VFORK uprobe_copy_process() does nothing if the child shares ->mm with the forking process, but there is a special case: CLONE_VFORK. In this case it would be more correct to do dup_utask() but avoid dup_xol(). This is not that important, the child should not unwind its stack too much, this can corrupt the parent's stack, but at least we need this to allow to ret-probe __vfork() itself. Note: in theory, it would be better to check task_pt_regs(p)->sp instead of CLONE_VFORK, we need to dup_utask() if and only if the child can return from the function called by the parent. But this needs the arch-dependant helper, and I think that nobody actually does clone(same_stack, CLONE_VM). Reported-by: Martin Cermak Reported-by: David Smith Signed-off-by: Oleg Nesterov diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index e6fba62..9e0d5a6 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -117,7 +117,7 @@ extern void uprobe_start_dup_mmap(void); extern void uprobe_end_dup_mmap(void); extern void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm); extern void uprobe_free_utask(struct task_struct *t); -extern void uprobe_copy_process(struct task_struct *t); +extern void uprobe_copy_process(struct task_struct *t, unsigned long flags); extern unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs); extern int uprobe_post_sstep_notifier(struct pt_regs *regs); extern int uprobe_pre_sstep_notifier(struct pt_regs *regs); @@ -174,7 +174,7 @@ static inline unsigned long uprobe_get_swbp_addr(struct pt_regs *regs) static inline void uprobe_free_utask(struct task_struct *t) { } -static inline void uprobe_copy_process(struct task_struct *t) +static inline void uprobe_copy_process(struct task_struct *t, unsigned long flags) { } static inline void uprobe_clear_state(struct mm_struct *mm) diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 9f282e1..ae9e1d2 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -1415,7 +1415,7 @@ static void dup_xol_work(struct callback_head *work) /* * Called in context of a new clone/fork from copy_process. */ -void uprobe_copy_process(struct task_struct *t) +void uprobe_copy_process(struct task_struct *t, unsigned long flags) { struct uprobe_task *utask = current->utask; struct mm_struct *mm = current->mm; @@ -1424,7 +1424,10 @@ void uprobe_copy_process(struct task_struct *t) t->utask = NULL; - if (mm == t->mm || !utask || !utask->return_instances) + if (!utask || !utask->return_instances) + return; + + if (mm == t->mm && !(flags & CLONE_VFORK)) return; if (dup_utask(t, utask)) @@ -1435,6 +1438,9 @@ void uprobe_copy_process(struct task_struct *t) if (!area) return uprobe_warn(t, "dup xol area"); + if (mm == t->mm) + return; + /* TODO: move it into the union in uprobe_task */ work = kmalloc(sizeof(*work), GFP_KERNEL); if (!work) diff --git a/kernel/fork.c b/kernel/fork.c index d3603b8..8531609 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1489,7 +1489,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, perf_event_fork(p); trace_task_newtask(p, clone_flags); - uprobe_copy_process(p); + uprobe_copy_process(p, clone_flags); return p; -- cgit v0.10.2 From b69ac52449c658b7ac40034dc3c5f5f4a71a723d Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sun, 20 Oct 2013 15:14:59 -0700 Subject: gpiolib: make GPIO_DEVRES depend on GPIOLIB Current Kconfig allows GPIO_DEVRES to be selected and compiled without GPIOLIB. This does not make sense anymore since GPIOLIB has become the exclusive way to deal with GPIOs. This patch makes GPIO_DEVRES available only if GPIOLIB is selected. Signed-off-by: Alexandre Courbot Signed-off-by: Linus Walleij diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 92e258c..62eb9ef 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -30,10 +30,6 @@ config ARCH_REQUIRE_GPIOLIB Selecting this from the architecture code will cause the gpiolib code to always get built in. -config GPIO_DEVRES - def_bool y - depends on HAS_IOMEM - menuconfig GPIOLIB bool "GPIO Support" @@ -47,6 +43,10 @@ menuconfig GPIOLIB if GPIOLIB +config GPIO_DEVRES + def_bool y + depends on HAS_IOMEM + config OF_GPIO def_bool y depends on OF -- cgit v0.10.2 From 3c2c628f82a2c48c0d684dbf63b6a4765e784444 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sun, 20 Oct 2013 15:14:58 -0700 Subject: gpiolib: devres: add missing headers Add missing headers for drivers/gpiolib/devres.c. Signed-off-by: Alexandre Courbot Signed-off-by: Linus Walleij diff --git a/drivers/gpio/devres.c b/drivers/gpio/devres.c index fceebdc..307464f 100644 --- a/drivers/gpio/devres.c +++ b/drivers/gpio/devres.c @@ -15,7 +15,9 @@ */ #include +#include #include +#include #include #include -- cgit v0.10.2 From 403c1d0be5ccbd750d25c59d8358843a81e52e3b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 25 Oct 2013 12:59:05 +0200 Subject: gpio: provide stubs for devres gpio functions commit 6b3d8145dcfdbbb43f13544e16f44f4574f941dd "gpiolib: make GPIO_DEVRES depend on GPIOLIB" breaks builds when device drivers are using devm_gpio* devres functions without enabling GPIOLIB, relying on the devres code to be compiled anyway. Provide stubs so that we get these if we're using the devres functions without GPIOLIB. Reported-by: Fengguang Wu Cc: Alexandre Courbot Signed-off-by: Linus Walleij diff --git a/include/linux/gpio.h b/include/linux/gpio.h index c691df0..0c56b9e 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -77,6 +77,15 @@ static inline int irq_to_gpio(unsigned int irq) #endif /* ! CONFIG_ARCH_HAVE_CUSTOM_GPIO_H */ +/* CONFIG_GPIOLIB: bindings for managed devices that want to request gpios */ + +struct device; + +int devm_gpio_request(struct device *dev, unsigned gpio, const char *label); +int devm_gpio_request_one(struct device *dev, unsigned gpio, + unsigned long flags, const char *label); +void devm_gpio_free(struct device *dev, unsigned int gpio); + #else /* ! CONFIG_GPIOLIB */ #include @@ -241,14 +250,25 @@ gpiochip_remove_pin_ranges(struct gpio_chip *chip) WARN_ON(1); } -#endif /* ! CONFIG_GPIOLIB */ +static inline int devm_gpio_request(struct device *dev, unsigned gpio, + const char *label) +{ + WARN_ON(1); + return -EINVAL; +} -struct device; +static inline int devm_gpio_request_one(struct device *dev, unsigned gpio, + unsigned long flags, const char *label) +{ + WARN_ON(1); + return -EINVAL; +} -/* bindings for managed devices that want to request gpios */ -int devm_gpio_request(struct device *dev, unsigned gpio, const char *label); -int devm_gpio_request_one(struct device *dev, unsigned gpio, - unsigned long flags, const char *label); -void devm_gpio_free(struct device *dev, unsigned int gpio); +static inline void devm_gpio_free(struct device *dev, unsigned int gpio) +{ + WARN_ON(1); +} + +#endif /* ! CONFIG_GPIOLIB */ #endif /* __LINUX_GPIO_H */ -- cgit v0.10.2 From 335d7a7d63aa3a6da4d4903ef6e64de4a88f27da Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Tue, 29 Oct 2013 20:10:13 +1100 Subject: gpiolib: include gpio/consumer.h in of_gpio.h for desc_to_gpio() Fixes this build error on sparc: In file included from drivers/spi/spi.c:33:0: include/linux/of_gpio.h: In function 'of_get_named_gpio_flags': include/linux/of_gpio.h:93:3: error: implicit declaration of function 'desc_to_gpio' [-Werror=implicit-function-declaration] Signed-off-by: Stephen Rothwell Signed-off-by: Linus Walleij diff --git a/include/linux/of_gpio.h b/include/linux/of_gpio.h index d71f2cc..f14123a 100644 --- a/include/linux/of_gpio.h +++ b/include/linux/of_gpio.h @@ -19,9 +19,9 @@ #include #include #include +#include struct device_node; -struct gpio_desc; /* * This is Linux-specific flags. By default controllers' and Linux' mapping -- cgit v0.10.2 From f3ed0b66482fa2a0403280174a998487e9054867 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Tue, 29 Oct 2013 01:06:23 +1100 Subject: gpiolib: provide a declaration of seq_file in gpio/driver.h Fixes this build error: In file included from include/asm-generic/gpio.h:13:0, from include/linux/gpio.h:51, from include/linux/of_gpio.h:20, from arch/powerpc/sysdev/ppc4xx_gpio.c:29: include/linux/gpio/driver.h:85:14: error: 'struct seq_file' declared inside= parameter list [-Werror] include/linux/gpio/driver.h:85:14: error: its scope is only this definition= or declaration, which is probably not what you want [-Werror] Signed-off-by: Stephen Rothwell Signed-off-by: Linus Walleij diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index cd9da38..656a27e 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -5,6 +5,7 @@ struct device; struct gpio_desc; +struct seq_file; /** * struct gpio_chip - abstract a GPIO controller -- cgit v0.10.2 From 1400b4204ca334bb67be5b091b45214b57912b33 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Mon, 28 Oct 2013 19:34:41 +1100 Subject: powerpc: Add includes to fix powernv/rng.c build Caused by commit a4da0d50b2a0 ("powerpc: Implement arch_get_random_long/int() for powernv") from the powerpc tree interacting with commit b5b4bb3f6a11 ("of: only include prom.h on sparc") from the dt-rh tree. I added this merge fix patch (which will need to be sent to Linus when these two trees get merged, or could be applied now to the powerpc tree): [ Also add linux/smp.h to get cpu_to_chip_id -- BenH ] Signed-off-by: Stephen Rothwell Acked-by: Rob Herring Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/powernv/rng.c b/arch/powerpc/platforms/powernv/rng.c index 02db7d7..8844628 100644 --- a/arch/powerpc/platforms/powernv/rng.c +++ b/arch/powerpc/platforms/powernv/rng.c @@ -11,10 +11,13 @@ #include #include +#include #include #include +#include #include #include +#include #include -- cgit v0.10.2 From f5467e28d4e2569b2c71435d438ce3c7f9ac238e Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 29 Oct 2013 16:21:26 +1100 Subject: powerpc/boot: Don't change link address for OF-based platforms Commit c55aef0e5bc6 ("powerpc/boot: Change the load address for the wrapper to fit the kernel") adjusts the wrapper address unnecessarily for platforms that use arch/powerpc/boot/of.c, since the code there allocates space for the kernel wherever it can find it and doesn't necessarily load the kernel at address 0. Changing the link address is actually harmful since it can cause the zImage to overlap with Open Firmware and thus fail to boot. To fix this, we set make_space to n for all of the platforms that use of.o. Signed-off-by: Paul Mackerras Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/boot/wrapper b/arch/powerpc/boot/wrapper index cd7af84..ac16e99 100755 --- a/arch/powerpc/boot/wrapper +++ b/arch/powerpc/boot/wrapper @@ -150,18 +150,22 @@ case "$platform" in pseries) platformo="$object/of.o $object/epapr.o" link_address='0x4000000' + make_space=n ;; maple) platformo="$object/of.o $object/epapr.o" link_address='0x400000' + make_space=n ;; pmac|chrp) platformo="$object/of.o $object/epapr.o" + make_space=n ;; coff) platformo="$object/crt0.o $object/of.o $object/epapr.o" lds=$object/zImage.coff.lds link_address='0x500000' + make_space=n pie= ;; miboot|uboot*) -- cgit v0.10.2 From ec32dd663da29d0e2e3ca964d6a94c683a6582a9 Mon Sep 17 00:00:00 2001 From: Robert Jennings Date: Mon, 28 Oct 2013 09:20:50 -0500 Subject: powerpc: Fix warnings for arch/powerpc/mm/numa.c Simple fixes for sparse warnings in this file. Resolves: arch/powerpc/mm/numa.c:198:24: warning: Using plain integer as NULL pointer arch/powerpc/mm/numa.c:1157:5: warning: symbol 'hot_add_node_scn_to_nid' was not declared. Should it be static? arch/powerpc/mm/numa.c:1238:28: warning: Using plain integer as NULL pointer arch/powerpc/mm/numa.c:1538:6: warning: symbol 'topology_schedule_update' was not declared. Should it be static? Signed-off-by: Robert C Jennings Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index c916127..33d6784 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -195,7 +195,7 @@ static const __be32 *of_get_usable_memory(struct device_node *memory) u32 len; prop = of_get_property(memory, "linux,drconf-usable-memory", &len); if (!prop || len < sizeof(unsigned int)) - return 0; + return NULL; return prop; } @@ -1154,7 +1154,7 @@ static int hot_add_drconf_scn_to_nid(struct device_node *memory, * represented in the device tree as a node (i.e. memory@XXXX) for * each memblock. */ -int hot_add_node_scn_to_nid(unsigned long scn_addr) +static int hot_add_node_scn_to_nid(unsigned long scn_addr) { struct device_node *memory; int nid = -1; @@ -1235,7 +1235,7 @@ static u64 hot_add_drconf_memory_max(void) struct device_node *memory = NULL; unsigned int drconf_cell_cnt = 0; u64 lmb_size = 0; - const __be32 *dm = 0; + const __be32 *dm = NULL; memory = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory"); if (memory) { @@ -1535,7 +1535,7 @@ static void topology_work_fn(struct work_struct *work) } static DECLARE_WORK(topology_work, topology_work_fn); -void topology_schedule_update(void) +static void topology_schedule_update(void) { schedule_work(&topology_work); } -- cgit v0.10.2 From b88c4767d9e2290aaa22b8b3702ad72af0ebd113 Mon Sep 17 00:00:00 2001 From: Robert Jennings Date: Mon, 28 Oct 2013 09:20:51 -0500 Subject: powerpc: Move local setup.h declarations to arch includes Move the few declarations from arch/powerpc/kernel/setup.h into arch/powerpc/include/asm/setup.h. This resolves a sparse warning for arch/powerpc/mm/numa.c which defines do_init_bootmem() but can't include the setup.h header in the prior path. Resolves: arch/powerpc/mm/numa.c:998:13: warning: symbol 'do_init_bootmem' was not declared. Should it be static? Signed-off-by: Robert C Jennings Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/setup.h b/arch/powerpc/include/asm/setup.h index d3ca855..703a841 100644 --- a/arch/powerpc/include/asm/setup.h +++ b/arch/powerpc/include/asm/setup.h @@ -23,6 +23,10 @@ extern void reloc_got2(unsigned long); #define PTRRELOC(x) ((typeof(x)) add_reloc_offset((unsigned long)(x))) +void check_for_initrd(void); +void do_init_bootmem(void); +void setup_panic(void); + #endif /* !__ASSEMBLY__ */ #endif /* _ASM_POWERPC_SETUP_H */ diff --git a/arch/powerpc/kernel/module.c b/arch/powerpc/kernel/module.c index 2d27570..9547381 100644 --- a/arch/powerpc/kernel/module.c +++ b/arch/powerpc/kernel/module.c @@ -25,8 +25,7 @@ #include #include #include - -#include "setup.h" +#include LIST_HEAD(module_bug_list); diff --git a/arch/powerpc/kernel/module_32.c b/arch/powerpc/kernel/module_32.c index 2e3200c..6cff040 100644 --- a/arch/powerpc/kernel/module_32.c +++ b/arch/powerpc/kernel/module_32.c @@ -26,8 +26,7 @@ #include #include #include - -#include "setup.h" +#include #if 0 #define DEBUGP printk diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c index a102f44..12664c1 100644 --- a/arch/powerpc/kernel/module_64.c +++ b/arch/powerpc/kernel/module_64.c @@ -26,8 +26,7 @@ #include #include #include - -#include "setup.h" +#include /* FIXME: We don't do .init separately. To do this, we'd need to have a separate r2 value in the init and core section, and stub between diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c index 3d261c0..febc804 100644 --- a/arch/powerpc/kernel/setup-common.c +++ b/arch/powerpc/kernel/setup-common.c @@ -62,8 +62,6 @@ #include #include -#include "setup.h" - #ifdef DEBUG #include #define DBG(fmt...) udbg_printf(fmt) diff --git a/arch/powerpc/kernel/setup.h b/arch/powerpc/kernel/setup.h deleted file mode 100644 index 4c67ad7..0000000 --- a/arch/powerpc/kernel/setup.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _POWERPC_KERNEL_SETUP_H -#define _POWERPC_KERNEL_SETUP_H - -void check_for_initrd(void); -void do_init_bootmem(void); -void setup_panic(void); -extern int do_early_xmon; - -#endif /* _POWERPC_KERNEL_SETUP_H */ diff --git a/arch/powerpc/kernel/setup_32.c b/arch/powerpc/kernel/setup_32.c index a4bbcae..b903dc5 100644 --- a/arch/powerpc/kernel/setup_32.c +++ b/arch/powerpc/kernel/setup_32.c @@ -40,8 +40,6 @@ #include #include -#include "setup.h" - #define DBG(fmt...) extern void bootx_init(unsigned long r4, unsigned long phys); diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index 278ca93..4085aaa 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c @@ -68,8 +68,6 @@ #include #include -#include "setup.h" - #ifdef DEBUG #define DBG(fmt...) udbg_printf(fmt) #else diff --git a/arch/powerpc/kernel/vdso.c b/arch/powerpc/kernel/vdso.c index 1d9c926..094e45c 100644 --- a/arch/powerpc/kernel/vdso.c +++ b/arch/powerpc/kernel/vdso.c @@ -34,8 +34,7 @@ #include #include #include - -#include "setup.h" +#include #undef DEBUG -- cgit v0.10.2 From b83941798c35f9cffba36927011df2b53c3884d8 Mon Sep 17 00:00:00 2001 From: Vaishnavi Bhat Date: Sun, 27 Oct 2013 11:47:19 +0530 Subject: powerpc: Fix a typo in comments of va to pa conversion This patch fixes typo in comments virtual to physical address conversion. Signed-off-by: Vaishnavi Bhat Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/page.h b/arch/powerpc/include/asm/page.h index b9f4262..753c662 100644 --- a/arch/powerpc/include/asm/page.h +++ b/arch/powerpc/include/asm/page.h @@ -78,7 +78,7 @@ extern unsigned int HPAGE_SHIFT; * * Also, KERNELBASE >= PAGE_OFFSET and PHYSICAL_START >= MEMORY_START * - * There are two was to determine a physical address from a virtual one: + * There are two ways to determine a physical address from a virtual one: * va = pa + PAGE_OFFSET - MEMORY_START * va = pa + KERNELBASE - PHYSICAL_START * -- cgit v0.10.2 From 90890b1ef568a54547ef0490a7c15d7357b98bc9 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Mon, 28 Oct 2013 15:00:35 +1100 Subject: powerpc: FA_DUMP depends on KEXEC If you try and build the FA_DUMP code with CONFIG_KEXEC=n, you see errors such as the following: arch/powerpc/kernel/fadump.c 408:2: error: 'crashing_cpu' undeclared (first use in this function) 410:2: error: implicit declaration of function 'crash_save_vmcoreinfo' 513:22: error: storage size of 'prstatus' isn't known 520:2: error: implicit declaration of function 'elf_core_copy_kernel_regs' 521:36: error: 'KEXEC_CORE_NOTE_NAME' undeclared (first use in this function) 624:49: error: 'note_buf_t' undeclared (first use in this function) 872:2: error: implicit declaration of function 'paddr_vmcoreinfo_note' 874:18: error: 'vmcoreinfo_max_size' undeclared (first use in this function) This is because although FA_DUMP doesn't use kexec as the actual reboot mechanism, it does use parts of the kexec code to assemble/disassemble the crash image. Signed-off-by: Michael Ellerman Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 5f15468..de5c61d 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -407,7 +407,7 @@ config CRASH_DUMP config FA_DUMP bool "Firmware-assisted dump" - depends on PPC64 && PPC_RTAS && CRASH_DUMP + depends on PPC64 && PPC_RTAS && CRASH_DUMP && KEXEC help A robust mechanism to get reliable kernel crash dump with assistance from firmware. This approach does not use kexec, -- cgit v0.10.2 From ecb35c3943040f4db735c7d14c24ee7750cbb482 Mon Sep 17 00:00:00 2001 From: Alistair Popple Date: Thu, 17 Oct 2013 17:08:28 +1100 Subject: powerpc: Fix 64K page size support for PPC44x PPC44x supports page sizes other than 4K however when 64K page sizes are selected compilation fails. This is due to a change in the definition of pgtable_t introduced by the following patch: commit 5c1f6ee9a31cbdac90bbb8ae1ba4475031ac74b4 Author: Aneesh Kumar K.V powerpc: Reduce PTE table memory wastage The above patch only implements the new layout for PPC64 so it doesn't compile for PPC32 with a 64K page size. Ideally we should implement the same layout for PPC32 however for the meantime this patch reverts the definition of pgtable_t for PPC32. Signed-off-by: Alistair Popple Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/page.h b/arch/powerpc/include/asm/page.h index 753c662..32e4e21 100644 --- a/arch/powerpc/include/asm/page.h +++ b/arch/powerpc/include/asm/page.h @@ -403,7 +403,7 @@ void arch_free_page(struct page *page, int order); struct vm_area_struct; -#ifdef CONFIG_PPC_64K_PAGES +#if defined(CONFIG_PPC_64K_PAGES) && defined(CONFIG_PPC64) typedef pte_t *pgtable_t; #else typedef struct page *pgtable_t; -- cgit v0.10.2 From 411cabf79e684171669ad29a0628c400b4431e95 Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Thu, 17 Oct 2013 08:00:11 -0400 Subject: powerpc/vio: use strcpy in modalias_show Commit e82b89a6f19bae73fb064d1b3dd91fcefbb478f4 used strcat instead of strcpy which can result in an overflow of newlines on the buffer. Signed-off-by: Prarit Bhargava Cc: benh@kernel.crashing.org Cc: ben@decadent.org.uk Cc: stable@vger.kernel.org Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c index d38cc08..cb92d82 100644 --- a/arch/powerpc/kernel/vio.c +++ b/arch/powerpc/kernel/vio.c @@ -1531,12 +1531,12 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, dn = dev->of_node; if (!dn) { - strcat(buf, "\n"); + strcpy(buf, "\n"); return strlen(buf); } cp = of_get_property(dn, "compatible", NULL); if (!cp) { - strcat(buf, "\n"); + strcpy(buf, "\n"); return strlen(buf); } -- cgit v0.10.2 From 3e7cec6b17d1c915cf4de1a3853f1b4ae0ed26b0 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Thu, 17 Oct 2013 23:19:43 +1100 Subject: powerpc: Fix little endian issue in OF PCI scan This issue was causing the QEMU emulated USB device to fail dring PCI probe. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/pci_of_scan.c b/arch/powerpc/kernel/pci_of_scan.c index 4368ec6..ac0b034 100644 --- a/arch/powerpc/kernel/pci_of_scan.c +++ b/arch/powerpc/kernel/pci_of_scan.c @@ -302,7 +302,7 @@ static struct pci_dev *of_scan_pci_dev(struct pci_bus *bus, struct device_node *dn) { struct pci_dev *dev = NULL; - const u32 *reg; + const __be32 *reg; int reglen, devfn; pr_debug(" * %s\n", dn->full_name); @@ -312,7 +312,7 @@ static struct pci_dev *of_scan_pci_dev(struct pci_bus *bus, reg = of_get_property(dn, "reg", ®len); if (reg == NULL || reglen < 20) return NULL; - devfn = (reg[0] >> 8) & 0xff; + devfn = (of_read_number(reg, 1) >> 8) & 0xff; /* Check if the PCI device is already there */ dev = pci_get_slot(bus, devfn); -- cgit v0.10.2 From df015604cf5bb09f79c353926dd7e40d00d251a3 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Thu, 17 Oct 2013 23:21:15 +1100 Subject: powerpc/pseries: Fix endian issues in pseries iommu code Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index 0307901..f253361 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -52,7 +52,7 @@ static void tce_invalidate_pSeries_sw(struct iommu_table *tbl, - u64 *startp, u64 *endp) + __be64 *startp, __be64 *endp) { u64 __iomem *invalidate = (u64 __iomem *)tbl->it_index; unsigned long start, end, inc; @@ -86,7 +86,7 @@ static int tce_build_pSeries(struct iommu_table *tbl, long index, struct dma_attrs *attrs) { u64 proto_tce; - u64 *tcep, *tces; + __be64 *tcep, *tces; u64 rpn; proto_tce = TCE_PCI_READ; // Read allowed @@ -94,12 +94,12 @@ static int tce_build_pSeries(struct iommu_table *tbl, long index, if (direction != DMA_TO_DEVICE) proto_tce |= TCE_PCI_WRITE; - tces = tcep = ((u64 *)tbl->it_base) + index; + tces = tcep = ((__be64 *)tbl->it_base) + index; while (npages--) { /* can't move this out since we might cross MEMBLOCK boundary */ rpn = __pa(uaddr) >> TCE_SHIFT; - *tcep = proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT; + *tcep = cpu_to_be64(proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT); uaddr += TCE_PAGE_SIZE; tcep++; @@ -113,9 +113,9 @@ static int tce_build_pSeries(struct iommu_table *tbl, long index, static void tce_free_pSeries(struct iommu_table *tbl, long index, long npages) { - u64 *tcep, *tces; + __be64 *tcep, *tces; - tces = tcep = ((u64 *)tbl->it_base) + index; + tces = tcep = ((__be64 *)tbl->it_base) + index; while (npages--) *(tcep++) = 0; @@ -126,11 +126,11 @@ static void tce_free_pSeries(struct iommu_table *tbl, long index, long npages) static unsigned long tce_get_pseries(struct iommu_table *tbl, long index) { - u64 *tcep; + __be64 *tcep; - tcep = ((u64 *)tbl->it_base) + index; + tcep = ((__be64 *)tbl->it_base) + index; - return *tcep; + return be64_to_cpu(*tcep); } static void tce_free_pSeriesLP(struct iommu_table*, long, long); @@ -177,7 +177,7 @@ static int tce_build_pSeriesLP(struct iommu_table *tbl, long tcenum, return ret; } -static DEFINE_PER_CPU(u64 *, tce_page); +static DEFINE_PER_CPU(__be64 *, tce_page); static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, long npages, unsigned long uaddr, @@ -186,7 +186,7 @@ static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, { u64 rc = 0; u64 proto_tce; - u64 *tcep; + __be64 *tcep; u64 rpn; long l, limit; long tcenum_start = tcenum, npages_start = npages; @@ -206,7 +206,7 @@ static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, * from iommu_alloc{,_sg}() */ if (!tcep) { - tcep = (u64 *)__get_free_page(GFP_ATOMIC); + tcep = (__be64 *)__get_free_page(GFP_ATOMIC); /* If allocation fails, fall back to the loop implementation */ if (!tcep) { local_irq_restore(flags); @@ -230,7 +230,7 @@ static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, limit = min_t(long, npages, 4096/TCE_ENTRY_SIZE); for (l = 0; l < limit; l++) { - tcep[l] = proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT; + tcep[l] = cpu_to_be64(proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT); rpn++; } @@ -329,16 +329,16 @@ struct direct_window { /* Dynamic DMA Window support */ struct ddw_query_response { - u32 windows_available; - u32 largest_available_block; - u32 page_size; - u32 migration_capable; + __be32 windows_available; + __be32 largest_available_block; + __be32 page_size; + __be32 migration_capable; }; struct ddw_create_response { - u32 liobn; - u32 addr_hi; - u32 addr_lo; + __be32 liobn; + __be32 addr_hi; + __be32 addr_lo; }; static LIST_HEAD(direct_window_list); @@ -392,7 +392,8 @@ static int tce_setrange_multi_pSeriesLP(unsigned long start_pfn, unsigned long num_pfn, const void *arg) { const struct dynamic_dma_window_prop *maprange = arg; - u64 *tcep, tce_size, num_tce, dma_offset, next, proto_tce, liobn; + u64 tce_size, num_tce, dma_offset, next, proto_tce, liobn; + __be64 *tcep; u32 tce_shift; u64 rc = 0; long l, limit; @@ -401,7 +402,7 @@ static int tce_setrange_multi_pSeriesLP(unsigned long start_pfn, tcep = __get_cpu_var(tce_page); if (!tcep) { - tcep = (u64 *)__get_free_page(GFP_ATOMIC); + tcep = (__be64 *)__get_free_page(GFP_ATOMIC); if (!tcep) { local_irq_enable(); return -ENOMEM; @@ -435,7 +436,7 @@ static int tce_setrange_multi_pSeriesLP(unsigned long start_pfn, dma_offset = next + be64_to_cpu(maprange->dma_base); for (l = 0; l < limit; l++) { - tcep[l] = proto_tce | next; + tcep[l] = cpu_to_be64(proto_tce | next); next += tce_size; } @@ -780,7 +781,7 @@ static u64 find_existing_ddw(struct device_node *pdn) list_for_each_entry(window, &direct_window_list, list) { if (window->device == pdn) { direct64 = window->prop; - dma_addr = direct64->dma_base; + dma_addr = be64_to_cpu(direct64->dma_base); break; } } @@ -1045,11 +1046,11 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn) dev_dbg(&dev->dev, "no free dynamic windows"); goto out_restore_window; } - if (query.page_size & 4) { + if (be32_to_cpu(query.page_size) & 4) { page_shift = 24; /* 16MB */ - } else if (query.page_size & 2) { + } else if (be32_to_cpu(query.page_size) & 2) { page_shift = 16; /* 64kB */ - } else if (query.page_size & 1) { + } else if (be32_to_cpu(query.page_size) & 1) { page_shift = 12; /* 4kB */ } else { dev_dbg(&dev->dev, "no supported direct page size in mask %x", @@ -1059,7 +1060,7 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn) /* verify the window * number of ptes will map the partition */ /* check largest block * page size > max memory hotplug addr */ max_addr = memory_hotplug_max(); - if (query.largest_available_block < (max_addr >> page_shift)) { + if (be32_to_cpu(query.largest_available_block) < (max_addr >> page_shift)) { dev_dbg(&dev->dev, "can't map partiton max 0x%llx with %u " "%llu-sized pages\n", max_addr, query.largest_available_block, 1ULL << page_shift); @@ -1085,7 +1086,7 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn) if (ret != 0) goto out_free_prop; - ddwprop->liobn = cpu_to_be32(create.liobn); + ddwprop->liobn = create.liobn; ddwprop->dma_base = cpu_to_be64(of_read_number(&create.addr_hi, 2)); ddwprop->tce_shift = cpu_to_be32(page_shift); ddwprop->window_shift = cpu_to_be32(len); -- cgit v0.10.2 From 075f6311af30b011eaa8e50341c06a9082a796a9 Mon Sep 17 00:00:00 2001 From: Tom Musta Date: Fri, 18 Oct 2013 12:07:10 -0500 Subject: powerpc: Fix Handler of Unaligned Load/Store Strings The alignment handler is incorrect for unaligned string instructions in little endian mode. These instructions access data as arrays of bytes and thus are endian neutral. However, the routine also handles the load/store multiple instructions, which are NOT endian neutral. This patch toggles the byte swapping flag for the string instructions in little endian builds. This effectively disables the byte swapping logic. Signed-off-by: Tom Musta Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c index 6e3f977..a3169a9 100644 --- a/arch/powerpc/kernel/align.c +++ b/arch/powerpc/kernel/align.c @@ -254,15 +254,20 @@ static int emulate_dcbz(struct pt_regs *regs, unsigned char __user *addr) * bottom 4 bytes of each register, and the loads clear the * top 4 bytes of the affected register. */ +#ifdef __BIG_ENDIAN__ #ifdef CONFIG_PPC64 #define REG_BYTE(rp, i) *((u8 *)((rp) + ((i) >> 2)) + ((i) & 3) + 4) #else #define REG_BYTE(rp, i) *((u8 *)(rp) + (i)) #endif +#endif + +#ifdef __LITTLE_ENDIAN__ +#define REG_BYTE(rp, i) (*(((u8 *)((rp) + ((i)>>2)) + ((i)&3)))) +#endif #define SWIZ_PTR(p) ((unsigned char __user *)((p) ^ swiz)) -#ifdef __BIG_ENDIAN__ static int emulate_multiple(struct pt_regs *regs, unsigned char __user *addr, unsigned int reg, unsigned int nb, unsigned int flags, unsigned int instr, @@ -304,6 +309,15 @@ static int emulate_multiple(struct pt_regs *regs, unsigned char __user *addr, nb0 = nb + reg * 4 - 128; nb = 128 - reg * 4; } +#ifdef __LITTLE_ENDIAN__ + /* + * String instructions are endian neutral but the code + * below is not. Force byte swapping on so that the + * effects of swizzling are undone in the load/store + * loops below. + */ + flags ^= SW; +#endif } else { /* lwm, stmw */ nb = (32 - reg) * 4; @@ -364,6 +378,7 @@ static int emulate_multiple(struct pt_regs *regs, unsigned char __user *addr, * Only POWER6 has these instructions, and it does true little-endian, * so we don't need the address swizzling. */ +#ifdef __BIG_ENDIAN__ static int emulate_fp_pair(unsigned char __user *addr, unsigned int reg, unsigned int flags) { @@ -882,13 +897,9 @@ int fix_alignment(struct pt_regs *regs) * function */ if (flags & M) { -#ifdef __BIG_ENDIAN__ PPC_WARN_ALIGNMENT(multiple, regs); return emulate_multiple(regs, addr, reg, nb, flags, instr, swiz); -#else - return -EFAULT; -#endif } /* Verify the address of the operand */ -- cgit v0.10.2 From 630c8a5fc9fb2f3541652b65f23630757d304cc9 Mon Sep 17 00:00:00 2001 From: Tom Musta Date: Fri, 18 Oct 2013 12:08:22 -0500 Subject: powerpc: Enable Little Endian Alignment Handler for Float Pair Instructions This patch enables alignment handling for the load/store floating point pair instructions (lfdp, lfdpx, stfdp, stfdpx). The handler routine is properly coded and only needs to be enabled. Signed-off-by: Tom Musta Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c index a3169a9..de91f3a 100644 --- a/arch/powerpc/kernel/align.c +++ b/arch/powerpc/kernel/align.c @@ -378,7 +378,6 @@ static int emulate_multiple(struct pt_regs *regs, unsigned char __user *addr, * Only POWER6 has these instructions, and it does true little-endian, * so we don't need the address swizzling. */ -#ifdef __BIG_ENDIAN__ static int emulate_fp_pair(unsigned char __user *addr, unsigned int reg, unsigned int flags) { @@ -406,7 +405,6 @@ static int emulate_fp_pair(unsigned char __user *addr, unsigned int reg, return -EFAULT; return 1; /* exception handled and fixed up */ } -#endif #ifdef CONFIG_SPE @@ -918,12 +916,8 @@ int fix_alignment(struct pt_regs *regs) /* Special case for 16-byte FP loads and stores */ if (nb == 16) { -#ifdef __BIG_ENDIAN__ PPC_WARN_ALIGNMENT(fp_pair, regs); return emulate_fp_pair(addr, reg, flags); -#else - return -EFAULT; -#endif } PPC_WARN_ALIGNMENT(unaligned, regs); -- cgit v0.10.2 From 6506b4718bb59c5d4e59235b81b5e13ea5d3c49a Mon Sep 17 00:00:00 2001 From: Tom Musta Date: Fri, 18 Oct 2013 14:42:08 -0500 Subject: powerpc: Fix Unaligned Loads and Stores This patch modifies the unaligned access routines of the sstep.c module so that it properly reverses the bytes of storage operands in the little endian kernel kernel. This is implemented by breaking an unaligned little endian access into a combination of single byte accesses plus an overal byte reversal operation. Signed-off-by: Tom Musta Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/lib/sstep.c b/arch/powerpc/lib/sstep.c index b1faa15..0121d21 100644 --- a/arch/powerpc/lib/sstep.c +++ b/arch/powerpc/lib/sstep.c @@ -212,11 +212,19 @@ static int __kprobes read_mem_unaligned(unsigned long *dest, unsigned long ea, { int err; unsigned long x, b, c; +#ifdef __LITTLE_ENDIAN__ + int len = nb; /* save a copy of the length for byte reversal */ +#endif /* unaligned, do this in pieces */ x = 0; for (; nb > 0; nb -= c) { +#ifdef __LITTLE_ENDIAN__ + c = 1; +#endif +#ifdef __BIG_ENDIAN__ c = max_align(ea); +#endif if (c > nb) c = max_align(nb); err = read_mem_aligned(&b, ea, c); @@ -225,7 +233,24 @@ static int __kprobes read_mem_unaligned(unsigned long *dest, unsigned long ea, x = (x << (8 * c)) + b; ea += c; } +#ifdef __LITTLE_ENDIAN__ + switch (len) { + case 2: + *dest = byterev_2(x); + break; + case 4: + *dest = byterev_4(x); + break; +#ifdef __powerpc64__ + case 8: + *dest = byterev_8(x); + break; +#endif + } +#endif +#ifdef __BIG_ENDIAN__ *dest = x; +#endif return 0; } @@ -273,9 +298,29 @@ static int __kprobes write_mem_unaligned(unsigned long val, unsigned long ea, int err; unsigned long c; +#ifdef __LITTLE_ENDIAN__ + switch (nb) { + case 2: + val = byterev_2(val); + break; + case 4: + val = byterev_4(val); + break; +#ifdef __powerpc64__ + case 8: + val = byterev_8(val); + break; +#endif + } +#endif /* unaligned or little-endian, do this in pieces */ for (; nb > 0; nb -= c) { +#ifdef __LITTLE_ENDIAN__ + c = 1; +#endif +#ifdef __BIG_ENDIAN__ c = max_align(ea); +#endif if (c > nb) c = max_align(nb); err = write_mem_aligned(val >> (nb - c) * 8, ea, c); -- cgit v0.10.2 From dbc2fbd7c29a78724e761711d516930246c0e1c2 Mon Sep 17 00:00:00 2001 From: Tom Musta Date: Fri, 18 Oct 2013 14:44:17 -0500 Subject: powerpc: Fix Unaligned LE Floating Point Loads and Stores This patch addresses unaligned single precision floating point loads and stores in the single-step code. The old implementation improperly treated an 8 byte structure as an array of two 4 byte words, which is a classic little endian bug. Signed-off-by: Tom Musta Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/lib/sstep.c b/arch/powerpc/lib/sstep.c index 0121d21..c0511c2 100644 --- a/arch/powerpc/lib/sstep.c +++ b/arch/powerpc/lib/sstep.c @@ -355,22 +355,36 @@ static int __kprobes do_fp_load(int rn, int (*func)(int, unsigned long), struct pt_regs *regs) { int err; - unsigned long val[sizeof(double) / sizeof(long)]; + union { + double dbl; + unsigned long ul[2]; + struct { +#ifdef __BIG_ENDIAN__ + unsigned _pad_; + unsigned word; +#endif +#ifdef __LITTLE_ENDIAN__ + unsigned word; + unsigned _pad_; +#endif + } single; + } data; unsigned long ptr; if (!address_ok(regs, ea, nb)) return -EFAULT; if ((ea & 3) == 0) return (*func)(rn, ea); - ptr = (unsigned long) &val[0]; + ptr = (unsigned long) &data.ul; if (sizeof(unsigned long) == 8 || nb == 4) { - err = read_mem_unaligned(&val[0], ea, nb, regs); - ptr += sizeof(unsigned long) - nb; + err = read_mem_unaligned(&data.ul[0], ea, nb, regs); + if (nb == 4) + ptr = (unsigned long)&(data.single.word); } else { /* reading a double on 32-bit */ - err = read_mem_unaligned(&val[0], ea, 4, regs); + err = read_mem_unaligned(&data.ul[0], ea, 4, regs); if (!err) - err = read_mem_unaligned(&val[1], ea + 4, 4, regs); + err = read_mem_unaligned(&data.ul[1], ea + 4, 4, regs); } if (err) return err; @@ -382,28 +396,42 @@ static int __kprobes do_fp_store(int rn, int (*func)(int, unsigned long), struct pt_regs *regs) { int err; - unsigned long val[sizeof(double) / sizeof(long)]; + union { + double dbl; + unsigned long ul[2]; + struct { +#ifdef __BIG_ENDIAN__ + unsigned _pad_; + unsigned word; +#endif +#ifdef __LITTLE_ENDIAN__ + unsigned word; + unsigned _pad_; +#endif + } single; + } data; unsigned long ptr; if (!address_ok(regs, ea, nb)) return -EFAULT; if ((ea & 3) == 0) return (*func)(rn, ea); - ptr = (unsigned long) &val[0]; + ptr = (unsigned long) &data.ul[0]; if (sizeof(unsigned long) == 8 || nb == 4) { - ptr += sizeof(unsigned long) - nb; + if (nb == 4) + ptr = (unsigned long)&(data.single.word); err = (*func)(rn, ptr); if (err) return err; - err = write_mem_unaligned(val[0], ea, nb, regs); + err = write_mem_unaligned(data.ul[0], ea, nb, regs); } else { /* writing a double on 32-bit */ err = (*func)(rn, ptr); if (err) return err; - err = write_mem_unaligned(val[0], ea, 4, regs); + err = write_mem_unaligned(data.ul[0], ea, 4, regs); if (!err) - err = write_mem_unaligned(val[1], ea + 4, 4, regs); + err = write_mem_unaligned(data.ul[1], ea + 4, 4, regs); } return err; } -- cgit v0.10.2 From 733187e29576041ceccf3b82092ca900fc929170 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Sun, 20 Oct 2013 10:26:20 +1100 Subject: powerpc/pseries: Fix dedicated processor partition detection commit f13c13a00512 (powerpc: Stop using non-architected shared_proc field in lppaca) fixed a potential issue with shared/dedicated partition detection. The old method of detection relied on an unarchitected field (shared_proc), and this patch switched to using something architected (a non zero yield_count). Unfortunately the assertion in the Linux header that yield_count is only non zero on shared processor partitions is not true. It turns out dedicated processor partitions can increment yield_count and as such we falsely detect dedicated partitions as shared. Fix the comment, and switch back to using the old method. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/lppaca.h b/arch/powerpc/include/asm/lppaca.h index 4470d1e..844c28d 100644 --- a/arch/powerpc/include/asm/lppaca.h +++ b/arch/powerpc/include/asm/lppaca.h @@ -84,8 +84,8 @@ struct lppaca { * the processor is yielded (either because of an OS yield or a * hypervisor preempt). An even value implies that the processor is * currently executing. - * NOTE: This value will ALWAYS be zero for dedicated processors and - * will NEVER be zero for shared processors (ie, initialized to a 1). + * NOTE: Even dedicated processor partitions can yield so this + * field cannot be used to determine if we are shared or dedicated. */ volatile __be32 yield_count; volatile __be32 dispersion_count; /* dispatch changed physical cpu */ @@ -106,15 +106,15 @@ extern struct lppaca lppaca[]; #define lppaca_of(cpu) (*paca[cpu].lppaca_ptr) /* - * Old kernels used a reserved bit in the VPA to determine if it was running - * in shared processor mode. New kernels look for a non zero yield count - * but KVM still needs to set the bit to keep the old stuff happy. + * We are using a non architected field to determine if a partition is + * shared or dedicated. This currently works on both KVM and PHYP, but + * we will have to transition to something better. */ #define LPPACA_OLD_SHARED_PROC 2 static inline bool lppaca_shared_proc(struct lppaca *l) { - return l->yield_count != 0; + return !!(l->__old_status & LPPACA_OLD_SHARED_PROC); } /* -- cgit v0.10.2 From 686245bee9528a2f62fa88b1f776eacd94d858ee Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Tue, 22 Oct 2013 11:05:25 +1100 Subject: powerpc: Use -mcpu=power7 on ppc64 little endian builds Using -mcpu=power7 allows gcc to use a number of new instructions including 64 bit byte reversed loads. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype index 6704e2e..c2a566f 100644 --- a/arch/powerpc/platforms/Kconfig.cputype +++ b/arch/powerpc/platforms/Kconfig.cputype @@ -93,22 +93,23 @@ choice config GENERIC_CPU bool "Generic" + depends on !CPU_LITTLE_ENDIAN config CELL_CPU bool "Cell Broadband Engine" - depends on PPC_BOOK3S_64 + depends on PPC_BOOK3S_64 && !CPU_LITTLE_ENDIAN config POWER4_CPU bool "POWER4" - depends on PPC_BOOK3S_64 + depends on PPC_BOOK3S_64 && !CPU_LITTLE_ENDIAN config POWER5_CPU bool "POWER5" - depends on PPC_BOOK3S_64 + depends on PPC_BOOK3S_64 && !CPU_LITTLE_ENDIAN config POWER6_CPU bool "POWER6" - depends on PPC_BOOK3S_64 + depends on PPC_BOOK3S_64 && !CPU_LITTLE_ENDIAN config POWER7_CPU bool "POWER7" -- cgit v0.10.2 From 4d16bd39c094751adb556382b95373ac53edba3c Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Tue, 22 Oct 2013 11:44:50 +1100 Subject: powerpc: sync ppc64, ppc64e and pseries configs Run savedefconfig over the ppc64, ppc64e and pseries config Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/configs/ppc64_defconfig b/arch/powerpc/configs/ppc64_defconfig index 0e8cfd0..56ac8bb 100644 --- a/arch/powerpc/configs/ppc64_defconfig +++ b/arch/powerpc/configs/ppc64_defconfig @@ -2,7 +2,6 @@ CONFIG_PPC64=y CONFIG_ALTIVEC=y CONFIG_VSX=y CONFIG_SMP=y -CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_IRQ_DOMAIN_DEBUG=y @@ -25,7 +24,6 @@ CONFIG_MODULE_UNLOAD=y CONFIG_MODVERSIONS=y CONFIG_MODULE_SRCVERSION_ALL=y CONFIG_PARTITION_ADVANCED=y -CONFIG_EFI_PARTITION=y CONFIG_PPC_SPLPAR=y CONFIG_SCANLOG=m CONFIG_PPC_SMLPAR=y @@ -50,12 +48,10 @@ CONFIG_CPU_FREQ_PMAC64=y CONFIG_HZ_100=y CONFIG_BINFMT_MISC=m CONFIG_PPC_TRANSACTIONAL_MEM=y -CONFIG_HOTPLUG_CPU=y CONFIG_KEXEC=y CONFIG_IRQ_ALL_CPUS=y CONFIG_MEMORY_HOTREMOVE=y CONFIG_SCHED_SMT=y -CONFIG_PPC_DENORMALISATION=y CONFIG_PCCARD=y CONFIG_ELECTRA_CF=y CONFIG_HOTPLUG_PCI=y @@ -89,7 +85,6 @@ CONFIG_NF_CONNTRACK_PPTP=m CONFIG_NF_CONNTRACK_SIP=m CONFIG_NF_CONNTRACK_TFTP=m CONFIG_NF_CT_NETLINK=m -CONFIG_NETFILTER_TPROXY=m CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m CONFIG_NETFILTER_XT_TARGET_CONNMARK=m CONFIG_NETFILTER_XT_TARGET_DSCP=m @@ -131,7 +126,6 @@ CONFIG_NETFILTER_XT_MATCH_STRING=m CONFIG_NETFILTER_XT_MATCH_TCPMSS=m CONFIG_NETFILTER_XT_MATCH_U32=m CONFIG_NF_CONNTRACK_IPV4=m -CONFIG_IP_NF_QUEUE=m CONFIG_IP_NF_IPTABLES=m CONFIG_IP_NF_MATCH_AH=m CONFIG_IP_NF_MATCH_ECN=m @@ -216,6 +210,7 @@ CONFIG_DUMMY=m CONFIG_NETCONSOLE=y CONFIG_NETPOLL_TRAP=y CONFIG_TUN=m +CONFIG_VHOST_NET=m CONFIG_VORTEX=y CONFIG_ACENIC=m CONFIG_ACENIC_OMIT_TIGON_I=y @@ -301,7 +296,6 @@ CONFIG_HID_GYRATION=y CONFIG_HID_PANTHERLORD=y CONFIG_HID_PETALYNX=y CONFIG_HID_SAMSUNG=y -CONFIG_HID_SONY=y CONFIG_HID_SUNPLUS=y CONFIG_USB_HIDDEV=y CONFIG_USB=y @@ -386,21 +380,19 @@ CONFIG_NLS_UTF8=y CONFIG_CRC_T10DIF=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_KERNEL=y +CONFIG_DEBUG_STACK_USAGE=y +CONFIG_DEBUG_STACKOVERFLOW=y CONFIG_LOCKUP_DETECTOR=y CONFIG_DEBUG_MUTEXES=y -CONFIG_DEBUG_STACK_USAGE=y CONFIG_LATENCYTOP=y CONFIG_SCHED_TRACER=y CONFIG_BLK_DEV_IO_TRACE=y -CONFIG_DEBUG_STACKOVERFLOW=y CONFIG_CODE_PATCHING_SELFTEST=y CONFIG_FTR_FIXUP_SELFTEST=y CONFIG_MSI_BITMAP_SELFTEST=y CONFIG_XMON=y CONFIG_BOOTX_TEXT=y CONFIG_PPC_EARLY_DEBUG=y -CONFIG_PPC_EARLY_DEBUG_BOOTX=y -CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_TEST=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y @@ -422,4 +414,3 @@ CONFIG_CRYPTO_DEV_NX_ENCRYPT=m CONFIG_VIRTUALIZATION=y CONFIG_KVM_BOOK3S_64=m CONFIG_KVM_BOOK3S_64_HV=y -CONFIG_VHOST_NET=m diff --git a/arch/powerpc/configs/ppc64e_defconfig b/arch/powerpc/configs/ppc64e_defconfig index 0a6be6d..f627fda 100644 --- a/arch/powerpc/configs/ppc64e_defconfig +++ b/arch/powerpc/configs/ppc64e_defconfig @@ -1,7 +1,6 @@ CONFIG_PPC64=y CONFIG_PPC_BOOK3E_64=y CONFIG_SMP=y -CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_NO_HZ=y @@ -61,7 +60,6 @@ CONFIG_NF_CONNTRACK_PPTP=m CONFIG_NF_CONNTRACK_SIP=m CONFIG_NF_CONNTRACK_TFTP=m CONFIG_NF_CT_NETLINK=m -CONFIG_NETFILTER_TPROXY=m CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m CONFIG_NETFILTER_XT_TARGET_CONNMARK=m CONFIG_NETFILTER_XT_TARGET_DSCP=m @@ -103,7 +101,6 @@ CONFIG_NETFILTER_XT_MATCH_STRING=m CONFIG_NETFILTER_XT_MATCH_TCPMSS=m CONFIG_NETFILTER_XT_MATCH_U32=m CONFIG_NF_CONNTRACK_IPV4=m -CONFIG_IP_NF_QUEUE=m CONFIG_IP_NF_IPTABLES=m CONFIG_IP_NF_MATCH_AH=m CONFIG_IP_NF_MATCH_ECN=m @@ -193,7 +190,6 @@ CONFIG_PPP_SYNC_TTY=m CONFIG_INPUT_EVDEV=m CONFIG_INPUT_MISC=y # CONFIG_SERIO_SERPORT is not set -CONFIG_VT_HW_CONSOLE_BINDING=y CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y # CONFIG_HW_RANDOM is not set @@ -230,7 +226,6 @@ CONFIG_HID_NTRIG=y CONFIG_HID_PANTHERLORD=y CONFIG_HID_PETALYNX=y CONFIG_HID_SAMSUNG=y -CONFIG_HID_SONY=y CONFIG_HID_SUNPLUS=y CONFIG_HID_GREENASIA=y CONFIG_HID_SMARTJOYPLUS=y @@ -302,19 +297,18 @@ CONFIG_NLS_UTF8=y CONFIG_CRC_T10DIF=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_KERNEL=y +CONFIG_DEBUG_STACK_USAGE=y +CONFIG_DEBUG_STACKOVERFLOW=y CONFIG_DETECT_HUNG_TASK=y CONFIG_DEBUG_MUTEXES=y -CONFIG_DEBUG_STACK_USAGE=y CONFIG_LATENCYTOP=y CONFIG_IRQSOFF_TRACER=y CONFIG_SCHED_TRACER=y CONFIG_BLK_DEV_IO_TRACE=y -CONFIG_DEBUG_STACKOVERFLOW=y CONFIG_CODE_PATCHING_SELFTEST=y CONFIG_FTR_FIXUP_SELFTEST=y CONFIG_MSI_BITMAP_SELFTEST=y CONFIG_XMON=y -CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_TEST=m CONFIG_CRYPTO_CCM=m CONFIG_CRYPTO_GCM=m diff --git a/arch/powerpc/configs/pseries_defconfig b/arch/powerpc/configs/pseries_defconfig index 1d4b976..2dec56d 100644 --- a/arch/powerpc/configs/pseries_defconfig +++ b/arch/powerpc/configs/pseries_defconfig @@ -3,7 +3,6 @@ CONFIG_ALTIVEC=y CONFIG_VSX=y CONFIG_SMP=y CONFIG_NR_CPUS=2048 -CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_AUDIT=y @@ -33,7 +32,6 @@ CONFIG_MODULE_UNLOAD=y CONFIG_MODVERSIONS=y CONFIG_MODULE_SRCVERSION_ALL=y CONFIG_PARTITION_ADVANCED=y -CONFIG_EFI_PARTITION=y CONFIG_PPC_SPLPAR=y CONFIG_SCANLOG=m CONFIG_PPC_SMLPAR=y @@ -44,7 +42,6 @@ CONFIG_IBMEBUS=y CONFIG_HZ_100=y CONFIG_BINFMT_MISC=m CONFIG_PPC_TRANSACTIONAL_MEM=y -CONFIG_HOTPLUG_CPU=y CONFIG_KEXEC=y CONFIG_IRQ_ALL_CPUS=y CONFIG_MEMORY_HOTPLUG=y @@ -52,7 +49,6 @@ CONFIG_MEMORY_HOTREMOVE=y CONFIG_PPC_64K_PAGES=y CONFIG_PPC_SUBPAGE_PROT=y CONFIG_SCHED_SMT=y -CONFIG_PPC_DENORMALISATION=y CONFIG_HOTPLUG_PCI=y CONFIG_HOTPLUG_PCI_RPA=m CONFIG_HOTPLUG_PCI_RPA_DLPAR=m @@ -113,7 +109,6 @@ CONFIG_NETFILTER_XT_MATCH_TCPMSS=m CONFIG_NETFILTER_XT_MATCH_TIME=m CONFIG_NETFILTER_XT_MATCH_U32=m CONFIG_NF_CONNTRACK_IPV4=m -CONFIG_IP_NF_QUEUE=m CONFIG_IP_NF_IPTABLES=m CONFIG_IP_NF_MATCH_AH=m CONFIG_IP_NF_MATCH_ECN=m @@ -179,6 +174,7 @@ CONFIG_DUMMY=m CONFIG_NETCONSOLE=y CONFIG_NETPOLL_TRAP=y CONFIG_TUN=m +CONFIG_VHOST_NET=m CONFIG_VORTEX=y CONFIG_ACENIC=m CONFIG_ACENIC_OMIT_TIGON_I=y @@ -237,7 +233,6 @@ CONFIG_HID_GYRATION=y CONFIG_HID_PANTHERLORD=y CONFIG_HID_PETALYNX=y CONFIG_HID_SAMSUNG=y -CONFIG_HID_SONY=y CONFIG_HID_SUNPLUS=y CONFIG_USB_HIDDEV=y CONFIG_USB=y @@ -314,18 +309,17 @@ CONFIG_NLS_UTF8=y CONFIG_CRC_T10DIF=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_KERNEL=y -CONFIG_LOCKUP_DETECTOR=y CONFIG_DEBUG_STACK_USAGE=y +CONFIG_DEBUG_STACKOVERFLOW=y +CONFIG_LOCKUP_DETECTOR=y CONFIG_LATENCYTOP=y CONFIG_SCHED_TRACER=y CONFIG_BLK_DEV_IO_TRACE=y -CONFIG_DEBUG_STACKOVERFLOW=y CONFIG_CODE_PATCHING_SELFTEST=y CONFIG_FTR_FIXUP_SELFTEST=y CONFIG_MSI_BITMAP_SELFTEST=y CONFIG_XMON=y CONFIG_XMON_DEFAULT=y -CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_TEST=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y @@ -347,4 +341,3 @@ CONFIG_CRYPTO_DEV_NX_ENCRYPT=m CONFIG_VIRTUALIZATION=y CONFIG_KVM_BOOK3S_64=m CONFIG_KVM_BOOK3S_64_HV=y -CONFIG_VHOST_NET=m -- cgit v0.10.2 From b7697592ef7c52aaf42879ae718a4b0a8a8b17dc Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Tue, 22 Oct 2013 11:45:31 +1100 Subject: powerpc: Enable multipath modules on ppc64 and pseries Enable a few modules required to boot on a POWER multipath box. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/configs/ppc64_defconfig b/arch/powerpc/configs/ppc64_defconfig index 56ac8bb..f2e1d1b 100644 --- a/arch/powerpc/configs/ppc64_defconfig +++ b/arch/powerpc/configs/ppc64_defconfig @@ -179,6 +179,9 @@ CONFIG_SCSI_IPR=y CONFIG_SCSI_QLA_FC=m CONFIG_SCSI_QLA_ISCSI=m CONFIG_SCSI_LPFC=m +CONFIG_SCSI_DH=m +CONFIG_SCSI_DH_RDAC=m +CONFIG_SCSI_DH_ALUA=m CONFIG_ATA=y CONFIG_SATA_SIL24=y CONFIG_SATA_SVW=y @@ -197,6 +200,9 @@ CONFIG_DM_SNAPSHOT=m CONFIG_DM_MIRROR=m CONFIG_DM_ZERO=m CONFIG_DM_MULTIPATH=m +CONFIG_DM_MULTIPATH_QL=m +CONFIG_DM_MULTIPATH_ST=m +CONFIG_DM_UEVENT=y CONFIG_ADB_PMU=y CONFIG_PMAC_SMU=y CONFIG_THERM_PM72=y diff --git a/arch/powerpc/configs/pseries_defconfig b/arch/powerpc/configs/pseries_defconfig index 2dec56d..eafec1a 100644 --- a/arch/powerpc/configs/pseries_defconfig +++ b/arch/powerpc/configs/pseries_defconfig @@ -152,6 +152,9 @@ CONFIG_SCSI_IPR=y CONFIG_SCSI_QLA_FC=m CONFIG_SCSI_QLA_ISCSI=m CONFIG_SCSI_LPFC=m +CONFIG_SCSI_DH=m +CONFIG_SCSI_DH_RDAC=m +CONFIG_SCSI_DH_ALUA=m CONFIG_ATA=y # CONFIG_ATA_SFF is not set CONFIG_MD=y @@ -169,6 +172,9 @@ CONFIG_DM_SNAPSHOT=m CONFIG_DM_MIRROR=m CONFIG_DM_ZERO=m CONFIG_DM_MULTIPATH=m +CONFIG_DM_MULTIPATH_QL=m +CONFIG_DM_MULTIPATH_ST=m +CONFIG_DM_UEVENT=y CONFIG_BONDING=m CONFIG_DUMMY=m CONFIG_NETCONSOLE=y -- cgit v0.10.2 From 65508689d43afd0d8a3b659e83ed51b90c5083cf Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Tue, 22 Oct 2013 11:46:15 +1100 Subject: powerpc: Enable virtio on ppc64 and pseries configs Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/configs/ppc64_defconfig b/arch/powerpc/configs/ppc64_defconfig index f2e1d1b..581a3bc 100644 --- a/arch/powerpc/configs/ppc64_defconfig +++ b/arch/powerpc/configs/ppc64_defconfig @@ -151,6 +151,7 @@ CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_NBD=m CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=65536 +CONFIG_VIRTIO_BLK=m CONFIG_IDE=y CONFIG_BLK_DEV_IDECD=y CONFIG_BLK_DEV_GENERIC=y @@ -179,6 +180,7 @@ CONFIG_SCSI_IPR=y CONFIG_SCSI_QLA_FC=m CONFIG_SCSI_QLA_ISCSI=m CONFIG_SCSI_LPFC=m +CONFIG_SCSI_VIRTIO=m CONFIG_SCSI_DH=m CONFIG_SCSI_DH_RDAC=m CONFIG_SCSI_DH_ALUA=m @@ -216,6 +218,7 @@ CONFIG_DUMMY=m CONFIG_NETCONSOLE=y CONFIG_NETPOLL_TRAP=y CONFIG_TUN=m +CONFIG_VIRTIO_NET=m CONFIG_VHOST_NET=m CONFIG_VORTEX=y CONFIG_ACENIC=m @@ -263,6 +266,7 @@ CONFIG_HVC_CONSOLE=y CONFIG_HVC_RTAS=y CONFIG_HVC_BEAT=y CONFIG_HVCS=m +CONFIG_VIRTIO_CONSOLE=m CONFIG_IBM_BSR=m CONFIG_RAW_DRIVER=y CONFIG_I2C_CHARDEV=y @@ -328,6 +332,8 @@ CONFIG_EDAC_MM_EDAC=y CONFIG_EDAC_PASEMI=y CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_DS1307=y +CONFIG_VIRTIO_PCI=m +CONFIG_VIRTIO_BALLOON=m CONFIG_EXT2_FS=y CONFIG_EXT2_FS_XATTR=y CONFIG_EXT2_FS_POSIX_ACL=y diff --git a/arch/powerpc/configs/pseries_defconfig b/arch/powerpc/configs/pseries_defconfig index eafec1a..e9a8b4e 100644 --- a/arch/powerpc/configs/pseries_defconfig +++ b/arch/powerpc/configs/pseries_defconfig @@ -127,6 +127,7 @@ CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_NBD=m CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=65536 +CONFIG_VIRTIO_BLK=m CONFIG_IDE=y CONFIG_BLK_DEV_IDECD=y CONFIG_BLK_DEV_GENERIC=y @@ -152,6 +153,7 @@ CONFIG_SCSI_IPR=y CONFIG_SCSI_QLA_FC=m CONFIG_SCSI_QLA_ISCSI=m CONFIG_SCSI_LPFC=m +CONFIG_SCSI_VIRTIO=m CONFIG_SCSI_DH=m CONFIG_SCSI_DH_RDAC=m CONFIG_SCSI_DH_ALUA=m @@ -180,6 +182,7 @@ CONFIG_DUMMY=m CONFIG_NETCONSOLE=y CONFIG_NETPOLL_TRAP=y CONFIG_TUN=m +CONFIG_VIRTIO_NET=m CONFIG_VHOST_NET=m CONFIG_VORTEX=y CONFIG_ACENIC=m @@ -218,6 +221,7 @@ CONFIG_SERIAL_JSM=m CONFIG_HVC_CONSOLE=y CONFIG_HVC_RTAS=y CONFIG_HVCS=m +CONFIG_VIRTIO_CONSOLE=m CONFIG_IBM_BSR=m CONFIG_GEN_RTC=y CONFIG_RAW_DRIVER=y @@ -259,6 +263,8 @@ CONFIG_INFINIBAND_IPOIB=m CONFIG_INFINIBAND_IPOIB_CM=y CONFIG_INFINIBAND_SRP=m CONFIG_INFINIBAND_ISER=m +CONFIG_VIRTIO_PCI=m +CONFIG_VIRTIO_BALLOON=m CONFIG_EXT2_FS=y CONFIG_EXT2_FS_XATTR=y CONFIG_EXT2_FS_POSIX_ACL=y -- cgit v0.10.2 From bbe30b3b576b4ba96641a7c244071282618b2cbb Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Tue, 15 Oct 2013 14:36:31 +1100 Subject: powerpc: Use 32 bit loads and stores when operating on condition register values The condition register (CR) is a 32 bit quantity so we should use 32 bit loads and stores. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/tm.S b/arch/powerpc/kernel/tm.S index 761af4f..ef47bcb 100644 --- a/arch/powerpc/kernel/tm.S +++ b/arch/powerpc/kernel/tm.S @@ -106,7 +106,7 @@ DSCR_DEFAULT: _GLOBAL(tm_reclaim) mfcr r6 mflr r0 - std r6, 8(r1) + stw r6, 8(r1) std r0, 16(r1) std r2, 40(r1) stdu r1, -TM_FRAME_SIZE(r1) @@ -285,7 +285,7 @@ dont_backup_fp: REST_NVGPRS(r1) addi r1, r1, TM_FRAME_SIZE - ld r4, 8(r1) + lwz r4, 8(r1) ld r0, 16(r1) mtcr r4 mtlr r0 @@ -310,7 +310,7 @@ dont_backup_fp: _GLOBAL(tm_recheckpoint) mfcr r5 mflr r0 - std r5, 8(r1) + stw r5, 8(r1) std r0, 16(r1) std r2, 40(r1) stdu r1, -TM_FRAME_SIZE(r1) @@ -444,7 +444,7 @@ restore_gprs: REST_NVGPRS(r1) addi r1, r1, TM_FRAME_SIZE - ld r4, 8(r1) + lwz r4, 8(r1) ld r0, 16(r1) mtcr r4 mtlr r0 diff --git a/arch/powerpc/platforms/powernv/opal-wrappers.S b/arch/powerpc/platforms/powernv/opal-wrappers.S index 2a03e1e..403d058 100644 --- a/arch/powerpc/platforms/powernv/opal-wrappers.S +++ b/arch/powerpc/platforms/powernv/opal-wrappers.S @@ -24,7 +24,7 @@ mflr r0; \ mfcr r12; \ std r0,16(r1); \ - std r12,8(r1); \ + stw r12,8(r1); \ std r1,PACAR1(r13); \ li r0,0; \ mfmsr r12; \ @@ -53,7 +53,7 @@ _STATIC(opal_return) */ FIXUP_ENDIAN ld r2,PACATOC(r13); - ld r4,8(r1); + lwz r4,8(r1); ld r5,16(r1); ld r6,PACASAVEDMSR(r13); mtspr SPRN_SRR0,r5; -- cgit v0.10.2 From 07fb41a7525539d7ad37c25f2a2689fd95a6ab68 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 14 Oct 2013 09:40:16 +0200 Subject: powerpc/kexec: kexec_sequence() is in misc_64.S Correct reference to the location of the kexec_sequence() assembly helper. There never was a kexec_stub.S in mainline. Signed-off-by: Geert Uytterhoeven Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/machine_kexec_64.c b/arch/powerpc/kernel/machine_kexec_64.c index 611acdf..be4e6d6 100644 --- a/arch/powerpc/kernel/machine_kexec_64.c +++ b/arch/powerpc/kernel/machine_kexec_64.c @@ -312,7 +312,7 @@ static union thread_union kexec_stack __init_task_data = */ struct paca_struct kexec_paca; -/* Our assembly helper, in kexec_stub.S */ +/* Our assembly helper, in misc_64.S */ extern void kexec_sequence(void *newstack, unsigned long start, void *image, void *control, void (*clear_all)(void)) __noreturn; -- cgit v0.10.2 From ef1313deafb7baa6d3382044e962d5ad5e8c8dd6 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 14 Oct 2013 21:03:58 +1100 Subject: powerpc: Add VMX optimised xor for RAID5 Add a VMX optimised xor, used primarily for RAID5. On a POWER7 blade this is a decent win: 32regs : 17932.800 MB/sec altivec : 19724.800 MB/sec The bigger gain is when the same test is run in SMT4 mode, as it would if there was a lot of work going on: 8regs : 8377.600 MB/sec altivec : 15801.600 MB/sec I tested this against an array created without the patch, and also verified it worked as expected on a little endian kernel. [ Fix !CONFIG_ALTIVEC build -- BenH ] Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/xor.h b/arch/powerpc/include/asm/xor.h index c82eb12..0abb97f 100644 --- a/arch/powerpc/include/asm/xor.h +++ b/arch/powerpc/include/asm/xor.h @@ -1 +1,68 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2012 + * + * Author: Anton Blanchard + */ +#ifndef _ASM_POWERPC_XOR_H +#define _ASM_POWERPC_XOR_H + +#ifdef CONFIG_ALTIVEC + +#include + +void xor_altivec_2(unsigned long bytes, unsigned long *v1_in, + unsigned long *v2_in); +void xor_altivec_3(unsigned long bytes, unsigned long *v1_in, + unsigned long *v2_in, unsigned long *v3_in); +void xor_altivec_4(unsigned long bytes, unsigned long *v1_in, + unsigned long *v2_in, unsigned long *v3_in, + unsigned long *v4_in); +void xor_altivec_5(unsigned long bytes, unsigned long *v1_in, + unsigned long *v2_in, unsigned long *v3_in, + unsigned long *v4_in, unsigned long *v5_in); + +static struct xor_block_template xor_block_altivec = { + .name = "altivec", + .do_2 = xor_altivec_2, + .do_3 = xor_altivec_3, + .do_4 = xor_altivec_4, + .do_5 = xor_altivec_5, +}; + +#define XOR_SPEED_ALTIVEC() \ + do { \ + if (cpu_has_feature(CPU_FTR_ALTIVEC)) \ + xor_speed(&xor_block_altivec); \ + } while (0) +#else +#define XOR_SPEED_ALTIVEC() +#endif + +/* Also try the generic routines. */ #include + +#undef XOR_TRY_TEMPLATES +#define XOR_TRY_TEMPLATES \ +do { \ + xor_speed(&xor_block_8regs); \ + xor_speed(&xor_block_8regs_p); \ + xor_speed(&xor_block_32regs); \ + xor_speed(&xor_block_32regs_p); \ + XOR_SPEED_ALTIVEC(); \ +} while (0) + +#endif /* _ASM_POWERPC_XOR_H */ diff --git a/arch/powerpc/lib/Makefile b/arch/powerpc/lib/Makefile index 5310132..95a20e1 100644 --- a/arch/powerpc/lib/Makefile +++ b/arch/powerpc/lib/Makefile @@ -39,3 +39,6 @@ obj-$(CONFIG_PPC_LIB_RHEAP) += rheap.o obj-y += code-patching.o obj-y += feature-fixups.o obj-$(CONFIG_FTR_FIXUP_SELFTEST) += feature-fixups-test.o + +obj-$(CONFIG_ALTIVEC) += xor_vmx.o +CFLAGS_xor_vmx.o += -maltivec -mabi=altivec diff --git a/arch/powerpc/lib/xor_vmx.c b/arch/powerpc/lib/xor_vmx.c new file mode 100644 index 0000000..e905f7c --- /dev/null +++ b/arch/powerpc/lib/xor_vmx.c @@ -0,0 +1,177 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2012 + * + * Author: Anton Blanchard + */ +#include + +#include +#include +#include +#include + +typedef vector signed char unative_t; + +#define DEFINE(V) \ + unative_t *V = (unative_t *)V##_in; \ + unative_t V##_0, V##_1, V##_2, V##_3 + +#define LOAD(V) \ + do { \ + V##_0 = V[0]; \ + V##_1 = V[1]; \ + V##_2 = V[2]; \ + V##_3 = V[3]; \ + } while (0) + +#define STORE(V) \ + do { \ + V[0] = V##_0; \ + V[1] = V##_1; \ + V[2] = V##_2; \ + V[3] = V##_3; \ + } while (0) + +#define XOR(V1, V2) \ + do { \ + V1##_0 = vec_xor(V1##_0, V2##_0); \ + V1##_1 = vec_xor(V1##_1, V2##_1); \ + V1##_2 = vec_xor(V1##_2, V2##_2); \ + V1##_3 = vec_xor(V1##_3, V2##_3); \ + } while (0) + +void xor_altivec_2(unsigned long bytes, unsigned long *v1_in, + unsigned long *v2_in) +{ + DEFINE(v1); + DEFINE(v2); + unsigned long lines = bytes / (sizeof(unative_t)) / 4; + + preempt_disable(); + enable_kernel_altivec(); + + do { + LOAD(v1); + LOAD(v2); + XOR(v1, v2); + STORE(v1); + + v1 += 4; + v2 += 4; + } while (--lines > 0); + + preempt_enable(); +} +EXPORT_SYMBOL(xor_altivec_2); + +void xor_altivec_3(unsigned long bytes, unsigned long *v1_in, + unsigned long *v2_in, unsigned long *v3_in) +{ + DEFINE(v1); + DEFINE(v2); + DEFINE(v3); + unsigned long lines = bytes / (sizeof(unative_t)) / 4; + + preempt_disable(); + enable_kernel_altivec(); + + do { + LOAD(v1); + LOAD(v2); + LOAD(v3); + XOR(v1, v2); + XOR(v1, v3); + STORE(v1); + + v1 += 4; + v2 += 4; + v3 += 4; + } while (--lines > 0); + + preempt_enable(); +} +EXPORT_SYMBOL(xor_altivec_3); + +void xor_altivec_4(unsigned long bytes, unsigned long *v1_in, + unsigned long *v2_in, unsigned long *v3_in, + unsigned long *v4_in) +{ + DEFINE(v1); + DEFINE(v2); + DEFINE(v3); + DEFINE(v4); + unsigned long lines = bytes / (sizeof(unative_t)) / 4; + + preempt_disable(); + enable_kernel_altivec(); + + do { + LOAD(v1); + LOAD(v2); + LOAD(v3); + LOAD(v4); + XOR(v1, v2); + XOR(v3, v4); + XOR(v1, v3); + STORE(v1); + + v1 += 4; + v2 += 4; + v3 += 4; + v4 += 4; + } while (--lines > 0); + + preempt_enable(); +} +EXPORT_SYMBOL(xor_altivec_4); + +void xor_altivec_5(unsigned long bytes, unsigned long *v1_in, + unsigned long *v2_in, unsigned long *v3_in, + unsigned long *v4_in, unsigned long *v5_in) +{ + DEFINE(v1); + DEFINE(v2); + DEFINE(v3); + DEFINE(v4); + DEFINE(v5); + unsigned long lines = bytes / (sizeof(unative_t)) / 4; + + preempt_disable(); + enable_kernel_altivec(); + + do { + LOAD(v1); + LOAD(v2); + LOAD(v3); + LOAD(v4); + LOAD(v5); + XOR(v1, v2); + XOR(v3, v4); + XOR(v1, v5); + XOR(v1, v3); + STORE(v1); + + v1 += 4; + v2 += 4; + v3 += 4; + v4 += 4; + v5 += 4; + } while (--lines > 0); + + preempt_enable(); +} +EXPORT_SYMBOL(xor_altivec_5); -- cgit v0.10.2 From 6f68b5e2c6c04e9cf0e3074f884da36957ce9aae Mon Sep 17 00:00:00 2001 From: Vasant Hegde Date: Tue, 27 Aug 2013 15:09:52 +0530 Subject: powerpc/powernv: Create opal sysfs directory Create /sys/firmware/opal directory. We wil use this interface to fetch opal error logs, firmware update, etc. Signed-off-by: Vasant Hegde Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h index 4cc33ba..ee0efd2 100644 --- a/arch/powerpc/include/asm/opal.h +++ b/arch/powerpc/include/asm/opal.h @@ -601,6 +601,9 @@ typedef struct oppanel_line { uint64_t line_len; } oppanel_line_t; +/* /sys/firmware/opal */ +extern struct kobject *opal_kobj; + /* API functions */ int64_t opal_console_write(int64_t term_number, __be64 *length, const uint8_t *buffer); diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c index 09336f0..37f0658 100644 --- a/arch/powerpc/platforms/powernv/opal.c +++ b/arch/powerpc/platforms/powernv/opal.c @@ -17,11 +17,15 @@ #include #include #include +#include #include #include #include "powernv.h" +/* /sys/firmware/opal */ +struct kobject *opal_kobj; + struct opal { u64 base; u64 entry; @@ -375,6 +379,17 @@ static irqreturn_t opal_interrupt(int irq, void *data) return IRQ_HANDLED; } +static int opal_sysfs_init(void) +{ + opal_kobj = kobject_create_and_add("opal", firmware_kobj); + if (!opal_kobj) { + pr_warn("kobject_create_and_add opal failed\n"); + return -ENOMEM; + } + + return 0; +} + static int __init opal_init(void) { struct device_node *np, *consoles; @@ -420,6 +435,10 @@ static int __init opal_init(void) " (0x%x)\n", rc, irq, hwirq); opal_irqs[i] = irq; } + + /* Create "opal" kobject under /sys/firmware */ + rc = opal_sysfs_init(); + return 0; } subsys_initcall(opal_init); -- cgit v0.10.2 From 50bd6153d1a68354a0a0c8bca1fe949fa8875875 Mon Sep 17 00:00:00 2001 From: Vasant Hegde Date: Thu, 24 Oct 2013 16:04:58 +0530 Subject: powerpc/powernv: Code update interface Code update interface for powernv platform. This provides sysfs interface to pass new image, validate, update and commit images. This patch includes: - Below OPAL APIs for code update - opal_validate_flash() - opal_manage_flash() - opal_update_flash() - Create below sysfs files under /sys/firmware/opal - image : Interface to pass new FW image - validate_flash : Validate candidate image - manage_flash : Commit/Reject operations - update_flash : Flash new candidate image Updating Image: "update_flash" is an interface to indicate flash new FW. It just passes image SG list to FW. Actual flashing is done during system reboot time. Note: - SG entry format: I have kept version number to keep this list similar to what PAPR is defined. Signed-off-by: Vasant Hegde Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h index ee0efd2..033c06b 100644 --- a/arch/powerpc/include/asm/opal.h +++ b/arch/powerpc/include/asm/opal.h @@ -129,6 +129,9 @@ extern int opal_enter_rtas(struct rtas_args *args, #define OPAL_LPC_READ 67 #define OPAL_LPC_WRITE 68 #define OPAL_RETURN_CPU 69 +#define OPAL_FLASH_VALIDATE 76 +#define OPAL_FLASH_MANAGE 77 +#define OPAL_FLASH_UPDATE 78 #ifndef __ASSEMBLY__ @@ -724,6 +727,9 @@ int64_t opal_lpc_write(uint32_t chip_id, enum OpalLPCAddressType addr_type, uint32_t addr, uint32_t data, uint32_t sz); int64_t opal_lpc_read(uint32_t chip_id, enum OpalLPCAddressType addr_type, uint32_t addr, uint32_t *data, uint32_t sz); +int64_t opal_validate_flash(uint64_t buffer, uint32_t *size, uint32_t *result); +int64_t opal_manage_flash(uint8_t op); +int64_t opal_update_flash(uint64_t blk_list); /* Internal functions */ extern int early_init_dt_scan_opal(unsigned long node, const char *uname, int depth, void *data); @@ -752,6 +758,7 @@ extern int opal_set_rtc_time(struct rtc_time *tm); extern void opal_get_rtc_time(struct rtc_time *tm); extern unsigned long opal_get_boot_time(void); extern void opal_nvram_init(void); +extern void opal_flash_init(void); extern int opal_machine_check(struct pt_regs *regs); diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile index 050d57e..873fa13 100644 --- a/arch/powerpc/platforms/powernv/Makefile +++ b/arch/powerpc/platforms/powernv/Makefile @@ -1,5 +1,6 @@ obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o -obj-y += opal-rtc.o opal-nvram.o opal-lpc.o rng.o +obj-y += opal-rtc.o opal-nvram.o opal-lpc.o opal-flash.o +obj-y += rng.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o pci-ioda.o diff --git a/arch/powerpc/platforms/powernv/opal-flash.c b/arch/powerpc/platforms/powernv/opal-flash.c new file mode 100644 index 0000000..6ffa6b1 --- /dev/null +++ b/arch/powerpc/platforms/powernv/opal-flash.c @@ -0,0 +1,667 @@ +/* + * PowerNV OPAL Firmware Update Interface + * + * Copyright 2013 IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* FLASH status codes */ +#define FLASH_NO_OP -1099 /* No operation initiated by user */ +#define FLASH_NO_AUTH -9002 /* Not a service authority partition */ + +/* Validate image status values */ +#define VALIDATE_IMG_READY -1001 /* Image ready for validation */ +#define VALIDATE_IMG_INCOMPLETE -1002 /* User copied < VALIDATE_BUF_SIZE */ + +/* Manage image status values */ +#define MANAGE_ACTIVE_ERR -9001 /* Cannot overwrite active img */ + +/* Flash image status values */ +#define FLASH_IMG_READY 0 /* Img ready for flash on reboot */ +#define FLASH_INVALID_IMG -1003 /* Flash image shorter than expected */ +#define FLASH_IMG_NULL_DATA -1004 /* Bad data in sg list entry */ +#define FLASH_IMG_BAD_LEN -1005 /* Bad length in sg list entry */ + +/* Manage operation tokens */ +#define FLASH_REJECT_TMP_SIDE 0 /* Reject temporary fw image */ +#define FLASH_COMMIT_TMP_SIDE 1 /* Commit temporary fw image */ + +/* Update tokens */ +#define FLASH_UPDATE_CANCEL 0 /* Cancel update request */ +#define FLASH_UPDATE_INIT 1 /* Initiate update */ + +/* Validate image update result tokens */ +#define VALIDATE_TMP_UPDATE 0 /* T side will be updated */ +#define VALIDATE_FLASH_AUTH 1 /* Partition does not have authority */ +#define VALIDATE_INVALID_IMG 2 /* Candidate image is not valid */ +#define VALIDATE_CUR_UNKNOWN 3 /* Current fixpack level is unknown */ +/* + * Current T side will be committed to P side before being replace with new + * image, and the new image is downlevel from current image + */ +#define VALIDATE_TMP_COMMIT_DL 4 +/* + * Current T side will be committed to P side before being replaced with new + * image + */ +#define VALIDATE_TMP_COMMIT 5 +/* + * T side will be updated with a downlevel image + */ +#define VALIDATE_TMP_UPDATE_DL 6 +/* + * The candidate image's release date is later than the system's firmware + * service entitlement date - service warranty period has expired + */ +#define VALIDATE_OUT_OF_WRNTY 7 + +/* Validate buffer size */ +#define VALIDATE_BUF_SIZE 4096 + +/* XXX: Assume candidate image size is <= 256MB */ +#define MAX_IMAGE_SIZE 0x10000000 + +/* Flash sg list version */ +#define SG_LIST_VERSION (1UL) + +/* Image status */ +enum { + IMAGE_INVALID, + IMAGE_LOADING, + IMAGE_READY, +}; + +/* Candidate image data */ +struct image_data_t { + int status; + void *data; + uint32_t size; +}; + +/* Candidate image header */ +struct image_header_t { + uint16_t magic; + uint16_t version; + uint32_t size; +}; + +/* Scatter/gather entry */ +struct opal_sg_entry { + void *data; + long length; +}; + +/* We calculate number of entries based on PAGE_SIZE */ +#define SG_ENTRIES_PER_NODE ((PAGE_SIZE - 16) / sizeof(struct opal_sg_entry)) + +/* + * This struct is very similar but not identical to that + * needed by the opal flash update. All we need to do for + * opal is rewrite num_entries into a version/length and + * translate the pointers to absolute. + */ +struct opal_sg_list { + unsigned long num_entries; + struct opal_sg_list *next; + struct opal_sg_entry entry[SG_ENTRIES_PER_NODE]; +}; + +struct validate_flash_t { + int status; /* Return status */ + void *buf; /* Candiate image buffer */ + uint32_t buf_size; /* Image size */ + uint32_t result; /* Update results token */ +}; + +struct manage_flash_t { + int status; /* Return status */ +}; + +struct update_flash_t { + int status; /* Return status */ +}; + +static struct image_header_t image_header; +static struct image_data_t image_data; +static struct validate_flash_t validate_flash_data; +static struct manage_flash_t manage_flash_data; +static struct update_flash_t update_flash_data; + +static DEFINE_MUTEX(image_data_mutex); + +/* + * Validate candidate image + */ +static inline void opal_flash_validate(void) +{ + struct validate_flash_t *args_buf = &validate_flash_data; + + args_buf->status = opal_validate_flash(__pa(args_buf->buf), + &(args_buf->buf_size), + &(args_buf->result)); +} + +/* + * Validate output format: + * validate result token + * current image version details + * new image version details + */ +static ssize_t validate_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct validate_flash_t *args_buf = &validate_flash_data; + int len; + + /* Candidate image is not validated */ + if (args_buf->status < VALIDATE_TMP_UPDATE) { + len = sprintf(buf, "%d\n", args_buf->status); + goto out; + } + + /* Result token */ + len = sprintf(buf, "%d\n", args_buf->result); + + /* Current and candidate image version details */ + if ((args_buf->result != VALIDATE_TMP_UPDATE) && + (args_buf->result < VALIDATE_CUR_UNKNOWN)) + goto out; + + if (args_buf->buf_size > (VALIDATE_BUF_SIZE - len)) { + memcpy(buf + len, args_buf->buf, VALIDATE_BUF_SIZE - len); + len = VALIDATE_BUF_SIZE; + } else { + memcpy(buf + len, args_buf->buf, args_buf->buf_size); + len += args_buf->buf_size; + } +out: + /* Set status to default */ + args_buf->status = FLASH_NO_OP; + return len; +} + +/* + * Validate candidate firmware image + * + * Note: + * We are only interested in first 4K bytes of the + * candidate image. + */ +static ssize_t validate_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct validate_flash_t *args_buf = &validate_flash_data; + + if (buf[0] != '1') + return -EINVAL; + + mutex_lock(&image_data_mutex); + + if (image_data.status != IMAGE_READY || + image_data.size < VALIDATE_BUF_SIZE) { + args_buf->result = VALIDATE_INVALID_IMG; + args_buf->status = VALIDATE_IMG_INCOMPLETE; + goto out; + } + + /* Copy first 4k bytes of candidate image */ + memcpy(args_buf->buf, image_data.data, VALIDATE_BUF_SIZE); + + args_buf->status = VALIDATE_IMG_READY; + args_buf->buf_size = VALIDATE_BUF_SIZE; + + /* Validate candidate image */ + opal_flash_validate(); + +out: + mutex_unlock(&image_data_mutex); + return count; +} + +/* + * Manage flash routine + */ +static inline void opal_flash_manage(uint8_t op) +{ + struct manage_flash_t *const args_buf = &manage_flash_data; + + args_buf->status = opal_manage_flash(op); +} + +/* + * Show manage flash status + */ +static ssize_t manage_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct manage_flash_t *const args_buf = &manage_flash_data; + int rc; + + rc = sprintf(buf, "%d\n", args_buf->status); + /* Set status to default*/ + args_buf->status = FLASH_NO_OP; + return rc; +} + +/* + * Manage operations: + * 0 - Reject + * 1 - Commit + */ +static ssize_t manage_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + uint8_t op; + switch (buf[0]) { + case '0': + op = FLASH_REJECT_TMP_SIDE; + break; + case '1': + op = FLASH_COMMIT_TMP_SIDE; + break; + default: + return -EINVAL; + } + + /* commit/reject temporary image */ + opal_flash_manage(op); + return count; +} + +/* + * Free sg list + */ +static void free_sg_list(struct opal_sg_list *list) +{ + struct opal_sg_list *sg1; + while (list) { + sg1 = list->next; + kfree(list); + list = sg1; + } + list = NULL; +} + +/* + * Build candidate image scatter gather list + * + * list format: + * ----------------------------------- + * | VER (8) | Entry length in bytes | + * ----------------------------------- + * | Pointer to next entry | + * ----------------------------------- + * | Address of memory area 1 | + * ----------------------------------- + * | Length of memory area 1 | + * ----------------------------------- + * | ......... | + * ----------------------------------- + * | ......... | + * ----------------------------------- + * | Address of memory area N | + * ----------------------------------- + * | Length of memory area N | + * ----------------------------------- + */ +static struct opal_sg_list *image_data_to_sglist(void) +{ + struct opal_sg_list *sg1, *list = NULL; + void *addr; + int size; + + addr = image_data.data; + size = image_data.size; + + sg1 = kzalloc((sizeof(struct opal_sg_list)), GFP_KERNEL); + if (!sg1) + return NULL; + + list = sg1; + sg1->num_entries = 0; + while (size > 0) { + /* Translate virtual address to physical address */ + sg1->entry[sg1->num_entries].data = + (void *)(vmalloc_to_pfn(addr) << PAGE_SHIFT); + + if (size > PAGE_SIZE) + sg1->entry[sg1->num_entries].length = PAGE_SIZE; + else + sg1->entry[sg1->num_entries].length = size; + + sg1->num_entries++; + if (sg1->num_entries >= SG_ENTRIES_PER_NODE) { + sg1->next = kzalloc((sizeof(struct opal_sg_list)), + GFP_KERNEL); + if (!sg1->next) { + pr_err("%s : Failed to allocate memory\n", + __func__); + goto nomem; + } + + sg1 = sg1->next; + sg1->num_entries = 0; + } + addr += PAGE_SIZE; + size -= PAGE_SIZE; + } + return list; +nomem: + free_sg_list(list); + return NULL; +} + +/* + * OPAL update flash + */ +static int opal_flash_update(int op) +{ + struct opal_sg_list *sg, *list, *next; + unsigned long addr; + int64_t rc = OPAL_PARAMETER; + + if (op == FLASH_UPDATE_CANCEL) { + pr_alert("FLASH: Image update cancelled\n"); + addr = '\0'; + goto flash; + } + + list = image_data_to_sglist(); + if (!list) + goto invalid_img; + + /* First entry address */ + addr = __pa(list); + + /* Translate sg list address to absolute */ + for (sg = list; sg; sg = next) { + next = sg->next; + /* Don't translate NULL pointer for last entry */ + if (sg->next) + sg->next = (struct opal_sg_list *)__pa(sg->next); + else + sg->next = NULL; + + /* Make num_entries into the version/length field */ + sg->num_entries = (SG_LIST_VERSION << 56) | + (sg->num_entries * sizeof(struct opal_sg_entry) + 16); + } + + pr_alert("FLASH: Image is %u bytes\n", image_data.size); + pr_alert("FLASH: Image update requested\n"); + pr_alert("FLASH: Image will be updated during system reboot\n"); + pr_alert("FLASH: This will take several minutes. Do not power off!\n"); + +flash: + rc = opal_update_flash(addr); + +invalid_img: + return rc; +} + +/* + * Show candidate image status + */ +static ssize_t update_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct update_flash_t *const args_buf = &update_flash_data; + return sprintf(buf, "%d\n", args_buf->status); +} + +/* + * Set update image flag + * 1 - Flash new image + * 0 - Cancel flash request + */ +static ssize_t update_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct update_flash_t *const args_buf = &update_flash_data; + int rc = count; + + mutex_lock(&image_data_mutex); + + switch (buf[0]) { + case '0': + if (args_buf->status == FLASH_IMG_READY) + opal_flash_update(FLASH_UPDATE_CANCEL); + args_buf->status = FLASH_NO_OP; + break; + case '1': + /* Image is loaded? */ + if (image_data.status == IMAGE_READY) + args_buf->status = + opal_flash_update(FLASH_UPDATE_INIT); + else + args_buf->status = FLASH_INVALID_IMG; + break; + default: + rc = -EINVAL; + } + + mutex_unlock(&image_data_mutex); + return rc; +} + +/* + * Free image buffer + */ +static void free_image_buf(void) +{ + void *addr; + int size; + + addr = image_data.data; + size = PAGE_ALIGN(image_data.size); + while (size > 0) { + ClearPageReserved(vmalloc_to_page(addr)); + addr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(image_data.data); + image_data.data = NULL; + image_data.status = IMAGE_INVALID; +} + +/* + * Allocate image buffer. + */ +static int alloc_image_buf(char *buffer, size_t count) +{ + void *addr; + int size; + + if (count < sizeof(struct image_header_t)) { + pr_warn("FLASH: Invalid candidate image\n"); + return -EINVAL; + } + + memcpy(&image_header, (void *)buffer, sizeof(struct image_header_t)); + image_data.size = be32_to_cpu(image_header.size); + pr_debug("FLASH: Candiate image size = %u\n", image_data.size); + + if (image_data.size > MAX_IMAGE_SIZE) { + pr_warn("FLASH: Too large image\n"); + return -EINVAL; + } + if (image_data.size < VALIDATE_BUF_SIZE) { + pr_warn("FLASH: Image is shorter than expected\n"); + return -EINVAL; + } + + image_data.data = vzalloc(PAGE_ALIGN(image_data.size)); + if (!image_data.data) { + pr_err("%s : Failed to allocate memory\n", __func__); + return -ENOMEM; + } + + /* Pin memory */ + addr = image_data.data; + size = PAGE_ALIGN(image_data.size); + while (size > 0) { + SetPageReserved(vmalloc_to_page(addr)); + addr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + image_data.status = IMAGE_LOADING; + return 0; +} + +/* + * Copy candidate image + * + * Parse candidate image header to get total image size + * and pre-allocate required memory. + */ +static ssize_t image_data_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buffer, loff_t pos, size_t count) +{ + int rc; + + mutex_lock(&image_data_mutex); + + /* New image ? */ + if (pos == 0) { + /* Free memory, if already allocated */ + if (image_data.data) + free_image_buf(); + + /* Cancel outstanding image update request */ + if (update_flash_data.status == FLASH_IMG_READY) + opal_flash_update(FLASH_UPDATE_CANCEL); + + /* Allocate memory */ + rc = alloc_image_buf(buffer, count); + if (rc) + goto out; + } + + if (image_data.status != IMAGE_LOADING) { + rc = -ENOMEM; + goto out; + } + + if ((pos + count) > image_data.size) { + rc = -EINVAL; + goto out; + } + + memcpy(image_data.data + pos, (void *)buffer, count); + rc = count; + + /* Set image status */ + if ((pos + count) == image_data.size) { + pr_debug("FLASH: Candidate image loaded....\n"); + image_data.status = IMAGE_READY; + } + +out: + mutex_unlock(&image_data_mutex); + return rc; +} + +/* + * sysfs interface : + * OPAL uses below sysfs files for code update. + * We create these files under /sys/firmware/opal. + * + * image : Interface to load candidate firmware image + * validate_flash : Validate firmware image + * manage_flash : Commit/Reject firmware image + * update_flash : Flash new firmware image + * + */ +static struct bin_attribute image_data_attr = { + .attr = {.name = "image", .mode = 0200}, + .size = MAX_IMAGE_SIZE, /* Limit image size */ + .write = image_data_write, +}; + +static struct kobj_attribute validate_attribute = + __ATTR(validate_flash, 0600, validate_show, validate_store); + +static struct kobj_attribute manage_attribute = + __ATTR(manage_flash, 0600, manage_show, manage_store); + +static struct kobj_attribute update_attribute = + __ATTR(update_flash, 0600, update_show, update_store); + +static struct attribute *image_op_attrs[] = { + &validate_attribute.attr, + &manage_attribute.attr, + &update_attribute.attr, + NULL /* need to NULL terminate the list of attributes */ +}; + +static struct attribute_group image_op_attr_group = { + .attrs = image_op_attrs, +}; + +void __init opal_flash_init(void) +{ + int ret; + + /* Allocate validate image buffer */ + validate_flash_data.buf = kzalloc(VALIDATE_BUF_SIZE, GFP_KERNEL); + if (!validate_flash_data.buf) { + pr_err("%s : Failed to allocate memory\n", __func__); + return; + } + + /* Make sure /sys/firmware/opal directory is created */ + if (!opal_kobj) { + pr_warn("FLASH: opal kobject is not available\n"); + goto nokobj; + } + + /* Create the sysfs files */ + ret = sysfs_create_group(opal_kobj, &image_op_attr_group); + if (ret) { + pr_warn("FLASH: Failed to create sysfs files\n"); + goto nokobj; + } + + ret = sysfs_create_bin_file(opal_kobj, &image_data_attr); + if (ret) { + pr_warn("FLASH: Failed to create sysfs files\n"); + goto nosysfs_file; + } + + /* Set default status */ + validate_flash_data.status = FLASH_NO_OP; + manage_flash_data.status = FLASH_NO_OP; + update_flash_data.status = FLASH_NO_OP; + image_data.status = IMAGE_INVALID; + return; + +nosysfs_file: + sysfs_remove_group(opal_kobj, &image_op_attr_group); + +nokobj: + kfree(validate_flash_data.buf); + return; +} diff --git a/arch/powerpc/platforms/powernv/opal-wrappers.S b/arch/powerpc/platforms/powernv/opal-wrappers.S index 403d058..e780650 100644 --- a/arch/powerpc/platforms/powernv/opal-wrappers.S +++ b/arch/powerpc/platforms/powernv/opal-wrappers.S @@ -123,3 +123,6 @@ OPAL_CALL(opal_xscom_write, OPAL_XSCOM_WRITE); OPAL_CALL(opal_lpc_read, OPAL_LPC_READ); OPAL_CALL(opal_lpc_write, OPAL_LPC_WRITE); OPAL_CALL(opal_return_cpu, OPAL_RETURN_CPU); +OPAL_CALL(opal_validate_flash, OPAL_FLASH_VALIDATE); +OPAL_CALL(opal_manage_flash, OPAL_FLASH_MANAGE); +OPAL_CALL(opal_update_flash, OPAL_FLASH_UPDATE); diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c index 37f0658..b56c243 100644 --- a/arch/powerpc/platforms/powernv/opal.c +++ b/arch/powerpc/platforms/powernv/opal.c @@ -438,6 +438,10 @@ static int __init opal_init(void) /* Create "opal" kobject under /sys/firmware */ rc = opal_sysfs_init(); + if (rc == 0) { + /* Setup code update interface */ + opal_flash_init(); + } return 0; } -- cgit v0.10.2 From d2e92709e88d97c001b6bb96054ecb06d99d0dc6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 30 Oct 2013 07:50:53 +0100 Subject: ALSA: hda - Disable AA-loopback on ALC283 Chromebook ALC283-based Chromebook suffers from occasional white noise, and it turned out that this comes from AA-loopback. Disable this output path by just clearing mixer_nid, then the generic parser will skip the creation of AA-loopback path. Reported-and-tested-by: Kailang Yang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8947035..c516fa9 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3445,6 +3445,8 @@ static void alc283_fixup_chromebook(struct hda_codec *codec, switch (action) { case HDA_FIXUP_ACT_PRE_PROBE: alc283_chromebook_caps(codec); + /* Disable AA-loopback as it causes white noise */ + spec->gen.mixer_nid = 0; spec->gen.hp_automute_hook = alc283_hp_automute_hook; /* MIC2-VREF control */ /* Set to manual mode */ -- cgit v0.10.2 From 00ecdd93a80fda1336bf5413b1d705c742a5b598 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 30 Oct 2013 08:34:59 +0100 Subject: ASoC: ab8500: Add missing of NULL check of devm_kzalloc() Spotted by coverity CID 712316. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index 7f6ca11..10be4cb 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -2570,6 +2570,8 @@ static int ab8500_codec_driver_probe(struct platform_device *pdev) /* Create driver private-data struct */ drvdata = devm_kzalloc(&pdev->dev, sizeof(struct ab8500_codec_drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; drvdata->sid_status = SID_UNCONFIGURED; drvdata->anc_status = ANC_UNCONFIGURED; dev_set_drvdata(&pdev->dev, drvdata); -- cgit v0.10.2 From 166a34d27fcad1eeb0322cff23939a1910f8a77c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 30 Oct 2013 08:35:01 +0100 Subject: ASoC: ab8500: Fix invalid cast to long pointer Don't cast to long pointers blindly just for using find_first_bit() and co. This is certainly not portable at all. Reimplement the code with ffs() and fls() instead. This is a slight optimization, too. Spotted by coverity CID 1056484 and 1056485. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index 10be4cb..3ef4815 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -2295,17 +2295,17 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai, case 0: break; case 1: - slot = find_first_bit((unsigned long *)&tx_mask, 32); + slot = ffs(tx_mask); snd_soc_update_bits(codec, AB8500_DASLOTCONF1, mask, slot); snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, slot); snd_soc_update_bits(codec, AB8500_DASLOTCONF2, mask, slot); snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, slot); break; case 2: - slot = find_first_bit((unsigned long *)&tx_mask, 32); + slot = ffs(tx_mask); snd_soc_update_bits(codec, AB8500_DASLOTCONF1, mask, slot); snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, slot); - slot = find_next_bit((unsigned long *)&tx_mask, 32, slot + 1); + slot = fls(tx_mask); snd_soc_update_bits(codec, AB8500_DASLOTCONF2, mask, slot); snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, slot); break; @@ -2336,18 +2336,18 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai, case 0: break; case 1: - slot = find_first_bit((unsigned long *)&rx_mask, 32); + slot = ffs(rx_mask); snd_soc_update_bits(codec, AB8500_ADSLOTSEL(slot), AB8500_MASK_SLOT(slot), AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT3, slot)); break; case 2: - slot = find_first_bit((unsigned long *)&rx_mask, 32); + slot = ffs(rx_mask); snd_soc_update_bits(codec, AB8500_ADSLOTSEL(slot), AB8500_MASK_SLOT(slot), AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT3, slot)); - slot = find_next_bit((unsigned long *)&rx_mask, 32, slot + 1); + slot = fls(rx_mask); snd_soc_update_bits(codec, AB8500_ADSLOTSEL(slot), AB8500_MASK_SLOT(slot), -- cgit v0.10.2 From c36c89096cb9f95fbdb0a6f3d80d4b9a50537ed3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 30 Oct 2013 08:35:03 +0100 Subject: ASoC: wm0010: Fix possible out-of-bounds array read Spotted by coverity CID 744701. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index d5ebcb0..bf7804a 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -793,11 +793,11 @@ static int wm0010_set_sysclk(struct snd_soc_codec *codec, int source, wm0010->max_spi_freq = 0; } else { for (i = 0; i < ARRAY_SIZE(pll_clock_map); i++) - if (freq >= pll_clock_map[i].max_sysclk) + if (freq >= pll_clock_map[i].max_sysclk) { + wm0010->max_spi_freq = pll_clock_map[i].max_pll_spi_speed; + wm0010->pll_clkctrl1 = pll_clock_map[i].pll_clkctrl1; break; - - wm0010->max_spi_freq = pll_clock_map[i].max_pll_spi_speed; - wm0010->pll_clkctrl1 = pll_clock_map[i].pll_clkctrl1; + } } return 0; -- cgit v0.10.2 From 8e38395360844806041ea69ab9690f5f174bc40c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 30 Oct 2013 17:41:12 +0100 Subject: ALSA: hda - Add extra chmap for 2.1 outputs on ASUS laptops ASUS N56VZ and N76VZ laptops have a bass speaker but its output comes only from the right channel. This patch adds the extra chmap specific to these models. Bugzilla: https://bugzilla.novell.com/show_bug.cgi?id=846531 Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 13ad49c..2d7c86d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4426,6 +4426,25 @@ static void alc272_fixup_mario(struct hda_codec *codec, "hda_codec: failed to override amp caps for NID 0x2\n"); } +static const struct snd_pcm_chmap_elem asus_pcm_2_1_chmaps[] = { + { .channels = 2, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, + { .channels = 4, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, + SNDRV_CHMAP_NA, SNDRV_CHMAP_LFE } }, /* LFE only on right */ + { } +}; + +/* override the 2.1 chmap */ +static void alc662_fixup_bass_chmap(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_BUILD) { + struct alc_spec *spec = codec->spec; + spec->gen.pcm_rec[0].stream[0].chmap = asus_pcm_2_1_chmaps; + } +} + enum { ALC662_FIXUP_ASPIRE, ALC662_FIXUP_IDEAPAD, @@ -4446,6 +4465,7 @@ enum { ALC662_FIXUP_INV_DMIC, ALC668_FIXUP_DELL_MIC_NO_PRESENCE, ALC668_FIXUP_HEADSET_MODE, + ALC662_FIXUP_BASS_CHMAP, }; static const struct hda_fixup alc662_fixups[] = { @@ -4620,6 +4640,12 @@ static const struct hda_fixup alc662_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_headset_mode_alc668, }, + [ALC662_FIXUP_BASS_CHMAP] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc662_fixup_bass_chmap, + .chained = true, + .chain_id = ALC662_FIXUP_ASUS_MODE4 + }, }; static const struct snd_pci_quirk alc662_fixup_tbl[] = { @@ -4633,8 +4659,8 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x05d8, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05db, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800), - SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_ASUS_MODE4), - SND_PCI_QUIRK(0x1043, 0x1bf3, "ASUS N76VZ", ALC662_FIXUP_ASUS_MODE4), + SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_BASS_CHMAP), + SND_PCI_QUIRK(0x1043, 0x1bf3, "ASUS N76VZ", ALC662_FIXUP_BASS_CHMAP), SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT), SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_FIXUP_ASUS_MODE2), SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD), -- cgit v0.10.2 From fe329a1a92cfe2d0c7e04fe3bc63761dc0f35950 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 30 Oct 2013 08:35:07 +0100 Subject: ASoC: wm8996: Fix negative array index read Spotted by coverity CID 146355. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index 46fe83d..b70379e 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -438,6 +438,8 @@ static int wm8996_get_retune_mobile_enum(struct snd_kcontrol *kcontrol, struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); int block = wm8996_get_retune_mobile_block(kcontrol->id.name); + if (block < 0) + return block; ucontrol->value.enumerated.item[0] = wm8996->retune_mobile_cfg[block]; return 0; -- cgit v0.10.2 From 5a7615cf1fcaaf1598b5689e54915d88c2344788 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 30 Oct 2013 08:35:06 +0100 Subject: ASoC: rt5640: Fix ignored error checks The negative error value returned from get_sdp_info() is ignored because it's assigned to unsigned variables. Spotted by coverity CIDs 1042657, 1042658. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 4d041d3..a3fb411 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -1604,8 +1604,8 @@ static int rt5640_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_codec *codec = rtd->codec; struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); - unsigned int val_len = 0, val_clk, mask_clk, dai_sel; - int pre_div, bclk_ms, frame_size; + unsigned int val_len = 0, val_clk, mask_clk; + int dai_sel, pre_div, bclk_ms, frame_size; rt5640->lrck[dai->id] = params_rate(params); pre_div = get_clk_info(rt5640->sysclk, rt5640->lrck[dai->id]); @@ -1675,7 +1675,8 @@ static int rt5640_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct snd_soc_codec *codec = dai->codec; struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); - unsigned int reg_val = 0, dai_sel; + unsigned int reg_val = 0; + int dai_sel; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: -- cgit v0.10.2 From afb3690c3cbd0bd82b267934b419c0643e2b938a Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 29 Oct 2013 11:49:20 +0800 Subject: gpio: bcm-kona: add missing .owner to struct gpio_chip Add missing .owner of struct gpio_chip. This prevents the module from being removed from underneath its users. Signed-off-by: Wei Yongjun Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index b3d0f81..72c927d 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -280,6 +280,7 @@ static int bcm_kona_gpio_set_debounce(struct gpio_chip *chip, unsigned gpio, static struct gpio_chip template_chip = { .label = "bcm-kona-gpio", + .owner = THIS_MODULE, .direction_input = bcm_kona_gpio_direction_input, .get = bcm_kona_gpio_get, .direction_output = bcm_kona_gpio_direction_output, -- cgit v0.10.2 From 85aa391528ad11bfdfa5e5dec1f3e91cfe8ab078 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 30 Oct 2013 11:08:55 +0800 Subject: gpio: tb10x: use module_platform_driver to simplify the code module_platform_driver() makes the code simpler by eliminating boilerplate code. Signed-off-by: Wei Yongjun Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c index 833d0f4..af4975c 100644 --- a/drivers/gpio/gpio-tb10x.c +++ b/drivers/gpio/gpio-tb10x.c @@ -324,18 +324,7 @@ static struct platform_driver tb10x_gpio_driver = { } }; -static int __init ab_gpio_init(void) -{ - return platform_driver_register(&tb10x_gpio_driver); -} - -static void __exit ab_gpio_exit(void) -{ - platform_driver_unregister(&tb10x_gpio_driver); -} - -module_init(ab_gpio_init); -module_exit(ab_gpio_exit); +module_platform_driver(tb10x_gpio_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("tb10x gpio."); MODULE_VERSION("0.0.1"); -- cgit v0.10.2 From 9a4864c897549e048b582372eca36e967b87784c Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 30 Oct 2013 11:09:15 +0800 Subject: gpio: tb10x: fix return value check in tb10x_gpio_probe() In case of error, the function devm_ioremap_resource() returns ERR_PTR() and never returns NULL. The NULL test in the return value check should be replaced with IS_ERR(). Also remove the dev_err call to avoid redundant error message. Signed-off-by: Wei Yongjun Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c index af4975c..0502b9a 100644 --- a/drivers/gpio/gpio-tb10x.c +++ b/drivers/gpio/gpio-tb10x.c @@ -207,10 +207,8 @@ static int tb10x_gpio_probe(struct platform_device *pdev) spin_lock_init(&tb10x_gpio->spinlock); tb10x_gpio->base = devm_ioremap_resource(&pdev->dev, mem); - if (!tb10x_gpio->base) { - dev_err(&pdev->dev, "Could not remap reg space.\n"); - goto fail_ioremap; - } + if (IS_ERR(tb10x_gpio->base)) + return PTR_ERR(tb10x_gpio->base); tb10x_gpio->gc.label = of_node_full_name(dn); tb10x_gpio->gc.dev = &pdev->dev; -- cgit v0.10.2 From 8b4b30365ce6cefe4193f439ac7263bb2cdd66fd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 30 Oct 2013 18:40:04 +0100 Subject: ASoC: ml26124: Fix negative array index read get_coeff() may return an error. Spotted by coverity CID 703394. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c index 2611882..185fa3b 100644 --- a/sound/soc/codecs/ml26124.c +++ b/sound/soc/codecs/ml26124.c @@ -342,6 +342,8 @@ static int ml26124_hw_params(struct snd_pcm_substream *substream, struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec); int i = get_coeff(priv->mclk, params_rate(hw_params)); + if (i < 0) + return i; priv->substream = substream; priv->rate = params_rate(hw_params); -- cgit v0.10.2 From 45d20e8348969a165c53cfb91f445e7cc599a9f0 Mon Sep 17 00:00:00 2001 From: Gerhard Sittig Date: Fri, 27 Sep 2013 17:28:38 +0200 Subject: powerpc/mpc512x: silence build warning upon disabled DIU a disabled Kconfig option results in a reference to a not implemented routine when the IS_ENABLED() macro is used for both conditional implementation of the routine as well as a C language source code test at the call site -- the "if (0) func();" construct only gets eliminated later by the optimizer, while the compiler already has emitted its warning about "func()" being undeclared provide an empty implementation for the mpc512x_setup_diu() and mpc512x_init_diu() routines in case of the disabled option, to avoid the compiler warning which is considered fatal and breaks compilation the bug appeared with commit 2abbbb63c90ab55ca3f054772c2e5ba7df810c48 "powerpc/mpc512x: move common code to shared.c file", how to reproduce: make mpc512x_defconfig echo CONFIG_FB_FSL_DIU=n >> .config && make olddefconfig make CC arch/powerpc/platforms/512x/mpc512x_shared.o .../arch/powerpc/platforms/512x/mpc512x_shared.c: In function 'mpc512x_init_early': .../arch/powerpc/platforms/512x/mpc512x_shared.c:456:3: error: implicit declaration of function 'mpc512x_init_diu' [-Werror=implicit-function-declaration] .../arch/powerpc/platforms/512x/mpc512x_shared.c: In function 'mpc512x_setup_arch': .../arch/powerpc/platforms/512x/mpc512x_shared.c:469:3: error: implicit declaration of function 'mpc512x_setup_diu' [-Werror=implicit-function-declaration] cc1: all warnings being treated as errors make[4]: *** [arch/powerpc/platforms/512x/mpc512x_shared.o] Error 1 Signed-off-by: Gerhard Sittig CC: # v3.11 Signed-off-by: Anatolij Gustschin diff --git a/arch/powerpc/platforms/512x/mpc512x_shared.c b/arch/powerpc/platforms/512x/mpc512x_shared.c index a82a41b..1a7b1d0 100644 --- a/arch/powerpc/platforms/512x/mpc512x_shared.c +++ b/arch/powerpc/platforms/512x/mpc512x_shared.c @@ -303,6 +303,9 @@ void __init mpc512x_setup_diu(void) diu_ops.release_bootmem = mpc512x_release_bootmem; } +#else +void __init mpc512x_setup_diu(void) { /* EMPTY */ } +void __init mpc512x_init_diu(void) { /* EMPTY */ } #endif void __init mpc512x_init_IRQ(void) -- cgit v0.10.2 From 509a02df44df8454b96da0b9bfcaa19f0a349df6 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 4 Oct 2013 17:37:09 +0200 Subject: Kind of revert "powerpc: 52xx: provide a default in mpc52xx_irqhost_map()" This more or less reverts commit 6391f697d4892a6f233501beea553e13f7745a23. Instead of adding an unneeded 'default', mark the variable to prevent the false positive 'uninitialized var'. The other change (fixing the printout) needs revert, too. We want to know WHICH critical irq failed, not which level it had. Signed-off-by: Wolfram Sang Cc: Sebastian Andrzej Siewior Cc: Anatolij Gustschin Acked-by: Sebastian Andrzej Siewior Signed-off-by: Anatolij Gustschin diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pic.c b/arch/powerpc/platforms/52xx/mpc52xx_pic.c index b69221b..2898b73 100644 --- a/arch/powerpc/platforms/52xx/mpc52xx_pic.c +++ b/arch/powerpc/platforms/52xx/mpc52xx_pic.c @@ -340,7 +340,7 @@ static int mpc52xx_irqhost_map(struct irq_domain *h, unsigned int virq, { int l1irq; int l2irq; - struct irq_chip *irqchip; + struct irq_chip *uninitialized_var(irqchip); void *hndlr; int type; u32 reg; @@ -373,9 +373,8 @@ static int mpc52xx_irqhost_map(struct irq_domain *h, unsigned int virq, case MPC52xx_IRQ_L1_PERP: irqchip = &mpc52xx_periph_irqchip; break; case MPC52xx_IRQ_L1_SDMA: irqchip = &mpc52xx_sdma_irqchip; break; case MPC52xx_IRQ_L1_CRIT: - default: pr_warn("%s: Critical IRQ #%d is unsupported! Nopping it.\n", - __func__, l1irq); + __func__, l2irq); irq_set_chip(virq, &no_irq_chip); return 0; } -- cgit v0.10.2 From 2bf75084f6d9f9a91ba6e30a501ff070d8a1acf6 Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Wed, 16 Oct 2013 13:11:27 +0200 Subject: powerpc/52xx: fix build breakage for MPC5200 LPBFIFO module The MPC5200 LPBFIFO driver requires the bestcomm module to be enabled, otherwise building will fail. Fix it. Cc: # 3.10+ Reported-by: Wolfgang Denk Signed-off-by: Anatolij Gustschin diff --git a/arch/powerpc/platforms/52xx/Kconfig b/arch/powerpc/platforms/52xx/Kconfig index 90f4496..af54174 100644 --- a/arch/powerpc/platforms/52xx/Kconfig +++ b/arch/powerpc/platforms/52xx/Kconfig @@ -57,5 +57,5 @@ config PPC_MPC5200_BUGFIX config PPC_MPC5200_LPBFIFO tristate "MPC5200 LocalPlus bus FIFO driver" - depends on PPC_MPC52xx + depends on PPC_MPC52xx && PPC_BESTCOMM select PPC_BESTCOMM_GEN_BD -- cgit v0.10.2 From 7e198197ec878c720af4dc35c49c0c6a99b83f9f Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 11 Oct 2013 10:37:38 -0700 Subject: powerpc/mpc512x: remove unnecessary #if Several functions are only ever referenced locally, so make them static. Of those functions, many of them are protected by an #if. However, the code which can compile fine in either case. Now that (1) the unneeded code is marked 'static' and (2) the code is only used under a C 'if (IS_ENABLED(CONFIG_FB_FSL_DIU))', the compiler can automatically remove the unneeded code, and we don't need the #if or the empty stub functions. Signed-off-by: Brian Norris Signed-off-by: Anatolij Gustschin diff --git a/arch/powerpc/platforms/512x/mpc512x_shared.c b/arch/powerpc/platforms/512x/mpc512x_shared.c index 1a7b1d0..36b5652 100644 --- a/arch/powerpc/platforms/512x/mpc512x_shared.c +++ b/arch/powerpc/platforms/512x/mpc512x_shared.c @@ -60,8 +60,6 @@ void mpc512x_restart(char *cmd) ; } -#if IS_ENABLED(CONFIG_FB_FSL_DIU) - struct fsl_diu_shared_fb { u8 gamma[0x300]; /* 32-bit aligned! */ struct diu_ad ad0; /* 32-bit aligned! */ @@ -71,7 +69,7 @@ struct fsl_diu_shared_fb { }; #define DIU_DIV_MASK 0x000000ff -void mpc512x_set_pixel_clock(unsigned int pixclock) +static void mpc512x_set_pixel_clock(unsigned int pixclock) { unsigned long bestval, bestfreq, speed, busfreq; unsigned long minpixclock, maxpixclock, pixval; @@ -164,7 +162,7 @@ void mpc512x_set_pixel_clock(unsigned int pixclock) iounmap(ccm); } -enum fsl_diu_monitor_port +static enum fsl_diu_monitor_port mpc512x_valid_monitor_port(enum fsl_diu_monitor_port port) { return FSL_DIU_PORT_DVI; @@ -179,7 +177,7 @@ static inline void mpc512x_free_bootmem(struct page *page) free_reserved_page(page); } -void mpc512x_release_bootmem(void) +static void mpc512x_release_bootmem(void) { unsigned long addr = diu_shared_fb.fb_phys & PAGE_MASK; unsigned long size = diu_shared_fb.fb_len; @@ -205,7 +203,7 @@ void mpc512x_release_bootmem(void) * address range will be reserved in setup_arch() after bootmem * allocator is up. */ -void __init mpc512x_init_diu(void) +static void __init mpc512x_init_diu(void) { struct device_node *np; struct diu __iomem *diu_reg; @@ -274,7 +272,7 @@ out: iounmap(diu_reg); } -void __init mpc512x_setup_diu(void) +static void __init mpc512x_setup_diu(void) { int ret; @@ -303,11 +301,6 @@ void __init mpc512x_setup_diu(void) diu_ops.release_bootmem = mpc512x_release_bootmem; } -#else -void __init mpc512x_setup_diu(void) { /* EMPTY */ } -void __init mpc512x_init_diu(void) { /* EMPTY */ } -#endif - void __init mpc512x_init_IRQ(void) { struct device_node *np; @@ -340,7 +333,7 @@ static struct of_device_id __initdata of_bus_ids[] = { {}, }; -void __init mpc512x_declare_of_platform_devices(void) +static void __init mpc512x_declare_of_platform_devices(void) { if (of_platform_bus_probe(NULL, of_bus_ids, NULL)) printk(KERN_ERR __FILE__ ": " @@ -390,7 +383,7 @@ static unsigned int __init get_fifo_size(struct device_node *np, ((u32)(_base) + sizeof(struct mpc52xx_psc))) /* Init PSC FIFO space for TX and RX slices */ -void __init mpc512x_psc_fifo_init(void) +static void __init mpc512x_psc_fifo_init(void) { struct device_node *np; void __iomem *psc; diff --git a/arch/powerpc/sysdev/fsl_soc.h b/arch/powerpc/sysdev/fsl_soc.h index c6d0073..4c5a19e 100644 --- a/arch/powerpc/sysdev/fsl_soc.h +++ b/arch/powerpc/sysdev/fsl_soc.h @@ -21,8 +21,6 @@ struct device_node; extern void fsl_rstcr_restart(char *cmd); -#if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE) - /* The different ports that the DIU can be connected to */ enum fsl_diu_monitor_port { FSL_DIU_PORT_DVI, /* DVI */ @@ -43,7 +41,6 @@ struct platform_diu_data_ops { }; extern struct platform_diu_data_ops diu_ops; -#endif void fsl_hv_restart(char *cmd); void fsl_hv_halt(void); -- cgit v0.10.2 From d68ce0177c1e51fb332369e0c99852a6d05668af Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Fri, 11 Oct 2013 16:07:31 -0700 Subject: x86, hyperv: Fix build error due to missing include 9e7827b5ea4c ("x86, hyperv: Get the local APIC timer frequency from the hypervisor") breaks the build with some configs because apic.h isn't directly included: arch/x86/kernel/cpu/mshyperv.c: In function 'ms_hyperv_init_platform': arch/x86/kernel/cpu/mshyperv.c:90:3: error: 'lapic_timer_frequency' undeclared (first use in this function) arch/x86/kernel/cpu/mshyperv.c:90:3: note: each undeclared identifier is reported only once for each function it appears in Fix it by including asm/apic.h. Signed-off-by: David Rientjes Link: http://lkml.kernel.org/r/alpine.DEB.2.02.1310111604160.31170@chino.kir.corp.google.com Acked-by: K. Y. Srinivasan Signed-off-by: H. Peter Anvin diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c index 628ff50..9f6e9f8 100644 --- a/arch/x86/kernel/cpu/mshyperv.c +++ b/arch/x86/kernel/cpu/mshyperv.c @@ -25,6 +25,7 @@ #include #include #include +#include struct ms_hyperv_info ms_hyperv; EXPORT_SYMBOL_GPL(ms_hyperv); -- cgit v0.10.2 From 65f36f4149ce4490b2174ef45e1dbe494f4b8268 Mon Sep 17 00:00:00 2001 From: Cedric Le Goater Date: Wed, 30 Oct 2013 14:47:06 +0100 Subject: powerpc/nvram: Scan partitions only once nvram_scan_partitions() is called twice when initializing the "lnx,oops-log" partition and the "ibm,rtas-log" partition. This fills the partition list with duplicate entries. This patch moves the partition scan in the init routine pseries_nvram_init_log_partitions() which is called only once. Signed-off-by: Cedric Le Goater Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c index d276cd3..2498d4d 100644 --- a/arch/powerpc/platforms/pseries/nvram.c +++ b/arch/powerpc/platforms/pseries/nvram.c @@ -429,9 +429,6 @@ static int __init pseries_nvram_init_os_partition(struct nvram_os_partition loff_t p; int size; - /* Scan nvram for partitions */ - nvram_scan_partitions(); - /* Look for ours */ p = nvram_find_partition(part->name, NVRAM_SIG_OS, &size); @@ -795,6 +792,9 @@ static int __init pseries_nvram_init_log_partitions(void) { int rc; + /* Scan nvram for partitions */ + nvram_scan_partitions(); + rc = pseries_nvram_init_os_partition(&rtas_log_partition); nvram_init_oops_partition(rc == 0); return 0; -- cgit v0.10.2 From 563c5d8af5d56739bc31f9f4b1c8a0994ed5742c Mon Sep 17 00:00:00 2001 From: Cedric Le Goater Date: Wed, 30 Oct 2013 14:47:07 +0100 Subject: powerpc/nvram: Fix endian issue when reading the NVRAM size Signed-off-by: Cedric Le Goater Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/chrp/nvram.c b/arch/powerpc/platforms/chrp/nvram.c index d3ceff0..9ef8cc3 100644 --- a/arch/powerpc/platforms/chrp/nvram.c +++ b/arch/powerpc/platforms/chrp/nvram.c @@ -66,7 +66,7 @@ static void chrp_nvram_write(int addr, unsigned char val) void __init chrp_nvram_init(void) { struct device_node *nvram; - const unsigned int *nbytes_p; + const __be32 *nbytes_p; unsigned int proplen; nvram = of_find_node_by_type(NULL, "nvram"); @@ -79,7 +79,7 @@ void __init chrp_nvram_init(void) return; } - nvram_size = *nbytes_p; + nvram_size = be32_to_cpup(nbytes_p); printk(KERN_INFO "CHRP nvram contains %u bytes\n", nvram_size); of_node_put(nvram); diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c index 2498d4d..057fc89 100644 --- a/arch/powerpc/platforms/pseries/nvram.c +++ b/arch/powerpc/platforms/pseries/nvram.c @@ -804,7 +804,7 @@ machine_arch_initcall(pseries, pseries_nvram_init_log_partitions); int __init pSeries_nvram_init(void) { struct device_node *nvram; - const unsigned int *nbytes_p; + const __be32 *nbytes_p; unsigned int proplen; nvram = of_find_node_by_type(NULL, "nvram"); @@ -817,7 +817,7 @@ int __init pSeries_nvram_init(void) return -EIO; } - nvram_size = *nbytes_p; + nvram_size = be32_to_cpup(nbytes_p); nvram_fetch = rtas_token("nvram-fetch"); nvram_store = rtas_token("nvram-store"); -- cgit v0.10.2 From c81095a465b2c1cd819fb14ee3cd07bc1b377af1 Mon Sep 17 00:00:00 2001 From: Cedric Le Goater Date: Wed, 30 Oct 2013 14:47:08 +0100 Subject: powerpc/nvram: Fix endian issue when using the partition length When reading partitions, the length has to be translated from big endian to the endian order of the host. Similarly, when writing partitions, the length needs to be in big endian order. The userspace tool 'nvram' needs a similar fix as it is reading and writing partitions through /dev/nram : http://sourceforge.net/p/powerpc-utils/mailman/message/31571277/ Signed-off-by: Cedric Le Goater Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/nvram_64.c b/arch/powerpc/kernel/nvram_64.c index 8213ee1..fd82c28 100644 --- a/arch/powerpc/kernel/nvram_64.c +++ b/arch/powerpc/kernel/nvram_64.c @@ -223,9 +223,13 @@ static int __init nvram_write_header(struct nvram_partition * part) { loff_t tmp_index; int rc; - + struct nvram_header phead; + + memcpy(&phead, &part->header, NVRAM_HEADER_LEN); + phead.length = cpu_to_be16(phead.length); + tmp_index = part->index; - rc = ppc_md.nvram_write((char *)&part->header, NVRAM_HEADER_LEN, &tmp_index); + rc = ppc_md.nvram_write((char *)&phead, NVRAM_HEADER_LEN, &tmp_index); return rc; } @@ -505,6 +509,8 @@ int __init nvram_scan_partitions(void) memcpy(&phead, header, NVRAM_HEADER_LEN); + phead.length = be16_to_cpu(phead.length); + err = 0; c_sum = nvram_checksum(&phead); if (c_sum != phead.checksum) { -- cgit v0.10.2 From de00b30d054857990dc573648e4767ec512009c7 Mon Sep 17 00:00:00 2001 From: Christian Kujau Date: Tue, 29 Oct 2013 21:25:43 -0700 Subject: powerpc/pmu: Fix ADB_PMU_LED_IDE dependencies for quite some time the following is printed (twice) after doing "make oldconfig": [...] scripts/kconfig/conf --oldconfig Kconfig warning: (ADB_PMU_LED_IDE) selects LEDS_TRIGGER_IDE_DISK which has unmet direct dependencies (NEW_LEDS && IDE_GD_ATA && LEDS_TRIGGERS) warning: (ADB_PMU_LED_IDE) selects LEDS_TRIGGER_IDE_DISK which has unmet direct dependencies (NEW_LEDS && IDE_GD_ATA && LEDS_TRIGGERS) The following patch causes ADB_PMU_LED to depend on IDE_GD_ATA, so that the options above are only available when IDE_GD_ATA is actually selected and thus eliminates the warning. Signed-off-by: Christian Kujau Signed-off-by: Benjamin Herrenschmidt diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig index 696238b..d26a312 100644 --- a/drivers/macintosh/Kconfig +++ b/drivers/macintosh/Kconfig @@ -103,6 +103,7 @@ config ADB_PMU_LED_IDE bool "Use front LED as IDE LED by default" depends on ADB_PMU_LED depends on LEDS_CLASS + depends on IDE_GD_ATA select LEDS_TRIGGERS select LEDS_TRIGGER_IDE_DISK help -- cgit v0.10.2 From 711b5138d54fcfd5e312cea895d6e706a46eff19 Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Tue, 29 Oct 2013 22:25:14 -0400 Subject: powerpc: Only save/restore SDR1 if in hypervisor mode Currently, when not in hypervisor mode the kernel Oopses during suspend or hibernation when accessing the SDR1 register, because it is only available in hypervisor mode. Access to it needs to be protected in BEGIN/END_FW_FTR_SECTION. Signed-off-by: Dan Streetman Cc: Benjamin Herrenschmidt Reported-by: Jimmy Pan Tested-by: Jimmy Pan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/swsusp_asm64.S b/arch/powerpc/kernel/swsusp_asm64.S index 2204598..988f38d 100644 --- a/arch/powerpc/kernel/swsusp_asm64.S +++ b/arch/powerpc/kernel/swsusp_asm64.S @@ -114,7 +114,9 @@ _GLOBAL(swsusp_arch_suspend) SAVE_SPECIAL(MSR) SAVE_SPECIAL(XER) #ifdef CONFIG_PPC_BOOK3S_64 +BEGIN_FW_FTR_SECTION SAVE_SPECIAL(SDR1) +END_FW_FTR_SECTION_IFCLR(FW_FEATURE_LPAR) #else SAVE_SPR(TCR) @@ -231,7 +233,9 @@ nothing_to_copy: /* can't use RESTORE_SPECIAL(MSR) */ ld r0, SL_MSR(r11) mtmsrd r0, 0 +BEGIN_FW_FTR_SECTION RESTORE_SPECIAL(SDR1) +END_FW_FTR_SECTION_IFCLR(FW_FEATURE_LPAR) #else /* Restore SPRG1, be used to save paca */ ld r0, SL_SPRG1(r11) -- cgit v0.10.2 From 9c662cad2fb66ff3a44b1d4f545bf496bf67ab10 Mon Sep 17 00:00:00 2001 From: Philippe Bergheaud Date: Tue, 24 Sep 2013 14:13:35 +0200 Subject: powerpc/bpf: BPF JIT compiler for 64-bit Little Endian This enables the Berkeley Packet Filter JIT compiler for the PowerPC running in 64bit Little Endian. Signed-off-by: Philippe Bergheaud Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h index 442edee..99f8790 100644 --- a/arch/powerpc/include/asm/ppc-opcode.h +++ b/arch/powerpc/include/asm/ppc-opcode.h @@ -203,6 +203,7 @@ /* Misc instructions for BPF compiler */ #define PPC_INST_LD 0xe8000000 #define PPC_INST_LHZ 0xa0000000 +#define PPC_INST_LHBRX 0x7c00062c #define PPC_INST_LWZ 0x80000000 #define PPC_INST_STD 0xf8000000 #define PPC_INST_STDU 0xf8000001 diff --git a/arch/powerpc/net/bpf_jit.h b/arch/powerpc/net/bpf_jit.h index 8a5dfaf..0baf2b8 100644 --- a/arch/powerpc/net/bpf_jit.h +++ b/arch/powerpc/net/bpf_jit.h @@ -92,6 +92,8 @@ DECLARE_LOAD_FUNC(sk_load_byte_msh); ___PPC_RA(base) | IMM_L(i)) #define PPC_LHZ(r, base, i) EMIT(PPC_INST_LHZ | ___PPC_RT(r) | \ ___PPC_RA(base) | IMM_L(i)) +#define PPC_LHBRX(r, base, b) EMIT(PPC_INST_LHBRX | ___PPC_RT(r) | \ + ___PPC_RA(base) | ___PPC_RB(b)) /* Convenience helpers for the above with 'far' offsets: */ #define PPC_LD_OFFS(r, base, i) do { if ((i) < 32768) PPC_LD(r, base, i); \ else { PPC_ADDIS(r, base, IMM_HA(i)); \ @@ -186,6 +188,14 @@ DECLARE_LOAD_FUNC(sk_load_byte_msh); PPC_ORI(d, d, (uintptr_t)(i) & 0xffff); \ } } while (0); +#define PPC_LHBRX_OFFS(r, base, i) \ + do { PPC_LI32(r, i); PPC_LHBRX(r, r, base); } while(0) +#ifdef __LITTLE_ENDIAN__ +#define PPC_NTOHS_OFFS(r, base, i) PPC_LHBRX_OFFS(r, base, i) +#else +#define PPC_NTOHS_OFFS(r, base, i) PPC_LHZ_OFFS(r, base, i) +#endif + static inline bool is_nearbranch(int offset) { return (offset < 32768) && (offset >= -32768); diff --git a/arch/powerpc/net/bpf_jit_64.S b/arch/powerpc/net/bpf_jit_64.S index 7d3a3b5..e76eba7 100644 --- a/arch/powerpc/net/bpf_jit_64.S +++ b/arch/powerpc/net/bpf_jit_64.S @@ -43,8 +43,11 @@ sk_load_word_positive_offset: cmpd r_scratch1, r_addr blt bpf_slow_path_word /* Nope, just hitting the header. cr0 here is eq or gt! */ +#ifdef __LITTLE_ENDIAN__ + lwbrx r_A, r_D, r_addr +#else lwzx r_A, r_D, r_addr - /* When big endian we don't need to byteswap. */ +#endif blr /* Return success, cr0 != LT */ .globl sk_load_half @@ -56,7 +59,11 @@ sk_load_half_positive_offset: subi r_scratch1, r_HL, 2 cmpd r_scratch1, r_addr blt bpf_slow_path_half +#ifdef __LITTLE_ENDIAN__ + lhbrx r_A, r_D, r_addr +#else lhzx r_A, r_D, r_addr +#endif blr .globl sk_load_byte diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c index bf56e33..81cd6c7 100644 --- a/arch/powerpc/net/bpf_jit_comp.c +++ b/arch/powerpc/net/bpf_jit_comp.c @@ -17,14 +17,8 @@ #include "bpf_jit.h" -#ifndef __BIG_ENDIAN -/* There are endianness assumptions herein. */ -#error "Little-endian PPC not supported in BPF compiler" -#endif - int bpf_jit_enable __read_mostly; - static inline void bpf_flush_icache(void *start, void *end) { smp_wmb(); @@ -346,18 +340,11 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image, break; /*** Ancillary info loads ***/ - - /* None of the BPF_S_ANC* codes appear to be passed by - * sk_chk_filter(). The interpreter and the x86 BPF - * compiler implement them so we do too -- they may be - * planted in future. - */ case BPF_S_ANC_PROTOCOL: /* A = ntohs(skb->protocol); */ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, protocol) != 2); - PPC_LHZ_OFFS(r_A, r_skb, offsetof(struct sk_buff, - protocol)); - /* ntohs is a NOP with BE loads. */ + PPC_NTOHS_OFFS(r_A, r_skb, offsetof(struct sk_buff, + protocol)); break; case BPF_S_ANC_IFINDEX: PPC_LD_OFFS(r_scratch1, r_skb, offsetof(struct sk_buff, -- cgit v0.10.2 From d0cebfa650a084f041131207d81f9b311babd5ef Mon Sep 17 00:00:00 2001 From: Philippe Bergheaud Date: Thu, 26 Sep 2013 08:30:09 +0200 Subject: powerpc: word-at-a-time optimization for 64-bit Little Endian This is an optimization for the PowerPC in 64-bit little-endian. Bit counting is used in find_zero(), instead of the multiply and shift. It is modelled after Alan Modra's PowerPC LE strlen patch http://sourceware.org/ml/libc-alpha/2013-08/msg00097.html. Signed-off-by: Philippe Bergheaud Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/word-at-a-time.h b/arch/powerpc/include/asm/word-at-a-time.h index 213a5f2..9a5c928 100644 --- a/arch/powerpc/include/asm/word-at-a-time.h +++ b/arch/powerpc/include/asm/word-at-a-time.h @@ -42,13 +42,6 @@ static inline bool has_zero(unsigned long val, unsigned long *data, const struct #else -/* - * This is largely generic for little-endian machines, but the - * optimal byte mask counting is probably going to be something - * that is architecture-specific. If you have a reliably fast - * bit count instruction, that might be better than the multiply - * and shift, for example. - */ struct word_at_a_time { const unsigned long one_bits, high_bits; }; @@ -57,19 +50,32 @@ struct word_at_a_time { #ifdef CONFIG_64BIT -/* - * Jan Achrenius on G+: microoptimized version of - * the simpler "(mask & ONEBYTES) * ONEBYTES >> 56" - * that works for the bytemasks without having to - * mask them first. - */ -static inline long count_masked_bytes(unsigned long mask) +/* Alan Modra's little-endian strlen tail for 64-bit */ +#define create_zero_mask(mask) (mask) + +static inline unsigned long find_zero(unsigned long mask) { - return mask*0x0001020304050608ul >> 56; + unsigned long leading_zero_bits; + long trailing_zero_bit_mask; + + asm ("addi %1,%2,-1\n\t" + "andc %1,%1,%2\n\t" + "popcntd %0,%1" + : "=r" (leading_zero_bits), "=&r" (trailing_zero_bit_mask) + : "r" (mask)); + return leading_zero_bits >> 3; } #else /* 32-bit case */ +/* + * This is largely generic for little-endian machines, but the + * optimal byte mask counting is probably going to be something + * that is architecture-specific. If you have a reliably fast + * bit count instruction, that might be better than the multiply + * and shift, for example. + */ + /* Carl Chatfield / Jan Achrenius G+ version for 32-bit */ static inline long count_masked_bytes(long mask) { @@ -79,6 +85,17 @@ static inline long count_masked_bytes(long mask) return a & mask; } +static inline unsigned long create_zero_mask(unsigned long bits) +{ + bits = (bits - 1) & ~bits; + return bits >> 7; +} + +static inline unsigned long find_zero(unsigned long mask) +{ + return count_masked_bytes(mask); +} + #endif /* Return nonzero if it has a zero */ @@ -94,19 +111,9 @@ static inline unsigned long prep_zero_mask(unsigned long a, unsigned long bits, return bits; } -static inline unsigned long create_zero_mask(unsigned long bits) -{ - bits = (bits - 1) & ~bits; - return bits >> 7; -} - /* The mask we created is directly usable as a bytemask */ #define zero_bytemask(mask) (mask) -static inline unsigned long find_zero(unsigned long mask) -{ - return count_masked_bytes(mask); -} #endif #endif /* _ASM_WORD_AT_A_TIME_H */ -- cgit v0.10.2 From 7ba5fef7d9e1880635cbb2fd698e8a24dc366d0f Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Wed, 2 Oct 2013 17:15:14 +1000 Subject: powerpc/tm: Remove interrupt disable in __switch_to() We currently turn IRQs off in __switch_to(0 but this is unnecessary as it's already disabled in the caller. This removes the IRQ disable but adds a check to make sure it is really off in case this changes in future. Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 4d42c4d..75c2d10 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -597,12 +597,13 @@ struct task_struct *__switch_to(struct task_struct *prev, struct task_struct *new) { struct thread_struct *new_thread, *old_thread; - unsigned long flags; struct task_struct *last; #ifdef CONFIG_PPC_BOOK3S_64 struct ppc64_tlb_batch *batch; #endif + WARN_ON(!irqs_disabled()); + /* Back up the TAR across context switches. * Note that the TAR is not available for use in the kernel. (To * provide this, the TAR should be backed up/restored on exception @@ -722,8 +723,6 @@ struct task_struct *__switch_to(struct task_struct *prev, } #endif /* CONFIG_PPC_BOOK3S_64 */ - local_irq_save(flags); - /* * We can't take a PMU exception inside _switch() since there is a * window where the kernel stack SLB and the kernel stack are out @@ -743,8 +742,6 @@ struct task_struct *__switch_to(struct task_struct *prev, } #endif /* CONFIG_PPC_BOOK3S_64 */ - local_irq_restore(flags); - return last; } -- cgit v0.10.2 From afaf53985431bb295e4b723b08a55d0540cf8070 Mon Sep 17 00:00:00 2001 From: Sudeep KarkadaNagesha Date: Wed, 18 Sep 2013 11:53:04 +0100 Subject: powerpc: Remove big endianness assumption in of_find_next_cache_node Currently big endianness of the device tree data is assumed in of_find_next_cache_node for 'handle' when calling of_find_node_by_phandle. In preparation to move this function to common code, this patch fixes the endianness using 'be32_to_cpup' Cc: Benjamin Herrenschmidt Cc: Grant Likely Cc: Rob Herring Signed-off-by: Sudeep KarkadaNagesha Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index b7634ce..09be275 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -778,7 +778,7 @@ struct device_node *of_find_next_cache_node(struct device_node *np) handle = of_get_property(np, "next-level-cache", NULL); if (handle) - return of_find_node_by_phandle(*handle); + return of_find_node_by_phandle(be32_to_cpup(handle)); /* OF on pmac has nodes instead of properties named "l2-cache" * beneath CPU nodes. -- cgit v0.10.2 From a3e31b4588443f37d82195096c6b30dff1c152c2 Mon Sep 17 00:00:00 2001 From: Sudeep KarkadaNagesha Date: Wed, 18 Sep 2013 11:53:05 +0100 Subject: of: Move definition of of_find_next_cache_node into common code. Since the definition of_find_next_cache_node is architecture independent, the existing definition in powerpc can be moved to driver/of/base.c Cc: Benjamin Herrenschmidt Cc: Grant Likely Cc: Rob Herring Signed-off-by: Sudeep KarkadaNagesha Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/prom.h b/arch/powerpc/include/asm/prom.h index 7d0c7f3..bf09e5a 100644 --- a/arch/powerpc/include/asm/prom.h +++ b/arch/powerpc/include/asm/prom.h @@ -44,9 +44,6 @@ void of_parse_dma_window(struct device_node *dn, const __be32 *dma_window, extern void kdump_move_device_tree(void); -/* cache lookup */ -struct device_node *of_find_next_cache_node(struct device_node *np); - #ifdef CONFIG_NUMA extern int of_node_to_nid(struct device_node *device); #else diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index 09be275..4432fd8 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -761,37 +761,6 @@ void __init early_init_devtree(void *params) *******/ /** - * of_find_next_cache_node - Find a node's subsidiary cache - * @np: node of type "cpu" or "cache" - * - * Returns a node pointer with refcount incremented, use - * of_node_put() on it when done. Caller should hold a reference - * to np. - */ -struct device_node *of_find_next_cache_node(struct device_node *np) -{ - struct device_node *child; - const phandle *handle; - - handle = of_get_property(np, "l2-cache", NULL); - if (!handle) - handle = of_get_property(np, "next-level-cache", NULL); - - if (handle) - return of_find_node_by_phandle(be32_to_cpup(handle)); - - /* OF on pmac has nodes instead of properties named "l2-cache" - * beneath CPU nodes. - */ - if (!strcmp(np->type, "cpu")) - for_each_child_of_node(np, child) - if (!strcmp(child->type, "cache")) - return child; - - return NULL; -} - -/** * of_get_ibm_chip_id - Returns the IBM "chip-id" of a device * @np: device node of the device * diff --git a/drivers/of/base.c b/drivers/of/base.c index 865d3f6..b2cee3d 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1884,3 +1884,34 @@ int of_device_is_stdout_path(struct device_node *dn) return of_stdout == dn; } EXPORT_SYMBOL_GPL(of_device_is_stdout_path); + +/** + * of_find_next_cache_node - Find a node's subsidiary cache + * @np: node of type "cpu" or "cache" + * + * Returns a node pointer with refcount incremented, use + * of_node_put() on it when done. Caller should hold a reference + * to np. + */ +struct device_node *of_find_next_cache_node(const struct device_node *np) +{ + struct device_node *child; + const phandle *handle; + + handle = of_get_property(np, "l2-cache", NULL); + if (!handle) + handle = of_get_property(np, "next-level-cache", NULL); + + if (handle) + return of_find_node_by_phandle(be32_to_cpup(handle)); + + /* OF on pmac has nodes instead of properties named "l2-cache" + * beneath CPU nodes. + */ + if (!strcmp(np->type, "cpu")) + for_each_child_of_node(np, child) + if (!strcmp(child->type, "cache")) + return child; + + return NULL; +} diff --git a/include/linux/of.h b/include/linux/of.h index f95aee3..c08c07e 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -226,6 +226,8 @@ static inline int of_get_child_count(const struct device_node *np) return num; } +/* cache lookup */ +extern struct device_node *of_find_next_cache_node(const struct device_node *); extern struct device_node *of_find_node_with_property( struct device_node *from, const char *prop_name); #define for_each_node_with_property(dn, prop_name) \ -- cgit v0.10.2 From a40a2b670706494610d794927b9aebe77e18af8d Mon Sep 17 00:00:00 2001 From: Vladimir Murzin Date: Sat, 28 Sep 2013 10:22:00 +0200 Subject: powerpc/bpf: Fix DIVWU instruction opcode Currently DIVWU stands for *signed* divw opcode: 7d 2a 4b 96 divwu r9,r10,r9 7d 2a 4b d6 divw r9,r10,r9 Use the *unsigned* divw opcode for DIVWU. Suggested-by: Vassili Karpov Reviewed-by: Vassili Karpov Signed-off-by: Vladimir Murzin Acked-by: Matt Evans Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h index 99f8790..3132bb9 100644 --- a/arch/powerpc/include/asm/ppc-opcode.h +++ b/arch/powerpc/include/asm/ppc-opcode.h @@ -222,7 +222,7 @@ #define PPC_INST_MULLW 0x7c0001d6 #define PPC_INST_MULHWU 0x7c000016 #define PPC_INST_MULLI 0x1c000000 -#define PPC_INST_DIVWU 0x7c0003d6 +#define PPC_INST_DIVWU 0x7c000396 #define PPC_INST_RLWINM 0x54000000 #define PPC_INST_RLDICR 0x78000004 #define PPC_INST_SLW 0x7c000030 -- cgit v0.10.2 From b0c06d333505c5503d0de5dee1ddf8478dcf5251 Mon Sep 17 00:00:00 2001 From: Vladimir Murzin Date: Sat, 28 Sep 2013 10:22:01 +0200 Subject: powerpc/bpf: Support MOD operation commit b6069a9570 (filter: add MOD operation) added generic support for modulus operation in BPF. This patch brings JIT support for PPC64 Signed-off-by: Vladimir Murzin Acked-by: Matt Evans Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/net/bpf_jit.h b/arch/powerpc/net/bpf_jit.h index 0baf2b8..9aee27c 100644 --- a/arch/powerpc/net/bpf_jit.h +++ b/arch/powerpc/net/bpf_jit.h @@ -39,6 +39,7 @@ #define r_X 5 #define r_addr 6 #define r_scratch1 7 +#define r_scratch2 8 #define r_D 14 #define r_HL 15 #define r_M 16 diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c index 81cd6c7..475d4f2 100644 --- a/arch/powerpc/net/bpf_jit_comp.c +++ b/arch/powerpc/net/bpf_jit_comp.c @@ -187,6 +187,26 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image, PPC_MUL(r_A, r_A, r_scratch1); } break; + case BPF_S_ALU_MOD_X: /* A %= X; */ + ctx->seen |= SEEN_XREG; + PPC_CMPWI(r_X, 0); + if (ctx->pc_ret0 != -1) { + PPC_BCC(COND_EQ, addrs[ctx->pc_ret0]); + } else { + PPC_BCC_SHORT(COND_NE, (ctx->idx*4)+12); + PPC_LI(r_ret, 0); + PPC_JMP(exit_addr); + } + PPC_DIVWU(r_scratch1, r_A, r_X); + PPC_MUL(r_scratch1, r_X, r_scratch1); + PPC_SUB(r_A, r_A, r_scratch1); + break; + case BPF_S_ALU_MOD_K: /* A %= K; */ + PPC_LI32(r_scratch2, K); + PPC_DIVWU(r_scratch1, r_A, r_scratch2); + PPC_MUL(r_scratch1, r_scratch2, r_scratch1); + PPC_SUB(r_A, r_A, r_scratch1); + break; case BPF_S_ALU_DIV_X: /* A /= X; */ ctx->seen |= SEEN_XREG; PPC_CMPWI(r_X, 0); -- cgit v0.10.2 From 9ade09d6c62e48fba6c74ce3958ca1035dfd8427 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 29 Oct 2013 00:52:19 -0700 Subject: ASoC: rcar: remove original filter from rsnd_dma_init() Remove original filter from rsnd_dma_init(), and use SH-DMA suitable filter. This new style can be used from Device Tree. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index b234ed6..78c35b4 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -94,6 +94,7 @@ * */ #include +#include #include "rsnd.h" #define RSND_RATES SNDRV_PCM_RATE_8000_96000 @@ -209,13 +210,6 @@ int rsnd_dma_available(struct rsnd_dma *dma) return !!dma->chan; } -static bool rsnd_dma_filter(struct dma_chan *chan, void *param) -{ - chan->private = param; - - return true; -} - int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int is_play, int id, int (*inquiry)(struct rsnd_dma *dma, @@ -223,7 +217,9 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int (*complete)(struct rsnd_dma *dma)) { struct device *dev = rsnd_priv_to_dev(priv); + struct dma_slave_config cfg; dma_cap_mask_t mask; + int ret; if (dma->chan) { dev_err(dev, "it already has dma channel\n"); @@ -233,15 +229,23 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - dma->slave.shdma_slave.slave_id = id; - - dma->chan = dma_request_channel(mask, rsnd_dma_filter, - &dma->slave.shdma_slave); + dma->chan = dma_request_slave_channel_compat(mask, shdma_chan_filter, + (void *)id, dev, + is_play ? "tx" : "rx"); if (!dma->chan) { dev_err(dev, "can't get dma channel\n"); return -EIO; } + cfg.slave_id = id; + cfg.dst_addr = 0; /* use default addr when playback */ + cfg.src_addr = 0; /* use default addr when capture */ + cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; + + ret = dmaengine_slave_config(dma->chan, &cfg); + if (ret < 0) + goto rsnd_dma_init_err; + dma->dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE; dma->priv = priv; dma->inquiry = inquiry; @@ -249,6 +253,11 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, INIT_WORK(&dma->work, rsnd_dma_do_work); return 0; + +rsnd_dma_init_err: + rsnd_dma_quit(priv, dma); + + return ret; } void rsnd_dma_quit(struct rsnd_priv *priv, -- cgit v0.10.2 From a19685cb72bb6a80ac453e76b3ab3bb7770e1742 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 28 Oct 2013 14:21:46 +0100 Subject: ASoC: Use strlcpy() for copying in snd_soc_info_enum_double() The provided texts aren't guaranteed to be in the fixed size. Spotted by coverity CID 139318. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 0860a7f..b38e0ee 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2552,8 +2552,9 @@ int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol, if (uinfo->value.enumerated.item > e->max - 1) uinfo->value.enumerated.item = e->max - 1; - strcpy(uinfo->value.enumerated.name, - e->texts[uinfo->value.enumerated.item]); + strlcpy(uinfo->value.enumerated.name, + e->texts[uinfo->value.enumerated.item], + sizeof(uinfo->value.enumerated.name)); return 0; } EXPORT_SYMBOL_GPL(snd_soc_info_enum_double); -- cgit v0.10.2 From c5914b0aaea6494aaa9e415cbd32f8b7eb604af0 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 30 Oct 2013 17:47:39 -0700 Subject: ASoC: pcm: Check for ops before deferencing them Ensure that we always check that an ops structure is present before we try to use it, improving the robustness of the system. Reported-by: Russell King Signed-off-by: Mark Brown diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index d449872..d81b792 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -190,7 +190,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); /* startup the audio subsystem */ - if (cpu_dai->driver->ops->startup) { + if (cpu_dai->driver->ops && cpu_dai->driver->ops->startup) { ret = cpu_dai->driver->ops->startup(substream, cpu_dai); if (ret < 0) { dev_err(cpu_dai->dev, "ASoC: can't open interface" @@ -208,7 +208,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) } } - if (codec_dai->driver->ops->startup) { + if (codec_dai->driver->ops && codec_dai->driver->ops->startup) { ret = codec_dai->driver->ops->startup(substream, codec_dai); if (ret < 0) { dev_err(codec_dai->dev, "ASoC: can't open codec" @@ -463,7 +463,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) } } - if (codec_dai->driver->ops->prepare) { + if (codec_dai->driver->ops && codec_dai->driver->ops->prepare) { ret = codec_dai->driver->ops->prepare(substream, codec_dai); if (ret < 0) { dev_err(codec_dai->dev, "ASoC: DAI prepare error: %d\n", @@ -472,7 +472,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) } } - if (cpu_dai->driver->ops->prepare) { + if (cpu_dai->driver->ops && cpu_dai->driver->ops->prepare) { ret = cpu_dai->driver->ops->prepare(substream, cpu_dai); if (ret < 0) { dev_err(cpu_dai->dev, "ASoC: DAI prepare error: %d\n", @@ -523,7 +523,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, } } - if (codec_dai->driver->ops->hw_params) { + if (codec_dai->driver->ops && codec_dai->driver->ops->hw_params) { ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai); if (ret < 0) { dev_err(codec_dai->dev, "ASoC: can't set %s hw params:" @@ -532,7 +532,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, } } - if (cpu_dai->driver->ops->hw_params) { + if (cpu_dai->driver->ops && cpu_dai->driver->ops->hw_params) { ret = cpu_dai->driver->ops->hw_params(substream, params, cpu_dai); if (ret < 0) { dev_err(cpu_dai->dev, "ASoC: %s hw params failed: %d\n", @@ -559,11 +559,11 @@ out: return ret; platform_err: - if (cpu_dai->driver->ops->hw_free) + if (cpu_dai->driver->ops && cpu_dai->driver->ops->hw_free) cpu_dai->driver->ops->hw_free(substream, cpu_dai); interface_err: - if (codec_dai->driver->ops->hw_free) + if (codec_dai->driver->ops && codec_dai->driver->ops->hw_free) codec_dai->driver->ops->hw_free(substream, codec_dai); codec_err: @@ -600,10 +600,10 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) platform->driver->ops->hw_free(substream); /* now free hw params for the DAIs */ - if (codec_dai->driver->ops->hw_free) + if (codec_dai->driver->ops && codec_dai->driver->ops->hw_free) codec_dai->driver->ops->hw_free(substream, codec_dai); - if (cpu_dai->driver->ops->hw_free) + if (cpu_dai->driver->ops && cpu_dai->driver->ops->hw_free) cpu_dai->driver->ops->hw_free(substream, cpu_dai); mutex_unlock(&rtd->pcm_mutex); @@ -618,7 +618,7 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) struct snd_soc_dai *codec_dai = rtd->codec_dai; int ret; - if (codec_dai->driver->ops->trigger) { + if (codec_dai->driver->ops && codec_dai->driver->ops->trigger) { ret = codec_dai->driver->ops->trigger(substream, cmd, codec_dai); if (ret < 0) return ret; @@ -630,7 +630,7 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) return ret; } - if (cpu_dai->driver->ops->trigger) { + if (cpu_dai->driver->ops && cpu_dai->driver->ops->trigger) { ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai); if (ret < 0) return ret; @@ -647,19 +647,20 @@ static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai = rtd->codec_dai; int ret; - if (codec_dai->driver->ops->bespoke_trigger) { + if (codec_dai->driver->ops && + codec_dai->driver->ops->bespoke_trigger) { ret = codec_dai->driver->ops->bespoke_trigger(substream, cmd, codec_dai); if (ret < 0) return ret; } - if (platform->driver->bespoke_trigger) { + if (platform->driver->ops && platform->driver->bespoke_trigger) { ret = platform->driver->bespoke_trigger(substream, cmd); if (ret < 0) return ret; } - if (cpu_dai->driver->ops->bespoke_trigger) { + if (cpu_dai->driver->ops && cpu_dai->driver->ops->bespoke_trigger) { ret = cpu_dai->driver->ops->bespoke_trigger(substream, cmd, cpu_dai); if (ret < 0) return ret; @@ -684,10 +685,10 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) if (platform->driver->ops && platform->driver->ops->pointer) offset = platform->driver->ops->pointer(substream); - if (cpu_dai->driver->ops->delay) + if (cpu_dai->driver->ops && cpu_dai->driver->ops->delay) delay += cpu_dai->driver->ops->delay(substream, cpu_dai); - if (codec_dai->driver->ops->delay) + if (codec_dai->driver->ops && codec_dai->driver->ops->delay) delay += codec_dai->driver->ops->delay(substream, codec_dai); if (platform->driver->delay) @@ -1673,7 +1674,7 @@ static int soc_pcm_ioctl(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; - if (platform->driver->ops->ioctl) + if (platform->driver->ops && platform->driver->ops->ioctl) return platform->driver->ops->ioctl(substream, cmd, arg); return snd_pcm_lib_ioctl(substream, cmd, arg); } @@ -1934,8 +1935,8 @@ int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute) dev_dbg(be->dev, "ASoC: BE digital mute %s\n", be->dai_link->name); - if (drv->ops->digital_mute && dai->playback_active) - drv->ops->digital_mute(dai, mute); + if (drv->ops && drv->ops->digital_mute && dai->playback_active) + drv->ops->digital_mute(dai, mute); } return 0; @@ -2224,7 +2225,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_params); int snd_soc_platform_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_platform *platform) { - if (platform->driver->ops->trigger) + if (platform->driver->ops && platform->driver->ops->trigger) return platform->driver->ops->trigger(substream, cmd); return 0; } -- cgit v0.10.2 From 2062b4c5d2d8fa47dc89105464e67a7ba310c9e7 Mon Sep 17 00:00:00 2001 From: Russell King - ARM Linux Date: Thu, 31 Oct 2013 15:09:20 +0000 Subject: ASoC: dpcm: improve robustness Avoid oopsing if there is no backend stream associated with a front end stream. Signed-off-by: Russell King Signed-off-by: Mark Brown diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index d81b792..591f0f3 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1038,6 +1038,12 @@ static int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) struct snd_pcm_substream *be_substream = snd_soc_dpcm_get_substream(be, stream); + if (!be_substream) { + dev_err(be->dev, "ASoC: no backend %s stream\n", + stream ? "capture" : "playback"); + continue; + } + /* is this op for this BE ? */ if (!snd_soc_dpcm_be_can_update(fe, be, stream)) continue; @@ -1055,7 +1061,8 @@ static int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) (be->dpcm[stream].state != SND_SOC_DPCM_STATE_CLOSE)) continue; - dev_dbg(be->dev, "ASoC: open BE %s\n", be->dai_link->name); + dev_dbg(be->dev, "ASoC: open %s BE %s\n", + stream ? "capture" : "playback", be->dai_link->name); be_substream->runtime = be->dpcm[stream].runtime; err = soc_pcm_open(be_substream); -- cgit v0.10.2 From 7ea6c6c15ee16f4c7f5eeaa62f77e696f85ae0d1 Mon Sep 17 00:00:00 2001 From: "Luck, Tony" Date: Mon, 28 Oct 2013 14:06:55 -0700 Subject: Move cper.c from drivers/acpi/apei to drivers/firmware/efi cper.c contains code to decode and print "Common Platform Error Records". Originally added under drivers/acpi/apei because the only user was in that same directory - but now we have another consumer, and we shouldn't have to force CONFIG_ACPI_APEI get access to this code. Since CPER is defined in the UEFI specification - the logical home for this code is under drivers/firmware/efi/ Acked-by: Matt Fleming Acked-by: Ingo Molnar Signed-off-by: Tony Luck diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 252f0e8..08eadb4 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -374,7 +374,9 @@ source "drivers/acpi/apei/Kconfig" config ACPI_EXTLOG tristate "Extended Error Log support" - depends on X86_MCE && ACPI_APEI + depends on X86_MCE + select EFI + select UEFI_CPER default n help Certain usages such as Predictive Failure Analysis (PFA) require diff --git a/drivers/acpi/apei/Kconfig b/drivers/acpi/apei/Kconfig index f0c1ce9..786294b 100644 --- a/drivers/acpi/apei/Kconfig +++ b/drivers/acpi/apei/Kconfig @@ -2,6 +2,8 @@ config ACPI_APEI bool "ACPI Platform Error Interface (APEI)" select MISC_FILESYSTEMS select PSTORE + select EFI + select UEFI_CPER depends on X86 help APEI allows to report errors (for example from the chipset) diff --git a/drivers/acpi/apei/Makefile b/drivers/acpi/apei/Makefile index d1d1bc0..5d575a9 100644 --- a/drivers/acpi/apei/Makefile +++ b/drivers/acpi/apei/Makefile @@ -3,4 +3,4 @@ obj-$(CONFIG_ACPI_APEI_GHES) += ghes.o obj-$(CONFIG_ACPI_APEI_EINJ) += einj.o obj-$(CONFIG_ACPI_APEI_ERST_DEBUG) += erst-dbg.o -apei-y := apei-base.o hest.o cper.o erst.o +apei-y := apei-base.o hest.o erst.o diff --git a/drivers/acpi/apei/cper.c b/drivers/acpi/apei/cper.c deleted file mode 100644 index 1491dd4..0000000 --- a/drivers/acpi/apei/cper.c +++ /dev/null @@ -1,410 +0,0 @@ -/* - * UEFI Common Platform Error Record (CPER) support - * - * Copyright (C) 2010, Intel Corp. - * Author: Huang Ying - * - * CPER is the format used to describe platform hardware error by - * various tables, such as ERST, BERT and HEST etc. - * - * For more information about CPER, please refer to Appendix N of UEFI - * Specification version 2.4. - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define INDENT_SP " " -/* - * CPER record ID need to be unique even after reboot, because record - * ID is used as index for ERST storage, while CPER records from - * multiple boot may co-exist in ERST. - */ -u64 cper_next_record_id(void) -{ - static atomic64_t seq; - - if (!atomic64_read(&seq)) - atomic64_set(&seq, ((u64)get_seconds()) << 32); - - return atomic64_inc_return(&seq); -} -EXPORT_SYMBOL_GPL(cper_next_record_id); - -static const char *cper_severity_strs[] = { - "recoverable", - "fatal", - "corrected", - "info", -}; - -static const char *cper_severity_str(unsigned int severity) -{ - return severity < ARRAY_SIZE(cper_severity_strs) ? - cper_severity_strs[severity] : "unknown"; -} - -/* - * cper_print_bits - print strings for set bits - * @pfx: prefix for each line, including log level and prefix string - * @bits: bit mask - * @strs: string array, indexed by bit position - * @strs_size: size of the string array: @strs - * - * For each set bit in @bits, print the corresponding string in @strs. - * If the output length is longer than 80, multiple line will be - * printed, with @pfx is printed at the beginning of each line. - */ -void cper_print_bits(const char *pfx, unsigned int bits, - const char * const strs[], unsigned int strs_size) -{ - int i, len = 0; - const char *str; - char buf[84]; - - for (i = 0; i < strs_size; i++) { - if (!(bits & (1U << i))) - continue; - str = strs[i]; - if (!str) - continue; - if (len && len + strlen(str) + 2 > 80) { - printk("%s\n", buf); - len = 0; - } - if (!len) - len = snprintf(buf, sizeof(buf), "%s%s", pfx, str); - else - len += snprintf(buf+len, sizeof(buf)-len, ", %s", str); - } - if (len) - printk("%s\n", buf); -} - -static const char * const cper_proc_type_strs[] = { - "IA32/X64", - "IA64", -}; - -static const char * const cper_proc_isa_strs[] = { - "IA32", - "IA64", - "X64", -}; - -static const char * const cper_proc_error_type_strs[] = { - "cache error", - "TLB error", - "bus error", - "micro-architectural error", -}; - -static const char * const cper_proc_op_strs[] = { - "unknown or generic", - "data read", - "data write", - "instruction execution", -}; - -static const char * const cper_proc_flag_strs[] = { - "restartable", - "precise IP", - "overflow", - "corrected", -}; - -static void cper_print_proc_generic(const char *pfx, - const struct cper_sec_proc_generic *proc) -{ - if (proc->validation_bits & CPER_PROC_VALID_TYPE) - printk("%s""processor_type: %d, %s\n", pfx, proc->proc_type, - proc->proc_type < ARRAY_SIZE(cper_proc_type_strs) ? - cper_proc_type_strs[proc->proc_type] : "unknown"); - if (proc->validation_bits & CPER_PROC_VALID_ISA) - printk("%s""processor_isa: %d, %s\n", pfx, proc->proc_isa, - proc->proc_isa < ARRAY_SIZE(cper_proc_isa_strs) ? - cper_proc_isa_strs[proc->proc_isa] : "unknown"); - if (proc->validation_bits & CPER_PROC_VALID_ERROR_TYPE) { - printk("%s""error_type: 0x%02x\n", pfx, proc->proc_error_type); - cper_print_bits(pfx, proc->proc_error_type, - cper_proc_error_type_strs, - ARRAY_SIZE(cper_proc_error_type_strs)); - } - if (proc->validation_bits & CPER_PROC_VALID_OPERATION) - printk("%s""operation: %d, %s\n", pfx, proc->operation, - proc->operation < ARRAY_SIZE(cper_proc_op_strs) ? - cper_proc_op_strs[proc->operation] : "unknown"); - if (proc->validation_bits & CPER_PROC_VALID_FLAGS) { - printk("%s""flags: 0x%02x\n", pfx, proc->flags); - cper_print_bits(pfx, proc->flags, cper_proc_flag_strs, - ARRAY_SIZE(cper_proc_flag_strs)); - } - if (proc->validation_bits & CPER_PROC_VALID_LEVEL) - printk("%s""level: %d\n", pfx, proc->level); - if (proc->validation_bits & CPER_PROC_VALID_VERSION) - printk("%s""version_info: 0x%016llx\n", pfx, proc->cpu_version); - if (proc->validation_bits & CPER_PROC_VALID_ID) - printk("%s""processor_id: 0x%016llx\n", pfx, proc->proc_id); - if (proc->validation_bits & CPER_PROC_VALID_TARGET_ADDRESS) - printk("%s""target_address: 0x%016llx\n", - pfx, proc->target_addr); - if (proc->validation_bits & CPER_PROC_VALID_REQUESTOR_ID) - printk("%s""requestor_id: 0x%016llx\n", - pfx, proc->requestor_id); - if (proc->validation_bits & CPER_PROC_VALID_RESPONDER_ID) - printk("%s""responder_id: 0x%016llx\n", - pfx, proc->responder_id); - if (proc->validation_bits & CPER_PROC_VALID_IP) - printk("%s""IP: 0x%016llx\n", pfx, proc->ip); -} - -static const char *cper_mem_err_type_strs[] = { - "unknown", - "no error", - "single-bit ECC", - "multi-bit ECC", - "single-symbol chipkill ECC", - "multi-symbol chipkill ECC", - "master abort", - "target abort", - "parity error", - "watchdog timeout", - "invalid address", - "mirror Broken", - "memory sparing", - "scrub corrected error", - "scrub uncorrected error", - "physical memory map-out event", -}; - -static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem) -{ - if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS) - printk("%s""error_status: 0x%016llx\n", pfx, mem->error_status); - if (mem->validation_bits & CPER_MEM_VALID_PA) - printk("%s""physical_address: 0x%016llx\n", - pfx, mem->physical_addr); - if (mem->validation_bits & CPER_MEM_VALID_PA_MASK) - printk("%s""physical_address_mask: 0x%016llx\n", - pfx, mem->physical_addr_mask); - if (mem->validation_bits & CPER_MEM_VALID_NODE) - pr_debug("node: %d\n", mem->node); - if (mem->validation_bits & CPER_MEM_VALID_CARD) - pr_debug("card: %d\n", mem->card); - if (mem->validation_bits & CPER_MEM_VALID_MODULE) - pr_debug("module: %d\n", mem->module); - if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER) - pr_debug("rank: %d\n", mem->rank); - if (mem->validation_bits & CPER_MEM_VALID_BANK) - pr_debug("bank: %d\n", mem->bank); - if (mem->validation_bits & CPER_MEM_VALID_DEVICE) - pr_debug("device: %d\n", mem->device); - if (mem->validation_bits & CPER_MEM_VALID_ROW) - pr_debug("row: %d\n", mem->row); - if (mem->validation_bits & CPER_MEM_VALID_COLUMN) - pr_debug("column: %d\n", mem->column); - if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION) - pr_debug("bit_position: %d\n", mem->bit_pos); - if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID) - pr_debug("requestor_id: 0x%016llx\n", mem->requestor_id); - if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID) - pr_debug("responder_id: 0x%016llx\n", mem->responder_id); - if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID) - pr_debug("target_id: 0x%016llx\n", mem->target_id); - if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) { - u8 etype = mem->error_type; - printk("%s""error_type: %d, %s\n", pfx, etype, - etype < ARRAY_SIZE(cper_mem_err_type_strs) ? - cper_mem_err_type_strs[etype] : "unknown"); - } - if (mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE) { - const char *bank = NULL, *device = NULL; - dmi_memdev_name(mem->mem_dev_handle, &bank, &device); - if (bank != NULL && device != NULL) - printk("%s""DIMM location: %s %s", pfx, bank, device); - else - printk("%s""DIMM DMI handle: 0x%.4x", - pfx, mem->mem_dev_handle); - } -} - -static const char *cper_pcie_port_type_strs[] = { - "PCIe end point", - "legacy PCI end point", - "unknown", - "unknown", - "root port", - "upstream switch port", - "downstream switch port", - "PCIe to PCI/PCI-X bridge", - "PCI/PCI-X to PCIe bridge", - "root complex integrated endpoint device", - "root complex event collector", -}; - -static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie, - const struct acpi_generic_data *gdata) -{ - if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE) - printk("%s""port_type: %d, %s\n", pfx, pcie->port_type, - pcie->port_type < ARRAY_SIZE(cper_pcie_port_type_strs) ? - cper_pcie_port_type_strs[pcie->port_type] : "unknown"); - if (pcie->validation_bits & CPER_PCIE_VALID_VERSION) - printk("%s""version: %d.%d\n", pfx, - pcie->version.major, pcie->version.minor); - if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS) - printk("%s""command: 0x%04x, status: 0x%04x\n", pfx, - pcie->command, pcie->status); - if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) { - const __u8 *p; - printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx, - pcie->device_id.segment, pcie->device_id.bus, - pcie->device_id.device, pcie->device_id.function); - printk("%s""slot: %d\n", pfx, - pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT); - printk("%s""secondary_bus: 0x%02x\n", pfx, - pcie->device_id.secondary_bus); - printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx, - pcie->device_id.vendor_id, pcie->device_id.device_id); - p = pcie->device_id.class_code; - printk("%s""class_code: %02x%02x%02x\n", pfx, p[0], p[1], p[2]); - } - if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER) - printk("%s""serial number: 0x%04x, 0x%04x\n", pfx, - pcie->serial_number.lower, pcie->serial_number.upper); - if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS) - printk( - "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n", - pfx, pcie->bridge.secondary_status, pcie->bridge.control); -} - -static void cper_estatus_print_section( - const char *pfx, const struct acpi_generic_data *gdata, int sec_no) -{ - uuid_le *sec_type = (uuid_le *)gdata->section_type; - __u16 severity; - char newpfx[64]; - - severity = gdata->error_severity; - printk("%s""Error %d, type: %s\n", pfx, sec_no, - cper_severity_str(severity)); - if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID) - printk("%s""fru_id: %pUl\n", pfx, (uuid_le *)gdata->fru_id); - if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) - printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text); - - snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP); - if (!uuid_le_cmp(*sec_type, CPER_SEC_PROC_GENERIC)) { - struct cper_sec_proc_generic *proc_err = (void *)(gdata + 1); - printk("%s""section_type: general processor error\n", newpfx); - if (gdata->error_data_length >= sizeof(*proc_err)) - cper_print_proc_generic(newpfx, proc_err); - else - goto err_section_too_small; - } else if (!uuid_le_cmp(*sec_type, CPER_SEC_PLATFORM_MEM)) { - struct cper_sec_mem_err *mem_err = (void *)(gdata + 1); - printk("%s""section_type: memory error\n", newpfx); - if (gdata->error_data_length >= sizeof(*mem_err)) - cper_print_mem(newpfx, mem_err); - else - goto err_section_too_small; - } else if (!uuid_le_cmp(*sec_type, CPER_SEC_PCIE)) { - struct cper_sec_pcie *pcie = (void *)(gdata + 1); - printk("%s""section_type: PCIe error\n", newpfx); - if (gdata->error_data_length >= sizeof(*pcie)) - cper_print_pcie(newpfx, pcie, gdata); - else - goto err_section_too_small; - } else - printk("%s""section type: unknown, %pUl\n", newpfx, sec_type); - - return; - -err_section_too_small: - pr_err(FW_WARN "error section length is too small\n"); -} - -void cper_estatus_print(const char *pfx, - const struct acpi_generic_status *estatus) -{ - struct acpi_generic_data *gdata; - unsigned int data_len, gedata_len; - int sec_no = 0; - char newpfx[64]; - __u16 severity; - - severity = estatus->error_severity; - if (severity == CPER_SEV_CORRECTED) - printk("%s%s\n", pfx, - "It has been corrected by h/w " - "and requires no further action"); - printk("%s""event severity: %s\n", pfx, cper_severity_str(severity)); - data_len = estatus->data_length; - gdata = (struct acpi_generic_data *)(estatus + 1); - snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP); - while (data_len >= sizeof(*gdata)) { - gedata_len = gdata->error_data_length; - cper_estatus_print_section(newpfx, gdata, sec_no); - data_len -= gedata_len + sizeof(*gdata); - gdata = (void *)(gdata + 1) + gedata_len; - sec_no++; - } -} -EXPORT_SYMBOL_GPL(cper_estatus_print); - -int cper_estatus_check_header(const struct acpi_generic_status *estatus) -{ - if (estatus->data_length && - estatus->data_length < sizeof(struct acpi_generic_data)) - return -EINVAL; - if (estatus->raw_data_length && - estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length) - return -EINVAL; - - return 0; -} -EXPORT_SYMBOL_GPL(cper_estatus_check_header); - -int cper_estatus_check(const struct acpi_generic_status *estatus) -{ - struct acpi_generic_data *gdata; - unsigned int data_len, gedata_len; - int rc; - - rc = cper_estatus_check_header(estatus); - if (rc) - return rc; - data_len = estatus->data_length; - gdata = (struct acpi_generic_data *)(estatus + 1); - while (data_len >= sizeof(*gdata)) { - gedata_len = gdata->error_data_length; - if (gedata_len > data_len - sizeof(*gdata)) - return -EINVAL; - data_len -= gedata_len + sizeof(*gdata); - gdata = (void *)(gdata + 1) + gedata_len; - } - if (data_len) - return -EINVAL; - - return 0; -} -EXPORT_SYMBOL_GPL(cper_estatus_check); diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index b0fc7c7..3150aa4 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -36,4 +36,7 @@ config EFI_VARS_PSTORE_DEFAULT_DISABLE backend for pstore by default. This setting can be overridden using the efivars module's pstore_disable parameter. +config UEFI_CPER + def_bool n + endmenu diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index 99245ab..9ba156d 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -4,3 +4,4 @@ obj-y += efi.o vars.o obj-$(CONFIG_EFI_VARS) += efivars.o obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o +obj-$(CONFIG_UEFI_CPER) += cper.o diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c new file mode 100644 index 0000000..1491dd4 --- /dev/null +++ b/drivers/firmware/efi/cper.c @@ -0,0 +1,410 @@ +/* + * UEFI Common Platform Error Record (CPER) support + * + * Copyright (C) 2010, Intel Corp. + * Author: Huang Ying + * + * CPER is the format used to describe platform hardware error by + * various tables, such as ERST, BERT and HEST etc. + * + * For more information about CPER, please refer to Appendix N of UEFI + * Specification version 2.4. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define INDENT_SP " " +/* + * CPER record ID need to be unique even after reboot, because record + * ID is used as index for ERST storage, while CPER records from + * multiple boot may co-exist in ERST. + */ +u64 cper_next_record_id(void) +{ + static atomic64_t seq; + + if (!atomic64_read(&seq)) + atomic64_set(&seq, ((u64)get_seconds()) << 32); + + return atomic64_inc_return(&seq); +} +EXPORT_SYMBOL_GPL(cper_next_record_id); + +static const char *cper_severity_strs[] = { + "recoverable", + "fatal", + "corrected", + "info", +}; + +static const char *cper_severity_str(unsigned int severity) +{ + return severity < ARRAY_SIZE(cper_severity_strs) ? + cper_severity_strs[severity] : "unknown"; +} + +/* + * cper_print_bits - print strings for set bits + * @pfx: prefix for each line, including log level and prefix string + * @bits: bit mask + * @strs: string array, indexed by bit position + * @strs_size: size of the string array: @strs + * + * For each set bit in @bits, print the corresponding string in @strs. + * If the output length is longer than 80, multiple line will be + * printed, with @pfx is printed at the beginning of each line. + */ +void cper_print_bits(const char *pfx, unsigned int bits, + const char * const strs[], unsigned int strs_size) +{ + int i, len = 0; + const char *str; + char buf[84]; + + for (i = 0; i < strs_size; i++) { + if (!(bits & (1U << i))) + continue; + str = strs[i]; + if (!str) + continue; + if (len && len + strlen(str) + 2 > 80) { + printk("%s\n", buf); + len = 0; + } + if (!len) + len = snprintf(buf, sizeof(buf), "%s%s", pfx, str); + else + len += snprintf(buf+len, sizeof(buf)-len, ", %s", str); + } + if (len) + printk("%s\n", buf); +} + +static const char * const cper_proc_type_strs[] = { + "IA32/X64", + "IA64", +}; + +static const char * const cper_proc_isa_strs[] = { + "IA32", + "IA64", + "X64", +}; + +static const char * const cper_proc_error_type_strs[] = { + "cache error", + "TLB error", + "bus error", + "micro-architectural error", +}; + +static const char * const cper_proc_op_strs[] = { + "unknown or generic", + "data read", + "data write", + "instruction execution", +}; + +static const char * const cper_proc_flag_strs[] = { + "restartable", + "precise IP", + "overflow", + "corrected", +}; + +static void cper_print_proc_generic(const char *pfx, + const struct cper_sec_proc_generic *proc) +{ + if (proc->validation_bits & CPER_PROC_VALID_TYPE) + printk("%s""processor_type: %d, %s\n", pfx, proc->proc_type, + proc->proc_type < ARRAY_SIZE(cper_proc_type_strs) ? + cper_proc_type_strs[proc->proc_type] : "unknown"); + if (proc->validation_bits & CPER_PROC_VALID_ISA) + printk("%s""processor_isa: %d, %s\n", pfx, proc->proc_isa, + proc->proc_isa < ARRAY_SIZE(cper_proc_isa_strs) ? + cper_proc_isa_strs[proc->proc_isa] : "unknown"); + if (proc->validation_bits & CPER_PROC_VALID_ERROR_TYPE) { + printk("%s""error_type: 0x%02x\n", pfx, proc->proc_error_type); + cper_print_bits(pfx, proc->proc_error_type, + cper_proc_error_type_strs, + ARRAY_SIZE(cper_proc_error_type_strs)); + } + if (proc->validation_bits & CPER_PROC_VALID_OPERATION) + printk("%s""operation: %d, %s\n", pfx, proc->operation, + proc->operation < ARRAY_SIZE(cper_proc_op_strs) ? + cper_proc_op_strs[proc->operation] : "unknown"); + if (proc->validation_bits & CPER_PROC_VALID_FLAGS) { + printk("%s""flags: 0x%02x\n", pfx, proc->flags); + cper_print_bits(pfx, proc->flags, cper_proc_flag_strs, + ARRAY_SIZE(cper_proc_flag_strs)); + } + if (proc->validation_bits & CPER_PROC_VALID_LEVEL) + printk("%s""level: %d\n", pfx, proc->level); + if (proc->validation_bits & CPER_PROC_VALID_VERSION) + printk("%s""version_info: 0x%016llx\n", pfx, proc->cpu_version); + if (proc->validation_bits & CPER_PROC_VALID_ID) + printk("%s""processor_id: 0x%016llx\n", pfx, proc->proc_id); + if (proc->validation_bits & CPER_PROC_VALID_TARGET_ADDRESS) + printk("%s""target_address: 0x%016llx\n", + pfx, proc->target_addr); + if (proc->validation_bits & CPER_PROC_VALID_REQUESTOR_ID) + printk("%s""requestor_id: 0x%016llx\n", + pfx, proc->requestor_id); + if (proc->validation_bits & CPER_PROC_VALID_RESPONDER_ID) + printk("%s""responder_id: 0x%016llx\n", + pfx, proc->responder_id); + if (proc->validation_bits & CPER_PROC_VALID_IP) + printk("%s""IP: 0x%016llx\n", pfx, proc->ip); +} + +static const char *cper_mem_err_type_strs[] = { + "unknown", + "no error", + "single-bit ECC", + "multi-bit ECC", + "single-symbol chipkill ECC", + "multi-symbol chipkill ECC", + "master abort", + "target abort", + "parity error", + "watchdog timeout", + "invalid address", + "mirror Broken", + "memory sparing", + "scrub corrected error", + "scrub uncorrected error", + "physical memory map-out event", +}; + +static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem) +{ + if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS) + printk("%s""error_status: 0x%016llx\n", pfx, mem->error_status); + if (mem->validation_bits & CPER_MEM_VALID_PA) + printk("%s""physical_address: 0x%016llx\n", + pfx, mem->physical_addr); + if (mem->validation_bits & CPER_MEM_VALID_PA_MASK) + printk("%s""physical_address_mask: 0x%016llx\n", + pfx, mem->physical_addr_mask); + if (mem->validation_bits & CPER_MEM_VALID_NODE) + pr_debug("node: %d\n", mem->node); + if (mem->validation_bits & CPER_MEM_VALID_CARD) + pr_debug("card: %d\n", mem->card); + if (mem->validation_bits & CPER_MEM_VALID_MODULE) + pr_debug("module: %d\n", mem->module); + if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER) + pr_debug("rank: %d\n", mem->rank); + if (mem->validation_bits & CPER_MEM_VALID_BANK) + pr_debug("bank: %d\n", mem->bank); + if (mem->validation_bits & CPER_MEM_VALID_DEVICE) + pr_debug("device: %d\n", mem->device); + if (mem->validation_bits & CPER_MEM_VALID_ROW) + pr_debug("row: %d\n", mem->row); + if (mem->validation_bits & CPER_MEM_VALID_COLUMN) + pr_debug("column: %d\n", mem->column); + if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION) + pr_debug("bit_position: %d\n", mem->bit_pos); + if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID) + pr_debug("requestor_id: 0x%016llx\n", mem->requestor_id); + if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID) + pr_debug("responder_id: 0x%016llx\n", mem->responder_id); + if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID) + pr_debug("target_id: 0x%016llx\n", mem->target_id); + if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) { + u8 etype = mem->error_type; + printk("%s""error_type: %d, %s\n", pfx, etype, + etype < ARRAY_SIZE(cper_mem_err_type_strs) ? + cper_mem_err_type_strs[etype] : "unknown"); + } + if (mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE) { + const char *bank = NULL, *device = NULL; + dmi_memdev_name(mem->mem_dev_handle, &bank, &device); + if (bank != NULL && device != NULL) + printk("%s""DIMM location: %s %s", pfx, bank, device); + else + printk("%s""DIMM DMI handle: 0x%.4x", + pfx, mem->mem_dev_handle); + } +} + +static const char *cper_pcie_port_type_strs[] = { + "PCIe end point", + "legacy PCI end point", + "unknown", + "unknown", + "root port", + "upstream switch port", + "downstream switch port", + "PCIe to PCI/PCI-X bridge", + "PCI/PCI-X to PCIe bridge", + "root complex integrated endpoint device", + "root complex event collector", +}; + +static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie, + const struct acpi_generic_data *gdata) +{ + if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE) + printk("%s""port_type: %d, %s\n", pfx, pcie->port_type, + pcie->port_type < ARRAY_SIZE(cper_pcie_port_type_strs) ? + cper_pcie_port_type_strs[pcie->port_type] : "unknown"); + if (pcie->validation_bits & CPER_PCIE_VALID_VERSION) + printk("%s""version: %d.%d\n", pfx, + pcie->version.major, pcie->version.minor); + if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS) + printk("%s""command: 0x%04x, status: 0x%04x\n", pfx, + pcie->command, pcie->status); + if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) { + const __u8 *p; + printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx, + pcie->device_id.segment, pcie->device_id.bus, + pcie->device_id.device, pcie->device_id.function); + printk("%s""slot: %d\n", pfx, + pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT); + printk("%s""secondary_bus: 0x%02x\n", pfx, + pcie->device_id.secondary_bus); + printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx, + pcie->device_id.vendor_id, pcie->device_id.device_id); + p = pcie->device_id.class_code; + printk("%s""class_code: %02x%02x%02x\n", pfx, p[0], p[1], p[2]); + } + if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER) + printk("%s""serial number: 0x%04x, 0x%04x\n", pfx, + pcie->serial_number.lower, pcie->serial_number.upper); + if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS) + printk( + "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n", + pfx, pcie->bridge.secondary_status, pcie->bridge.control); +} + +static void cper_estatus_print_section( + const char *pfx, const struct acpi_generic_data *gdata, int sec_no) +{ + uuid_le *sec_type = (uuid_le *)gdata->section_type; + __u16 severity; + char newpfx[64]; + + severity = gdata->error_severity; + printk("%s""Error %d, type: %s\n", pfx, sec_no, + cper_severity_str(severity)); + if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID) + printk("%s""fru_id: %pUl\n", pfx, (uuid_le *)gdata->fru_id); + if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) + printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text); + + snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP); + if (!uuid_le_cmp(*sec_type, CPER_SEC_PROC_GENERIC)) { + struct cper_sec_proc_generic *proc_err = (void *)(gdata + 1); + printk("%s""section_type: general processor error\n", newpfx); + if (gdata->error_data_length >= sizeof(*proc_err)) + cper_print_proc_generic(newpfx, proc_err); + else + goto err_section_too_small; + } else if (!uuid_le_cmp(*sec_type, CPER_SEC_PLATFORM_MEM)) { + struct cper_sec_mem_err *mem_err = (void *)(gdata + 1); + printk("%s""section_type: memory error\n", newpfx); + if (gdata->error_data_length >= sizeof(*mem_err)) + cper_print_mem(newpfx, mem_err); + else + goto err_section_too_small; + } else if (!uuid_le_cmp(*sec_type, CPER_SEC_PCIE)) { + struct cper_sec_pcie *pcie = (void *)(gdata + 1); + printk("%s""section_type: PCIe error\n", newpfx); + if (gdata->error_data_length >= sizeof(*pcie)) + cper_print_pcie(newpfx, pcie, gdata); + else + goto err_section_too_small; + } else + printk("%s""section type: unknown, %pUl\n", newpfx, sec_type); + + return; + +err_section_too_small: + pr_err(FW_WARN "error section length is too small\n"); +} + +void cper_estatus_print(const char *pfx, + const struct acpi_generic_status *estatus) +{ + struct acpi_generic_data *gdata; + unsigned int data_len, gedata_len; + int sec_no = 0; + char newpfx[64]; + __u16 severity; + + severity = estatus->error_severity; + if (severity == CPER_SEV_CORRECTED) + printk("%s%s\n", pfx, + "It has been corrected by h/w " + "and requires no further action"); + printk("%s""event severity: %s\n", pfx, cper_severity_str(severity)); + data_len = estatus->data_length; + gdata = (struct acpi_generic_data *)(estatus + 1); + snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP); + while (data_len >= sizeof(*gdata)) { + gedata_len = gdata->error_data_length; + cper_estatus_print_section(newpfx, gdata, sec_no); + data_len -= gedata_len + sizeof(*gdata); + gdata = (void *)(gdata + 1) + gedata_len; + sec_no++; + } +} +EXPORT_SYMBOL_GPL(cper_estatus_print); + +int cper_estatus_check_header(const struct acpi_generic_status *estatus) +{ + if (estatus->data_length && + estatus->data_length < sizeof(struct acpi_generic_data)) + return -EINVAL; + if (estatus->raw_data_length && + estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL_GPL(cper_estatus_check_header); + +int cper_estatus_check(const struct acpi_generic_status *estatus) +{ + struct acpi_generic_data *gdata; + unsigned int data_len, gedata_len; + int rc; + + rc = cper_estatus_check_header(estatus); + if (rc) + return rc; + data_len = estatus->data_length; + gdata = (struct acpi_generic_data *)(estatus + 1); + while (data_len >= sizeof(*gdata)) { + gedata_len = gdata->error_data_length; + if (gedata_len > data_len - sizeof(*gdata)) + return -EINVAL; + data_len -= gedata_len + sizeof(*gdata); + gdata = (void *)(gdata + 1) + gedata_len; + } + if (data_len) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL_GPL(cper_estatus_check); -- cgit v0.10.2 From 7b5bfb82882b9b1c8423ce0ed6852ca3762d967a Mon Sep 17 00:00:00 2001 From: Phil Edworthy Date: Thu, 31 Oct 2013 23:06:17 -0700 Subject: ASoC: ak4642: prevent un-necessary changes to SG_SL1 If you record the sound during playback, the playback sound becomes silent. Modify so that the codec driver does not clear SG_SL1::DACL bit which is controlled under widget Signed-off-by: Phil Edworthy Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 2d03787..687565d 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -257,7 +257,7 @@ static int ak4642_dai_startup(struct snd_pcm_substream *substream, * This operation came from example code of * "ASAHI KASEI AK4642" (japanese) manual p94. */ - snd_soc_write(codec, SG_SL1, PMMP | MGAIN0); + snd_soc_update_bits(codec, SG_SL1, PMMP | MGAIN0, PMMP | MGAIN0); snd_soc_write(codec, TIMER, ZTM(0x3) | WTM(0x3)); snd_soc_write(codec, ALC_CTL1, ALC | LMTH0); snd_soc_update_bits(codec, PW_MGMT1, PMADL, PMADL); -- cgit v0.10.2 From 7d716456a0ee4e9bd63be9234f886d20382ac950 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Thu, 31 Oct 2013 12:48:14 +0100 Subject: sched/wait: Fix __wait_event_interruptible_lock_irq_timeout() __wait_event_interruptible_lock_irq_timeout() needs the timeout parameter passed instead of "ret". This magically compiled since the only user has a local ret variable. Luckily we got a build warning: CC drivers/s390/scsi/zfcp_qdio.o drivers/s390/scsi/zfcp_qdio.c: In function 'zfcp_qdio_sbal_get': include/linux/wait.h:780:15: warning: 'ret' may be used uninitialized Signed-off-by: Heiko Carstens Acked-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20131031114814.GB5551@osiris Signed-off-by: Ingo Molnar diff --git a/include/linux/wait.h b/include/linux/wait.h index 3b23afa..61939ba 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -739,7 +739,7 @@ do { \ #define __wait_event_interruptible_lock_irq_timeout(wq, condition, \ lock, timeout) \ ___wait_event(wq, ___wait_cond_timeout(condition), \ - TASK_INTERRUPTIBLE, 0, ret, \ + TASK_INTERRUPTIBLE, 0, timeout, \ spin_unlock_irq(&lock); \ __ret = schedule_timeout(__ret); \ spin_lock_irq(&lock)); -- cgit v0.10.2 From 023838542dc8a4eac9650f98942671078a4ce73d Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Thu, 31 Oct 2013 18:31:51 -0400 Subject: ALSA: hda - not choose assigned converters for unused pins of Valleyview For Valleyview display codec, if an unused pin chooses an assgined converter selected by a used pin, playback on the unused pin can also give sound to the output device of the used pin. It's because data flows from the same convertor to the display port of the used pin. This issue is same as Haswell. So this patch avoids using assinged convertors for unused pins. The related function haswell_config_cvts() is renamed for code reuse. Signed-off-by: Mengdong Lin Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 772827b..d61cc2a 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -46,6 +46,7 @@ module_param(static_hdmi_pcm, bool, 0644); MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info"); #define is_haswell(codec) ((codec)->vendor_id == 0x80862807) +#define is_valleyview(codec) ((codec)->vendor_id == 0x80862882) struct hdmi_spec_per_cvt { hda_nid_t cvt_nid; @@ -1327,7 +1328,7 @@ static int hdmi_choose_cvt(struct hda_codec *codec, return 0; } -static void haswell_config_cvts(struct hda_codec *codec, +static void not_share_unassigned_cvt(struct hda_codec *codec, hda_nid_t pin_nid, int mux_idx) { struct hdmi_spec *spec = codec->spec; @@ -1406,8 +1407,8 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, mux_idx); /* configure unused pins to choose other converters */ - if (is_haswell(codec)) - haswell_config_cvts(codec, per_pin->pin_nid, mux_idx); + if (is_haswell(codec) || is_valleyview(codec)) + not_share_unassigned_cvt(codec, per_pin->pin_nid, mux_idx); snd_hda_spdif_ctls_assign(codec, pin_idx, per_cvt->cvt_nid); -- cgit v0.10.2 From 53805eca3d89b095062c11a6798689bb0af09216 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Thu, 31 Oct 2013 16:47:45 -0700 Subject: perf tools: Remove cast of non-variadic function to variadic The 4fb71074a570 (perf ui/hist: Consolidate hpp helpers) cset introduced a cast of percent_color_snprintf to a function pointer type with varargs. Change percent_color_snprintf to be variadic and remove the cast. The symptom of this was all percentages being reported as 0.00% in perf report --stdio output on the armhf arch. Signed-off-by: Michael Hudson-Doyle Acked-by: Namhyung Kim Acked-by: Will Deacon Cc: Jean Pihet Cc: Jiri Olsa Cc: Will Deacon Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/r/87zjppvw7y.fsf@canonical.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index 0a19328..78f4c92 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c @@ -117,7 +117,7 @@ static int hpp__color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ struct perf_hpp *hpp, struct hist_entry *he) \ { \ return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \ - (hpp_snprint_fn)percent_color_snprintf, true); \ + percent_color_snprintf, true); \ } #define __HPP_ENTRY_PERCENT_FN(_type, _field) \ diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index 11e46da..66e44a5 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c @@ -318,8 +318,15 @@ int percent_color_fprintf(FILE *fp, const char *fmt, double percent) return r; } -int percent_color_snprintf(char *bf, size_t size, const char *fmt, double percent) +int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...) { - const char *color = get_percent_color(percent); + va_list args; + double percent; + const char *color; + + va_start(args, fmt); + percent = va_arg(args, double); + va_end(args); + color = get_percent_color(percent); return color_snprintf(bf, size, color, fmt, percent); } diff --git a/tools/perf/util/color.h b/tools/perf/util/color.h index dea082b..fced384 100644 --- a/tools/perf/util/color.h +++ b/tools/perf/util/color.h @@ -39,7 +39,7 @@ int color_fprintf(FILE *fp, const char *color, const char *fmt, ...); int color_snprintf(char *bf, size_t size, const char *color, const char *fmt, ...); int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...); int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf); -int percent_color_snprintf(char *bf, size_t size, const char *fmt, double percent); +int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...); int percent_color_fprintf(FILE *fp, const char *fmt, double percent); const char *get_percent_color(double percent); -- cgit v0.10.2 From 32bf5bd181026fc99c0e15045abe409167285ba8 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Sun, 22 Sep 2013 16:49:24 +0800 Subject: perf bench: Fix two warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are two warnings in bench/numa, when building this on 32-bit machine. The warning output is attached: bench/numa.c:1113:20: error: comparison between signed and unsigned integer expressions [-Werror=sign-compare] bench/numa.c:1161:6: error: format ‘%lx’ expects argument of t'long unsigned int’, but argument 5 has type ‘u64’ [-Werror=format] This patch fixes these two warnings. Signed-off-by: Wei Yang Acked-by: Ingo Molnar Cc: Ingo Molnar Link: http://lkml.kernel.org/r/1379839764-9245-1-git-send-email-weiyang@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/bench/numa.c b/tools/perf/bench/numa.c index 30d1c32..a73c4ed 100644 --- a/tools/perf/bench/numa.c +++ b/tools/perf/bench/numa.c @@ -1110,7 +1110,7 @@ static void *worker_thread(void *__tdata) /* Check whether our max runtime timed out: */ if (g->p.nr_secs) { timersub(&stop, &start0, &diff); - if (diff.tv_sec >= g->p.nr_secs) { + if (diff.tv_sec >= (time_t)g->p.nr_secs) { g->stop_work = true; break; } @@ -1157,7 +1157,7 @@ static void *worker_thread(void *__tdata) runtime_ns_max += diff.tv_usec * 1000; if (details >= 0) { - printf(" #%2d / %2d: %14.2lf nsecs/op [val: %016lx]\n", + printf(" #%2d / %2d: %14.2lf nsecs/op [val: %016"PRIu64"]\n", process_nr, thread_nr, runtime_ns_max / bytes_done, val); } fflush(stdout); -- cgit v0.10.2 From 46d525eae2a076adfde92dca1db12d9a3b8ad8bb Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 31 Oct 2013 09:46:59 -0300 Subject: perf test: Update command line callchain attribute tests The "struct perf_event_attr setup" entry in 'perf test' is in fact a series of tests that will exec the tools, passing different sets of command line arguments to then intercept the sys_perf_event_open syscall, in user space, to check that the perf_event_attr->sample_type and other feature request bits are setup as expected. We recently restored the callchain requesting command line argument, -g, to not require a parameter ("dwarf" or "fp"), instead using a default ("fp" for now) and making the long option variant, --call-chain, be the one to be used when a different callchain collection method is preferred. The "struct perf_event_attr setup" test failed because we forgot to update the tests involving callchains, not switching from, '-g dwarf' to '--call-chain dwarf', making 'perf test' detect it: [root@sandy ~]# perf test -v 13 13: struct perf_event_attr setup : --- start --- running '/home/acme/libexec/perf-core/tests/attr/test-record-basic' running '/home/acme/libexec/perf-core/tests/attr/test-record-branch-any' running '/home/acme/libexec/perf-core/tests/attr/test-record-graph-default' running '/home/acme/libexec/perf-core/tests/attr/test-record-graph-dwarf' expected sample_type=12583, got 295 expected exclude_callchain_user=1, got 0 expected sample_stack_user=8192, got 0 FAILED '/home/acme/libexec/perf-core/tests/attr/test-record-graph-dwarf' - match failure ---- end ---- struct perf_event_attr setup: FAILED! [root@sandy ~]# Fix all of them now to use --call-chain when explicitely specifying a method. There is still work to do, as '-g fp', for instance, passed without problems. In that case 'perf test' saw no problems as the intercepted syscall got the bits as expected, i.e. the default is 'fp', but the fact that 'fp' may be an existing program and the specified workload would then be passed as a parameter to it is an usability problem that needs fixing. Next merge window tho. Acked-by: Jiri Olsa Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-jr3oq1k5iywnp7vvqlslzydm@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/tests/attr/README b/tools/perf/tests/attr/README index d102957..430024f 100644 --- a/tools/perf/tests/attr/README +++ b/tools/perf/tests/attr/README @@ -44,9 +44,9 @@ Following tests are defined (with perf commands): perf record -c 123 kill (test-record-count) perf record -d kill (test-record-data) perf record -F 100 kill (test-record-freq) - perf record -g -- kill (test-record-graph-default) - perf record -g dwarf -- kill (test-record-graph-dwarf) - perf record -g fp kill (test-record-graph-fp) + perf record -g kill (test-record-graph-default) + perf record --call-graph dwarf kill (test-record-graph-dwarf) + perf record --call-graph fp kill (test-record-graph-fp) perf record --group -e cycles,instructions kill (test-record-group) perf record -e '{cycles,instructions}' kill (test-record-group1) perf record -D kill (test-record-no-delay) diff --git a/tools/perf/tests/attr/test-record-graph-default b/tools/perf/tests/attr/test-record-graph-default index 833d184..853597a 100644 --- a/tools/perf/tests/attr/test-record-graph-default +++ b/tools/perf/tests/attr/test-record-graph-default @@ -1,6 +1,6 @@ [config] command = record -args = -g -- kill >/dev/null 2>&1 +args = -g kill >/dev/null 2>&1 [event:base-record] sample_type=295 diff --git a/tools/perf/tests/attr/test-record-graph-dwarf b/tools/perf/tests/attr/test-record-graph-dwarf index e93e082..d6f324e 100644 --- a/tools/perf/tests/attr/test-record-graph-dwarf +++ b/tools/perf/tests/attr/test-record-graph-dwarf @@ -1,6 +1,6 @@ [config] command = record -args = -g dwarf -- kill >/dev/null 2>&1 +args = --call-graph dwarf -- kill >/dev/null 2>&1 [event:base-record] sample_type=12583 diff --git a/tools/perf/tests/attr/test-record-graph-fp b/tools/perf/tests/attr/test-record-graph-fp index 7cef374..055e3be 100644 --- a/tools/perf/tests/attr/test-record-graph-fp +++ b/tools/perf/tests/attr/test-record-graph-fp @@ -1,6 +1,6 @@ [config] command = record -args = -g fp kill >/dev/null 2>&1 +args = --call-graph fp kill >/dev/null 2>&1 [event:base-record] sample_type=295 -- cgit v0.10.2 From 43bc3bf64b30cdfdffdc41e33bf21222e9396c42 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Fri, 1 Nov 2013 15:56:52 +0000 Subject: ASoC: wm_adsp: Print error when regmap reads/writes fail Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index b38f350..076da02 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -396,8 +396,8 @@ static int wm_coeff_write_control(struct snd_kcontrol *kcontrol, ret = regmap_raw_write(adsp->regmap, reg, scratch, ctl->len); if (ret) { - adsp_err(adsp, "Failed to write %zu bytes to %x\n", - ctl->len, reg); + adsp_err(adsp, "Failed to write %zu bytes to %x: %d\n", + ctl->len, reg, ret); kfree(scratch); return ret; } @@ -450,8 +450,8 @@ static int wm_coeff_read_control(struct snd_kcontrol *kcontrol, ret = regmap_raw_read(adsp->regmap, reg, scratch, ctl->len); if (ret) { - adsp_err(adsp, "Failed to read %zu bytes from %x\n", - ctl->len, reg); + adsp_err(adsp, "Failed to read %zu bytes from %x: %d\n", + ctl->len, reg, ret); kfree(scratch); return ret; } @@ -1313,8 +1313,8 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) le32_to_cpu(blk->len)); if (ret != 0) { adsp_err(dsp, - "%s.%d: Failed to write to %x in %s\n", - file, blocks, reg, region_name); + "%s.%d: Failed to write to %x in %s: %d\n", + file, blocks, reg, region_name, ret); } } -- cgit v0.10.2 From 7328823d0052bbdb15af162f9f510ced811bdfe8 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Fri, 1 Nov 2013 15:56:53 +0000 Subject: ASoC: wm_adsp: Release firmware on memory allocation failure Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 076da02..4008ceb 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -689,7 +689,8 @@ static int wm_adsp_load(struct wm_adsp *dsp) &buf_list); if (!buf) { adsp_err(dsp, "Out of memory\n"); - return -ENOMEM; + ret = -ENOMEM; + goto out_fw; } ret = regmap_raw_write_async(regmap, reg, buf->buf, -- cgit v0.10.2 From 562c5e6f52bc9ed48b8dc9cef97923b64bd843ec Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Fri, 1 Nov 2013 15:56:55 +0000 Subject: ASoC: wm_adsp: Add debug info on get()/put() transfers Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 4008ceb..1f1fc0d 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -401,6 +401,7 @@ static int wm_coeff_write_control(struct snd_kcontrol *kcontrol, kfree(scratch); return ret; } + adsp_dbg(adsp, "Wrote %zu bytes to %x\n", ctl->len, reg); kfree(scratch); @@ -455,6 +456,7 @@ static int wm_coeff_read_control(struct snd_kcontrol *kcontrol, kfree(scratch); return ret; } + adsp_dbg(adsp, "Read %zu bytes from %x\n", ctl->len, reg); memcpy(buf, scratch, ctl->len); kfree(scratch); -- cgit v0.10.2 From b0101b4f14d591719f53f7f38ede3651113e6a53 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Fri, 1 Nov 2013 15:56:56 +0000 Subject: ASoC: wm_adsp: Remove and free algorithm regions for ADSP1 Do it in a similar fashion as we do for ADSP2. Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 1f1fc0d..cc3575b 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -1361,6 +1361,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct snd_soc_codec *codec = w->codec; struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); struct wm_adsp *dsp = &dsps[w->shift]; + struct wm_adsp_alg_region *alg_region; struct wm_coeff_ctl *ctl; int ret; int val; @@ -1438,6 +1439,14 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, list_for_each_entry(ctl, &dsp->ctl_list, list) ctl->enabled = 0; + + while (!list_empty(&dsp->alg_regions)) { + alg_region = list_first_entry(&dsp->alg_regions, + struct wm_adsp_alg_region, + list); + list_del(&alg_region->list); + kfree(alg_region); + } break; default: -- cgit v0.10.2 From 3626992a21610fa19534d392bb0e79cc55a99c9a Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Fri, 1 Nov 2013 15:56:57 +0000 Subject: ASoC: wm_adsp: Print out the firmware version Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index cc3575b..53b6033 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -570,6 +570,7 @@ static int wm_adsp_load(struct wm_adsp *dsp) file, header->ver); goto out_fw; } + adsp_info(dsp, "Firmware version: %d\n", header->ver); if (header->core != dsp->type) { adsp_err(dsp, "%s: invalid core %d != %d\n", -- cgit v0.10.2 From dea0c74ff9e0d85d07b694e261aa7bda2c314ce8 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Fri, 1 Nov 2013 10:02:10 +0000 Subject: ASoC: wm8962: Add ALC coefficient support Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 7dd79c4..028686f 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -1775,6 +1775,11 @@ WM8962_DSP2_ENABLE("HPF2 Switch", WM8962_HPF2_ENA_SHIFT), SND_SOC_BYTES("HPF Coefficients", WM8962_LHPF2, 1), WM8962_DSP2_ENABLE("HD Bass Switch", WM8962_HDBASS_ENA_SHIFT), SND_SOC_BYTES("HD Bass Coefficients", WM8962_HDBASS_AI_1, 30), + +SOC_DOUBLE("ALC Switch", WM8962_ALC1, WM8962_ALCL_ENA_SHIFT, + WM8962_ALCR_ENA_SHIFT, 1, 0), +SND_SOC_BYTES_MASK("ALC Coefficients", WM8962_ALC1, 4, + WM8962_ALCL_ENA_MASK | WM8962_ALCR_ENA_MASK), }; static const struct snd_kcontrol_new wm8962_spk_mono_controls[] = { -- cgit v0.10.2 From ae2ff9f6c529ba28adea906037a93fd14e46e052 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Fri, 1 Nov 2013 10:02:58 +0000 Subject: ASoC: wm8962: Add EQ coefficient support Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 028686f..3a2f96c 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -1758,6 +1758,9 @@ SOC_DOUBLE_R_TLV("EQ4 Volume", WM8962_EQ3, WM8962_EQ23, WM8962_EQL_B4_GAIN_SHIFT, 31, 0, eq_tlv), SOC_DOUBLE_R_TLV("EQ5 Volume", WM8962_EQ3, WM8962_EQ23, WM8962_EQL_B5_GAIN_SHIFT, 31, 0, eq_tlv), +SND_SOC_BYTES("EQL Coefficients", WM8962_EQ4, 18), +SND_SOC_BYTES("EQR Coefficients", WM8962_EQ24, 18), + SOC_SINGLE("3D Switch", WM8962_THREED1, 0, 1, 0), SND_SOC_BYTES_MASK("3D Coefficients", WM8962_THREED1, 4, WM8962_THREED_ENA), -- cgit v0.10.2 From 0841c04d65937ad2808f59c43cb54a92473c8f0e Mon Sep 17 00:00:00 2001 From: "Luck, Tony" Date: Fri, 1 Nov 2013 13:59:52 -0700 Subject: dmi: Avoid unaligned memory access in save_mem_devices() Firmware is not required to maintain alignment of SMBIOS entries, so we should take care accessing fields within these structures. Use "get_unaligned()" to avoid problems. [ Found on ia64 (which grumbles about unaligned access) ] Signed-off-by: Tony Luck Cc: Chen Gong Link: http://lkml.kernel.org/r/27d82dbff5be1025bf18ab88498632d36c2fcf3c.1383331440.git.tony.luck@intel.com Signed-off-by: Ingo Molnar diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index 59579a7..c7e81ff 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -8,6 +8,7 @@ #include #include #include +#include /* * DMI stands for "Desktop Management Interface". It is part @@ -347,7 +348,7 @@ static void __init save_mem_devices(const struct dmi_header *dm, void *v) pr_warn(FW_BUG "Too many DIMM entries in SMBIOS table\n"); return; } - dmi_memdev[nr].handle = dm->handle; + dmi_memdev[nr].handle = get_unaligned(&dm->handle); dmi_memdev[nr].device = dmi_string(dm, d[0x10]); dmi_memdev[nr].bank = dmi_string(dm, d[0x11]); nr++; -- cgit v0.10.2 From f6f0747e5bc69401d7f90313aa1b46709d27840a Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 28 Oct 2013 13:12:35 -0500 Subject: of: Add empty for_each_available_child_of_node() macro definition Add this empty macro definition so users can be compiled without excluding this macro call with preprocessor directives when CONFIG_OF is disabled. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Rob Herring diff --git a/include/linux/of.h b/include/linux/of.h index 54017b8..b97f685 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -366,6 +366,9 @@ static inline bool of_have_populated_dt(void) #define for_each_child_of_node(parent, child) \ while (0) +#define for_each_available_child_of_node(parent, child) \ + while (0) + static inline struct device_node *of_get_child_by_name( const struct device_node *node, const char *name) -- cgit v0.10.2 From 67aeb39a173b5c172c75c5b9f1844853aec16eb4 Mon Sep 17 00:00:00 2001 From: Soren Brinkmann Date: Fri, 1 Nov 2013 08:42:04 -0700 Subject: of: Add vendor prefix for Cadence Drivers like clocksource/cadence_ttc and net/macb already use the 'cdns' prefix for Cadence IP. Signed-off-by: Soren Brinkmann Signed-off-by: Rob Herring diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 2956800..1ce180c 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -16,6 +16,7 @@ avago Avago Technologies bosch Bosch Sensortec GmbH brcm Broadcom Corporation cavium Cavium, Inc. +cdns Cadence Design Systems Inc. chrp Common Hardware Reference Platform cirrus Cirrus Logic, Inc. cortina Cortina Systems, Inc. -- cgit v0.10.2 From d6c3073e7fb5a9e9accfb11b07bad96022acf7c4 Mon Sep 17 00:00:00 2001 From: Silvio F Date: Sat, 2 Nov 2013 09:04:54 +0100 Subject: DT: sort vendor-prefixes.txt Signed-off-by: Silvio F Signed-off-by: Rob Herring diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 1ce180c..139b847 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -65,12 +65,12 @@ snps Synopsys, Inc. st STMicroelectronics ste ST-Ericsson stericsson ST-Ericsson -toumaz Toumaz ti Texas Instruments toshiba Toshiba Corporation +toumaz Toumaz v3 V3 Semiconductor via VIA Technologies, Inc. +winbond Winbond Electronics corp. wlf Wolfson Microelectronics wm Wondermedia Technologies, Inc. -winbond Winbond Electronics corp. xlnx Xilinx -- cgit v0.10.2 From 13ccacd5945aa5ce81d8566f57587e95fbd90742 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Thu, 17 Oct 2013 08:09:16 -0400 Subject: of: add vendor prefix for PHYTEC Messtechnik GmbH Adds PHYTEC to the list of DT vendor prefixes. Signed-off-by: Matt Porter Signed-off-by: Rob Herring diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 139b847..640e86d 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -46,6 +46,7 @@ nintendo Nintendo nvidia NVIDIA nxp NXP Semiconductors onnn ON Semiconductor Corp. +phytec PHYTEC Messtechnik GmbH picochip Picochip Ltd powervr PowerVR (deprecated, use img) qca Qualcomm Atheros, Inc. -- cgit v0.10.2 From 0589342c27944e50ebd7a54f5215002b6598b748 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 29 Oct 2013 23:36:46 -0500 Subject: of: set dma_mask to point to coherent_dma_mask Platform devices created by DT code don't initialize dma_mask pointer to anything. Set it to coherent_dma_mask by default if the architecture code has not set it. Signed-off-by: Rob Herring diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 9b439ac..c005495 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -216,6 +216,8 @@ static struct platform_device *of_platform_device_create_pdata( dev->archdata.dma_mask = 0xffffffffUL; #endif dev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + if (!dev->dev.dma_mask) + dev->dev.dma_mask = &dev->dev.coherent_dma_mask; dev->dev.bus = &platform_bus_type; dev->dev.platform_data = platform_data; -- cgit v0.10.2 From 78119fd1068cc068f6112a57a5f6de0e5b20245a Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Fri, 1 Nov 2013 10:50:50 -0700 Subject: of/irq: Fix bug in interrupt parsing refactor. Commit 2361613206e6, "of/irq: Refactor interrupt-map parsing" introduced a bug. The irq parsing will fail for some nodes that don't have a reg property. It is fixed by deferring the check for reg until it is actually needed. Also adjust the testcase data to catch the bug. Signed-off-by: Grant Likely Tested-by: Stephen Warren Tested-by: Ming Lei Tested-by: Stephen Warren Cc: Rob Herring diff --git a/arch/arm/boot/dts/testcases/tests-interrupts.dtsi b/arch/arm/boot/dts/testcases/tests-interrupts.dtsi index 560d6bf..c843720 100644 --- a/arch/arm/boot/dts/testcases/tests-interrupts.dtsi +++ b/arch/arm/boot/dts/testcases/tests-interrupts.dtsi @@ -2,7 +2,8 @@ / { testcase-data { interrupts { - #address-cells = <0>; + #address-cells = <1>; + #size-cells = <1>; test_intc0: intc0 { interrupt-controller; #interrupt-cells = <1>; @@ -29,8 +30,7 @@ test_intmap1: intmap1 { #interrupt-cells = <2>; - #address-cells = <0>; - interrupt-map = <1 2 &test_intc0 15>; + interrupt-map = <0x5000 1 2 &test_intc0 15>; }; interrupts0 { @@ -44,6 +44,7 @@ }; interrupts-extended0 { + reg = <0x5000 0x100>; interrupts-extended = <&test_intc0 1>, <&test_intc1 2 3 4>, <&test_intc2 5 6>, diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 8cc62b4..52dba6a 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -147,18 +147,9 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) pr_debug(" -> addrsize=%d\n", addrsize); - /* If we were passed no "reg" property and we attempt to parse - * an interrupt-map, then #address-cells must be 0. - * Fail if it's not. - */ - if (addr == NULL && addrsize != 0) { - pr_debug(" -> no reg passed in when needed !\n"); - return -EINVAL; - } - /* Precalculate the match array - this simplifies match loop */ for (i = 0; i < addrsize; i++) - initial_match_array[i] = addr[i]; + initial_match_array[i] = addr ? addr[i] : 0; for (i = 0; i < intsize; i++) initial_match_array[addrsize + i] = cpu_to_be32(out_irq->args[i]); @@ -174,6 +165,15 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) return 0; } + /* + * interrupt-map parsing does not work without a reg + * property when #address-cells != 0 + */ + if (addrsize && !addr) { + pr_debug(" -> no reg passed in when needed !\n"); + goto fail; + } + /* Now look for an interrupt-map */ imap = of_get_property(ipar, "interrupt-map", &imaplen); /* No interrupt map, check for an interrupt parent */ -- cgit v0.10.2 From 355e62f5ad12b005c862838156262eb2df2f8dff Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Sat, 2 Nov 2013 00:11:02 -0700 Subject: of/irq: Fix potential buffer overflow Commit 2361613206e6, "of/irq: Refactor interrupt-map parsing" introduced a potential buffer overflow bug because it doesn't do sufficient range checking on the input data. This patch adds the appropriate checking and buffer size adjustments. If the bounds are out of range then warn loudly. MAX_PHANDLE_ARGS should be sufficient. If it is not then the value can be increased. Signed-off-by: Grant Likely Cc: Rob Herring diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 52dba6a..d385bb8 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -95,9 +95,9 @@ struct device_node *of_irq_find_parent(struct device_node *child) int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) { struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL; - __be32 initial_match_array[8]; + __be32 initial_match_array[MAX_PHANDLE_ARGS]; const __be32 *match_array = initial_match_array; - const __be32 *tmp, *imap, *imask, dummy_imask[] = { ~0, ~0, ~0, ~0, ~0 }; + const __be32 *tmp, *imap, *imask, dummy_imask[] = { [0 ... MAX_PHANDLE_ARGS] = ~0 }; u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0; int imaplen, match, i; @@ -147,6 +147,10 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) pr_debug(" -> addrsize=%d\n", addrsize); + /* Range check so that the temporary buffer doesn't overflow */ + if (WARN_ON(addrsize + intsize > MAX_PHANDLE_ARGS)) + goto fail; + /* Precalculate the match array - this simplifies match loop */ for (i = 0; i < addrsize; i++) initial_match_array[i] = addr ? addr[i] : 0; @@ -229,6 +233,8 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) newintsize, newaddrsize); /* Check for malformed properties */ + if (WARN_ON(newaddrsize + newintsize > MAX_PHANDLE_ARGS)) + goto fail; if (imaplen < (newaddrsize + newintsize)) goto fail; -- cgit v0.10.2 From 611885bc963a2fa6fbd6141d149abbcce4dd8923 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sun, 3 Nov 2013 17:15:00 +0200 Subject: ALSA: hda - hdmi: Disallow unsupported 2ch remapping on NVIDIA codecs NVIDIA HDMI codecs do not seem to follow the Audio Sample Packet (ASP) channel mapping (as set by verb F32h per HDA specification 7.3.3.41) when playing back 2-channel audio (CEA CA 0x00). Basically this means that specifying swapped channels for stereo audio (FR,FL) does not take effect, and e.g. this command plays back on the wrong channel: speaker-test -c2 -Dhdmi:CARD=NVidia,DEV=0 -m FR,FL -s1 Multichannel audio is not affected. This issue has been confirmed to exist on codec 0x10de0015 by me and on 0x10de0040 by Juho Teperi. Disable 2ch FL/FR channel swapping on all NVIDIA HDMI codecs that use the standard HDA channel mapping system. Since this is a very minor functionality loss, we err on the side of disabling it for newer codecs as well until any future testing confirms that this issue has been fixed. Signed-off-by: Anssi Hannula Helped-by: Juho Teperi Cc: Aaron Plattner Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index d61cc2a..c3e3537 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2787,6 +2787,46 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) } /* + * NVIDIA codecs ignore ASP mapping for 2ch - confirmed on: + * - 0x10de0015 + * - 0x10de0040 + */ +static int nvhdmi_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap, + int channels) +{ + if (cap->ca_index == 0x00 && channels == 2) + return SNDRV_CTL_TLVT_CHMAP_FIXED; + + return hdmi_chmap_cea_alloc_validate_get_type(cap, channels); +} + +static int nvhdmi_chmap_validate(int ca, int chs, unsigned char *map) +{ + if (ca == 0x00 && (map[0] != SNDRV_CHMAP_FL || map[1] != SNDRV_CHMAP_FR)) + return -EINVAL; + + return 0; +} + +static int patch_nvhdmi(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + int err; + + err = patch_generic_hdmi(codec); + if (err) + return err; + + spec = codec->spec; + + spec->ops.chmap_cea_alloc_validate_get_type = + nvhdmi_chmap_cea_alloc_validate_get_type; + spec->ops.chmap_validate = nvhdmi_chmap_validate; + + return 0; +} + +/* * ATI/AMD-specific implementations */ @@ -3167,30 +3207,30 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = { { .id = 0x10de0005, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x }, { .id = 0x10de0006, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x }, { .id = 0x10de0007, .name = "MCP79/7A HDMI", .patch = patch_nvhdmi_8ch_7x }, -{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0015, .name = "GPU 15 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0016, .name = "GPU 16 HDMI/DP", .patch = patch_generic_hdmi }, +{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_nvhdmi }, +{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_nvhdmi }, +{ .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_nvhdmi }, +{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_nvhdmi }, +{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_nvhdmi }, +{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_nvhdmi }, +{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_nvhdmi }, +{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_nvhdmi }, +{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_nvhdmi }, +{ .id = 0x10de0015, .name = "GPU 15 HDMI/DP", .patch = patch_nvhdmi }, +{ .id = 0x10de0016, .name = "GPU 16 HDMI/DP", .patch = patch_nvhdmi }, /* 17 is known to be absent */ -{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0051, .name = "GPU 51 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x10de0060, .name = "GPU 60 HDMI/DP", .patch = patch_generic_hdmi }, +{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_nvhdmi }, +{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_nvhdmi }, +{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi }, +{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi }, +{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi }, +{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi }, +{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi }, +{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi }, +{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_nvhdmi }, +{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_nvhdmi }, +{ .id = 0x10de0051, .name = "GPU 51 HDMI/DP", .patch = patch_nvhdmi }, +{ .id = 0x10de0060, .name = "GPU 60 HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch }, { .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch }, { .id = 0x11069f80, .name = "VX900 HDMI/DP", .patch = patch_via_hdmi }, -- cgit v0.10.2 From 5e1109adde6acd0f3424886da09402ac22ed244b Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 31 Oct 2013 15:51:18 +0800 Subject: pinctrl: imx1: fix return value check in imx1_pinctrl_core_probe() In case of error, the function devm_ioremap_nocache() returns NULL pointer not ERR_PTR(). The IS_ERR() test in the return value check should be replaced with NULL test. Signed-off-by: Wei Yongjun Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/pinctrl-imx1-core.c b/drivers/pinctrl/pinctrl-imx1-core.c index 4d5a9e7..f77914a 100644 --- a/drivers/pinctrl/pinctrl-imx1-core.c +++ b/drivers/pinctrl/pinctrl-imx1-core.c @@ -615,8 +615,8 @@ int imx1_pinctrl_core_probe(struct platform_device *pdev, ipctl->base = devm_ioremap_nocache(&pdev->dev, res->start, resource_size(res)); - if (IS_ERR(ipctl->base)) - return PTR_ERR(ipctl->base); + if (!ipctl->base) + return -ENOMEM; pctl_desc = &imx1_pinctrl_desc; pctl_desc->name = dev_name(&pdev->dev); -- cgit v0.10.2 From a3183c60e3e9be7abd830ebed904491625e07d2e Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Mon, 28 Oct 2013 14:01:16 +0800 Subject: pinctrl: imx: fix using pin->input_val wrongly The commit: "pinctrl: imx: Use struct type for pins" relaced pin->input_reg by pin->input_val wrongly, fix it at this commit. Signed-off-by: Peter Chen Acked-by: Sascha Hauer Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/pinctrl-imx.c b/drivers/pinctrl/pinctrl-imx.c index d78dd81..4779b8e 100644 --- a/drivers/pinctrl/pinctrl-imx.c +++ b/drivers/pinctrl/pinctrl-imx.c @@ -245,11 +245,11 @@ static int imx_pmx_enable(struct pinctrl_dev *pctldev, unsigned selector, * The input_reg[i] here is actually some IOMUXC general * purpose register, not regular select input register. */ - val = readl(ipctl->base + pin->input_val); + val = readl(ipctl->base + pin->input_reg); val &= ~mask; val |= select << shift; - writel(val, ipctl->base + pin->input_val); - } else if (pin->input_val) { + writel(val, ipctl->base + pin->input_reg); + } else if (pin->input_reg) { /* * Regular select input register can never be at offset * 0, and we only print register value for regular case. -- cgit v0.10.2 From 808e657c5c709844074d47a4284c746070b0772c Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Tue, 29 Oct 2013 04:50:45 +0100 Subject: pinctrl: remove minor dead code This removes a test whether the 'desc' variable is NULL. This possibility has already been eliminated by the below test earlier in the loop: if (desc == NULL) { dev_warn(pctldev->dev, "could not get pin desc for pin %d\n", pins[i]); continue; } Found with Coverity: CID #1090078 Signed-off-by: Michael Opdenacker Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c index 9d144a2..9248ce4 100644 --- a/drivers/pinctrl/pinmux.c +++ b/drivers/pinctrl/pinmux.c @@ -505,16 +505,14 @@ void pinmux_disable_setting(struct pinctrl_setting const *setting) pin_free(pctldev, pins[i], NULL); } else { const char *gname; - const char *pname; - pname = desc ? desc->name : "non-existing"; gname = pctlops->get_group_name(pctldev, setting->data.mux.group); dev_warn(pctldev->dev, "not freeing pin %d (%s) as part of " "deactivating group %s - it is already " "used for some other setting", - pins[i], pname, gname); + pins[i], desc->name, gname); } } -- cgit v0.10.2 From 300016b960661b4df63690177b22ba5426ff5706 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Mon, 4 Nov 2013 01:13:13 -0500 Subject: ALSA: hda - rename function not_share_unassigned_cvt() The function name not_share_unassigned_cvt() is opposite to what it does. This patch renames it to intel_not_share_assigned_cvt(), and addes comments to explain why some Intel display codecs need this workaround. Signed-off-by: Mengdong Lin Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index c3e3537..e22323f 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1328,7 +1328,16 @@ static int hdmi_choose_cvt(struct hda_codec *codec, return 0; } -static void not_share_unassigned_cvt(struct hda_codec *codec, +/* Intel HDMI workaround to fix audio routing issue: + * For some Intel display codecs, pins share the same connection list. + * So a conveter can be selected by multiple pins and playback on any of these + * pins will generate sound on the external display, because audio flows from + * the same converter to the display pipeline. Also muting one pin may make + * other pins have no sound output. + * So this function assures that an assigned converter for a pin is not selected + * by any other pins. + */ +static void intel_not_share_assigned_cvt(struct hda_codec *codec, hda_nid_t pin_nid, int mux_idx) { struct hdmi_spec *spec = codec->spec; @@ -1408,7 +1417,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, /* configure unused pins to choose other converters */ if (is_haswell(codec) || is_valleyview(codec)) - not_share_unassigned_cvt(codec, per_pin->pin_nid, mux_idx); + intel_not_share_assigned_cvt(codec, per_pin->pin_nid, mux_idx); snd_hda_spdif_ctls_assign(codec, pin_idx, per_cvt->cvt_nid); -- cgit v0.10.2 From 9ef0438a957937bf0edc26d58bce891034ff9e30 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 24 Oct 2013 17:36:31 -0300 Subject: perf probe: Fix typo s/tyep/type/g. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-cznw5tnruonyoisxu8be11bv@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index e41b094..2200dad 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -583,7 +583,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, } if (die_find_member(&type, field->name, die_mem) == NULL) { - pr_warning("%s(tyep:%s) has no member %s.\n", varname, + pr_warning("%s(type:%s) has no member %s.\n", varname, dwarf_diename(&type), field->name); return -EINVAL; } -- cgit v0.10.2 From 6e6dc401d528e3b64626de82322fa237f1c1e576 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sat, 26 Oct 2013 20:53:14 +0200 Subject: perf tools: Add missing data.h into LIB_H headers Adding missing data.h into LIB_H headers so the build could keep up with its changes. Reported-by: Adrian Hunter Cc: Adrian Hunter Cc: Andi Kleen Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20131026185314.GA14973@krava.brq.redhat.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 8a9ca38..bc7cfa1 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -295,6 +295,7 @@ LIB_H += ui/helpline.h LIB_H += ui/progress.h LIB_H += ui/util.h LIB_H += ui/ui.h +LIB_H += util/data.h LIB_OBJS += $(OUTPUT)util/abspath.o LIB_OBJS += $(OUTPUT)util/alias.o -- cgit v0.10.2 From b9c5143a012a543c4ee872498d6dbae5c10beb2e Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 11 Sep 2013 14:46:56 +0200 Subject: perf tools: Use an accessor to read thread comm As the thread comm is going to be implemented by way of a more complicated data structure than just a pointer to a string from the thread struct, convert the readers of comm to use an accessor instead of accessing it directly. The accessor will be later overriden to support an enhanced comm implementation. Signed-off-by: Frederic Weisbecker Tested-by: Jiri Olsa Cc: Jiri Olsa Cc: David Ahern Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-wr683zwy94hmj4ibogmnv9ce@git.kernel.org [ Rename thread__comm_curr() to thread__comm_str() ] Signed-off-by: Namhyung Kim [ Fixed up some minor const pointer issues ] Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 1126382..a28970f 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -315,7 +315,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, return -1; } - dump_printf(" ... thread: %s:%d\n", thread->comm, thread->tid); + dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid); if (evsel->handler.func != NULL) { tracepoint_handler f = evsel->handler.func; diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 33c7253..35f9aaa 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -767,7 +767,7 @@ static void dump_threads(void) while (node) { st = container_of(node, struct thread_stat, rb); t = perf_session__findnew(session, st->tid); - pr_info("%10d: %s\n", st->tid, t->comm); + pr_info("%10d: %s\n", st->tid, thread__comm_str(t)); node = rb_next(node); }; } diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index ddb5dc1..a81ab18 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -737,12 +737,12 @@ static int replay_fork_event(struct perf_sched *sched, if (verbose) { printf("fork event\n"); - printf("... parent: %s/%d\n", parent->comm, parent->tid); - printf("... child: %s/%d\n", child->comm, child->tid); + printf("... parent: %s/%d\n", thread__comm_str(parent), parent->tid); + printf("... child: %s/%d\n", thread__comm_str(child), child->tid); } - register_pid(sched, parent->tid, parent->comm); - register_pid(sched, child->tid, child->comm); + register_pid(sched, parent->tid, thread__comm_str(parent)); + register_pid(sched, child->tid, thread__comm_str(child)); return 0; } @@ -1077,7 +1077,7 @@ static int latency_migrate_task_event(struct perf_sched *sched, if (!atoms) { if (thread_atoms_insert(sched, migrant)) return -1; - register_pid(sched, migrant->tid, migrant->comm); + register_pid(sched, migrant->tid, thread__comm_str(migrant)); atoms = thread_atoms_search(&sched->atom_root, migrant, &sched->cmp_pid); if (!atoms) { pr_err("migration-event: Internal tree error"); @@ -1111,13 +1111,13 @@ static void output_lat_thread(struct perf_sched *sched, struct work_atoms *work_ /* * Ignore idle threads: */ - if (!strcmp(work_list->thread->comm, "swapper")) + if (!strcmp(thread__comm_str(work_list->thread), "swapper")) return; sched->all_runtime += work_list->total_runtime; sched->all_count += work_list->nb_atoms; - ret = printf(" %s:%d ", work_list->thread->comm, work_list->thread->tid); + ret = printf(" %s:%d ", thread__comm_str(work_list->thread), work_list->thread->tid); for (i = 0; i < 24 - ret; i++) printf(" "); @@ -1334,7 +1334,7 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel, printf(" %12.6f secs ", (double)timestamp/1e9); if (new_shortname) { printf("%s => %s:%d\n", - sched_in->shortname, sched_in->comm, sched_in->tid); + sched_in->shortname, thread__comm_str(sched_in), sched_in->tid); } else { printf("\n"); } diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 0ae88c2..b866cc8 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -291,11 +291,11 @@ static void print_sample_start(struct perf_sample *sample, if (PRINT_FIELD(COMM)) { if (latency_format) - printf("%8.8s ", thread->comm); + printf("%8.8s ", thread__comm_str(thread)); else if (PRINT_FIELD(IP) && symbol_conf.use_callchain) - printf("%s ", thread->comm); + printf("%s ", thread__comm_str(thread)); else - printf("%16s ", thread->comm); + printf("%16s ", thread__comm_str(thread)); } if (PRINT_FIELD(PID) && PRINT_FIELD(TID)) diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index b51abcb..4475b0f 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c @@ -421,7 +421,7 @@ static void print_hists(struct hists *hists) he = rb_entry(node, struct hist_entry, rb_node_in); pr_info("%2d: entry: %-8s [%-8s] %20s: period = %"PRIu64"\n", - i, he->thread->comm, he->ms.map->dso->short_name, + i, thread__comm_str(he->thread), he->ms.map->dso->short_name, he->ms.sym->name, he->stat.period); i++; diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 7ef36c3..a91b6b2 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -1255,7 +1255,7 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size, if (thread) printed += scnprintf(bf + printed, size - printed, ", Thread: %s(%d)", - (thread->comm_set ? thread->comm : ""), + (thread->comm_set ? thread__comm_str(thread) : ""), thread->tid); if (dso) printed += scnprintf(bf + printed, size - printed, @@ -1578,7 +1578,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, if (thread != NULL && asprintf(&options[nr_options], "Zoom %s %s(%d) thread", (browser->hists->thread_filter ? "out of" : "into"), - (thread->comm_set ? thread->comm : ""), + (thread->comm_set ? thread__comm_str(thread) : ""), thread->tid) > 0) zoom_thread = nr_options++; @@ -1598,7 +1598,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, struct symbol *sym; if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]", - browser->he_selection->thread->comm) > 0) + thread__comm_str(browser->he_selection->thread)) > 0) scripts_comm = nr_options++; sym = browser->he_selection->ms.sym; @@ -1701,7 +1701,7 @@ zoom_out_thread: sort_thread.elide = false; } else { ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", - thread->comm_set ? thread->comm : "", + thread->comm_set ? thread__comm_str(thread) : "", thread->tid); browser->hists->thread_filter = thread; sort_thread.elide = true; @@ -1717,7 +1717,7 @@ do_scripts: memset(script_opt, 0, 64); if (choice == scripts_comm) - sprintf(script_opt, " -c %s ", browser->he_selection->thread->comm); + sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread)); if (choice == scripts_symbol) sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name); diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 49096ea..7a2842e 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -721,10 +721,10 @@ int perf_event__preprocess_sample(const union perf_event *event, return -1; if (symbol_conf.comm_list && - !strlist__has_entry(symbol_conf.comm_list, thread->comm)) + !strlist__has_entry(symbol_conf.comm_list, thread__comm_str(thread))) goto out_filtered; - dump_printf(" ... thread: %s:%d\n", thread->comm, thread->tid); + dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid); /* * Have we already created the kernel maps for this machine? * diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index c0c9795..d5e5969 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c @@ -273,7 +273,7 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused, int cpu = sample->cpu; void *data = sample->raw_data; unsigned long long nsecs = sample->time; - char *comm = thread->comm; + const char *comm = thread__comm_str(thread); dSP; diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 95d91a0..53c20e7 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -250,7 +250,7 @@ static void python_process_tracepoint(union perf_event *perf_event int cpu = sample->cpu; void *data = sample->raw_data; unsigned long long nsecs = sample->time; - char *comm = thread->comm; + const char *comm = thread__comm_str(thread); t = PyTuple_New(MAX_FIELDS); if (!t) @@ -389,7 +389,7 @@ static void python_process_general_event(union perf_event *perf_event pydict_set_item_string_decref(dict, "raw_buf", PyString_FromStringAndSize( (const char *)sample->raw_data, sample->raw_size)); pydict_set_item_string_decref(dict, "comm", - PyString_FromString(thread->comm)); + PyString_FromString(thread__comm_str(thread))); if (al->map) { pydict_set_item_string_decref(dict, "dso", PyString_FromString(al->map->dso->name)); diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 19b4aa2..835e8bd 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -42,7 +42,7 @@ static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) return n; } -static int64_t cmp_null(void *l, void *r) +static int64_t cmp_null(const void *l, const void *r) { if (!l && !r) return 0; @@ -63,8 +63,9 @@ sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { + const char *comm = thread__comm_str(he->thread); return repsep_snprintf(bf, size, "%*s:%5d", width - 6, - he->thread->comm ?: "", he->thread->tid); + comm ?: "", he->thread->tid); } struct sort_entry sort_thread = { @@ -85,8 +86,8 @@ sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) static int64_t sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) { - char *comm_l = left->thread->comm; - char *comm_r = right->thread->comm; + const char *comm_l = thread__comm_str(left->thread); + const char *comm_r = thread__comm_str(right->thread); if (!comm_l || !comm_r) return cmp_null(comm_l, comm_r); @@ -97,7 +98,7 @@ sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { - return repsep_snprintf(bf, size, "%*s", width, he->thread->comm); + return repsep_snprintf(bf, size, "%*s", width, thread__comm_str(he->thread)); } struct sort_entry sort_comm = { diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 80d19a0..5676007 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -45,6 +45,11 @@ int thread__set_comm(struct thread *thread, const char *comm) return err; } +const char *thread__comm_str(const struct thread *thread) +{ + return thread->comm; +} + int thread__comm_len(struct thread *thread) { if (!thread->comm_len) { @@ -58,7 +63,7 @@ int thread__comm_len(struct thread *thread) size_t thread__fprintf(struct thread *thread, FILE *fp) { - return fprintf(fp, "Thread %d %s\n", thread->tid, thread->comm) + + return fprintf(fp, "Thread %d %s\n", thread->tid, thread__comm_str(thread)) + map_groups__fprintf(&thread->mg, verbose, fp); } diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 4ebbb40..6561ad2 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -35,6 +35,7 @@ static inline void thread__exited(struct thread *thread) int thread__set_comm(struct thread *self, const char *comm); int thread__comm_len(struct thread *self); +const char *thread__comm_str(const struct thread *thread); void thread__insert_map(struct thread *self, struct map *map); int thread__fork(struct thread *self, struct thread *parent); size_t thread__fprintf(struct thread *thread, FILE *fp); -- cgit v0.10.2 From b400126add8fccf47ff663e5f20604e121f55f84 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 4 Nov 2013 09:22:01 +0100 Subject: CREDITS: Add Yoshinori Sato for h8300 Signed-off-by: Geert Uytterhoeven Signed-off-by: Guenter Roeck diff --git a/CREDITS b/CREDITS index 9416a9a..e84f4e9 100644 --- a/CREDITS +++ b/CREDITS @@ -3153,6 +3153,11 @@ N: Dipankar Sarma E: dipankar@in.ibm.com D: RCU +N: Yoshinori Sato +E: ysato@users.sourceforge.jp +D: uClinux for Renesas H8/300 (H8300) +D: http://uclinux-h8.sourceforge.jp/ + N: Hannu Savolainen E: hannu@opensound.com D: Maintainer of the sound drivers until 2.1.x days. -- cgit v0.10.2 From 162f0befda3becc2cc9f44075fccc030e55baec1 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 11 Sep 2013 16:18:24 +0200 Subject: perf tools: Add time argument on COMM setting This way we can later delimit a lifecycle for the COMM and map a hist to a precise COMM:timeslice couple. PERF_RECORD_COMM and PERF_RECORD_FORK events that don't have PERF_SAMPLE_TIME samples can only send 0 value as a timestamp and thus should overwrite any previous COMM on a given thread because there is no sensible way to keep track of all the comms lifecycles in a thread without time informations. Signed-off-by: Frederic Weisbecker Tested-by: Jiri Olsa Cc: Jiri Olsa Cc: David Ahern Cc: Ingo Molnar Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-6tyow99vgmmtt9qwr2u2lqd7@git.kernel.org [ Made it cope with PERF_RECORD_MMAP2 ] Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index a6ea956..21db76d 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -856,7 +856,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx) &sample, machine); } else if (event->header.type < PERF_RECORD_MAX) { hists__inc_nr_events(&evsel->hists, event->header.type); - machine__process_event(machine, event); + machine__process_event(machine, event, &sample); } else ++session->stats.nr_unknown_events; next_event: diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index dc3da65..95d6392 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1122,7 +1122,7 @@ static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thre } static int trace__process_event(struct trace *trace, struct machine *machine, - union perf_event *event) + union perf_event *event, struct perf_sample *sample) { int ret = 0; @@ -1130,9 +1130,9 @@ static int trace__process_event(struct trace *trace, struct machine *machine, case PERF_RECORD_LOST: color_fprintf(trace->output, PERF_COLOR_RED, "LOST %" PRIu64 " events!\n", event->lost.lost); - ret = machine__process_lost_event(machine, event); + ret = machine__process_lost_event(machine, event, sample); default: - ret = machine__process_event(machine, event); + ret = machine__process_event(machine, event, sample); break; } @@ -1141,11 +1141,11 @@ static int trace__process_event(struct trace *trace, struct machine *machine, static int trace__tool_process(struct perf_tool *tool, union perf_event *event, - struct perf_sample *sample __maybe_unused, + struct perf_sample *sample, struct machine *machine) { struct trace *trace = container_of(tool, struct trace, tool); - return trace__process_event(trace, machine, event); + return trace__process_event(trace, machine, event, sample); } static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) @@ -1751,7 +1751,7 @@ again: trace->base_time = sample.time; if (type != PERF_RECORD_SAMPLE) { - trace__process_event(trace, trace->host, event); + trace__process_event(trace, trace->host, event, &sample); continue; } diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index e3fedfa..49ccc3b 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -276,7 +276,7 @@ static int process_event(struct machine *machine, struct perf_evlist *evlist, return process_sample_event(machine, evlist, event, state); if (event->header.type < PERF_RECORD_MAX) - return machine__process_event(machine, event); + return machine__process_event(machine, event, NULL); return 0; } diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index 4475b0f..6c337e6 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c @@ -93,7 +93,7 @@ static struct machine *setup_fake_machine(struct machines *machines) if (thread == NULL) goto out; - thread__set_comm(thread, fake_threads[i].comm); + thread__set_comm(thread, fake_threads[i].comm, 0); } for (i = 0; i < ARRAY_SIZE(fake_mmap_info); i++) { @@ -110,7 +110,7 @@ static struct machine *setup_fake_machine(struct machines *machines) strcpy(fake_mmap_event.mmap.filename, fake_mmap_info[i].filename); - machine__process_mmap_event(machine, &fake_mmap_event); + machine__process_mmap_event(machine, &fake_mmap_event, NULL); } for (i = 0; i < ARRAY_SIZE(fake_symbols); i++) { diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 7a2842e..c26b353 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -512,18 +512,18 @@ size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp) int perf_event__process_comm(struct perf_tool *tool __maybe_unused, union perf_event *event, - struct perf_sample *sample __maybe_unused, + struct perf_sample *sample, struct machine *machine) { - return machine__process_comm_event(machine, event); + return machine__process_comm_event(machine, event, sample); } int perf_event__process_lost(struct perf_tool *tool __maybe_unused, union perf_event *event, - struct perf_sample *sample __maybe_unused, + struct perf_sample *sample, struct machine *machine) { - return machine__process_lost_event(machine, event); + return machine__process_lost_event(machine, event, sample); } size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp) @@ -546,18 +546,18 @@ size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp) int perf_event__process_mmap(struct perf_tool *tool __maybe_unused, union perf_event *event, - struct perf_sample *sample __maybe_unused, + struct perf_sample *sample, struct machine *machine) { - return machine__process_mmap_event(machine, event); + return machine__process_mmap_event(machine, event, sample); } int perf_event__process_mmap2(struct perf_tool *tool __maybe_unused, union perf_event *event, - struct perf_sample *sample __maybe_unused, + struct perf_sample *sample, struct machine *machine) { - return machine__process_mmap2_event(machine, event); + return machine__process_mmap2_event(machine, event, sample); } size_t perf_event__fprintf_task(union perf_event *event, FILE *fp) @@ -569,18 +569,18 @@ size_t perf_event__fprintf_task(union perf_event *event, FILE *fp) int perf_event__process_fork(struct perf_tool *tool __maybe_unused, union perf_event *event, - struct perf_sample *sample __maybe_unused, + struct perf_sample *sample, struct machine *machine) { - return machine__process_fork_event(machine, event); + return machine__process_fork_event(machine, event, sample); } int perf_event__process_exit(struct perf_tool *tool __maybe_unused, union perf_event *event, - struct perf_sample *sample __maybe_unused, + struct perf_sample *sample, struct machine *machine) { - return machine__process_exit_event(machine, event); + return machine__process_exit_event(machine, event, sample); } size_t perf_event__fprintf(union perf_event *event, FILE *fp) @@ -611,10 +611,10 @@ size_t perf_event__fprintf(union perf_event *event, FILE *fp) int perf_event__process(struct perf_tool *tool __maybe_unused, union perf_event *event, - struct perf_sample *sample __maybe_unused, + struct perf_sample *sample, struct machine *machine) { - return machine__process_event(machine, event); + return machine__process_event(machine, event, sample); } void thread__find_addr_map(struct thread *self, diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index ea93425..ce034c1 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -40,7 +40,7 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid) return -ENOMEM; snprintf(comm, sizeof(comm), "[guest/%d]", pid); - thread__set_comm(thread, comm); + thread__set_comm(thread, comm, 0); } return 0; @@ -331,7 +331,8 @@ struct thread *machine__find_thread(struct machine *machine, pid_t tid) return __machine__findnew_thread(machine, 0, tid, false); } -int machine__process_comm_event(struct machine *machine, union perf_event *event) +int machine__process_comm_event(struct machine *machine, union perf_event *event, + struct perf_sample *sample) { struct thread *thread = machine__findnew_thread(machine, event->comm.pid, @@ -340,7 +341,7 @@ int machine__process_comm_event(struct machine *machine, union perf_event *event if (dump_trace) perf_event__fprintf_comm(event, stdout); - if (thread == NULL || thread__set_comm(thread, event->comm.comm)) { + if (thread == NULL || thread__set_comm(thread, event->comm.comm, sample->time)) { dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); return -1; } @@ -349,7 +350,7 @@ int machine__process_comm_event(struct machine *machine, union perf_event *event } int machine__process_lost_event(struct machine *machine __maybe_unused, - union perf_event *event) + union perf_event *event, struct perf_sample *sample __maybe_unused) { dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n", event->lost.id, event->lost.lost); @@ -984,7 +985,8 @@ out_problem: } int machine__process_mmap2_event(struct machine *machine, - union perf_event *event) + union perf_event *event, + struct perf_sample *sample __maybe_unused) { u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; struct thread *thread; @@ -1031,7 +1033,8 @@ out_problem: return 0; } -int machine__process_mmap_event(struct machine *machine, union perf_event *event) +int machine__process_mmap_event(struct machine *machine, union perf_event *event, + struct perf_sample *sample __maybe_unused) { u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; struct thread *thread; @@ -1088,7 +1091,8 @@ static void machine__remove_thread(struct machine *machine, struct thread *th) list_add_tail(&th->node, &machine->dead_threads); } -int machine__process_fork_event(struct machine *machine, union perf_event *event) +int machine__process_fork_event(struct machine *machine, union perf_event *event, + struct perf_sample *sample) { struct thread *thread = machine__find_thread(machine, event->fork.tid); struct thread *parent = machine__findnew_thread(machine, @@ -1105,7 +1109,7 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event perf_event__fprintf_task(event, stdout); if (thread == NULL || parent == NULL || - thread__fork(thread, parent) < 0) { + thread__fork(thread, parent, sample->time) < 0) { dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n"); return -1; } @@ -1113,8 +1117,8 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event return 0; } -int machine__process_exit_event(struct machine *machine __maybe_unused, - union perf_event *event) +int machine__process_exit_event(struct machine *machine, union perf_event *event, + struct perf_sample *sample __maybe_unused) { struct thread *thread = machine__find_thread(machine, event->fork.tid); @@ -1127,23 +1131,24 @@ int machine__process_exit_event(struct machine *machine __maybe_unused, return 0; } -int machine__process_event(struct machine *machine, union perf_event *event) +int machine__process_event(struct machine *machine, union perf_event *event, + struct perf_sample *sample) { int ret; switch (event->header.type) { case PERF_RECORD_COMM: - ret = machine__process_comm_event(machine, event); break; + ret = machine__process_comm_event(machine, event, sample); break; case PERF_RECORD_MMAP: - ret = machine__process_mmap_event(machine, event); break; + ret = machine__process_mmap_event(machine, event, sample); break; case PERF_RECORD_MMAP2: - ret = machine__process_mmap2_event(machine, event); break; + ret = machine__process_mmap2_event(machine, event, sample); break; case PERF_RECORD_FORK: - ret = machine__process_fork_event(machine, event); break; + ret = machine__process_fork_event(machine, event, sample); break; case PERF_RECORD_EXIT: - ret = machine__process_exit_event(machine, event); break; + ret = machine__process_exit_event(machine, event, sample); break; case PERF_RECORD_LOST: - ret = machine__process_lost_event(machine, event); break; + ret = machine__process_lost_event(machine, event, sample); break; default: ret = -1; break; diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 4c1f5d5..2389ba8 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -40,13 +40,20 @@ struct map *machine__kernel_map(struct machine *machine, enum map_type type) struct thread *machine__find_thread(struct machine *machine, pid_t tid); -int machine__process_comm_event(struct machine *machine, union perf_event *event); -int machine__process_exit_event(struct machine *machine, union perf_event *event); -int machine__process_fork_event(struct machine *machine, union perf_event *event); -int machine__process_lost_event(struct machine *machine, union perf_event *event); -int machine__process_mmap_event(struct machine *machine, union perf_event *event); -int machine__process_mmap2_event(struct machine *machine, union perf_event *event); -int machine__process_event(struct machine *machine, union perf_event *event); +int machine__process_comm_event(struct machine *machine, union perf_event *event, + struct perf_sample *sample); +int machine__process_exit_event(struct machine *machine, union perf_event *event, + struct perf_sample *sample); +int machine__process_fork_event(struct machine *machine, union perf_event *event, + struct perf_sample *sample); +int machine__process_lost_event(struct machine *machine, union perf_event *event, + struct perf_sample *sample); +int machine__process_mmap_event(struct machine *machine, union perf_event *event, + struct perf_sample *sample); +int machine__process_mmap2_event(struct machine *machine, union perf_event *event, + struct perf_sample *sample); +int machine__process_event(struct machine *machine, union perf_event *event, + struct perf_sample *sample); typedef void (*machine__process_t)(struct machine *machine, void *data); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 4ba7b54..3c1b301 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1100,7 +1100,7 @@ static struct thread *perf_session__register_idle_thread(struct perf_session *se { struct thread *thread = perf_session__findnew(self, 0); - if (thread == NULL || thread__set_comm(thread, "swapper")) { + if (thread == NULL || thread__set_comm(thread, "swapper", 0)) { pr_err("problem inserting idle task.\n"); thread = NULL; } diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 5676007..0ea73fe 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -31,7 +31,8 @@ void thread__delete(struct thread *thread) free(thread); } -int thread__set_comm(struct thread *thread, const char *comm) +int thread__set_comm(struct thread *thread, const char *comm, + u64 timestamp __maybe_unused) { int err; @@ -73,7 +74,8 @@ void thread__insert_map(struct thread *thread, struct map *map) map_groups__insert(&thread->mg, map); } -int thread__fork(struct thread *thread, struct thread *parent) +int thread__fork(struct thread *thread, struct thread *parent, + u64 timestamp __maybe_unused) { int i; diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 6561ad2..4e97242 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -33,11 +33,11 @@ static inline void thread__exited(struct thread *thread) thread->dead = true; } -int thread__set_comm(struct thread *self, const char *comm); +int thread__set_comm(struct thread *thread, const char *comm, u64 timestamp); int thread__comm_len(struct thread *self); const char *thread__comm_str(const struct thread *thread); void thread__insert_map(struct thread *self, struct map *map); -int thread__fork(struct thread *self, struct thread *parent); +int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp); size_t thread__fprintf(struct thread *thread, FILE *fp); static inline struct map *thread__find_map(struct thread *self, -- cgit v0.10.2 From 1902efe7f626fdebe1520f5ff11f1309ec506708 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 11 Sep 2013 16:56:44 +0200 Subject: perf tools: Add new COMM infrastructure This new COMM infrastructure provides two features: 1) It keeps track of all comms lifecycle for a given thread. This way we can associate a timeframe to any thread COMM, as long as PERF_SAMPLE_TIME samples are joined to COMM and fork events. As a result we should have more precise COMM sorted hists with seperated entries for pre and post exec time after a fork. 2) It also makes sure that a given COMM string is not duplicated but rather shared among the threads that refer to it. This way the threads COMM can be compared against pointer values from the sort infrastructure. Signed-off-by: Frederic Weisbecker Tested-by: Jiri Olsa Cc: Jiri Olsa Cc: David Ahern Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-hwjf70b2wve9m2kosxiq8bb3@git.kernel.org [ Rename some accessor functions ] Signed-off-by: Namhyung Kim [ Use __ as separator for class__method for private comm_str methods ] Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index bc7cfa1..cb52bdb 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -273,6 +273,7 @@ LIB_H += util/color.h LIB_H += util/values.h LIB_H += util/sort.h LIB_H += util/hist.h +LIB_H += util/comm.h LIB_H += util/thread.h LIB_H += util/thread_map.h LIB_H += util/trace-event.h @@ -341,6 +342,7 @@ LIB_OBJS += $(OUTPUT)util/machine.o LIB_OBJS += $(OUTPUT)util/map.o LIB_OBJS += $(OUTPUT)util/pstack.o LIB_OBJS += $(OUTPUT)util/session.o +LIB_OBJS += $(OUTPUT)util/comm.o LIB_OBJS += $(OUTPUT)util/thread.o LIB_OBJS += $(OUTPUT)util/thread_map.o LIB_OBJS += $(OUTPUT)util/trace-event-parse.o diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 95d6392..b3e57dc 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1114,7 +1114,7 @@ static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thre if (trace->multiple_threads) { if (trace->show_comm) - printed += fprintf(fp, "%.14s/", thread->comm); + printed += fprintf(fp, "%.14s/", thread__comm_str(thread)); printed += fprintf(fp, "%d ", thread->tid); } @@ -1986,7 +1986,7 @@ static int trace__fprintf_one_thread(struct thread *thread, void *priv) else if (ratio > 5.0) color = PERF_COLOR_YELLOW; - printed += color_fprintf(fp, color, "%20s", thread->comm); + printed += color_fprintf(fp, color, "%20s", thread__comm_str(thread)); printed += fprintf(fp, " - %-5d :%11lu [", thread->tid, ttrace->nr_events); printed += color_fprintf(fp, color, "%5.1f%%", ratio); printed += fprintf(fp, " ] %10.3f ms\n", ttrace->runtime_ms); diff --git a/tools/perf/util/comm.c b/tools/perf/util/comm.c new file mode 100644 index 0000000..8b3ac9f --- /dev/null +++ b/tools/perf/util/comm.c @@ -0,0 +1,106 @@ +#include "comm.h" +#include "util.h" +#include +#include + +struct comm_str { + char *str; + struct rb_node rb_node; + int ref; +}; + +/* Should perhaps be moved to struct machine */ +static struct rb_root comm_str_root; + +static void comm_str__get(struct comm_str *cs) +{ + cs->ref++; +} + +static void comm_str__put(struct comm_str *cs) +{ + if (!--cs->ref) { + rb_erase(&cs->rb_node, &comm_str_root); + free(cs->str); + free(cs); + } +} + +static struct comm_str *comm_str__alloc(const char *str) +{ + struct comm_str *cs; + + cs = zalloc(sizeof(*cs)); + if (!cs) + return NULL; + + cs->str = strdup(str); + if (!cs->str) { + free(cs); + return NULL; + } + + return cs; +} + +static struct comm_str *comm_str__findnew(const char *str, struct rb_root *root) +{ + struct rb_node **p = &root->rb_node; + struct rb_node *parent = NULL; + struct comm_str *iter, *new; + int cmp; + + while (*p != NULL) { + parent = *p; + iter = rb_entry(parent, struct comm_str, rb_node); + + cmp = strcmp(str, iter->str); + if (!cmp) + return iter; + + if (cmp < 0) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + + new = comm_str__alloc(str); + if (!new) + return NULL; + + rb_link_node(&new->rb_node, parent, p); + rb_insert_color(&new->rb_node, root); + + return new; +} + +struct comm *comm__new(const char *str, u64 timestamp) +{ + struct comm *comm = zalloc(sizeof(*comm)); + + if (!comm) + return NULL; + + comm->start = timestamp; + + comm->comm_str = comm_str__findnew(str, &comm_str_root); + if (!comm->comm_str) { + free(comm); + return NULL; + } + + comm_str__get(comm->comm_str); + + return comm; +} + +void comm__free(struct comm *comm) +{ + comm_str__put(comm->comm_str); + free(comm); +} + +const char *comm__str(const struct comm *comm) +{ + return comm->comm_str->str; +} diff --git a/tools/perf/util/comm.h b/tools/perf/util/comm.h new file mode 100644 index 0000000..f62d215 --- /dev/null +++ b/tools/perf/util/comm.h @@ -0,0 +1,20 @@ +#ifndef __PERF_COMM_H +#define __PERF_COMM_H + +#include "../perf.h" +#include +#include + +struct comm_str; + +struct comm { + struct comm_str *comm_str; + u64 start; + struct list_head list; +}; + +void comm__free(struct comm *comm); +struct comm *comm__new(const char *str, u64 timestamp); +const char *comm__str(const struct comm *comm); + +#endif /* __PERF_COMM_H */ diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 0ea73fe..15c53c2 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -6,9 +6,12 @@ #include "thread.h" #include "util.h" #include "debug.h" +#include "comm.h" struct thread *thread__new(pid_t pid, pid_t tid) { + char *comm_str; + struct comm *comm; struct thread *thread = zalloc(sizeof(*thread)); if (thread != NULL) { @@ -16,47 +19,88 @@ struct thread *thread__new(pid_t pid, pid_t tid) thread->pid_ = pid; thread->tid = tid; thread->ppid = -1; - thread->comm = malloc(32); - if (thread->comm) - snprintf(thread->comm, 32, ":%d", thread->tid); + INIT_LIST_HEAD(&thread->comm_list); + + comm_str = malloc(32); + if (!comm_str) + goto err_thread; + + snprintf(comm_str, 32, ":%d", tid); + comm = comm__new(comm_str, 0); + free(comm_str); + if (!comm) + goto err_thread; + + list_add(&comm->list, &thread->comm_list); } return thread; + +err_thread: + free(thread); + return NULL; } void thread__delete(struct thread *thread) { + struct comm *comm, *tmp; + map_groups__exit(&thread->mg); - free(thread->comm); + list_for_each_entry_safe(comm, tmp, &thread->comm_list, list) { + list_del(&comm->list); + comm__free(comm); + } + free(thread); } -int thread__set_comm(struct thread *thread, const char *comm, - u64 timestamp __maybe_unused) +static struct comm *thread__comm(const struct thread *thread) { - int err; + if (list_empty(&thread->comm_list)) + return NULL; - if (thread->comm) - free(thread->comm); - thread->comm = strdup(comm); - err = thread->comm == NULL ? -ENOMEM : 0; - if (!err) { - thread->comm_set = true; + return list_first_entry(&thread->comm_list, struct comm, list); +} + +/* CHECKME: time should always be 0 if event aren't ordered */ +int thread__set_comm(struct thread *thread, const char *str, u64 timestamp) +{ + struct comm *new, *curr = thread__comm(thread); + + /* Override latest entry if it had no specific time coverage */ + if (!curr->start) { + list_del(&curr->list); + comm__free(curr); } - return err; + + new = comm__new(str, timestamp); + if (!new) + return -ENOMEM; + + list_add(&new->list, &thread->comm_list); + thread->comm_set = true; + + return 0; } const char *thread__comm_str(const struct thread *thread) { - return thread->comm; + const struct comm *comm = thread__comm(thread); + + if (!comm) + return NULL; + + return comm__str(comm); } +/* CHECKME: it should probably better return the max comm len from its comm list */ int thread__comm_len(struct thread *thread) { if (!thread->comm_len) { - if (!thread->comm) + const char *comm = thread__comm_str(thread); + if (!comm) return 0; - thread->comm_len = strlen(thread->comm); + thread->comm_len = strlen(comm); } return thread->comm_len; @@ -74,17 +118,17 @@ void thread__insert_map(struct thread *thread, struct map *map) map_groups__insert(&thread->mg, map); } -int thread__fork(struct thread *thread, struct thread *parent, - u64 timestamp __maybe_unused) +int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) { - int i; + int i, err; if (parent->comm_set) { - if (thread->comm) - free(thread->comm); - thread->comm = strdup(parent->comm); - if (!thread->comm) + const char *comm = thread__comm_str(parent); + if (!comm) return -ENOMEM; + err = thread__set_comm(thread, comm, timestamp); + if (!err) + return err; thread->comm_set = true; } diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 4e97242..8702c6b 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -2,6 +2,7 @@ #define __PERF_THREAD_H #include +#include #include #include #include "symbol.h" @@ -18,7 +19,7 @@ struct thread { char shortname[3]; bool comm_set; bool dead; /* if set thread has exited */ - char *comm; + struct list_head comm_list; int comm_len; void *priv; -- cgit v0.10.2 From fedd63d3cdc9004df43b02df5c874b8957992fe8 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 11 Sep 2013 17:18:09 +0200 Subject: perf tools: Compare hists comm by addresses Now that comm strings are allocated only once and refcounted to be shared among threads, these can now be safely compared by addresses. This should remove most hists collapses on post processing. Signed-off-by: Frederic Weisbecker Tested-by: Jiri Olsa Cc: Jiri Olsa Cc: David Ahern Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1381468543-25334-8-git-send-email-namhyung@kernel.org Signed-off-by: Namhyung Kim diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 835e8bd..bf91d0e 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -80,7 +80,8 @@ struct sort_entry sort_thread = { static int64_t sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) { - return right->thread->tid - left->thread->tid; + /* Compare the addr that should be unique among comm */ + return thread__comm_str(right->thread) - thread__comm_str(left->thread); } static int64_t -- cgit v0.10.2 From 4dfced359fbc719a35527416f1b4b3999647f68b Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 13 Sep 2013 16:28:57 +0900 Subject: perf tools: Get current comm instead of last one At insert time, a hist entry should reference comm at the time otherwise it'll get the last comm anyway. Signed-off-by: Namhyung Kim Acked-by: Frederic Weisbecker Tested-by: Jiri Olsa Cc: Frederic Weisbecker Link: http://lkml.kernel.org/n/tip-n6pykiiymtgmcjs834go2t8x@git.kernel.org [ Fixed up const pointer issues ] Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/comm.c b/tools/perf/util/comm.c index 8b3ac9f..ee0df0e 100644 --- a/tools/perf/util/comm.c +++ b/tools/perf/util/comm.c @@ -94,6 +94,21 @@ struct comm *comm__new(const char *str, u64 timestamp) return comm; } +void comm__override(struct comm *comm, const char *str, u64 timestamp) +{ + struct comm_str *old = comm->comm_str; + + comm->comm_str = comm_str__findnew(str, &comm_str_root); + if (!comm->comm_str) { + comm->comm_str = old; + return; + } + + comm->start = timestamp; + comm_str__get(comm->comm_str); + comm_str__put(old); +} + void comm__free(struct comm *comm) { comm_str__put(comm->comm_str); diff --git a/tools/perf/util/comm.h b/tools/perf/util/comm.h index f62d215..7a86e56 100644 --- a/tools/perf/util/comm.h +++ b/tools/perf/util/comm.h @@ -16,5 +16,6 @@ struct comm { void comm__free(struct comm *comm); struct comm *comm__new(const char *str, u64 timestamp); const char *comm__str(const struct comm *comm); +void comm__override(struct comm *comm, const char *str, u64 timestamp); #endif /* __PERF_COMM_H */ diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 7e80253..30793f9 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -416,6 +416,7 @@ struct hist_entry *__hists__add_mem_entry(struct hists *hists, { struct hist_entry entry = { .thread = al->thread, + .comm = thread__comm(al->thread), .ms = { .map = al->map, .sym = al->sym, @@ -446,6 +447,7 @@ struct hist_entry *__hists__add_branch_entry(struct hists *hists, { struct hist_entry entry = { .thread = al->thread, + .comm = thread__comm(al->thread), .ms = { .map = bi->to.map, .sym = bi->to.sym, @@ -475,6 +477,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists, { struct hist_entry entry = { .thread = al->thread, + .comm = thread__comm(al->thread), .ms = { .map = al->map, .sym = al->sym, diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index bf91d0e..3c1b75c 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1,5 +1,6 @@ #include "sort.h" #include "hist.h" +#include "comm.h" #include "symbol.h" regex_t parent_regex; @@ -81,25 +82,20 @@ static int64_t sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) { /* Compare the addr that should be unique among comm */ - return thread__comm_str(right->thread) - thread__comm_str(left->thread); + return comm__str(right->comm) - comm__str(left->comm); } static int64_t sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) { - const char *comm_l = thread__comm_str(left->thread); - const char *comm_r = thread__comm_str(right->thread); - - if (!comm_l || !comm_r) - return cmp_null(comm_l, comm_r); - - return strcmp(comm_l, comm_r); + /* Compare the addr that should be unique among comm */ + return comm__str(right->comm) - comm__str(left->comm); } static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { - return repsep_snprintf(bf, size, "%*s", width, thread__comm_str(he->thread)); + return repsep_snprintf(bf, size, "%*s", width, comm__str(he->comm)); } struct sort_entry sort_comm = { diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index bf43336..f4e16f3 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -84,6 +84,7 @@ struct hist_entry { struct he_stat stat; struct map_symbol ms; struct thread *thread; + struct comm *comm; u64 ip; u64 transaction; s32 cpu; diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 15c53c2..cd8e2f5 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -54,7 +54,7 @@ void thread__delete(struct thread *thread) free(thread); } -static struct comm *thread__comm(const struct thread *thread) +struct comm *thread__comm(const struct thread *thread) { if (list_empty(&thread->comm_list)) return NULL; @@ -69,8 +69,8 @@ int thread__set_comm(struct thread *thread, const char *str, u64 timestamp) /* Override latest entry if it had no specific time coverage */ if (!curr->start) { - list_del(&curr->list); - comm__free(curr); + comm__override(curr, str, timestamp); + return 0; } new = comm__new(str, timestamp); diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 8702c6b..373c055 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -26,6 +26,7 @@ struct thread { }; struct machine; +struct comm; struct thread *thread__new(pid_t pid, pid_t tid); void thread__delete(struct thread *self); @@ -36,6 +37,7 @@ static inline void thread__exited(struct thread *thread) int thread__set_comm(struct thread *thread, const char *comm, u64 timestamp); int thread__comm_len(struct thread *self); +struct comm *thread__comm(const struct thread *thread); const char *thread__comm_str(const struct thread *thread); void thread__insert_map(struct thread *self, struct map *map); int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp); -- cgit v0.10.2 From f852fd621ca19f557f2e3d05900366be7c7afb83 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 1 Nov 2013 15:51:29 +0200 Subject: perf evsel: Add a debug print if perf_event_open fails There is a debug print (at verbose level 2) for each call to perf_event_open. Add another debug print if the call fails, and print the error number. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1383313899-15987-2-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 3a334f0..f0e65de 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1051,6 +1051,8 @@ retry_open: group_fd, flags); if (FD(evsel, cpu, thread) < 0) { err = -errno; + pr_debug2("perf_event_open failed, error %d\n", + err); goto try_fallback; } set_rlimit = NO_CHANGE; -- cgit v0.10.2 From 7ea95727af571d592c9d6aa7627690d44b114a2d Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 1 Nov 2013 15:51:30 +0200 Subject: perf script: Set up output options for in-stream attributes Attributes (struct perf_event_attr) are recorded separately in the perf.data file. perf script uses them to set up output options. However attributes can also be in the event stream, for example when the input is a pipe (i.e. live mode). This patch makes perf script process in-stream attributes in the same way as on-file attributes. Here is an example: Before this patch: $ perf record uname | perf script Linux [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.015 MB (null) (~655 samples) ] :4220 4220 [-01] 2933367.838906: cycles: :4220 4220 [-01] 2933367.838910: cycles: :4220 4220 [-01] 2933367.838912: cycles: :4220 4220 [-01] 2933367.838914: cycles: :4220 4220 [-01] 2933367.838916: cycles: :4220 4220 [-01] 2933367.838918: cycles: uname 4220 [-01] 2933367.838938: cycles: uname 4220 [-01] 2933367.839207: cycles: After this patch: $ perf record uname | perf script Linux [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.015 MB (null) (~655 samples) ] :4582 4582 2933425.707724: cycles: ffffffff81043ffa native_write_msr_safe ([kernel.kallsyms]) :4582 4582 2933425.707728: cycles: ffffffff81043ffa native_write_msr_safe ([kernel.kallsyms]) :4582 4582 2933425.707730: cycles: ffffffff81043ffa native_write_msr_safe ([kernel.kallsyms]) :4582 4582 2933425.707732: cycles: ffffffff81043ffa native_write_msr_safe ([kernel.kallsyms]) :4582 4582 2933425.707734: cycles: ffffffff81043ffa native_write_msr_safe ([kernel.kallsyms]) :4582 4582 2933425.707736: cycles: ffffffff81309a24 memcpy ([kernel.kallsyms]) uname 4582 2933425.707760: cycles: ffffffff8109c1c7 enqueue_task_fair ([kernel.kallsyms]) uname 4582 2933425.707978: cycles: ffffffff81308457 clear_page_c ([kernel.kallsyms]) Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1383313899-15987-3-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index b866cc8..baf1798 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -229,6 +229,24 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel, return 0; } +static void set_print_ip_opts(struct perf_event_attr *attr) +{ + unsigned int type = attr->type; + + output[type].print_ip_opts = 0; + if (PRINT_FIELD(IP)) + output[type].print_ip_opts |= PRINT_IP_OPT_IP; + + if (PRINT_FIELD(SYM)) + output[type].print_ip_opts |= PRINT_IP_OPT_SYM; + + if (PRINT_FIELD(DSO)) + output[type].print_ip_opts |= PRINT_IP_OPT_DSO; + + if (PRINT_FIELD(SYMOFFSET)) + output[type].print_ip_opts |= PRINT_IP_OPT_SYMOFFSET; +} + /* * verify all user requested events exist and the samples * have the expected data @@ -237,7 +255,6 @@ static int perf_session__check_output_opt(struct perf_session *session) { int j; struct perf_evsel *evsel; - struct perf_event_attr *attr; for (j = 0; j < PERF_TYPE_MAX; ++j) { evsel = perf_session__find_first_evtype(session, j); @@ -260,20 +277,7 @@ static int perf_session__check_output_opt(struct perf_session *session) if (evsel == NULL) continue; - attr = &evsel->attr; - - output[j].print_ip_opts = 0; - if (PRINT_FIELD(IP)) - output[j].print_ip_opts |= PRINT_IP_OPT_IP; - - if (PRINT_FIELD(SYM)) - output[j].print_ip_opts |= PRINT_IP_OPT_SYM; - - if (PRINT_FIELD(DSO)) - output[j].print_ip_opts |= PRINT_IP_OPT_DSO; - - if (PRINT_FIELD(SYMOFFSET)) - output[j].print_ip_opts |= PRINT_IP_OPT_SYMOFFSET; + set_print_ip_opts(&evsel->attr); } return 0; @@ -547,6 +551,34 @@ struct perf_script { struct perf_session *session; }; +static int process_attr(struct perf_tool *tool, union perf_event *event, + struct perf_evlist **pevlist) +{ + struct perf_script *scr = container_of(tool, struct perf_script, tool); + struct perf_evlist *evlist; + struct perf_evsel *evsel, *pos; + int err; + + err = perf_event__process_attr(tool, event, pevlist); + if (err) + return err; + + evlist = *pevlist; + evsel = perf_evlist__last(*pevlist); + + if (evsel->attr.type >= PERF_TYPE_MAX) + return 0; + + list_for_each_entry(pos, &evlist->entries, node) { + if (pos->attr.type == evsel->attr.type && pos != evsel) + return 0; + } + + set_print_ip_opts(&evsel->attr); + + return perf_evsel__check_attr(evsel, scr->session); +} + static void sig_handler(int sig __maybe_unused) { session_done = 1; @@ -1272,7 +1304,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) .comm = perf_event__process_comm, .exit = perf_event__process_exit, .fork = perf_event__process_fork, - .attr = perf_event__process_attr, + .attr = process_attr, .tracing_data = perf_event__process_tracing_data, .build_id = perf_event__process_build_id, .ordered_samples = true, -- cgit v0.10.2 From 28e962b9d79f496f214d7fc8ffd1a3f2a67e9090 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 1 Nov 2013 15:51:31 +0200 Subject: perf tools: Fix 32-bit cross build Setting EXTRA_CFLAGS=-m32 did not work because it was not passed around. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1383313899-15987-4-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index cb52bdb..5b86390 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -711,7 +711,7 @@ $(LIB_FILE): $(LIB_OBJS) TE_SOURCES = $(wildcard $(TRACE_EVENT_DIR)*.[ch]) $(LIBTRACEEVENT): $(TE_SOURCES) - $(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) libtraceevent.a + $(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) CFLAGS="-g -Wall $(EXTRA_CFLAGS)" libtraceevent.a $(LIBTRACEEVENT)-clean: $(call QUIET_CLEAN, libtraceevent) diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 543aa95..2f1d7d7 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -96,7 +96,7 @@ endif feature_check = $(eval $(feature_check_code)) define feature_check_code - feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) LDFLAGS=$(LDFLAGS) -C config/feature-checks test-$1 >/dev/null 2>/dev/null && echo 1 || echo 0) + feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS)" LDFLAGS=$(LDFLAGS) -C config/feature-checks test-$1 >/dev/null 2>/dev/null && echo 1 || echo 0) endef feature_set = $(eval $(feature_set_code)) @@ -173,7 +173,7 @@ ifeq ($(feature-all), 1) # $(foreach feat,$(CORE_FEATURE_TESTS),$(call feature_set,$(feat))) else - $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) LDFLAGS=$(LDFLAGS) -i -j -C config/feature-checks $(CORE_FEATURE_TESTS) >/dev/null 2>&1) + $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS)" LDFLAGS=$(LDFLAGS) -i -j -C config/feature-checks $(CORE_FEATURE_TESTS) >/dev/null 2>&1) $(foreach feat,$(CORE_FEATURE_TESTS),$(call feature_check,$(feat))) endif diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 452b67c..353c00c 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -31,7 +31,7 @@ CC := $(CC) -MD all: $(FILES) -BUILD = $(CC) $(LDFLAGS) -o $(OUTPUT)$@ $@.c +BUILD = $(CC) $(CFLAGS) $(LDFLAGS) -o $(OUTPUT)$@ $@.c ############################### -- cgit v0.10.2 From 8a0c4c2843d3b72e23c3c12079b8d5c3ae99f3e3 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 1 Nov 2013 15:51:32 +0200 Subject: perf tools: Fix libunwind build and feature detection for 32-bit build Use -lunwind-x86 instead of -lunwind-x86_64 for 32-bit build. Signed-off-by: Adrian Hunter Acked-by: Jiri Olsa Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1383313899-15987-5-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 2f1d7d7..ffb5f55 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -25,9 +25,11 @@ ifeq ($(ARCH),x86_64) RAW_ARCH := x86_64 CFLAGS += -DHAVE_ARCH_X86_64_SUPPORT ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S ../../arch/x86/lib/memset_64.S + LIBUNWIND_LIBS = -lunwind -lunwind-x86_64 + else + LIBUNWIND_LIBS = -lunwind -lunwind-x86 endif NO_PERF_REGS := 0 - LIBUNWIND_LIBS = -lunwind -lunwind-x86_64 endif ifeq ($(NO_PERF_REGS),0) @@ -96,7 +98,7 @@ endif feature_check = $(eval $(feature_check_code)) define feature_check_code - feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS)" LDFLAGS=$(LDFLAGS) -C config/feature-checks test-$1 >/dev/null 2>/dev/null && echo 1 || echo 0) + feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS)" LDFLAGS=$(LDFLAGS) LIBUNWIND_LIBS="$(LIBUNWIND_LIBS)" -C config/feature-checks test-$1 >/dev/null 2>/dev/null && echo 1 || echo 0) endef feature_set = $(eval $(feature_set_code)) diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 353c00c..d37d58d 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -36,7 +36,7 @@ BUILD = $(CC) $(CFLAGS) $(LDFLAGS) -o $(OUTPUT)$@ $@.c ############################### test-all: - $(BUILD) -Werror -fstack-protector -fstack-protector-all -O2 -Werror -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lunwind -lunwind-x86_64 -lelf -laudit -I/usr/include/slang -lslang $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl + $(BUILD) -Werror -fstack-protector -fstack-protector-all -O2 -Werror -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma $(LIBUNWIND_LIBS) -lelf -laudit -I/usr/include/slang -lslang $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl test-hello: $(BUILD) @@ -72,7 +72,7 @@ test-libnuma: $(BUILD) -lnuma test-libunwind: - $(BUILD) -lunwind -lunwind-x86_64 -lelf + $(BUILD) $(LIBUNWIND_LIBS) -lelf test-libaudit: $(BUILD) -laudit -- cgit v0.10.2 From 026359658aecd348bc5c4a136a26f204b169103b Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 1 Nov 2013 15:51:33 +0200 Subject: perf evlist: Add a debug print if event buffer mmap fails Add a debug print if mmap of the perf event ring buffer fails. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1383313899-15987-6-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 0582f67..1c173cc 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -607,6 +607,8 @@ static int __perf_evlist__mmap(struct perf_evlist *evlist, evlist->mmap[idx].base = mmap(NULL, evlist->mmap_len, prot, MAP_SHARED, fd, 0); if (evlist->mmap[idx].base == MAP_FAILED) { + pr_debug2("failed to mmap perf event ring buffer, error %d\n", + errno); evlist->mmap[idx].base = NULL; return -1; } -- cgit v0.10.2 From 1e7ed5ec54e3998bda6ea625599a0644404cb421 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 1 Nov 2013 15:51:35 +0200 Subject: perf evsel: Always use perf_evsel__set_sample_bit() Always use perf_evsel__set_sample_bit() rather than just setting the bit. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1383313899-15987-8-git-send-email-adrian.hunter@intel.com [ Cope with 3090ffb "perf: Disable PERF_RECORD_MMAP2 support" ] Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index f0e65de..47bbf03 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -663,7 +663,7 @@ void perf_evsel__config(struct perf_evsel *evsel, } if (opts->sample_address) - attr->sample_type |= PERF_SAMPLE_DATA_SRC; + perf_evsel__set_sample_bit(evsel, DATA_SRC); if (opts->no_delay) { attr->watermark = 0; @@ -675,13 +675,13 @@ void perf_evsel__config(struct perf_evsel *evsel, } if (opts->sample_weight) - attr->sample_type |= PERF_SAMPLE_WEIGHT; + perf_evsel__set_sample_bit(evsel, WEIGHT); attr->mmap = track; attr->comm = track; if (opts->sample_transaction) - attr->sample_type |= PERF_SAMPLE_TRANSACTION; + perf_evsel__set_sample_bit(evsel, TRANSACTION); /* * XXX see the function comment above -- cgit v0.10.2 From 87b955247d71975460774435241be3aa05218a7b Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 1 Nov 2013 15:51:36 +0200 Subject: perf evsel: Add missing overflow check for TRANSACTION Add missing overflow check for PERF_SAMPLE_TRANSACTION in perf_evsel__parse_sample(). Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1383313899-15987-9-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 47bbf03..b121717 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1481,6 +1481,7 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, data->transaction = 0; if (type & PERF_SAMPLE_TRANSACTION) { + OVERFLOW_CHECK_u64(array); data->transaction = *array; array++; } -- cgit v0.10.2 From 091a4ef5a94d46d26a05f0c32d2f64800ed91306 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 1 Nov 2013 15:51:37 +0200 Subject: perf test: Update "sample parsing" test for PERF_SAMPLE_TRANSACTION In fact the "sample parsing" test does not automatically check new sample type bits - they must be added to the comparison logic. Doing that shows that the test fails because the functions perf_event__synthesize_sample() and perf_event__sample_event_size() have not been updated with PERF_SAMPLE_TRANSACTION either. Signed-off-by: Adrian Hunter Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1383313899-15987-10-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-parsing.c index 61c9da2..1b67720 100644 --- a/tools/perf/tests/sample-parsing.c +++ b/tools/perf/tests/sample-parsing.c @@ -121,6 +121,9 @@ static bool samples_same(const struct perf_sample *s1, if (type & PERF_SAMPLE_DATA_SRC) COMP(data_src); + if (type & PERF_SAMPLE_TRANSACTION) + COMP(transaction); + return true; } @@ -165,6 +168,7 @@ static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format) .cpu = 110, .raw_size = sizeof(raw_data), .data_src = 111, + .transaction = 112, .raw_data = (void *)raw_data, .callchain = &callchain.callchain, .branch_stack = &branch_stack.branch_stack, @@ -273,7 +277,8 @@ int test__sample_parsing(void) /* * Fail the test if it has not been updated when new sample format bits - * were added. + * were added. Please actually update the test rather than just change + * the condition below. */ if (PERF_SAMPLE_MAX > PERF_SAMPLE_TRANSACTION << 1) { pr_debug("sample format has changed, some new PERF_SAMPLE_ bit was introduced - test needs updating\n"); -- cgit v0.10.2 From 42d88910c717ba21089251d0ca559abfef0df22d Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 1 Nov 2013 15:51:38 +0200 Subject: perf evsel: Synthesize PERF_SAMPLE_TRANSACTION Add missing PERF_SAMPLE_TRANSACTION to perf_event__synthesize_sample() and perf_event__sample_event_size(). This makes the "sample parsing" test pass. Signed-off-by: Adrian Hunter Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1383313899-15987-11-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index b121717..5280820 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1578,6 +1578,9 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, if (type & PERF_SAMPLE_DATA_SRC) result += sizeof(u64); + if (type & PERF_SAMPLE_TRANSACTION) + result += sizeof(u64); + return result; } @@ -1751,6 +1754,11 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, array++; } + if (type & PERF_SAMPLE_TRANSACTION) { + *array = sample->transaction; + array++; + } + return 0; } -- cgit v0.10.2 From ac6976255076d4bf761abfbecb19d46af5b88046 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 1 Nov 2013 16:33:11 +0900 Subject: perf tools: Show single option when failed to parse Current option parser outputs whole option help string when it failed to parse an option. However this is not good for user if the command has many option, she might feel hard which one is related easily. Fix it by just showing the help message of the given option only. Signed-off-by: Namhyung Kim Requested-by: Ingo Molnar Acked-by: Ingo Molnar Reviewed-by: Ingo Molnar Enthusiastically-Supported-by: Ingo Molnar Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1383291195-24386-2-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index 2bc9e70..1caf7b9 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c @@ -339,10 +339,10 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, if (arg[1] != '-') { ctx->opt = arg + 1; if (internal_help && *ctx->opt == 'h') - return parse_options_usage(usagestr, options); + return usage_with_options_internal(usagestr, options, 0); switch (parse_short_opt(ctx, options)) { case -1: - return parse_options_usage(usagestr, options); + return parse_options_usage(usagestr, options, arg + 1, 1); case -2: goto unknown; default: @@ -352,10 +352,11 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, check_typos(arg + 1, options); while (ctx->opt) { if (internal_help && *ctx->opt == 'h') - return parse_options_usage(usagestr, options); + return usage_with_options_internal(usagestr, options, 0); + arg = ctx->opt; switch (parse_short_opt(ctx, options)) { case -1: - return parse_options_usage(usagestr, options); + return parse_options_usage(usagestr, options, arg, 1); case -2: /* fake a short option thing to hide the fact that we may have * started to parse aggregated stuff @@ -383,12 +384,12 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, if (internal_help && !strcmp(arg + 2, "help-all")) return usage_with_options_internal(usagestr, options, 1); if (internal_help && !strcmp(arg + 2, "help")) - return parse_options_usage(usagestr, options); + return usage_with_options_internal(usagestr, options, 0); if (!strcmp(arg + 2, "list-opts")) return PARSE_OPT_LIST; switch (parse_long_opt(ctx, arg + 2, options)) { case -1: - return parse_options_usage(usagestr, options); + return parse_options_usage(usagestr, options, arg + 2, 0); case -2: goto unknown; default: @@ -445,6 +446,89 @@ int parse_options(int argc, const char **argv, const struct option *options, #define USAGE_OPTS_WIDTH 24 #define USAGE_GAP 2 +static void print_option_help(const struct option *opts, int full) +{ + size_t pos; + int pad; + + if (opts->type == OPTION_GROUP) { + fputc('\n', stderr); + if (*opts->help) + fprintf(stderr, "%s\n", opts->help); + return; + } + if (!full && (opts->flags & PARSE_OPT_HIDDEN)) + return; + + pos = fprintf(stderr, " "); + if (opts->short_name) + pos += fprintf(stderr, "-%c", opts->short_name); + else + pos += fprintf(stderr, " "); + + if (opts->long_name && opts->short_name) + pos += fprintf(stderr, ", "); + if (opts->long_name) + pos += fprintf(stderr, "--%s", opts->long_name); + + switch (opts->type) { + case OPTION_ARGUMENT: + break; + case OPTION_LONG: + case OPTION_U64: + case OPTION_INTEGER: + case OPTION_UINTEGER: + if (opts->flags & PARSE_OPT_OPTARG) + if (opts->long_name) + pos += fprintf(stderr, "[=]"); + else + pos += fprintf(stderr, "[]"); + else + pos += fprintf(stderr, " "); + break; + case OPTION_CALLBACK: + if (opts->flags & PARSE_OPT_NOARG) + break; + /* FALLTHROUGH */ + case OPTION_STRING: + if (opts->argh) { + if (opts->flags & PARSE_OPT_OPTARG) + if (opts->long_name) + pos += fprintf(stderr, "[=<%s>]", opts->argh); + else + pos += fprintf(stderr, "[<%s>]", opts->argh); + else + pos += fprintf(stderr, " <%s>", opts->argh); + } else { + if (opts->flags & PARSE_OPT_OPTARG) + if (opts->long_name) + pos += fprintf(stderr, "[=...]"); + else + pos += fprintf(stderr, "[...]"); + else + pos += fprintf(stderr, " ..."); + } + break; + default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */ + case OPTION_END: + case OPTION_GROUP: + case OPTION_BIT: + case OPTION_BOOLEAN: + case OPTION_INCR: + case OPTION_SET_UINT: + case OPTION_SET_PTR: + break; + } + + if (pos <= USAGE_OPTS_WIDTH) + pad = USAGE_OPTS_WIDTH - pos; + else { + fputc('\n', stderr); + pad = USAGE_OPTS_WIDTH; + } + fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help); +} + int usage_with_options_internal(const char * const *usagestr, const struct option *opts, int full) { @@ -464,87 +548,9 @@ int usage_with_options_internal(const char * const *usagestr, if (opts->type != OPTION_GROUP) fputc('\n', stderr); - for (; opts->type != OPTION_END; opts++) { - size_t pos; - int pad; - - if (opts->type == OPTION_GROUP) { - fputc('\n', stderr); - if (*opts->help) - fprintf(stderr, "%s\n", opts->help); - continue; - } - if (!full && (opts->flags & PARSE_OPT_HIDDEN)) - continue; - - pos = fprintf(stderr, " "); - if (opts->short_name) - pos += fprintf(stderr, "-%c", opts->short_name); - else - pos += fprintf(stderr, " "); - - if (opts->long_name && opts->short_name) - pos += fprintf(stderr, ", "); - if (opts->long_name) - pos += fprintf(stderr, "--%s", opts->long_name); + for ( ; opts->type != OPTION_END; opts++) + print_option_help(opts, full); - switch (opts->type) { - case OPTION_ARGUMENT: - break; - case OPTION_LONG: - case OPTION_U64: - case OPTION_INTEGER: - case OPTION_UINTEGER: - if (opts->flags & PARSE_OPT_OPTARG) - if (opts->long_name) - pos += fprintf(stderr, "[=]"); - else - pos += fprintf(stderr, "[]"); - else - pos += fprintf(stderr, " "); - break; - case OPTION_CALLBACK: - if (opts->flags & PARSE_OPT_NOARG) - break; - /* FALLTHROUGH */ - case OPTION_STRING: - if (opts->argh) { - if (opts->flags & PARSE_OPT_OPTARG) - if (opts->long_name) - pos += fprintf(stderr, "[=<%s>]", opts->argh); - else - pos += fprintf(stderr, "[<%s>]", opts->argh); - else - pos += fprintf(stderr, " <%s>", opts->argh); - } else { - if (opts->flags & PARSE_OPT_OPTARG) - if (opts->long_name) - pos += fprintf(stderr, "[=...]"); - else - pos += fprintf(stderr, "[...]"); - else - pos += fprintf(stderr, " ..."); - } - break; - default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */ - case OPTION_END: - case OPTION_GROUP: - case OPTION_BIT: - case OPTION_BOOLEAN: - case OPTION_INCR: - case OPTION_SET_UINT: - case OPTION_SET_PTR: - break; - } - - if (pos <= USAGE_OPTS_WIDTH) - pad = USAGE_OPTS_WIDTH - pos; - else { - fputc('\n', stderr); - pad = USAGE_OPTS_WIDTH; - } - fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help); - } fputc('\n', stderr); return PARSE_OPT_HELP; @@ -559,9 +565,44 @@ void usage_with_options(const char * const *usagestr, } int parse_options_usage(const char * const *usagestr, - const struct option *opts) + const struct option *opts, + const char *optstr, bool short_opt) { - return usage_with_options_internal(usagestr, opts, 0); + if (!usagestr) + return PARSE_OPT_HELP; + + fprintf(stderr, "\n usage: %s\n", *usagestr++); + while (*usagestr && **usagestr) + fprintf(stderr, " or: %s\n", *usagestr++); + while (*usagestr) { + fprintf(stderr, "%s%s\n", + **usagestr ? " " : "", + *usagestr); + usagestr++; + } + fputc('\n', stderr); + + for ( ; opts->type != OPTION_END; opts++) { + if (short_opt) { + if (opts->short_name == *optstr) + break; + continue; + } + + if (opts->long_name == NULL) + continue; + + if (!prefixcmp(optstr, opts->long_name)) + break; + if (!prefixcmp(optstr, "no-") && + !prefixcmp(optstr + 3, opts->long_name)) + break; + } + + if (opts->type != OPTION_END) + print_option_help(opts, 0); + + return PARSE_OPT_HELP; } diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index 7bb5999..b0241e2 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h @@ -158,7 +158,9 @@ struct parse_opt_ctx_t { }; extern int parse_options_usage(const char * const *usagestr, - const struct option *opts); + const struct option *opts, + const char *optstr, + bool short_opt); extern void parse_options_start(struct parse_opt_ctx_t *ctx, int argc, const char **argv, int flags); -- cgit v0.10.2 From 4bceffbc26fab2444742db59c6f8124c29e41369 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 1 Nov 2013 16:33:12 +0900 Subject: perf report: Postpone setting up browser after parsing options If setup_browser() called earlier than option parsing, the actual error message can be discarded during the terminal reset. So move it after setup_sorting() checks whether the sort keys are valid. Signed-off-by: Namhyung Kim Acked-by: Ingo Molnar Reviewed-by: Ingo Molnar Enthusiastically-Supported-by: Ingo Molnar Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1383291195-24386-3-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 98d3891..4df3161 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -905,13 +905,6 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) input_name = "perf.data"; } - if (strcmp(input_name, "-") != 0) - setup_browser(true); - else { - use_browser = 0; - perf_hpp__init(); - } - file.path = input_name; file.force = report.force; @@ -957,6 +950,18 @@ repeat: if (setup_sorting() < 0) usage_with_options(report_usage, options); + if (parent_pattern != default_parent_pattern) { + if (sort_dimension__add("parent") < 0) + goto error; + } + + if (strcmp(input_name, "-") != 0) + setup_browser(true); + else { + use_browser = 0; + perf_hpp__init(); + } + /* * Only in the TUI browser we are doing integrated annotation, * so don't allocate extra space that won't be used in the stdio @@ -986,11 +991,6 @@ repeat: if (symbol__init() < 0) goto error; - if (parent_pattern != default_parent_pattern) { - if (sort_dimension__add("parent") < 0) - goto error; - } - if (argc) { /* * Special case: if there's an argument left then assume that -- cgit v0.10.2 From 91aba0a62e24ff7a567e13e1d88deab711df6f0f Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 1 Nov 2013 16:33:13 +0900 Subject: perf report: Use parse_options_usage() for -s option failure The -s (--sort) option was processed after normal option parsing so that it cannot call the parse_options_usage() automatically. Currently it calls usage_with_options() which shows entire help messages for event option. Fix it by showing just -s options. $ perf report -s help Error: Unknown --sort key: `help' usage: perf report [] -s, --sort sort by key(s): pid, comm, dso, symbol, ... Signed-off-by: Namhyung Kim Acked-by: Ingo Molnar Reviewed-by: Ingo Molnar Enthusiastically-Supported-by: Ingo Molnar Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1383291195-24386-4-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 4df3161..25f83d5 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -947,8 +947,10 @@ repeat: sort_order = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked"; } - if (setup_sorting() < 0) - usage_with_options(report_usage, options); + if (setup_sorting() < 0) { + parse_options_usage(report_usage, options, "s", 1); + goto error; + } if (parent_pattern != default_parent_pattern) { if (sort_dimension__add("parent") < 0) -- cgit v0.10.2 From d37a92dcb45094dc02836c8a77c693c6f9916fb2 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 1 Nov 2013 16:33:14 +0900 Subject: perf top: Use parse_options_usage() for -s option failure The -s (--sort) option was processed after normal option parsing so that it cannot call the parse_options_usage() automatically. Currently it calls usage_with_options() which shows entire help messages for event option. Fix it by showing just -s options. $ perf top -s help Error: Unknown --sort key: `help' usage: perf top [] -s, --sort sort by key(s): pid, comm, dso, symbol, ... Signed-off-by: Namhyung Kim Acked-by: Ingo Molnar Reviewed-by: Ingo Molnar Enthusiastically-Supported-by: Ingo Molnar Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1383291195-24386-5-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 21db76d..ca5ca37 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1040,7 +1040,7 @@ parse_percent_limit(const struct option *opt, const char *arg, int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) { - int status; + int status = -1; char errbuf[BUFSIZ]; struct perf_top top = { .count_filter = 5, @@ -1159,8 +1159,10 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) if (sort_order == default_sort_order) sort_order = "dso,symbol"; - if (setup_sorting() < 0) - usage_with_options(top_usage, options); + if (setup_sorting() < 0) { + parse_options_usage(top_usage, options, "s", 1); + goto out_delete_evlist; + } /* display thread wants entries to be collapsed in a different tree */ sort__need_collapse = 1; -- cgit v0.10.2 From cc03c54296ccbeca5363dfe8f49af42d14960f28 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 1 Nov 2013 16:33:15 +0900 Subject: perf stat: Enhance option parse error message Print related option help messages only when it failed to process options. While at it, modify parse_options_usage() to skip usage part so that it can be used for showing multiple option help messages naturally like below: $ perf stat -Bx, ls -B option not supported with -x usage: perf stat [] [] -B, --big-num print large numbers with thousands' separators -x, --field-separator print counts with custom separator Signed-off-by: Namhyung Kim Acked-by: Ingo Molnar Reviewed-by: Ingo Molnar Enthusiastically-Supported-by: Ingo Molnar Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1383291195-24386-6-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 1a9c95d..0fc1c94 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1596,7 +1596,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) "perf stat [] []", NULL }; - int status = -ENOMEM, run_idx; + int status = -EINVAL, run_idx; const char *mode; setlocale(LC_ALL, ""); @@ -1614,12 +1614,15 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) if (output_name && output_fd) { fprintf(stderr, "cannot use both --output and --log-fd\n"); - usage_with_options(stat_usage, options); + parse_options_usage(stat_usage, options, "o", 1); + parse_options_usage(NULL, options, "log-fd", 0); + goto out; } if (output_fd < 0) { fprintf(stderr, "argument to --log-fd must be a > 0\n"); - usage_with_options(stat_usage, options); + parse_options_usage(stat_usage, options, "log-fd", 0); + goto out; } if (!output) { @@ -1656,7 +1659,9 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) /* User explicitly passed -B? */ if (big_num_opt == 1) { fprintf(stderr, "-B option not supported with -x\n"); - usage_with_options(stat_usage, options); + parse_options_usage(stat_usage, options, "B", 1); + parse_options_usage(NULL, options, "x", 1); + goto out; } else /* Nope, so disable big number formatting */ big_num = false; } else if (big_num_opt == 0) /* User passed --no-big-num */ @@ -1666,7 +1671,9 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) usage_with_options(stat_usage, options); if (run_count < 0) { - usage_with_options(stat_usage, options); + pr_err("Run count must be a positive number\n"); + parse_options_usage(stat_usage, options, "r", 1); + goto out; } else if (run_count == 0) { forever = true; run_count = 1; @@ -1678,8 +1685,10 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) fprintf(stderr, "both cgroup and no-aggregation " "modes only available in system-wide mode\n"); - usage_with_options(stat_usage, options); - return -1; + parse_options_usage(stat_usage, options, "G", 1); + parse_options_usage(NULL, options, "A", 1); + parse_options_usage(NULL, options, "a", 1); + goto out; } if (add_default_attributes()) @@ -1688,25 +1697,28 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) perf_target__validate(&target); if (perf_evlist__create_maps(evsel_list, &target) < 0) { - if (perf_target__has_task(&target)) + if (perf_target__has_task(&target)) { pr_err("Problems finding threads of monitor\n"); - if (perf_target__has_cpu(&target)) + parse_options_usage(stat_usage, options, "p", 1); + parse_options_usage(NULL, options, "t", 1); + } else if (perf_target__has_cpu(&target)) { perror("failed to parse CPUs map"); - - usage_with_options(stat_usage, options); - return -1; + parse_options_usage(stat_usage, options, "C", 1); + parse_options_usage(NULL, options, "a", 1); + } + goto out; } if (interval && interval < 100) { pr_err("print interval must be >= 100ms\n"); - usage_with_options(stat_usage, options); - return -1; + parse_options_usage(stat_usage, options, "I", 1); + goto out_free_maps; } if (perf_evlist__alloc_stats(evsel_list, interval)) goto out_free_maps; if (perf_stat_init_aggr_mode()) - goto out; + goto out_free_maps; /* * We dont want to block the signals - that would cause diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index 1caf7b9..31f404a 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c @@ -569,7 +569,7 @@ int parse_options_usage(const char * const *usagestr, const char *optstr, bool short_opt) { if (!usagestr) - return PARSE_OPT_HELP; + goto opt; fprintf(stderr, "\n usage: %s\n", *usagestr++); while (*usagestr && **usagestr) @@ -582,6 +582,7 @@ int parse_options_usage(const char * const *usagestr, } fputc('\n', stderr); +opt: for ( ; opts->type != OPTION_END; opts++) { if (short_opt) { if (opts->short_name == *optstr) -- cgit v0.10.2 From c01422a4a184a183b03fb3046af88d61828f6d56 Mon Sep 17 00:00:00 2001 From: Nariman Poushin Date: Mon, 4 Nov 2013 12:03:44 +0000 Subject: ASoC: wm_adsp: Interpret ADSP memory region lengths as 32 bit words Pad the ADSP word (3 bytes) to 4 bytes in the kernel and calculate lengths based on padded ADSP words instead of treating them as bytes Signed-off-by: Nariman Poushin Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index b38f350..60b6b59 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -1062,6 +1062,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) if (i + 1 < algs) { region->len = be32_to_cpu(adsp1_alg[i + 1].dm); region->len -= be32_to_cpu(adsp1_alg[i].dm); + region->len *= 4; wm_adsp_create_control(dsp, region); } else { adsp_warn(dsp, "Missing length info for region DM with ID %x\n", @@ -1079,6 +1080,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) if (i + 1 < algs) { region->len = be32_to_cpu(adsp1_alg[i + 1].zm); region->len -= be32_to_cpu(adsp1_alg[i].zm); + region->len *= 4; wm_adsp_create_control(dsp, region); } else { adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", @@ -1108,6 +1110,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) if (i + 1 < algs) { region->len = be32_to_cpu(adsp2_alg[i + 1].xm); region->len -= be32_to_cpu(adsp2_alg[i].xm); + region->len *= 4; wm_adsp_create_control(dsp, region); } else { adsp_warn(dsp, "Missing length info for region XM with ID %x\n", @@ -1125,6 +1128,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) if (i + 1 < algs) { region->len = be32_to_cpu(adsp2_alg[i + 1].ym); region->len -= be32_to_cpu(adsp2_alg[i].ym); + region->len *= 4; wm_adsp_create_control(dsp, region); } else { adsp_warn(dsp, "Missing length info for region YM with ID %x\n", @@ -1142,6 +1146,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) if (i + 1 < algs) { region->len = be32_to_cpu(adsp2_alg[i + 1].zm); region->len -= be32_to_cpu(adsp2_alg[i].zm); + region->len *= 4; wm_adsp_create_control(dsp, region); } else { adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", -- cgit v0.10.2 From 9c1fc209138a7c1ee439c7b05fde484441c84e7e Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 4 Nov 2013 09:51:20 -0200 Subject: ASoC: fsl: fsl_spdif: No need to check the return value of platform_get_resource() When using devm_ioremap_resource(), we do not need to check the return value of platform_get_resource(), so just remove it. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index e1bf5ef..bab95d7 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -1107,11 +1107,6 @@ static int fsl_spdif_probe(struct platform_device *pdev) /* Get the addresses and IRQ */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "could not determine device resources\n"); - return -ENXIO; - } - regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(regs)) return PTR_ERR(regs); -- cgit v0.10.2 From 988e8cc41efecbab53f13184940aa6317047765b Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Mon, 4 Nov 2013 14:57:31 +0800 Subject: ASoC: Add pinctrl PM to components of active DAIs It's quite popular that more drivers are using pinctrl PM, for example: (Documentation/devicetree/bindings/arm/primecell.txt). Just like what runtime PM does, it would deactivate and activate pin group depending on whether it's being used or not. And this pinctrl PM might be also beneficial to cpu dai drivers because they might have actual pinctrl so as to sleep their pins and wake them up as needed. To achieve this goal, this patch sets pins to the default state during resume or startup; While during suspend and shutdown, it would set pins to the sleep state. As pinctrl PM would return zero if there is no such pinctrl sleep state settings, this patch would not break current ASoC subsystem directly. [ However, there is still an exception that the patch can not handle, that is, when cpu dai driver does not have pinctrl property but another device has it. (The AUDMUX <-> SSI on Freescale i.MX6 series for example. SSI as a cpu dai doesn't contain pinctrl property while AUDMUX, an Audio Multiplexer, has it). In this case, this kind of cpu dai driver needs to find a way to obtain the pinctrl property as its own, by moving property from AUDMUX to SSI, or creating a pins link/dependency between these two devices, or using a more decent way after we figure it out. ] Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index b38e0ee..1a4e977 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -662,6 +662,8 @@ int snd_soc_suspend(struct device *dev) codec->cache_sync = 1; if (codec->using_regmap) regcache_mark_dirty(codec->control_data); + /* deactivate pins to sleep state */ + pinctrl_pm_select_sleep_state(codec->dev); break; default: dev_dbg(codec->dev, @@ -679,6 +681,9 @@ int snd_soc_suspend(struct device *dev) if (cpu_dai->driver->suspend && cpu_dai->driver->ac97_control) cpu_dai->driver->suspend(cpu_dai); + + /* deactivate pins to sleep state */ + pinctrl_pm_select_sleep_state(cpu_dai->dev); } if (card->suspend_post) @@ -807,6 +812,16 @@ int snd_soc_resume(struct device *dev) if (list_empty(&card->codec_dev_list)) return 0; + /* activate pins from sleep state */ + for (i = 0; i < card->num_rtd; i++) { + struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; + struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai; + if (cpu_dai->active) + pinctrl_pm_select_default_state(cpu_dai->dev); + if (codec_dai->active) + pinctrl_pm_select_default_state(codec_dai->dev); + } + /* AC97 devices might have other drivers hanging off them so * need to resume immediately. Other drivers don't have that * problem and may take a substantial amount of time to resume @@ -1930,6 +1945,14 @@ int snd_soc_poweroff(struct device *dev) snd_soc_dapm_shutdown(card); + /* deactivate pins to sleep state */ + for (i = 0; i < card->num_rtd; i++) { + struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; + struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai; + pinctrl_pm_select_sleep_state(codec_dai->dev); + pinctrl_pm_select_sleep_state(cpu_dai->dev); + } + return 0; } EXPORT_SYMBOL_GPL(snd_soc_poweroff); @@ -3752,6 +3775,16 @@ int snd_soc_register_card(struct snd_soc_card *card) if (ret != 0) soc_cleanup_card_debugfs(card); + /* deactivate pins to sleep state */ + for (i = 0; i < card->num_rtd; i++) { + struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; + struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai; + if (!codec_dai->active) + pinctrl_pm_select_sleep_state(codec_dai->dev); + if (!cpu_dai->active) + pinctrl_pm_select_sleep_state(cpu_dai->dev); + } + return ret; } EXPORT_SYMBOL_GPL(snd_soc_register_card); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 591f0f3..42782c0 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -183,6 +184,8 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver; int ret = 0; + pinctrl_pm_select_default_state(cpu_dai->dev); + pinctrl_pm_select_default_state(codec_dai->dev); pm_runtime_get_sync(cpu_dai->dev); pm_runtime_get_sync(codec_dai->dev); pm_runtime_get_sync(platform->dev); @@ -317,6 +320,10 @@ out: pm_runtime_put(platform->dev); pm_runtime_put(codec_dai->dev); pm_runtime_put(cpu_dai->dev); + if (!codec_dai->active) + pinctrl_pm_select_sleep_state(codec_dai->dev); + if (!cpu_dai->active) + pinctrl_pm_select_sleep_state(cpu_dai->dev); return ret; } @@ -426,6 +433,10 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) pm_runtime_put(platform->dev); pm_runtime_put(codec_dai->dev); pm_runtime_put(cpu_dai->dev); + if (!codec_dai->active) + pinctrl_pm_select_sleep_state(codec_dai->dev); + if (!cpu_dai->active) + pinctrl_pm_select_sleep_state(cpu_dai->dev); return 0; } -- cgit v0.10.2 From f0e1bfbd2d1b0900a56c25cea35be2467a5cfc32 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 4 Nov 2013 09:28:24 +0100 Subject: of: Add AU Optronics Corporation vendor prefix AU Optronics is listed as AUO on the stock exchange, so use that as the vendor prefix. Signed-off-by: Thierry Reding Signed-off-by: Rob Herring diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 640e86d..5165846 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -12,6 +12,7 @@ amcc Applied Micro Circuits Corporation (APM, formally AMCC) apm Applied Micro Circuits Corporation (APM) arm ARM Ltd. atmel Atmel Corporation +auo AU Optronics Corporation avago Avago Technologies bosch Bosch Sensortec GmbH brcm Broadcom Corporation -- cgit v0.10.2 From bf80eb17ebce1af6023c2de72ba1954ca6b1a1f3 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 4 Nov 2013 09:28:25 +0100 Subject: of: Add Chunghwa Picture Tubes Ltd. vendor prefix Chunghwa Picture Tubes Ltd. isn't listed on the stock exchange, so use the generic "chunghwa" as vendor prefix. Signed-off-by: Thierry Reding Signed-off-by: Rob Herring diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 5165846..932e6e7 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -19,6 +19,7 @@ brcm Broadcom Corporation cavium Cavium, Inc. cdns Cadence Design Systems Inc. chrp Common Hardware Reference Platform +chunghwa Chunghwa Picture Tubes Ltd. cirrus Cirrus Logic, Inc. cortina Cortina Systems, Inc. dallas Maxim Integrated Products (formerly Dallas Semiconductor) -- cgit v0.10.2 From b6d4eeb18a863a9613f49adf2a59db3beeaf1bca Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 4 Nov 2013 09:28:26 +0100 Subject: of: Add Panasonic Corporation vendor prefix Use "panasonic" as the vendor prefix for the Panasonic Corporation. Signed-off-by: Thierry Reding Signed-off-by: Rob Herring diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 932e6e7..1809eae 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -48,6 +48,7 @@ nintendo Nintendo nvidia NVIDIA nxp NXP Semiconductors onnn ON Semiconductor Corp. +panasonic Panasonic Corporation phytec PHYTEC Messtechnik GmbH picochip Picochip Ltd powervr PowerVR (deprecated, use img) -- cgit v0.10.2 From 4eeca499be4ff4216b745e35ae8c8bffa6445eac Mon Sep 17 00:00:00 2001 From: James Ralston Date: Mon, 4 Nov 2013 09:27:45 -0800 Subject: ALSA: hda - Add Device IDs for Intel Wildcat Point-LP PCH This patch adds the HD Audio Device IDs for the Intel Wildcat Point-LP PCH. Signed-off-by: James Ralston Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index a0a06f7..66f7e1e 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -169,6 +169,7 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6}," "{Intel, PPT}," "{Intel, LPT}," "{Intel, LPT_LP}," + "{Intel, WPT_LP}," "{Intel, HPT}," "{Intel, PBG}," "{Intel, SCH}," @@ -3986,6 +3987,9 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { /* Lynx Point-LP */ { PCI_DEVICE(0x8086, 0x9c21), .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, + /* Wildcat Point-LP */ + { PCI_DEVICE(0x8086, 0x9ca0), + .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, /* Haswell */ { PCI_DEVICE(0x8086, 0x0a0c), .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH | -- cgit v0.10.2 From 1b372ca52a02cc97520c13d79bdfb0a7ff81b772 Mon Sep 17 00:00:00 2001 From: Yoshihiro YUNOMAE Date: Fri, 1 Nov 2013 17:53:53 -0400 Subject: tools lib traceevent: Add support for extracting trace_clock in report If trace-cmd extracts trace_clock, trace-cmd reads trace_clock data from the trace.dat and switches outputting format of timestamp for each trace_clock. Signed-off-by: Yoshihiro YUNOMAE Cc: Andrew Morton Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Link: http://lkml.kernel.org/r/20130424231305.14877.86147.stgit@yunodevel Signed-off-by: Steven Rostedt Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index d1c2a6a..deedff9 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c @@ -305,6 +305,11 @@ int pevent_register_comm(struct pevent *pevent, const char *comm, int pid) return 0; } +void pevent_register_trace_clock(struct pevent *pevent, char *trace_clock) +{ + pevent->trace_clock = trace_clock; +} + struct func_map { unsigned long long addr; char *func; @@ -4443,8 +4448,21 @@ void pevent_event_info(struct trace_seq *s, struct event_format *event, trace_seq_terminate(s); } +static bool is_timestamp_in_us(char *trace_clock, bool use_trace_clock) +{ + if (!use_trace_clock) + return true; + + if (!strcmp(trace_clock, "local") || !strcmp(trace_clock, "global") + || !strcmp(trace_clock, "uptime") || !strcmp(trace_clock, "perf")) + return true; + + /* trace_clock is setting in tsc or counter mode */ + return false; +} + void pevent_print_event(struct pevent *pevent, struct trace_seq *s, - struct pevent_record *record) + struct pevent_record *record, bool use_trace_clock) { static const char *spaces = " "; /* 20 spaces */ struct event_format *event; @@ -4457,9 +4475,14 @@ void pevent_print_event(struct pevent *pevent, struct trace_seq *s, int pid; int len; int p; + bool use_usec_format; - secs = record->ts / NSECS_PER_SEC; - nsecs = record->ts - secs * NSECS_PER_SEC; + use_usec_format = is_timestamp_in_us(pevent->trace_clock, + use_trace_clock); + if (use_usec_format) { + secs = record->ts / NSECS_PER_SEC; + nsecs = record->ts - secs * NSECS_PER_SEC; + } if (record->size < 0) { do_warning("ug! negative record size %d", record->size); @@ -4484,15 +4507,20 @@ void pevent_print_event(struct pevent *pevent, struct trace_seq *s, } else trace_seq_printf(s, "%16s-%-5d [%03d]", comm, pid, record->cpu); - if (pevent->flags & PEVENT_NSEC_OUTPUT) { - usecs = nsecs; - p = 9; - } else { - usecs = (nsecs + 500) / NSECS_PER_USEC; - p = 6; - } + if (use_usec_format) { + if (pevent->flags & PEVENT_NSEC_OUTPUT) { + usecs = nsecs; + p = 9; + } else { + usecs = (nsecs + 500) / NSECS_PER_USEC; + p = 6; + } - trace_seq_printf(s, " %5lu.%0*lu: %s: ", secs, p, usecs, event->name); + trace_seq_printf(s, " %5lu.%0*lu: %s: ", + secs, p, usecs, event->name); + } else + trace_seq_printf(s, " %12llu: %s: ", + record->ts, event->name); /* Space out the event names evenly. */ len = strlen(event->name); diff --git a/tools/lib/traceevent/event-parse.h b/tools/lib/traceevent/event-parse.h index c37b202..7503edf 100644 --- a/tools/lib/traceevent/event-parse.h +++ b/tools/lib/traceevent/event-parse.h @@ -20,6 +20,7 @@ #ifndef _PARSE_EVENTS_H #define _PARSE_EVENTS_H +#include #include #include @@ -450,6 +451,8 @@ struct pevent { /* cache */ struct event_format *last_event; + + char *trace_clock; }; static inline void pevent_set_flag(struct pevent *pevent, int flag) @@ -527,6 +530,7 @@ enum trace_flag_type { }; int pevent_register_comm(struct pevent *pevent, const char *comm, int pid); +void pevent_register_trace_clock(struct pevent *pevent, char *trace_clock); int pevent_register_function(struct pevent *pevent, char *name, unsigned long long addr, char *mod); int pevent_register_print_string(struct pevent *pevent, char *fmt, @@ -534,7 +538,7 @@ int pevent_register_print_string(struct pevent *pevent, char *fmt, int pevent_pid_is_registered(struct pevent *pevent, int pid); void pevent_print_event(struct pevent *pevent, struct trace_seq *s, - struct pevent_record *record); + struct pevent_record *record, bool use_trace_clock); int pevent_parse_header_page(struct pevent *pevent, char *buf, unsigned long size, int long_size); -- cgit v0.10.2 From 18900af8292180151c82f0762506fa0740aa54a5 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Fri, 1 Nov 2013 17:53:54 -0400 Subject: tools lib traceevent: Update printk formats when entered Instead of cropping off the '"' and '\n"' from a printk format every time it is referenced, do it when it's added. This makes it easier to reference a printk_map and should speed things up a little. Signed-off-by: Steven Rostedt Cc: Andrew Morton Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Link: http://lkml.kernel.org/r/20131101215500.495619312@goodmis.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index deedff9..856b791 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c @@ -604,10 +604,11 @@ find_printk(struct pevent *pevent, unsigned long long addr) * This registers a string by the address it was stored in the kernel. * The @fmt passed in is duplicated. */ -int pevent_register_print_string(struct pevent *pevent, char *fmt, +int pevent_register_print_string(struct pevent *pevent, const char *fmt, unsigned long long addr) { struct printk_list *item = malloc(sizeof(*item)); + char *p; if (!item) return -1; @@ -615,10 +616,21 @@ int pevent_register_print_string(struct pevent *pevent, char *fmt, item->next = pevent->printklist; item->addr = addr; + /* Strip off quotes and '\n' from the end */ + if (fmt[0] == '"') + fmt++; item->printk = strdup(fmt); if (!item->printk) goto out_free; + p = item->printk + strlen(item->printk) - 1; + if (*p == '"') + *p = 0; + + p -= 2; + if (strcmp(p, "\\n") == 0) + *p = 0; + pevent->printklist = item; pevent->printk_count++; @@ -3887,7 +3899,6 @@ get_bprint_format(void *data, int size __maybe_unused, struct format_field *field; struct printk_map *printk; char *format; - char *p; field = pevent->bprint_fmt_field; @@ -3909,20 +3920,8 @@ get_bprint_format(void *data, int size __maybe_unused, return format; } - p = printk->printk; - /* Remove any quotes. */ - if (*p == '"') - p++; - if (asprintf(&format, "%s : %s", "%pf", p) < 0) + if (asprintf(&format, "%s : %s", "%pf", printk->printk) < 0) return NULL; - /* remove ending quotes and new line since we will add one too */ - p = format + strlen(format) - 1; - if (*p == '"') - *p = 0; - - p -= 2; - if (strcmp(p, "\\n") == 0) - *p = 0; return format; } diff --git a/tools/lib/traceevent/event-parse.h b/tools/lib/traceevent/event-parse.h index 7503edf..9ab6367 100644 --- a/tools/lib/traceevent/event-parse.h +++ b/tools/lib/traceevent/event-parse.h @@ -533,7 +533,7 @@ int pevent_register_comm(struct pevent *pevent, const char *comm, int pid); void pevent_register_trace_clock(struct pevent *pevent, char *trace_clock); int pevent_register_function(struct pevent *pevent, char *name, unsigned long long addr, char *mod); -int pevent_register_print_string(struct pevent *pevent, char *fmt, +int pevent_register_print_string(struct pevent *pevent, const char *fmt, unsigned long long addr); int pevent_pid_is_registered(struct pevent *pevent, int pid); -- cgit v0.10.2 From 0970b5f438261216afcd0ccaa2fcfffc83df7ca2 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Fri, 1 Nov 2013 17:53:55 -0400 Subject: tools lib traceevent: If %s is a pointer, check printk formats If the format string of TP_printk() contains a %s, and the argument is not a string, check if the argument is a pointer that might match the printk_formats that were stored. Signed-off-by: Steven Rostedt Cc: Andrew Morton Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Link: http://lkml.kernel.org/r/20131101215500.698924777@goodmis.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index 856b791..013c8d3 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c @@ -3505,6 +3505,7 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, struct pevent *pevent = event->pevent; struct print_flag_sym *flag; struct format_field *field; + struct printk_map *printk; unsigned long long val, fval; unsigned long addr; char *str; @@ -3540,7 +3541,12 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, if (!(field->flags & FIELD_IS_ARRAY) && field->size == pevent->long_size) { addr = *(unsigned long *)(data + field->offset); - trace_seq_printf(s, "%lx", addr); + /* Check if it matches a print format */ + printk = find_printk(pevent, addr); + if (printk) + trace_seq_puts(s, printk->printk); + else + trace_seq_printf(s, "%lx", addr); break; } str = malloc(len + 1); -- cgit v0.10.2 From b30f75eba27a9ab0704cbc501e9be3b025ce56fe Mon Sep 17 00:00:00 2001 From: Howard Cochran Date: Fri, 1 Nov 2013 17:53:56 -0400 Subject: tools lib traceevent: Handle __print_hex(__get_dynamic_array(fieldname), len) The kernel has a few events with a format similar to this excerpt: field:unsigned int len; offset:12; size:4; signed:0; field:__data_loc unsigned char[] data_array; offset:16; size:4; signed:0; print fmt: "%s", __print_hex(__get_dynamic_array(data_array), REC->len) trace-cmd could already parse that arg correctly, but print_str_arg() was unable to handle the first parameter being a dynamic array. (It just printed a "field not found" warning). Teach print_str_arg's PRINT_HEX case to handle the nested PRINT_DYNAMIC_ARRAY correctly. The output now matches the kernel's own formatting for this case. Signed-off-by: Howard Cochran Cc: Andrew Morton Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Link: http://lkml.kernel.org/r/1381503349-12271-1-git-send-email-hcochran@lexmark.com [ Removed "polish compare", we don't do that here ] Signed-off-by: Steven Rostedt Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index 013c8d3..0a1ffe0 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c @@ -3588,15 +3588,23 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, } break; case PRINT_HEX: - field = arg->hex.field->field.field; - if (!field) { - str = arg->hex.field->field.name; - field = pevent_find_any_field(event, str); - if (!field) - goto out_warning_field; - arg->hex.field->field.field = field; + if (arg->hex.field->type == PRINT_DYNAMIC_ARRAY) { + unsigned long offset; + offset = pevent_read_number(pevent, + data + arg->hex.field->dynarray.field->offset, + arg->hex.field->dynarray.field->size); + hex = data + (offset & 0xffff); + } else { + field = arg->hex.field->field.field; + if (!field) { + str = arg->hex.field->field.name; + field = pevent_find_any_field(event, str); + if (!field) + goto out_warning_field; + arg->hex.field->field.field = field; + } + hex = data + field->offset; } - hex = data + field->offset; len = eval_num_arg(data, size, event, arg->hex.size); for (i = 0; i < len; i++) { if (i) -- cgit v0.10.2 From 0883d9d730fc294c3d90ebd190b94e5782ead316 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Fri, 1 Nov 2013 17:53:57 -0400 Subject: tools lib traceevent: Have bprintk output the same as the kernel does The trace_bprintk() in the kernel looks like: ring_buffer_producer_thread: Missed: 0 ring_buffer_producer_thread: Hit: 62174350 ring_buffer_producer_thread: Entries per millisec: 6296 ring_buffer_producer_thread: 158 ns per entry ring_buffer_producer_thread: Sleeping for 10 secs ring_buffer_producer_thread: Starting ring buffer hammer ring_buffer_producer_thread: End ring buffer hammer But the current output looks like this: ring_buffer_producer_thread : Time: 9407018 (usecs) ring_buffer_producer_thread : Overruns: 43285485 ring_buffer_producer_thread : Read: 4405365 (by events) ring_buffer_producer_thread : Entries: 0 ring_buffer_producer_thread : Total: 47690850 ring_buffer_producer_thread : Missed: 0 ring_buffer_producer_thread : Hit: 47690850 Remove the space between the function and the colon. Signed-off-by: Steven Rostedt Cc: Andrew Morton Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Link: http://lkml.kernel.org/r/20131101215501.272654481@goodmis.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index 0a1ffe0..e1c743c 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c @@ -3802,8 +3802,8 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc if (asprintf(&arg->atom.atom, "%lld", ip) < 0) goto out_free; - /* skip the first "%pf : " */ - for (ptr = fmt + 6, bptr = data + field->offset; + /* skip the first "%pf: " */ + for (ptr = fmt + 5, bptr = data + field->offset; bptr < data + size && *ptr; ptr++) { int ls = 0; @@ -3929,12 +3929,12 @@ get_bprint_format(void *data, int size __maybe_unused, printk = find_printk(pevent, addr); if (!printk) { - if (asprintf(&format, "%%pf : (NO FORMAT FOUND at %llx)\n", addr) < 0) + if (asprintf(&format, "%%pf: (NO FORMAT FOUND at %llx)\n", addr) < 0) return NULL; return format; } - if (asprintf(&format, "%s : %s", "%pf", printk->printk) < 0) + if (asprintf(&format, "%s: %s", "%pf", printk->printk) < 0) return NULL; return format; -- cgit v0.10.2 From 5efb9fbd5f1bfe4435bd0a3ea5f0e187875509c2 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Fri, 1 Nov 2013 17:53:58 -0400 Subject: tools lib traceevent: Check for spaces in character array Currently when using the raw format for fields, when looking at a character array, to determine if it is a string or not, we make sure all characters are "isprint()". If not, then we consider it a numeric array, and print the hex numbers of the characters instead. But it seems that '\n' fails the isprint() check! Add isspace() to the check as well, such that if all characters pass isprint() or isspace() it will assume the character array is a string. Reported-by: Xenia Ragiadakou Signed-off-by: Steven Rostedt Cc: Andrew Morton Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Xenia Ragiadakou Link: http://lkml.kernel.org/r/20131101215501.465091682@goodmis.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index e1c743c..85cbbdd 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c @@ -3981,7 +3981,7 @@ static int is_printable_array(char *p, unsigned int len) unsigned int i; for (i = 0; i < len && p[i]; i++) - if (!isprint(p[i])) + if (!isprint(p[i]) && !isspace(p[i])) return 0; return 1; } -- cgit v0.10.2 From c6c2b960b7a4105f096499fba3df65d6c0272a20 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 1 Nov 2013 17:53:59 -0400 Subject: tools lib traceevent: Add flags NOHANDLE and PRINTRAW to individual events Add the flags EVENT_FL_NOHANDLE and EVENT_FL_PRINTRAW to the event flags to have the event either ignore the register handler or to ignore the handler and also print the raw format respectively. This allows a tool to force a raw format or non handle for an event. Signed-off-by: Steven Rostedt Cc: Andrew Morton Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Link: http://lkml.kernel.org/r/20131101215501.655258742@goodmis.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index 85cbbdd..fc6f35f 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c @@ -4446,11 +4446,11 @@ void pevent_event_info(struct trace_seq *s, struct event_format *event, { int print_pretty = 1; - if (event->pevent->print_raw) + if (event->pevent->print_raw || (event->flags & EVENT_FL_PRINTRAW)) print_event_fields(s, record->data, record->size, event); else { - if (event->handler) + if (event->handler && !(event->flags & EVENT_FL_NOHANDLE)) print_pretty = event->handler(s, record, event, event->context); diff --git a/tools/lib/traceevent/event-parse.h b/tools/lib/traceevent/event-parse.h index 9ab6367..dc8539e 100644 --- a/tools/lib/traceevent/event-parse.h +++ b/tools/lib/traceevent/event-parse.h @@ -308,6 +308,8 @@ enum { EVENT_FL_ISBPRINT = 0x04, EVENT_FL_ISFUNCENT = 0x10, EVENT_FL_ISFUNCRET = 0x20, + EVENT_FL_NOHANDLE = 0x40, + EVENT_FL_PRINTRAW = 0x80, EVENT_FL_FAILED = 0x80000000 }; -- cgit v0.10.2 From 6d862b8c14ba539c7c87ffc77f2e1d6dc9630c4d Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 1 Nov 2013 17:54:00 -0400 Subject: tools lib traceevent: Add pevent_print_func_field() helper function Add the pevent_print_func_field() that will look up a field that is expected to be a function pointer, and it will print the function name and offset of the address given by the field. Signed-off-by: Steven Rostedt Cc: Andrew Morton Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Link: http://lkml.kernel.org/r/20131101215501.869542711@goodmis.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index fc6f35f..8f450ad 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c @@ -5367,6 +5367,48 @@ int pevent_print_num_field(struct trace_seq *s, const char *fmt, return -1; } +/** + * pevent_print_func_field - print a field and a format for function pointers + * @s: The seq to print to + * @fmt: The printf format to print the field with. + * @event: the event that the field is for + * @name: The name of the field + * @record: The record with the field name. + * @err: print default error if failed. + * + * Returns: 0 on success, -1 field not found, or 1 if buffer is full. + */ +int pevent_print_func_field(struct trace_seq *s, const char *fmt, + struct event_format *event, const char *name, + struct pevent_record *record, int err) +{ + struct format_field *field = pevent_find_field(event, name); + struct pevent *pevent = event->pevent; + unsigned long long val; + struct func_map *func; + char tmp[128]; + + if (!field) + goto failed; + + if (pevent_read_number_field(field, record->data, &val)) + goto failed; + + func = find_func(pevent, val); + + if (func) + snprintf(tmp, 128, "%s/0x%llx", func->func, func->addr - val); + else + sprintf(tmp, "0x%08llx", val); + + return trace_seq_printf(s, fmt, tmp); + + failed: + if (err) + trace_seq_printf(s, "CAN'T FIND FIELD \"%s\"", name); + return -1; +} + static void free_func_handle(struct pevent_function_handler *func) { struct pevent_func_params *params; diff --git a/tools/lib/traceevent/event-parse.h b/tools/lib/traceevent/event-parse.h index dc8539e..8d73d25 100644 --- a/tools/lib/traceevent/event-parse.h +++ b/tools/lib/traceevent/event-parse.h @@ -569,6 +569,10 @@ int pevent_print_num_field(struct trace_seq *s, const char *fmt, struct event_format *event, const char *name, struct pevent_record *record, int err); +int pevent_print_func_field(struct trace_seq *s, const char *fmt, + struct event_format *event, const char *name, + struct pevent_record *record, int err); + int pevent_register_event_handler(struct pevent *pevent, int id, const char *sys_name, const char *event_name, pevent_event_handler_func func, void *context); -- cgit v0.10.2 From 24eff328f65c8ef352c90b6adb7c2f39eb94205d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 4 Nov 2013 18:21:08 +0100 Subject: ALSA: hda - Enable SPDIF for Acer TravelMate 6293 BIOS on Acer TravelMate 6293 doesn't set up the SPDIF output pin correctly as default, so enable it via a fixup entry. Reported-and-tested-by: Hagen Heiduck Cc: Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 2d7c86d..01bf812 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2386,6 +2386,7 @@ static const struct hda_verb alc268_beep_init_verbs[] = { enum { ALC268_FIXUP_INV_DMIC, ALC268_FIXUP_HP_EAPD, + ALC268_FIXUP_SPDIF, }; static const struct hda_fixup alc268_fixups[] = { @@ -2400,6 +2401,13 @@ static const struct hda_fixup alc268_fixups[] = { {} } }, + [ALC268_FIXUP_SPDIF] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1e, 0x014b1180 }, /* enable SPDIF out */ + {} + } + }, }; static const struct hda_model_fixup alc268_fixup_models[] = { @@ -2409,6 +2417,7 @@ static const struct hda_model_fixup alc268_fixup_models[] = { }; static const struct snd_pci_quirk alc268_fixup_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x0139, "Acer TravelMate 6293", ALC268_FIXUP_SPDIF), SND_PCI_QUIRK(0x1025, 0x015b, "Acer AOA 150 (ZG5)", ALC268_FIXUP_INV_DMIC), /* below is codec SSID since multiple Toshiba laptops have the * same PCI SSID 1179:ff00 -- cgit v0.10.2 From 9ed9c07d9b7d42fc7042247c2be283731186464f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 8 Oct 2013 09:57:20 +0200 Subject: clk: new driver for efm32 SoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds support for the clocks provided by the Clock Management Unit of Energy Micro's efm32 Giant Gecko SoCs including device tree bindings. Signed-off-by: Uwe Kleine-König Signed-off-by: Mike Turquette diff --git a/Documentation/devicetree/bindings/clock/efm32-clock.txt b/Documentation/devicetree/bindings/clock/efm32-clock.txt new file mode 100644 index 0000000..263d293 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/efm32-clock.txt @@ -0,0 +1,11 @@ +* Clock bindings for Energy Micro efm32 Giant Gecko's Clock Management Unit + +Required properties: +- compatible: Should be "efm32gg,cmu" +- reg: Base address and length of the register set +- interrupts: Interrupt used by the CMU +- #clock-cells: Should be <1> + +The clock consumer should specify the desired clock by having the clock ID in +its "clocks" phandle cell. The header efm32-clk.h contains a list of available +IDs. diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index df33820..7a10bc9 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-composite.o # SoCs specific obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o +obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o diff --git a/drivers/clk/clk-efm32gg.c b/drivers/clk/clk-efm32gg.c new file mode 100644 index 0000000..bac2ddf --- /dev/null +++ b/drivers/clk/clk-efm32gg.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2013 Pengutronix + * Uwe Kleine-Koenig + * + * 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. + */ +#include +#include +#include +#include +#include + +#include + +#define CMU_HFPERCLKEN0 0x44 + +static struct clk *clk[37]; +static struct clk_onecell_data clk_data = { + .clks = clk, + .clk_num = ARRAY_SIZE(clk), +}; + +static int __init efm32gg_cmu_init(struct device_node *np) +{ + int i; + void __iomem *base; + + for (i = 0; i < ARRAY_SIZE(clk); ++i) + clk[i] = ERR_PTR(-ENOENT); + + base = of_iomap(np, 0); + if (!base) { + pr_warn("Failed to map address range for efm32gg,cmu node\n"); + return -EADDRNOTAVAIL; + } + + clk[clk_HFXO] = clk_register_fixed_rate(NULL, "HFXO", NULL, + CLK_IS_ROOT, 48000000); + + clk[clk_HFPERCLKUSART0] = clk_register_gate(NULL, "HFPERCLK.USART0", + "HFXO", 0, base + CMU_HFPERCLKEN0, 0, 0, NULL); + clk[clk_HFPERCLKUSART1] = clk_register_gate(NULL, "HFPERCLK.USART1", + "HFXO", 0, base + CMU_HFPERCLKEN0, 1, 0, NULL); + clk[clk_HFPERCLKUSART2] = clk_register_gate(NULL, "HFPERCLK.USART2", + "HFXO", 0, base + CMU_HFPERCLKEN0, 2, 0, NULL); + clk[clk_HFPERCLKUART0] = clk_register_gate(NULL, "HFPERCLK.UART0", + "HFXO", 0, base + CMU_HFPERCLKEN0, 3, 0, NULL); + clk[clk_HFPERCLKUART1] = clk_register_gate(NULL, "HFPERCLK.UART1", + "HFXO", 0, base + CMU_HFPERCLKEN0, 4, 0, NULL); + clk[clk_HFPERCLKTIMER0] = clk_register_gate(NULL, "HFPERCLK.TIMER0", + "HFXO", 0, base + CMU_HFPERCLKEN0, 5, 0, NULL); + clk[clk_HFPERCLKTIMER1] = clk_register_gate(NULL, "HFPERCLK.TIMER1", + "HFXO", 0, base + CMU_HFPERCLKEN0, 6, 0, NULL); + clk[clk_HFPERCLKTIMER2] = clk_register_gate(NULL, "HFPERCLK.TIMER2", + "HFXO", 0, base + CMU_HFPERCLKEN0, 7, 0, NULL); + clk[clk_HFPERCLKTIMER3] = clk_register_gate(NULL, "HFPERCLK.TIMER3", + "HFXO", 0, base + CMU_HFPERCLKEN0, 8, 0, NULL); + clk[clk_HFPERCLKACMP0] = clk_register_gate(NULL, "HFPERCLK.ACMP0", + "HFXO", 0, base + CMU_HFPERCLKEN0, 9, 0, NULL); + clk[clk_HFPERCLKACMP1] = clk_register_gate(NULL, "HFPERCLK.ACMP1", + "HFXO", 0, base + CMU_HFPERCLKEN0, 10, 0, NULL); + clk[clk_HFPERCLKI2C0] = clk_register_gate(NULL, "HFPERCLK.I2C0", + "HFXO", 0, base + CMU_HFPERCLKEN0, 11, 0, NULL); + clk[clk_HFPERCLKI2C1] = clk_register_gate(NULL, "HFPERCLK.I2C1", + "HFXO", 0, base + CMU_HFPERCLKEN0, 12, 0, NULL); + clk[clk_HFPERCLKGPIO] = clk_register_gate(NULL, "HFPERCLK.GPIO", + "HFXO", 0, base + CMU_HFPERCLKEN0, 13, 0, NULL); + clk[clk_HFPERCLKVCMP] = clk_register_gate(NULL, "HFPERCLK.VCMP", + "HFXO", 0, base + CMU_HFPERCLKEN0, 14, 0, NULL); + clk[clk_HFPERCLKPRS] = clk_register_gate(NULL, "HFPERCLK.PRS", + "HFXO", 0, base + CMU_HFPERCLKEN0, 15, 0, NULL); + clk[clk_HFPERCLKADC0] = clk_register_gate(NULL, "HFPERCLK.ADC0", + "HFXO", 0, base + CMU_HFPERCLKEN0, 16, 0, NULL); + clk[clk_HFPERCLKDAC0] = clk_register_gate(NULL, "HFPERCLK.DAC0", + "HFXO", 0, base + CMU_HFPERCLKEN0, 17, 0, NULL); + + return of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); +} +CLK_OF_DECLARE(efm32ggcmu, "efm32gg,cmu", efm32gg_cmu_init); diff --git a/include/dt-bindings/clock/efm32-cmu.h b/include/dt-bindings/clock/efm32-cmu.h new file mode 100644 index 0000000..b21b91e --- /dev/null +++ b/include/dt-bindings/clock/efm32-cmu.h @@ -0,0 +1,42 @@ +#ifndef __DT_BINDINGS_CLOCK_EFM32_CMU_H +#define __DT_BINDINGS_CLOCK_EFM32_CMU_H + +#define clk_HFXO 0 +#define clk_HFRCO 1 +#define clk_LFXO 2 +#define clk_LFRCO 3 +#define clk_ULFRCO 4 +#define clk_AUXHFRCO 5 +#define clk_HFCLKNODIV 6 +#define clk_HFCLK 7 +#define clk_HFPERCLK 8 +#define clk_HFCORECLK 9 +#define clk_LFACLK 10 +#define clk_LFBCLK 11 +#define clk_WDOGCLK 12 +#define clk_HFCORECLKDMA 13 +#define clk_HFCORECLKAES 14 +#define clk_HFCORECLKUSBC 15 +#define clk_HFCORECLKUSB 16 +#define clk_HFCORECLKLE 17 +#define clk_HFCORECLKEBI 18 +#define clk_HFPERCLKUSART0 19 +#define clk_HFPERCLKUSART1 20 +#define clk_HFPERCLKUSART2 21 +#define clk_HFPERCLKUART0 22 +#define clk_HFPERCLKUART1 23 +#define clk_HFPERCLKTIMER0 24 +#define clk_HFPERCLKTIMER1 25 +#define clk_HFPERCLKTIMER2 26 +#define clk_HFPERCLKTIMER3 27 +#define clk_HFPERCLKACMP0 28 +#define clk_HFPERCLKACMP1 29 +#define clk_HFPERCLKI2C0 30 +#define clk_HFPERCLKI2C1 31 +#define clk_HFPERCLKGPIO 32 +#define clk_HFPERCLKVCMP 33 +#define clk_HFPERCLKPRS 34 +#define clk_HFPERCLKADC0 35 +#define clk_HFPERCLKDAC0 36 + +#endif /* __DT_BINDINGS_CLOCK_EFM32_CMU_H */ -- cgit v0.10.2 From 44790a0b93d8481a8dc5bf6aa600941627b56d56 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 5 Nov 2013 10:09:11 +1100 Subject: powerpc/boot: Properly handle the base "of" boot wrapper The wrapper script needs an explicit rule for the "of" boot wrapper (generic wrapper, similar to pseries). Before 0c9fa29149d3726e14262aeb0c8461a948cc9d56 it was hanlded implicitly by the statement: platformo=$object/"$platform".o But now that epapr.o needs to be added, that doesn't work and an explicit rule must be added. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/boot/wrapper b/arch/powerpc/boot/wrapper index ac16e99..2e1af74 100755 --- a/arch/powerpc/boot/wrapper +++ b/arch/powerpc/boot/wrapper @@ -147,6 +147,10 @@ link_address='0x400000' make_space=y case "$platform" in +of) + platformo="$object/of.o $object/epapr.o" + make_space=n + ;; pseries) platformo="$object/of.o $object/epapr.o" link_address='0x4000000' -- cgit v0.10.2 From 41a4e6e2a0237e8ac895f43158ef7c91ab7af157 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 31 Oct 2013 15:56:03 +0900 Subject: perf hists: Consolidate __hists__add_*entry() The __hists__add_{branch,mem}_entry() does almost the same thing that __hists__add_entry() does. Consolidate them into one. Signed-off-by: Namhyung Kim Acked-by: Jiri Olsa Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Rodrigo Campos Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1383202576-28141-2-git-send-email-namhyung@kernel.org [ Fixup clash with new COMM infrastructure ] Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 6c5ae57..4087ab1 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -65,7 +65,7 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel, return 0; } - he = __hists__add_entry(&evsel->hists, al, NULL, 1, 1, 0); + he = __hists__add_entry(&evsel->hists, al, NULL, NULL, NULL, 1, 1, 0); if (he == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index b605009..3b67ea2 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -307,7 +307,8 @@ static int hists__add_entry(struct hists *hists, struct addr_location *al, u64 period, u64 weight, u64 transaction) { - if (__hists__add_entry(hists, al, NULL, period, weight, transaction) != NULL) + if (__hists__add_entry(hists, al, NULL, NULL, NULL, period, weight, + transaction) != NULL) return 0; return -ENOMEM; } diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 25f83d5..8cf8e66 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -115,7 +115,8 @@ static int perf_report__add_mem_hist_entry(struct perf_tool *tool, * and this is indirectly achieved by passing period=weight here * and the he_stat__add_period() function. */ - he = __hists__add_mem_entry(&evsel->hists, al, parent, mi, cost, cost); + he = __hists__add_entry(&evsel->hists, al, parent, NULL, mi, + cost, cost, 0); if (!he) return -ENOMEM; @@ -200,12 +201,16 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool, err = -ENOMEM; + /* overwrite the 'al' to branch-to info */ + al->map = bi[i].to.map; + al->sym = bi[i].to.sym; + al->addr = bi[i].to.addr; /* * The report shows the percentage of total branches captured * and not events sampled. Thus we use a pseudo period of 1. */ - he = __hists__add_branch_entry(&evsel->hists, al, parent, - &bi[i], 1, 1); + he = __hists__add_entry(&evsel->hists, al, parent, &bi[i], NULL, + 1, 1, 0); if (he) { struct annotation *notes; bx = he->branch_info; @@ -266,8 +271,9 @@ static int perf_evsel__add_hist_entry(struct perf_tool *tool, return err; } - he = __hists__add_entry(&evsel->hists, al, parent, sample->period, - sample->weight, sample->transaction); + he = __hists__add_entry(&evsel->hists, al, parent, NULL, NULL, + sample->period, sample->weight, + sample->transaction); if (he == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index ca5ca37..21897f0 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -246,8 +246,9 @@ static struct hist_entry *perf_evsel__add_hist_entry(struct perf_evsel *evsel, struct hist_entry *he; pthread_mutex_lock(&evsel->hists.lock); - he = __hists__add_entry(&evsel->hists, al, NULL, sample->period, - sample->weight, sample->transaction); + he = __hists__add_entry(&evsel->hists, al, NULL, NULL, NULL, + sample->period, sample->weight, + sample->transaction); pthread_mutex_unlock(&evsel->hists.lock); if (he == NULL) return NULL; diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index 6c337e6..173bf42 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c @@ -223,7 +223,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) goto out; he = __hists__add_entry(&evsel->hists, &al, NULL, - 1, 1, 0); + NULL, NULL, 1, 1, 0); if (he == NULL) goto out; @@ -245,8 +245,8 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) &sample) < 0) goto out; - he = __hists__add_entry(&evsel->hists, &al, NULL, 1, 1, - 0); + he = __hists__add_entry(&evsel->hists, &al, NULL, + NULL, NULL, 1, 1, 0); if (he == NULL) goto out; diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 30793f9..822903e 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -407,73 +407,12 @@ out: return he; } -struct hist_entry *__hists__add_mem_entry(struct hists *hists, - struct addr_location *al, - struct symbol *sym_parent, - struct mem_info *mi, - u64 period, - u64 weight) -{ - struct hist_entry entry = { - .thread = al->thread, - .comm = thread__comm(al->thread), - .ms = { - .map = al->map, - .sym = al->sym, - }, - .stat = { - .period = period, - .weight = weight, - .nr_events = 1, - }, - .cpu = al->cpu, - .ip = al->addr, - .level = al->level, - .parent = sym_parent, - .filtered = symbol__parent_filter(sym_parent), - .hists = hists, - .mem_info = mi, - .branch_info = NULL, - }; - return add_hist_entry(hists, &entry, al, period, weight); -} - -struct hist_entry *__hists__add_branch_entry(struct hists *hists, - struct addr_location *al, - struct symbol *sym_parent, - struct branch_info *bi, - u64 period, - u64 weight) -{ - struct hist_entry entry = { - .thread = al->thread, - .comm = thread__comm(al->thread), - .ms = { - .map = bi->to.map, - .sym = bi->to.sym, - }, - .cpu = al->cpu, - .ip = bi->to.addr, - .level = al->level, - .stat = { - .period = period, - .nr_events = 1, - .weight = weight, - }, - .parent = sym_parent, - .filtered = symbol__parent_filter(sym_parent), - .branch_info = bi, - .hists = hists, - .mem_info = NULL, - }; - - return add_hist_entry(hists, &entry, al, period, weight); -} - struct hist_entry *__hists__add_entry(struct hists *hists, struct addr_location *al, - struct symbol *sym_parent, u64 period, - u64 weight, u64 transaction) + struct symbol *sym_parent, + struct branch_info *bi, + struct mem_info *mi, + u64 period, u64 weight, u64 transaction) { struct hist_entry entry = { .thread = al->thread, @@ -486,15 +425,15 @@ struct hist_entry *__hists__add_entry(struct hists *hists, .ip = al->addr, .level = al->level, .stat = { - .period = period, .nr_events = 1, + .period = period, .weight = weight, }, .parent = sym_parent, .filtered = symbol__parent_filter(sym_parent), .hists = hists, - .branch_info = NULL, - .mem_info = NULL, + .branch_info = bi, + .mem_info = mi, .transaction = transaction, }; diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 9d2d022..307f1c7 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -86,7 +86,9 @@ struct hists { struct hist_entry *__hists__add_entry(struct hists *self, struct addr_location *al, - struct symbol *parent, u64 period, + struct symbol *parent, + struct branch_info *bi, + struct mem_info *mi, u64 period, u64 weight, u64 transaction); int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right); int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); @@ -95,20 +97,6 @@ int hist_entry__sort_snprintf(struct hist_entry *self, char *bf, size_t size, struct hists *hists); void hist_entry__free(struct hist_entry *); -struct hist_entry *__hists__add_branch_entry(struct hists *self, - struct addr_location *al, - struct symbol *sym_parent, - struct branch_info *bi, - u64 period, - u64 weight); - -struct hist_entry *__hists__add_mem_entry(struct hists *self, - struct addr_location *al, - struct symbol *sym_parent, - struct mem_info *mi, - u64 period, - u64 weight); - void hists__output_resort(struct hists *self); void hists__collapse_resort(struct hists *self, struct ui_progress *prog); -- cgit v0.10.2 From 7f71be4c9f9a334e7bd0adc66225db4eb88c3bc4 Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Tue, 5 Nov 2013 09:46:39 +0800 Subject: x86, defconfig: Add DEVTMPFS and DEVTMPFS_MOUNT to *86*_defconfig The defconfig kernel can not run under neither fedora16 x86_64 laptop nor fedora17 x86_64 pc. After enable DEVTMPFS* in x86_64_defconfig, it will be OK. DEVTMPFS* is only related with software, so for i386_defconfig may also need them (at least, it has no negative effect for defconfig). Signed-off-by: Chen Gang Link: http://lkml.kernel.org/r/52784DFF.8040004@asianux.com Signed-off-by: H. Peter Anvin diff --git a/arch/x86/configs/i386_defconfig b/arch/x86/configs/i386_defconfig index 9444708..a7fef26 100644 --- a/arch/x86/configs/i386_defconfig +++ b/arch/x86/configs/i386_defconfig @@ -142,6 +142,8 @@ CONFIG_MAC80211=y CONFIG_MAC80211_LEDS=y CONFIG_RFKILL=y CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y CONFIG_DEBUG_DEVRES=y CONFIG_CONNECTOR=y CONFIG_BLK_DEV_LOOP=y diff --git a/arch/x86/configs/x86_64_defconfig b/arch/x86/configs/x86_64_defconfig index 671524d..c1119d4 100644 --- a/arch/x86/configs/x86_64_defconfig +++ b/arch/x86/configs/x86_64_defconfig @@ -141,6 +141,8 @@ CONFIG_MAC80211=y CONFIG_MAC80211_LEDS=y CONFIG_RFKILL=y CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y CONFIG_DEBUG_DEVRES=y CONFIG_CONNECTOR=y CONFIG_BLK_DEV_LOOP=y -- cgit v0.10.2 From 0dca01c37a68017fe7112b46bc4b48c927db18c6 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Tue, 5 Nov 2013 04:41:06 +0100 Subject: ALSA: usb: supply channel maps even when wChannelConfig is unspecified If wChannelconfig is given for some formats but not others, userspace might not be able to set the channel map. This is RFC because I'm not sure what the best behaviour is - to guess the channel map from the given number of channels (it's quite likely that one channel is MONO and two channels is FL FR), or just to supply UNKNOWN for all channels. But the complete lack of channel map for a format leads userspace to believe that the format is not available at all. Or am I misunderstanding how this should be used? Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/usb/stream.c b/sound/usb/stream.c index c4339f9..b43b6ee 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -281,8 +281,6 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits, const unsigned int *maps; int c; - if (!bits) - return NULL; if (channels > ARRAY_SIZE(chmap->map)) return NULL; @@ -293,9 +291,19 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits, maps = protocol == UAC_VERSION_2 ? uac2_maps : uac1_maps; chmap->channels = channels; c = 0; - for (; bits && *maps; maps++, bits >>= 1) { - if (bits & 1) - chmap->map[c++] = *maps; + + if (bits) { + for (; bits && *maps; maps++, bits >>= 1) + if (bits & 1) + chmap->map[c++] = *maps; + } else { + /* If we're missing wChannelConfig, then guess something + to make sure the channel map is not skipped entirely */ + if (channels == 1) + chmap->map[c++] = SNDRV_CHMAP_MONO; + else + for (; c < channels && *maps; maps++) + chmap->map[c++] = *maps; } for (; c < channels; c++) -- cgit v0.10.2 From e3e35f750fff74e701c8913fd7dd714e37a848cd Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Tue, 5 Nov 2013 04:41:07 +0100 Subject: ALSA: usb - For class 2 devices, use channel map from altsettings The channel config from the streaming descriptor is probably a better indicator of the channel map than the input terminal. Use the input terminal's channel map as fallback only. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/usb/stream.c b/sound/usb/stream.c index b43b6ee..badd1d6d 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -587,6 +587,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) num_channels = as->bNrChannels; format = le32_to_cpu(as->bmFormats); + chconfig = le32_to_cpu(as->bmChannelConfig); /* lookup the terminal associated to this interface * to extract the clock */ @@ -594,7 +595,8 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) as->bTerminalLink); if (input_term) { clock = input_term->bCSourceID; - chconfig = le32_to_cpu(input_term->bmChannelConfig); + if (!chconfig && (num_channels == input_term->bNrChannels)) + chconfig = le32_to_cpu(input_term->bmChannelConfig); break; } -- cgit v0.10.2 From 504333df8ba5fc310260285a22ab5d7cf3208795 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Tue, 5 Nov 2013 04:41:08 +0100 Subject: ALSA: usb - Don't trust the channel config if the channel count changed In case the channel count of the input terminal is not the same as the channel count of the streaming descriptor, the channel config of the input terminal can not be trusted. Instead fall back to a default (guessed) channel map. This was found on a Logitech USB Headset. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/usb/stream.c b/sound/usb/stream.c index badd1d6d..d737d0e 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -662,7 +662,6 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) * (fp->maxpacksize & 0x7ff); fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol, iface_no); fp->clock = clock; - fp->chmap = convert_chmap(num_channels, chconfig, protocol); /* some quirks for attributes here */ @@ -698,12 +697,16 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) /* ok, let's parse further... */ if (snd_usb_parse_audio_format(chip, fp, format, fmt, stream) < 0) { kfree(fp->rate_table); - kfree(fp->chmap); kfree(fp); fp = NULL; continue; } + /* Create chmap */ + if (fp->channels != num_channels) + chconfig = 0; + fp->chmap = convert_chmap(fp->channels, chconfig, protocol); + snd_printdd(KERN_INFO "%d:%u:%d: add audio endpoint %#x\n", dev->devnum, iface_no, altno, fp->endpoint); err = snd_usb_add_audio_stream(chip, stream, fp); if (err < 0) { -- cgit v0.10.2 From a4e7a121685a137eeeb01f05d5ed570c1b45017a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 4 Nov 2013 15:44:09 +0100 Subject: ALSA: hda - Fix possible zero-division Check the TLV db scale result before actually dividing in vmaster slave init code. Also mask TLV_DB_SCALE_MUTE bit so that the right value is obtained even if this bit is set by the codec driver. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index de1a767..33c01d5 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2676,8 +2676,13 @@ static int get_kctl_0dB_offset(struct snd_kcontrol *kctl) set_fs(fs); } else if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) tlv = kctl->tlv.p; - if (tlv && tlv[0] == SNDRV_CTL_TLVT_DB_SCALE) - val = -tlv[2] / tlv[3]; + if (tlv && tlv[0] == SNDRV_CTL_TLVT_DB_SCALE) { + int step = tlv[3]; + step &= ~TLV_DB_SCALE_MUTE; + if (!step) + return -1; + val = -tlv[2] / step; + } return val; } -- cgit v0.10.2 From 485e3e0cdf353e2e37570eafaad7208138bb6620 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 4 Nov 2013 15:51:00 +0100 Subject: ALSA: hda - Add sanity check of vmaster slave dB steps Check whether all vmaster slaves have the same dB steps. Otherwise the behavior would become inconsistent. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 33c01d5..dd5403d 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2661,7 +2661,7 @@ static int check_slave_present(void *data, struct snd_kcontrol *sctl) } /* guess the value corresponding to 0dB */ -static int get_kctl_0dB_offset(struct snd_kcontrol *kctl) +static int get_kctl_0dB_offset(struct snd_kcontrol *kctl, int *step_to_check) { int _tlv[4]; const int *tlv = NULL; @@ -2681,6 +2681,12 @@ static int get_kctl_0dB_offset(struct snd_kcontrol *kctl) step &= ~TLV_DB_SCALE_MUTE; if (!step) return -1; + if (*step_to_check && *step_to_check != step) { + snd_printk(KERN_ERR "hda_codec: Mismatching dB step for vmaster slave (%d!=%d)\n", + *step_to_check, step); + return -1; + } + *step_to_check = step; val = -tlv[2] / step; } return val; @@ -2703,7 +2709,7 @@ static int put_kctl_with_value(struct snd_kcontrol *kctl, int val) /* initialize the slave volume with 0dB */ static int init_slave_0dB(void *data, struct snd_kcontrol *slave) { - int offset = get_kctl_0dB_offset(slave); + int offset = get_kctl_0dB_offset(slave, data); if (offset > 0) put_kctl_with_value(slave, offset); return 0; @@ -2764,9 +2770,11 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name, /* init with master mute & zero volume */ put_kctl_with_value(kctl, 0); - if (init_slave_vol) + if (init_slave_vol) { + int step = 0; map_slaves(codec, slaves, suffix, - tlv ? init_slave_0dB : init_slave_unmute, kctl); + tlv ? init_slave_0dB : init_slave_unmute, &step); + } if (ctl_ret) *ctl_ret = kctl; -- cgit v0.10.2 From a1114a8c681b0724d6ad905f53ff06aa756f5fb8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 4 Nov 2013 16:32:01 +0100 Subject: ALSA: hda - Introduce the bitmask for excluding output volume Add a bitmask to hda_gen_spec indicating NIDs to exclude from the possible volume controls. That is, when the bit is set, the NID corresponding to the bit won't be picked as an output volume control any longer. Basically this is just a band-aid for working around the issue found with CS4208 codec, where only the headphone pin has a volume AMP with different dB steps. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=60811 Cc: [v3.12+] Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index b7c89df..276f6e7 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -549,11 +549,15 @@ static hda_nid_t look_for_out_mute_nid(struct hda_codec *codec, static hda_nid_t look_for_out_vol_nid(struct hda_codec *codec, struct nid_path *path) { + struct hda_gen_spec *spec = codec->spec; int i; for (i = path->depth - 1; i >= 0; i--) { - if (nid_has_volume(codec, path->path[i], HDA_OUTPUT)) - return path->path[i]; + hda_nid_t nid = path->path[i]; + if ((spec->out_vol_mask >> nid) & 1) + continue; + if (nid_has_volume(codec, nid, HDA_OUTPUT)) + return nid; } return 0; } diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 48d4402..7e45cb4 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -242,6 +242,9 @@ struct hda_gen_spec { /* additional mute flags (only effective with auto_mute_via_amp=1) */ u64 mute_bits; + /* bitmask for skipping volume controls */ + u64 out_vol_mask; + /* badness tables for output path evaluations */ const struct badness_table *main_out_badness; const struct badness_table *extra_out_badness; diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 18d9725..38d073e 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -660,6 +660,8 @@ static int patch_cs4208(struct hda_codec *codec) return -ENOMEM; spec->gen.automute_hook = cs_automute; + /* exclude NID 0x10 (HP) from output volumes due to different steps */ + spec->gen.out_vol_mask = 1ULL << 0x10; snd_hda_pick_fixup(codec, cs4208_models, cs4208_fixup_tbl, cs4208_fixups); -- cgit v0.10.2 From bbaa0d6665bc14133d7eb573d2b5ff898a06f365 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Tue, 5 Nov 2013 09:27:10 +0100 Subject: ALSA: hda: add device IDs for AMD Evergreen/Northern Islands HDMI The device IDs of the AMD Cypress/Juniper/Redwood/Cedar/Cayman/Antilles/ Barts/Turks/Caicos HDMI HDA controllers weren't added explicitly because the generic entry works, but it made the device appearing as "Generic", and people are confused as if it's no proper HDMI controller. Add them so that the name shows up properly as "ATI HDMI" instead of "Generic". According to Takashi's tests and the lack of complaints, these devices work fine without disabling snooping. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 66f7e1e..d6dcec7 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -4079,6 +4079,22 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, { PCI_DEVICE(0x1002, 0xaa48), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, + { PCI_DEVICE(0x1002, 0xaa50), + .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, + { PCI_DEVICE(0x1002, 0xaa58), + .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, + { PCI_DEVICE(0x1002, 0xaa60), + .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, + { PCI_DEVICE(0x1002, 0xaa68), + .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, + { PCI_DEVICE(0x1002, 0xaa80), + .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, + { PCI_DEVICE(0x1002, 0xaa88), + .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, + { PCI_DEVICE(0x1002, 0xaa90), + .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, + { PCI_DEVICE(0x1002, 0xaa98), + .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, { PCI_DEVICE(0x1002, 0x9902), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI }, { PCI_DEVICE(0x1002, 0xaaa0), -- cgit v0.10.2 From 94daf85e3c4db3b804205277eec7c4eae6efe9db Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 5 Nov 2013 10:30:14 +0100 Subject: pinctrl: at91: copy define to driver The #define for the maximum number of GPIO blocks was retrieved into pinctrl-at91.c by implicit inclusion of from creating a dependency on machine-local . Break the depenency by copying this single define into the driver. Acked-by: Nicolas Ferre Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index c4598dc..a7549c4 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -33,6 +33,7 @@ #include "core.h" +#define MAX_GPIO_BANKS 5 #define MAX_NB_GPIO_PER_BANK 32 struct at91_pinctrl_mux_ops; -- cgit v0.10.2 From b67ae3f1c97e2e8532ce051f0ed4365e9d796590 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Tue, 5 Nov 2013 13:11:37 +0100 Subject: ALSA: hda - Enable Thinkpad mute/micmute LEDs for Realtek Same as we already have for Conexant. Right now it's only enabled for one machine. Tested-by: Hui Wang Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 01bf812..02071a5 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -829,7 +829,11 @@ static inline void alc_shutup(struct hda_codec *codec) snd_hda_shutup_pins(codec); } -#define alc_free snd_hda_gen_free +static void alc_free(struct hda_codec *codec) +{ + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_FREE); + snd_hda_gen_free(codec); +} #ifdef CONFIG_PM static void alc_power_eapd(struct hda_codec *codec) @@ -3527,6 +3531,74 @@ static void alc290_fixup_mono_speakers(struct hda_codec *codec, snd_hda_override_wcaps(codec, 0x03, 0); } +#if IS_ENABLED(CONFIG_THINKPAD_ACPI) + +#include + +static int (*led_set_func)(int, bool); + +static void update_tpacpi_mute_led(void *private_data, int enabled) +{ + if (led_set_func) + led_set_func(TPACPI_LED_MUTE, !enabled); +} + +static void update_tpacpi_micmute_led(struct hda_codec *codec, + struct snd_ctl_elem_value *ucontrol) +{ + if (!ucontrol || !led_set_func) + return; + if (strcmp("Capture Switch", ucontrol->id.name) == 0 && ucontrol->id.index == 0) { + /* TODO: How do I verify if it's a mono or stereo here? */ + bool val = ucontrol->value.integer.value[0] || ucontrol->value.integer.value[1]; + led_set_func(TPACPI_LED_MICMUTE, !val); + } +} + +static void alc_fixup_thinkpad_acpi(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + bool removefunc = false; + + if (action == HDA_FIXUP_ACT_PROBE) { + if (!led_set_func) + led_set_func = symbol_request(tpacpi_led_set); + if (!led_set_func) { + snd_printk(KERN_WARNING "Failed to find thinkpad-acpi symbol tpacpi_led_set\n"); + return; + } + + removefunc = true; + if (led_set_func(TPACPI_LED_MUTE, false) >= 0) { + spec->gen.vmaster_mute.hook = update_tpacpi_mute_led; + removefunc = false; + } + if (led_set_func(TPACPI_LED_MICMUTE, false) >= 0) { + if (spec->gen.num_adc_nids > 1) + snd_printdd("Skipping micmute LED control due to several ADCs"); + else { + spec->gen.cap_sync_hook = update_tpacpi_micmute_led; + removefunc = false; + } + } + } + + if (led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) { + symbol_put(tpacpi_led_set); + led_set_func = NULL; + } +} + +#else + +static void alc_fixup_thinkpad_acpi(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ +} + +#endif + enum { ALC269_FIXUP_SONY_VAIO, ALC275_FIXUP_SONY_VAIO_GPIO2, @@ -3570,6 +3642,7 @@ enum { ALC282_FIXUP_ASUS_TX300, ALC283_FIXUP_INT_MIC, ALC290_FIXUP_MONO_SPEAKERS, + ALC269_FIXUP_THINKPAD_ACPI, }; static const struct hda_fixup alc269_fixups[] = { @@ -3867,6 +3940,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, }, + [ALC269_FIXUP_THINKPAD_ACPI] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_thinkpad_acpi, + .chained = true, + .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -3950,7 +4029,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x2208, "Thinkpad T431s", ALC269_FIXUP_LENOVO_DOCK), SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad", ALC269_FIXUP_THINKPAD_ACPI), SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC283_FIXUP_INT_MIC), -- cgit v0.10.2 From a1c22cdc77744aa2f72504c42fccadef739d4159 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 5 Nov 2013 10:29:49 +0100 Subject: ALSA: atmel: remove dependency on This include is completely unused since the AT91 sound driver actually uses gpiolib properly. Signed-off-by: Linus Walleij Acked-by: Nicolas Ferre Signed-off-by: Takashi Iwai diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c index ae63d22..c5f0ddd 100644 --- a/sound/atmel/ac97c.c +++ b/sound/atmel/ac97c.c @@ -34,7 +34,6 @@ #include #include -#include #ifdef CONFIG_ARCH_AT91 #include -- cgit v0.10.2 From 87419c9afff1431d4b62b388baf6bfa07e0b14ff Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 29 Oct 2013 10:43:16 -0600 Subject: perf kvm: Disable live command if timerfd is not supported If the OS does not have timerfd support (e.g., older OS'es like RHEL5) disable perf kvm stat live. Signed-off-by: David Ahern Reviewed-by: Jiri Olsa Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1383064996-20933-2-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index cb05f39..cd9f920 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -20,7 +20,9 @@ #include "util/data.h" #include +#ifdef HAVE_TIMERFD_SUPPORT #include +#endif #include #include @@ -337,6 +339,7 @@ static void init_kvm_event_record(struct perf_kvm_stat *kvm) INIT_LIST_HEAD(&kvm->kvm_events_cache[i]); } +#ifdef HAVE_TIMERFD_SUPPORT static void clear_events_cache_stats(struct list_head *kvm_events_cache) { struct list_head *head; @@ -358,6 +361,7 @@ static void clear_events_cache_stats(struct list_head *kvm_events_cache) } } } +#endif static int kvm_events_hash_fn(u64 key) { @@ -783,6 +787,7 @@ static void print_result(struct perf_kvm_stat *kvm) pr_info("\nLost events: %" PRIu64 "\n\n", kvm->lost_events); } +#ifdef HAVE_TIMERFD_SUPPORT static int process_lost_event(struct perf_tool *tool, union perf_event *event __maybe_unused, struct perf_sample *sample __maybe_unused, @@ -793,6 +798,7 @@ static int process_lost_event(struct perf_tool *tool, kvm->lost_events++; return 0; } +#endif static bool skip_sample(struct perf_kvm_stat *kvm, struct perf_sample *sample) @@ -872,6 +878,7 @@ static bool verify_vcpu(int vcpu) return true; } +#ifdef HAVE_TIMERFD_SUPPORT /* keeping the max events to a modest level to keep * the processing of samples per mmap smooth. */ @@ -1213,6 +1220,7 @@ static int kvm_live_open_events(struct perf_kvm_stat *kvm) out: return rc; } +#endif static int read_events(struct perf_kvm_stat *kvm) { @@ -1379,6 +1387,7 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv) return kvm_events_report_vcpu(kvm); } +#ifdef HAVE_TIMERFD_SUPPORT static struct perf_evlist *kvm_live_event_list(void) { struct perf_evlist *evlist; @@ -1566,6 +1575,7 @@ out: return err; } +#endif static void print_kvm_stat_usage(void) { @@ -1604,8 +1614,10 @@ static int kvm_cmd_stat(const char *file_name, int argc, const char **argv) if (!strncmp(argv[1], "rep", 3)) return kvm_events_report(&kvm, argc - 1 , argv + 1); +#ifdef HAVE_TIMERFD_SUPPORT if (!strncmp(argv[1], "live", 4)) return kvm_events_live(&kvm, argc - 1 , argv + 1); +#endif perf_stat: return cmd_stat(argc, argv, NULL); diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index ffb5f55..be8bb9a 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -397,6 +397,13 @@ else endif endif +$(call feature_check,timerfd) +ifeq ($(feature-timerfd), 1) + CFLAGS += -DHAVE_TIMERFD_SUPPORT +else + msg := $(warning No timerfd support. Disables 'perf kvm stat live'); +endif + disable-python = $(eval $(disable-python_code)) define disable-python_code CFLAGS += -DNO_LIBPYTHON diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index d37d58d..c803f17 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -25,7 +25,8 @@ FILES= \ test-libunwind \ test-on-exit \ test-stackprotector-all \ - test-stackprotector + test-stackprotector \ + test-timerfd CC := $(CC) -MD @@ -136,6 +137,9 @@ test-on-exit: test-backtrace: $(BUILD) +test-timerfd: + $(BUILD) + -include *.d ############################### diff --git a/tools/perf/config/feature-checks/test-all.c b/tools/perf/config/feature-checks/test-all.c index 50d4318..59e7a70 100644 --- a/tools/perf/config/feature-checks/test-all.c +++ b/tools/perf/config/feature-checks/test-all.c @@ -81,6 +81,10 @@ # include "test-libnuma.c" #undef main +#define main main_test_timerfd +# include "test-timerfd.c" +#undef main + int main(int argc, char *argv[]) { main_test_libpython(); @@ -101,6 +105,7 @@ int main(int argc, char *argv[]) main_test_on_exit(); main_test_backtrace(); main_test_libnuma(); + main_test_timerfd(); return 0; } diff --git a/tools/perf/config/feature-checks/test-timerfd.c b/tools/perf/config/feature-checks/test-timerfd.c new file mode 100644 index 0000000..8c5c083 --- /dev/null +++ b/tools/perf/config/feature-checks/test-timerfd.c @@ -0,0 +1,18 @@ +/* + * test for timerfd functions used by perf-kvm-stat-live + */ +#include + +int main(void) +{ + struct itimerspec new_value; + + int fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); + if (fd < 0) + return 1; + + if (timerfd_settime(fd, 0, &new_value, NULL) != 0) + return 1; + + return 0; +} -- cgit v0.10.2 From 5febff0066b8111785d58903b54d414e9ec6a3d0 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 29 Oct 2013 10:43:15 -0600 Subject: tools/perf/build: Fix detection of non-core features feature_check needs to be invoked through call, and LDFLAGS may not be set so quotes are needed. Thanks to Jiri for spotting the quotes around LDFLAGS; that one was driving me nuts with the upcoming timerfd feature detection. Signed-off-by: David Ahern Reviewed-by: Jiri Olsa Tested-by: Jiri Olsa Acked-by: Ingo Molnar Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1383064996-20933-1-git-send-email-dsahern@gmail.com [ Fixed conflict with 8a0c4c2843d3 ("perf tools: Fix libunwind build and feature detection for 32-bit build") ] Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index be8bb9a..58b2d37 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -98,7 +98,7 @@ endif feature_check = $(eval $(feature_check_code)) define feature_check_code - feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS)" LDFLAGS=$(LDFLAGS) LIBUNWIND_LIBS="$(LIBUNWIND_LIBS)" -C config/feature-checks test-$1 >/dev/null 2>/dev/null && echo 1 || echo 0) + feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS)" LDFLAGS="$(LDFLAGS)" LIBUNWIND_LIBS="$(LIBUNWIND_LIBS)" -C config/feature-checks test-$1 >/dev/null 2>/dev/null && echo 1 || echo 0) endef feature_set = $(eval $(feature_set_code)) @@ -235,7 +235,7 @@ CFLAGS += -I$(LIB_INCLUDE) CFLAGS += -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE ifndef NO_BIONIC - $(feature_check,bionic) + $(call feature_check,bionic) ifeq ($(feature-bionic), 1) BIONIC := 1 EXTLIBS := $(filter-out -lrt,$(EXTLIBS)) @@ -479,15 +479,15 @@ else CFLAGS += -DHAVE_CPLUS_DEMANGLE_SUPPORT else ifneq ($(feature-libbfd), 1) - $(feature_check,liberty) + $(call feature_check,liberty) ifeq ($(feature-liberty), 1) EXTLIBS += -lbfd -liberty else - $(feature_check,liberty-z) + $(call feature_check,liberty-z) ifeq ($(feature-liberty-z), 1) EXTLIBS += -lbfd -liberty -lz else - $(feature_check,cplus-demangle) + $(call feature_check,cplus-demangle) ifeq ($(feature-cplus-demangle), 1) EXTLIBS += -liberty CFLAGS += -DHAVE_CPLUS_DEMANGLE_SUPPORT -- cgit v0.10.2 From 33499a15c2f7addc81695778753c2338b960eff7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 17:34:46 +0100 Subject: ALSA: hda - Force buffer alignment for Haswell HDMI controllers Haswell HDMI audio controllers seem to get stuck when unaligned buffer size is used. Let's enable the buffer alignment for the corresponding entries. Since AZX_DCAPS_INTEL_PCH contains AZX_DCAPS_BUFSIZE that disables the buffer alignment forcibly, define AZX_DCAPS_INTEL_HASWELL and put the necessary AZX_DCAPS bits there. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=60769 Reported-by: Alexander E. Patrakov Cc: Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index d6dcec7..9cbd125 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -613,6 +613,11 @@ enum { #define AZX_DCAPS_INTEL_PCH \ (AZX_DCAPS_INTEL_PCH_NOPM | AZX_DCAPS_PM_RUNTIME) +#define AZX_DCAPS_INTEL_HASWELL \ + (AZX_DCAPS_SCH_SNOOP | AZX_DCAPS_ALIGN_BUFSIZE | \ + AZX_DCAPS_COUNT_LPIB_DELAY | AZX_DCAPS_PM_RUNTIME | \ + AZX_DCAPS_I915_POWERWELL) + /* quirks for ATI SB / AMD Hudson */ #define AZX_DCAPS_PRESET_ATI_SB \ (AZX_DCAPS_ATI_SNOOP | AZX_DCAPS_NO_TCSEL | \ @@ -3992,14 +3997,11 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, /* Haswell */ { PCI_DEVICE(0x8086, 0x0a0c), - .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH | - AZX_DCAPS_I915_POWERWELL }, + .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_HASWELL }, { PCI_DEVICE(0x8086, 0x0c0c), - .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH | - AZX_DCAPS_I915_POWERWELL }, + .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_HASWELL }, { PCI_DEVICE(0x8086, 0x0d0c), - .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH | - AZX_DCAPS_I915_POWERWELL }, + .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_HASWELL }, /* 5 Series/3400 */ { PCI_DEVICE(0x8086, 0x3b56), .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM }, -- cgit v0.10.2 From fab1285a51b7bf55adb4678d82e606829c9dab85 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 17:54:05 +0100 Subject: ALSA: hda - Name Haswell HDMI controllers better "HDA Intel MID" is no correct name for Haswell HDMI controllers. Give them a better name, "HDA Intel HDMI". Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 9cbd125..17f4aa8 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -569,6 +569,7 @@ enum { AZX_DRIVER_ICH, AZX_DRIVER_PCH, AZX_DRIVER_SCH, + AZX_DRIVER_HDMI, AZX_DRIVER_ATI, AZX_DRIVER_ATIHDMI, AZX_DRIVER_ATIHDMI_NS, @@ -648,6 +649,7 @@ static char *driver_short_names[] = { [AZX_DRIVER_ICH] = "HDA Intel", [AZX_DRIVER_PCH] = "HDA Intel PCH", [AZX_DRIVER_SCH] = "HDA Intel MID", + [AZX_DRIVER_HDMI] = "HDA Intel HDMI", [AZX_DRIVER_ATI] = "HDA ATI SB", [AZX_DRIVER_ATIHDMI] = "HDA ATI HDMI", [AZX_DRIVER_ATIHDMI_NS] = "HDA ATI HDMI", @@ -3997,11 +3999,11 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, /* Haswell */ { PCI_DEVICE(0x8086, 0x0a0c), - .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_HASWELL }, + .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL }, { PCI_DEVICE(0x8086, 0x0c0c), - .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_HASWELL }, + .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL }, { PCI_DEVICE(0x8086, 0x0d0c), - .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_HASWELL }, + .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL }, /* 5 Series/3400 */ { PCI_DEVICE(0x8086, 0x3b56), .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM }, -- cgit v0.10.2 From 8e00ddc9dfe09ee131144fdaf6c96ebe95bbbbcb Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 30 Oct 2013 10:15:06 -0600 Subject: perf list: Remove a level of indentation With a return after the if check an indentation level can be removed. Indentation shift only; no functional changes. Signed-off-by: David Ahern Acked-by: Ingo Molnar Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1383149707-1008-1-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c index e79f423..45000e7 100644 --- a/tools/perf/builtin-list.c +++ b/tools/perf/builtin-list.c @@ -17,48 +17,49 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) { + int i; + setup_pager(); - if (argc == 1) + if (argc == 1) { print_events(NULL, false); - else { - int i; - - for (i = 1; i < argc; ++i) { - if (i > 2) - putchar('\n'); - if (strncmp(argv[i], "tracepoint", 10) == 0) - print_tracepoint_events(NULL, NULL, false); - else if (strcmp(argv[i], "hw") == 0 || - strcmp(argv[i], "hardware") == 0) - print_events_type(PERF_TYPE_HARDWARE); - else if (strcmp(argv[i], "sw") == 0 || - strcmp(argv[i], "software") == 0) - print_events_type(PERF_TYPE_SOFTWARE); - else if (strcmp(argv[i], "cache") == 0 || - strcmp(argv[i], "hwcache") == 0) - print_hwcache_events(NULL, false); - else if (strcmp(argv[i], "pmu") == 0) - print_pmu_events(NULL, false); - else if (strcmp(argv[i], "--raw-dump") == 0) - print_events(NULL, true); - else { - char *sep = strchr(argv[i], ':'), *s; - int sep_idx; + return 0; + } - if (sep == NULL) { - print_events(argv[i], false); - continue; - } - sep_idx = sep - argv[i]; - s = strdup(argv[i]); - if (s == NULL) - return -1; + for (i = 1; i < argc; ++i) { + if (i > 2) + putchar('\n'); + if (strncmp(argv[i], "tracepoint", 10) == 0) + print_tracepoint_events(NULL, NULL, false); + else if (strcmp(argv[i], "hw") == 0 || + strcmp(argv[i], "hardware") == 0) + print_events_type(PERF_TYPE_HARDWARE); + else if (strcmp(argv[i], "sw") == 0 || + strcmp(argv[i], "software") == 0) + print_events_type(PERF_TYPE_SOFTWARE); + else if (strcmp(argv[i], "cache") == 0 || + strcmp(argv[i], "hwcache") == 0) + print_hwcache_events(NULL, false); + else if (strcmp(argv[i], "pmu") == 0) + print_pmu_events(NULL, false); + else if (strcmp(argv[i], "--raw-dump") == 0) + print_events(NULL, true); + else { + char *sep = strchr(argv[i], ':'), *s; + int sep_idx; - s[sep_idx] = '\0'; - print_tracepoint_events(s, s + sep_idx + 1, false); - free(s); + if (sep == NULL) { + print_events(argv[i], false); + continue; } + sep_idx = sep - argv[i]; + s = strdup(argv[i]); + if (s == NULL) + return -1; + + s[sep_idx] = '\0'; + print_tracepoint_events(s, s + sep_idx + 1, false); + free(s); } } return 0; -- cgit v0.10.2 From 44d742e01e6d3dd544ee1873b660a3c8bc1413bb Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 30 Oct 2013 10:28:29 -0600 Subject: perf list: Add usage Currently 'perf list' is not very helpful if you forget the syntax: $ perf list -h List of pre-defined events (to be used in -e): After: $ perf list -h usage: perf list [hw|sw|cache|tracepoint|pmu|event_glob] Signed-off-by: David Ahern Acked-by: Ingo Molnar Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/527133AD.4030003@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c index 45000e7..011195e 100644 --- a/tools/perf/builtin-list.c +++ b/tools/perf/builtin-list.c @@ -14,20 +14,31 @@ #include "util/parse-events.h" #include "util/cache.h" #include "util/pmu.h" +#include "util/parse-options.h" int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) { int i; + const struct option list_options[] = { + OPT_END() + }; + const char * const list_usage[] = { + "perf list [hw|sw|cache|tracepoint|pmu|event_glob]", + NULL + }; + + argc = parse_options(argc, argv, list_options, list_usage, + PARSE_OPT_STOP_AT_NON_OPTION); setup_pager(); - if (argc == 1) { + if (argc == 0) { print_events(NULL, false); return 0; } - for (i = 1; i < argc; ++i) { - if (i > 2) + for (i = 0; i < argc; ++i) { + if (i) putchar('\n'); if (strncmp(argv[i], "tracepoint", 10) == 0) print_tracepoint_events(NULL, NULL, false); -- cgit v0.10.2 From 4299a549979783668d787959d61ba22b6b200877 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 5 Nov 2013 15:14:45 +0100 Subject: perf tools: Factor sysfs code into generic fs object Moving sysfs code into generic fs object and preparing it to carry procfs support. This should be merged with tools/lib/lk/debugfs.c at some point in the future. Signed-off-by: Jiri Olsa Cc: Adrian Hunter Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1383660887-1734-2-git-send-email-jolsa@redhat.com [ Added fs__ namespace qualifier to some more functions ] Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 5b86390..7fc8f17 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -242,7 +242,7 @@ LIB_H += util/cache.h LIB_H += util/callchain.h LIB_H += util/build-id.h LIB_H += util/debug.h -LIB_H += util/sysfs.h +LIB_H += util/fs.h LIB_H += util/pmu.h LIB_H += util/event.h LIB_H += util/evsel.h @@ -304,7 +304,7 @@ LIB_OBJS += $(OUTPUT)util/annotate.o LIB_OBJS += $(OUTPUT)util/build-id.o LIB_OBJS += $(OUTPUT)util/config.o LIB_OBJS += $(OUTPUT)util/ctype.o -LIB_OBJS += $(OUTPUT)util/sysfs.o +LIB_OBJS += $(OUTPUT)util/fs.o LIB_OBJS += $(OUTPUT)util/pmu.o LIB_OBJS += $(OUTPUT)util/environment.o LIB_OBJS += $(OUTPUT)util/event.o diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 48114d1..f47bf45 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -2,7 +2,7 @@ #include "parse-events.h" #include "evsel.h" #include "evlist.h" -#include "sysfs.h" +#include "fs.h" #include #include "tests.h" #include diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index beb8cf9..4af5a23 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -1,5 +1,5 @@ #include "util.h" -#include "sysfs.h" +#include "fs.h" #include "../perf.h" #include "cpumap.h" #include diff --git a/tools/perf/util/fs.c b/tools/perf/util/fs.c new file mode 100644 index 0000000..a2413e8 --- /dev/null +++ b/tools/perf/util/fs.c @@ -0,0 +1,107 @@ + +/* TODO merge/factor into tools/lib/lk/debugfs.c */ + +#include "util.h" +#include "util/fs.h" + +static const char * const sysfs__fs_known_mountpoints[] = { + "/sys", + 0, +}; + +struct fs { + const char *name; + const char * const *mounts; + char path[PATH_MAX + 1]; + bool found; + long magic; +}; + +enum { + FS__SYSFS = 0, +}; + +static struct fs fs__entries[] = { + [FS__SYSFS] = { + .name = "sysfs", + .mounts = sysfs__fs_known_mountpoints, + .magic = SYSFS_MAGIC, + }, +}; + +static bool fs__read_mounts(struct fs *fs) +{ + bool found = false; + char type[100]; + FILE *fp; + + fp = fopen("/proc/mounts", "r"); + if (fp == NULL) + return NULL; + + while (!found && + fscanf(fp, "%*s %" STR(PATH_MAX) "s %99s %*s %*d %*d\n", + fs->path, type) == 2) { + + if (strcmp(type, fs->name) == 0) + found = true; + } + + fclose(fp); + return fs->found = found; +} + +static int fs__valid_mount(const char *fs, long magic) +{ + struct statfs st_fs; + + if (statfs(fs, &st_fs) < 0) + return -ENOENT; + else if (st_fs.f_type != magic) + return -ENOENT; + + return 0; +} + +static bool fs__check_mounts(struct fs *fs) +{ + const char * const *ptr; + + ptr = fs->mounts; + while (*ptr) { + if (fs__valid_mount(*ptr, fs->magic) == 0) { + fs->found = true; + strcpy(fs->path, *ptr); + return true; + } + ptr++; + } + + return false; +} + +static const char *fs__get_mountpoint(struct fs *fs) +{ + if (fs__check_mounts(fs)) + return fs->path; + + return fs__read_mounts(fs) ? fs->path : NULL; +} + +static const char *fs__find_mountpoint(int idx) +{ + struct fs *fs = &fs__entries[idx]; + + if (fs->found) + return (const char *)fs->path; + + return fs__get_mountpoint(fs); +} + +#define FIND_MOUNTPOINT(name, idx) \ +const char *name##_find_mountpoint(void) \ +{ \ + return fs__find_mountpoint(idx); \ +} + +FIND_MOUNTPOINT(sysfs, FS__SYSFS); diff --git a/tools/perf/util/fs.h b/tools/perf/util/fs.h new file mode 100644 index 0000000..082edbd --- /dev/null +++ b/tools/perf/util/fs.h @@ -0,0 +1,6 @@ +#ifndef __PERF_FS +#define __PERF_FS + +const char *sysfs_find_mountpoint(void); + +#endif /* __PERF_FS */ diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 64362fe..45b42df 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -4,7 +4,7 @@ #include #include #include -#include "sysfs.h" +#include "fs.h" #include "util.h" #include "pmu.h" #include "parse-events.h" diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources index f75ae1b..239036f 100644 --- a/tools/perf/util/python-ext-sources +++ b/tools/perf/util/python-ext-sources @@ -17,5 +17,5 @@ util/xyarray.c util/cgroup.c util/rblist.c util/strlist.c -util/sysfs.c +util/fs.c ../../lib/rbtree.c diff --git a/tools/perf/util/sysfs.c b/tools/perf/util/sysfs.c deleted file mode 100644 index f71e9ea..0000000 --- a/tools/perf/util/sysfs.c +++ /dev/null @@ -1,60 +0,0 @@ - -#include "util.h" -#include "sysfs.h" - -static const char * const sysfs_known_mountpoints[] = { - "/sys", - 0, -}; - -static int sysfs_found; -char sysfs_mountpoint[PATH_MAX + 1]; - -static int sysfs_valid_mountpoint(const char *sysfs) -{ - struct statfs st_fs; - - if (statfs(sysfs, &st_fs) < 0) - return -ENOENT; - else if (st_fs.f_type != (long) SYSFS_MAGIC) - return -ENOENT; - - return 0; -} - -const char *sysfs_find_mountpoint(void) -{ - const char * const *ptr; - char type[100]; - FILE *fp; - - if (sysfs_found) - return (const char *) sysfs_mountpoint; - - ptr = sysfs_known_mountpoints; - while (*ptr) { - if (sysfs_valid_mountpoint(*ptr) == 0) { - sysfs_found = 1; - strcpy(sysfs_mountpoint, *ptr); - return sysfs_mountpoint; - } - ptr++; - } - - /* give up and parse /proc/mounts */ - fp = fopen("/proc/mounts", "r"); - if (fp == NULL) - return NULL; - - while (!sysfs_found && - fscanf(fp, "%*s %" STR(PATH_MAX) "s %99s %*s %*d %*d\n", - sysfs_mountpoint, type) == 2) { - - if (strcmp(type, "sysfs") == 0) - sysfs_found = 1; - } - - fclose(fp); - - return sysfs_found ? sysfs_mountpoint : NULL; -} diff --git a/tools/perf/util/sysfs.h b/tools/perf/util/sysfs.h deleted file mode 100644 index a813b72..0000000 --- a/tools/perf/util/sysfs.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __SYSFS_H__ -#define __SYSFS_H__ - -const char *sysfs_find_mountpoint(void); - -#endif /* __DEBUGFS_H__ */ -- cgit v0.10.2 From cf38fadade52df937521dd70d4437df1a9354cd9 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 5 Nov 2013 14:48:50 -0300 Subject: perf fs: Rename NAME_find_mountpoint() to NAME__mountpoint() Shorten it, "finding" it is an implementation detail, what callers want is the pathname, not to ask for it to _always_ do the lookup. And the existing implementation already caches it, i.e. it doesn't "finds" it on every call. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-r24wa4bvtccg7mnkessrbbdj@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index f47bf45..ef671cd 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -1456,7 +1456,7 @@ static int test_pmu(void) int ret; snprintf(path, PATH_MAX, "%s/bus/event_source/devices/cpu/format/", - sysfs_find_mountpoint()); + sysfs__mountpoint()); ret = stat(path, &st); if (ret) @@ -1473,7 +1473,7 @@ static int test_pmu_events(void) int ret; snprintf(path, PATH_MAX, "%s/bus/event_source/devices/cpu/events/", - sysfs_find_mountpoint()); + sysfs__mountpoint()); ret = stat(path, &st); if (ret) { diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 4af5a23..a9b48c4 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -216,7 +216,7 @@ int cpu_map__get_socket(struct cpu_map *map, int idx) cpu = map->map[idx]; - mnt = sysfs_find_mountpoint(); + mnt = sysfs__mountpoint(); if (!mnt) return -1; @@ -279,7 +279,7 @@ int cpu_map__get_core(struct cpu_map *map, int idx) cpu = map->map[idx]; - mnt = sysfs_find_mountpoint(); + mnt = sysfs__mountpoint(); if (!mnt) return -1; diff --git a/tools/perf/util/fs.c b/tools/perf/util/fs.c index a2413e8..77bac4e 100644 --- a/tools/perf/util/fs.c +++ b/tools/perf/util/fs.c @@ -88,7 +88,7 @@ static const char *fs__get_mountpoint(struct fs *fs) return fs__read_mounts(fs) ? fs->path : NULL; } -static const char *fs__find_mountpoint(int idx) +static const char *fs__mountpoint(int idx) { struct fs *fs = &fs__entries[idx]; @@ -98,10 +98,10 @@ static const char *fs__find_mountpoint(int idx) return fs__get_mountpoint(fs); } -#define FIND_MOUNTPOINT(name, idx) \ -const char *name##_find_mountpoint(void) \ -{ \ - return fs__find_mountpoint(idx); \ +#define FS__MOUNTPOINT(name, idx) \ +const char *name##__mountpoint(void) \ +{ \ + return fs__mountpoint(idx); \ } -FIND_MOUNTPOINT(sysfs, FS__SYSFS); +FS__MOUNTPOINT(sysfs, FS__SYSFS); diff --git a/tools/perf/util/fs.h b/tools/perf/util/fs.h index 082edbd..a7561c8 100644 --- a/tools/perf/util/fs.h +++ b/tools/perf/util/fs.h @@ -1,6 +1,6 @@ #ifndef __PERF_FS #define __PERF_FS -const char *sysfs_find_mountpoint(void); +const char *sysfs__mountpoint(void); #endif /* __PERF_FS */ diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 45b42df..c232d8d 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -77,9 +77,8 @@ static int pmu_format(const char *name, struct list_head *format) { struct stat st; char path[PATH_MAX]; - const char *sysfs; + const char *sysfs = sysfs__mountpoint(); - sysfs = sysfs_find_mountpoint(); if (!sysfs) return -1; @@ -166,9 +165,8 @@ static int pmu_aliases(const char *name, struct list_head *head) { struct stat st; char path[PATH_MAX]; - const char *sysfs; + const char *sysfs = sysfs__mountpoint(); - sysfs = sysfs_find_mountpoint(); if (!sysfs) return -1; @@ -212,11 +210,10 @@ static int pmu_type(const char *name, __u32 *type) { struct stat st; char path[PATH_MAX]; - const char *sysfs; FILE *file; int ret = 0; + const char *sysfs = sysfs__mountpoint(); - sysfs = sysfs_find_mountpoint(); if (!sysfs) return -1; @@ -241,11 +238,10 @@ static int pmu_type(const char *name, __u32 *type) static void pmu_read_sysfs(void) { char path[PATH_MAX]; - const char *sysfs; DIR *dir; struct dirent *dent; + const char *sysfs = sysfs__mountpoint(); - sysfs = sysfs_find_mountpoint(); if (!sysfs) return; @@ -270,11 +266,10 @@ static struct cpu_map *pmu_cpumask(const char *name) { struct stat st; char path[PATH_MAX]; - const char *sysfs; FILE *file; struct cpu_map *cpus; + const char *sysfs = sysfs__mountpoint(); - sysfs = sysfs_find_mountpoint(); if (!sysfs) return NULL; -- cgit v0.10.2 From a9862418547e818aa5842840aecfa81d733b97e9 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 5 Nov 2013 15:14:46 +0100 Subject: perf fs: Add procfs support Adding procfs support into fs class. The interface function: const char *procfs__mountpoint(void); provides existing mountpoint path for procfs. Signed-off-by: Jiri Olsa Cc: Adrian Hunter Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1383660887-1734-3-git-send-email-jolsa@redhat.com [ Fixup namespace ] Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/fs.c b/tools/perf/util/fs.c index 77bac4e..f5be1f2 100644 --- a/tools/perf/util/fs.c +++ b/tools/perf/util/fs.c @@ -9,6 +9,11 @@ static const char * const sysfs__fs_known_mountpoints[] = { 0, }; +static const char * const procfs__known_mountpoints[] = { + "/proc", + 0, +}; + struct fs { const char *name; const char * const *mounts; @@ -18,7 +23,8 @@ struct fs { }; enum { - FS__SYSFS = 0, + FS__SYSFS = 0, + FS__PROCFS = 1, }; static struct fs fs__entries[] = { @@ -27,6 +33,11 @@ static struct fs fs__entries[] = { .mounts = sysfs__fs_known_mountpoints, .magic = SYSFS_MAGIC, }, + [FS__PROCFS] = { + .name = "proc", + .mounts = procfs__known_mountpoints, + .magic = PROC_SUPER_MAGIC, + }, }; static bool fs__read_mounts(struct fs *fs) @@ -104,4 +115,5 @@ const char *name##__mountpoint(void) \ return fs__mountpoint(idx); \ } -FS__MOUNTPOINT(sysfs, FS__SYSFS); +FS__MOUNTPOINT(sysfs, FS__SYSFS); +FS__MOUNTPOINT(procfs, FS__PROCFS); diff --git a/tools/perf/util/fs.h b/tools/perf/util/fs.h index a7561c8..5e09ce1 100644 --- a/tools/perf/util/fs.h +++ b/tools/perf/util/fs.h @@ -2,5 +2,6 @@ #define __PERF_FS const char *sysfs__mountpoint(void); +const char *procfs__mountpoint(void); #endif /* __PERF_FS */ diff --git a/tools/perf/util/include/linux/magic.h b/tools/perf/util/include/linux/magic.h index 58b64ed..07d63cf 100644 --- a/tools/perf/util/include/linux/magic.h +++ b/tools/perf/util/include/linux/magic.h @@ -9,4 +9,8 @@ #define SYSFS_MAGIC 0x62656572 #endif +#ifndef PROC_SUPER_MAGIC +#define PROC_SUPER_MAGIC 0x9fa0 +#endif + #endif -- cgit v0.10.2 From 714647bdc516330e4405b39677d7f763e016c685 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 5 Nov 2013 15:14:47 +0100 Subject: perf tools: Check maximum frequency rate for record/top Adding the check for maximum allowed frequency rate defined in following file: /proc/sys/kernel/perf_event_max_sample_rate When we cross the maximum value we fail and display detailed error message with advise. $ perf record -F 3000 ls Maximum frequency rate (2000) reached. Please use -F freq option with lower value or consider tweaking /proc/sys/kernel/perf_event_max_sample_rate. In case user does not specify the frequency and the default value cross the maximum, we display warning and set the frequency value to the current maximum. $ perf record ls Lowering default frequency rate to 2000. Please consider tweaking /proc/sys/kernel/perf_event_max_sample_rate. Same messages are used for 'perf top'. Signed-off-by: Jiri Olsa Cc: Adrian Hunter Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1383660887-1734-4-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 8b45fce..ea4c04f 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -958,20 +958,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) if (perf_evlist__create_maps(evsel_list, &rec->opts.target) < 0) usage_with_options(record_usage, record_options); - if (rec->opts.user_interval != ULLONG_MAX) - rec->opts.default_interval = rec->opts.user_interval; - if (rec->opts.user_freq != UINT_MAX) - rec->opts.freq = rec->opts.user_freq; - - /* - * User specified count overrides default frequency. - */ - if (rec->opts.default_interval) - rec->opts.freq = 0; - else if (rec->opts.freq) { - rec->opts.default_interval = rec->opts.freq; - } else { - ui__error("frequency and count are zero, aborting\n"); + if (perf_record_opts__config(&rec->opts)) { err = -EINVAL; goto out_free_fd; } diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 21897f0..9acca88 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1209,20 +1209,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) if (top.delay_secs < 1) top.delay_secs = 1; - if (opts->user_interval != ULLONG_MAX) - opts->default_interval = opts->user_interval; - if (opts->user_freq != UINT_MAX) - opts->freq = opts->user_freq; - - /* - * User specified count overrides default frequency. - */ - if (opts->default_interval) - opts->freq = 0; - else if (opts->freq) { - opts->default_interval = opts->freq; - } else { - ui__error("frequency and count are zero, aborting\n"); + if (perf_record_opts__config(opts)) { status = -EINVAL; goto out_delete_maps; } diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 6e8acc9..0617ce2 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -99,6 +99,7 @@ void perf_evlist__set_id_pos(struct perf_evlist *evlist); bool perf_can_sample_identifier(void); void perf_evlist__config(struct perf_evlist *evlist, struct perf_record_opts *opts); +int perf_record_opts__config(struct perf_record_opts *opts); int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct perf_target *target, diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c index 18d73aa..c8845b1 100644 --- a/tools/perf/util/record.c +++ b/tools/perf/util/record.c @@ -2,6 +2,8 @@ #include "evsel.h" #include "cpumap.h" #include "parse-events.h" +#include "fs.h" +#include "util.h" typedef void (*setup_probe_fn_t)(struct perf_evsel *evsel); @@ -106,3 +108,72 @@ void perf_evlist__config(struct perf_evlist *evlist, perf_evlist__set_id_pos(evlist); } + +static int get_max_rate(unsigned int *rate) +{ + char path[PATH_MAX]; + const char *procfs = procfs__mountpoint(); + + if (!procfs) + return -1; + + snprintf(path, PATH_MAX, + "%s/sys/kernel/perf_event_max_sample_rate", procfs); + + return filename__read_int(path, (int *) rate); +} + +static int perf_record_opts__config_freq(struct perf_record_opts *opts) +{ + bool user_freq = opts->user_freq != UINT_MAX; + unsigned int max_rate; + + if (opts->user_interval != ULLONG_MAX) + opts->default_interval = opts->user_interval; + if (user_freq) + opts->freq = opts->user_freq; + + /* + * User specified count overrides default frequency. + */ + if (opts->default_interval) + opts->freq = 0; + else if (opts->freq) { + opts->default_interval = opts->freq; + } else { + pr_err("frequency and count are zero, aborting\n"); + return -1; + } + + if (get_max_rate(&max_rate)) + return 0; + + /* + * User specified frequency is over current maximum. + */ + if (user_freq && (max_rate < opts->freq)) { + pr_err("Maximum frequency rate (%u) reached.\n" + "Please use -F freq option with lower value or consider\n" + "tweaking /proc/sys/kernel/perf_event_max_sample_rate.\n", + max_rate); + return -1; + } + + /* + * Default frequency is over current maximum. + */ + if (max_rate < opts->freq) { + pr_warning("Lowering default frequency rate to %u.\n" + "Please consider tweaking " + "/proc/sys/kernel/perf_event_max_sample_rate.\n", + max_rate); + opts->freq = max_rate; + } + + return 0; +} + +int perf_record_opts__config(struct perf_record_opts *opts) +{ + return perf_record_opts__config_freq(opts); +} -- cgit v0.10.2 From 316c7136f8bad924609163b9b115f68d59a68c82 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 5 Nov 2013 15:32:36 -0300 Subject: perf tools: Finish the removal of 'self' arguments They convey no information, perhaps I was bitten by some snake at some point, complete the detox by naming the last of those arguments more sensibly. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-u1r0dnjoro08dgztiy2g3t2q@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/scripts/python/Perf-Trace-Util/Context.c b/tools/perf/scripts/python/Perf-Trace-Util/Context.c index 315067b..fcd1dd6 100644 --- a/tools/perf/scripts/python/Perf-Trace-Util/Context.c +++ b/tools/perf/scripts/python/Perf-Trace-Util/Context.c @@ -25,7 +25,7 @@ PyMODINIT_FUNC initperf_trace_context(void); -static PyObject *perf_trace_context_common_pc(PyObject *self, PyObject *args) +static PyObject *perf_trace_context_common_pc(PyObject *obj, PyObject *args) { static struct scripting_context *scripting_context; PyObject *context; @@ -40,7 +40,7 @@ static PyObject *perf_trace_context_common_pc(PyObject *self, PyObject *args) return Py_BuildValue("i", retval); } -static PyObject *perf_trace_context_common_flags(PyObject *self, +static PyObject *perf_trace_context_common_flags(PyObject *obj, PyObject *args) { static struct scripting_context *scripting_context; @@ -56,7 +56,7 @@ static PyObject *perf_trace_context_common_flags(PyObject *self, return Py_BuildValue("i", retval); } -static PyObject *perf_trace_context_common_lock_depth(PyObject *self, +static PyObject *perf_trace_context_common_lock_depth(PyObject *obj, PyObject *args) { static struct scripting_context *scripting_context; diff --git a/tools/perf/ui/browser.h b/tools/perf/ui/browser.h index 404ff66a..7d45d2f 100644 --- a/tools/perf/ui/browser.h +++ b/tools/perf/ui/browser.h @@ -21,32 +21,32 @@ struct ui_browser { void *priv; const char *title; char *helpline; - unsigned int (*refresh)(struct ui_browser *self); - void (*write)(struct ui_browser *self, void *entry, int row); - void (*seek)(struct ui_browser *self, off_t offset, int whence); - bool (*filter)(struct ui_browser *self, void *entry); + unsigned int (*refresh)(struct ui_browser *browser); + void (*write)(struct ui_browser *browser, void *entry, int row); + void (*seek)(struct ui_browser *browser, off_t offset, int whence); + bool (*filter)(struct ui_browser *browser, void *entry); u32 nr_entries; bool navkeypressed; bool use_navkeypressed; }; int ui_browser__set_color(struct ui_browser *browser, int color); -void ui_browser__set_percent_color(struct ui_browser *self, +void ui_browser__set_percent_color(struct ui_browser *browser, double percent, bool current); -bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row); -void ui_browser__refresh_dimensions(struct ui_browser *self); -void ui_browser__reset_index(struct ui_browser *self); +bool ui_browser__is_current_entry(struct ui_browser *browser, unsigned row); +void ui_browser__refresh_dimensions(struct ui_browser *browser); +void ui_browser__reset_index(struct ui_browser *browser); -void ui_browser__gotorc(struct ui_browser *self, int y, int x); +void ui_browser__gotorc(struct ui_browser *browser, int y, int x); void ui_browser__write_graph(struct ui_browser *browser, int graph); void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column, u64 start, u64 end); void __ui_browser__show_title(struct ui_browser *browser, const char *title); void ui_browser__show_title(struct ui_browser *browser, const char *title); -int ui_browser__show(struct ui_browser *self, const char *title, +int ui_browser__show(struct ui_browser *browser, const char *title, const char *helpline, ...); -void ui_browser__hide(struct ui_browser *self); -int ui_browser__refresh(struct ui_browser *self); +void ui_browser__hide(struct ui_browser *browser); +int ui_browser__refresh(struct ui_browser *browser); int ui_browser__run(struct ui_browser *browser, int delay_secs); void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries); void ui_browser__handle_resize(struct ui_browser *browser); @@ -63,11 +63,11 @@ int ui_browser__input_window(const char *title, const char *text, char *input, void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence); unsigned int ui_browser__argv_refresh(struct ui_browser *browser); -void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence); -unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self); +void ui_browser__rb_tree_seek(struct ui_browser *browser, off_t offset, int whence); +unsigned int ui_browser__rb_tree_refresh(struct ui_browser *browser); -void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence); -unsigned int ui_browser__list_head_refresh(struct ui_browser *self); +void ui_browser__list_head_seek(struct ui_browser *browser, off_t offset, int whence); +unsigned int ui_browser__list_head_refresh(struct ui_browser *browser); void ui_browser__init(void); void annotate_browser__init(void); diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index a91b6b2..16848bb 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -1889,7 +1889,7 @@ out: return key; } -static bool filter_group_entries(struct ui_browser *self __maybe_unused, +static bool filter_group_entries(struct ui_browser *browser __maybe_unused, void *entry) { struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c index 95c7cfb..b11639f 100644 --- a/tools/perf/ui/browsers/map.c +++ b/tools/perf/ui/browsers/map.c @@ -18,30 +18,30 @@ struct map_browser { u8 addrlen; }; -static void map_browser__write(struct ui_browser *self, void *nd, int row) +static void map_browser__write(struct ui_browser *browser, void *nd, int row) { struct symbol *sym = rb_entry(nd, struct symbol, rb_node); - struct map_browser *mb = container_of(self, struct map_browser, b); - bool current_entry = ui_browser__is_current_entry(self, row); + struct map_browser *mb = container_of(browser, struct map_browser, b); + bool current_entry = ui_browser__is_current_entry(browser, row); int width; - ui_browser__set_percent_color(self, 0, current_entry); + ui_browser__set_percent_color(browser, 0, current_entry); slsmg_printf("%*" PRIx64 " %*" PRIx64 " %c ", mb->addrlen, sym->start, mb->addrlen, sym->end, sym->binding == STB_GLOBAL ? 'g' : sym->binding == STB_LOCAL ? 'l' : 'w'); - width = self->width - ((mb->addrlen * 2) + 4); + width = browser->width - ((mb->addrlen * 2) + 4); if (width > 0) slsmg_write_nstring(sym->name, width); } /* FIXME uber-kludgy, see comment on cmd_report... */ -static u32 *symbol__browser_index(struct symbol *self) +static u32 *symbol__browser_index(struct symbol *browser) { - return ((void *)self) - sizeof(struct rb_node) - sizeof(u32); + return ((void *)browser) - sizeof(struct rb_node) - sizeof(u32); } -static int map_browser__search(struct map_browser *self) +static int map_browser__search(struct map_browser *browser) { char target[512]; struct symbol *sym; @@ -53,37 +53,37 @@ static int map_browser__search(struct map_browser *self) if (target[0] == '0' && tolower(target[1]) == 'x') { u64 addr = strtoull(target, NULL, 16); - sym = map__find_symbol(self->map, addr, NULL); + sym = map__find_symbol(browser->map, addr, NULL); } else - sym = map__find_symbol_by_name(self->map, target, NULL); + sym = map__find_symbol_by_name(browser->map, target, NULL); if (sym != NULL) { u32 *idx = symbol__browser_index(sym); - self->b.top = &sym->rb_node; - self->b.index = self->b.top_idx = *idx; + browser->b.top = &sym->rb_node; + browser->b.index = browser->b.top_idx = *idx; } else ui_helpline__fpush("%s not found!", target); return 0; } -static int map_browser__run(struct map_browser *self) +static int map_browser__run(struct map_browser *browser) { int key; - if (ui_browser__show(&self->b, self->map->dso->long_name, + if (ui_browser__show(&browser->b, browser->map->dso->long_name, "Press <- or ESC to exit, %s / to search", verbose ? "" : "restart with -v to use") < 0) return -1; while (1) { - key = ui_browser__run(&self->b, 0); + key = ui_browser__run(&browser->b, 0); switch (key) { case '/': if (verbose) - map_browser__search(self); + map_browser__search(browser); default: break; case K_LEFT: @@ -94,20 +94,20 @@ static int map_browser__run(struct map_browser *self) } } out: - ui_browser__hide(&self->b); + ui_browser__hide(&browser->b); return key; } -int map__browse(struct map *self) +int map__browse(struct map *map) { struct map_browser mb = { .b = { - .entries = &self->dso->symbols[self->type], + .entries = &map->dso->symbols[map->type], .refresh = ui_browser__rb_tree_refresh, .seek = ui_browser__rb_tree_seek, .write = map_browser__write, }, - .map = self, + .map = map, }; struct rb_node *nd; char tmp[BITS_PER_LONG / 4]; diff --git a/tools/perf/ui/browsers/map.h b/tools/perf/ui/browsers/map.h index df8581a..2d58e4b 100644 --- a/tools/perf/ui/browsers/map.h +++ b/tools/perf/ui/browsers/map.h @@ -2,5 +2,5 @@ #define _PERF_UI_MAP_BROWSER_H_ 1 struct map; -int map__browse(struct map *self); +int map__browse(struct map *map); #endif /* _PERF_UI_MAP_BROWSER_H_ */ diff --git a/tools/perf/ui/browsers/scripts.c b/tools/perf/ui/browsers/scripts.c index 12f009e..d63c68e 100644 --- a/tools/perf/ui/browsers/scripts.c +++ b/tools/perf/ui/browsers/scripts.c @@ -84,22 +84,22 @@ static void script_browser__write(struct ui_browser *browser, slsmg_write_nstring(sline->line, browser->width); } -static int script_browser__run(struct perf_script_browser *self) +static int script_browser__run(struct perf_script_browser *browser) { int key; - if (ui_browser__show(&self->b, self->script_name, + if (ui_browser__show(&browser->b, browser->script_name, "Press <- or ESC to exit") < 0) return -1; while (1) { - key = ui_browser__run(&self->b, 0); + key = ui_browser__run(&browser->b, 0); /* We can add some special key handling here if needed */ break; } - ui_browser__hide(&self->b); + ui_browser__hide(&browser->b); return key; } diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index 6c15268..c244cb5 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c @@ -213,20 +213,19 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root, return ret; } -static size_t __callchain__fprintf_flat(FILE *fp, - struct callchain_node *self, +static size_t __callchain__fprintf_flat(FILE *fp, struct callchain_node *node, u64 total_samples) { struct callchain_list *chain; size_t ret = 0; - if (!self) + if (!node) return 0; - ret += __callchain__fprintf_flat(fp, self->parent, total_samples); + ret += __callchain__fprintf_flat(fp, node->parent, total_samples); - list_for_each_entry(chain, &self->val, list) { + list_for_each_entry(chain, &node->val, list) { if (chain->ip >= PERF_CONTEXT_MAX) continue; if (chain->ms.sym) @@ -239,15 +238,14 @@ static size_t __callchain__fprintf_flat(FILE *fp, return ret; } -static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *self, +static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *tree, u64 total_samples) { size_t ret = 0; u32 entries_printed = 0; - struct rb_node *rb_node; struct callchain_node *chain; + struct rb_node *rb_node = rb_first(tree); - rb_node = rb_first(self); while (rb_node) { double percent; diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h index a811f5c..929f28a 100644 --- a/tools/perf/util/build-id.h +++ b/tools/perf/util/build-id.h @@ -10,10 +10,9 @@ extern struct perf_tool build_id__mark_dso_hit_ops; struct dso; int build_id__sprintf(const u8 *build_id, int len, char *bf); -char *dso__build_id_filename(struct dso *self, char *bf, size_t size); +char *dso__build_id_filename(struct dso *dso, char *bf, size_t size); int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct perf_evsel *evsel, struct machine *machine); - #endif diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index c26b353..ec9ae11 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -617,15 +617,15 @@ int perf_event__process(struct perf_tool *tool __maybe_unused, return machine__process_event(machine, event, sample); } -void thread__find_addr_map(struct thread *self, +void thread__find_addr_map(struct thread *thread, struct machine *machine, u8 cpumode, enum map_type type, u64 addr, struct addr_location *al) { - struct map_groups *mg = &self->mg; + struct map_groups *mg = &thread->mg; bool load_map = false; - al->thread = self; + al->thread = thread; al->addr = addr; al->cpumode = cpumode; al->filtered = false; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 752709c..f8d70f3 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -251,7 +251,8 @@ int perf_event__process(struct perf_tool *tool, struct machine *machine); struct addr_location; -int perf_event__preprocess_sample(const union perf_event *self, + +int perf_event__preprocess_sample(const union perf_event *event, struct machine *machine, struct addr_location *al, struct perf_sample *sample); diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 0617ce2..e99eaed 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -88,7 +88,7 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id); struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id); -union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx); +union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx); void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx); diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 307f1c7..b621347a 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -84,7 +84,7 @@ struct hists { u16 col_len[HISTC_NR_COLS]; }; -struct hist_entry *__hists__add_entry(struct hists *self, +struct hist_entry *__hists__add_entry(struct hists *hists, struct addr_location *al, struct symbol *parent, struct branch_info *bi, @@ -93,34 +93,34 @@ struct hist_entry *__hists__add_entry(struct hists *self, int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right); int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); int hist_entry__transaction_len(void); -int hist_entry__sort_snprintf(struct hist_entry *self, char *bf, size_t size, +int hist_entry__sort_snprintf(struct hist_entry *he, char *bf, size_t size, struct hists *hists); void hist_entry__free(struct hist_entry *); -void hists__output_resort(struct hists *self); -void hists__collapse_resort(struct hists *self, struct ui_progress *prog); +void hists__output_resort(struct hists *hists); +void hists__collapse_resort(struct hists *hists, struct ui_progress *prog); void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); void hists__output_recalc_col_len(struct hists *hists, int max_rows); void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h); -void hists__inc_nr_events(struct hists *self, u32 type); +void hists__inc_nr_events(struct hists *hists, u32 type); void events_stats__inc(struct events_stats *stats, u32 type); size_t events_stats__fprintf(struct events_stats *stats, FILE *fp); -size_t hists__fprintf(struct hists *self, bool show_header, int max_rows, +size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, int max_cols, float min_pcnt, FILE *fp); -int hist_entry__inc_addr_samples(struct hist_entry *self, int evidx, u64 addr); -int hist_entry__annotate(struct hist_entry *self, size_t privsize); +int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 addr); +int hist_entry__annotate(struct hist_entry *he, size_t privsize); void hists__filter_by_dso(struct hists *hists); void hists__filter_by_thread(struct hists *hists); void hists__filter_by_symbol(struct hists *hists); -u16 hists__col_len(struct hists *self, enum hist_column col); -void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); -bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len); +u16 hists__col_len(struct hists *hists, enum hist_column col); +void hists__set_col_len(struct hists *hists, enum hist_column col, u16 len); +bool hists__new_col_len(struct hists *hists, enum hist_column col, u16 len); void hists__reset_col_len(struct hists *hists); void hists__calc_col_len(struct hists *hists, struct hist_entry *he); @@ -210,12 +210,9 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused, return 0; } -static inline int hist_entry__tui_annotate(struct hist_entry *self - __maybe_unused, - struct perf_evsel *evsel - __maybe_unused, - struct hist_browser_timer *hbt - __maybe_unused) +static inline int hist_entry__tui_annotate(struct hist_entry *he __maybe_unused, + struct perf_evsel *evsel __maybe_unused, + struct hist_browser_timer *hbt __maybe_unused) { return 0; } @@ -230,5 +227,5 @@ static inline int script_browse(const char *script_opt __maybe_unused) #define K_SWITCH_INPUT_DATA -3000 #endif -unsigned int hists__sort_list_width(struct hists *self); +unsigned int hists__sort_list_width(struct hists *hists); #endif /* __PERF_HIST_H */ diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 2200dad..ffb657f 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -115,7 +115,7 @@ static const Dwfl_Callbacks offline_callbacks = { }; /* Get a Dwarf from offline image */ -static int debuginfo__init_offline_dwarf(struct debuginfo *self, +static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, const char *path) { int fd; @@ -124,25 +124,25 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *self, if (fd < 0) return fd; - self->dwfl = dwfl_begin(&offline_callbacks); - if (!self->dwfl) + dbg->dwfl = dwfl_begin(&offline_callbacks); + if (!dbg->dwfl) goto error; - self->mod = dwfl_report_offline(self->dwfl, "", "", fd); - if (!self->mod) + dbg->mod = dwfl_report_offline(dbg->dwfl, "", "", fd); + if (!dbg->mod) goto error; - self->dbg = dwfl_module_getdwarf(self->mod, &self->bias); - if (!self->dbg) + dbg->dbg = dwfl_module_getdwarf(dbg->mod, &dbg->bias); + if (!dbg->dbg) goto error; return 0; error: - if (self->dwfl) - dwfl_end(self->dwfl); + if (dbg->dwfl) + dwfl_end(dbg->dwfl); else close(fd); - memset(self, 0, sizeof(*self)); + memset(dbg, 0, sizeof(*dbg)); return -ENOENT; } @@ -180,24 +180,24 @@ static const Dwfl_Callbacks kernel_callbacks = { }; /* Get a Dwarf from live kernel image */ -static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, +static int debuginfo__init_online_kernel_dwarf(struct debuginfo *dbg, Dwarf_Addr addr) { - self->dwfl = dwfl_begin(&kernel_callbacks); - if (!self->dwfl) + dbg->dwfl = dwfl_begin(&kernel_callbacks); + if (!dbg->dwfl) return -EINVAL; /* Load the kernel dwarves: Don't care the result here */ - dwfl_linux_kernel_report_kernel(self->dwfl); - dwfl_linux_kernel_report_modules(self->dwfl); + dwfl_linux_kernel_report_kernel(dbg->dwfl); + dwfl_linux_kernel_report_modules(dbg->dwfl); - self->dbg = dwfl_addrdwarf(self->dwfl, addr, &self->bias); + dbg->dbg = dwfl_addrdwarf(dbg->dwfl, addr, &dbg->bias); /* Here, check whether we could get a real dwarf */ - if (!self->dbg) { + if (!dbg->dbg) { pr_debug("Failed to find kernel dwarf at %lx\n", (unsigned long)addr); - dwfl_end(self->dwfl); - memset(self, 0, sizeof(*self)); + dwfl_end(dbg->dwfl); + memset(dbg, 0, sizeof(*dbg)); return -ENOENT; } @@ -205,7 +205,7 @@ static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, } #else /* With older elfutils, this just support kernel module... */ -static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, +static int debuginfo__init_online_kernel_dwarf(struct debuginfo *dbg, Dwarf_Addr addr __maybe_unused) { const char *path = kernel_get_module_path("kernel"); @@ -216,44 +216,45 @@ static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, } pr_debug2("Use file %s for debuginfo\n", path); - return debuginfo__init_offline_dwarf(self, path); + return debuginfo__init_offline_dwarf(dbg, path); } #endif struct debuginfo *debuginfo__new(const char *path) { - struct debuginfo *self = zalloc(sizeof(struct debuginfo)); - if (!self) + struct debuginfo *dbg = zalloc(sizeof(*dbg)); + if (!dbg) return NULL; - if (debuginfo__init_offline_dwarf(self, path) < 0) { - free(self); - self = NULL; + if (debuginfo__init_offline_dwarf(dbg, path) < 0) { + free(dbg); + dbg = NULL; } - return self; + return dbg; } struct debuginfo *debuginfo__new_online_kernel(unsigned long addr) { - struct debuginfo *self = zalloc(sizeof(struct debuginfo)); - if (!self) + struct debuginfo *dbg = zalloc(sizeof(*dbg)); + + if (!dbg) return NULL; - if (debuginfo__init_online_kernel_dwarf(self, (Dwarf_Addr)addr) < 0) { - free(self); - self = NULL; + if (debuginfo__init_online_kernel_dwarf(dbg, (Dwarf_Addr)addr) < 0) { + free(dbg); + dbg = NULL; } - return self; + return dbg; } -void debuginfo__delete(struct debuginfo *self) +void debuginfo__delete(struct debuginfo *dbg) { - if (self) { - if (self->dwfl) - dwfl_end(self->dwfl); - free(self); + if (dbg) { + if (dbg->dwfl) + dwfl_end(dbg->dwfl); + free(dbg); } } @@ -1083,7 +1084,7 @@ static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data) } /* Find probe points from debuginfo */ -static int debuginfo__find_probes(struct debuginfo *self, +static int debuginfo__find_probes(struct debuginfo *dbg, struct probe_finder *pf) { struct perf_probe_point *pp = &pf->pev->point; @@ -1094,7 +1095,7 @@ static int debuginfo__find_probes(struct debuginfo *self, #if _ELFUTILS_PREREQ(0, 142) /* Get the call frame information from this dwarf */ - pf->cfi = dwarf_getcfi(self->dbg); + pf->cfi = dwarf_getcfi(dbg->dbg); #endif off = 0; @@ -1113,7 +1114,7 @@ static int debuginfo__find_probes(struct debuginfo *self, .data = pf, }; - dwarf_getpubnames(self->dbg, pubname_search_cb, + dwarf_getpubnames(dbg->dbg, pubname_search_cb, &pubname_param, 0); if (pubname_param.found) { ret = probe_point_search_cb(&pf->sp_die, &probe_param); @@ -1123,9 +1124,9 @@ static int debuginfo__find_probes(struct debuginfo *self, } /* Loop on CUs (Compilation Unit) */ - while (!dwarf_nextcu(self->dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { + while (!dwarf_nextcu(dbg->dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { /* Get the DIE(Debugging Information Entry) of this CU */ - diep = dwarf_offdie(self->dbg, off + cuhl, &pf->cu_die); + diep = dwarf_offdie(dbg->dbg, off + cuhl, &pf->cu_die); if (!diep) continue; @@ -1281,13 +1282,13 @@ end: } /* Find probe_trace_events specified by perf_probe_event from debuginfo */ -int debuginfo__find_trace_events(struct debuginfo *self, +int debuginfo__find_trace_events(struct debuginfo *dbg, struct perf_probe_event *pev, struct probe_trace_event **tevs, int max_tevs) { struct trace_event_finder tf = { .pf = {.pev = pev, .callback = add_probe_trace_event}, - .mod = self->mod, .max_tevs = max_tevs}; + .mod = dbg->mod, .max_tevs = max_tevs}; int ret; /* Allocate result tevs array */ @@ -1298,7 +1299,7 @@ int debuginfo__find_trace_events(struct debuginfo *self, tf.tevs = *tevs; tf.ntevs = 0; - ret = debuginfo__find_probes(self, &tf.pf); + ret = debuginfo__find_probes(dbg, &tf.pf); if (ret < 0) { free(*tevs); *tevs = NULL; @@ -1389,14 +1390,14 @@ out: } /* Find available variables at given probe point */ -int debuginfo__find_available_vars_at(struct debuginfo *self, +int debuginfo__find_available_vars_at(struct debuginfo *dbg, struct perf_probe_event *pev, struct variable_list **vls, int max_vls, bool externs) { struct available_var_finder af = { .pf = {.pev = pev, .callback = add_available_vars}, - .mod = self->mod, + .mod = dbg->mod, .max_vls = max_vls, .externs = externs}; int ret; @@ -1408,7 +1409,7 @@ int debuginfo__find_available_vars_at(struct debuginfo *self, af.vls = *vls; af.nvls = 0; - ret = debuginfo__find_probes(self, &af.pf); + ret = debuginfo__find_probes(dbg, &af.pf); if (ret < 0) { /* Free vlist for error */ while (af.nvls--) { @@ -1426,7 +1427,7 @@ int debuginfo__find_available_vars_at(struct debuginfo *self, } /* Reverse search */ -int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr, +int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr, struct perf_probe_point *ppt) { Dwarf_Die cudie, spdie, indie; @@ -1435,10 +1436,10 @@ int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr, int baseline = 0, lineno = 0, ret = 0; /* Adjust address with bias */ - addr += self->bias; + addr += dbg->bias; /* Find cu die */ - if (!dwarf_addrdie(self->dbg, (Dwarf_Addr)addr - self->bias, &cudie)) { + if (!dwarf_addrdie(dbg->dbg, (Dwarf_Addr)addr - dbg->bias, &cudie)) { pr_warning("Failed to find debug information for address %lx\n", addr); ret = -EINVAL; @@ -1639,7 +1640,7 @@ static int find_line_range_by_func(struct line_finder *lf) return param.retval; } -int debuginfo__find_line_range(struct debuginfo *self, struct line_range *lr) +int debuginfo__find_line_range(struct debuginfo *dbg, struct line_range *lr) { struct line_finder lf = {.lr = lr, .found = 0}; int ret = 0; @@ -1656,7 +1657,7 @@ int debuginfo__find_line_range(struct debuginfo *self, struct line_range *lr) struct dwarf_callback_param line_range_param = { .data = (void *)&lf, .retval = 0}; - dwarf_getpubnames(self->dbg, pubname_search_cb, + dwarf_getpubnames(dbg->dbg, pubname_search_cb, &pubname_param, 0); if (pubname_param.found) { line_range_search_cb(&lf.sp_die, &line_range_param); @@ -1667,12 +1668,12 @@ int debuginfo__find_line_range(struct debuginfo *self, struct line_range *lr) /* Loop on CUs (Compilation Unit) */ while (!lf.found && ret >= 0) { - if (dwarf_nextcu(self->dbg, off, &noff, &cuhl, + if (dwarf_nextcu(dbg->dbg, off, &noff, &cuhl, NULL, NULL, NULL) != 0) break; /* Get the DIE(Debugging Information Entry) of this CU */ - diep = dwarf_offdie(self->dbg, off + cuhl, &lf.cu_die); + diep = dwarf_offdie(dbg->dbg, off + cuhl, &lf.cu_die); if (!diep) continue; diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index d6dab0e..ffc33cd 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -31,25 +31,25 @@ struct debuginfo { extern struct debuginfo *debuginfo__new(const char *path); extern struct debuginfo *debuginfo__new_online_kernel(unsigned long addr); -extern void debuginfo__delete(struct debuginfo *self); +extern void debuginfo__delete(struct debuginfo *dbg); /* Find probe_trace_events specified by perf_probe_event from debuginfo */ -extern int debuginfo__find_trace_events(struct debuginfo *self, +extern int debuginfo__find_trace_events(struct debuginfo *dbg, struct perf_probe_event *pev, struct probe_trace_event **tevs, int max_tevs); /* Find a perf_probe_point from debuginfo */ -extern int debuginfo__find_probe_point(struct debuginfo *self, +extern int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr, struct perf_probe_point *ppt); /* Find a line range */ -extern int debuginfo__find_line_range(struct debuginfo *self, +extern int debuginfo__find_line_range(struct debuginfo *dbg, struct line_range *lr); /* Find available variables */ -extern int debuginfo__find_available_vars_at(struct debuginfo *self, +extern int debuginfo__find_available_vars_at(struct debuginfo *dbg, struct perf_probe_event *pev, struct variable_list **vls, int max_points, bool externs); diff --git a/tools/perf/util/pstack.h b/tools/perf/util/pstack.h index 4cedea5..c3cb658 100644 --- a/tools/perf/util/pstack.h +++ b/tools/perf/util/pstack.h @@ -5,10 +5,10 @@ struct pstack; struct pstack *pstack__new(unsigned short max_nr_entries); -void pstack__delete(struct pstack *self); -bool pstack__empty(const struct pstack *self); -void pstack__remove(struct pstack *self, void *key); -void pstack__push(struct pstack *self, void *key); -void *pstack__pop(struct pstack *self); +void pstack__delete(struct pstack *pstack); +bool pstack__empty(const struct pstack *pstack); +void pstack__remove(struct pstack *pstack, void *key); +void pstack__push(struct pstack *pstack, void *key); +void *pstack__pop(struct pstack *pstack); #endif /* _PERF_PSTACK_ */ diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 3c1b301..0ce4694 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -16,11 +16,11 @@ #include "perf_regs.h" #include "vdso.h" -static int perf_session__open(struct perf_session *self) +static int perf_session__open(struct perf_session *session) { - struct perf_data_file *file = self->file; + struct perf_data_file *file = session->file; - if (perf_session__read_header(self) < 0) { + if (perf_session__read_header(session) < 0) { pr_err("incompatible file format (rerun with -v to learn more)"); return -1; } @@ -28,17 +28,17 @@ static int perf_session__open(struct perf_session *self) if (perf_data_file__is_pipe(file)) return 0; - if (!perf_evlist__valid_sample_type(self->evlist)) { + if (!perf_evlist__valid_sample_type(session->evlist)) { pr_err("non matching sample_type"); return -1; } - if (!perf_evlist__valid_sample_id_all(self->evlist)) { + if (!perf_evlist__valid_sample_id_all(session->evlist)) { pr_err("non matching sample_id_all"); return -1; } - if (!perf_evlist__valid_read_format(self->evlist)) { + if (!perf_evlist__valid_read_format(session->evlist)) { pr_err("non matching read_format"); return -1; } @@ -53,46 +53,45 @@ void perf_session__set_id_hdr_size(struct perf_session *session) machines__set_id_hdr_size(&session->machines, id_hdr_size); } -int perf_session__create_kernel_maps(struct perf_session *self) +int perf_session__create_kernel_maps(struct perf_session *session) { - int ret = machine__create_kernel_maps(&self->machines.host); + int ret = machine__create_kernel_maps(&session->machines.host); if (ret >= 0) - ret = machines__create_guest_kernel_maps(&self->machines); + ret = machines__create_guest_kernel_maps(&session->machines); return ret; } -static void perf_session__destroy_kernel_maps(struct perf_session *self) +static void perf_session__destroy_kernel_maps(struct perf_session *session) { - machines__destroy_kernel_maps(&self->machines); + machines__destroy_kernel_maps(&session->machines); } struct perf_session *perf_session__new(struct perf_data_file *file, bool repipe, struct perf_tool *tool) { - struct perf_session *self; + struct perf_session *session = zalloc(sizeof(*session)); - self = zalloc(sizeof(*self)); - if (!self) + if (!session) goto out; - self->repipe = repipe; - INIT_LIST_HEAD(&self->ordered_samples.samples); - INIT_LIST_HEAD(&self->ordered_samples.sample_cache); - INIT_LIST_HEAD(&self->ordered_samples.to_free); - machines__init(&self->machines); + session->repipe = repipe; + INIT_LIST_HEAD(&session->ordered_samples.samples); + INIT_LIST_HEAD(&session->ordered_samples.sample_cache); + INIT_LIST_HEAD(&session->ordered_samples.to_free); + machines__init(&session->machines); if (file) { if (perf_data_file__open(file)) goto out_delete; - self->file = file; + session->file = file; if (perf_data_file__is_read(file)) { - if (perf_session__open(self) < 0) + if (perf_session__open(session) < 0) goto out_close; - perf_session__set_id_hdr_size(self); + perf_session__set_id_hdr_size(session); } } @@ -101,22 +100,22 @@ struct perf_session *perf_session__new(struct perf_data_file *file, * In O_RDONLY mode this will be performed when reading the * kernel MMAP event, in perf_event__process_mmap(). */ - if (perf_session__create_kernel_maps(self) < 0) + if (perf_session__create_kernel_maps(session) < 0) goto out_delete; } if (tool && tool->ordering_requires_timestamps && - tool->ordered_samples && !perf_evlist__sample_id_all(self->evlist)) { + tool->ordered_samples && !perf_evlist__sample_id_all(session->evlist)) { dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n"); tool->ordered_samples = false; } - return self; + return session; out_close: perf_data_file__close(file); out_delete: - perf_session__delete(self); + perf_session__delete(session); out: return NULL; } @@ -147,16 +146,16 @@ static void perf_session_env__delete(struct perf_session_env *env) free(env->pmu_mappings); } -void perf_session__delete(struct perf_session *self) +void perf_session__delete(struct perf_session *session) { - perf_session__destroy_kernel_maps(self); - perf_session__delete_dead_threads(self); - perf_session__delete_threads(self); - perf_session_env__delete(&self->header.env); - machines__exit(&self->machines); - if (self->file) - perf_data_file__close(self->file); - free(self); + perf_session__destroy_kernel_maps(session); + perf_session__delete_dead_threads(session); + perf_session__delete_threads(session); + perf_session_env__delete(&session->header.env); + machines__exit(&session->machines); + if (session->file) + perf_data_file__close(session->file); + free(session); vdso__exit(); } @@ -1084,11 +1083,11 @@ static int perf_session__process_event(struct perf_session *session, file_offset); } -void perf_event_header__bswap(struct perf_event_header *self) +void perf_event_header__bswap(struct perf_event_header *hdr) { - self->type = bswap_32(self->type); - self->misc = bswap_16(self->misc); - self->size = bswap_16(self->size); + hdr->type = bswap_32(hdr->type); + hdr->misc = bswap_16(hdr->misc); + hdr->size = bswap_16(hdr->size); } struct thread *perf_session__findnew(struct perf_session *session, pid_t pid) @@ -1096,9 +1095,9 @@ struct thread *perf_session__findnew(struct perf_session *session, pid_t pid) return machine__findnew_thread(&session->machines.host, 0, pid); } -static struct thread *perf_session__register_idle_thread(struct perf_session *self) +static struct thread *perf_session__register_idle_thread(struct perf_session *session) { - struct thread *thread = perf_session__findnew(self, 0); + struct thread *thread = perf_session__findnew(session, 0); if (thread == NULL || thread__set_comm(thread, "swapper", 0)) { pr_err("problem inserting idle task.\n"); @@ -1150,10 +1149,10 @@ static void perf_session__warn_about_errors(const struct perf_session *session, volatile int session_done; -static int __perf_session__process_pipe_events(struct perf_session *self, +static int __perf_session__process_pipe_events(struct perf_session *session, struct perf_tool *tool) { - int fd = perf_data_file__fd(self->file); + int fd = perf_data_file__fd(session->file); union perf_event *event; uint32_t size, cur_size = 0; void *buf = NULL; @@ -1181,7 +1180,7 @@ more: goto out_err; } - if (self->header.needs_swap) + if (session->header.needs_swap) perf_event_header__bswap(&event->header); size = event->header.size; @@ -1216,7 +1215,7 @@ more: } } - if ((skip = perf_session__process_event(self, event, tool, head)) < 0) { + if ((skip = perf_session__process_event(session, event, tool, head)) < 0) { pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n", head, event->header.size, event->header.type); err = -EINVAL; @@ -1232,12 +1231,12 @@ more: goto more; done: /* do the final flush for ordered samples */ - self->ordered_samples.next_flush = ULLONG_MAX; - err = flush_sample_queue(self, tool); + session->ordered_samples.next_flush = ULLONG_MAX; + err = flush_sample_queue(session, tool); out_err: free(buf); - perf_session__warn_about_errors(self, tool); - perf_session_free_sample_buffers(self); + perf_session__warn_about_errors(session, tool); + perf_session_free_sample_buffers(session); return err; } @@ -1377,22 +1376,22 @@ out_err: return err; } -int perf_session__process_events(struct perf_session *self, +int perf_session__process_events(struct perf_session *session, struct perf_tool *tool) { - u64 size = perf_data_file__size(self->file); + u64 size = perf_data_file__size(session->file); int err; - if (perf_session__register_idle_thread(self) == NULL) + if (perf_session__register_idle_thread(session) == NULL) return -ENOMEM; - if (!perf_data_file__is_pipe(self->file)) - err = __perf_session__process_events(self, - self->header.data_offset, - self->header.data_size, + if (!perf_data_file__is_pipe(session->file)) + err = __perf_session__process_events(session, + session->header.data_offset, + session->header.data_size, size, tool); else - err = __perf_session__process_pipe_events(self, tool); + err = __perf_session__process_pipe_events(session, tool); return err; } @@ -1441,15 +1440,15 @@ int maps__set_kallsyms_ref_reloc_sym(struct map **maps, return 0; } -size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp) +size_t perf_session__fprintf_dsos(struct perf_session *session, FILE *fp) { - return machines__fprintf_dsos(&self->machines, fp); + return machines__fprintf_dsos(&session->machines, fp); } -size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp, +size_t perf_session__fprintf_dsos_buildid(struct perf_session *session, FILE *fp, bool (skip)(struct dso *dso, int parm), int parm) { - return machines__fprintf_dsos_buildid(&self->machines, fp, skip, parm); + return machines__fprintf_dsos_buildid(&session->machines, fp, skip, parm); } size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 27c74d3..50f6409 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -51,12 +51,12 @@ struct perf_session *perf_session__new(struct perf_data_file *file, bool repipe, struct perf_tool *tool); void perf_session__delete(struct perf_session *session); -void perf_event_header__bswap(struct perf_event_header *self); +void perf_event_header__bswap(struct perf_event_header *hdr); -int __perf_session__process_events(struct perf_session *self, +int __perf_session__process_events(struct perf_session *session, u64 data_offset, u64 data_size, u64 size, struct perf_tool *tool); -int perf_session__process_events(struct perf_session *self, +int perf_session__process_events(struct perf_session *session, struct perf_tool *tool); int perf_session_queue_event(struct perf_session *s, union perf_event *event, @@ -64,37 +64,38 @@ int perf_session_queue_event(struct perf_session *s, union perf_event *event, void perf_tool__fill_defaults(struct perf_tool *tool); -int perf_session__resolve_callchain(struct perf_session *self, struct perf_evsel *evsel, +int perf_session__resolve_callchain(struct perf_session *session, + struct perf_evsel *evsel, struct thread *thread, struct ip_callchain *chain, struct symbol **parent); -bool perf_session__has_traces(struct perf_session *self, const char *msg); +bool perf_session__has_traces(struct perf_session *session, const char *msg); void mem_bswap_64(void *src, int byte_size); void mem_bswap_32(void *src, int byte_size); void perf_event__attr_swap(struct perf_event_attr *attr); -int perf_session__create_kernel_maps(struct perf_session *self); +int perf_session__create_kernel_maps(struct perf_session *session); void perf_session__set_id_hdr_size(struct perf_session *session); static inline -struct machine *perf_session__find_machine(struct perf_session *self, pid_t pid) +struct machine *perf_session__find_machine(struct perf_session *session, pid_t pid) { - return machines__find(&self->machines, pid); + return machines__find(&session->machines, pid); } static inline -struct machine *perf_session__findnew_machine(struct perf_session *self, pid_t pid) +struct machine *perf_session__findnew_machine(struct perf_session *session, pid_t pid) { - return machines__findnew(&self->machines, pid); + return machines__findnew(&session->machines, pid); } -struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); -size_t perf_session__fprintf(struct perf_session *self, FILE *fp); +struct thread *perf_session__findnew(struct perf_session *session, pid_t pid); +size_t perf_session__fprintf(struct perf_session *session, FILE *fp); -size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp); +size_t perf_session__fprintf_dsos(struct perf_session *session, FILE *fp); size_t perf_session__fprintf_dsos_buildid(struct perf_session *session, FILE *fp, bool (fn)(struct dso *dso, int parm), int parm); diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index f4e16f3..f4cc147 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -180,7 +180,7 @@ struct sort_entry { int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *); int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *); - int (*se_snprintf)(struct hist_entry *self, char *bf, size_t size, + int (*se_snprintf)(struct hist_entry *he, char *bf, size_t size, unsigned int width); u8 se_width_idx; bool elide; diff --git a/tools/perf/util/strfilter.c b/tools/perf/util/strfilter.c index 67e4a00..3edd053 100644 --- a/tools/perf/util/strfilter.c +++ b/tools/perf/util/strfilter.c @@ -62,15 +62,15 @@ static struct strfilter_node *strfilter_node__alloc(const char *op, struct strfilter_node *l, struct strfilter_node *r) { - struct strfilter_node *ret = zalloc(sizeof(struct strfilter_node)); + struct strfilter_node *node = zalloc(sizeof(*node)); - if (ret) { - ret->p = op; - ret->l = l; - ret->r = r; + if (node) { + node->p = op; + node->l = l; + node->r = r; } - return ret; + return node; } static struct strfilter_node *strfilter_node__new(const char *s, @@ -154,20 +154,20 @@ error: */ struct strfilter *strfilter__new(const char *rules, const char **err) { - struct strfilter *ret = zalloc(sizeof(struct strfilter)); + struct strfilter *filter = zalloc(sizeof(*filter)); const char *ep = NULL; - if (ret) - ret->root = strfilter_node__new(rules, &ep); + if (filter) + filter->root = strfilter_node__new(rules, &ep); - if (!ret || !ret->root || *ep != '\0') { + if (!filter || !filter->root || *ep != '\0') { if (err) *err = ep; - strfilter__delete(ret); - ret = NULL; + strfilter__delete(filter); + filter = NULL; } - return ret; + return filter; } static bool strfilter_node__compare(struct strfilter_node *node, @@ -191,9 +191,9 @@ static bool strfilter_node__compare(struct strfilter_node *node, } /* Return true if STR matches the filter rules */ -bool strfilter__compare(struct strfilter *node, const char *str) +bool strfilter__compare(struct strfilter *filter, const char *str) { - if (!node) + if (!filter) return false; - return strfilter_node__compare(node->root, str); + return strfilter_node__compare(filter->root, str); } diff --git a/tools/perf/util/strfilter.h b/tools/perf/util/strfilter.h index 00f58a7..fe611f3 100644 --- a/tools/perf/util/strfilter.h +++ b/tools/perf/util/strfilter.h @@ -30,19 +30,19 @@ struct strfilter *strfilter__new(const char *rules, const char **err); /** * strfilter__compare - compare given string and a string filter - * @self: String filter + * @filter: String filter * @str: target string * - * Compare @str and @self. Return true if the str match the rule + * Compare @str and @filter. Return true if the str match the rule */ -bool strfilter__compare(struct strfilter *self, const char *str); +bool strfilter__compare(struct strfilter *filter, const char *str); /** * strfilter__delete - delete a string filter - * @self: String filter to delete + * @filter: String filter to delete * - * Delete @self. + * Delete @filter. */ -void strfilter__delete(struct strfilter *self); +void strfilter__delete(struct strfilter *filter); #endif diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 373c055..897c1b2 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -29,24 +29,24 @@ struct machine; struct comm; struct thread *thread__new(pid_t pid, pid_t tid); -void thread__delete(struct thread *self); +void thread__delete(struct thread *thread); static inline void thread__exited(struct thread *thread) { thread->dead = true; } int thread__set_comm(struct thread *thread, const char *comm, u64 timestamp); -int thread__comm_len(struct thread *self); +int thread__comm_len(struct thread *thread); struct comm *thread__comm(const struct thread *thread); const char *thread__comm_str(const struct thread *thread); -void thread__insert_map(struct thread *self, struct map *map); +void thread__insert_map(struct thread *thread, struct map *map); int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp); size_t thread__fprintf(struct thread *thread, FILE *fp); -static inline struct map *thread__find_map(struct thread *self, +static inline struct map *thread__find_map(struct thread *thread, enum map_type type, u64 addr) { - return self ? map_groups__find(&self->mg, type, addr) : NULL; + return thread ? map_groups__find(&thread->mg, type, addr) : NULL; } void thread__find_addr_map(struct thread *thread, struct machine *machine, -- cgit v0.10.2 From d7a88c7eb46acb486922822eec3224c0bcab29dc Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 10 Oct 2013 19:18:02 +1100 Subject: powerpc/scom: Enable 64-bit addresses On P8, XSCOM addresses has a special "indirect" form that requires more than 32-bits, so let's use u64 everywhere in the code instead of u32. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/scom.h b/arch/powerpc/include/asm/scom.h index 07dcdcf..f5cde45 100644 --- a/arch/powerpc/include/asm/scom.h +++ b/arch/powerpc/include/asm/scom.h @@ -54,8 +54,8 @@ struct scom_controller { scom_map_t (*map)(struct device_node *ctrl_dev, u64 reg, u64 count); void (*unmap)(scom_map_t map); - int (*read)(scom_map_t map, u32 reg, u64 *value); - int (*write)(scom_map_t map, u32 reg, u64 value); + int (*read)(scom_map_t map, u64 reg, u64 *value); + int (*write)(scom_map_t map, u64 reg, u64 value); }; extern const struct scom_controller *scom_controller; @@ -137,7 +137,7 @@ static inline void scom_unmap(scom_map_t map) * * Returns 0 (success) or a negative error code */ -static inline int scom_read(scom_map_t map, u32 reg, u64 *value) +static inline int scom_read(scom_map_t map, u64 reg, u64 *value) { int rc; @@ -155,7 +155,7 @@ static inline int scom_read(scom_map_t map, u32 reg, u64 *value) * * Returns 0 (success) or a negative error code */ -static inline int scom_write(scom_map_t map, u32 reg, u64 value) +static inline int scom_write(scom_map_t map, u64 reg, u64 value) { return scom_controller->write(map, reg, value); } diff --git a/arch/powerpc/platforms/powernv/opal-xscom.c b/arch/powerpc/platforms/powernv/opal-xscom.c index 3ed5c64..09a90d8 100644 --- a/arch/powerpc/platforms/powernv/opal-xscom.c +++ b/arch/powerpc/platforms/powernv/opal-xscom.c @@ -27,7 +27,7 @@ */ struct opal_scom_map { uint32_t chip; - uint32_t addr; + uint64_t addr; }; static scom_map_t opal_scom_map(struct device_node *dev, u64 reg, u64 count) @@ -71,7 +71,7 @@ static int opal_xscom_err_xlate(int64_t rc) } } -static int opal_scom_read(scom_map_t map, u32 reg, u64 *value) +static int opal_scom_read(scom_map_t map, u64 reg, u64 *value) { struct opal_scom_map *m = map; int64_t rc; @@ -80,7 +80,7 @@ static int opal_scom_read(scom_map_t map, u32 reg, u64 *value) return opal_xscom_err_xlate(rc); } -static int opal_scom_write(scom_map_t map, u32 reg, u64 value) +static int opal_scom_write(scom_map_t map, u64 reg, u64 value) { struct opal_scom_map *m = map; int64_t rc; diff --git a/arch/powerpc/platforms/wsp/scom_wsp.c b/arch/powerpc/platforms/wsp/scom_wsp.c index 54172c4..8928507 100644 --- a/arch/powerpc/platforms/wsp/scom_wsp.c +++ b/arch/powerpc/platforms/wsp/scom_wsp.c @@ -50,7 +50,7 @@ static void wsp_scom_unmap(scom_map_t map) iounmap((void *)map); } -static int wsp_scom_read(scom_map_t map, u32 reg, u64 *value) +static int wsp_scom_read(scom_map_t map, u64 reg, u64 *value) { u64 __iomem *addr = (u64 __iomem *)map; @@ -59,7 +59,7 @@ static int wsp_scom_read(scom_map_t map, u32 reg, u64 *value) return 0; } -static int wsp_scom_write(scom_map_t map, u32 reg, u64 value) +static int wsp_scom_write(scom_map_t map, u64 reg, u64 value) { u64 __iomem *addr = (u64 __iomem *)map; -- cgit v0.10.2 From cda13552d5055a87dd39334dabf47249b01fc5aa Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 4 Nov 2013 13:20:35 +1100 Subject: powerpc/scom: Improve debugfs interface The current debugfs interface to scom is essentially unused and racy. It uses two different files "address" and "data" to perform accesses which is at best impractical for anything but manual use by a developer. This replaces it with an "access" file which represent the entire scom address space which can be lseek/read/writen too. This file only supports accesses that are 8 bytes aligned and multiple of 8 bytes in size. The offset is logically the SCOM address multiplied by 8. Since nothing in userspace exploits that file at the moment, the ABI change is a no-brainer. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/sysdev/scom.c b/arch/powerpc/sysdev/scom.c index 3963d99..6f5a8d1 100644 --- a/arch/powerpc/sysdev/scom.c +++ b/arch/powerpc/sysdev/scom.c @@ -25,6 +25,7 @@ #include #include #include +#include const struct scom_controller *scom_controller; EXPORT_SYMBOL_GPL(scom_controller); @@ -98,61 +99,89 @@ EXPORT_SYMBOL_GPL(scom_map_device); #ifdef CONFIG_SCOM_DEBUGFS struct scom_debug_entry { struct device_node *dn; - unsigned long addr; - scom_map_t map; - spinlock_t lock; - char name[8]; - struct debugfs_blob_wrapper blob; + struct debugfs_blob_wrapper path; + char name[16]; }; -static int scom_addr_set(void *data, u64 val) -{ - struct scom_debug_entry *ent = data; - - ent->addr = 0; - scom_unmap(ent->map); - - ent->map = scom_map(ent->dn, val, 1); - if (scom_map_ok(ent->map)) - ent->addr = val; - else - return -EFAULT; - - return 0; -} - -static int scom_addr_get(void *data, u64 *val) +static ssize_t scom_debug_read(struct file *filp, char __user *ubuf, + size_t count, loff_t *ppos) { - struct scom_debug_entry *ent = data; - *val = ent->addr; - return 0; + struct scom_debug_entry *ent = filp->private_data; + u64 __user *ubuf64 = (u64 __user *)ubuf; + loff_t off = *ppos; + ssize_t done = 0; + u64 reg, reg_cnt, val; + scom_map_t map; + int rc; + + if (off < 0 || (off & 7) || (count & 7)) + return -EINVAL; + reg = off >> 3; + reg_cnt = count >> 3; + + map = scom_map(ent->dn, reg, reg_cnt); + if (!scom_map_ok(map)) + return -ENXIO; + + for (reg = 0; reg < reg_cnt; reg++) { + rc = scom_read(map, reg, &val); + if (!rc) + rc = put_user(val, ubuf64); + if (rc) { + if (!done) + done = rc; + break; + } + ubuf64++; + *ppos += 8; + done += 8; + } + scom_unmap(map); + return done; } -DEFINE_SIMPLE_ATTRIBUTE(scom_addr_fops, scom_addr_get, scom_addr_set, - "0x%llx\n"); -static int scom_val_set(void *data, u64 val) +static ssize_t scom_debug_write(struct file* filp, const char __user *ubuf, + size_t count, loff_t *ppos) { - struct scom_debug_entry *ent = data; - - if (!scom_map_ok(ent->map)) - return -EFAULT; - - scom_write(ent->map, 0, val); - - return 0; + struct scom_debug_entry *ent = filp->private_data; + u64 __user *ubuf64 = (u64 __user *)ubuf; + loff_t off = *ppos; + ssize_t done = 0; + u64 reg, reg_cnt, val; + scom_map_t map; + int rc; + + if (off < 0 || (off & 7) || (count & 7)) + return -EINVAL; + reg = off >> 3; + reg_cnt = count >> 3; + + map = scom_map(ent->dn, reg, reg_cnt); + if (!scom_map_ok(map)) + return -ENXIO; + + for (reg = 0; reg < reg_cnt; reg++) { + rc = get_user(val, ubuf64); + if (!rc) + rc = scom_write(map, reg, val); + if (rc) { + if (!done) + done = rc; + break; + } + ubuf64++; + done += 8; + } + scom_unmap(map); + return done; } -static int scom_val_get(void *data, u64 *val) -{ - struct scom_debug_entry *ent = data; - - if (!scom_map_ok(ent->map)) - return -EFAULT; - - return scom_read(ent->map, 0, val); -} -DEFINE_SIMPLE_ATTRIBUTE(scom_val_fops, scom_val_get, scom_val_set, - "0x%llx\n"); +static const struct file_operations scom_debug_fops = { + .read = scom_debug_read, + .write = scom_debug_write, + .open = simple_open, + .llseek = default_llseek, +}; static int scom_debug_init_one(struct dentry *root, struct device_node *dn, int i) @@ -165,11 +194,9 @@ static int scom_debug_init_one(struct dentry *root, struct device_node *dn, return -ENOMEM; ent->dn = of_node_get(dn); - ent->map = SCOM_MAP_INVALID; - spin_lock_init(&ent->lock); - snprintf(ent->name, 8, "scom%d", i); - ent->blob.data = (void*) dn->full_name; - ent->blob.size = strlen(dn->full_name); + snprintf(ent->name, 16, "%08x", i); + ent->path.data = (void*) dn->full_name; + ent->path.size = strlen(dn->full_name); dir = debugfs_create_dir(ent->name, root); if (!dir) { @@ -178,9 +205,8 @@ static int scom_debug_init_one(struct dentry *root, struct device_node *dn, return -1; } - debugfs_create_file("addr", 0600, dir, ent, &scom_addr_fops); - debugfs_create_file("value", 0600, dir, ent, &scom_val_fops); - debugfs_create_blob("devspec", 0400, dir, &ent->blob); + debugfs_create_blob("devspec", 0400, dir, &ent->path); + debugfs_create_file("access", 0600, dir, ent, &scom_debug_fops); return 0; } -- cgit v0.10.2 From 80546ac51396bd1d2e37cc78c7bed44c08f90352 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 10 Oct 2013 19:19:15 +1100 Subject: powerpc/powernv: Add support for indirect XSCOM via debugfs Indirect XSCOM addresses normally have the top bit set (of the 64-bit address). This doesn't work via the normal debugfs interface, so we use a different encoding, which we need to convert before calling OPAL. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/powernv/opal-xscom.c b/arch/powerpc/platforms/powernv/opal-xscom.c index 09a90d8..4d99a8f 100644 --- a/arch/powerpc/platforms/powernv/opal-xscom.c +++ b/arch/powerpc/platforms/powernv/opal-xscom.c @@ -71,11 +71,33 @@ static int opal_xscom_err_xlate(int64_t rc) } } +static u64 opal_scom_unmangle(u64 reg) +{ + /* + * XSCOM indirect addresses have the top bit set. Additionally + * the reset of the top 3 nibbles is always 0. + * + * Because the debugfs interface uses signed offsets and shifts + * the address left by 3, we basically cannot use the top 4 bits + * of the 64-bit address, and thus cannot use the indirect bit. + * + * To deal with that, we support the indirect bit being in bit + * 4 (IBM notation) instead of bit 0 in this API, we do the + * conversion here. To leave room for further xscom address + * expansion, we only clear out the top byte + * + */ + if (reg & (1ull << 59)) + reg = (reg & ~(0xffull << 56)) | (1ull << 63); + return reg; +} + static int opal_scom_read(scom_map_t map, u64 reg, u64 *value) { struct opal_scom_map *m = map; int64_t rc; + reg = opal_scom_unmangle(reg); rc = opal_xscom_read(m->chip, m->addr + reg, (uint64_t *)__pa(value)); return opal_xscom_err_xlate(rc); } @@ -85,6 +107,7 @@ static int opal_scom_write(scom_map_t map, u64 reg, u64 value) struct opal_scom_map *m = map; int64_t rc; + reg = opal_scom_unmangle(reg); rc = opal_xscom_write(m->chip, m->addr + reg, value); return opal_xscom_err_xlate(rc); } -- cgit v0.10.2 From 631ad691b5818291d89af9be607d2fe40be0886e Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Mon, 4 Nov 2013 16:32:46 +0800 Subject: powerpc/powernv: Add PE to its own PELTV We need add PE to its own PELTV. Otherwise, the errors originated from the PE might contribute to other PEs. In the result, we can't clear up the error successfully even we're checking and clearing errors during access to PCI config space. Cc: stable@vger.kernel.org Reported-by: kalshett@in.ibm.com Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index c639af7..198566e 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -163,13 +163,23 @@ static int pnv_ioda_configure_pe(struct pnv_phb *phb, struct pnv_ioda_pe *pe) rid_end = pe->rid + 1; } - /* Associate PE in PELT */ + /* + * Associate PE in PELT. We need add the PE into the + * corresponding PELT-V as well. Otherwise, the error + * originated from the PE might contribute to other + * PEs. + */ rc = opal_pci_set_pe(phb->opal_id, pe->pe_number, pe->rid, bcomp, dcomp, fcomp, OPAL_MAP_PE); if (rc) { pe_err(pe, "OPAL error %ld trying to setup PELT table\n", rc); return -ENXIO; } + + rc = opal_pci_set_peltv(phb->opal_id, pe->pe_number, + pe->pe_number, OPAL_ADD_PE_TO_DOMAIN); + if (rc) + pe_warn(pe, "OPAL error %d adding self to PELTV\n", rc); opal_pci_eeh_freeze_clear(phb->opal_id, pe->pe_number, OPAL_EEH_ACTION_CLEAR_FREEZE_ALL); -- cgit v0.10.2 From 36954dc78d8a1dcd4780cf4bd0fc6292791821b9 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Mon, 4 Nov 2013 16:32:47 +0800 Subject: powerpc/powernv: Reserve the correct PE number We're assigning PE numbers after the completion of PCI probe. During the PCI probe, we had PE#0 as the super container to encompass all PCI devices. However, that's inappropriate since PELTM has ascending order of priority on search on P7IOC. So we need PE#127 takes the role that PE#0 has previously. For PHB3, we still have PE#0 as the reserved PE. The patch supposes that the underly firmware has built the RID to PE# mapping after resetting IODA tables: all PELTM entries except last one has invalid mapping on P7IOC, but all RTEs have binding to PE#0. The reserved PE# is being exported by firmware by device tree. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index 198566e..084cdfa 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -1209,12 +1209,13 @@ void __init pnv_pci_init_ioda_phb(struct device_node *np, pr_err(" Failed to map registers !\n"); /* Initialize more IODA stuff */ + phb->ioda.total_pe = 1; prop32 = of_get_property(np, "ibm,opal-num-pes", NULL); - if (!prop32) - phb->ioda.total_pe = 1; - else + if (prop32) phb->ioda.total_pe = be32_to_cpup(prop32); - + prop32 = of_get_property(np, "ibm,opal-reserved-pe", NULL); + if (prop32) + phb->ioda.reserved_pe = be32_to_cpup(prop32); phb->ioda.m32_size = resource_size(&hose->mem_resources[0]); /* FW Has already off top 64k of M32 space (MSI space) */ phb->ioda.m32_size += 0x10000; @@ -1243,7 +1244,7 @@ void __init pnv_pci_init_ioda_phb(struct device_node *np, if (phb->type == PNV_PHB_IODA1) phb->ioda.io_segmap = aux + iomap_off; phb->ioda.pe_array = aux + pemap_off; - set_bit(0, phb->ioda.pe_alloc); + set_bit(phb->ioda.reserved_pe, phb->ioda.pe_alloc); INIT_LIST_HEAD(&phb->ioda.pe_dma_list); INIT_LIST_HEAD(&phb->ioda.pe_list); @@ -1268,8 +1269,10 @@ void __init pnv_pci_init_ioda_phb(struct device_node *np, segment_size); #endif - pr_info(" %d PE's M32: 0x%x [segment=0x%x] IO: 0x%x [segment=0x%x]\n", + pr_info(" %d (%d) PE's M32: 0x%x [segment=0x%x]" + " IO: 0x%x [segment=0x%x]\n", phb->ioda.total_pe, + phb->ioda.reserved_pe, phb->ioda.m32_size, phb->ioda.m32_segsize, phb->ioda.io_size, phb->ioda.io_segsize); @@ -1306,13 +1309,6 @@ void __init pnv_pci_init_ioda_phb(struct device_node *np, rc = opal_pci_reset(phb_id, OPAL_PCI_IODA_TABLE_RESET, OPAL_ASSERT_RESET); if (rc) pr_warning(" OPAL Error %ld performing IODA table reset !\n", rc); - - /* - * On IODA1 map everything to PE#0, on IODA2 we assume the IODA reset - * has cleared the RTT which has the same effect - */ - if (ioda_type == PNV_PHB_IODA1) - opal_pci_set_pe(phb_id, 0, 0, 7, 1, 1 , OPAL_MAP_PE); } void __init pnv_pci_init_ioda2_phb(struct device_node *np) diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c index 921ae67..4eb33a9 100644 --- a/arch/powerpc/platforms/powernv/pci.c +++ b/arch/powerpc/platforms/powernv/pci.c @@ -242,11 +242,15 @@ static void pnv_pci_config_check_eeh(struct pnv_phb *phb, /* * Get the PE#. During the PCI probe stage, we might not * setup that yet. So all ER errors should be mapped to - * PE#0 + * reserved PE. */ pe_no = PCI_DN(dn)->pe_number; - if (pe_no == IODA_INVALID_PE) - pe_no = 0; + if (pe_no == IODA_INVALID_PE) { + if (phb->type == PNV_PHB_P5IOC2) + pe_no = 0; + else + pe_no = phb->ioda.reserved_pe; + } /* Read freeze status */ rc = opal_pci_eeh_freeze_status(phb->opal_id, pe_no, &fstate, &pcierr, diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h index 64d3b12..911c24e 100644 --- a/arch/powerpc/platforms/powernv/pci.h +++ b/arch/powerpc/platforms/powernv/pci.h @@ -125,6 +125,7 @@ struct pnv_phb { struct { /* Global bridge info */ unsigned int total_pe; + unsigned int reserved_pe; unsigned int m32_size; unsigned int m32_segsize; unsigned int m32_pci_base; -- cgit v0.10.2 From 0c4888ef1d8a8b82c29075ce7e257ff795af15c7 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 5 Nov 2013 16:33:22 +1100 Subject: powerpc: Fix fatal SLB miss when restoring PPR When restoring the PPR value, we incorrectly access the thread structure at a time where MSR:RI is clear, which means we cannot recover from nested faults. However the thread structure isn't covered by the "bolted" SLB entries and thus accessing can fault. This fixes it by splitting the code so that the PPR value is loaded into a GPR before MSR:RI is cleared. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/ppc_asm.h b/arch/powerpc/include/asm/ppc_asm.h index 8deaaad..3c1acc3 100644 --- a/arch/powerpc/include/asm/ppc_asm.h +++ b/arch/powerpc/include/asm/ppc_asm.h @@ -406,13 +406,6 @@ BEGIN_FTR_SECTION_NESTED(945) \ std ra,TASKTHREADPPR(rb); \ END_FTR_SECTION_NESTED(CPU_FTR_HAS_PPR,CPU_FTR_HAS_PPR,945) -#define RESTORE_PPR(ra, rb) \ -BEGIN_FTR_SECTION_NESTED(946) \ - ld ra,PACACURRENT(r13); \ - ld rb,TASKTHREADPPR(ra); \ - mtspr SPRN_PPR,rb; /* Restore PPR */ \ -END_FTR_SECTION_NESTED(CPU_FTR_HAS_PPR,CPU_FTR_HAS_PPR,946) - #endif /* diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index 12679cd..bbfb029 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -818,6 +818,12 @@ fast_exception_return: andi. r0,r3,MSR_RI beq- unrecov_restore + /* Load PPR from thread struct before we clear MSR:RI */ +BEGIN_FTR_SECTION + ld r2,PACACURRENT(r13) + ld r2,TASKTHREADPPR(r2) +END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR) + /* * Clear RI before restoring r13. If we are returning to * userspace and we take an exception after restoring r13, @@ -838,8 +844,10 @@ fast_exception_return: */ andi. r0,r3,MSR_PR beq 1f +BEGIN_FTR_SECTION + mtspr SPRN_PPR,r2 /* Restore PPR */ +END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR) ACCOUNT_CPU_USER_EXIT(r2, r4) - RESTORE_PPR(r2, r4) REST_GPR(13, r1) 1: mtspr SPRN_SRR1,r3 -- cgit v0.10.2 From 7a6354e241d8fbc145836ac24e47630f12754536 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 31 Oct 2013 18:07:08 +0100 Subject: sched: Move wait.c into kernel/sched/ Suggested-by: Ingo Molnar Signed-off-by: Peter Zijlstra Cc: Linus Torvalds Cc: Andrew Morton Link: http://lkml.kernel.org/n/tip-5q5yqvdaen0rmapwloeaotx3@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/kernel/Makefile b/kernel/Makefile index 1ce4755..b3d51e2 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -7,7 +7,7 @@ obj-y = fork.o exec_domain.o panic.o \ sysctl.o sysctl_binary.o capability.o ptrace.o timer.o user.o \ signal.o sys.o kmod.o workqueue.o pid.o task_work.o \ rcupdate.o extable.o params.o posix-timers.o \ - kthread.o wait.o sys_ni.o posix-cpu-timers.o mutex.o \ + kthread.o sys_ni.o posix-cpu-timers.o mutex.o \ hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \ notifier.o ksysfs.o cred.o reboot.o \ async.o range.o groups.o lglock.o smpboot.o diff --git a/kernel/sched/Makefile b/kernel/sched/Makefile index 54adcf3..f8d3f4b 100644 --- a/kernel/sched/Makefile +++ b/kernel/sched/Makefile @@ -12,6 +12,7 @@ CFLAGS_core.o := $(PROFILING) -fno-omit-frame-pointer endif obj-y += core.o proc.o clock.o cputime.o idle_task.o fair.o rt.o stop_task.o +obj-y += wait.o obj-$(CONFIG_SMP) += cpupri.o obj-$(CONFIG_SCHED_AUTOGROUP) += auto_group.o obj-$(CONFIG_SCHEDSTATS) += stats.o diff --git a/kernel/sched/wait.c b/kernel/sched/wait.c new file mode 100644 index 0000000..de21c63 --- /dev/null +++ b/kernel/sched/wait.c @@ -0,0 +1,401 @@ +/* + * Generic waiting primitives. + * + * (C) 2004 Nadia Yvette Chambers, Oracle + */ +#include +#include +#include +#include +#include +#include + +void __init_waitqueue_head(wait_queue_head_t *q, const char *name, struct lock_class_key *key) +{ + spin_lock_init(&q->lock); + lockdep_set_class_and_name(&q->lock, key, name); + INIT_LIST_HEAD(&q->task_list); +} + +EXPORT_SYMBOL(__init_waitqueue_head); + +void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait) +{ + unsigned long flags; + + wait->flags &= ~WQ_FLAG_EXCLUSIVE; + spin_lock_irqsave(&q->lock, flags); + __add_wait_queue(q, wait); + spin_unlock_irqrestore(&q->lock, flags); +} +EXPORT_SYMBOL(add_wait_queue); + +void add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t *wait) +{ + unsigned long flags; + + wait->flags |= WQ_FLAG_EXCLUSIVE; + spin_lock_irqsave(&q->lock, flags); + __add_wait_queue_tail(q, wait); + spin_unlock_irqrestore(&q->lock, flags); +} +EXPORT_SYMBOL(add_wait_queue_exclusive); + +void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait) +{ + unsigned long flags; + + spin_lock_irqsave(&q->lock, flags); + __remove_wait_queue(q, wait); + spin_unlock_irqrestore(&q->lock, flags); +} +EXPORT_SYMBOL(remove_wait_queue); + + +/* + * Note: we use "set_current_state()" _after_ the wait-queue add, + * because we need a memory barrier there on SMP, so that any + * wake-function that tests for the wait-queue being active + * will be guaranteed to see waitqueue addition _or_ subsequent + * tests in this thread will see the wakeup having taken place. + * + * The spin_unlock() itself is semi-permeable and only protects + * one way (it only protects stuff inside the critical region and + * stops them from bleeding out - it would still allow subsequent + * loads to move into the critical region). + */ +void +prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state) +{ + unsigned long flags; + + wait->flags &= ~WQ_FLAG_EXCLUSIVE; + spin_lock_irqsave(&q->lock, flags); + if (list_empty(&wait->task_list)) + __add_wait_queue(q, wait); + set_current_state(state); + spin_unlock_irqrestore(&q->lock, flags); +} +EXPORT_SYMBOL(prepare_to_wait); + +void +prepare_to_wait_exclusive(wait_queue_head_t *q, wait_queue_t *wait, int state) +{ + unsigned long flags; + + wait->flags |= WQ_FLAG_EXCLUSIVE; + spin_lock_irqsave(&q->lock, flags); + if (list_empty(&wait->task_list)) + __add_wait_queue_tail(q, wait); + set_current_state(state); + spin_unlock_irqrestore(&q->lock, flags); +} +EXPORT_SYMBOL(prepare_to_wait_exclusive); + +long prepare_to_wait_event(wait_queue_head_t *q, wait_queue_t *wait, int state) +{ + unsigned long flags; + + if (signal_pending_state(state, current)) + return -ERESTARTSYS; + + wait->private = current; + wait->func = autoremove_wake_function; + + spin_lock_irqsave(&q->lock, flags); + if (list_empty(&wait->task_list)) { + if (wait->flags & WQ_FLAG_EXCLUSIVE) + __add_wait_queue_tail(q, wait); + else + __add_wait_queue(q, wait); + } + set_current_state(state); + spin_unlock_irqrestore(&q->lock, flags); + + return 0; +} +EXPORT_SYMBOL(prepare_to_wait_event); + +/** + * finish_wait - clean up after waiting in a queue + * @q: waitqueue waited on + * @wait: wait descriptor + * + * Sets current thread back to running state and removes + * the wait descriptor from the given waitqueue if still + * queued. + */ +void finish_wait(wait_queue_head_t *q, wait_queue_t *wait) +{ + unsigned long flags; + + __set_current_state(TASK_RUNNING); + /* + * We can check for list emptiness outside the lock + * IFF: + * - we use the "careful" check that verifies both + * the next and prev pointers, so that there cannot + * be any half-pending updates in progress on other + * CPU's that we haven't seen yet (and that might + * still change the stack area. + * and + * - all other users take the lock (ie we can only + * have _one_ other CPU that looks at or modifies + * the list). + */ + if (!list_empty_careful(&wait->task_list)) { + spin_lock_irqsave(&q->lock, flags); + list_del_init(&wait->task_list); + spin_unlock_irqrestore(&q->lock, flags); + } +} +EXPORT_SYMBOL(finish_wait); + +/** + * abort_exclusive_wait - abort exclusive waiting in a queue + * @q: waitqueue waited on + * @wait: wait descriptor + * @mode: runstate of the waiter to be woken + * @key: key to identify a wait bit queue or %NULL + * + * Sets current thread back to running state and removes + * the wait descriptor from the given waitqueue if still + * queued. + * + * Wakes up the next waiter if the caller is concurrently + * woken up through the queue. + * + * This prevents waiter starvation where an exclusive waiter + * aborts and is woken up concurrently and no one wakes up + * the next waiter. + */ +void abort_exclusive_wait(wait_queue_head_t *q, wait_queue_t *wait, + unsigned int mode, void *key) +{ + unsigned long flags; + + __set_current_state(TASK_RUNNING); + spin_lock_irqsave(&q->lock, flags); + if (!list_empty(&wait->task_list)) + list_del_init(&wait->task_list); + else if (waitqueue_active(q)) + __wake_up_locked_key(q, mode, key); + spin_unlock_irqrestore(&q->lock, flags); +} +EXPORT_SYMBOL(abort_exclusive_wait); + +int autoremove_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key) +{ + int ret = default_wake_function(wait, mode, sync, key); + + if (ret) + list_del_init(&wait->task_list); + return ret; +} +EXPORT_SYMBOL(autoremove_wake_function); + +int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *arg) +{ + struct wait_bit_key *key = arg; + struct wait_bit_queue *wait_bit + = container_of(wait, struct wait_bit_queue, wait); + + if (wait_bit->key.flags != key->flags || + wait_bit->key.bit_nr != key->bit_nr || + test_bit(key->bit_nr, key->flags)) + return 0; + else + return autoremove_wake_function(wait, mode, sync, key); +} +EXPORT_SYMBOL(wake_bit_function); + +/* + * To allow interruptible waiting and asynchronous (i.e. nonblocking) + * waiting, the actions of __wait_on_bit() and __wait_on_bit_lock() are + * permitted return codes. Nonzero return codes halt waiting and return. + */ +int __sched +__wait_on_bit(wait_queue_head_t *wq, struct wait_bit_queue *q, + int (*action)(void *), unsigned mode) +{ + int ret = 0; + + do { + prepare_to_wait(wq, &q->wait, mode); + if (test_bit(q->key.bit_nr, q->key.flags)) + ret = (*action)(q->key.flags); + } while (test_bit(q->key.bit_nr, q->key.flags) && !ret); + finish_wait(wq, &q->wait); + return ret; +} +EXPORT_SYMBOL(__wait_on_bit); + +int __sched out_of_line_wait_on_bit(void *word, int bit, + int (*action)(void *), unsigned mode) +{ + wait_queue_head_t *wq = bit_waitqueue(word, bit); + DEFINE_WAIT_BIT(wait, word, bit); + + return __wait_on_bit(wq, &wait, action, mode); +} +EXPORT_SYMBOL(out_of_line_wait_on_bit); + +int __sched +__wait_on_bit_lock(wait_queue_head_t *wq, struct wait_bit_queue *q, + int (*action)(void *), unsigned mode) +{ + do { + int ret; + + prepare_to_wait_exclusive(wq, &q->wait, mode); + if (!test_bit(q->key.bit_nr, q->key.flags)) + continue; + ret = action(q->key.flags); + if (!ret) + continue; + abort_exclusive_wait(wq, &q->wait, mode, &q->key); + return ret; + } while (test_and_set_bit(q->key.bit_nr, q->key.flags)); + finish_wait(wq, &q->wait); + return 0; +} +EXPORT_SYMBOL(__wait_on_bit_lock); + +int __sched out_of_line_wait_on_bit_lock(void *word, int bit, + int (*action)(void *), unsigned mode) +{ + wait_queue_head_t *wq = bit_waitqueue(word, bit); + DEFINE_WAIT_BIT(wait, word, bit); + + return __wait_on_bit_lock(wq, &wait, action, mode); +} +EXPORT_SYMBOL(out_of_line_wait_on_bit_lock); + +void __wake_up_bit(wait_queue_head_t *wq, void *word, int bit) +{ + struct wait_bit_key key = __WAIT_BIT_KEY_INITIALIZER(word, bit); + if (waitqueue_active(wq)) + __wake_up(wq, TASK_NORMAL, 1, &key); +} +EXPORT_SYMBOL(__wake_up_bit); + +/** + * wake_up_bit - wake up a waiter on a bit + * @word: the word being waited on, a kernel virtual address + * @bit: the bit of the word being waited on + * + * There is a standard hashed waitqueue table for generic use. This + * is the part of the hashtable's accessor API that wakes up waiters + * on a bit. For instance, if one were to have waiters on a bitflag, + * one would call wake_up_bit() after clearing the bit. + * + * In order for this to function properly, as it uses waitqueue_active() + * internally, some kind of memory barrier must be done prior to calling + * this. Typically, this will be smp_mb__after_clear_bit(), but in some + * cases where bitflags are manipulated non-atomically under a lock, one + * may need to use a less regular barrier, such fs/inode.c's smp_mb(), + * because spin_unlock() does not guarantee a memory barrier. + */ +void wake_up_bit(void *word, int bit) +{ + __wake_up_bit(bit_waitqueue(word, bit), word, bit); +} +EXPORT_SYMBOL(wake_up_bit); + +wait_queue_head_t *bit_waitqueue(void *word, int bit) +{ + const int shift = BITS_PER_LONG == 32 ? 5 : 6; + const struct zone *zone = page_zone(virt_to_page(word)); + unsigned long val = (unsigned long)word << shift | bit; + + return &zone->wait_table[hash_long(val, zone->wait_table_bits)]; +} +EXPORT_SYMBOL(bit_waitqueue); + +/* + * Manipulate the atomic_t address to produce a better bit waitqueue table hash + * index (we're keying off bit -1, but that would produce a horrible hash + * value). + */ +static inline wait_queue_head_t *atomic_t_waitqueue(atomic_t *p) +{ + if (BITS_PER_LONG == 64) { + unsigned long q = (unsigned long)p; + return bit_waitqueue((void *)(q & ~1), q & 1); + } + return bit_waitqueue(p, 0); +} + +static int wake_atomic_t_function(wait_queue_t *wait, unsigned mode, int sync, + void *arg) +{ + struct wait_bit_key *key = arg; + struct wait_bit_queue *wait_bit + = container_of(wait, struct wait_bit_queue, wait); + atomic_t *val = key->flags; + + if (wait_bit->key.flags != key->flags || + wait_bit->key.bit_nr != key->bit_nr || + atomic_read(val) != 0) + return 0; + return autoremove_wake_function(wait, mode, sync, key); +} + +/* + * To allow interruptible waiting and asynchronous (i.e. nonblocking) waiting, + * the actions of __wait_on_atomic_t() are permitted return codes. Nonzero + * return codes halt waiting and return. + */ +static __sched +int __wait_on_atomic_t(wait_queue_head_t *wq, struct wait_bit_queue *q, + int (*action)(atomic_t *), unsigned mode) +{ + atomic_t *val; + int ret = 0; + + do { + prepare_to_wait(wq, &q->wait, mode); + val = q->key.flags; + if (atomic_read(val) == 0) + break; + ret = (*action)(val); + } while (!ret && atomic_read(val) != 0); + finish_wait(wq, &q->wait); + return ret; +} + +#define DEFINE_WAIT_ATOMIC_T(name, p) \ + struct wait_bit_queue name = { \ + .key = __WAIT_ATOMIC_T_KEY_INITIALIZER(p), \ + .wait = { \ + .private = current, \ + .func = wake_atomic_t_function, \ + .task_list = \ + LIST_HEAD_INIT((name).wait.task_list), \ + }, \ + } + +__sched int out_of_line_wait_on_atomic_t(atomic_t *p, int (*action)(atomic_t *), + unsigned mode) +{ + wait_queue_head_t *wq = atomic_t_waitqueue(p); + DEFINE_WAIT_ATOMIC_T(wait, p); + + return __wait_on_atomic_t(wq, &wait, action, mode); +} +EXPORT_SYMBOL(out_of_line_wait_on_atomic_t); + +/** + * wake_up_atomic_t - Wake up a waiter on a atomic_t + * @p: The atomic_t being waited on, a kernel virtual address + * + * Wake up anyone waiting for the atomic_t to go to zero. + * + * Abuse the bit-waker function and its waitqueue hash table set (the atomic_t + * check is done by the waiter's wake function, not the by the waker itself). + */ +void wake_up_atomic_t(atomic_t *p) +{ + __wake_up_bit(atomic_t_waitqueue(p), p, WAIT_ATOMIC_T_BIT_NR); +} +EXPORT_SYMBOL(wake_up_atomic_t); diff --git a/kernel/wait.c b/kernel/wait.c deleted file mode 100644 index de21c63..0000000 --- a/kernel/wait.c +++ /dev/null @@ -1,401 +0,0 @@ -/* - * Generic waiting primitives. - * - * (C) 2004 Nadia Yvette Chambers, Oracle - */ -#include -#include -#include -#include -#include -#include - -void __init_waitqueue_head(wait_queue_head_t *q, const char *name, struct lock_class_key *key) -{ - spin_lock_init(&q->lock); - lockdep_set_class_and_name(&q->lock, key, name); - INIT_LIST_HEAD(&q->task_list); -} - -EXPORT_SYMBOL(__init_waitqueue_head); - -void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait) -{ - unsigned long flags; - - wait->flags &= ~WQ_FLAG_EXCLUSIVE; - spin_lock_irqsave(&q->lock, flags); - __add_wait_queue(q, wait); - spin_unlock_irqrestore(&q->lock, flags); -} -EXPORT_SYMBOL(add_wait_queue); - -void add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t *wait) -{ - unsigned long flags; - - wait->flags |= WQ_FLAG_EXCLUSIVE; - spin_lock_irqsave(&q->lock, flags); - __add_wait_queue_tail(q, wait); - spin_unlock_irqrestore(&q->lock, flags); -} -EXPORT_SYMBOL(add_wait_queue_exclusive); - -void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait) -{ - unsigned long flags; - - spin_lock_irqsave(&q->lock, flags); - __remove_wait_queue(q, wait); - spin_unlock_irqrestore(&q->lock, flags); -} -EXPORT_SYMBOL(remove_wait_queue); - - -/* - * Note: we use "set_current_state()" _after_ the wait-queue add, - * because we need a memory barrier there on SMP, so that any - * wake-function that tests for the wait-queue being active - * will be guaranteed to see waitqueue addition _or_ subsequent - * tests in this thread will see the wakeup having taken place. - * - * The spin_unlock() itself is semi-permeable and only protects - * one way (it only protects stuff inside the critical region and - * stops them from bleeding out - it would still allow subsequent - * loads to move into the critical region). - */ -void -prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state) -{ - unsigned long flags; - - wait->flags &= ~WQ_FLAG_EXCLUSIVE; - spin_lock_irqsave(&q->lock, flags); - if (list_empty(&wait->task_list)) - __add_wait_queue(q, wait); - set_current_state(state); - spin_unlock_irqrestore(&q->lock, flags); -} -EXPORT_SYMBOL(prepare_to_wait); - -void -prepare_to_wait_exclusive(wait_queue_head_t *q, wait_queue_t *wait, int state) -{ - unsigned long flags; - - wait->flags |= WQ_FLAG_EXCLUSIVE; - spin_lock_irqsave(&q->lock, flags); - if (list_empty(&wait->task_list)) - __add_wait_queue_tail(q, wait); - set_current_state(state); - spin_unlock_irqrestore(&q->lock, flags); -} -EXPORT_SYMBOL(prepare_to_wait_exclusive); - -long prepare_to_wait_event(wait_queue_head_t *q, wait_queue_t *wait, int state) -{ - unsigned long flags; - - if (signal_pending_state(state, current)) - return -ERESTARTSYS; - - wait->private = current; - wait->func = autoremove_wake_function; - - spin_lock_irqsave(&q->lock, flags); - if (list_empty(&wait->task_list)) { - if (wait->flags & WQ_FLAG_EXCLUSIVE) - __add_wait_queue_tail(q, wait); - else - __add_wait_queue(q, wait); - } - set_current_state(state); - spin_unlock_irqrestore(&q->lock, flags); - - return 0; -} -EXPORT_SYMBOL(prepare_to_wait_event); - -/** - * finish_wait - clean up after waiting in a queue - * @q: waitqueue waited on - * @wait: wait descriptor - * - * Sets current thread back to running state and removes - * the wait descriptor from the given waitqueue if still - * queued. - */ -void finish_wait(wait_queue_head_t *q, wait_queue_t *wait) -{ - unsigned long flags; - - __set_current_state(TASK_RUNNING); - /* - * We can check for list emptiness outside the lock - * IFF: - * - we use the "careful" check that verifies both - * the next and prev pointers, so that there cannot - * be any half-pending updates in progress on other - * CPU's that we haven't seen yet (and that might - * still change the stack area. - * and - * - all other users take the lock (ie we can only - * have _one_ other CPU that looks at or modifies - * the list). - */ - if (!list_empty_careful(&wait->task_list)) { - spin_lock_irqsave(&q->lock, flags); - list_del_init(&wait->task_list); - spin_unlock_irqrestore(&q->lock, flags); - } -} -EXPORT_SYMBOL(finish_wait); - -/** - * abort_exclusive_wait - abort exclusive waiting in a queue - * @q: waitqueue waited on - * @wait: wait descriptor - * @mode: runstate of the waiter to be woken - * @key: key to identify a wait bit queue or %NULL - * - * Sets current thread back to running state and removes - * the wait descriptor from the given waitqueue if still - * queued. - * - * Wakes up the next waiter if the caller is concurrently - * woken up through the queue. - * - * This prevents waiter starvation where an exclusive waiter - * aborts and is woken up concurrently and no one wakes up - * the next waiter. - */ -void abort_exclusive_wait(wait_queue_head_t *q, wait_queue_t *wait, - unsigned int mode, void *key) -{ - unsigned long flags; - - __set_current_state(TASK_RUNNING); - spin_lock_irqsave(&q->lock, flags); - if (!list_empty(&wait->task_list)) - list_del_init(&wait->task_list); - else if (waitqueue_active(q)) - __wake_up_locked_key(q, mode, key); - spin_unlock_irqrestore(&q->lock, flags); -} -EXPORT_SYMBOL(abort_exclusive_wait); - -int autoremove_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key) -{ - int ret = default_wake_function(wait, mode, sync, key); - - if (ret) - list_del_init(&wait->task_list); - return ret; -} -EXPORT_SYMBOL(autoremove_wake_function); - -int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *arg) -{ - struct wait_bit_key *key = arg; - struct wait_bit_queue *wait_bit - = container_of(wait, struct wait_bit_queue, wait); - - if (wait_bit->key.flags != key->flags || - wait_bit->key.bit_nr != key->bit_nr || - test_bit(key->bit_nr, key->flags)) - return 0; - else - return autoremove_wake_function(wait, mode, sync, key); -} -EXPORT_SYMBOL(wake_bit_function); - -/* - * To allow interruptible waiting and asynchronous (i.e. nonblocking) - * waiting, the actions of __wait_on_bit() and __wait_on_bit_lock() are - * permitted return codes. Nonzero return codes halt waiting and return. - */ -int __sched -__wait_on_bit(wait_queue_head_t *wq, struct wait_bit_queue *q, - int (*action)(void *), unsigned mode) -{ - int ret = 0; - - do { - prepare_to_wait(wq, &q->wait, mode); - if (test_bit(q->key.bit_nr, q->key.flags)) - ret = (*action)(q->key.flags); - } while (test_bit(q->key.bit_nr, q->key.flags) && !ret); - finish_wait(wq, &q->wait); - return ret; -} -EXPORT_SYMBOL(__wait_on_bit); - -int __sched out_of_line_wait_on_bit(void *word, int bit, - int (*action)(void *), unsigned mode) -{ - wait_queue_head_t *wq = bit_waitqueue(word, bit); - DEFINE_WAIT_BIT(wait, word, bit); - - return __wait_on_bit(wq, &wait, action, mode); -} -EXPORT_SYMBOL(out_of_line_wait_on_bit); - -int __sched -__wait_on_bit_lock(wait_queue_head_t *wq, struct wait_bit_queue *q, - int (*action)(void *), unsigned mode) -{ - do { - int ret; - - prepare_to_wait_exclusive(wq, &q->wait, mode); - if (!test_bit(q->key.bit_nr, q->key.flags)) - continue; - ret = action(q->key.flags); - if (!ret) - continue; - abort_exclusive_wait(wq, &q->wait, mode, &q->key); - return ret; - } while (test_and_set_bit(q->key.bit_nr, q->key.flags)); - finish_wait(wq, &q->wait); - return 0; -} -EXPORT_SYMBOL(__wait_on_bit_lock); - -int __sched out_of_line_wait_on_bit_lock(void *word, int bit, - int (*action)(void *), unsigned mode) -{ - wait_queue_head_t *wq = bit_waitqueue(word, bit); - DEFINE_WAIT_BIT(wait, word, bit); - - return __wait_on_bit_lock(wq, &wait, action, mode); -} -EXPORT_SYMBOL(out_of_line_wait_on_bit_lock); - -void __wake_up_bit(wait_queue_head_t *wq, void *word, int bit) -{ - struct wait_bit_key key = __WAIT_BIT_KEY_INITIALIZER(word, bit); - if (waitqueue_active(wq)) - __wake_up(wq, TASK_NORMAL, 1, &key); -} -EXPORT_SYMBOL(__wake_up_bit); - -/** - * wake_up_bit - wake up a waiter on a bit - * @word: the word being waited on, a kernel virtual address - * @bit: the bit of the word being waited on - * - * There is a standard hashed waitqueue table for generic use. This - * is the part of the hashtable's accessor API that wakes up waiters - * on a bit. For instance, if one were to have waiters on a bitflag, - * one would call wake_up_bit() after clearing the bit. - * - * In order for this to function properly, as it uses waitqueue_active() - * internally, some kind of memory barrier must be done prior to calling - * this. Typically, this will be smp_mb__after_clear_bit(), but in some - * cases where bitflags are manipulated non-atomically under a lock, one - * may need to use a less regular barrier, such fs/inode.c's smp_mb(), - * because spin_unlock() does not guarantee a memory barrier. - */ -void wake_up_bit(void *word, int bit) -{ - __wake_up_bit(bit_waitqueue(word, bit), word, bit); -} -EXPORT_SYMBOL(wake_up_bit); - -wait_queue_head_t *bit_waitqueue(void *word, int bit) -{ - const int shift = BITS_PER_LONG == 32 ? 5 : 6; - const struct zone *zone = page_zone(virt_to_page(word)); - unsigned long val = (unsigned long)word << shift | bit; - - return &zone->wait_table[hash_long(val, zone->wait_table_bits)]; -} -EXPORT_SYMBOL(bit_waitqueue); - -/* - * Manipulate the atomic_t address to produce a better bit waitqueue table hash - * index (we're keying off bit -1, but that would produce a horrible hash - * value). - */ -static inline wait_queue_head_t *atomic_t_waitqueue(atomic_t *p) -{ - if (BITS_PER_LONG == 64) { - unsigned long q = (unsigned long)p; - return bit_waitqueue((void *)(q & ~1), q & 1); - } - return bit_waitqueue(p, 0); -} - -static int wake_atomic_t_function(wait_queue_t *wait, unsigned mode, int sync, - void *arg) -{ - struct wait_bit_key *key = arg; - struct wait_bit_queue *wait_bit - = container_of(wait, struct wait_bit_queue, wait); - atomic_t *val = key->flags; - - if (wait_bit->key.flags != key->flags || - wait_bit->key.bit_nr != key->bit_nr || - atomic_read(val) != 0) - return 0; - return autoremove_wake_function(wait, mode, sync, key); -} - -/* - * To allow interruptible waiting and asynchronous (i.e. nonblocking) waiting, - * the actions of __wait_on_atomic_t() are permitted return codes. Nonzero - * return codes halt waiting and return. - */ -static __sched -int __wait_on_atomic_t(wait_queue_head_t *wq, struct wait_bit_queue *q, - int (*action)(atomic_t *), unsigned mode) -{ - atomic_t *val; - int ret = 0; - - do { - prepare_to_wait(wq, &q->wait, mode); - val = q->key.flags; - if (atomic_read(val) == 0) - break; - ret = (*action)(val); - } while (!ret && atomic_read(val) != 0); - finish_wait(wq, &q->wait); - return ret; -} - -#define DEFINE_WAIT_ATOMIC_T(name, p) \ - struct wait_bit_queue name = { \ - .key = __WAIT_ATOMIC_T_KEY_INITIALIZER(p), \ - .wait = { \ - .private = current, \ - .func = wake_atomic_t_function, \ - .task_list = \ - LIST_HEAD_INIT((name).wait.task_list), \ - }, \ - } - -__sched int out_of_line_wait_on_atomic_t(atomic_t *p, int (*action)(atomic_t *), - unsigned mode) -{ - wait_queue_head_t *wq = atomic_t_waitqueue(p); - DEFINE_WAIT_ATOMIC_T(wait, p); - - return __wait_on_atomic_t(wq, &wait, action, mode); -} -EXPORT_SYMBOL(out_of_line_wait_on_atomic_t); - -/** - * wake_up_atomic_t - Wake up a waiter on a atomic_t - * @p: The atomic_t being waited on, a kernel virtual address - * - * Wake up anyone waiting for the atomic_t to go to zero. - * - * Abuse the bit-waker function and its waitqueue hash table set (the atomic_t - * check is done by the waiter's wake function, not the by the waker itself). - */ -void wake_up_atomic_t(atomic_t *p) -{ - __wake_up_bit(atomic_t_waitqueue(p), p, WAIT_ATOMIC_T_BIT_NR); -} -EXPORT_SYMBOL(wake_up_atomic_t); -- cgit v0.10.2 From b4145872f7049e429718b40b86e1b46659988398 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 4 Oct 2013 17:24:35 +0200 Subject: sched: Move wait code from core.c to wait.c For some reason only the wait part of the wait api lives in kernel/sched/wait.c and the wake part still lives in kernel/sched/core.c; ammend this. Signed-off-by: Peter Zijlstra Cc: Linus Torvalds Cc: Andrew Morton Link: http://lkml.kernel.org/n/tip-ftycee88naznulqk7ei5mbci@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 450a34b..91b2845 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2688,109 +2688,6 @@ int default_wake_function(wait_queue_t *curr, unsigned mode, int wake_flags, } EXPORT_SYMBOL(default_wake_function); -/* - * The core wakeup function. Non-exclusive wakeups (nr_exclusive == 0) just - * wake everything up. If it's an exclusive wakeup (nr_exclusive == small +ve - * number) then we wake all the non-exclusive tasks and one exclusive task. - * - * There are circumstances in which we can try to wake a task which has already - * started to run but is not in state TASK_RUNNING. try_to_wake_up() returns - * zero in this (rare) case, and we handle it by continuing to scan the queue. - */ -static void __wake_up_common(wait_queue_head_t *q, unsigned int mode, - int nr_exclusive, int wake_flags, void *key) -{ - wait_queue_t *curr, *next; - - list_for_each_entry_safe(curr, next, &q->task_list, task_list) { - unsigned flags = curr->flags; - - if (curr->func(curr, mode, wake_flags, key) && - (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive) - break; - } -} - -/** - * __wake_up - wake up threads blocked on a waitqueue. - * @q: the waitqueue - * @mode: which threads - * @nr_exclusive: how many wake-one or wake-many threads to wake up - * @key: is directly passed to the wakeup function - * - * It may be assumed that this function implies a write memory barrier before - * changing the task state if and only if any tasks are woken up. - */ -void __wake_up(wait_queue_head_t *q, unsigned int mode, - int nr_exclusive, void *key) -{ - unsigned long flags; - - spin_lock_irqsave(&q->lock, flags); - __wake_up_common(q, mode, nr_exclusive, 0, key); - spin_unlock_irqrestore(&q->lock, flags); -} -EXPORT_SYMBOL(__wake_up); - -/* - * Same as __wake_up but called with the spinlock in wait_queue_head_t held. - */ -void __wake_up_locked(wait_queue_head_t *q, unsigned int mode, int nr) -{ - __wake_up_common(q, mode, nr, 0, NULL); -} -EXPORT_SYMBOL_GPL(__wake_up_locked); - -void __wake_up_locked_key(wait_queue_head_t *q, unsigned int mode, void *key) -{ - __wake_up_common(q, mode, 1, 0, key); -} -EXPORT_SYMBOL_GPL(__wake_up_locked_key); - -/** - * __wake_up_sync_key - wake up threads blocked on a waitqueue. - * @q: the waitqueue - * @mode: which threads - * @nr_exclusive: how many wake-one or wake-many threads to wake up - * @key: opaque value to be passed to wakeup targets - * - * The sync wakeup differs that the waker knows that it will schedule - * away soon, so while the target thread will be woken up, it will not - * be migrated to another CPU - ie. the two threads are 'synchronized' - * with each other. This can prevent needless bouncing between CPUs. - * - * On UP it can prevent extra preemption. - * - * It may be assumed that this function implies a write memory barrier before - * changing the task state if and only if any tasks are woken up. - */ -void __wake_up_sync_key(wait_queue_head_t *q, unsigned int mode, - int nr_exclusive, void *key) -{ - unsigned long flags; - int wake_flags = WF_SYNC; - - if (unlikely(!q)) - return; - - if (unlikely(nr_exclusive != 1)) - wake_flags = 0; - - spin_lock_irqsave(&q->lock, flags); - __wake_up_common(q, mode, nr_exclusive, wake_flags, key); - spin_unlock_irqrestore(&q->lock, flags); -} -EXPORT_SYMBOL_GPL(__wake_up_sync_key); - -/* - * __wake_up_sync - see __wake_up_sync_key() - */ -void __wake_up_sync(wait_queue_head_t *q, unsigned int mode, int nr_exclusive) -{ - __wake_up_sync_key(q, mode, nr_exclusive, NULL); -} -EXPORT_SYMBOL_GPL(__wake_up_sync); /* For internal use only */ - /** * complete: - signals a single thread waiting on this completion * @x: holds the state of this particular completion @@ -2809,7 +2706,7 @@ void complete(struct completion *x) spin_lock_irqsave(&x->wait.lock, flags); x->done++; - __wake_up_common(&x->wait, TASK_NORMAL, 1, 0, NULL); + __wake_up_locked(&x->wait, TASK_NORMAL, 1); spin_unlock_irqrestore(&x->wait.lock, flags); } EXPORT_SYMBOL(complete); @@ -2829,7 +2726,7 @@ void complete_all(struct completion *x) spin_lock_irqsave(&x->wait.lock, flags); x->done += UINT_MAX/2; - __wake_up_common(&x->wait, TASK_NORMAL, 0, 0, NULL); + __wake_up_locked(&x->wait, TASK_NORMAL, 0); spin_unlock_irqrestore(&x->wait.lock, flags); } EXPORT_SYMBOL(complete_all); diff --git a/kernel/sched/wait.c b/kernel/sched/wait.c index de21c63..7d50f79 100644 --- a/kernel/sched/wait.c +++ b/kernel/sched/wait.c @@ -53,6 +53,109 @@ EXPORT_SYMBOL(remove_wait_queue); /* + * The core wakeup function. Non-exclusive wakeups (nr_exclusive == 0) just + * wake everything up. If it's an exclusive wakeup (nr_exclusive == small +ve + * number) then we wake all the non-exclusive tasks and one exclusive task. + * + * There are circumstances in which we can try to wake a task which has already + * started to run but is not in state TASK_RUNNING. try_to_wake_up() returns + * zero in this (rare) case, and we handle it by continuing to scan the queue. + */ +static void __wake_up_common(wait_queue_head_t *q, unsigned int mode, + int nr_exclusive, int wake_flags, void *key) +{ + wait_queue_t *curr, *next; + + list_for_each_entry_safe(curr, next, &q->task_list, task_list) { + unsigned flags = curr->flags; + + if (curr->func(curr, mode, wake_flags, key) && + (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive) + break; + } +} + +/** + * __wake_up - wake up threads blocked on a waitqueue. + * @q: the waitqueue + * @mode: which threads + * @nr_exclusive: how many wake-one or wake-many threads to wake up + * @key: is directly passed to the wakeup function + * + * It may be assumed that this function implies a write memory barrier before + * changing the task state if and only if any tasks are woken up. + */ +void __wake_up(wait_queue_head_t *q, unsigned int mode, + int nr_exclusive, void *key) +{ + unsigned long flags; + + spin_lock_irqsave(&q->lock, flags); + __wake_up_common(q, mode, nr_exclusive, 0, key); + spin_unlock_irqrestore(&q->lock, flags); +} +EXPORT_SYMBOL(__wake_up); + +/* + * Same as __wake_up but called with the spinlock in wait_queue_head_t held. + */ +void __wake_up_locked(wait_queue_head_t *q, unsigned int mode, int nr) +{ + __wake_up_common(q, mode, nr, 0, NULL); +} +EXPORT_SYMBOL_GPL(__wake_up_locked); + +void __wake_up_locked_key(wait_queue_head_t *q, unsigned int mode, void *key) +{ + __wake_up_common(q, mode, 1, 0, key); +} +EXPORT_SYMBOL_GPL(__wake_up_locked_key); + +/** + * __wake_up_sync_key - wake up threads blocked on a waitqueue. + * @q: the waitqueue + * @mode: which threads + * @nr_exclusive: how many wake-one or wake-many threads to wake up + * @key: opaque value to be passed to wakeup targets + * + * The sync wakeup differs that the waker knows that it will schedule + * away soon, so while the target thread will be woken up, it will not + * be migrated to another CPU - ie. the two threads are 'synchronized' + * with each other. This can prevent needless bouncing between CPUs. + * + * On UP it can prevent extra preemption. + * + * It may be assumed that this function implies a write memory barrier before + * changing the task state if and only if any tasks are woken up. + */ +void __wake_up_sync_key(wait_queue_head_t *q, unsigned int mode, + int nr_exclusive, void *key) +{ + unsigned long flags; + int wake_flags = 1; /* XXX WF_SYNC */ + + if (unlikely(!q)) + return; + + if (unlikely(nr_exclusive != 1)) + wake_flags = 0; + + spin_lock_irqsave(&q->lock, flags); + __wake_up_common(q, mode, nr_exclusive, wake_flags, key); + spin_unlock_irqrestore(&q->lock, flags); +} +EXPORT_SYMBOL_GPL(__wake_up_sync_key); + +/* + * __wake_up_sync - see __wake_up_sync_key() + */ +void __wake_up_sync(wait_queue_head_t *q, unsigned int mode, int nr_exclusive) +{ + __wake_up_sync_key(q, mode, nr_exclusive, NULL); +} +EXPORT_SYMBOL_GPL(__wake_up_sync); /* For internal use only */ + +/* * Note: we use "set_current_state()" _after_ the wait-queue add, * because we need a memory barrier there on SMP, so that any * wake-function that tests for the wait-queue being active -- cgit v0.10.2 From b8a216269ec0ce2e961d32e6d640d7010b8a818e Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 4 Oct 2013 22:06:53 +0200 Subject: sched: Move completion code from core.c to completion.c Completions already have their own header file: linux/completion.h Move the implementation out of kernel/sched/core.c and into its own file: kernel/sched/completion.c. Signed-off-by: Peter Zijlstra Cc: Linus Torvalds Cc: Andrew Morton Link: http://lkml.kernel.org/n/tip-x2y49rmxu5dljt66ai2lcfuw@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/include/linux/completion.h b/include/linux/completion.h index 3cd574d..22c33e3 100644 --- a/include/linux/completion.h +++ b/include/linux/completion.h @@ -5,7 +5,7 @@ * (C) Copyright 2001 Linus Torvalds * * Atomic wait-for-completion handler data structures. - * See kernel/sched/core.c for details. + * See kernel/sched/completion.c for details. */ #include diff --git a/kernel/sched/Makefile b/kernel/sched/Makefile index f8d3f4b..7b62140 100644 --- a/kernel/sched/Makefile +++ b/kernel/sched/Makefile @@ -12,7 +12,7 @@ CFLAGS_core.o := $(PROFILING) -fno-omit-frame-pointer endif obj-y += core.o proc.o clock.o cputime.o idle_task.o fair.o rt.o stop_task.o -obj-y += wait.o +obj-y += wait.o completion.o obj-$(CONFIG_SMP) += cpupri.o obj-$(CONFIG_SCHED_AUTOGROUP) += auto_group.o obj-$(CONFIG_SCHEDSTATS) += stats.o diff --git a/kernel/sched/completion.c b/kernel/sched/completion.c new file mode 100644 index 0000000..a63f4dc --- /dev/null +++ b/kernel/sched/completion.c @@ -0,0 +1,299 @@ +/* + * Generic wait-for-completion handler; + * + * It differs from semaphores in that their default case is the opposite, + * wait_for_completion default blocks whereas semaphore default non-block. The + * interface also makes it easy to 'complete' multiple waiting threads, + * something which isn't entirely natural for semaphores. + * + * But more importantly, the primitive documents the usage. Semaphores would + * typically be used for exclusion which gives rise to priority inversion. + * Waiting for completion is a typically sync point, but not an exclusion point. + */ + +#include +#include + +/** + * complete: - signals a single thread waiting on this completion + * @x: holds the state of this particular completion + * + * This will wake up a single thread waiting on this completion. Threads will be + * awakened in the same order in which they were queued. + * + * See also complete_all(), wait_for_completion() and related routines. + * + * It may be assumed that this function implies a write memory barrier before + * changing the task state if and only if any tasks are woken up. + */ +void complete(struct completion *x) +{ + unsigned long flags; + + spin_lock_irqsave(&x->wait.lock, flags); + x->done++; + __wake_up_locked(&x->wait, TASK_NORMAL, 1); + spin_unlock_irqrestore(&x->wait.lock, flags); +} +EXPORT_SYMBOL(complete); + +/** + * complete_all: - signals all threads waiting on this completion + * @x: holds the state of this particular completion + * + * This will wake up all threads waiting on this particular completion event. + * + * It may be assumed that this function implies a write memory barrier before + * changing the task state if and only if any tasks are woken up. + */ +void complete_all(struct completion *x) +{ + unsigned long flags; + + spin_lock_irqsave(&x->wait.lock, flags); + x->done += UINT_MAX/2; + __wake_up_locked(&x->wait, TASK_NORMAL, 0); + spin_unlock_irqrestore(&x->wait.lock, flags); +} +EXPORT_SYMBOL(complete_all); + +static inline long __sched +do_wait_for_common(struct completion *x, + long (*action)(long), long timeout, int state) +{ + if (!x->done) { + DECLARE_WAITQUEUE(wait, current); + + __add_wait_queue_tail_exclusive(&x->wait, &wait); + do { + if (signal_pending_state(state, current)) { + timeout = -ERESTARTSYS; + break; + } + __set_current_state(state); + spin_unlock_irq(&x->wait.lock); + timeout = action(timeout); + spin_lock_irq(&x->wait.lock); + } while (!x->done && timeout); + __remove_wait_queue(&x->wait, &wait); + if (!x->done) + return timeout; + } + x->done--; + return timeout ?: 1; +} + +static inline long __sched +__wait_for_common(struct completion *x, + long (*action)(long), long timeout, int state) +{ + might_sleep(); + + spin_lock_irq(&x->wait.lock); + timeout = do_wait_for_common(x, action, timeout, state); + spin_unlock_irq(&x->wait.lock); + return timeout; +} + +static long __sched +wait_for_common(struct completion *x, long timeout, int state) +{ + return __wait_for_common(x, schedule_timeout, timeout, state); +} + +static long __sched +wait_for_common_io(struct completion *x, long timeout, int state) +{ + return __wait_for_common(x, io_schedule_timeout, timeout, state); +} + +/** + * wait_for_completion: - waits for completion of a task + * @x: holds the state of this particular completion + * + * This waits to be signaled for completion of a specific task. It is NOT + * interruptible and there is no timeout. + * + * See also similar routines (i.e. wait_for_completion_timeout()) with timeout + * and interrupt capability. Also see complete(). + */ +void __sched wait_for_completion(struct completion *x) +{ + wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE); +} +EXPORT_SYMBOL(wait_for_completion); + +/** + * wait_for_completion_timeout: - waits for completion of a task (w/timeout) + * @x: holds the state of this particular completion + * @timeout: timeout value in jiffies + * + * This waits for either a completion of a specific task to be signaled or for a + * specified timeout to expire. The timeout is in jiffies. It is not + * interruptible. + * + * Return: 0 if timed out, and positive (at least 1, or number of jiffies left + * till timeout) if completed. + */ +unsigned long __sched +wait_for_completion_timeout(struct completion *x, unsigned long timeout) +{ + return wait_for_common(x, timeout, TASK_UNINTERRUPTIBLE); +} +EXPORT_SYMBOL(wait_for_completion_timeout); + +/** + * wait_for_completion_io: - waits for completion of a task + * @x: holds the state of this particular completion + * + * This waits to be signaled for completion of a specific task. It is NOT + * interruptible and there is no timeout. The caller is accounted as waiting + * for IO. + */ +void __sched wait_for_completion_io(struct completion *x) +{ + wait_for_common_io(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE); +} +EXPORT_SYMBOL(wait_for_completion_io); + +/** + * wait_for_completion_io_timeout: - waits for completion of a task (w/timeout) + * @x: holds the state of this particular completion + * @timeout: timeout value in jiffies + * + * This waits for either a completion of a specific task to be signaled or for a + * specified timeout to expire. The timeout is in jiffies. It is not + * interruptible. The caller is accounted as waiting for IO. + * + * Return: 0 if timed out, and positive (at least 1, or number of jiffies left + * till timeout) if completed. + */ +unsigned long __sched +wait_for_completion_io_timeout(struct completion *x, unsigned long timeout) +{ + return wait_for_common_io(x, timeout, TASK_UNINTERRUPTIBLE); +} +EXPORT_SYMBOL(wait_for_completion_io_timeout); + +/** + * wait_for_completion_interruptible: - waits for completion of a task (w/intr) + * @x: holds the state of this particular completion + * + * This waits for completion of a specific task to be signaled. It is + * interruptible. + * + * Return: -ERESTARTSYS if interrupted, 0 if completed. + */ +int __sched wait_for_completion_interruptible(struct completion *x) +{ + long t = wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_INTERRUPTIBLE); + if (t == -ERESTARTSYS) + return t; + return 0; +} +EXPORT_SYMBOL(wait_for_completion_interruptible); + +/** + * wait_for_completion_interruptible_timeout: - waits for completion (w/(to,intr)) + * @x: holds the state of this particular completion + * @timeout: timeout value in jiffies + * + * This waits for either a completion of a specific task to be signaled or for a + * specified timeout to expire. It is interruptible. The timeout is in jiffies. + * + * Return: -ERESTARTSYS if interrupted, 0 if timed out, positive (at least 1, + * or number of jiffies left till timeout) if completed. + */ +long __sched +wait_for_completion_interruptible_timeout(struct completion *x, + unsigned long timeout) +{ + return wait_for_common(x, timeout, TASK_INTERRUPTIBLE); +} +EXPORT_SYMBOL(wait_for_completion_interruptible_timeout); + +/** + * wait_for_completion_killable: - waits for completion of a task (killable) + * @x: holds the state of this particular completion + * + * This waits to be signaled for completion of a specific task. It can be + * interrupted by a kill signal. + * + * Return: -ERESTARTSYS if interrupted, 0 if completed. + */ +int __sched wait_for_completion_killable(struct completion *x) +{ + long t = wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_KILLABLE); + if (t == -ERESTARTSYS) + return t; + return 0; +} +EXPORT_SYMBOL(wait_for_completion_killable); + +/** + * wait_for_completion_killable_timeout: - waits for completion of a task (w/(to,killable)) + * @x: holds the state of this particular completion + * @timeout: timeout value in jiffies + * + * This waits for either a completion of a specific task to be + * signaled or for a specified timeout to expire. It can be + * interrupted by a kill signal. The timeout is in jiffies. + * + * Return: -ERESTARTSYS if interrupted, 0 if timed out, positive (at least 1, + * or number of jiffies left till timeout) if completed. + */ +long __sched +wait_for_completion_killable_timeout(struct completion *x, + unsigned long timeout) +{ + return wait_for_common(x, timeout, TASK_KILLABLE); +} +EXPORT_SYMBOL(wait_for_completion_killable_timeout); + +/** + * try_wait_for_completion - try to decrement a completion without blocking + * @x: completion structure + * + * Return: 0 if a decrement cannot be done without blocking + * 1 if a decrement succeeded. + * + * If a completion is being used as a counting completion, + * attempt to decrement the counter without blocking. This + * enables us to avoid waiting if the resource the completion + * is protecting is not available. + */ +bool try_wait_for_completion(struct completion *x) +{ + unsigned long flags; + int ret = 1; + + spin_lock_irqsave(&x->wait.lock, flags); + if (!x->done) + ret = 0; + else + x->done--; + spin_unlock_irqrestore(&x->wait.lock, flags); + return ret; +} +EXPORT_SYMBOL(try_wait_for_completion); + +/** + * completion_done - Test to see if a completion has any waiters + * @x: completion structure + * + * Return: 0 if there are waiters (wait_for_completion() in progress) + * 1 if there are no waiters. + * + */ +bool completion_done(struct completion *x) +{ + unsigned long flags; + int ret = 1; + + spin_lock_irqsave(&x->wait.lock, flags); + if (!x->done) + ret = 0; + spin_unlock_irqrestore(&x->wait.lock, flags); + return ret; +} +EXPORT_SYMBOL(completion_done); diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 91b2845..aa066f3 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2688,290 +2688,6 @@ int default_wake_function(wait_queue_t *curr, unsigned mode, int wake_flags, } EXPORT_SYMBOL(default_wake_function); -/** - * complete: - signals a single thread waiting on this completion - * @x: holds the state of this particular completion - * - * This will wake up a single thread waiting on this completion. Threads will be - * awakened in the same order in which they were queued. - * - * See also complete_all(), wait_for_completion() and related routines. - * - * It may be assumed that this function implies a write memory barrier before - * changing the task state if and only if any tasks are woken up. - */ -void complete(struct completion *x) -{ - unsigned long flags; - - spin_lock_irqsave(&x->wait.lock, flags); - x->done++; - __wake_up_locked(&x->wait, TASK_NORMAL, 1); - spin_unlock_irqrestore(&x->wait.lock, flags); -} -EXPORT_SYMBOL(complete); - -/** - * complete_all: - signals all threads waiting on this completion - * @x: holds the state of this particular completion - * - * This will wake up all threads waiting on this particular completion event. - * - * It may be assumed that this function implies a write memory barrier before - * changing the task state if and only if any tasks are woken up. - */ -void complete_all(struct completion *x) -{ - unsigned long flags; - - spin_lock_irqsave(&x->wait.lock, flags); - x->done += UINT_MAX/2; - __wake_up_locked(&x->wait, TASK_NORMAL, 0); - spin_unlock_irqrestore(&x->wait.lock, flags); -} -EXPORT_SYMBOL(complete_all); - -static inline long __sched -do_wait_for_common(struct completion *x, - long (*action)(long), long timeout, int state) -{ - if (!x->done) { - DECLARE_WAITQUEUE(wait, current); - - __add_wait_queue_tail_exclusive(&x->wait, &wait); - do { - if (signal_pending_state(state, current)) { - timeout = -ERESTARTSYS; - break; - } - __set_current_state(state); - spin_unlock_irq(&x->wait.lock); - timeout = action(timeout); - spin_lock_irq(&x->wait.lock); - } while (!x->done && timeout); - __remove_wait_queue(&x->wait, &wait); - if (!x->done) - return timeout; - } - x->done--; - return timeout ?: 1; -} - -static inline long __sched -__wait_for_common(struct completion *x, - long (*action)(long), long timeout, int state) -{ - might_sleep(); - - spin_lock_irq(&x->wait.lock); - timeout = do_wait_for_common(x, action, timeout, state); - spin_unlock_irq(&x->wait.lock); - return timeout; -} - -static long __sched -wait_for_common(struct completion *x, long timeout, int state) -{ - return __wait_for_common(x, schedule_timeout, timeout, state); -} - -static long __sched -wait_for_common_io(struct completion *x, long timeout, int state) -{ - return __wait_for_common(x, io_schedule_timeout, timeout, state); -} - -/** - * wait_for_completion: - waits for completion of a task - * @x: holds the state of this particular completion - * - * This waits to be signaled for completion of a specific task. It is NOT - * interruptible and there is no timeout. - * - * See also similar routines (i.e. wait_for_completion_timeout()) with timeout - * and interrupt capability. Also see complete(). - */ -void __sched wait_for_completion(struct completion *x) -{ - wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE); -} -EXPORT_SYMBOL(wait_for_completion); - -/** - * wait_for_completion_timeout: - waits for completion of a task (w/timeout) - * @x: holds the state of this particular completion - * @timeout: timeout value in jiffies - * - * This waits for either a completion of a specific task to be signaled or for a - * specified timeout to expire. The timeout is in jiffies. It is not - * interruptible. - * - * Return: 0 if timed out, and positive (at least 1, or number of jiffies left - * till timeout) if completed. - */ -unsigned long __sched -wait_for_completion_timeout(struct completion *x, unsigned long timeout) -{ - return wait_for_common(x, timeout, TASK_UNINTERRUPTIBLE); -} -EXPORT_SYMBOL(wait_for_completion_timeout); - -/** - * wait_for_completion_io: - waits for completion of a task - * @x: holds the state of this particular completion - * - * This waits to be signaled for completion of a specific task. It is NOT - * interruptible and there is no timeout. The caller is accounted as waiting - * for IO. - */ -void __sched wait_for_completion_io(struct completion *x) -{ - wait_for_common_io(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE); -} -EXPORT_SYMBOL(wait_for_completion_io); - -/** - * wait_for_completion_io_timeout: - waits for completion of a task (w/timeout) - * @x: holds the state of this particular completion - * @timeout: timeout value in jiffies - * - * This waits for either a completion of a specific task to be signaled or for a - * specified timeout to expire. The timeout is in jiffies. It is not - * interruptible. The caller is accounted as waiting for IO. - * - * Return: 0 if timed out, and positive (at least 1, or number of jiffies left - * till timeout) if completed. - */ -unsigned long __sched -wait_for_completion_io_timeout(struct completion *x, unsigned long timeout) -{ - return wait_for_common_io(x, timeout, TASK_UNINTERRUPTIBLE); -} -EXPORT_SYMBOL(wait_for_completion_io_timeout); - -/** - * wait_for_completion_interruptible: - waits for completion of a task (w/intr) - * @x: holds the state of this particular completion - * - * This waits for completion of a specific task to be signaled. It is - * interruptible. - * - * Return: -ERESTARTSYS if interrupted, 0 if completed. - */ -int __sched wait_for_completion_interruptible(struct completion *x) -{ - long t = wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_INTERRUPTIBLE); - if (t == -ERESTARTSYS) - return t; - return 0; -} -EXPORT_SYMBOL(wait_for_completion_interruptible); - -/** - * wait_for_completion_interruptible_timeout: - waits for completion (w/(to,intr)) - * @x: holds the state of this particular completion - * @timeout: timeout value in jiffies - * - * This waits for either a completion of a specific task to be signaled or for a - * specified timeout to expire. It is interruptible. The timeout is in jiffies. - * - * Return: -ERESTARTSYS if interrupted, 0 if timed out, positive (at least 1, - * or number of jiffies left till timeout) if completed. - */ -long __sched -wait_for_completion_interruptible_timeout(struct completion *x, - unsigned long timeout) -{ - return wait_for_common(x, timeout, TASK_INTERRUPTIBLE); -} -EXPORT_SYMBOL(wait_for_completion_interruptible_timeout); - -/** - * wait_for_completion_killable: - waits for completion of a task (killable) - * @x: holds the state of this particular completion - * - * This waits to be signaled for completion of a specific task. It can be - * interrupted by a kill signal. - * - * Return: -ERESTARTSYS if interrupted, 0 if completed. - */ -int __sched wait_for_completion_killable(struct completion *x) -{ - long t = wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_KILLABLE); - if (t == -ERESTARTSYS) - return t; - return 0; -} -EXPORT_SYMBOL(wait_for_completion_killable); - -/** - * wait_for_completion_killable_timeout: - waits for completion of a task (w/(to,killable)) - * @x: holds the state of this particular completion - * @timeout: timeout value in jiffies - * - * This waits for either a completion of a specific task to be - * signaled or for a specified timeout to expire. It can be - * interrupted by a kill signal. The timeout is in jiffies. - * - * Return: -ERESTARTSYS if interrupted, 0 if timed out, positive (at least 1, - * or number of jiffies left till timeout) if completed. - */ -long __sched -wait_for_completion_killable_timeout(struct completion *x, - unsigned long timeout) -{ - return wait_for_common(x, timeout, TASK_KILLABLE); -} -EXPORT_SYMBOL(wait_for_completion_killable_timeout); - -/** - * try_wait_for_completion - try to decrement a completion without blocking - * @x: completion structure - * - * Return: 0 if a decrement cannot be done without blocking - * 1 if a decrement succeeded. - * - * If a completion is being used as a counting completion, - * attempt to decrement the counter without blocking. This - * enables us to avoid waiting if the resource the completion - * is protecting is not available. - */ -bool try_wait_for_completion(struct completion *x) -{ - unsigned long flags; - int ret = 1; - - spin_lock_irqsave(&x->wait.lock, flags); - if (!x->done) - ret = 0; - else - x->done--; - spin_unlock_irqrestore(&x->wait.lock, flags); - return ret; -} -EXPORT_SYMBOL(try_wait_for_completion); - -/** - * completion_done - Test to see if a completion has any waiters - * @x: completion structure - * - * Return: 0 if there are waiters (wait_for_completion() in progress) - * 1 if there are no waiters. - * - */ -bool completion_done(struct completion *x) -{ - unsigned long flags; - int ret = 1; - - spin_lock_irqsave(&x->wait.lock, flags); - if (!x->done) - ret = 0; - spin_unlock_irqrestore(&x->wait.lock, flags); - return ret; -} -EXPORT_SYMBOL(completion_done); - static long __sched sleep_on_common(wait_queue_head_t *q, int state, long timeout) { -- cgit v0.10.2 From 8fe7b65ab4656e5db466a7d98b1fd48ff83b2c64 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 10:31:07 +0100 Subject: ALSA: hda - Apply GPIO setup for MacBooks with CS4208 Apply the existing GPIO0 fixup as default for MacBooks with CS4208 codec. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=64401 Cc: [v3.12+] Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 38d073e..1ce1f40 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -611,6 +611,7 @@ static const struct snd_pci_quirk cs4208_fixup_tbl[] = { /* codec SSID */ SND_PCI_QUIRK(0x106b, 0x7100, "MacBookAir 6,1", CS4208_MBA6), SND_PCI_QUIRK(0x106b, 0x7200, "MacBookAir 6,2", CS4208_MBA6), + SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS4208_GPIO0), {} /* terminator */ }; -- cgit v0.10.2 From a477c8594bee3bff639739c48258a8c737ab721e Mon Sep 17 00:00:00 2001 From: HATAYAMA Daisuke Date: Tue, 5 Nov 2013 02:15:48 +0900 Subject: x86/cpu: Always print SMP information in /proc/cpuinfo Currently show_cpuinfo_core() displays cpu core information only if the number of threads per a whole cores is 2 or larger. However, this condition doesn't care about the number of sockets. For example, this condition doesn't hold on systems with two logical cpus consisting of two sockets and a single core on each socket - yet the topology information would be interesting to see in that case as well. I don't know whether or not there are processors in real world by which such configurations are possible, but at least on vitual machine environments, such configuration can occur, typically when no explicit SMP information is provided in advance. For example, on qemu/KVM, SMP information is specified via -smp command-line option, more specifically, its syntax is: -smp n[,cores=cores][,threads=threads][,sockets=sockets][,maxcpus=maxcpus] If this is not specified, qemu tells configuration with n-sockets, 1-core and 1-thread to the guest machine, on which guest, MP information is not displayed in /proc/cpuinfo. I saw this situation on VMWare guest environment, too. To fix this issue, this patch simply removes the condition because this information is useful even if there's only 1 thread. Signed-off-by: HATAYAMA Daisuke Cc: Vivek Goyal Cc: H. Peter Anvin Cc: Linus Torvalds Cc: Andrew Morton Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/5277D644.4090707@jp.fujitsu.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/proc.c b/arch/x86/kernel/cpu/proc.c index aee6317..06fe3ed 100644 --- a/arch/x86/kernel/cpu/proc.c +++ b/arch/x86/kernel/cpu/proc.c @@ -11,15 +11,12 @@ static void show_cpuinfo_core(struct seq_file *m, struct cpuinfo_x86 *c, unsigned int cpu) { #ifdef CONFIG_SMP - if (c->x86_max_cores * smp_num_siblings > 1) { - seq_printf(m, "physical id\t: %d\n", c->phys_proc_id); - seq_printf(m, "siblings\t: %d\n", - cpumask_weight(cpu_core_mask(cpu))); - seq_printf(m, "core id\t\t: %d\n", c->cpu_core_id); - seq_printf(m, "cpu cores\t: %d\n", c->booted_cores); - seq_printf(m, "apicid\t\t: %d\n", c->apicid); - seq_printf(m, "initial apicid\t: %d\n", c->initial_apicid); - } + seq_printf(m, "physical id\t: %d\n", c->phys_proc_id); + seq_printf(m, "siblings\t: %d\n", cpumask_weight(cpu_core_mask(cpu))); + seq_printf(m, "core id\t\t: %d\n", c->cpu_core_id); + seq_printf(m, "cpu cores\t: %d\n", c->booted_cores); + seq_printf(m, "apicid\t\t: %d\n", c->apicid); + seq_printf(m, "initial apicid\t: %d\n", c->initial_apicid); #endif } -- cgit v0.10.2 From 791b3f596e2331f80244cc8532a25b48d45f126d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 6 Nov 2013 08:09:35 +0100 Subject: ALSA: intel8x0: Fix chmap application The playback chmap for multi-channel stream hasn't been properly added to intel8x0 devices due to the wrong condition. Reported-by: Raymond Yau Signed-off-by: Takashi Iwai diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 59c8aae..08d8733 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -1541,17 +1541,16 @@ static int snd_intel8x0_pcm1(struct intel8x0 *chip, int device, snd_dma_pci_data(chip->pci), rec->prealloc_size, rec->prealloc_max_size); - if (rec->ac97_idx == ICHD_PCMOUT && rec->playback_ops) { + if (rec->playback_ops && + rec->playback_ops->open == snd_intel8x0_playback_open) { struct snd_pcm_chmap *chmap; int chs = 2; - if (rec->ac97_idx == ICHD_PCMOUT) { - if (chip->multi8) - chs = 8; - else if (chip->multi6) - chs = 6; - else if (chip->multi4) - chs = 4; - } + if (chip->multi8) + chs = 8; + else if (chip->multi6) + chs = 6; + else if (chip->multi4) + chs = 4; err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, snd_pcm_alt_chmaps, chs, 0, &chmap); -- cgit v0.10.2 From bb61ccc7dbcbc1ed4600bef5610d99ec91fd7032 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Tue, 5 Nov 2013 09:37:29 -0500 Subject: x86/cpu: Allow higher NR_CPUS values The current range for SMP configs is 2 - 512 CPUs, or a full 4096 in the case of MAXSMP. There are machines that have 1024 CPUs in them today and configuring a kernel for that means you are forced to set MAXSMP. This adds additional unnecessary overhead. While that overhead might be considered tiny for large machines, it isn't necessarily so if you are building a kernel that runs across a wide variety of machines. To cover the range of more common machines today, we allow NR_CPUS to be up to 4096 when CPUMASK_OFFSTACK is enabled. Signed-off-by: Josh Boyer Cc: prarit@redhat.com Cc: Russ Anderson Cc: Linus Torvalds Cc: Andrew Morton Link: http://lkml.kernel.org/r/20131105143728.GJ9944@hansolo.jdub.homelinux.org Signed-off-by: Ingo Molnar diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index f67e839..f03e428 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -825,14 +825,16 @@ config MAXSMP config NR_CPUS int "Maximum number of CPUs" if SMP && !MAXSMP range 2 8 if SMP && X86_32 && !X86_BIGSMP - range 2 512 if SMP && !MAXSMP + range 2 512 if SMP && !MAXSMP && !CPUMASK_OFFSTACK + range 2 4096 if SMP && !MAXSMP && CPUMASK_OFFSTACK && X86_64 default "1" if !SMP default "4096" if MAXSMP default "32" if SMP && (X86_NUMAQ || X86_SUMMIT || X86_BIGSMP || X86_ES7000) default "8" if SMP ---help--- This allows you to specify the maximum number of CPUs which this - kernel will support. The maximum supported value is 512 and the + kernel will support. If CPUMASK_OFFSTACK is enabled, the maximum + supported value is 4096, otherwise the maximum value is 512. The minimum value which makes sense is 2. This is purely to save memory - each supported CPU adds -- cgit v0.10.2 From b53b5eda8194214928c8243d711a75dbf51809fc Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Tue, 5 Nov 2013 09:38:16 -0500 Subject: x86/cpu: Increase max CPU count to 8192 The MAXSMP option is intended to enable silly large numbers of CPUs for testing purposes. The current value of 4096 isn't very silly any longer as there are actual SGI machines that approach 6096 CPUs when taking HT into account. Increase the value to a nice round 8192 to account for this and allow for short term future increases. Signed-off-by: Josh Boyer Cc: prarit@redhat.com Cc: Russ Anderson Cc: Linus Torvalds Cc: Andrew Morton Link: http://lkml.kernel.org/r/20131105143816.GK9944@hansolo.jdub.homelinux.org [ Tweaked it so that MAXSMP simply sets the maximum of the normal range. ] Signed-off-by: Ingo Molnar diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index f03e428..805469a 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -826,9 +826,9 @@ config NR_CPUS int "Maximum number of CPUs" if SMP && !MAXSMP range 2 8 if SMP && X86_32 && !X86_BIGSMP range 2 512 if SMP && !MAXSMP && !CPUMASK_OFFSTACK - range 2 4096 if SMP && !MAXSMP && CPUMASK_OFFSTACK && X86_64 + range 2 8192 if SMP && !MAXSMP && CPUMASK_OFFSTACK && X86_64 default "1" if !SMP - default "4096" if MAXSMP + default "8192" if MAXSMP default "32" if SMP && (X86_NUMAQ || X86_SUMMIT || X86_BIGSMP || X86_ES7000) default "8" if SMP ---help--- -- cgit v0.10.2 From 29fa9578046ea51e9cf97c4e16207ea6ec375084 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 15:00:02 +0100 Subject: ALSA: ctxfi: Use WARN_ON() instead of BUG_ON() BUG_ON() is rather useless for debugging as it leads to panic(). Use WARN_ON() and handle the error cases accordingly. Signed-off-by: Takashi Iwai diff --git a/sound/pci/ctxfi/cthardware.c b/sound/pci/ctxfi/cthardware.c index 110b8ac..a689f25 100644 --- a/sound/pci/ctxfi/cthardware.c +++ b/sound/pci/ctxfi/cthardware.c @@ -69,7 +69,8 @@ unsigned int get_field(unsigned int data, unsigned int field) { int i; - BUG_ON(!field); + if (WARN_ON(!field)) + return 0; /* @field should always be greater than 0 */ for (i = 0; !(field & (1 << i)); ) i++; @@ -81,7 +82,8 @@ void set_field(unsigned int *data, unsigned int field, unsigned int value) { int i; - BUG_ON(!field); + if (WARN_ON(!field)) + return; /* @field should always be greater than 0 */ for (i = 0; !(field & (1 << i)); ) i++; -- cgit v0.10.2 From 025be74c1db44158dd3cfe43b89ba16266d01062 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 15:01:47 +0100 Subject: ALSA: ps3: Use WARN_ON() instead of BUG_ON() BUG_ON() is rather useless for debugging as it leads to panic(). Use WARN_ON() and handle the error cases accordingly. Signed-off-by: Takashi Iwai diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c index 8c7dcbe..ebb76f2 100644 --- a/sound/ppc/snd_ps3.c +++ b/sound/ppc/snd_ps3.c @@ -933,8 +933,10 @@ static int snd_ps3_driver_probe(struct ps3_system_bus_device *dev) int i, ret; u64 lpar_addr, lpar_size; - BUG_ON(!firmware_has_feature(FW_FEATURE_PS3_LV1)); - BUG_ON(dev->match_id != PS3_MATCH_ID_SOUND); + if (WARN_ON(!firmware_has_feature(FW_FEATURE_PS3_LV1))) + return -ENODEV; + if (WARN_ON(dev->match_id != PS3_MATCH_ID_SOUND)) + return -ENODEV; the_card.ps3_dev = dev; -- cgit v0.10.2 From 5a19b178d7b4dd162b3d4c7e729f30348f2eb177 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 15:02:42 +0100 Subject: ALSA: sparc/cs4231: Use WARN_ON() instead of BUG_ON() BUG_ON() is rather useless for debugging as it leads to panic(). Use WARN_ON() and handle the error cases accordingly. Signed-off-by: Takashi Iwai diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c index 54aaad2..b47f6fe 100644 --- a/sound/sparc/cs4231.c +++ b/sound/sparc/cs4231.c @@ -429,7 +429,8 @@ static void snd_cs4231_advance_dma(struct cs4231_dma_control *dma_cont, unsigned int period_size = snd_pcm_lib_period_bytes(substream); unsigned int offset = period_size * (*periods_sent); - BUG_ON(period_size >= (1 << 24)); + if (WARN_ON(period_size >= (1 << 24))) + return; if (dma_cont->request(dma_cont, runtime->dma_addr + offset, period_size)) @@ -912,7 +913,8 @@ static int snd_cs4231_playback_prepare(struct snd_pcm_substream *substream) chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO); - BUG_ON(runtime->period_size > 0xffff + 1); + if (WARN_ON(runtime->period_size > 0xffff + 1)) + return -EINVAL; chip->p_periods_sent = 0; spin_unlock_irqrestore(&chip->lock, flags); -- cgit v0.10.2 From c3cd1badc86c62f64f7e1d7034b078cb7e9b254b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 15:03:05 +0100 Subject: ALSA: mips/ad1843: Use WARN_ON() instead of BUG_ON() BUG_ON() is rather useless for debugging as it leads to panic(). Use WARN_ON() and handle the error cases accordingly. Signed-off-by: Takashi Iwai diff --git a/sound/mips/ad1843.c b/sound/mips/ad1843.c index c624510..5869075 100644 --- a/sound/mips/ad1843.c +++ b/sound/mips/ad1843.c @@ -276,7 +276,7 @@ static void ad1843_write_multi(struct snd_ad1843 *ad1843, int argcount, ...) if (reg == -1) reg = fp->reg; else - BUG_ON(reg != fp->reg); + WARN_ON(reg != fp->reg); m = ((1 << fp->nbits) - 1) << fp->lo_bit; mask |= m; bits |= (value << fp->lo_bit) & m; -- cgit v0.10.2 From 88ec7ae82dff8ca815ff8a31622a2811240b5d0c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 15:33:40 +0100 Subject: ALSA: pxa2xx: Replace BUG() with snd_BUG() BUG() used in the driver is just to spit the stack trace on buggy points, not really needed to stop the whole operation. For that purpose, it'd be more convenient to use snd_BUG() instead. Signed-off-by: Takashi Iwai diff --git a/sound/arm/pxa2xx-ac97-lib.c b/sound/arm/pxa2xx-ac97-lib.c index 99a4668..66de90e 100644 --- a/sound/arm/pxa2xx-ac97-lib.c +++ b/sound/arm/pxa2xx-ac97-lib.c @@ -208,7 +208,7 @@ bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97) pxa_ac97_warm_pxa3xx(); else #endif - BUG(); + snd_BUG(); while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--) mdelay(1); @@ -245,7 +245,7 @@ bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97) pxa_ac97_cold_pxa3xx(); else #endif - BUG(); + snd_BUG(); while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--) mdelay(1); -- cgit v0.10.2 From edf6844ebf4d66546caf2a75e9d05e579990678c Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Mon, 4 Nov 2013 09:30:18 +0100 Subject: microblaze: Remove unused NO_MMU Kconfig parameter This removes the NO_MMU Kconfig parameter, which was no longer used anywhere in the source code and Makefiles. This also updates a comment refering to this parameter. Signed-off-by: Michael Opdenacker Signed-off-by: Michal Simek diff --git a/arch/microblaze/Kconfig b/arch/microblaze/Kconfig index b82f82b..d8ec74b 100644 --- a/arch/microblaze/Kconfig +++ b/arch/microblaze/Kconfig @@ -82,11 +82,6 @@ config MMU bool "MMU support" default n -config NO_MMU - bool - depends on !MMU - default y - comment "Boot options" config CMDLINE_BOOL diff --git a/arch/microblaze/kernel/hw_exception_handler.S b/arch/microblaze/kernel/hw_exception_handler.S index 61b3a1f..fc6b89f 100644 --- a/arch/microblaze/kernel/hw_exception_handler.S +++ b/arch/microblaze/kernel/hw_exception_handler.S @@ -193,8 +193,8 @@ * - W S REG EXC * * - * STACK FRAME STRUCTURE (for NO_MMU) - * --------------------------------- + * STACK FRAME STRUCTURE (for CONFIG_MMU=n) + * ---------------------------------------- * * +-------------+ + 0 * | MSR | -- cgit v0.10.2 From 9da8312048edcf246ac1d7ab6aa0293f252de559 Mon Sep 17 00:00:00 2001 From: Greg Ungerer Date: Tue, 5 Nov 2013 23:11:51 +1000 Subject: pinctrl: imx50: add pinctrl support code for the IMX50 SoC Add code to support the specific pin arrangements of the Freescale IMX50 SoC. Signed-off-by: Greg Ungerer Acked-by: Shawn Guo Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index db1ddca..33f9dc1 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -130,6 +130,14 @@ config PINCTRL_IMX35 help Say Y here to enable the imx35 pinctrl driver +config PINCTRL_IMX50 + bool "IMX50 pinctrl driver" + depends on OF + depends on SOC_IMX50 + select PINCTRL_IMX + help + Say Y here to enable the imx50 pinctrl driver + config PINCTRL_IMX51 bool "IMX51 pinctrl driver" depends on OF diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 915d19c..4f7be29 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_PINCTRL_IMX) += pinctrl-imx.o obj-$(CONFIG_PINCTRL_IMX1_CORE) += pinctrl-imx1-core.o obj-$(CONFIG_PINCTRL_IMX27) += pinctrl-imx27.o obj-$(CONFIG_PINCTRL_IMX35) += pinctrl-imx35.o +obj-$(CONFIG_PINCTRL_IMX50) += pinctrl-imx50.o obj-$(CONFIG_PINCTRL_IMX51) += pinctrl-imx51.o obj-$(CONFIG_PINCTRL_IMX53) += pinctrl-imx53.o obj-$(CONFIG_PINCTRL_IMX6Q) += pinctrl-imx6q.o diff --git a/drivers/pinctrl/pinctrl-imx50.c b/drivers/pinctrl/pinctrl-imx50.c new file mode 100644 index 0000000..b06feed --- /dev/null +++ b/drivers/pinctrl/pinctrl-imx50.c @@ -0,0 +1,426 @@ +/* + * imx50 pinctrl driver based on imx pinmux core + * + * Copyright (C) 2013 Greg Ungerer + * Copyright (C) 2012 Freescale Semiconductor, Inc. + * Copyright (C) 2012 Linaro, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "pinctrl-imx.h" + +enum imx50_pads { + MX50_PAD_RESERVE0 = 0, + MX50_PAD_RESERVE1 = 1, + MX50_PAD_RESERVE2 = 2, + MX50_PAD_RESERVE3 = 3, + MX50_PAD_RESERVE4 = 4, + MX50_PAD_RESERVE5 = 5, + MX50_PAD_RESERVE6 = 6, + MX50_PAD_RESERVE7 = 7, + MX50_PAD_KEY_COL0 = 8, + MX50_PAD_KEY_ROW0 = 9, + MX50_PAD_KEY_COL1 = 10, + MX50_PAD_KEY_ROW1 = 11, + MX50_PAD_KEY_COL2 = 12, + MX50_PAD_KEY_ROW2 = 13, + MX50_PAD_KEY_COL3 = 14, + MX50_PAD_KEY_ROW3 = 15, + MX50_PAD_I2C1_SCL = 16, + MX50_PAD_I2C1_SDA = 17, + MX50_PAD_I2C2_SCL = 18, + MX50_PAD_I2C2_SDA = 19, + MX50_PAD_I2C3_SCL = 20, + MX50_PAD_I2C3_SDA = 21, + MX50_PAD_PWM1 = 22, + MX50_PAD_PWM2 = 23, + MX50_PAD_0WIRE = 24, + MX50_PAD_EPITO = 25, + MX50_PAD_WDOG = 26, + MX50_PAD_SSI_TXFS = 27, + MX50_PAD_SSI_TXC = 28, + MX50_PAD_SSI_TXD = 29, + MX50_PAD_SSI_RXD = 30, + MX50_PAD_SSI_RXF = 31, + MX50_PAD_SSI_RXC = 32, + MX50_PAD_UART1_TXD = 33, + MX50_PAD_UART1_RXD = 34, + MX50_PAD_UART1_CTS = 35, + MX50_PAD_UART1_RTS = 36, + MX50_PAD_UART2_TXD = 37, + MX50_PAD_UART2_RXD = 38, + MX50_PAD_UART2_CTS = 39, + MX50_PAD_UART2_RTS = 40, + MX50_PAD_UART3_TXD = 41, + MX50_PAD_UART3_RXD = 42, + MX50_PAD_UART4_TXD = 43, + MX50_PAD_UART4_RXD = 44, + MX50_PAD_CSPI_CLK = 45, + MX50_PAD_CSPI_MOSI = 46, + MX50_PAD_CSPI_MISO = 47, + MX50_PAD_CSPI_SS0 = 48, + MX50_PAD_ECSPI1_CLK = 49, + MX50_PAD_ECSPI1_MOSI = 50, + MX50_PAD_ECSPI1_MISO = 51, + MX50_PAD_ECSPI1_SS0 = 52, + MX50_PAD_ECSPI2_CLK = 53, + MX50_PAD_ECSPI2_MOSI = 54, + MX50_PAD_ECSPI2_MISO = 55, + MX50_PAD_ECSPI2_SS0 = 56, + MX50_PAD_SD1_CLK = 57, + MX50_PAD_SD1_CMD = 58, + MX50_PAD_SD1_D0 = 59, + MX50_PAD_SD1_D1 = 60, + MX50_PAD_SD1_D2 = 61, + MX50_PAD_SD1_D3 = 62, + MX50_PAD_SD2_CLK = 63, + MX50_PAD_SD2_CMD = 64, + MX50_PAD_SD2_D0 = 65, + MX50_PAD_SD2_D1 = 66, + MX50_PAD_SD2_D2 = 67, + MX50_PAD_SD2_D3 = 68, + MX50_PAD_SD2_D4 = 69, + MX50_PAD_SD2_D5 = 70, + MX50_PAD_SD2_D6 = 71, + MX50_PAD_SD2_D7 = 72, + MX50_PAD_SD2_WP = 73, + MX50_PAD_SD2_CD = 74, + MX50_PAD_DISP_D0 = 75, + MX50_PAD_DISP_D1 = 76, + MX50_PAD_DISP_D2 = 77, + MX50_PAD_DISP_D3 = 78, + MX50_PAD_DISP_D4 = 79, + MX50_PAD_DISP_D5 = 80, + MX50_PAD_DISP_D6 = 81, + MX50_PAD_DISP_D7 = 82, + MX50_PAD_DISP_WR = 83, + MX50_PAD_DISP_RD = 84, + MX50_PAD_DISP_RS = 85, + MX50_PAD_DISP_CS = 86, + MX50_PAD_DISP_BUSY = 87, + MX50_PAD_DISP_RESET = 88, + MX50_PAD_SD3_CLK = 89, + MX50_PAD_SD3_CMD = 90, + MX50_PAD_SD3_D0 = 91, + MX50_PAD_SD3_D1 = 92, + MX50_PAD_SD3_D2 = 93, + MX50_PAD_SD3_D3 = 94, + MX50_PAD_SD3_D4 = 95, + MX50_PAD_SD3_D5 = 96, + MX50_PAD_SD3_D6 = 97, + MX50_PAD_SD3_D7 = 98, + MX50_PAD_SD3_WP = 99, + MX50_PAD_DISP_D8 = 100, + MX50_PAD_DISP_D9 = 101, + MX50_PAD_DISP_D10 = 102, + MX50_PAD_DISP_D11 = 103, + MX50_PAD_DISP_D12 = 104, + MX50_PAD_DISP_D13 = 105, + MX50_PAD_DISP_D14 = 106, + MX50_PAD_DISP_D15 = 107, + MX50_PAD_EPDC_D0 = 108, + MX50_PAD_EPDC_D1 = 109, + MX50_PAD_EPDC_D2 = 110, + MX50_PAD_EPDC_D3 = 111, + MX50_PAD_EPDC_D4 = 112, + MX50_PAD_EPDC_D5 = 113, + MX50_PAD_EPDC_D6 = 114, + MX50_PAD_EPDC_D7 = 115, + MX50_PAD_EPDC_D8 = 116, + MX50_PAD_EPDC_D9 = 117, + MX50_PAD_EPDC_D10 = 118, + MX50_PAD_EPDC_D11 = 119, + MX50_PAD_EPDC_D12 = 120, + MX50_PAD_EPDC_D13 = 121, + MX50_PAD_EPDC_D14 = 122, + MX50_PAD_EPDC_D15 = 123, + MX50_PAD_EPDC_GDCLK = 124, + MX50_PAD_EPDC_GDSP = 125, + MX50_PAD_EPDC_GDOE = 126, + MX50_PAD_EPDC_GDRL = 127, + MX50_PAD_EPDC_SDCLK = 128, + MX50_PAD_EPDC_SDOEZ = 129, + MX50_PAD_EPDC_SDOED = 130, + MX50_PAD_EPDC_SDOE = 131, + MX50_PAD_EPDC_SDLE = 132, + MX50_PAD_EPDC_SDCLKN = 133, + MX50_PAD_EPDC_SDSHR = 134, + MX50_PAD_EPDC_PWRCOM = 135, + MX50_PAD_EPDC_PWRSTAT = 136, + MX50_PAD_EPDC_PWRCTRL0 = 137, + MX50_PAD_EPDC_PWRCTRL1 = 138, + MX50_PAD_EPDC_PWRCTRL2 = 139, + MX50_PAD_EPDC_PWRCTRL3 = 140, + MX50_PAD_EPDC_VCOM0 = 141, + MX50_PAD_EPDC_VCOM1 = 142, + MX50_PAD_EPDC_BDR0 = 143, + MX50_PAD_EPDC_BDR1 = 144, + MX50_PAD_EPDC_SDCE0 = 145, + MX50_PAD_EPDC_SDCE1 = 146, + MX50_PAD_EPDC_SDCE2 = 147, + MX50_PAD_EPDC_SDCE3 = 148, + MX50_PAD_EPDC_SDCE4 = 149, + MX50_PAD_EPDC_SDCE5 = 150, + MX50_PAD_EIM_DA0 = 151, + MX50_PAD_EIM_DA1 = 152, + MX50_PAD_EIM_DA2 = 153, + MX50_PAD_EIM_DA3 = 154, + MX50_PAD_EIM_DA4 = 155, + MX50_PAD_EIM_DA5 = 156, + MX50_PAD_EIM_DA6 = 157, + MX50_PAD_EIM_DA7 = 158, + MX50_PAD_EIM_DA8 = 159, + MX50_PAD_EIM_DA9 = 160, + MX50_PAD_EIM_DA10 = 161, + MX50_PAD_EIM_DA11 = 162, + MX50_PAD_EIM_DA12 = 163, + MX50_PAD_EIM_DA13 = 164, + MX50_PAD_EIM_DA14 = 165, + MX50_PAD_EIM_DA15 = 166, + MX50_PAD_EIM_CS2 = 167, + MX50_PAD_EIM_CS1 = 168, + MX50_PAD_EIM_CS0 = 169, + MX50_PAD_EIM_EB0 = 170, + MX50_PAD_EIM_EB1 = 171, + MX50_PAD_EIM_WAIT = 172, + MX50_PAD_EIM_BCLK = 173, + MX50_PAD_EIM_RDY = 174, + MX50_PAD_EIM_OE = 175, + MX50_PAD_EIM_RW = 176, + MX50_PAD_EIM_LBA = 177, + MX50_PAD_EIM_CRE = 178, +}; + +/* Pad names for the pinmux subsystem */ +static const struct pinctrl_pin_desc imx50_pinctrl_pads[] = { + IMX_PINCTRL_PIN(MX50_PAD_RESERVE0), + IMX_PINCTRL_PIN(MX50_PAD_RESERVE1), + IMX_PINCTRL_PIN(MX50_PAD_RESERVE2), + IMX_PINCTRL_PIN(MX50_PAD_RESERVE3), + IMX_PINCTRL_PIN(MX50_PAD_RESERVE4), + IMX_PINCTRL_PIN(MX50_PAD_RESERVE5), + IMX_PINCTRL_PIN(MX50_PAD_RESERVE6), + IMX_PINCTRL_PIN(MX50_PAD_RESERVE7), + IMX_PINCTRL_PIN(MX50_PAD_KEY_COL0), + IMX_PINCTRL_PIN(MX50_PAD_KEY_ROW0), + IMX_PINCTRL_PIN(MX50_PAD_KEY_COL1), + IMX_PINCTRL_PIN(MX50_PAD_KEY_ROW1), + IMX_PINCTRL_PIN(MX50_PAD_KEY_COL2), + IMX_PINCTRL_PIN(MX50_PAD_KEY_ROW2), + IMX_PINCTRL_PIN(MX50_PAD_KEY_COL3), + IMX_PINCTRL_PIN(MX50_PAD_KEY_ROW3), + IMX_PINCTRL_PIN(MX50_PAD_I2C1_SCL), + IMX_PINCTRL_PIN(MX50_PAD_I2C1_SDA), + IMX_PINCTRL_PIN(MX50_PAD_I2C2_SCL), + IMX_PINCTRL_PIN(MX50_PAD_I2C2_SDA), + IMX_PINCTRL_PIN(MX50_PAD_I2C3_SCL), + IMX_PINCTRL_PIN(MX50_PAD_I2C3_SDA), + IMX_PINCTRL_PIN(MX50_PAD_PWM1), + IMX_PINCTRL_PIN(MX50_PAD_PWM2), + IMX_PINCTRL_PIN(MX50_PAD_0WIRE), + IMX_PINCTRL_PIN(MX50_PAD_EPITO), + IMX_PINCTRL_PIN(MX50_PAD_WDOG), + IMX_PINCTRL_PIN(MX50_PAD_SSI_TXFS), + IMX_PINCTRL_PIN(MX50_PAD_SSI_TXC), + IMX_PINCTRL_PIN(MX50_PAD_SSI_TXD), + IMX_PINCTRL_PIN(MX50_PAD_SSI_RXD), + IMX_PINCTRL_PIN(MX50_PAD_SSI_RXF), + IMX_PINCTRL_PIN(MX50_PAD_SSI_RXC), + IMX_PINCTRL_PIN(MX50_PAD_UART1_TXD), + IMX_PINCTRL_PIN(MX50_PAD_UART1_RXD), + IMX_PINCTRL_PIN(MX50_PAD_UART1_CTS), + IMX_PINCTRL_PIN(MX50_PAD_UART1_RTS), + IMX_PINCTRL_PIN(MX50_PAD_UART2_TXD), + IMX_PINCTRL_PIN(MX50_PAD_UART2_RXD), + IMX_PINCTRL_PIN(MX50_PAD_UART2_CTS), + IMX_PINCTRL_PIN(MX50_PAD_UART2_RTS), + IMX_PINCTRL_PIN(MX50_PAD_UART3_TXD), + IMX_PINCTRL_PIN(MX50_PAD_UART3_RXD), + IMX_PINCTRL_PIN(MX50_PAD_UART4_TXD), + IMX_PINCTRL_PIN(MX50_PAD_UART4_RXD), + IMX_PINCTRL_PIN(MX50_PAD_CSPI_CLK), + IMX_PINCTRL_PIN(MX50_PAD_CSPI_MOSI), + IMX_PINCTRL_PIN(MX50_PAD_CSPI_MISO), + IMX_PINCTRL_PIN(MX50_PAD_CSPI_SS0), + IMX_PINCTRL_PIN(MX50_PAD_ECSPI1_CLK), + IMX_PINCTRL_PIN(MX50_PAD_ECSPI1_MOSI), + IMX_PINCTRL_PIN(MX50_PAD_ECSPI1_MISO), + IMX_PINCTRL_PIN(MX50_PAD_ECSPI1_SS0), + IMX_PINCTRL_PIN(MX50_PAD_ECSPI2_CLK), + IMX_PINCTRL_PIN(MX50_PAD_ECSPI2_MOSI), + IMX_PINCTRL_PIN(MX50_PAD_ECSPI2_MISO), + IMX_PINCTRL_PIN(MX50_PAD_ECSPI2_SS0), + IMX_PINCTRL_PIN(MX50_PAD_SD1_CLK), + IMX_PINCTRL_PIN(MX50_PAD_SD1_CMD), + IMX_PINCTRL_PIN(MX50_PAD_SD1_D0), + IMX_PINCTRL_PIN(MX50_PAD_SD1_D1), + IMX_PINCTRL_PIN(MX50_PAD_SD1_D2), + IMX_PINCTRL_PIN(MX50_PAD_SD1_D3), + IMX_PINCTRL_PIN(MX50_PAD_SD2_CLK), + IMX_PINCTRL_PIN(MX50_PAD_SD2_CMD), + IMX_PINCTRL_PIN(MX50_PAD_SD2_D0), + IMX_PINCTRL_PIN(MX50_PAD_SD2_D1), + IMX_PINCTRL_PIN(MX50_PAD_SD2_D2), + IMX_PINCTRL_PIN(MX50_PAD_SD2_D3), + IMX_PINCTRL_PIN(MX50_PAD_SD2_D4), + IMX_PINCTRL_PIN(MX50_PAD_SD2_D5), + IMX_PINCTRL_PIN(MX50_PAD_SD2_D6), + IMX_PINCTRL_PIN(MX50_PAD_SD2_D7), + IMX_PINCTRL_PIN(MX50_PAD_SD2_WP), + IMX_PINCTRL_PIN(MX50_PAD_SD2_CD), + IMX_PINCTRL_PIN(MX50_PAD_DISP_D0), + IMX_PINCTRL_PIN(MX50_PAD_DISP_D1), + IMX_PINCTRL_PIN(MX50_PAD_DISP_D2), + IMX_PINCTRL_PIN(MX50_PAD_DISP_D3), + IMX_PINCTRL_PIN(MX50_PAD_DISP_D4), + IMX_PINCTRL_PIN(MX50_PAD_DISP_D5), + IMX_PINCTRL_PIN(MX50_PAD_DISP_D6), + IMX_PINCTRL_PIN(MX50_PAD_DISP_D7), + IMX_PINCTRL_PIN(MX50_PAD_DISP_WR), + IMX_PINCTRL_PIN(MX50_PAD_DISP_RD), + IMX_PINCTRL_PIN(MX50_PAD_DISP_RS), + IMX_PINCTRL_PIN(MX50_PAD_DISP_CS), + IMX_PINCTRL_PIN(MX50_PAD_DISP_BUSY), + IMX_PINCTRL_PIN(MX50_PAD_DISP_RESET), + IMX_PINCTRL_PIN(MX50_PAD_SD3_CLK), + IMX_PINCTRL_PIN(MX50_PAD_SD3_CMD), + IMX_PINCTRL_PIN(MX50_PAD_SD3_D0), + IMX_PINCTRL_PIN(MX50_PAD_SD3_D1), + IMX_PINCTRL_PIN(MX50_PAD_SD3_D2), + IMX_PINCTRL_PIN(MX50_PAD_SD3_D3), + IMX_PINCTRL_PIN(MX50_PAD_SD3_D4), + IMX_PINCTRL_PIN(MX50_PAD_SD3_D5), + IMX_PINCTRL_PIN(MX50_PAD_SD3_D6), + IMX_PINCTRL_PIN(MX50_PAD_SD3_D7), + IMX_PINCTRL_PIN(MX50_PAD_SD3_WP), + IMX_PINCTRL_PIN(MX50_PAD_DISP_D8), + IMX_PINCTRL_PIN(MX50_PAD_DISP_D9), + IMX_PINCTRL_PIN(MX50_PAD_DISP_D10), + IMX_PINCTRL_PIN(MX50_PAD_DISP_D11), + IMX_PINCTRL_PIN(MX50_PAD_DISP_D12), + IMX_PINCTRL_PIN(MX50_PAD_DISP_D13), + IMX_PINCTRL_PIN(MX50_PAD_DISP_D14), + IMX_PINCTRL_PIN(MX50_PAD_DISP_D15), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_D0), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_D1), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_D2), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_D3), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_D4), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_D5), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_D6), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_D7), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_D8), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_D9), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_D10), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_D11), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_D12), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_D13), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_D14), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_D15), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_GDCLK), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_GDSP), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_GDOE), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_GDRL), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_SDCLK), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_SDOEZ), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_SDOED), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_SDOE), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_SDLE), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_SDCLKN), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_SDSHR), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_PWRCOM), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_PWRSTAT), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_PWRCTRL0), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_PWRCTRL1), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_PWRCTRL2), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_PWRCTRL3), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_VCOM0), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_VCOM1), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_BDR0), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_BDR1), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_SDCE0), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_SDCE1), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_SDCE2), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_SDCE3), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_SDCE4), + IMX_PINCTRL_PIN(MX50_PAD_EPDC_SDCE5), + IMX_PINCTRL_PIN(MX50_PAD_EIM_DA0), + IMX_PINCTRL_PIN(MX50_PAD_EIM_DA1), + IMX_PINCTRL_PIN(MX50_PAD_EIM_DA2), + IMX_PINCTRL_PIN(MX50_PAD_EIM_DA3), + IMX_PINCTRL_PIN(MX50_PAD_EIM_DA4), + IMX_PINCTRL_PIN(MX50_PAD_EIM_DA5), + IMX_PINCTRL_PIN(MX50_PAD_EIM_DA6), + IMX_PINCTRL_PIN(MX50_PAD_EIM_DA7), + IMX_PINCTRL_PIN(MX50_PAD_EIM_DA8), + IMX_PINCTRL_PIN(MX50_PAD_EIM_DA9), + IMX_PINCTRL_PIN(MX50_PAD_EIM_DA10), + IMX_PINCTRL_PIN(MX50_PAD_EIM_DA11), + IMX_PINCTRL_PIN(MX50_PAD_EIM_DA12), + IMX_PINCTRL_PIN(MX50_PAD_EIM_DA13), + IMX_PINCTRL_PIN(MX50_PAD_EIM_DA14), + IMX_PINCTRL_PIN(MX50_PAD_EIM_DA15), + IMX_PINCTRL_PIN(MX50_PAD_EIM_CS2), + IMX_PINCTRL_PIN(MX50_PAD_EIM_CS1), + IMX_PINCTRL_PIN(MX50_PAD_EIM_CS0), + IMX_PINCTRL_PIN(MX50_PAD_EIM_EB0), + IMX_PINCTRL_PIN(MX50_PAD_EIM_EB1), + IMX_PINCTRL_PIN(MX50_PAD_EIM_WAIT), + IMX_PINCTRL_PIN(MX50_PAD_EIM_BCLK), + IMX_PINCTRL_PIN(MX50_PAD_EIM_RDY), + IMX_PINCTRL_PIN(MX50_PAD_EIM_OE), + IMX_PINCTRL_PIN(MX50_PAD_EIM_RW), + IMX_PINCTRL_PIN(MX50_PAD_EIM_LBA), + IMX_PINCTRL_PIN(MX50_PAD_EIM_CRE), +}; + +static struct imx_pinctrl_soc_info imx50_pinctrl_info = { + .pins = imx50_pinctrl_pads, + .npins = ARRAY_SIZE(imx50_pinctrl_pads), +}; + +static struct of_device_id imx50_pinctrl_of_match[] = { + { .compatible = "fsl,imx50-iomuxc", }, + { /* sentinel */ } +}; + +static int imx50_pinctrl_probe(struct platform_device *pdev) +{ + return imx_pinctrl_probe(pdev, &imx50_pinctrl_info); +} + +static struct platform_driver imx50_pinctrl_driver = { + .driver = { + .name = "imx50-pinctrl", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(imx50_pinctrl_of_match), + }, + .probe = imx50_pinctrl_probe, + .remove = imx_pinctrl_remove, +}; + +static int __init imx50_pinctrl_init(void) +{ + return platform_driver_register(&imx50_pinctrl_driver); +} +arch_initcall(imx50_pinctrl_init); + +static void __exit imx50_pinctrl_exit(void) +{ + platform_driver_unregister(&imx50_pinctrl_driver); +} +module_exit(imx50_pinctrl_exit); +MODULE_DESCRIPTION("Freescale IMX50 pinctrl driver"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 8a24284275f682e3f92b0f91d7d06f2778bc4256 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 5 Nov 2013 21:27:02 -0600 Subject: gpio: pl061: don't depend on CONFIG_ARM The pl061 driver has no real dependency on ARM, so remove the kconfig dependency. Signed-off-by: Rob Herring Cc: Linus Walleij Cc: linux-gpio@vger.kernel.org Signed-off-by: Linus Walleij diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 62eb9ef..972eaa0 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -213,7 +213,7 @@ config GPIO_OCTEON config GPIO_PL061 bool "PrimeCell PL061 GPIO support" - depends on ARM && ARM_AMBA + depends on ARM_AMBA select GENERIC_IRQ_CHIP help Say yes here to support the PrimeCell PL061 GPIO device -- cgit v0.10.2 From 68f9672b1388f0999d4087b72c3c80cf1c983fa2 Mon Sep 17 00:00:00 2001 From: Oskar Schirmer Date: Tue, 5 Nov 2013 12:13:54 +0000 Subject: ASoC: fsl: imx-pcm-fiq: remove bogus period delta calculation Originally snd_hrtimer_callback() used iprtd->period_time for some jiffies based estimation to determine the right moment to call snd_pcm_period_elapsed(). As timer drifts may well be a problem, this was changed in commit b4e82b5b785670b6 to be based on buffer transmission progress, using iprtd->offset and runtime->buffer_size to calculate the amount of data since last period had elapsed. Unfortunately, iprtd->offset counts in bytes, while runtime->buffer_size counts frames, so adding these to find some delta is like comparing apples and oranges, and eventually results in negative delta values every now and then. This is no big harm, because it simply causes snd_pcm_period_elapsed() being called more often than necessary, as negative delta is taken for a large unsigned value by implicit conversion rule. Nonetheless, the calculation is broken, so one would replace the runtime->buffer_size by its equivalent in bytes. But then, there are chances snd_pcm_period_elapsed() is called late, because calculating the moment for the elapsed period into delta is based against the iprtd->last_offset, which is not necessarily the first byte of the period in question, but some random byte which the FIQ handler left us with in r8/r9 by accident. Again, negative impact is low, as there are plenty of periods already prefilled with data, and snd_pcm_period_elapsed() will probably be called latest when the following period is reached. However, the calculation is conceptually broken, and we are best off removing the clever stuff altogether. snd_pcm_period_elapsed() is now simply called once everytime snd_hrtimer_callback() is run, which may not be most accurate, but at least this way we are quite sure we dont miss an end of period. There is not much extra effort wasted by superfluous calls to snd_pcm_period_elapsed(), as the timer frequency closely matches the period size anyway. Signed-off-by: Oskar Schirmer Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c index 34043c5..94720a6 100644 --- a/sound/soc/fsl/imx-pcm-fiq.c +++ b/sound/soc/fsl/imx-pcm-fiq.c @@ -39,8 +39,6 @@ struct imx_pcm_runtime_data { unsigned int period; int periods; unsigned long offset; - unsigned long last_offset; - unsigned long size; struct hrtimer hrt; int poll_time_ns; struct snd_pcm_substream *substream; @@ -54,7 +52,6 @@ static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt) struct snd_pcm_substream *substream = iprtd->substream; struct snd_pcm_runtime *runtime = substream->runtime; struct pt_regs regs; - unsigned long delta; if (!atomic_read(&iprtd->running)) return HRTIMER_NORESTART; @@ -66,19 +63,7 @@ static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt) else iprtd->offset = regs.ARM_r9 & 0xffff; - /* How much data have we transferred since the last period report? */ - if (iprtd->offset >= iprtd->last_offset) - delta = iprtd->offset - iprtd->last_offset; - else - delta = runtime->buffer_size + iprtd->offset - - iprtd->last_offset; - - /* If we've transferred at least a period then report it and - * reset our poll time */ - if (delta >= iprtd->period) { - snd_pcm_period_elapsed(substream); - iprtd->last_offset = iprtd->offset; - } + snd_pcm_period_elapsed(substream); hrtimer_forward_now(hrt, ns_to_ktime(iprtd->poll_time_ns)); @@ -95,11 +80,9 @@ static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; struct imx_pcm_runtime_data *iprtd = runtime->private_data; - iprtd->size = params_buffer_bytes(params); iprtd->periods = params_periods(params); - iprtd->period = params_period_bytes(params) ; + iprtd->period = params_period_bytes(params); iprtd->offset = 0; - iprtd->last_offset = 0; iprtd->poll_time_ns = 1000000000 / params_rate(params) * params_period_size(params); snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); -- cgit v0.10.2 From 993571273275bfecb5161806796eb368db234106 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 5 Nov 2013 17:21:22 -0200 Subject: gpio: gpio-mxs: Remove unneeded dt checks mxs is a devicetree only platform, so there is no need to check whether we are in dt or platform data case. Signed-off-by: Fabio Estevam Acked-by: Shawn Guo Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c index f8e6af2..532bcb3 100644 --- a/drivers/gpio/gpio-mxs.c +++ b/drivers/gpio/gpio-mxs.c @@ -254,7 +254,6 @@ static int mxs_gpio_probe(struct platform_device *pdev) struct device_node *parent; static void __iomem *base; struct mxs_gpio_port *port; - struct resource *iores = NULL; int irq_base; int err; @@ -262,16 +261,10 @@ static int mxs_gpio_probe(struct platform_device *pdev) if (!port) return -ENOMEM; - if (np) { - port->id = of_alias_get_id(np, "gpio"); - if (port->id < 0) - return port->id; - port->devid = (enum mxs_gpio_id) of_id->data; - } else { - port->id = pdev->id; - port->devid = pdev->id_entry->driver_data; - } - + port->id = of_alias_get_id(np, "gpio"); + if (port->id < 0) + return port->id; + port->devid = (enum mxs_gpio_id) of_id->data; port->irq = platform_get_irq(pdev, 0); if (port->irq < 0) return port->irq; @@ -281,18 +274,11 @@ static int mxs_gpio_probe(struct platform_device *pdev) * share the same one */ if (!base) { - if (np) { - parent = of_get_parent(np); - base = of_iomap(parent, 0); - of_node_put(parent); - if (!base) - return -EADDRNOTAVAIL; - } else { - iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, iores); - if (IS_ERR(base)) - return PTR_ERR(base); - } + parent = of_get_parent(np); + base = of_iomap(parent, 0); + of_node_put(parent); + if (!base) + return -EADDRNOTAVAIL; } port->base = base; -- cgit v0.10.2 From d5b6b65e75ce607c2734227524e11574317a1c1a Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Wed, 6 Nov 2013 10:50:44 +0100 Subject: ALSA: hda - Make sure mute LEDs stay on during runtime suspend (Realtek) Some HP machines with Realtek codecs have mute LEDs connected to VREF pins. However when these go into runtime suspend, the pin powers down and its pin control is disabled, thus disabling the LED too. This patch fixes that issue by making sure that the pin stays in D0 with correct pin control. Cc: stable@kernel.org BugLink: https://bugs.launchpad.net/bugs/1248465 Tested-by: Franz Hsieh Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 02071a5..f19cd4f 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2959,6 +2959,23 @@ static void alc269_fixup_mic_mute_hook(void *private_data, int enabled) snd_hda_set_pin_ctl_cache(codec, spec->mute_led_nid, pinval); } +/* Make sure the led works even in runtime suspend */ +static unsigned int led_power_filter(struct hda_codec *codec, + hda_nid_t nid, + unsigned int power_state) +{ + struct alc_spec *spec = codec->spec; + + if (power_state != AC_PWRST_D3 || nid != spec->mute_led_nid) + return power_state; + + /* Set pin ctl again, it might have just been set to 0 */ + snd_hda_set_pin_ctl(codec, nid, + snd_hda_codec_get_pin_target(codec, nid)); + + return AC_PWRST_D0; +} + static void alc269_fixup_hp_mute_led(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -2978,6 +2995,7 @@ static void alc269_fixup_hp_mute_led(struct hda_codec *codec, spec->mute_led_nid = pin - 0x0a + 0x18; spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook; spec->gen.vmaster_mute_enum = 1; + codec->power_filter = led_power_filter; snd_printd("Detected mute LED for %x:%d\n", spec->mute_led_nid, spec->mute_led_polarity); break; @@ -2993,6 +3011,7 @@ static void alc269_fixup_hp_mute_led_mic1(struct hda_codec *codec, spec->mute_led_nid = 0x18; spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook; spec->gen.vmaster_mute_enum = 1; + codec->power_filter = led_power_filter; } } @@ -3005,6 +3024,7 @@ static void alc269_fixup_hp_mute_led_mic2(struct hda_codec *codec, spec->mute_led_nid = 0x19; spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook; spec->gen.vmaster_mute_enum = 1; + codec->power_filter = led_power_filter; } } -- cgit v0.10.2 From ddb146da23cb08851fa418481dd84412f99eec1e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 18:39:48 +0100 Subject: ASoC: blackfin: Use WARN_ON() instead of BUG_ON() Use WARN_ON() and handle the error cases accordingly. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/blackfin/bf5xx-sport.c b/sound/soc/blackfin/bf5xx-sport.c index 6953512..9dfa124 100644 --- a/sound/soc/blackfin/bf5xx-sport.c +++ b/sound/soc/blackfin/bf5xx-sport.c @@ -179,8 +179,9 @@ static inline int sport_hook_rx_dummy(struct sport_device *sport) struct dmasg *desc, temp_desc; unsigned long flags; - BUG_ON(sport->dummy_rx_desc == NULL); - BUG_ON(sport->curr_rx_desc == sport->dummy_rx_desc); + if (WARN_ON(!sport->dummy_rx_desc) || + WARN_ON(sport->curr_rx_desc == sport->dummy_rx_desc)) + return -EINVAL; /* Maybe the dummy buffer descriptor ring is damaged */ sport->dummy_rx_desc->next_desc_addr = sport->dummy_rx_desc + 1; @@ -250,8 +251,9 @@ int sport_rx_start(struct sport_device *sport) return -EBUSY; if (sport->tx_run) { /* tx is running, rx is not running */ - BUG_ON(sport->dma_rx_desc == NULL); - BUG_ON(sport->curr_rx_desc != sport->dummy_rx_desc); + if (WARN_ON(!sport->dma_rx_desc) || + WARN_ON(sport->curr_rx_desc != sport->dummy_rx_desc)) + return -EINVAL; local_irq_save(flags); while ((get_dma_curr_desc_ptr(sport->dma_rx_chan) - sizeof(struct dmasg)) != sport->dummy_rx_desc) @@ -298,8 +300,9 @@ static inline int sport_hook_tx_dummy(struct sport_device *sport) struct dmasg *desc, temp_desc; unsigned long flags; - BUG_ON(sport->dummy_tx_desc == NULL); - BUG_ON(sport->curr_tx_desc == sport->dummy_tx_desc); + if (WARN_ON(!sport->dummy_tx_desc) || + WARN_ON(sport->curr_tx_desc == sport->dummy_tx_desc)) + return -EINVAL; sport->dummy_tx_desc->next_desc_addr = sport->dummy_tx_desc + 1; @@ -331,8 +334,9 @@ int sport_tx_start(struct sport_device *sport) if (sport->tx_run) return -EBUSY; if (sport->rx_run) { - BUG_ON(sport->dma_tx_desc == NULL); - BUG_ON(sport->curr_tx_desc != sport->dummy_tx_desc); + if (WARN_ON(!sport->dma_tx_desc) || + WARN_ON(sport->curr_tx_desc != sport->dummy_tx_desc)) + return -EINVAL; /* Hook the normal buffer descriptor */ local_irq_save(flags); while ((get_dma_curr_desc_ptr(sport->dma_tx_chan) - @@ -767,7 +771,8 @@ static irqreturn_t err_handler(int irq, void *dev_id) int sport_set_rx_callback(struct sport_device *sport, void (*rx_callback)(void *), void *rx_data) { - BUG_ON(rx_callback == NULL); + if (WARN_ON(!rx_callback)) + return -EINVAL; sport->rx_callback = rx_callback; sport->rx_data = rx_data; @@ -778,7 +783,8 @@ EXPORT_SYMBOL(sport_set_rx_callback); int sport_set_tx_callback(struct sport_device *sport, void (*tx_callback)(void *), void *tx_data) { - BUG_ON(tx_callback == NULL); + if (WARN_ON(!tx_callback)) + return -EINVAL; sport->tx_callback = tx_callback; sport->tx_data = tx_data; @@ -789,7 +795,8 @@ EXPORT_SYMBOL(sport_set_tx_callback); int sport_set_err_callback(struct sport_device *sport, void (*err_callback)(void *), void *err_data) { - BUG_ON(err_callback == NULL); + if (WARN_ON(!err_callback)) + return -EINVAL; sport->err_callback = err_callback; sport->err_data = err_data; @@ -856,7 +863,8 @@ struct sport_device *sport_init(struct platform_device *pdev, param.wdsize = wdsize; param.dummy_count = dummy_count; - BUG_ON(param.wdsize == 0 || param.dummy_count == 0); + if (WARN_ON(param.wdsize == 0 || param.dummy_count == 0)) + return NULL; ret = sport_config_pdev(pdev, ¶m); if (ret) -- cgit v0.10.2 From bee026d0fe4bcc2b29e9279c319ed5e60a3bda97 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 18:39:49 +0100 Subject: ASoC: max98088: Use WARN_ON() instead of BUG_ON() Use WARN_ON() and handle the error cases accordingly. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index 566a367..8bd2d8a 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -621,8 +621,9 @@ static void m98088_eq_band(struct snd_soc_codec *codec, unsigned int dai, unsigned int eq_reg; unsigned int i; - BUG_ON(band > 4); - BUG_ON(dai > 1); + if (WARN_ON(band > 4) || + WARN_ON(dai > 1)) + return; /* Load the base register address */ eq_reg = dai ? M98088_REG_84_DAI2_EQ_BASE : M98088_REG_52_DAI1_EQ_BASE; @@ -962,7 +963,8 @@ static int max98088_line_pga(struct snd_soc_dapm_widget *w, struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); u8 *state; - BUG_ON(!((channel == 1) || (channel == 2))); + if (WARN_ON(!(channel == 1 || channel == 2))) + return -EINVAL; switch (line) { case LINE_INA: -- cgit v0.10.2 From a922cd7151371f429c7456951b77999c75520955 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 18:39:50 +0100 Subject: ASoC: max98095: Use WARN_ON() instead of BUG_ON() Use WARN_ON() and handle the error cases accordingly. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index 8dbcacd..04618a5 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -637,8 +637,9 @@ static void m98095_eq_band(struct snd_soc_codec *codec, unsigned int dai, unsigned int eq_reg; unsigned int i; - BUG_ON(band > 4); - BUG_ON(dai > 1); + if (WARN_ON(band > 4) || + WARN_ON(dai > 1)) + return; /* Load the base register address */ eq_reg = dai ? M98095_142_DAI2_EQ_BASE : M98095_110_DAI1_EQ_BASE; @@ -662,8 +663,9 @@ static void m98095_biquad_band(struct snd_soc_codec *codec, unsigned int dai, unsigned int bq_reg; unsigned int i; - BUG_ON(band > 1); - BUG_ON(dai > 1); + if (WARN_ON(band > 1) || + WARN_ON(dai > 1)) + return; /* Load the base register address */ bq_reg = dai ? M98095_17E_DAI2_BQ_BASE : M98095_174_DAI1_BQ_BASE; @@ -1011,7 +1013,8 @@ static int max98095_line_pga(struct snd_soc_dapm_widget *w, struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); u8 *state; - BUG_ON(!((channel == 1) || (channel == 2))); + if (WARN_ON(!(channel == 1 || channel == 2))) + return -EINVAL; state = &max98095->lin_state; @@ -1868,7 +1871,8 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol, int fs, best, best_val, i; int regmask, regsave; - BUG_ON(channel > 1); + if (WARN_ON(channel > 1)) + return -EINVAL; if (!pdata || !max98095->eq_textcnt) return 0; -- cgit v0.10.2 From 773392b25ce1e5d99c72fcc227bb53ba358497be Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 18:39:51 +0100 Subject: ASoC: tpa6130a2: Use WARN_ON() instead of BUG_ON() Use WARN_ON() and handle the error cases accordingly. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c index c58bee8..348552e 100644 --- a/sound/soc/codecs/tpa6130a2.c +++ b/sound/soc/codecs/tpa6130a2.c @@ -55,7 +55,8 @@ static int tpa6130a2_i2c_read(int reg) struct tpa6130a2_data *data; int val; - BUG_ON(tpa6130a2_client == NULL); + if (WARN_ON(!tpa6130a2_client)) + return -EINVAL; data = i2c_get_clientdata(tpa6130a2_client); /* If powered off, return the cached value */ @@ -77,7 +78,8 @@ static int tpa6130a2_i2c_write(int reg, u8 value) struct tpa6130a2_data *data; int val = 0; - BUG_ON(tpa6130a2_client == NULL); + if (WARN_ON(!tpa6130a2_client)) + return -EINVAL; data = i2c_get_clientdata(tpa6130a2_client); if (data->power_state) { @@ -98,7 +100,8 @@ static u8 tpa6130a2_read(int reg) { struct tpa6130a2_data *data; - BUG_ON(tpa6130a2_client == NULL); + if (WARN_ON(!tpa6130a2_client)) + return 0; data = i2c_get_clientdata(tpa6130a2_client); return data->regs[reg]; @@ -109,7 +112,8 @@ static int tpa6130a2_initialize(void) struct tpa6130a2_data *data; int i, ret = 0; - BUG_ON(tpa6130a2_client == NULL); + if (WARN_ON(!tpa6130a2_client)) + return -EINVAL; data = i2c_get_clientdata(tpa6130a2_client); for (i = 1; i < TPA6130A2_REG_VERSION; i++) { @@ -127,7 +131,8 @@ static int tpa6130a2_power(u8 power) u8 val; int ret = 0; - BUG_ON(tpa6130a2_client == NULL); + if (WARN_ON(!tpa6130a2_client)) + return -EINVAL; data = i2c_get_clientdata(tpa6130a2_client); mutex_lock(&data->mutex); @@ -193,7 +198,8 @@ static int tpa6130a2_get_volsw(struct snd_kcontrol *kcontrol, unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; - BUG_ON(tpa6130a2_client == NULL); + if (WARN_ON(!tpa6130a2_client)) + return -EINVAL; data = i2c_get_clientdata(tpa6130a2_client); mutex_lock(&data->mutex); @@ -223,7 +229,8 @@ static int tpa6130a2_put_volsw(struct snd_kcontrol *kcontrol, unsigned int val = (ucontrol->value.integer.value[0] & mask); unsigned int val_reg; - BUG_ON(tpa6130a2_client == NULL); + if (WARN_ON(!tpa6130a2_client)) + return -EINVAL; data = i2c_get_clientdata(tpa6130a2_client); if (invert) -- cgit v0.10.2 From f5b3a563943f415b99823be9918119f07337e328 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 18:39:52 +0100 Subject: ASoC: wm0010: Use WARN_ON() instead of BUG_ON() Use WARN_ON() and handle the error cases accordingly. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index d5ebcb0..aea9164 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -372,7 +372,8 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_codec *codec) offset = 0; dsp = inforec->dsp_target; wm0010->boot_failed = false; - BUG_ON(!list_empty(&xfer_list)); + if (WARN_ON(!list_empty(&xfer_list))) + return -EINVAL; init_completion(&done); /* First record should be INFO */ -- cgit v0.10.2 From bf90e895b51f61722681c1ee6e99113cbeba7d13 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 18:39:53 +0100 Subject: ASoC: wm2000: Use WARN_ON() instead of BUG_ON() Use WARN_ON() and handle the error cases accordingly. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 7fefd76..8ae5027 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -137,7 +137,8 @@ static int wm2000_power_up(struct i2c_client *i2c, int analogue) unsigned long rate; int ret; - BUG_ON(wm2000->anc_mode != ANC_OFF); + if (WARN_ON(wm2000->anc_mode != ANC_OFF)) + return -EINVAL; dev_dbg(&i2c->dev, "Beginning power up\n"); @@ -277,7 +278,8 @@ static int wm2000_enter_bypass(struct i2c_client *i2c, int analogue) { struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); - BUG_ON(wm2000->anc_mode != ANC_ACTIVE); + if (WARN_ON(wm2000->anc_mode != ANC_ACTIVE)) + return -EINVAL; if (analogue) { wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, @@ -315,7 +317,8 @@ static int wm2000_exit_bypass(struct i2c_client *i2c, int analogue) { struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); - BUG_ON(wm2000->anc_mode != ANC_BYPASS); + if (WARN_ON(wm2000->anc_mode != ANC_BYPASS)) + return -EINVAL; wm2000_write(i2c, WM2000_REG_SYS_CTL1, 0); @@ -349,7 +352,8 @@ static int wm2000_enter_standby(struct i2c_client *i2c, int analogue) { struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); - BUG_ON(wm2000->anc_mode != ANC_ACTIVE); + if (WARN_ON(wm2000->anc_mode != ANC_ACTIVE)) + return -EINVAL; if (analogue) { wm2000_write(i2c, WM2000_REG_ANA_VMID_PD_TIME, 248 / 4); @@ -392,7 +396,8 @@ static int wm2000_exit_standby(struct i2c_client *i2c, int analogue) { struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); - BUG_ON(wm2000->anc_mode != ANC_STANDBY); + if (WARN_ON(wm2000->anc_mode != ANC_STANDBY)) + return -EINVAL; wm2000_write(i2c, WM2000_REG_SYS_CTL1, 0); -- cgit v0.10.2 From 4a9e0f919c44fad6a2c591c6aed519ca3247515b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 18:39:54 +0100 Subject: ASoC: wm8580: Use WARN() instead of BUG_ON() Use WARN() instead. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index 5e9c40f..08a414b 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -736,7 +736,7 @@ static int wm8580_set_sysclk(struct snd_soc_dai *dai, int clk_id, break; default: - BUG_ON("Unknown DAI driver ID\n"); + WARN(1, "Unknown DAI driver ID\n"); return -EINVAL; } -- cgit v0.10.2 From 95ff71e9383fdb6efca11455b8e495af034b7ce9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 18:39:55 +0100 Subject: ASoC: wm5100: Use WARN_ON() instead of BUG_ON() Use WARN_ON() and handle the error cases accordingly. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index ac1745d..4cf91de 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -1972,7 +1972,8 @@ static void wm5100_set_detect_mode(struct wm5100_priv *wm5100, int the_mode) { struct wm5100_jack_mode *mode = &wm5100->pdata.jack_modes[the_mode]; - BUG_ON(the_mode >= ARRAY_SIZE(wm5100->pdata.jack_modes)); + if (WARN_ON(the_mode >= ARRAY_SIZE(wm5100->pdata.jack_modes))) + return; gpio_set_value_cansleep(wm5100->pdata.hp_pol, mode->hp_pol); regmap_update_bits(wm5100->regmap, WM5100_ACCESSORY_DETECT_MODE_1, -- cgit v0.10.2 From 22a7038c3950a7a4c4d1656a04c3ca8da8296324 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 18:39:56 +0100 Subject: ASoC: wm8776: Use WARN_ON() instead of BUG_ON() Use WARN_ON() and handle the error cases accordingly. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c index f31017e..942d58e 100644 --- a/sound/soc/codecs/wm8776.c +++ b/sound/soc/codecs/wm8776.c @@ -325,7 +325,8 @@ static int wm8776_set_sysclk(struct snd_soc_dai *dai, struct snd_soc_codec *codec = dai->codec; struct wm8776_priv *wm8776 = snd_soc_codec_get_drvdata(codec); - BUG_ON(dai->driver->id >= ARRAY_SIZE(wm8776->sysclk)); + if (WARN_ON(dai->driver->id >= ARRAY_SIZE(wm8776->sysclk))) + return -EINVAL; wm8776->sysclk[dai->driver->id] = freq; -- cgit v0.10.2 From 246e884b82305c5669a892c04e67864f4e7c10d5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 18:39:57 +0100 Subject: ASoC: wm8900: Use WARN_ON() instead of BUG_ON() Use WARN_ON() and handle the error cases accordingly. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index 7c8257c..de67e74 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -691,7 +691,8 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref, unsigned int K, Ndiv, Nmod, target; unsigned int div; - BUG_ON(!Fout); + if (WARN_ON(!Fout)) + return -EINVAL; /* The FLL must run at 90-100MHz which is then scaled down to * the output value by FLLCLK_DIV. */ @@ -742,8 +743,9 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref, /* Move down to proper range now rounding is done */ fll_div->k = K / 10; - BUG_ON(target != Fout * (fll_div->fllclk_div << 2)); - BUG_ON(!K && target != Fref * fll_div->fll_ratio * fll_div->n); + if (WARN_ON(target != Fout * (fll_div->fllclk_div << 2)) || + WARN_ON(!K && target != Fref * fll_div->fll_ratio * fll_div->n)) + return -EINVAL; return 0; } -- cgit v0.10.2 From 4c8d620ac9e3aea0a0c2edf36851b59b44bd12f2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 18:39:58 +0100 Subject: ASoC: wm8904: Use WARN_ON() instead of BUG_ON() Use WARN_ON() and handle the error cases accordingly. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 4dfa8dc..c173ab3 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -658,7 +658,8 @@ SOC_SINGLE_TLV("EQ5 Volume", WM8904_EQ6, 0, 24, 0, eq_tlv), static int cp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { - BUG_ON(event != SND_SOC_DAPM_POST_PMU); + if (WARN_ON(event != SND_SOC_DAPM_POST_PMU)) + return -EINVAL; /* Maximum startup time */ udelay(500); -- cgit v0.10.2 From fb2e3e70193f6757e536dfdecf21be2e6ee308d4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 18:39:59 +0100 Subject: ASoC: wm9713: Use WARN_ON() instead of BUG_ON() Use WARN_ON() and handle the error cases accordingly. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index a53e175..acea892 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -221,7 +221,8 @@ static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w, struct snd_soc_codec *codec = w->codec; u16 status, rate; - BUG_ON(event != SND_SOC_DAPM_PRE_PMD); + if (WARN_ON(event != SND_SOC_DAPM_PRE_PMD)) + return -EINVAL; /* Gracefully shut down the voice interface. */ status = ac97_read(codec, AC97_EXTENDED_MID) | 0x1000; -- cgit v0.10.2 From 95281171a713f4d32f1a044b937563ec7776ccfe Mon Sep 17 00:00:00 2001 From: James Hogan Date: Tue, 5 Nov 2013 14:42:03 +0000 Subject: metag: handle low level kicks directly Kick interrupts trigger the LWK (low level kick) signal, usually handled by the __TBIDoStdLWK() function which is the only handler inherited from the bootloader. The LWK signal is converted either to a SWK (plain software kick) or a SWS (software kick with an attached message). Linux has kick_handler() to handle SWK and call registered kick handlers (IPIs and inter-thread comms), but SWS is as far as I'm aware unused with Linux. Therefore remove that abstraction and have Linux handle LWK directly. This will reduce kick latency slightly, and reduce our dependence on the bootloader, which makes it easier to directly boot a kernel in QEMU (particularly for SMP). Signed-off-by: James Hogan diff --git a/arch/metag/include/asm/tbx.h b/arch/metag/include/asm/tbx.h index 287b36f..703b9cb 100644 --- a/arch/metag/include/asm/tbx.h +++ b/arch/metag/include/asm/tbx.h @@ -150,11 +150,9 @@ #else /* Reserved 0x04-0x09 */ #endif -#define TBID_SIGNUM_SWS 0x0A /* KICK received with SigMask != 0 */ -#define TBID_SIGNUM_SWK 0x0B /* KICK received with SigMask == 0 */ -/* Reserved 0x0C-0x0F */ +/* Reserved 0x0A-0x0F */ #define TBID_SIGNUM_TRT 0x10 /* Timer trigger */ -#define TBID_SIGNUM_LWK 0x11 /* Low level kick (handler provided by TBI) */ +#define TBID_SIGNUM_LWK 0x11 /* Low level kick */ #define TBID_SIGNUM_XXF 0x12 /* Fault handler - receives ALL _xxF sigs */ #ifdef TBI_1_4 #define TBID_SIGNUM_DFR 0x13 /* Deferred Exception handler */ @@ -183,8 +181,7 @@ each hardware signal, sometimes this is a many-to-one relationship. */ #define TBI_TRIG_BIT(SigNum) (\ ((SigNum) >= TBID_SIGNUM_TRT) ? 1<<((SigNum)-TBID_SIGNUM_TRT) :\ - ( ((SigNum) == TBID_SIGNUM_SWS) || \ - ((SigNum) == TBID_SIGNUM_SWK) ) ? \ + ((SigNum) == TBID_SIGNUM_LWK) ? \ TXSTAT_KICK_BIT : TXSTATI_BGNDHALT_BIT ) /* Return the hardware trigger vector number for entries in the @@ -687,10 +684,8 @@ typedef union _tbires_tag_ { Triggers will indicate the status of TXSTAT or TXSTATI sampled by the code that called the handler. - InstOrSWSId is defined firstly as 'Inst' if the SigNum is TBID_SIGNUM_SWx - and hold the actual SWITCH instruction detected, secondly if SigNum - is TBID_SIGNUM_SWS the 'SWSId' is defined to hold the Id of the - software signal detected, in other cases the value of this + Inst is defined as 'Inst' if the SigNum is TBID_SIGNUM_SWx and holds the + actual SWITCH instruction detected, in other cases the value of this parameter is undefined. pTBI points at the PTBI structure related to the thread and processing @@ -709,7 +704,7 @@ typedef union _tbires_tag_ { */ typedef TBIRES (*PTBIAPIFN)( TBIRES State, int SigNum, - int Triggers, int InstOrSWSId, + int Triggers, int Inst, volatile struct _tbi_tag_ *pTBI ); #endif /* ifndef __ASSEMBLY__ */ @@ -757,7 +752,7 @@ typedef volatile struct _tbi_tag_ { #ifndef __ASSEMBLY__ /* This handler should be used for TBID_SIGNUM_DFR */ extern TBIRES __TBIHandleDFR ( TBIRES State, int SigNum, - int Triggers, int InstOrSWSId, + int Triggers, int Inst, volatile struct _tbi_tag_ *pTBI ); #endif #endif diff --git a/arch/metag/kernel/setup.c b/arch/metag/kernel/setup.c index c396cd0..e639bae 100644 --- a/arch/metag/kernel/setup.c +++ b/arch/metag/kernel/setup.c @@ -302,13 +302,9 @@ void __init setup_arch(char **cmdline_p) * rather than the version from the bootloader. This makes call * stacks easier to understand and may allow us to unmap the * bootloader at some point. - * - * We need to keep the LWK handler that TBI installed in order to - * be able to do inter-thread comms. */ for (i = 0; i <= TBID_SIGNUM_MAX; i++) - if (i != TBID_SIGNUM_LWK) - _pTBI->fnSigs[i] = __TBIUnExpXXX; + _pTBI->fnSigs[i] = __TBIUnExpXXX; /* A Meta requirement is that the kernel is loaded (virtually) * at the PAGE_OFFSET. diff --git a/arch/metag/kernel/traps.c b/arch/metag/kernel/traps.c index 25f9d1c..17b2e2e 100644 --- a/arch/metag/kernel/traps.c +++ b/arch/metag/kernel/traps.c @@ -819,8 +819,7 @@ void per_cpu_trap_init(unsigned long cpu) set_trigger_mask(TBI_INTS_INIT(thread) | /* interrupts */ TBI_TRIG_BIT(TBID_SIGNUM_LWK) | /* low level kick */ - TBI_TRIG_BIT(TBID_SIGNUM_SW1) | - TBI_TRIG_BIT(TBID_SIGNUM_SWS)); + TBI_TRIG_BIT(TBID_SIGNUM_SW1)); /* non-priv - use current stack */ int_context.Sig.pCtx = NULL; @@ -842,7 +841,7 @@ void __init trap_init(void) _pTBI->fnSigs[TBID_SIGNUM_SW1] = switch1_handler; _pTBI->fnSigs[TBID_SIGNUM_SW2] = switchx_handler; _pTBI->fnSigs[TBID_SIGNUM_SW3] = switchx_handler; - _pTBI->fnSigs[TBID_SIGNUM_SWK] = kick_handler; + _pTBI->fnSigs[TBID_SIGNUM_LWK] = kick_handler; #ifdef CONFIG_METAG_META21 _pTBI->fnSigs[TBID_SIGNUM_DFR] = __TBIHandleDFR; diff --git a/arch/metag/tbx/tbidefr.S b/arch/metag/tbx/tbidefr.S index 3eb165e..8f0902b 100644 --- a/arch/metag/tbx/tbidefr.S +++ b/arch/metag/tbx/tbidefr.S @@ -20,7 +20,7 @@ /* D1Ar1:D0Ar2 -- State * D0Ar3 -- SigNum * D0Ar4 -- Triggers - * D1Ar5 -- InstOrSWSId + * D1Ar5 -- Inst * D0Ar6 -- pTBI (volatile) */ ___TBIHandleDFR: -- cgit v0.10.2 From a2b4f8a473efd82d634117a057e0ba64443354cf Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 6 Nov 2013 00:06:45 -0800 Subject: ASoC: rcar: remove un-needed select from Kconfig config RCAR_CLK_ADG is not exist Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 56d8ff6..14011d9 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -37,7 +37,6 @@ config SND_SOC_SH4_SIU config SND_SOC_RCAR tristate "R-Car series SRU/SCU/SSIU/SSI support" select SND_SIMPLE_CARD - select RCAR_CLK_ADG help This option enables R-Car SUR/SCU/SSIU/SSI sound support -- cgit v0.10.2 From 8e35cd4ac9967620e03f027f27db18b2e249dcd7 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Wed, 6 Nov 2013 11:20:01 +0100 Subject: ALSA: HDA - Limit mic boost and add mute LED for an HP machine This machine has a mute LED as well as a noisy internal mic. Hence it needs quirks for both limiting the mic boost as well as enabling the LED. BugLink: https://bugs.launchpad.net/bugs/1248476 Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f19cd4f..215db60 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3657,6 +3657,7 @@ enum { ALC271_FIXUP_HP_GATE_MIC_JACK, ALC269_FIXUP_ACER_AC700, ALC269_FIXUP_LIMIT_INT_MIC_BOOST, + ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED, ALC269VB_FIXUP_ORDISSIMO_EVE2, ALC283_FIXUP_CHROME_BOOK, ALC282_FIXUP_ASUS_TX300, @@ -3927,6 +3928,12 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_limit_int_mic_boost, }, + [ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_limit_int_mic_boost, + .chained = true, + .chain_id = ALC269_FIXUP_HP_MUTE_LED_MIC1, + }, [ALC269VB_FIXUP_ORDISSIMO_EVE2] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -4014,6 +4021,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x1973, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x1983, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x218b, "HP", ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x21ed, "HP Falco Chromebook", ALC283_FIXUP_CHROME_BOOK), SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), -- cgit v0.10.2 From 26c86da8821f7b64fced498674990318bc34c8de Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 31 Oct 2013 10:19:59 +0100 Subject: perf: Simplify the ring-buffer code By using CIRC_SPACE() we can obviate the need for perf_output_space(). Shrinks the size of perf_output_begin() by 17 bytes on x86_64-defconfig. Signed-off-by: Peter Zijlstra Cc: Benjamin Herrenschmidt Cc: Frederic Weisbecker Cc: Mathieu Desnoyers Cc: Michael Ellerman Cc: Michael Neuling Cc: "Paul E. McKenney" Cc: james.hogan@imgtec.com Cc: Vince Weaver Cc: Victor Kaplansky Cc: Oleg Nesterov Cc: Anton Blanchard Link: http://lkml.kernel.org/n/tip-vtb0xb0llebmsdlfn1v5vtfj@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index 9c2ddfb..6929c58 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -12,40 +12,10 @@ #include #include #include +#include #include "internal.h" -static bool perf_output_space(struct ring_buffer *rb, unsigned long tail, - unsigned long offset, unsigned long head) -{ - unsigned long sz = perf_data_size(rb); - unsigned long mask = sz - 1; - - /* - * check if user-writable - * overwrite : over-write its own tail - * !overwrite: buffer possibly drops events. - */ - if (rb->overwrite) - return true; - - /* - * verify that payload is not bigger than buffer - * otherwise masking logic may fail to detect - * the "not enough space" condition - */ - if ((head - offset) > sz) - return false; - - offset = (offset - tail) & mask; - head = (head - tail) & mask; - - if ((int)(head - offset) < 0) - return false; - - return true; -} - static void perf_output_wakeup(struct perf_output_handle *handle) { atomic_set(&handle->rb->poll, POLL_IN); @@ -181,9 +151,10 @@ int perf_output_begin(struct perf_output_handle *handle, tail = ACCESS_ONCE(rb->user_page->data_tail); smp_mb(); offset = head = local_read(&rb->head); - head += size; - if (unlikely(!perf_output_space(rb, tail, offset, head))) + if (!rb->overwrite && + unlikely(CIRC_SPACE(head, tail, perf_data_size(rb)) < size)) goto fail; + head += size; } while (local_cmpxchg(&rb->head, offset, head) != offset); if (head - local_read(&rb->wakeup) > rb->watermark) -- cgit v0.10.2 From c72b42a3dde487132da80202756c101b371b2add Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 31 Oct 2013 17:20:25 +0100 Subject: perf: Add unlikely() to the ring-buffer code Add unlikely() annotations to 'slow' paths: When having a sampling event but no output buffer; you have bigger issues -- also the bail is still faster than actually doing the work. When having a sampling event but a control page only buffer, you have bigger issues -- again the bail is still faster than actually doing work. Optimize for the case where you're not loosing events -- again, not doing the work is still faster but make sure that when you have to actually do work its as fast as possible. The typical watermark is 1/2 the buffer size, so most events will not take this path. Shrinks perf_output_begin() by 16 bytes on x86_64-defconfig. Signed-off-by: Peter Zijlstra Cc: Benjamin Herrenschmidt Cc: Frederic Weisbecker Cc: Mathieu Desnoyers Cc: Michael Ellerman Cc: Michael Neuling Cc: "Paul E. McKenney" Cc: james.hogan@imgtec.com Cc: Vince Weaver Cc: Victor Kaplansky Cc: Oleg Nesterov Cc: Anton Blanchard Link: http://lkml.kernel.org/n/tip-wlg3jew3qnutm8opd0hyeuwn@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index 6929c58..383cde4 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -121,17 +121,17 @@ int perf_output_begin(struct perf_output_handle *handle, event = event->parent; rb = rcu_dereference(event->rb); - if (!rb) + if (unlikely(!rb)) goto out; - handle->rb = rb; - handle->event = event; - - if (!rb->nr_pages) + if (unlikely(!rb->nr_pages)) goto out; + handle->rb = rb; + handle->event = event; + have_lost = local_read(&rb->lost); - if (have_lost) { + if (unlikely(have_lost)) { lost_event.header.size = sizeof(lost_event); perf_event_header__init_id(&lost_event.header, &sample_data, event); @@ -157,7 +157,7 @@ int perf_output_begin(struct perf_output_handle *handle, head += size; } while (local_cmpxchg(&rb->head, offset, head) != offset); - if (head - local_read(&rb->wakeup) > rb->watermark) + if (unlikely(head - local_read(&rb->wakeup) > rb->watermark)) local_add(rb->watermark, &rb->wakeup); handle->page = offset >> (PAGE_SHIFT + page_order(rb)); @@ -167,7 +167,7 @@ int perf_output_begin(struct perf_output_handle *handle, handle->addr += handle->size; handle->size = (PAGE_SIZE << page_order(rb)) - handle->size; - if (have_lost) { + if (unlikely(have_lost)) { lost_event.header.type = PERF_RECORD_LOST; lost_event.header.misc = 0; lost_event.id = event->id; -- cgit v0.10.2 From 85f59edf9684603026c64c902791748116d29478 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 31 Oct 2013 17:25:38 +0100 Subject: perf: Optimize perf_output_begin() There's no point in re-doing the memory-barrier when we fail the cmpxchg(). Also placing it after the space reservation loop makes it clearer it only separates the userpage->tail read from the data stores. Signed-off-by: Peter Zijlstra Cc: Benjamin Herrenschmidt Cc: Frederic Weisbecker Cc: Mathieu Desnoyers Cc: Michael Ellerman Cc: Michael Neuling Cc: "Paul E. McKenney" Cc: james.hogan@imgtec.com Cc: Vince Weaver Cc: Victor Kaplansky Cc: Oleg Nesterov Cc: Anton Blanchard Link: http://lkml.kernel.org/n/tip-c19u6egfldyx86tpyc3zgkw9@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index 383cde4..6ed16ec 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -141,15 +141,7 @@ int perf_output_begin(struct perf_output_handle *handle, perf_output_get_handle(handle); do { - /* - * Userspace could choose to issue a mb() before updating the - * tail pointer. So that all reads will be completed before the - * write is issued. - * - * See perf_output_put_handle(). - */ tail = ACCESS_ONCE(rb->user_page->data_tail); - smp_mb(); offset = head = local_read(&rb->head); if (!rb->overwrite && unlikely(CIRC_SPACE(head, tail, perf_data_size(rb)) < size)) @@ -157,6 +149,15 @@ int perf_output_begin(struct perf_output_handle *handle, head += size; } while (local_cmpxchg(&rb->head, offset, head) != offset); + /* + * Separate the userpage->tail read from the data stores below. + * Matches the MB userspace SHOULD issue after reading the data + * and before storing the new tail position. + * + * See perf_output_put_handle(). + */ + smp_mb(); + if (unlikely(head - local_read(&rb->wakeup) > rb->watermark)) local_add(rb->watermark, &rb->wakeup); -- cgit v0.10.2 From d20a973f46ed83e0d7d24f6c512064133038e193 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 31 Oct 2013 17:29:29 +0100 Subject: perf: Optimize perf_output_begin() -- lost_event case Avoid touching the lost_event and sample_data cachelines twince. Its not like we end up doing less work, but it might help to keep all accesses to these cachelines in one place. Due to code shuffle, this looses 4 bytes on x86_64-defconfig. Signed-off-by: Peter Zijlstra Cc: Benjamin Herrenschmidt Cc: Frederic Weisbecker Cc: Mathieu Desnoyers Cc: Michael Ellerman Cc: Michael Neuling Cc: "Paul E. McKenney" Cc: james.hogan@imgtec.com Cc: Vince Weaver Cc: Victor Kaplansky Cc: Oleg Nesterov Cc: Anton Blanchard Link: http://lkml.kernel.org/n/tip-zfxnc58qxj0eawdoj31hhupv@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index 6ed16ec..e4d70f3 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -106,7 +106,6 @@ int perf_output_begin(struct perf_output_handle *handle, struct ring_buffer *rb; unsigned long tail, offset, head; int have_lost; - struct perf_sample_data sample_data; struct { struct perf_event_header header; u64 id; @@ -132,10 +131,9 @@ int perf_output_begin(struct perf_output_handle *handle, have_lost = local_read(&rb->lost); if (unlikely(have_lost)) { - lost_event.header.size = sizeof(lost_event); - perf_event_header__init_id(&lost_event.header, &sample_data, - event); - size += lost_event.header.size; + size += sizeof(lost_event); + if (event->attr.sample_id_all) + size += event->id_header_size; } perf_output_get_handle(handle); @@ -169,11 +167,16 @@ int perf_output_begin(struct perf_output_handle *handle, handle->size = (PAGE_SIZE << page_order(rb)) - handle->size; if (unlikely(have_lost)) { + struct perf_sample_data sample_data; + + lost_event.header.size = sizeof(lost_event); lost_event.header.type = PERF_RECORD_LOST; lost_event.header.misc = 0; lost_event.id = event->id; lost_event.lost = local_xchg(&rb->lost, 0); + perf_event_header__init_id(&lost_event.header, + &sample_data, event); perf_output_put(handle, lost_event); perf_event__output_id_sample(event, handle, &sample_data); } -- cgit v0.10.2 From 524feca5e9da9e5f9e5aa5d5613b1d762db9509e Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 31 Oct 2013 17:36:25 +0100 Subject: perf: Optimize perf_output_begin() -- address calculation Rewrite the handle address calculation code to be clearer. Saves 8 bytes on x86_64-defconfig. Signed-off-by: Peter Zijlstra Cc: Benjamin Herrenschmidt Cc: Frederic Weisbecker Cc: Mathieu Desnoyers Cc: Michael Ellerman Cc: Michael Neuling Cc: "Paul E. McKenney" Cc: james.hogan@imgtec.com Cc: Vince Weaver Cc: Victor Kaplansky Cc: Oleg Nesterov Cc: Anton Blanchard Link: http://lkml.kernel.org/n/tip-3trb2n2henb9m27tncef3ag7@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index e4d70f3..c52a32f 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -105,7 +105,7 @@ int perf_output_begin(struct perf_output_handle *handle, { struct ring_buffer *rb; unsigned long tail, offset, head; - int have_lost; + int have_lost, page_shift; struct { struct perf_event_header header; u64 id; @@ -159,12 +159,12 @@ int perf_output_begin(struct perf_output_handle *handle, if (unlikely(head - local_read(&rb->wakeup) > rb->watermark)) local_add(rb->watermark, &rb->wakeup); - handle->page = offset >> (PAGE_SHIFT + page_order(rb)); - handle->page &= rb->nr_pages - 1; - handle->size = offset & ((PAGE_SIZE << page_order(rb)) - 1); - handle->addr = rb->data_pages[handle->page]; - handle->addr += handle->size; - handle->size = (PAGE_SIZE << page_order(rb)) - handle->size; + page_shift = PAGE_SHIFT + page_order(rb); + + handle->page = (offset >> page_shift) & (rb->nr_pages - 1); + offset &= (1UL << page_shift) - 1; + handle->addr = rb->data_pages[handle->page] + offset; + handle->size = (1UL << page_shift) - offset; if (unlikely(have_lost)) { struct perf_sample_data sample_data; -- cgit v0.10.2 From 394570b7939e1262f39373866166d8ee0a506e88 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 31 Oct 2013 17:41:23 +0100 Subject: perf: Update a stale comment Signed-off-by: Peter Zijlstra Cc: Benjamin Herrenschmidt Cc: Frederic Weisbecker Cc: Mathieu Desnoyers Cc: Michael Ellerman Cc: Michael Neuling Cc: "Paul E. McKenney" Cc: james.hogan@imgtec.com Cc: Vince Weaver Cc: Victor Kaplansky Cc: Oleg Nesterov Cc: Anton Blanchard Link: http://lkml.kernel.org/n/tip-9s5mze78gmlz19agt39i8rii@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index c52a32f..e8b168a 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -85,8 +85,8 @@ again: rb->user_page->data_head = head; /* - * Now check if we missed an update, rely on the (compiler) - * barrier in atomic_dec_and_test() to re-read rb->head. + * Now check if we missed an update -- rely on previous implied + * compiler barriers to force a re-read. */ if (unlikely(head != local_read(&rb->head))) { local_inc(&rb->nest); -- cgit v0.10.2 From 0a196848ca365ec582c6d86659be456be6d4ed96 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 30 Oct 2013 21:16:22 +0100 Subject: perf: Fix arch_perf_out_copy_user default The arch_perf_output_copy_user() default of __copy_from_user_inatomic() returns bytes not copied, while all other argument functions given DEFINE_OUTPUT_COPY() return bytes copied. Since copy_from_user_nmi() is the odd duck out by returning bytes copied where all other *copy_{to,from}* functions return bytes not copied, change it over and ammend DEFINE_OUTPUT_COPY() to expect bytes not copied. Oddly enough DEFINE_OUTPUT_COPY() already returned bytes not copied while expecting its worker functions to return bytes copied. Signed-off-by: Peter Zijlstra Acked-by: will.deacon@arm.com Cc: Frederic Weisbecker Link: http://lkml.kernel.org/r/20131030201622.GR16117@laptop.programming.kicks-ass.net Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 8a87a32..8e13293 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -1989,7 +1989,7 @@ perf_callchain_user32(struct pt_regs *regs, struct perf_callchain_entry *entry) frame.return_address = 0; bytes = copy_from_user_nmi(&frame, fp, sizeof(frame)); - if (bytes != sizeof(frame)) + if (bytes != 0) break; if (!valid_user_frame(fp, sizeof(frame))) @@ -2041,7 +2041,7 @@ perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs) frame.return_address = 0; bytes = copy_from_user_nmi(&frame, fp, sizeof(frame)); - if (bytes != sizeof(frame)) + if (bytes != 0) break; if (!valid_user_frame(fp, sizeof(frame))) diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index c1760ff..ae96cfa 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -789,7 +789,7 @@ static int intel_pmu_pebs_fixup_ip(struct pt_regs *regs) size = ip - to; /* Must fit our buffer, see above */ bytes = copy_from_user_nmi(buf, (void __user *)to, size); - if (bytes != size) + if (bytes != 0) return 0; kaddr = buf; diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/arch/x86/kernel/cpu/perf_event_intel_lbr.c index 90ee6c1..d82d155 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_lbr.c +++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c @@ -491,7 +491,7 @@ static int branch_type(unsigned long from, unsigned long to, int abort) /* may fail if text not present */ bytes = copy_from_user_nmi(buf, (void __user *)from, size); - if (bytes != size) + if (bytes != 0) return X86_BR_NONE; addr = buf; diff --git a/arch/x86/lib/usercopy.c b/arch/x86/lib/usercopy.c index 5465b86..ddf9ecb 100644 --- a/arch/x86/lib/usercopy.c +++ b/arch/x86/lib/usercopy.c @@ -31,6 +31,6 @@ copy_from_user_nmi(void *to, const void __user *from, unsigned long n) ret = __copy_from_user_inatomic(to, from, n); pagefault_enable(); - return n - ret; + return ret; } EXPORT_SYMBOL_GPL(copy_from_user_nmi); diff --git a/arch/x86/oprofile/backtrace.c b/arch/x86/oprofile/backtrace.c index d6aa6e8..5d04be5 100644 --- a/arch/x86/oprofile/backtrace.c +++ b/arch/x86/oprofile/backtrace.c @@ -47,7 +47,7 @@ dump_user_backtrace_32(struct stack_frame_ia32 *head) unsigned long bytes; bytes = copy_from_user_nmi(bufhead, head, sizeof(bufhead)); - if (bytes != sizeof(bufhead)) + if (bytes != 0) return NULL; fp = (struct stack_frame_ia32 *) compat_ptr(bufhead[0].next_frame); @@ -93,7 +93,7 @@ static struct stack_frame *dump_user_backtrace(struct stack_frame *head) unsigned long bytes; bytes = copy_from_user_nmi(bufhead, head, sizeof(bufhead)); - if (bytes != sizeof(bufhead)) + if (bytes != 0) return NULL; oprofile_add_trace(bufhead[0].return_address); diff --git a/kernel/events/internal.h b/kernel/events/internal.h index ca65997..569b2187 100644 --- a/kernel/events/internal.h +++ b/kernel/events/internal.h @@ -82,16 +82,16 @@ static inline unsigned long perf_data_size(struct ring_buffer *rb) } #define DEFINE_OUTPUT_COPY(func_name, memcpy_func) \ -static inline unsigned int \ +static inline unsigned long \ func_name(struct perf_output_handle *handle, \ - const void *buf, unsigned int len) \ + const void *buf, unsigned long len) \ { \ unsigned long size, written; \ \ do { \ - size = min_t(unsigned long, handle->size, len); \ - \ + size = min(handle->size, len); \ written = memcpy_func(handle->addr, buf, size); \ + written = size - written; \ \ len -= written; \ handle->addr += written; \ @@ -110,20 +110,37 @@ func_name(struct perf_output_handle *handle, \ return len; \ } -static inline int memcpy_common(void *dst, const void *src, size_t n) +static inline unsigned long +memcpy_common(void *dst, const void *src, unsigned long n) { memcpy(dst, src, n); - return n; + return 0; } DEFINE_OUTPUT_COPY(__output_copy, memcpy_common) -#define MEMCPY_SKIP(dst, src, n) (n) +static inline unsigned long +memcpy_skip(void *dst, const void *src, unsigned long n) +{ + return 0; +} -DEFINE_OUTPUT_COPY(__output_skip, MEMCPY_SKIP) +DEFINE_OUTPUT_COPY(__output_skip, memcpy_skip) #ifndef arch_perf_out_copy_user -#define arch_perf_out_copy_user __copy_from_user_inatomic +#define arch_perf_out_copy_user arch_perf_out_copy_user + +static inline unsigned long +arch_perf_out_copy_user(void *dst, const void *src, unsigned long n) +{ + unsigned long ret; + + pagefault_disable(); + ret = __copy_from_user_inatomic(dst, src, n); + pagefault_enable(); + + return ret; +} #endif DEFINE_OUTPUT_COPY(__output_copy_user, arch_perf_out_copy_user) -- cgit v0.10.2 From a94d342b9cb09edfe888ea972af0883b6a8d992b Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 30 Oct 2013 11:42:46 +0100 Subject: tools/perf: Add required memory barriers To match patch bf378d341e48 ("perf: Fix perf ring buffer memory ordering") change userspace to also adhere to the ordering outlined. Signed-off-by: Peter Zijlstra Cc: Michael Neuling Cc: "Paul E. McKenney" Cc: james.hogan@imgtec.com Cc: Vince Weaver Cc: Victor Kaplansky Cc: Oleg Nesterov Cc: Anton Blanchard Cc: Benjamin Herrenschmidt Cc: Frederic Weisbecker Cc: Mathieu Desnoyers Cc: Michael Ellerman Link: http://lkml.kernel.org/r/20131030104246.GH16117@laptop.programming.kicks-ass.net Signed-off-by: Ingo Molnar diff --git a/tools/perf/perf.h b/tools/perf/perf.h index f61c230..6a587e84 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -4,6 +4,8 @@ #include #if defined(__i386__) +#define mb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") +#define wmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") #define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") #define cpu_relax() asm volatile("rep; nop" ::: "memory"); #define CPUINFO_PROC "model name" @@ -13,6 +15,8 @@ #endif #if defined(__x86_64__) +#define mb() asm volatile("mfence" ::: "memory") +#define wmb() asm volatile("sfence" ::: "memory") #define rmb() asm volatile("lfence" ::: "memory") #define cpu_relax() asm volatile("rep; nop" ::: "memory"); #define CPUINFO_PROC "model name" @@ -23,45 +27,61 @@ #ifdef __powerpc__ #include "../../arch/powerpc/include/uapi/asm/unistd.h" +#define mb() asm volatile ("sync" ::: "memory") +#define wmb() asm volatile ("sync" ::: "memory") #define rmb() asm volatile ("sync" ::: "memory") -#define cpu_relax() asm volatile ("" ::: "memory"); #define CPUINFO_PROC "cpu" #endif #ifdef __s390__ +#define mb() asm volatile("bcr 15,0" ::: "memory") +#define wmb() asm volatile("bcr 15,0" ::: "memory") #define rmb() asm volatile("bcr 15,0" ::: "memory") -#define cpu_relax() asm volatile("" ::: "memory"); #endif #ifdef __sh__ #if defined(__SH4A__) || defined(__SH5__) +# define mb() asm volatile("synco" ::: "memory") +# define wmb() asm volatile("synco" ::: "memory") # define rmb() asm volatile("synco" ::: "memory") #else +# define mb() asm volatile("" ::: "memory") +# define wmb() asm volatile("" ::: "memory") # define rmb() asm volatile("" ::: "memory") #endif -#define cpu_relax() asm volatile("" ::: "memory") #define CPUINFO_PROC "cpu type" #endif #ifdef __hppa__ +#define mb() asm volatile("" ::: "memory") +#define wmb() asm volatile("" ::: "memory") #define rmb() asm volatile("" ::: "memory") -#define cpu_relax() asm volatile("" ::: "memory"); #define CPUINFO_PROC "cpu" #endif #ifdef __sparc__ +#ifdef __LP64__ +#define mb() asm volatile("ba,pt %%xcc, 1f\n" \ + "membar #StoreLoad\n" \ + "1:\n":::"memory") +#else +#define mb() asm volatile("":::"memory") +#endif +#define wmb() asm volatile("":::"memory") #define rmb() asm volatile("":::"memory") -#define cpu_relax() asm volatile("":::"memory") #define CPUINFO_PROC "cpu" #endif #ifdef __alpha__ +#define mb() asm volatile("mb" ::: "memory") +#define wmb() asm volatile("wmb" ::: "memory") #define rmb() asm volatile("mb" ::: "memory") -#define cpu_relax() asm volatile("" ::: "memory") #define CPUINFO_PROC "cpu model" #endif #ifdef __ia64__ +#define mb() asm volatile ("mf" ::: "memory") +#define wmb() asm volatile ("mf" ::: "memory") #define rmb() asm volatile ("mf" ::: "memory") #define cpu_relax() asm volatile ("hint @pause" ::: "memory") #define CPUINFO_PROC "model name" @@ -72,40 +92,55 @@ * Use the __kuser_memory_barrier helper in the CPU helper page. See * arch/arm/kernel/entry-armv.S in the kernel source for details. */ +#define mb() ((void(*)(void))0xffff0fa0)() +#define wmb() ((void(*)(void))0xffff0fa0)() #define rmb() ((void(*)(void))0xffff0fa0)() -#define cpu_relax() asm volatile("":::"memory") #define CPUINFO_PROC "Processor" #endif #ifdef __aarch64__ -#define rmb() asm volatile("dmb ld" ::: "memory") +#define mb() asm volatile("dmb ish" ::: "memory") +#define wmb() asm volatile("dmb ishld" ::: "memory") +#define rmb() asm volatile("dmb ishst" ::: "memory") #define cpu_relax() asm volatile("yield" ::: "memory") #endif #ifdef __mips__ -#define rmb() asm volatile( \ +#define mb() asm volatile( \ ".set mips2\n\t" \ "sync\n\t" \ ".set mips0" \ : /* no output */ \ : /* no input */ \ : "memory") -#define cpu_relax() asm volatile("" ::: "memory") +#define wmb() mb() +#define rmb() mb() #define CPUINFO_PROC "cpu model" #endif #ifdef __arc__ +#define mb() asm volatile("" ::: "memory") +#define wmb() asm volatile("" ::: "memory") #define rmb() asm volatile("" ::: "memory") -#define cpu_relax() rmb() #define CPUINFO_PROC "Processor" #endif #ifdef __metag__ +#define mb() asm volatile("" ::: "memory") +#define wmb() asm volatile("" ::: "memory") #define rmb() asm volatile("" ::: "memory") -#define cpu_relax() asm volatile("" ::: "memory") #define CPUINFO_PROC "CPU" #endif +#define barrier() asm volatile ("" ::: "memory") + +#ifndef cpu_relax +#define cpu_relax() barrier() +#endif + +#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x)) + + #include #include #include diff --git a/tools/perf/tests/rdpmc.c b/tools/perf/tests/rdpmc.c index ff94886..46649c2 100644 --- a/tools/perf/tests/rdpmc.c +++ b/tools/perf/tests/rdpmc.c @@ -9,8 +9,6 @@ #if defined(__x86_64__) || defined(__i386__) -#define barrier() asm volatile("" ::: "memory") - static u64 rdpmc(unsigned int counter) { unsigned int low, high; diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index e99eaed..ecaa582 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -177,7 +177,7 @@ int perf_evlist__strerror_open(struct perf_evlist *evlist, int err, char *buf, s static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm) { struct perf_event_mmap_page *pc = mm->base; - int head = pc->data_head; + int head = ACCESS_ONCE(pc->data_head); rmb(); return head; } @@ -190,7 +190,7 @@ static inline void perf_mmap__write_tail(struct perf_mmap *md, /* * ensure all reads are done before we write the tail out. */ - /* mb(); */ + mb(); pc->data_tail = tail; } -- cgit v0.10.2 From c7e548b45ce85f765f6262149dd60d9956a31d60 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Thu, 17 Oct 2013 20:24:17 +0200 Subject: perf: Factor out strncpy() in perf_event_mmap_event() While this is really minor, but strncpy() does the unnecessary zero-padding till the end of tmp[16] and it is called every time we are going to use the string literal. Turn these strncpy()'s into the single strlcpy() under the new label, saves 72 bytes. Signed-off-by: Oleg Nesterov Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20131017182417.GA17753@redhat.com Signed-off-by: Ingo Molnar diff --git a/kernel/events/core.c b/kernel/events/core.c index 17b3c6c..4dc078d 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -5144,8 +5144,8 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event) buf = kmalloc(PATH_MAX, GFP_KERNEL); if (!buf) { - name = strncpy(tmp, "//enomem", sizeof(tmp)); - goto got_name; + name = "//enomem"; + goto cpy_name; } /* * d_path() works from the end of the rb backwards, so we @@ -5154,8 +5154,8 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event) */ name = d_path(&file->f_path, buf, PATH_MAX - sizeof(u64)); if (IS_ERR(name)) { - name = strncpy(tmp, "//toolong", sizeof(tmp)); - goto got_name; + name = "//toolong"; + goto cpy_name; } inode = file_inode(vma->vm_file); dev = inode->i_sb->s_dev; @@ -5163,30 +5163,30 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event) gen = inode->i_generation; maj = MAJOR(dev); min = MINOR(dev); - + goto got_name; } else { name = (char *)arch_vma_name(vma); - if (name) { - name = strncpy(tmp, name, sizeof(tmp) - 1); - tmp[sizeof(tmp) - 1] = '\0'; - goto got_name; - } + if (name) + goto cpy_name; if (vma->vm_start <= vma->vm_mm->start_brk && vma->vm_end >= vma->vm_mm->brk) { - name = strncpy(tmp, "[heap]", sizeof(tmp)); - goto got_name; + name = "[heap]"; + goto cpy_name; } if (vma->vm_start <= vma->vm_mm->start_stack && vma->vm_end >= vma->vm_mm->start_stack) { - name = strncpy(tmp, "[stack]", sizeof(tmp)); - goto got_name; + name = "[stack]"; + goto cpy_name; } - name = strncpy(tmp, "//anon", sizeof(tmp)); - goto got_name; + name = "//anon"; + goto cpy_name; } +cpy_name: + strlcpy(tmp, name, sizeof(tmp)); + name = tmp; got_name: /* * Since our buffer works in 8 byte units we need to align our string -- cgit v0.10.2 From d1e8f4a8365d16fd179cb8cad5b4d20aafd0a695 Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Thu, 31 Oct 2013 13:36:54 +0800 Subject: perf/x86/intel/uncore: Add filter support for IvyBridge-EP QPI boxes The encoding for filter registers of IvyBridge-EP uncore QPI boxes is completely the same as SandyBridge-EP. Signed-off-by: Yan, Zheng Signed-off-by: Peter Zijlstra Cc: eranian@google.com Cc: "Yan Zheng" Link: http://lkml.kernel.org/r/1383197815-17706-1-git-send-email-zheng.z.yan@intel.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore.c b/arch/x86/kernel/cpu/perf_event_intel_uncore.c index 4118f9f..6da3999 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_uncore.c +++ b/arch/x86/kernel/cpu/perf_event_intel_uncore.c @@ -1099,6 +1099,24 @@ static struct attribute *ivt_uncore_qpi_formats_attr[] = { &format_attr_umask.attr, &format_attr_edge.attr, &format_attr_thresh8.attr, + &format_attr_match_rds.attr, + &format_attr_match_rnid30.attr, + &format_attr_match_rnid4.attr, + &format_attr_match_dnid.attr, + &format_attr_match_mc.attr, + &format_attr_match_opc.attr, + &format_attr_match_vnw.attr, + &format_attr_match0.attr, + &format_attr_match1.attr, + &format_attr_mask_rds.attr, + &format_attr_mask_rnid30.attr, + &format_attr_mask_rnid4.attr, + &format_attr_mask_dnid.attr, + &format_attr_mask_mc.attr, + &format_attr_mask_opc.attr, + &format_attr_mask_vnw.attr, + &format_attr_mask0.attr, + &format_attr_mask1.attr, NULL, }; @@ -1312,17 +1330,30 @@ static struct intel_uncore_type ivt_uncore_imc = { IVT_UNCORE_PCI_COMMON_INIT(), }; +static struct intel_uncore_ops ivt_uncore_qpi_ops = { + .init_box = ivt_uncore_pci_init_box, + .disable_box = snbep_uncore_pci_disable_box, + .enable_box = snbep_uncore_pci_enable_box, + .disable_event = snbep_uncore_pci_disable_event, + .enable_event = snbep_qpi_enable_event, + .read_counter = snbep_uncore_pci_read_counter, + .hw_config = snbep_qpi_hw_config, + .get_constraint = uncore_get_constraint, + .put_constraint = uncore_put_constraint, +}; + static struct intel_uncore_type ivt_uncore_qpi = { - .name = "qpi", - .num_counters = 4, - .num_boxes = 3, - .perf_ctr_bits = 48, - .perf_ctr = SNBEP_PCI_PMON_CTR0, - .event_ctl = SNBEP_PCI_PMON_CTL0, - .event_mask = IVT_QPI_PCI_PMON_RAW_EVENT_MASK, - .box_ctl = SNBEP_PCI_PMON_BOX_CTL, - .ops = &ivt_uncore_pci_ops, - .format_group = &ivt_uncore_qpi_format_group, + .name = "qpi", + .num_counters = 4, + .num_boxes = 3, + .perf_ctr_bits = 48, + .perf_ctr = SNBEP_PCI_PMON_CTR0, + .event_ctl = SNBEP_PCI_PMON_CTL0, + .event_mask = IVT_QPI_PCI_PMON_RAW_EVENT_MASK, + .box_ctl = SNBEP_PCI_PMON_BOX_CTL, + .num_shared_regs = 1, + .ops = &ivt_uncore_qpi_ops, + .format_group = &ivt_uncore_qpi_format_group, }; static struct intel_uncore_type ivt_uncore_r2pcie = { @@ -1429,6 +1460,16 @@ static DEFINE_PCI_DEVICE_TABLE(ivt_uncore_pci_ids) = { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xe3e), .driver_data = UNCORE_PCI_DEV_DATA(IVT_PCI_UNCORE_R3QPI, 2), }, + { /* QPI Port 0 filter */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xe86), + .driver_data = UNCORE_PCI_DEV_DATA(UNCORE_EXTRA_PCI_DEV, + SNBEP_PCI_QPI_PORT0_FILTER), + }, + { /* QPI Port 0 filter */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xe96), + .driver_data = UNCORE_PCI_DEV_DATA(UNCORE_EXTRA_PCI_DEV, + SNBEP_PCI_QPI_PORT1_FILTER), + }, { /* end: all zeroes */ } }; -- cgit v0.10.2 From f891d8cfb8372eb9cfe9d0d4ca61c75bafeaae37 Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Thu, 31 Oct 2013 13:36:55 +0800 Subject: perf/x86/intel: Add Ivy Bridge-EP uncore IRP box support Unlike other uncore boxes, IRP boxes live in PCI buses with no UBOX device. For PCI bus without UBOX device, we find the next bus that has UBOX device and use its 'bus to socket' mapping. Besides the counter/control registers in IRP boxes are not properly aligned. Signed-off-by: Yan, Zheng Signed-off-by: Peter Zijlstra Cc: eranian@google.com Cc: "Yan Zheng" Link: http://lkml.kernel.org/r/1383197815-17706-2-git-send-email-zheng.z.yan@intel.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore.c b/arch/x86/kernel/cpu/perf_event_intel_uncore.c index 6da3999..29c2487 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_uncore.c +++ b/arch/x86/kernel/cpu/perf_event_intel_uncore.c @@ -997,6 +997,20 @@ static int snbep_pci2phy_map_init(int devid) } } + if (!err) { + /* + * For PCI bus with no UBOX device, find the next bus + * that has UBOX device and use its mapping. + */ + i = -1; + for (bus = 255; bus >= 0; bus--) { + if (pcibus_to_physid[bus] >= 0) + i = pcibus_to_physid[bus]; + else + pcibus_to_physid[bus] = i; + } + } + if (ubox_dev) pci_dev_put(ubox_dev); @@ -1330,6 +1344,59 @@ static struct intel_uncore_type ivt_uncore_imc = { IVT_UNCORE_PCI_COMMON_INIT(), }; +/* registers in IRP boxes are not properly aligned */ +static unsigned ivt_uncore_irp_ctls[] = {0xd8, 0xdc, 0xe0, 0xe4}; +static unsigned ivt_uncore_irp_ctrs[] = {0xa0, 0xb0, 0xb8, 0xc0}; + +static void ivt_uncore_irp_enable_event(struct intel_uncore_box *box, struct perf_event *event) +{ + struct pci_dev *pdev = box->pci_dev; + struct hw_perf_event *hwc = &event->hw; + + pci_write_config_dword(pdev, ivt_uncore_irp_ctls[hwc->idx], + hwc->config | SNBEP_PMON_CTL_EN); +} + +static void ivt_uncore_irp_disable_event(struct intel_uncore_box *box, struct perf_event *event) +{ + struct pci_dev *pdev = box->pci_dev; + struct hw_perf_event *hwc = &event->hw; + + pci_write_config_dword(pdev, ivt_uncore_irp_ctls[hwc->idx], hwc->config); +} + +static u64 ivt_uncore_irp_read_counter(struct intel_uncore_box *box, struct perf_event *event) +{ + struct pci_dev *pdev = box->pci_dev; + struct hw_perf_event *hwc = &event->hw; + u64 count = 0; + + pci_read_config_dword(pdev, ivt_uncore_irp_ctrs[hwc->idx], (u32 *)&count); + pci_read_config_dword(pdev, ivt_uncore_irp_ctrs[hwc->idx] + 4, (u32 *)&count + 1); + + return count; +} + +static struct intel_uncore_ops ivt_uncore_irp_ops = { + .init_box = ivt_uncore_pci_init_box, + .disable_box = snbep_uncore_pci_disable_box, + .enable_box = snbep_uncore_pci_enable_box, + .disable_event = ivt_uncore_irp_disable_event, + .enable_event = ivt_uncore_irp_enable_event, + .read_counter = ivt_uncore_irp_read_counter, +}; + +static struct intel_uncore_type ivt_uncore_irp = { + .name = "irp", + .num_counters = 4, + .num_boxes = 1, + .perf_ctr_bits = 48, + .event_mask = IVT_PMON_RAW_EVENT_MASK, + .box_ctl = SNBEP_PCI_PMON_BOX_CTL, + .ops = &ivt_uncore_irp_ops, + .format_group = &ivt_uncore_format_group, +}; + static struct intel_uncore_ops ivt_uncore_qpi_ops = { .init_box = ivt_uncore_pci_init_box, .disable_box = snbep_uncore_pci_disable_box, @@ -1377,6 +1444,7 @@ static struct intel_uncore_type ivt_uncore_r3qpi = { enum { IVT_PCI_UNCORE_HA, IVT_PCI_UNCORE_IMC, + IVT_PCI_UNCORE_IRP, IVT_PCI_UNCORE_QPI, IVT_PCI_UNCORE_R2PCIE, IVT_PCI_UNCORE_R3QPI, @@ -1385,6 +1453,7 @@ enum { static struct intel_uncore_type *ivt_pci_uncores[] = { [IVT_PCI_UNCORE_HA] = &ivt_uncore_ha, [IVT_PCI_UNCORE_IMC] = &ivt_uncore_imc, + [IVT_PCI_UNCORE_IRP] = &ivt_uncore_irp, [IVT_PCI_UNCORE_QPI] = &ivt_uncore_qpi, [IVT_PCI_UNCORE_R2PCIE] = &ivt_uncore_r2pcie, [IVT_PCI_UNCORE_R3QPI] = &ivt_uncore_r3qpi, @@ -1432,6 +1501,10 @@ static DEFINE_PCI_DEVICE_TABLE(ivt_uncore_pci_ids) = { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xef1), .driver_data = UNCORE_PCI_DEV_DATA(IVT_PCI_UNCORE_IMC, 7), }, + { /* IRP */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xe39), + .driver_data = UNCORE_PCI_DEV_DATA(IVT_PCI_UNCORE_IRP, 0), + }, { /* QPI0 Port 0 */ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xe32), .driver_data = UNCORE_PCI_DEV_DATA(IVT_PCI_UNCORE_QPI, 0), -- cgit v0.10.2 From 2042abe7977222ef606306faa2dce8fd51e98e65 Mon Sep 17 00:00:00 2001 From: Vaidyanathan Srinivasan Date: Wed, 30 Oct 2013 08:42:42 +0530 Subject: sched: Fix asymmetric scheduling for POWER7 Asymmetric scheduling within a core is a scheduler loadbalancing feature that is triggered when SD_ASYM_PACKING flag is set. The goal for the load balancer is to move tasks to lower order idle SMT threads within a core on a POWER7 system. In nohz_kick_needed(), we intend to check if our sched domain (core) is completely busy or we have idle cpu. The following check for SD_ASYM_PACKING: (cpumask_first_and(nohz.idle_cpus_mask, sched_domain_span(sd)) < cpu) already covers the case of checking if the domain has an idle cpu, because cpumask_first_and() will not yield any set bits if this domain has no idle cpu. Hence, nr_busy check against group weight can be removed. Reported-by: Michael Neuling Signed-off-by: Vaidyanathan Srinivasan Signed-off-by: Preeti U Murthy Tested-by: Michael Neuling Signed-off-by: Peter Zijlstra Cc: vincent.guittot@linaro.org Cc: bitbucket@online.de Cc: benh@kernel.crashing.org Cc: anton@samba.org Cc: Morten.Rasmussen@arm.com Cc: pjt@google.com Link: http://lkml.kernel.org/r/20131030031242.23426.13019.stgit@preeti.in.ibm.com Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 41c02b6..074551a 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6800,7 +6800,7 @@ static inline int nohz_kick_needed(struct rq *rq, int cpu) if (sd->flags & SD_SHARE_PKG_RESOURCES && nr_busy > 1) goto need_kick_unlock; - if (sd->flags & SD_ASYM_PACKING && nr_busy != sg->group_weight + if (sd->flags & SD_ASYM_PACKING && (cpumask_first_and(nohz.idle_cpus_mask, sched_domain_span(sd)) < cpu)) goto need_kick_unlock; -- cgit v0.10.2 From 37dc6b50cee97954c4e6edcd5b1fa614b76038ee Mon Sep 17 00:00:00 2001 From: Preeti U Murthy Date: Wed, 30 Oct 2013 08:42:52 +0530 Subject: sched: Remove unnecessary iteration over sched domains to update nr_busy_cpus nr_busy_cpus parameter is used by nohz_kick_needed() to find out the number of busy cpus in a sched domain which has SD_SHARE_PKG_RESOURCES flag set. Therefore instead of updating nr_busy_cpus at every level of sched domain, since it is irrelevant, we can update this parameter only at the parent domain of the sd which has this flag set. Introduce a per-cpu parameter sd_busy which represents this parent domain. In nohz_kick_needed() we directly query the nr_busy_cpus parameter associated with the groups of sd_busy. By associating sd_busy with the highest domain which has SD_SHARE_PKG_RESOURCES flag set, we cover all lower level domains which could have this flag set and trigger nohz_idle_balancing if any of the levels have more than one busy cpu. sd_busy is irrelevant for asymmetric load balancing. However sd_asym has been introduced to represent the highest sched domain which has SD_ASYM_PACKING flag set so that it can be queried directly when required. While we are at it, we might as well change the nohz_idle parameter to be updated at the sd_busy domain level alone and not the base domain level of a CPU. This will unify the concept of busy cpus at just one level of sched domain where it is currently used. Signed-off-by: Preeti U Murthy Signed-off-by: Peter Zijlstra Cc: svaidy@linux.vnet.ibm.com Cc: vincent.guittot@linaro.org Cc: bitbucket@online.de Cc: benh@kernel.crashing.org Cc: anton@samba.org Cc: Morten.Rasmussen@arm.com Cc: pjt@google.com Cc: peterz@infradead.org Cc: mikey@neuling.org Link: http://lkml.kernel.org/r/20131030031252.23426.4417.stgit@preeti.in.ibm.com Signed-off-by: Ingo Molnar diff --git a/kernel/sched/core.c b/kernel/sched/core.c index aa066f3..1deccd7 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -4883,6 +4883,8 @@ DEFINE_PER_CPU(struct sched_domain *, sd_llc); DEFINE_PER_CPU(int, sd_llc_size); DEFINE_PER_CPU(int, sd_llc_id); DEFINE_PER_CPU(struct sched_domain *, sd_numa); +DEFINE_PER_CPU(struct sched_domain *, sd_busy); +DEFINE_PER_CPU(struct sched_domain *, sd_asym); static void update_top_cache_domain(int cpu) { @@ -4894,6 +4896,7 @@ static void update_top_cache_domain(int cpu) if (sd) { id = cpumask_first(sched_domain_span(sd)); size = cpumask_weight(sched_domain_span(sd)); + rcu_assign_pointer(per_cpu(sd_busy, cpu), sd->parent); } rcu_assign_pointer(per_cpu(sd_llc, cpu), sd); @@ -4902,6 +4905,9 @@ static void update_top_cache_domain(int cpu) sd = lowest_flag_domain(cpu, SD_NUMA); rcu_assign_pointer(per_cpu(sd_numa, cpu), sd); + + sd = highest_flag_domain(cpu, SD_ASYM_PACKING); + rcu_assign_pointer(per_cpu(sd_asym, cpu), sd); } /* diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 074551a..df77c60 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6534,16 +6534,16 @@ static inline void nohz_balance_exit_idle(int cpu) static inline void set_cpu_sd_state_busy(void) { struct sched_domain *sd; + int cpu = smp_processor_id(); rcu_read_lock(); - sd = rcu_dereference_check_sched_domain(this_rq()->sd); + sd = rcu_dereference(per_cpu(sd_busy, cpu)); if (!sd || !sd->nohz_idle) goto unlock; sd->nohz_idle = 0; - for (; sd; sd = sd->parent) - atomic_inc(&sd->groups->sgp->nr_busy_cpus); + atomic_inc(&sd->groups->sgp->nr_busy_cpus); unlock: rcu_read_unlock(); } @@ -6551,16 +6551,16 @@ unlock: void set_cpu_sd_state_idle(void) { struct sched_domain *sd; + int cpu = smp_processor_id(); rcu_read_lock(); - sd = rcu_dereference_check_sched_domain(this_rq()->sd); + sd = rcu_dereference(per_cpu(sd_busy, cpu)); if (!sd || sd->nohz_idle) goto unlock; sd->nohz_idle = 1; - for (; sd; sd = sd->parent) - atomic_dec(&sd->groups->sgp->nr_busy_cpus); + atomic_dec(&sd->groups->sgp->nr_busy_cpus); unlock: rcu_read_unlock(); } @@ -6767,6 +6767,8 @@ static inline int nohz_kick_needed(struct rq *rq, int cpu) { unsigned long now = jiffies; struct sched_domain *sd; + struct sched_group_power *sgp; + int nr_busy; if (unlikely(idle_cpu(cpu))) return 0; @@ -6792,22 +6794,22 @@ static inline int nohz_kick_needed(struct rq *rq, int cpu) goto need_kick; rcu_read_lock(); - for_each_domain(cpu, sd) { - struct sched_group *sg = sd->groups; - struct sched_group_power *sgp = sg->sgp; - int nr_busy = atomic_read(&sgp->nr_busy_cpus); + sd = rcu_dereference(per_cpu(sd_busy, cpu)); - if (sd->flags & SD_SHARE_PKG_RESOURCES && nr_busy > 1) - goto need_kick_unlock; + if (sd) { + sgp = sd->groups->sgp; + nr_busy = atomic_read(&sgp->nr_busy_cpus); - if (sd->flags & SD_ASYM_PACKING - && (cpumask_first_and(nohz.idle_cpus_mask, - sched_domain_span(sd)) < cpu)) + if (nr_busy > 1) goto need_kick_unlock; - - if (!(sd->flags & (SD_SHARE_PKG_RESOURCES | SD_ASYM_PACKING))) - break; } + + sd = rcu_dereference(per_cpu(sd_asym, cpu)); + + if (sd && (cpumask_first_and(nohz.idle_cpus_mask, + sched_domain_span(sd)) < cpu)) + goto need_kick_unlock; + rcu_read_unlock(); return 0; diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 4e650ac..88c85b2 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -623,6 +623,8 @@ DECLARE_PER_CPU(struct sched_domain *, sd_llc); DECLARE_PER_CPU(int, sd_llc_size); DECLARE_PER_CPU(int, sd_llc_id); DECLARE_PER_CPU(struct sched_domain *, sd_numa); +DECLARE_PER_CPU(struct sched_domain *, sd_busy); +DECLARE_PER_CPU(struct sched_domain *, sd_asym); struct sched_group_power { atomic_t ref; -- cgit v0.10.2 From 77ed16cc6c4d8ff0fe7b18c53e13826a81bd4d06 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 6 Nov 2013 17:54:02 +0100 Subject: ALSA: cmi8328: Fix compile warnings without CONFIG_PM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just add an ifdef CONFIG_PM to shut up the warnings: sound/isa/cmi8328.c:129:13: warning: ‘snd_cmi8328_cfg_save’ defined but not used [-Wunused-function] sound/isa/cmi8328.c:136:13: warning: ‘snd_cmi8328_cfg_restore’ defined but not used [-Wunused-function] Signed-off-by: Takashi Iwai diff --git a/sound/isa/cmi8328.c b/sound/isa/cmi8328.c index f84f073..ab6b2dc 100644 --- a/sound/isa/cmi8328.c +++ b/sound/isa/cmi8328.c @@ -126,6 +126,7 @@ static void snd_cmi8328_cfg_write(u16 port, u8 reg, u8 val) outb(val, port + 3); /* yes, value goes to the same port as index */ } +#ifdef CONFIG_PM static void snd_cmi8328_cfg_save(u16 port, u8 cfg[]) { cfg[0] = snd_cmi8328_cfg_read(port, CFG1); @@ -139,6 +140,7 @@ static void snd_cmi8328_cfg_restore(u16 port, u8 cfg[]) snd_cmi8328_cfg_write(port, CFG2, cfg[1]); snd_cmi8328_cfg_write(port, CFG3, cfg[2]); } +#endif /* CONFIG_PM */ static int snd_cmi8328_mixer(struct snd_wss *chip) { -- cgit v0.10.2 From 293db84270c2fc37f56030d09c43c7eb7939a7c3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 6 Nov 2013 17:54:55 +0100 Subject: ALSA: lx6464es: Fix pointer cast compile warnings The warnings are really harmless but annoying. Since they are only about debug prints, and it's at most 32bit DMA, let's just cast to unsigned long. sound/pci/lx6464es/lx6464es.c:457:22: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast] sound/pci/lx6464es/lx_core.c:1195:21: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast] Signed-off-by: Takashi Iwai diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c index 3230e57..5fcaaa6 100644 --- a/sound/pci/lx6464es/lx6464es.c +++ b/sound/pci/lx6464es/lx6464es.c @@ -453,8 +453,8 @@ static void lx_trigger_start(struct lx6464es *chip, struct lx_stream *lx_stream) lower_32_bits(buf), upper_32_bits(buf), &buffer_index); - snd_printdd(LXP "starting: buffer index %x on %p (%d bytes)\n", - buffer_index, (void *)buf, period_bytes); + snd_printdd(LXP "starting: buffer index %x on 0x%lx (%d bytes)\n", + buffer_index, (unsigned long)buf, period_bytes); buf += period_bytes; } diff --git a/sound/pci/lx6464es/lx_core.c b/sound/pci/lx6464es/lx_core.c index 633c860..626ecad 100644 --- a/sound/pci/lx6464es/lx_core.c +++ b/sound/pci/lx6464es/lx_core.c @@ -1191,8 +1191,8 @@ static int lx_interrupt_request_new_buffer(struct lx6464es *chip, unpack_pointer(buf, &buf_lo, &buf_hi); err = lx_buffer_give(chip, 0, is_capture, period_bytes, buf_lo, buf_hi, &buffer_index); - snd_printdd(LXP "interrupt: gave buffer index %x on %p (%d bytes)\n", - buffer_index, (void *)buf, period_bytes); + snd_printdd(LXP "interrupt: gave buffer index %x on 0x%lx (%d bytes)\n", + buffer_index, (unsigned long)buf, period_bytes); lx_stream->frame_pos = next_pos; spin_unlock_irqrestore(&chip->lock, flags); -- cgit v0.10.2 From 4c08edd305019061bf1ac95ce089497bdbb8b8ac Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 6 Nov 2013 10:00:05 -0800 Subject: x86, hyperv: Move a variable to avoid an unused variable warning The variable hv_lapic_frequency causes an unused variable warning if CONFIG_X86_LOCAL_APIC is disabled. Since the variable is only used inside a small if statement, move the declaration of that variable into the if statement itself. Cc: K. Y. Srinivasan Link: http://lkml.kernel.org/r/1381444224-3303-1-git-send-email-kys@microsoft.com Signed-off-by: H. Peter Anvin diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c index 9f6e9f8..9f7ca26 100644 --- a/arch/x86/kernel/cpu/mshyperv.c +++ b/arch/x86/kernel/cpu/mshyperv.c @@ -70,8 +70,6 @@ static struct clocksource hyperv_cs = { static void __init ms_hyperv_init_platform(void) { - u64 hv_lapic_frequency; - /* * Extract the features and hints */ @@ -86,6 +84,8 @@ static void __init ms_hyperv_init_platform(void) /* * Get the APIC frequency. */ + u64 hv_lapic_frequency; + rdmsrl(HV_X64_MSR_APIC_FREQUENCY, hv_lapic_frequency); hv_lapic_frequency = div_u64(hv_lapic_frequency, HZ); lapic_timer_frequency = hv_lapic_frequency; -- cgit v0.10.2 From 3820b4d2789f5166afdb136bb14f93166e6cfbc2 Mon Sep 17 00:00:00 2001 From: "David A. Long" Date: Tue, 15 Oct 2013 17:04:16 -0400 Subject: uprobes: Move function declarations out of arch Move the function declarations from the arch headers to the common header, since only the function bodies are architecture-specific. These changes are from Vincent Rabin's uprobes patch. [ oleg: update arch/powerpc/include/asm/uprobes.h ] Signed-off-by: Rabin Vincent Signed-off-by: David A. Long Signed-off-by: Oleg Nesterov diff --git a/arch/powerpc/include/asm/uprobes.h b/arch/powerpc/include/asm/uprobes.h index 2301602..b6fc317 100644 --- a/arch/powerpc/include/asm/uprobes.h +++ b/arch/powerpc/include/asm/uprobes.h @@ -45,11 +45,4 @@ struct arch_uprobe_task { unsigned long saved_trap_nr; }; -extern int arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long addr); -extern int arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs); -extern int arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs); -extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk); -extern int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data); -extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs); -extern unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, struct pt_regs *regs); #endif /* _ASM_UPROBES_H */ diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h index 6e51979..b20b4d6 100644 --- a/arch/x86/include/asm/uprobes.h +++ b/arch/x86/include/asm/uprobes.h @@ -49,11 +49,4 @@ struct arch_uprobe_task { unsigned int saved_tf; }; -extern int arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long addr); -extern int arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs); -extern int arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs); -extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk); -extern int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data); -extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs); -extern unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, struct pt_regs *regs); #endif /* _ASM_UPROBES_H */ diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index 9e0d5a6..28473e3 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -30,6 +30,7 @@ struct vm_area_struct; struct mm_struct; struct inode; +struct notifier_block; #ifdef CONFIG_ARCH_SUPPORTS_UPROBES # include @@ -125,6 +126,13 @@ extern void uprobe_notify_resume(struct pt_regs *regs); extern bool uprobe_deny_signal(void); extern bool arch_uprobe_skip_sstep(struct arch_uprobe *aup, struct pt_regs *regs); extern void uprobe_clear_state(struct mm_struct *mm); +extern int arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long addr); +extern int arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs); +extern int arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs); +extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk); +extern int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data); +extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs); +extern unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, struct pt_regs *regs); #else /* !CONFIG_UPROBES */ struct uprobes_state { }; -- cgit v0.10.2 From 736e89d9f782a7dd9a38ecda13b2db916fa72f33 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Thu, 31 Oct 2013 19:28:22 +0100 Subject: uprobes: Kill module_init() and module_exit() Turn module_init() into __initcall() and kill module_exit(). This code can't be compiled as a module so these module_*() calls only add the confusion, especially if arch-dependant code needs its own initialization hooks. Signed-off-by: Oleg Nesterov diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index ae9e1d2..0012c8e 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -1941,9 +1941,4 @@ static int __init init_uprobes(void) return register_die_notifier(&uprobe_exception_nb); } -module_init(init_uprobes); - -static void __exit exit_uprobes(void) -{ -} -module_exit(exit_uprobes); +__initcall(init_uprobes); -- cgit v0.10.2 From 8a8de66c4f6ebd0f6d3da026ec24339aa5d1db12 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Mon, 4 Nov 2013 20:27:13 +0100 Subject: uprobes: Introduce arch_uprobe->ixol Currently xol_get_insn_slot() assumes that we should simply copy arch_uprobe->insn[] which is (ignoring arch_uprobe_analyze_insn) just the copy of the original insn. This is not true for arm which needs to create another insn to execute it out-of-line. So this patch simply adds the new member, ->ixol into the union. This doesn't make any difference for x86 and powerpc, but arm can divorce insn/ixol and initialize the correct xol insn in arch_uprobe_analyze_insn(). Signed-off-by: Oleg Nesterov diff --git a/arch/powerpc/include/asm/uprobes.h b/arch/powerpc/include/asm/uprobes.h index b6fc317..75c6ecd 100644 --- a/arch/powerpc/include/asm/uprobes.h +++ b/arch/powerpc/include/asm/uprobes.h @@ -37,6 +37,7 @@ typedef ppc_opcode_t uprobe_opcode_t; struct arch_uprobe { union { u8 insn[MAX_UINSN_BYTES]; + u8 ixol[MAX_UINSN_BYTES]; u32 ainsn; }; }; diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h index b20b4d6..3087ea9 100644 --- a/arch/x86/include/asm/uprobes.h +++ b/arch/x86/include/asm/uprobes.h @@ -35,7 +35,10 @@ typedef u8 uprobe_opcode_t; struct arch_uprobe { u16 fixups; - u8 insn[MAX_UINSN_BYTES]; + union { + u8 insn[MAX_UINSN_BYTES]; + u8 ixol[MAX_UINSN_BYTES]; + }; #ifdef CONFIG_X86_64 unsigned long rip_rela_target_address; #endif diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 0012c8e..fbcff61 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -1264,7 +1264,8 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe) return 0; /* Initialize the slot */ - copy_to_page(area->page, xol_vaddr, uprobe->arch.insn, MAX_UINSN_BYTES); + copy_to_page(area->page, xol_vaddr, + uprobe->arch.ixol, sizeof(uprobe->arch.ixol)); /* * We probably need flush_icache_user_range() but it needs vma. * This should work on supported architectures too. -- cgit v0.10.2 From f72d41fa902fb19a9b63028202a400b0ce497491 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Tue, 5 Nov 2013 19:50:39 +0100 Subject: uprobes: Export write_opcode() as uprobe_write_opcode() set_swbp() and set_orig_insn() are __weak, but this is pointless because write_opcode() is static. Export write_opcode() as uprobe_write_opcode() for the upcoming arm port, this way it can actually override set_swbp() and use __opcode_to_mem_arm(bpinsn) instead if UPROBE_SWBP_INSN. Signed-off-by: Oleg Nesterov diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index 28473e3..319eae7 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -109,6 +109,7 @@ extern int __weak set_swbp(struct arch_uprobe *aup, struct mm_struct *mm, unsign extern int __weak set_orig_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr); extern bool __weak is_swbp_insn(uprobe_opcode_t *insn); extern bool __weak is_trap_insn(uprobe_opcode_t *insn); +extern int uprobe_write_opcode(struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_t); extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); extern int uprobe_apply(struct inode *inode, loff_t offset, struct uprobe_consumer *uc, bool); extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index fbcff61..0ac346a 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -245,12 +245,12 @@ static int verify_opcode(struct page *page, unsigned long vaddr, uprobe_opcode_t * the architecture. If an arch has variable length instruction and the * breakpoint instruction is not of the smallest length instruction * supported by that architecture then we need to modify is_trap_at_addr and - * write_opcode accordingly. This would never be a problem for archs that - * have fixed length instructions. + * uprobe_write_opcode accordingly. This would never be a problem for archs + * that have fixed length instructions. */ /* - * write_opcode - write the opcode at a given virtual address. + * uprobe_write_opcode - write the opcode at a given virtual address. * @mm: the probed process address space. * @vaddr: the virtual address to store the opcode. * @opcode: opcode to be written at @vaddr. @@ -261,7 +261,7 @@ static int verify_opcode(struct page *page, unsigned long vaddr, uprobe_opcode_t * For mm @mm, write the opcode at @vaddr. * Return 0 (success) or a negative errno. */ -static int write_opcode(struct mm_struct *mm, unsigned long vaddr, +int uprobe_write_opcode(struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_t opcode) { struct page *old_page, *new_page; @@ -315,7 +315,7 @@ put_old: */ int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr) { - return write_opcode(mm, vaddr, UPROBE_SWBP_INSN); + return uprobe_write_opcode(mm, vaddr, UPROBE_SWBP_INSN); } /** @@ -330,7 +330,7 @@ int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned int __weak set_orig_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr) { - return write_opcode(mm, vaddr, *(uprobe_opcode_t *)auprobe->insn); + return uprobe_write_opcode(mm, vaddr, *(uprobe_opcode_t *)auprobe->insn); } static int match_uprobe(struct uprobe *l, struct uprobe *r) @@ -577,7 +577,7 @@ static int prepare_uprobe(struct uprobe *uprobe, struct file *file, if (ret) goto out; - /* write_opcode() assumes we don't cross page boundary */ + /* uprobe_write_opcode() assumes we don't cross page boundary */ BUG_ON((uprobe->offset & ~PAGE_MASK) + UPROBE_SWBP_INSN_SIZE > PAGE_SIZE); -- cgit v0.10.2 From e363bbac316ffb5daaf45d855f82680148cafe20 Mon Sep 17 00:00:00 2001 From: Jayachandran C Date: Mon, 4 Nov 2013 17:51:54 +0530 Subject: MIPS: Netlogic: replace early_init_devtree() call The early_init_devtree() API was removed in linux-next for 3.13 with commit "mips: use early_init_dt_scan". This causes Netlogic XLP compile to fail: arch/mips/netlogic/xlp/setup.c:101: undefined reference to `early_init_devtree' Add xlp_early_init_devtree() which uses the __dt_setup_arch() to handle early device tree related initialization to fix this. Signed-off-by: Jayachandran C Signed-off-by: Rob Herring diff --git a/arch/mips/include/asm/netlogic/xlp-hal/xlp.h b/arch/mips/include/asm/netlogic/xlp-hal/xlp.h index 17daffb2..470f209 100644 --- a/arch/mips/include/asm/netlogic/xlp-hal/xlp.h +++ b/arch/mips/include/asm/netlogic/xlp-hal/xlp.h @@ -69,6 +69,7 @@ void nlm_hal_init(void); int xlp_get_dram_map(int n, uint64_t *dram_map); /* Device tree related */ +void xlp_early_init_devtree(void); void *xlp_dt_init(void *fdtp); static inline int cpu_is_xlpii(void) diff --git a/arch/mips/netlogic/xlp/dt.c b/arch/mips/netlogic/xlp/dt.c index 88df445..8316d54 100644 --- a/arch/mips/netlogic/xlp/dt.c +++ b/arch/mips/netlogic/xlp/dt.c @@ -39,8 +39,11 @@ #include #include +#include + extern u32 __dtb_xlp_evp_begin[], __dtb_xlp_svp_begin[], __dtb_xlp_fvp_begin[], __dtb_start[]; +static void *xlp_fdt_blob; void __init *xlp_dt_init(void *fdtp) { @@ -67,19 +70,26 @@ void __init *xlp_dt_init(void *fdtp) break; } } - initial_boot_params = fdtp; + xlp_fdt_blob = fdtp; return fdtp; } +void __init xlp_early_init_devtree(void) +{ + __dt_setup_arch(xlp_fdt_blob); + strlcpy(arcs_cmdline, boot_command_line, COMMAND_LINE_SIZE); +} + void __init device_tree_init(void) { unsigned long base, size; + struct boot_param_header *fdtp = xlp_fdt_blob; - if (!initial_boot_params) + if (!fdtp) return; - base = virt_to_phys((void *)initial_boot_params); - size = be32_to_cpu(initial_boot_params->totalsize); + base = virt_to_phys(fdtp); + size = be32_to_cpu(fdtp->totalsize); /* Before we do anything, lets reserve the dt blob */ reserve_bootmem(base, size, BOOTMEM_DEFAULT); diff --git a/arch/mips/netlogic/xlp/setup.c b/arch/mips/netlogic/xlp/setup.c index 76a7131..6d981bb 100644 --- a/arch/mips/netlogic/xlp/setup.c +++ b/arch/mips/netlogic/xlp/setup.c @@ -98,7 +98,7 @@ void __init plat_mem_setup(void) pm_power_off = nlm_linux_exit; /* memory and bootargs from DT */ - early_init_devtree(initial_boot_params); + xlp_early_init_devtree(); if (boot_mem_map.nr_map == 0) { pr_info("Using DRAM BARs for memory map.\n"); -- cgit v0.10.2 From 8f42d7698751a45cd9f7134a5da49bc5b6206179 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 6 Nov 2013 18:47:42 +0100 Subject: ALSA: hda - Add support for CX20952 It's a superset of the existing CX2075x codecs, so we can reuse the existing parser code. Cc: Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 993b25c..c205bb1 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -3656,6 +3656,8 @@ static const struct hda_codec_preset snd_hda_preset_conexant[] = { .patch = patch_conexant_auto }, { .id = 0x14f15115, .name = "CX20757", .patch = patch_conexant_auto }, + { .id = 0x14f151d7, .name = "CX20952", + .patch = patch_conexant_auto }, {} /* terminator */ }; @@ -3682,6 +3684,7 @@ MODULE_ALIAS("snd-hda-codec-id:14f15111"); MODULE_ALIAS("snd-hda-codec-id:14f15113"); MODULE_ALIAS("snd-hda-codec-id:14f15114"); MODULE_ALIAS("snd-hda-codec-id:14f15115"); +MODULE_ALIAS("snd-hda-codec-id:14f151d7"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Conexant HD-audio codec"); -- cgit v0.10.2 From 487a588d09db0d6508261867df208d8bdc718251 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 7 Nov 2013 07:29:30 +0100 Subject: ALSA: hda - Add pincfg fixup for ASUS W5A BIOS on ASUS W5A laptop with ALC880 codec doesn't provide any pin configurations, so we have to set up all pins manually. Reported-and-tested-by: nb Cc: [v3.4+] Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 215db60..44f1210 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1045,6 +1045,7 @@ enum { ALC880_FIXUP_UNIWILL, ALC880_FIXUP_UNIWILL_DIG, ALC880_FIXUP_Z71V, + ALC880_FIXUP_ASUS_W5A, ALC880_FIXUP_3ST_BASE, ALC880_FIXUP_3ST, ALC880_FIXUP_3ST_DIG, @@ -1215,6 +1216,26 @@ static const struct hda_fixup alc880_fixups[] = { { } } }, + [ALC880_FIXUP_ASUS_W5A] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + /* set up the whole pins as BIOS is utterly broken */ + { 0x14, 0x0121411f }, /* HP */ + { 0x15, 0x411111f0 }, /* N/A */ + { 0x16, 0x411111f0 }, /* N/A */ + { 0x17, 0x411111f0 }, /* N/A */ + { 0x18, 0x90a60160 }, /* mic */ + { 0x19, 0x411111f0 }, /* N/A */ + { 0x1a, 0x411111f0 }, /* N/A */ + { 0x1b, 0x411111f0 }, /* N/A */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + { 0x1e, 0xb743111e }, /* SPDIF out */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_GPIO1, + }, [ALC880_FIXUP_3ST_BASE] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -1336,6 +1357,7 @@ static const struct hda_fixup alc880_fixups[] = { static const struct snd_pci_quirk alc880_fixup_tbl[] = { SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_FIXUP_W810), + SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS W5A", ALC880_FIXUP_ASUS_W5A), SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_FIXUP_Z71V), SND_PCI_QUIRK_VENDOR(0x1043, "ASUS", ALC880_FIXUP_GPIO1), SND_PCI_QUIRK(0x1558, 0x5401, "Clevo GPIO2", ALC880_FIXUP_GPIO2), -- cgit v0.10.2 From 7f15a256b556bf26780d7a0bd03c88914a852022 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 6 Nov 2013 16:36:08 +0100 Subject: microblaze: Calculate kernel pad automatically The kernel needs to setup the first two tlbs with pad which is used for early page allocation which is used by mapin_ram() to allocate tables for lowmem memory before memory initialisation is done. Calculate pad directly from lowmem size. Signed-off-by: Michal Simek diff --git a/arch/microblaze/Kconfig b/arch/microblaze/Kconfig index d8ec74b..655e1ca 100644 --- a/arch/microblaze/Kconfig +++ b/arch/microblaze/Kconfig @@ -245,10 +245,6 @@ config MICROBLAZE_64K_PAGES endchoice -config KERNEL_PAD - hex "Kernel PAD for unpacking" if ADVANCED_OPTIONS - default "0x80000" if MMU - endmenu source "mm/Kconfig" diff --git a/arch/microblaze/kernel/head.S b/arch/microblaze/kernel/head.S index fcc797f..817b7ee 100644 --- a/arch/microblaze/kernel/head.S +++ b/arch/microblaze/kernel/head.S @@ -176,7 +176,7 @@ _invalidate: /* start to do TLB calculation */ addik r12, r0, _end rsub r12, r3, r12 - addik r12, r12, CONFIG_KERNEL_PAD /* that's the pad */ + addik r12, r12, CONFIG_LOWMEM_SIZE >> PTE_SHIFT /* that's the pad */ or r9, r0, r0 /* TLB0 = 0 */ or r10, r0, r0 /* TLB1 = 0 */ -- cgit v0.10.2 From ad8ff99e6beb8708b0bdefd9d5658324e90200f0 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Thu, 7 Nov 2013 09:28:59 +0100 Subject: ALSA: hda - Another Dell headset quirk This machine has a multi-function headset jack. BugLink: https://bugs.launchpad.net/bugs/1248856 Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 44f1210..daf7205 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4796,6 +4796,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE), SND_PCI_QUIRK(0x1028, 0x05d8, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05db, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x0626, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800), SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_BASS_CHMAP), SND_PCI_QUIRK(0x1043, 0x1bf3, "ASUS N76VZ", ALC662_FIXUP_BASS_CHMAP), -- cgit v0.10.2 From f44f2a5417b2968a8724b352cc0b2545a6bcb1f4 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 7 Nov 2013 10:08:22 +0100 Subject: ALSA: compress: fix drain calls blocking other compress functions (v6) The drain and drain_notify callback were blocked by low level driver until the draining was complete. Due to this being invoked with big fat mutex held, others ops like reading timestamp, calling pause, drop were blocked. So to fix this we add a new snd_compr_drain_notify() API. This would be required to be invoked by low level driver when drain or partial drain has been completed by the DSP. Thus we make the drain and partial_drain callback as non blocking and driver returns immediately after notifying DSP. The waiting is done while releasing the lock so that other ops can go ahead. [ The commit 917f4b5cba78 was wrongly applied from the preliminary patch. This commit corrects to the final version. Sorry for inconvenience! -- tiwai ] Signed-off-by: Vinod Koul CC: stable@vger.kernel.org Signed-off-by: Takashi Iwai diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h index 175ab32..ae6c3b8 100644 --- a/include/sound/compress_driver.h +++ b/include/sound/compress_driver.h @@ -48,8 +48,6 @@ struct snd_compr_ops; * the ring buffer * @total_bytes_transferred: cumulative bytes transferred by offload DSP * @sleep: poll sleep - * @wait: drain wait queue - * @drain_wake: condition for drain wake */ struct snd_compr_runtime { snd_pcm_state_t state; @@ -61,8 +59,6 @@ struct snd_compr_runtime { u64 total_bytes_available; u64 total_bytes_transferred; wait_queue_head_t sleep; - wait_queue_head_t wait; - unsigned int drain_wake; void *private_data; }; @@ -177,10 +173,11 @@ static inline void snd_compr_fragment_elapsed(struct snd_compr_stream *stream) static inline void snd_compr_drain_notify(struct snd_compr_stream *stream) { - snd_BUG_ON(!stream); + if (snd_BUG_ON(!stream)) + return; - stream->runtime->drain_wake = 1; - wake_up(&stream->runtime->wait); + stream->runtime->state = SNDRV_PCM_STATE_SETUP; + wake_up(&stream->runtime->sleep); } #endif diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 3eb47d0..d9af638 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -123,7 +123,6 @@ static int snd_compr_open(struct inode *inode, struct file *f) } runtime->state = SNDRV_PCM_STATE_OPEN; init_waitqueue_head(&runtime->sleep); - init_waitqueue_head(&runtime->wait); data->stream.runtime = runtime; f->private_data = (void *)data; mutex_lock(&compr->lock); @@ -681,8 +680,6 @@ static int snd_compr_stop(struct snd_compr_stream *stream) return -EPERM; retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP); if (!retval) { - stream->runtime->state = SNDRV_PCM_STATE_SETUP; - wake_up(&stream->runtime->sleep); snd_compr_drain_notify(stream); stream->runtime->total_bytes_available = 0; stream->runtime->total_bytes_transferred = 0; @@ -692,6 +689,8 @@ static int snd_compr_stop(struct snd_compr_stream *stream) static int snd_compress_wait_for_drain(struct snd_compr_stream *stream) { + int ret; + /* * We are called with lock held. So drop the lock while we wait for * drain complete notfication from the driver @@ -703,12 +702,24 @@ static int snd_compress_wait_for_drain(struct snd_compr_stream *stream) stream->runtime->state = SNDRV_PCM_STATE_DRAINING; mutex_unlock(&stream->device->lock); - wait_event(stream->runtime->wait, stream->runtime->drain_wake); + /* we wait for drain to complete here, drain can return when + * interruption occurred, wait returned error or success. + * For the first two cases we don't do anything different here and + * return after waking up + */ + + ret = wait_event_interruptible(stream->runtime->sleep, + (stream->runtime->state != SNDRV_PCM_STATE_DRAINING)); + if (ret == -ERESTARTSYS) + pr_debug("wait aborted by a signal"); + else if (ret) + pr_debug("wait for drain failed with %d\n", ret); + wake_up(&stream->runtime->sleep); mutex_lock(&stream->device->lock); - return 0; + return ret; } static int snd_compr_drain(struct snd_compr_stream *stream) @@ -719,17 +730,14 @@ static int snd_compr_drain(struct snd_compr_stream *stream) stream->runtime->state == SNDRV_PCM_STATE_SETUP) return -EPERM; - stream->runtime->drain_wake = 0; retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN); if (retval) { - pr_err("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval); + pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval); wake_up(&stream->runtime->sleep); return retval; } - retval = snd_compress_wait_for_drain(stream); - stream->runtime->state = SNDRV_PCM_STATE_SETUP; - return retval; + return snd_compress_wait_for_drain(stream); } static int snd_compr_next_track(struct snd_compr_stream *stream) @@ -764,10 +772,9 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream) if (stream->next_track == false) return -EPERM; - stream->runtime->drain_wake = 0; retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN); if (retval) { - pr_err("Partial drain returned failure\n"); + pr_debug("Partial drain returned failure\n"); wake_up(&stream->runtime->sleep); return retval; } -- cgit v0.10.2 From 522e66464467543c0d88d023336eec4df03ad40b Mon Sep 17 00:00:00 2001 From: Fenghua Yu Date: Wed, 23 Oct 2013 18:30:12 -0700 Subject: x86/apic: Disable I/O APIC before shutdown of the local APIC In reboot and crash path, when we shut down the local APIC, the I/O APIC is still active. This may cause issues because external interrupts can still come in and disturb the local APIC during shutdown process. To quiet external interrupts, disable I/O APIC before shutdown local APIC. Signed-off-by: Fenghua Yu Link: http://lkml.kernel.org/r/1382578212-4677-1-git-send-email-fenghua.yu@intel.com Cc: [ I suppose the 'issue' is a hang during shutdown. It's a fine change nevertheless. ] Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/crash.c b/arch/x86/kernel/crash.c index e0e0841..18677a9 100644 --- a/arch/x86/kernel/crash.c +++ b/arch/x86/kernel/crash.c @@ -127,12 +127,12 @@ void native_machine_crash_shutdown(struct pt_regs *regs) cpu_emergency_vmxoff(); cpu_emergency_svm_disable(); - lapic_shutdown(); #ifdef CONFIG_X86_IO_APIC /* Prevent crash_kexec() from deadlocking on ioapic_lock. */ ioapic_zap_locks(); disable_IO_APIC(); #endif + lapic_shutdown(); #ifdef CONFIG_HPET_TIMER hpet_disable(); #endif diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index 7e920bf..618ce26 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -550,6 +550,10 @@ static void native_machine_emergency_restart(void) void native_machine_shutdown(void) { /* Stop the cpus and apics */ +#ifdef CONFIG_X86_IO_APIC + disable_IO_APIC(); +#endif + #ifdef CONFIG_SMP /* * Stop all of the others. Also disable the local irq to @@ -562,10 +566,6 @@ void native_machine_shutdown(void) lapic_shutdown(); -#ifdef CONFIG_X86_IO_APIC - disable_IO_APIC(); -#endif - #ifdef CONFIG_HPET_TIMER hpet_disable(); #endif -- cgit v0.10.2 From bffbbc0a2ccb9f3a3235ea6c646030e5fc3d771e Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 7 Nov 2013 11:09:54 +0300 Subject: ALSA: sb16 - info leak in snd_sb_csp_ioctl() There is a 2 byte hole after "info.func_nr" so we could leak unitialized stack information to userspace. Signed-off-by: Dan Carpenter Signed-off-by: Takashi Iwai diff --git a/sound/isa/sb/sb16_csp.c b/sound/isa/sb/sb16_csp.c index c1aa21e..48da227 100644 --- a/sound/isa/sb/sb16_csp.c +++ b/sound/isa/sb/sb16_csp.c @@ -208,6 +208,7 @@ static int snd_sb_csp_ioctl(struct snd_hwdep * hw, struct file *file, unsigned i switch (cmd) { /* get information */ case SNDRV_SB_CSP_IOCTL_INFO: + memset(&info, 0, sizeof(info)); *info.codec_name = *p->codec_name; info.func_nr = p->func_nr; info.acc_format = p->acc_format; -- cgit v0.10.2 From 3b098eb486868d57d7f2666d05b86c19a07df71b Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Thu, 7 Nov 2013 11:14:29 +0800 Subject: ALSA: include/uapi/sound/firewire.h: use "_UAPI" instead of "UAPI" When installing, "scripts/headers_install.sh" will strip guard macro' "_UAPI" to prevent from appearing it to users. And also, all another files which need uapi prefix always use "_UAPI", not "UAPI". So use "_UAPI" instead of "UAPI" on the guard macro, and also give a comment for "#endif". Signed-off-by: Chen Gang Acked-by: Clemens Ladisch Signed-off-by: Takashi Iwai diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h index e86131c..59f5961 100644 --- a/include/uapi/sound/firewire.h +++ b/include/uapi/sound/firewire.h @@ -1,5 +1,5 @@ -#ifndef UAPI_SOUND_FIREWIRE_H_INCLUDED -#define UAPI_SOUND_FIREWIRE_H_INCLUDED +#ifndef _UAPI_SOUND_FIREWIRE_H_INCLUDED +#define _UAPI_SOUND_FIREWIRE_H_INCLUDED #include @@ -48,4 +48,4 @@ struct snd_firewire_get_info { * Returns -EBUSY if the driver is already streaming. */ -#endif +#endif /* _UAPI_SOUND_FIREWIRE_H_INCLUDED */ -- cgit v0.10.2 From 5702941eec32cfd7b8cf9e36a0936e48170011a4 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 5 Jul 2013 00:31:36 +0800 Subject: irqchip: bcm2835: Convert to use IRQCHIP_DECLARE macro This patch converts irq-bcm2835 driver to use the new IRQCHIP_DECLARE and irqchip_init. Signed-off-by: Axel Lin Tested-by: Stephen Warren Cc: Simon Arlott Cc: Olof Johansson Cc: Arnd Bergmann Cc: linux-rpi-kernel@lists.infradead.org Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Thomas Gleixner diff --git a/arch/arm/mach-bcm2835/bcm2835.c b/arch/arm/mach-bcm2835/bcm2835.c index 40686d7..a4abd85 100644 --- a/arch/arm/mach-bcm2835/bcm2835.c +++ b/arch/arm/mach-bcm2835/bcm2835.c @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include #include @@ -131,8 +131,7 @@ static const char * const bcm2835_compat[] = { DT_MACHINE_START(BCM2835, "BCM2835") .map_io = bcm2835_map_io, - .init_irq = bcm2835_init_irq, - .handle_irq = bcm2835_handle_irq, + .init_irq = irqchip_init, .init_machine = bcm2835_init, .init_time = clocksource_of_init, .restart = bcm2835_restart, diff --git a/drivers/irqchip/irq-bcm2835.c b/drivers/irqchip/irq-bcm2835.c index 16c78f1..1693b8e 100644 --- a/drivers/irqchip/irq-bcm2835.c +++ b/drivers/irqchip/irq-bcm2835.c @@ -49,9 +49,11 @@ #include #include #include -#include #include +#include + +#include "irqchip.h" /* Put the bank and irq (32 bits) into the hwirq */ #define MAKE_HWIRQ(b, n) ((b << 5) | (n)) @@ -93,6 +95,8 @@ struct armctrl_ic { }; static struct armctrl_ic intc __read_mostly; +static asmlinkage void __exception_irq_entry bcm2835_handle_irq( + struct pt_regs *regs); static void armctrl_mask_irq(struct irq_data *d) { @@ -164,17 +168,9 @@ static int __init armctrl_of_init(struct device_node *node, set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); } } - return 0; -} - -static struct of_device_id irq_of_match[] __initconst = { - { .compatible = "brcm,bcm2835-armctrl-ic", .data = armctrl_of_init }, - { } -}; -void __init bcm2835_init_irq(void) -{ - of_irq_init(irq_of_match); + set_handle_irq(bcm2835_handle_irq); + return 0; } /* @@ -200,7 +196,7 @@ static void armctrl_handle_shortcut(int bank, struct pt_regs *regs, handle_IRQ(irq_linear_revmap(intc.domain, irq), regs); } -asmlinkage void __exception_irq_entry bcm2835_handle_irq( +static asmlinkage void __exception_irq_entry bcm2835_handle_irq( struct pt_regs *regs) { u32 stat, irq; @@ -222,3 +218,5 @@ asmlinkage void __exception_irq_entry bcm2835_handle_irq( } } } + +IRQCHIP_DECLARE(bcm2835_armctrl_ic, "brcm,bcm2835-armctrl-ic", armctrl_of_init); diff --git a/include/linux/irqchip/bcm2835.h b/include/linux/irqchip/bcm2835.h deleted file mode 100644 index 48a859b..0000000 --- a/include/linux/irqchip/bcm2835.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2010 Broadcom - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __LINUX_IRQCHIP_BCM2835_H_ -#define __LINUX_IRQCHIP_BCM2835_H_ - -#include - -extern void bcm2835_init_irq(void); - -extern asmlinkage void __exception_irq_entry bcm2835_handle_irq( - struct pt_regs *regs); - -#endif -- cgit v0.10.2 From a894bd7fb539d671149fea9420c94c0fbe6baf7a Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 6 Nov 2013 10:16:20 +0000 Subject: ASoC: generic-dmaengine-pcm: Clear slave_config memory We currently assume that the DMA Slave Config will be fully populated by the platform, however some DMA Engines make decisions based on zero (default) flags such as DMA_SLAVE_BUSWIDTH_UNDEFINED and as this is a static declaration we need to memset it to clear the data area. Signed-off-by: Lee Jones Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index ee07903..6ad4c7a 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -90,6 +90,8 @@ static int dmaengine_pcm_hw_params(struct snd_pcm_substream *substream, struct dma_slave_config slave_config; int ret; + memset(&slave_config, 0, sizeof(slave_config)); + if (!pcm->config) prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config; else -- cgit v0.10.2 From efe4710860fa6ed10dd041f13902f0e06c86e8cc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 7 Nov 2013 13:38:23 +0100 Subject: ALSA: hda - Delay HDMI presence reports while waiting for ELD information There is a small gap between the jack detection unsolicited event and the time the ELD is updated. When user-space queries the HDMI ELD immediately after receiving the notification, it might fail because of this gap. For avoiding such a problem, this patch tries to delay the HDMI jack detect notification until ELD information is fully updated. The workaround is imperfect, but good enough as a starting point. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index e22323f..ce412d1 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1140,7 +1140,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, * Unsolicited events */ -static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll); +static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll); static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) { @@ -1166,8 +1166,8 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) if (pin_idx < 0) return; - hdmi_present_sense(get_pin(spec, pin_idx), 1); - snd_hda_jack_report_sync(codec); + if (hdmi_present_sense(get_pin(spec, pin_idx), 1)) + snd_hda_jack_report_sync(codec); } static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) @@ -1475,7 +1475,7 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx) return 0; } -static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) +static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) { struct hda_codec *codec = per_pin->codec; struct hdmi_spec *spec = codec->spec; @@ -1493,6 +1493,7 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) int present = snd_hda_pin_sense(codec, pin_nid); bool update_eld = false; bool eld_changed = false; + bool ret; mutex_lock(&per_pin->lock); pin_eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE); @@ -1559,7 +1560,12 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, &per_pin->eld_ctl->id); unlock: + if ((codec->vendor_id & 0xffff0000) == 0x10020000) + ret = true; /* AMD codecs create ELD by itself */ + else + ret = !repoll || !pin_eld->monitor_present || pin_eld->eld_valid; mutex_unlock(&per_pin->lock); + return ret; } static void hdmi_repoll_eld(struct work_struct *work) @@ -1570,7 +1576,8 @@ static void hdmi_repoll_eld(struct work_struct *work) if (per_pin->repoll_count++ > 6) per_pin->repoll_count = 0; - hdmi_present_sense(per_pin, per_pin->repoll_count); + if (hdmi_present_sense(per_pin, per_pin->repoll_count)) + snd_hda_jack_report_sync(per_pin->codec); } static void intel_haswell_fixup_connect_list(struct hda_codec *codec, -- cgit v0.10.2 From 0f568959de1c90be3f9ec51c271a40d7583f5809 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Thu, 7 Nov 2013 13:38:24 +0100 Subject: ALSA: hda - Add a block_report flag to jacks If the jack should not be reported to userspace (e g, because it is in some transitional state), one can set this flag. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 05b3e3e..afe5944 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -286,7 +286,7 @@ void snd_hda_jack_report_sync(struct hda_codec *codec) jack = codec->jacktbl.list; for (i = 0; i < codec->jacktbl.used; i++, jack++) if (jack->nid) { - if (!jack->kctl) + if (!jack->kctl || jack->block_report) continue; state = get_jack_plug_state(jack->pin_sense); snd_kctl_jack_report(codec->bus->card, jack->kctl, state); diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index 379420c..46e1ea8 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -28,6 +28,7 @@ struct hda_jack_tbl { unsigned int jack_detect:1; /* capable of jack-detection? */ unsigned int jack_dirty:1; /* needs to update? */ unsigned int phantom_jack:1; /* a fixed, always present port? */ + unsigned int block_report:1; /* in a transitional state - do not report to userspace */ hda_nid_t gating_jack; /* valid when gating jack plugged */ hda_nid_t gated_jack; /* gated is dependent on this jack */ struct snd_kcontrol *kctl; /* assigned kctl for jack-detection */ -- cgit v0.10.2 From 464837a7bc0a3495e3490e3bf85099bb2300efbd Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Thu, 7 Nov 2013 13:38:25 +0100 Subject: ALSA: hda - block HDMI jack reports while repolling This fixes a race condition in case several monitors are being repolled in parallel. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index ce412d1..a96403a 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1477,6 +1477,7 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx) static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) { + struct hda_jack_tbl *jack; struct hda_codec *codec = per_pin->codec; struct hdmi_spec *spec = codec->spec; struct hdmi_eld *eld = &spec->temp_eld; @@ -1564,6 +1565,11 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) ret = true; /* AMD codecs create ELD by itself */ else ret = !repoll || !pin_eld->monitor_present || pin_eld->eld_valid; + + jack = snd_hda_jack_tbl_get(codec, pin_nid); + if (jack) + jack->block_report = !ret; + mutex_unlock(&per_pin->lock); return ret; } -- cgit v0.10.2 From 744a971940520cf0818e1fe882b64892c528e6de Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 6 Nov 2013 10:17:38 -0300 Subject: perf evsel: Ditch evsel->handler.data field Not needed since this cset: fcf65bf149af: perf evsel: Cache associated event_format So lets trim this struct a bit. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-j8setslokt0goiwxq9dogzqm@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 409ceaf..6a25085 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -106,8 +106,8 @@ static int perf_event__repipe_sample(struct perf_tool *tool, struct perf_evsel *evsel, struct machine *machine) { - if (evsel->handler.func) { - inject_handler f = evsel->handler.func; + if (evsel->handler) { + inject_handler f = evsel->handler; return f(tool, event, sample, evsel, machine); } @@ -383,11 +383,11 @@ static int __cmd_inject(struct perf_inject *inject) if (perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID")) return -EINVAL; - evsel->handler.func = perf_inject__sched_switch; + evsel->handler = perf_inject__sched_switch; } else if (!strcmp(name, "sched:sched_process_exit")) - evsel->handler.func = perf_inject__sched_process_exit; + evsel->handler = perf_inject__sched_process_exit; else if (!strncmp(name, "sched:sched_stat_", 17)) - evsel->handler.func = perf_inject__sched_stat; + evsel->handler = perf_inject__sched_stat; } } diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index a28970f..929462a 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -317,8 +317,8 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid); - if (evsel->handler.func != NULL) { - tracepoint_handler f = evsel->handler.func; + if (evsel->handler != NULL) { + tracepoint_handler f = evsel->handler; return f(evsel, sample); } diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 35f9aaa..c852c7a 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -819,8 +819,8 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, return -1; } - if (evsel->handler.func != NULL) { - tracepoint_handler f = evsel->handler.func; + if (evsel->handler != NULL) { + tracepoint_handler f = evsel->handler; return f(evsel, sample); } diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index a81ab18..0f3c6551 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1427,8 +1427,8 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool __maybe_ evsel->hists.stats.total_period += sample->period; hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); - if (evsel->handler.func != NULL) { - tracepoint_handler f = evsel->handler.func; + if (evsel->handler != NULL) { + tracepoint_handler f = evsel->handler; err = f(tool, evsel, sample, machine); } diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index e11c61d..41c9bde 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -483,8 +483,8 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, if (sample->cpu > numcpus) numcpus = sample->cpu; - if (evsel->handler.func != NULL) { - tracepoint_handler f = evsel->handler.func; + if (evsel->handler != NULL) { + tracepoint_handler f = evsel->handler; return f(evsel, sample); } diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index b3e57dc..ee59df3 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1570,7 +1570,7 @@ static int trace__process_sample(struct perf_tool *tool, struct trace *trace = container_of(tool, struct trace, tool); int err = 0; - tracepoint_handler handler = evsel->handler.func; + tracepoint_handler handler = evsel->handler; if (skip_sample(trace, sample)) return 0; @@ -1656,7 +1656,7 @@ static void perf_evlist__add_vfs_getname(struct perf_evlist *evlist) return; } - evsel->handler.func = trace__vfs_getname; + evsel->handler = trace__vfs_getname; perf_evlist__add(evlist, evsel); } @@ -1768,7 +1768,7 @@ again: goto next_event; } - handler = evsel->handler.func; + handler = evsel->handler; handler(trace, evsel, &sample); next_event: perf_evlist__mmap_consume(evlist, i); diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 1c173cc..b939221 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -255,7 +255,7 @@ int perf_evlist__add_newtp(struct perf_evlist *evlist, if (evsel == NULL) return -1; - evsel->handler.func = handler; + evsel->handler = handler; perf_evlist__add(evlist, evsel); return 0; } diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 5aa68cd..64ec8e1 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -74,10 +74,7 @@ struct perf_evsel { off_t id_offset; }; struct cgroup_sel *cgrp; - struct { - void *func; - void *data; - } handler; + void *handler; struct cpu_map *cpus; unsigned int sample_size; int id_pos; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 0ce4694..f36d24a 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1650,9 +1650,9 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session, continue; err = -EEXIST; - if (evsel->handler.func != NULL) + if (evsel->handler != NULL) goto out; - evsel->handler.func = assocs[i].handler; + evsel->handler = assocs[i].handler; } err = 0; -- cgit v0.10.2 From a614d01bdd0cc8200d917da25f5a3d539b944193 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 6 Nov 2013 08:55:35 -0700 Subject: perf tools: Fix version when building out of tree When building perf out of tree: $ make perf-tar-src-pkg $ tar -xf perf-.tar -C /tmp $ cd /tmp/perf $ make -C tools/perf you get this warning message: make[1]: *** No rule to make target `kernelversion'. Stop. Fix it by saving the perf version in the tar file and using that for the out of tree builds. v2: removed short form request and fixed up version string from usual output. Signed-off-by: David Ahern Suggested-by: Ingo Molnar Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Link: http://lkml.kernel.org/r/1383753335-25782-1-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/scripts/package/Makefile b/scripts/package/Makefile index a4f31c9..c5d4733 100644 --- a/scripts/package/Makefile +++ b/scripts/package/Makefile @@ -115,7 +115,9 @@ git --git-dir=$(srctree)/.git archive --prefix=$(perf-tar)/ \ -o $(perf-tar).tar; \ mkdir -p $(perf-tar); \ git --git-dir=$(srctree)/.git rev-parse HEAD > $(perf-tar)/HEAD; \ -tar rf $(perf-tar).tar $(perf-tar)/HEAD; \ +(cd $(srctree)/tools/perf; \ +util/PERF-VERSION-GEN ../../$(perf-tar)/ 2>/dev/null); \ +tar rf $(perf-tar).tar $(perf-tar)/HEAD $(perf-tar)/PERF-VERSION-FILE; \ rm -r $(perf-tar); \ $(if $(findstring tar-src,$@),, \ $(if $(findstring bz2,$@),bzip2, \ diff --git a/tools/perf/util/PERF-VERSION-GEN b/tools/perf/util/PERF-VERSION-GEN index ce7a804..39f1750 100755 --- a/tools/perf/util/PERF-VERSION-GEN +++ b/tools/perf/util/PERF-VERSION-GEN @@ -19,6 +19,9 @@ if test -d ../../.git -o -f ../../.git then TAG=$(git describe --abbrev=0 --match "v[0-9].[0-9]*" 2>/dev/null ) CID=$(git log -1 --abbrev=4 --pretty=format:"%h" 2>/dev/null) && CID="-g$CID" +elif test -f ../../PERF-VERSION-FILE +then + TAG=$(cut -d' ' -f3 ../../PERF-VERSION-FILE | sed -e 's/\"//g') fi if test -z "$TAG" then -- cgit v0.10.2 From 77170988ff67fb959602ab4df296ae676f556a59 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 6 Nov 2013 16:35:57 -0300 Subject: perf trace: Don't relookup fields by name in each sample Instead do the lookups just when creating the tracepoints, initially for the most common, raw_syscalls:sys_{enter,exit}. It works by having evsel->priv have a per tracepoint structure with entries for the fields, for direct access, with the offset and a function to get the value from the sample, doing the swap if needed. Using a simple workload that does M millions write syscalls, we go from: # perf stat -i -e cycles /tmp/oldperf trace ./sc_hello 100 > /dev/null Performance counter stats for '/tmp/oldperf trace ./sc_hello 100': 8,366,771,459 cycles 2.668025928 seconds time elapsed # perf stat -i -e cycles perf trace ./sc_hello 100 > /dev/null Performance counter stats for 'perf trace ./sc_hello 100': 8,345,187,650 cycles 2.631748425 seconds time elapsed Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-eyfhvoo510a5i10b27dnvm88@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index ee59df3..329b783 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -35,6 +35,189 @@ # define MADV_UNMERGEABLE 13 #endif +struct tp_field { + int offset; + union { + u64 (*integer)(struct tp_field *field, struct perf_sample *sample); + void *(*pointer)(struct tp_field *field, struct perf_sample *sample); + }; +}; + +#define TP_UINT_FIELD(bits) \ +static u64 tp_field__u##bits(struct tp_field *field, struct perf_sample *sample) \ +{ \ + return *(u##bits *)(sample->raw_data + field->offset); \ +} + +TP_UINT_FIELD(8); +TP_UINT_FIELD(16); +TP_UINT_FIELD(32); +TP_UINT_FIELD(64); + +#define TP_UINT_FIELD__SWAPPED(bits) \ +static u64 tp_field__swapped_u##bits(struct tp_field *field, struct perf_sample *sample) \ +{ \ + u##bits value = *(u##bits *)(sample->raw_data + field->offset); \ + return bswap_##bits(value);\ +} + +TP_UINT_FIELD__SWAPPED(16); +TP_UINT_FIELD__SWAPPED(32); +TP_UINT_FIELD__SWAPPED(64); + +static int tp_field__init_uint(struct tp_field *field, + struct format_field *format_field, + bool needs_swap) +{ + field->offset = format_field->offset; + + switch (format_field->size) { + case 1: + field->integer = tp_field__u8; + break; + case 2: + field->integer = needs_swap ? tp_field__swapped_u16 : tp_field__u16; + break; + case 4: + field->integer = needs_swap ? tp_field__swapped_u32 : tp_field__u32; + break; + case 8: + field->integer = needs_swap ? tp_field__swapped_u64 : tp_field__u64; + break; + default: + return -1; + } + + return 0; +} + +static void *tp_field__ptr(struct tp_field *field, struct perf_sample *sample) +{ + return sample->raw_data + field->offset; +} + +static int tp_field__init_ptr(struct tp_field *field, struct format_field *format_field) +{ + field->offset = format_field->offset; + field->pointer = tp_field__ptr; + return 0; +} + +struct syscall_tp { + struct tp_field id; + union { + struct tp_field args, ret; + }; +}; + +static int perf_evsel__init_tp_uint_field(struct perf_evsel *evsel, + struct tp_field *field, + const char *name) +{ + struct format_field *format_field = perf_evsel__field(evsel, name); + + if (format_field == NULL) + return -1; + + return tp_field__init_uint(field, format_field, evsel->needs_swap); +} + +#define perf_evsel__init_sc_tp_uint_field(evsel, name) \ + ({ struct syscall_tp *sc = evsel->priv;\ + perf_evsel__init_tp_uint_field(evsel, &sc->name, #name); }) + +static int perf_evsel__init_tp_ptr_field(struct perf_evsel *evsel, + struct tp_field *field, + const char *name) +{ + struct format_field *format_field = perf_evsel__field(evsel, name); + + if (format_field == NULL) + return -1; + + return tp_field__init_ptr(field, format_field); +} + +#define perf_evsel__init_sc_tp_ptr_field(evsel, name) \ + ({ struct syscall_tp *sc = evsel->priv;\ + perf_evsel__init_tp_ptr_field(evsel, &sc->name, #name); }) + +static void perf_evsel__delete_priv(struct perf_evsel *evsel) +{ + free(evsel->priv); + evsel->priv = NULL; + perf_evsel__delete(evsel); +} + +static struct perf_evsel *perf_evsel__syscall_newtp(const char *direction, + void *handler, int idx) +{ + struct perf_evsel *evsel = perf_evsel__newtp("raw_syscalls", direction, idx); + + if (evsel) { + evsel->priv = malloc(sizeof(struct syscall_tp)); + + if (evsel->priv == NULL) + goto out_delete; + + if (perf_evsel__init_sc_tp_uint_field(evsel, id)) + goto out_delete; + + evsel->handler = handler; + } + + return evsel; + +out_delete: + perf_evsel__delete_priv(evsel); + return NULL; +} + +#define perf_evsel__sc_tp_uint(evsel, name, sample) \ + ({ struct syscall_tp *fields = evsel->priv; \ + fields->name.integer(&fields->name, sample); }) + +#define perf_evsel__sc_tp_ptr(evsel, name, sample) \ + ({ struct syscall_tp *fields = evsel->priv; \ + fields->name.pointer(&fields->name, sample); }) + +static int perf_evlist__add_syscall_newtp(struct perf_evlist *evlist, + void *sys_enter_handler, + void *sys_exit_handler) +{ + int ret = -1; + int idx = evlist->nr_entries; + struct perf_evsel *sys_enter, *sys_exit; + + sys_enter = perf_evsel__syscall_newtp("sys_enter", sys_enter_handler, idx++); + if (sys_enter == NULL) + goto out; + + if (perf_evsel__init_sc_tp_ptr_field(sys_enter, args)) + goto out_delete_sys_enter; + + sys_exit = perf_evsel__syscall_newtp("sys_exit", sys_exit_handler, idx++); + if (sys_exit == NULL) + goto out_delete_sys_enter; + + if (perf_evsel__init_sc_tp_uint_field(sys_exit, ret)) + goto out_delete_sys_exit; + + perf_evlist__add(evlist, sys_enter); + perf_evlist__add(evlist, sys_exit); + + ret = 0; +out: + return ret; + +out_delete_sys_exit: + perf_evsel__delete_priv(sys_exit); +out_delete_sys_enter: + perf_evsel__delete_priv(sys_enter); + goto out; +} + + struct syscall_arg { unsigned long val; struct thread *thread; @@ -1392,7 +1575,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, void *args; size_t printed = 0; struct thread *thread; - int id = perf_evsel__intval(evsel, sample, "id"); + int id = perf_evsel__sc_tp_uint(evsel, id, sample); struct syscall *sc = trace__syscall_info(trace, evsel, id); struct thread_trace *ttrace; @@ -1407,12 +1590,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, if (ttrace == NULL) return -1; - args = perf_evsel__rawptr(evsel, sample, "args"); - if (args == NULL) { - fprintf(trace->output, "Problems reading syscall arguments\n"); - return -1; - } - + args = perf_evsel__sc_tp_ptr(evsel, args, sample); ttrace = thread->priv; if (ttrace->entry_str == NULL) { @@ -1445,7 +1623,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, int ret; u64 duration = 0; struct thread *thread; - int id = perf_evsel__intval(evsel, sample, "id"); + int id = perf_evsel__sc_tp_uint(evsel, id, sample); struct syscall *sc = trace__syscall_info(trace, evsel, id); struct thread_trace *ttrace; @@ -1463,7 +1641,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, if (trace->summary) thread__update_stats(ttrace, id, sample); - ret = perf_evsel__intval(evsel, sample, "ret"); + ret = perf_evsel__sc_tp_uint(evsel, ret, sample); if (id == trace->audit.open_id && ret >= 0 && trace->last_vfs_getname) { trace__set_fd_pathname(thread, ret, trace->last_vfs_getname); @@ -1675,8 +1853,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv) goto out; } - if (perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_enter", trace__sys_enter) || - perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_exit", trace__sys_exit)) + if (perf_evlist__add_syscall_newtp(evlist, trace__sys_enter, trace__sys_exit)) goto out_error_tp; perf_evlist__add_vfs_getname(evlist); -- cgit v0.10.2 From 57706abc19afc60f0b629af839d2ebee17739f59 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 6 Nov 2013 11:41:34 -0700 Subject: perf record: Refactor feature handling into a separate function Code move only. No logic changes. Signed-off-by: David Ahern Acked-by: Ingo Molnar Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1383763297-27066-2-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index ea4c04f..2932069 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -342,9 +342,28 @@ out: return rc; } +static void perf_record__init_features(struct perf_record *rec) +{ + struct perf_evlist *evsel_list = rec->evlist; + struct perf_session *session = rec->session; + int feat; + + for (feat = HEADER_FIRST_FEATURE; feat < HEADER_LAST_FEATURE; feat++) + perf_header__set_feat(&session->header, feat); + + if (rec->no_buildid) + perf_header__clear_feat(&session->header, HEADER_BUILD_ID); + + if (!have_tracepoints(&evsel_list->entries)) + perf_header__clear_feat(&session->header, HEADER_TRACING_DATA); + + if (!rec->opts.branch_stack) + perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK); +} + static int __cmd_record(struct perf_record *rec, int argc, const char **argv) { - int err, feat; + int err; unsigned long waking = 0; const bool forks = argc > 0; struct machine *machine; @@ -371,17 +390,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) rec->session = session; - for (feat = HEADER_FIRST_FEATURE; feat < HEADER_LAST_FEATURE; feat++) - perf_header__set_feat(&session->header, feat); - - if (rec->no_buildid) - perf_header__clear_feat(&session->header, HEADER_BUILD_ID); - - if (!have_tracepoints(&evsel_list->entries)) - perf_header__clear_feat(&session->header, HEADER_TRACING_DATA); - - if (!rec->opts.branch_stack) - perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK); + perf_record__init_features(rec); if (forks) { err = perf_evlist__prepare_workload(evsel_list, &opts->target, -- cgit v0.10.2 From f34b9001f9a2f6fa41d3582fe515d194cc86bfb2 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 6 Nov 2013 11:41:35 -0700 Subject: perf record: Remove advance_output function 1 line function with only 1 user; might as well embed directly. Signed-off-by: David Ahern Suggested-by: Ingo Molnar Acked-by: Ingo Molnar Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1383763297-27066-3-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 2932069..19c4db6 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -77,11 +77,6 @@ struct perf_record { off_t post_processing_offset; }; -static void advance_output(struct perf_record *rec, size_t size) -{ - rec->bytes_written += size; -} - static int write_output(struct perf_record *rec, void *buf, size_t size) { struct perf_data_file *file = &rec->file; @@ -461,7 +456,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) pr_err("Couldn't record tracing data.\n"); goto out_delete_session; } - advance_output(rec, err); + rec->bytes_written += err; } } -- cgit v0.10.2 From 7ab75cffd6a1b2195944b8522673522f09e7fcb0 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 6 Nov 2013 11:41:36 -0700 Subject: perf record: Remove post_processing_offset variable Duplicates the data_offset from header in the session. Signed-off-by: David Ahern Acked-by: Ingo Molnar Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1383763297-27066-4-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 19c4db6..15280b5 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -74,7 +74,6 @@ struct perf_record { bool no_buildid; bool no_buildid_cache; long samples; - off_t post_processing_offset; }; static int write_output(struct perf_record *rec, void *buf, size_t size) @@ -247,13 +246,14 @@ static int process_buildids(struct perf_record *rec) { struct perf_data_file *file = &rec->file; struct perf_session *session = rec->session; + u64 start = session->header.data_offset; u64 size = lseek(file->fd, 0, SEEK_CUR); if (size == 0) return 0; - return __perf_session__process_events(session, rec->post_processing_offset, - size - rec->post_processing_offset, + return __perf_session__process_events(session, start, + size - start, size, &build_id__mark_dso_hit_ops); } @@ -429,8 +429,6 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) goto out_delete_session; } - rec->post_processing_offset = lseek(file->fd, 0, SEEK_CUR); - machine = &session->machines.host; if (file->is_pipe) { -- cgit v0.10.2 From 512a4cb9e7285e6ef8104da808dc85806321aec8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 7 Nov 2013 15:44:11 +0100 Subject: ALSA: ice1724: Fix compile warning with CONFIG_PROC_FS=n Just added a missing ifdef: sound/pci/ice1712/quartet.c:210:14: warning: 'get_binary' defined but not used [-Wunused-function] Signed-off-by: Takashi Iwai diff --git a/sound/pci/ice1712/quartet.c b/sound/pci/ice1712/quartet.c index 975e035..71c6003 100644 --- a/sound/pci/ice1712/quartet.c +++ b/sound/pci/ice1712/quartet.c @@ -203,6 +203,7 @@ static const char * const ext_clock_names[3] = {"IEC958 In", "Word Clock 1xFS", #define AK4620_DEEMVOL_REG 0x03 #define AK4620_SMUTE (1<<7) +#ifdef CONFIG_PROC_FS /* * Conversion from int value to its binary form. Used for debugging. * The output buffer must be allocated prior to calling the function. @@ -227,6 +228,7 @@ static char *get_binary(char *buffer, int value) buffer[pos] = '\0'; return buffer; } +#endif /* CONFIG_PROC_FS */ /* * Initial setup of the conversion array GPIO <-> rate -- cgit v0.10.2 From 8ce000e83848578a621d64eccdc88bd34c2fc70c Mon Sep 17 00:00:00 2001 From: Rodrigo Campos Date: Wed, 6 Nov 2013 22:20:54 +0000 Subject: perf tools: Remove unneeded include There is no point in sort.h including itself. The include was added when the file was created, in commit "perf tools: Create util/sort.and use it" (dd68ada2d) and added a include to "sort.h" in lot of files (all the files that started using the file). It was probably added by mistake on sort.h too. Signed-off-by: Rodrigo Campos Acked-by: Namhyung Kim Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1383776454-10595-1-git-send-email-rodrigo@sdfg.com.ar Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index f4cc147..43e5ff4 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -22,7 +22,6 @@ #include "parse-events.h" #include "thread.h" -#include "sort.h" extern regex_t parent_regex; extern const char *sort_order; -- cgit v0.10.2 From aff747ebbf68c6efc4d091d54058af89b916f82d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 7 Nov 2013 16:39:37 +0100 Subject: ALSA: hda - Get rid of AMD HDMI exception in hdmi_present_sense() Since the recent fake ELD patches, we can remove the check for AMD HDMI in hdmi_present_sense() and decide the return value from eld_valid value. Suggested by Anssi Hannula. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index a96403a..e687923 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1561,10 +1561,7 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, &per_pin->eld_ctl->id); unlock: - if ((codec->vendor_id & 0xffff0000) == 0x10020000) - ret = true; /* AMD codecs create ELD by itself */ - else - ret = !repoll || !pin_eld->monitor_present || pin_eld->eld_valid; + ret = !repoll || !pin_eld->monitor_present || pin_eld->eld_valid; jack = snd_hda_jack_tbl_get(codec, pin_nid); if (jack) -- cgit v0.10.2 From 74dac2ed699cbe1dee0e4e7891619d53a5f2632f Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Tue, 5 Nov 2013 16:21:18 +0100 Subject: of: irq: Fix interrupt-map entry matching This patch fixes interrupt-map entry matching code to properly match all specifier cells with interrupt map entries. Signed-off-by: Tomasz Figa Tested-by: Sachin Kamat Signed-off-by: Rob Herring diff --git a/drivers/of/irq.c b/drivers/of/irq.c index d385bb8..786b0b4 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -199,7 +199,7 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) /* Compare specifiers */ match = 1; for (i = 0; i < (addrsize + intsize); i++, imaplen--) - match = !((match_array[i] ^ *imap++) & imask[i]); + match &= !((match_array[i] ^ *imap++) & imask[i]); pr_debug(" -> match=%d (imaplen=%d)\n", match, imaplen); -- cgit v0.10.2 From aeeca404266c8ed2997905afc342ba9561f49ca7 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Thu, 7 Nov 2013 13:34:50 -0500 Subject: x86, intel-mid: Do not re-introduce usage of obsolete __cpuinit The commit 712b6aa8731a7e148298c58cea66a5209c659e3c [Nov7 linux-next via tip/auto-latest] ("intel_mid: Renamed *mrst* to *intel_mid*") adds a __cpuinit. We removed this a couple versions ago; we now want to remove the compat no-op stubs. Introducing new users is not what we want to see at this point in time, as it will break once the stubs are gone. Cc: Kuppuswamy Sathyanarayanan Cc: David Cohen Signed-off-by: Paul Gortmaker Link: http://lkml.kernel.org/r/1383849290-11250-1-git-send-email-paul.gortmaker@windriver.com Signed-off-by: H. Peter Anvin diff --git a/arch/x86/platform/intel-mid/intel-mid.c b/arch/x86/platform/intel-mid/intel-mid.c index 523a1c8..f90e290 100644 --- a/arch/x86/platform/intel-mid/intel-mid.c +++ b/arch/x86/platform/intel-mid/intel-mid.c @@ -123,7 +123,7 @@ static void __init intel_mid_time_init(void) apbt_time_init(); } -static void __cpuinit intel_mid_arch_setup(void) +static void intel_mid_arch_setup(void) { if (boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model == 0x27) __intel_mid_cpu_chip = INTEL_MID_CPU_CHIP_PENWELL; -- cgit v0.10.2 From 656a22a105c8135a515daae3d79ede7b472c18ec Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 18:40:01 +0100 Subject: ASoC: mid-x86: Use WARN_ON() instead of BUG_ON() BUG_ON() is rather useless for debugging as it leads to panic(). Use WARN_ON() and handle the error cases accordingly. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/mid-x86/sst_platform.c b/sound/soc/mid-x86/sst_platform.c index 392fc0b..b6b5eb6 100644 --- a/sound/soc/mid-x86/sst_platform.c +++ b/sound/soc/mid-x86/sst_platform.c @@ -40,7 +40,8 @@ static DEFINE_MUTEX(sst_lock); int sst_register_dsp(struct sst_device *dev) { - BUG_ON(!dev); + if (WARN_ON(!dev)) + return -EINVAL; if (!try_module_get(dev->dev->driver->owner)) return -ENODEV; mutex_lock(&sst_lock); @@ -59,7 +60,8 @@ EXPORT_SYMBOL_GPL(sst_register_dsp); int sst_unregister_dsp(struct sst_device *dev) { - BUG_ON(!dev); + if (WARN_ON(!dev)) + return -EINVAL; if (dev != sst) return -EINVAL; -- cgit v0.10.2 From 96b61bc546519ae90317980294c161c86bcd140e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 18:40:02 +0100 Subject: ASoC: omap: Use WARN_ON() instead of BUG_ON() Use WARN_ON() and handle the error cases accordingly. Acked-by: Jarkko Nikula Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c index 5e8d640..6d216cb 100644 --- a/sound/soc/omap/n810.c +++ b/sound/soc/omap/n810.c @@ -344,8 +344,11 @@ static int __init n810_soc_init(void) clk_set_parent(sys_clkout2_src, func96m_clk); clk_set_rate(sys_clkout2, 12000000); - BUG_ON((gpio_request(N810_HEADSET_AMP_GPIO, "hs_amp") < 0) || - (gpio_request(N810_SPEAKER_AMP_GPIO, "spk_amp") < 0)); + if (WARN_ON((gpio_request(N810_HEADSET_AMP_GPIO, "hs_amp") < 0) || + (gpio_request(N810_SPEAKER_AMP_GPIO, "spk_amp") < 0))) { + err = -EINVAL; + goto err4; + } gpio_direction_output(N810_HEADSET_AMP_GPIO, 0); gpio_direction_output(N810_SPEAKER_AMP_GPIO, 0); -- cgit v0.10.2 From 8a2e2c86e979a9b3a53e880be65d5fd12820b932 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 18:40:03 +0100 Subject: ASoC: pxa: Use WARN_ON() instead of BUG_ON() Use WARN_ON() and handle the error cases accordingly. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index d5340a0..c0d648d 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -165,7 +165,8 @@ static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream, { struct snd_dmaengine_dai_dma_data *dma_data; - BUG_ON(IS_ERR(clk_i2s)); + if (WARN_ON(IS_ERR(clk_i2s))) + return -EINVAL; clk_prepare_enable(clk_i2s); clk_ena = 1; pxa_i2s_wait(); -- cgit v0.10.2 From 4a318f1e6c4331633a91ad67b782860c76d8894b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 18:40:04 +0100 Subject: ASoC: s6000: Use WARN_ON() instead of BUG_ON() Use WARN_ON() and handle the error cases accordingly. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/s6000/s6000-pcm.c b/sound/soc/s6000/s6000-pcm.c index d0740a7..5cfaa54 100644 --- a/sound/soc/s6000/s6000-pcm.c +++ b/sound/soc/s6000/s6000-pcm.c @@ -90,7 +90,8 @@ static void s6000_pcm_enqueue_dma(struct snd_pcm_substream *substream) return; } - BUG_ON(period_size & 15); + if (WARN_ON(period_size & 15)) + return; s6dmac_put_fifo(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel), src, dst, period_size); -- cgit v0.10.2 From 8b14719bebfe022f3637c323e76c88b2bc061a61 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 18:40:05 +0100 Subject: ASoC: rcar: Use WARN_ON() instead of BUG_ON() Use WARN_ON() and handle the error cases accordingly. Acked-by: Kuninori Morimoto Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c index 2df2e91..92e3f51 100644 --- a/sound/soc/sh/rcar/scu.c +++ b/sound/soc/sh/rcar/scu.c @@ -192,7 +192,8 @@ static struct rsnd_mod_ops rsnd_scu_ops = { struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id) { - BUG_ON(id < 0 || id >= rsnd_scu_nr(priv)); + if (WARN_ON(id < 0 || id >= rsnd_scu_nr(priv))) + id = 0; return &((struct rsnd_scu *)(priv->scu) + id)->mod; } diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index fae26d3..fc010d6 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -619,7 +619,8 @@ struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv, struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) { - BUG_ON(id < 0 || id >= rsnd_ssi_nr(priv)); + if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv))) + id = 0; return &(((struct rsnd_ssiu *)(priv->ssiu))->ssi + id)->mod; } -- cgit v0.10.2 From 5f29d4455992ab2edcaa377d4ef61b60240781ac Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 18:40:06 +0100 Subject: ASoC: sh: Use WARN_ON() instead of BUG_ON() Use WARN_ON() and handle the error cases accordingly. Acked-by: Kuninori Morimoto Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/sh/siu_dai.c b/sound/soc/sh/siu_dai.c index 9dc24ff..d55babe 100644 --- a/sound/soc/sh/siu_dai.c +++ b/sound/soc/sh/siu_dai.c @@ -543,7 +543,8 @@ static void siu_dai_shutdown(struct snd_pcm_substream *substream, /* Stop the siu if the other stream is not using it */ if (!port_info->play_cap) { /* during stmread or stmwrite ? */ - BUG_ON(port_info->playback.rw_flg || port_info->capture.rw_flg); + if (WARN_ON(port_info->playback.rw_flg || port_info->capture.rw_flg)) + return; siu_dai_spbstop(port_info); siu_dai_stop(port_info); } -- cgit v0.10.2 From cb1b10262f986f865fdbafd0af3327f15f83b8af Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 18:40:07 +0100 Subject: ASoC: txx9: Use WARN_ON() instead of BUG_ON() Use WARN_ON() and handle the error cases accordingly. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c index 45a6428..fbd077f 100644 --- a/sound/soc/txx9/txx9aclc.c +++ b/sound/soc/txx9/txx9aclc.c @@ -115,8 +115,8 @@ static void txx9aclc_dma_complete(void *arg) spin_lock_irqsave(&dmadata->dma_lock, flags); if (dmadata->frag_count >= 0) { dmadata->dmacount--; - BUG_ON(dmadata->dmacount < 0); - tasklet_schedule(&dmadata->tasklet); + if (!WARN_ON(dmadata->dmacount < 0)) + tasklet_schedule(&dmadata->tasklet); } spin_unlock_irqrestore(&dmadata->dma_lock, flags); } @@ -181,7 +181,10 @@ static void txx9aclc_dma_tasklet(unsigned long data) spin_unlock_irqrestore(&dmadata->dma_lock, flags); return; } - BUG_ON(dmadata->dmacount >= NR_DMA_CHAIN); + if (WARN_ON(dmadata->dmacount >= NR_DMA_CHAIN)) { + spin_unlock_irqrestore(&dmadata->dma_lock, flags); + return; + } while (dmadata->dmacount < NR_DMA_CHAIN) { dmadata->dmacount++; spin_unlock_irqrestore(&dmadata->dma_lock, flags); -- cgit v0.10.2 From a361f4525d5e01d7ebe2adafc263e107a34834b2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 6 Nov 2013 11:07:12 +0100 Subject: ASoC: wm8350: Replace BUG() with WARN() BUG() used in the driver is just to spit the stack trace on buggy points, not really needed to stop the whole operation. For that purpose, it'd be more convenient to use WARN() instead with more error information. Cc: patches@opensource.wolfsonmicro.com Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index af1318d..a183dcf 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -274,7 +274,7 @@ static int pga_event(struct snd_soc_dapm_widget *w, break; default: - BUG(); + WARN(1, "Invalid shift %d\n", w->shift); return -1; } -- cgit v0.10.2 From d9dea39671ff0066fa031d6469f251447beed14b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 6 Nov 2013 11:07:13 +0100 Subject: ASoC: wm8900: Replace BUG() with WARN() BUG() used in the driver is just to spit the stack trace on buggy points, not really needed to stop the whole operation. For that purpose, it'd be more convenient to use WARN() instead with more error information. Cc: patches@opensource.wolfsonmicro.com Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index de67e74..734209e 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -279,7 +279,8 @@ static int wm8900_hp_event(struct snd_soc_dapm_widget *w, break; default: - BUG(); + WARN(1, "Invalid event %d\n", event); + break; } return 0; -- cgit v0.10.2 From 8d8bb1ad1ef6e799e85de50f696d2443aa3e84cb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 6 Nov 2013 11:07:14 +0100 Subject: ASoC: wm8904: Replace BUG() with WARN() BUG() used in the driver is just to spit the stack trace on buggy points, not really needed to stop the whole operation. For that purpose, it'd be more convenient to use WARN() instead with more error information. Cc: patches@opensource.wolfsonmicro.com Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index c173ab3..3938fb1 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -741,7 +741,7 @@ static int out_pga_event(struct snd_soc_dapm_widget *w, dcs_r = 3; break; default: - BUG(); + WARN(1, "Invalid reg %d\n", reg); return -EINVAL; } -- cgit v0.10.2 From a845d59de67d29ac28c39684dfffd252c9d37215 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 6 Nov 2013 11:07:15 +0100 Subject: ASoC: wm8958: Replace BUG() with WARN() BUG() used in the driver is just to spit the stack trace on buggy points, not really needed to stop the whole operation. For that purpose, it'd be more convenient to use WARN() instead with more error information. Cc: patches@opensource.wolfsonmicro.com Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c index b0710d8..b7488f1 100644 --- a/sound/soc/codecs/wm8958-dsp2.c +++ b/sound/soc/codecs/wm8958-dsp2.c @@ -348,7 +348,7 @@ static void wm8958_dsp_apply(struct snd_soc_codec *codec, int path, int start) aif = 1; break; default: - BUG(); + WARN(1, "Invalid path %d\n", path); return; } -- cgit v0.10.2 From 69134367c34aa35e895f10911ed3587ba270dcb0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 6 Nov 2013 11:07:16 +0100 Subject: ASoC: wm8962: Replace BUG() with WARN() BUG() used in the driver is just to spit the stack trace on buggy points, not really needed to stop the whole operation. For that purpose, it'd be more convenient to use WARN() instead with more error information. Cc: patches@opensource.wolfsonmicro.com Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 11d80f3..22e42e7 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -1845,7 +1845,7 @@ static int cp_event(struct snd_soc_dapm_widget *w, break; default: - BUG(); + WARN(1, "Invalid event %d\n", event); return -EINVAL; } @@ -1937,7 +1937,7 @@ static int hp_event(struct snd_soc_dapm_widget *w, break; default: - BUG(); + WARN(1, "Invalid event %d\n", event); return -EINVAL; } @@ -1966,7 +1966,7 @@ static int out_pga_event(struct snd_soc_dapm_widget *w, reg = WM8962_SPKOUTL_VOLUME; break; default: - BUG(); + WARN(1, "Invalid shift %d\n", w->shift); return -EINVAL; } @@ -1974,7 +1974,7 @@ static int out_pga_event(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMU: return snd_soc_write(codec, reg, snd_soc_read(codec, reg)); default: - BUG(); + WARN(1, "Invalid event %d\n", event); return -EINVAL; } } @@ -1997,7 +1997,7 @@ static int dsp2_event(struct snd_soc_dapm_widget *w, break; default: - BUG(); + WARN(1, "Invalid event %d\n", event); return -EINVAL; } -- cgit v0.10.2 From d8e9a54414b92efd87e32c7320c894e7d0595158 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 6 Nov 2013 11:07:17 +0100 Subject: ASoC: wm8996: Replace BUG() with WARN() BUG() used in the driver is just to spit the stack trace on buggy points, not really needed to stop the whole operation. For that purpose, it'd be more convenient to use WARN() instead with more error information. Cc: patches@opensource.wolfsonmicro.com Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index 46fe83d..d666de0 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -608,7 +608,7 @@ static int bg_event(struct snd_soc_dapm_widget *w, wm8996_bg_disable(codec); break; default: - BUG(); + WARN(1, "Invalid event %d\n", event); ret = -EINVAL; } @@ -625,7 +625,7 @@ static int cp_event(struct snd_soc_dapm_widget *w, msleep(5); break; default: - BUG(); + WARN(1, "Invalid event %d\n", event); ret = -EINVAL; } @@ -646,7 +646,7 @@ static int rmv_short_event(struct snd_soc_dapm_widget *w, wm8996->hpout_pending |= w->shift; break; default: - BUG(); + WARN(1, "Invalid event %d\n", event); return -EINVAL; } @@ -767,7 +767,7 @@ static int dcs_start(struct snd_soc_dapm_widget *w, wm8996->dcs_pending |= 1 << w->shift; break; default: - BUG(); + WARN(1, "Invalid event %d\n", event); return -EINVAL; } @@ -1656,7 +1656,7 @@ static int wm8996_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) lrclk_rx_reg = WM8996_AIF2_RX_LRCLK_2; break; default: - BUG(); + WARN(1, "Invalid dai id %d\n", dai->id); return -EINVAL; } @@ -1768,7 +1768,7 @@ static int wm8996_hw_params(struct snd_pcm_substream *substream, dsp_shift = WM8996_DSP2_DIV_SHIFT; break; default: - BUG(); + WARN(1, "Invalid dai id %d\n", dai->id); return -EINVAL; } -- cgit v0.10.2 From 9a743400a032766a2b79c88233ea3f6430a59b86 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 6 Nov 2013 11:07:18 +0100 Subject: ASoC: wm_hubs: Replace BUG() with WARN() BUG() used in the driver is just to spit the stack trace on buggy points, not really needed to stop the whole operation. For that purpose, it'd be more convenient to use WARN() instead with more error information. Cc: patches@opensource.wolfsonmicro.com Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 01daf65..b371066 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -611,7 +611,7 @@ static int earpiece_event(struct snd_soc_dapm_widget *w, break; default: - BUG(); + WARN(1, "Invalid event %d\n", event); break; } -- cgit v0.10.2 From a6ed0608bd289b45a9e42e3b0f33ff55bdac9b0f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 6 Nov 2013 11:07:19 +0100 Subject: ASoC: Replace BUG() with WARN() BUG() used in the driver is just to spit the stack trace on buggy points, not really needed to stop the whole operation. For that purpose, it'd be more convenient to use WARN() instead with more error information. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index e72f554..223dc06 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -39,7 +39,8 @@ static bool snd_soc_set_cache_val(void *base, unsigned int idx, break; } default: - BUG(); + WARN(1, "Invalid word_size %d\n", word_size); + break; } return false; } @@ -60,7 +61,8 @@ static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx, return cache[idx]; } default: - BUG(); + WARN(1, "Invalid word_size %d\n", word_size); + break; } /* unreachable */ return -1; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index b2949ae..974a4ce 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1412,7 +1412,7 @@ static void dapm_seq_check_event(struct snd_soc_card *card, power = 0; break; default: - BUG(); + WARN(1, "Unknown event %d\n", event); return; } @@ -2001,7 +2001,7 @@ static ssize_t dapm_bias_read_file(struct file *file, char __user *user_buf, level = "Off\n"; break; default: - BUG(); + WARN(1, "Unknown bias_level %d\n", dapm->bias_level); level = "Unknown\n"; break; } @@ -3416,7 +3416,7 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, break; default: - BUG(); + WARN(1, "Unknown event %d\n", event); return -EINVAL; } -- cgit v0.10.2 From 6c452bdac799e5ab94d658ea3517cfd57d832e6e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 5 Nov 2013 18:40:00 +0100 Subject: ASoC: wm_adsp: Fix BUG_ON() and WARN_ON() usages This patch does: - Move the sanity check with WARN_ON() in wm_adsp_region_to_reg() and remove the checks in the callers, - Fix wrong WARN_ON() usages, replaced with WARN(), - Fix unreachable or wrong BUG_ON() usages and replace with WARN_ON(). Signed-off-by: Takashi Iwai Reviewed-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index b38f350..8dfce8f 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -341,6 +341,8 @@ static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region, unsigned int offset) { + if (WARN_ON(!region)) + return offset; switch (region->type) { case WMFW_ADSP1_PM: return region->base + (offset * 3); @@ -353,7 +355,7 @@ static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region, case WMFW_ADSP1_ZM: return region->base + (offset * 2); default: - WARN_ON(NULL != "Unknown memory region type"); + WARN(1, "Unknown memory region type"); return offset; } } @@ -602,7 +604,7 @@ static int wm_adsp_load(struct wm_adsp *dsp) break; default: - BUG_ON(NULL == "Unknown DSP type"); + WARN(1, "Unknown DSP type"); goto out_fw; } @@ -642,27 +644,22 @@ static int wm_adsp_load(struct wm_adsp *dsp) reg = offset; break; case WMFW_ADSP1_PM: - BUG_ON(!mem); region_name = "PM"; reg = wm_adsp_region_to_reg(mem, offset); break; case WMFW_ADSP1_DM: - BUG_ON(!mem); region_name = "DM"; reg = wm_adsp_region_to_reg(mem, offset); break; case WMFW_ADSP2_XM: - BUG_ON(!mem); region_name = "XM"; reg = wm_adsp_region_to_reg(mem, offset); break; case WMFW_ADSP2_YM: - BUG_ON(!mem); region_name = "YM"; reg = wm_adsp_region_to_reg(mem, offset); break; case WMFW_ADSP1_ZM: - BUG_ON(!mem); region_name = "ZM"; reg = wm_adsp_region_to_reg(mem, offset); break; @@ -901,10 +898,8 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) break; } - if (mem == NULL) { - BUG_ON(mem != NULL); + if (WARN_ON(!mem)) return -EINVAL; - } switch (dsp->type) { case WMFW_ADSP1: @@ -998,7 +993,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) break; default: - BUG_ON(NULL == "Unknown DSP type"); + WARN(1, "Unknown DSP type"); return -EINVAL; } -- cgit v0.10.2 From bf4edea863c435c302041cf8bb01c8b3ca729449 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 7 Nov 2013 18:38:47 +0100 Subject: ASoC: dapm: Use WARN_ON() instead of BUG_ON() Leaving BUG_ON() in a core layer like dapm is rather inappropriate as it leads to panic(), even though sanity checks might be still useful for debugging. Instead, Use WARN_ON(), and handle the error cases accordingly. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 974a4ce..47dfe17 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1444,7 +1444,7 @@ static void dapm_seq_run_coalesced(struct snd_soc_card *card, power_list)->reg; list_for_each_entry(w, pending, power_list) { - BUG_ON(reg != w->reg); + WARN_ON(reg != w->reg); w->power = w->new_power; mask |= w->mask << w->shift; @@ -3329,8 +3329,9 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, u64 fmt; int ret; - BUG_ON(!config); - BUG_ON(list_empty(&w->sources) || list_empty(&w->sinks)); + if (WARN_ON(!config) || + WARN_ON(list_empty(&w->sources) || list_empty(&w->sinks))) + return -EINVAL; /* We only support a single source and sink, pick the first */ source_p = list_first_entry(&w->sources, struct snd_soc_dapm_path, @@ -3338,9 +3339,10 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, sink_p = list_first_entry(&w->sinks, struct snd_soc_dapm_path, list_source); - BUG_ON(!source_p || !sink_p); - BUG_ON(!sink_p->source || !source_p->sink); - BUG_ON(!source_p->source || !sink_p->sink); + if (WARN_ON(!source_p || !sink_p) || + WARN_ON(!sink_p->source || !source_p->sink) || + WARN_ON(!source_p->source || !sink_p->sink)) + return -EINVAL; source = source_p->source->priv; sink = sink_p->sink->priv; -- cgit v0.10.2 From ca2b029515b4a81bbedcffc69a5d9574bb5ba475 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Thu, 7 Nov 2013 14:45:16 +0800 Subject: ASoC: generic-dmaengine-pcm: Use SNDRV_DMA_TYPE_DEV_IRAM as default When allocating memory space for DMA buffer, use on-chip internal SRAM as default choice to save power. Since the core would allocate memory from traditional external memory if iram allocation failed, we don't need to worry about any side effect. Signed-off-by: Nicolin Chen Acked-by: Lars-Peter Clausen Acked-by: Mark Brown Signed-off-by: Takashi Iwai diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index ee07903..51fded3 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -228,7 +228,7 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) } ret = snd_pcm_lib_preallocate_pages(substream, - SNDRV_DMA_TYPE_DEV, + SNDRV_DMA_TYPE_DEV_IRAM, dmaengine_dma_dev(pcm, substream), prealloc_buffer_size, max_buffer_size); -- cgit v0.10.2 From 3b70a67da0bebca02a130eff8084eee457a2b836 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 7 Nov 2013 11:55:15 -0800 Subject: ALSA: hda_intel: ratelimit "spurious response" message dmesg here has a 100+ consecutive lines of: [ 1464.219446] hda-intel 0000:00:14.2: spurious response 0x0:0x0, last cmd=0x170500 [ 1464.219451] hda-intel 0000:00:14.2: spurious response 0x0:0x0, last cmd=0x170500 [ 1464.219454] hda-intel 0000:00:14.2: spurious response 0x0:0x0, last cmd=0x170500 ... Ratelimit the message to reduce the dmesg log noise. Coalesce the format while at it. Signed-off-by: Joe Perches Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 17f4aa8..7a09404 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -914,12 +914,12 @@ static void azx_update_rirb(struct azx *chip) chip->rirb.res[addr] = res; smp_wmb(); chip->rirb.cmds[addr]--; - } else - snd_printk(KERN_ERR SFX "%s: spurious response %#x:%#x, " - "last cmd=%#08x\n", + } else if (printk_ratelimit()) { + snd_printk(KERN_ERR SFX "%s: spurious response %#x:%#x, last cmd=%#08x\n", pci_name(chip->pci), res, res_ex, chip->last_cmd[addr]); + } } } -- cgit v0.10.2 From 380702192f616df5b2ba704b85cc479a3da92c91 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Fri, 1 Nov 2013 15:57:35 +0800 Subject: ALSA: hda/realtek - Make fixup regs persist after resume Upon suspend / resume, the fixup register settings are lost because sending HDA_FIXUP_ACT_PRE_PROBE is not part of the resume path. Instead, write our registers in response to the HDA_FIXUP_ACT_INIT, which happens after initial probe and upon resume. Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index daf7205..6a21466 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3503,6 +3503,8 @@ static void alc283_fixup_chromebook(struct hda_codec *codec, /* Disable AA-loopback as it causes white noise */ spec->gen.mixer_nid = 0; spec->gen.hp_automute_hook = alc283_hp_automute_hook; + break; + case HDA_FIXUP_ACT_INIT: /* MIC2-VREF control */ /* Set to manual mode */ val = alc_read_coef_idx(codec, 0x06); -- cgit v0.10.2 From 10227a94874435b038d5303ca05c1e158927084b Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 8 Nov 2013 00:55:00 -0200 Subject: ASoC: fsl: imx-pcm-fiq: Remove unused 'runtime' variable Commit 68f9672b (ASoC: fsl: imx-pcm-fiq: remove bogus period delta calculation) introduced the following build warning: sound/soc/fsl/imx-pcm-fiq.c:53:26: warning: unused variable 'runtime' [-Wunused-variable] Remove the unused 'runtime' variable. Signed-off-by: Fabio Estevam Acked-by: Oskar Schirmer Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c index 94720a6..10e3305 100644 --- a/sound/soc/fsl/imx-pcm-fiq.c +++ b/sound/soc/fsl/imx-pcm-fiq.c @@ -50,7 +50,6 @@ static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt) struct imx_pcm_runtime_data *iprtd = container_of(hrt, struct imx_pcm_runtime_data, hrt); struct snd_pcm_substream *substream = iprtd->substream; - struct snd_pcm_runtime *runtime = substream->runtime; struct pt_regs regs; if (!atomic_read(&iprtd->running)) -- cgit v0.10.2 From ea8e5e591801b0d0c25dc0a0f1f5b508be7004cb Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 8 Nov 2013 12:54:44 +0300 Subject: ASoC: fsl: imx-wm8962: remove an unneeded check "data->codec_clk" can't be an ERR_PTR here so I have removed the superflous check. Signed-off-by: Dan Carpenter Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/imx-wm8962.c b/sound/soc/fsl/imx-wm8962.c index 8e5b2c6..f7be44a 100644 --- a/sound/soc/fsl/imx-wm8962.c +++ b/sound/soc/fsl/imx-wm8962.c @@ -279,8 +279,7 @@ static int imx_wm8962_probe(struct platform_device *pdev) return 0; clk_fail: - if (!IS_ERR(data->codec_clk)) - clk_disable_unprepare(data->codec_clk); + clk_disable_unprepare(data->codec_clk); fail: if (ssi_np) of_node_put(ssi_np); -- cgit v0.10.2 From 51387306b5c2b0c9eb1e2556de1b37267bb2b583 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 8 Nov 2013 12:45:33 +0300 Subject: metag: off by one in setup_bootmem_node() If "nid == MAX_NUMNODES" then we write beyond the end of the node_data[] array. Signed-off-by: Dan Carpenter Signed-off-by: James Hogan diff --git a/arch/metag/mm/numa.c b/arch/metag/mm/numa.c index 9ae578c..b172aa4 100644 --- a/arch/metag/mm/numa.c +++ b/arch/metag/mm/numa.c @@ -34,7 +34,7 @@ void __init setup_bootmem_node(int nid, unsigned long start, unsigned long end) unsigned long pgdat_paddr; /* Don't allow bogus node assignment */ - BUG_ON(nid > MAX_NUMNODES || nid <= 0); + BUG_ON(nid >= MAX_NUMNODES || nid <= 0); start_pfn = start >> PAGE_SHIFT; end_pfn = end >> PAGE_SHIFT; -- cgit v0.10.2 From 885845d78551be7bf8570f6283df8b7a7797c4d1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 8 Nov 2013 12:50:31 +0100 Subject: ALSA: hda - Apply MacBook fixups for CS4208 correctly The commit [8fe7b65ab465: ALSA: hda - Apply GPIO setup for MacBooks with CS4208] added a fixup entry matching with the vendor id 0x106b. This broke the fixups for previous MBA6,1 and 6,2, since the PCI SSID vendor id matches before evaluating the codec SSIDs. We had a similar issue on Mac with Sigmatel codecs, and solve this problem again similarly, by introducing a skeleton entry matching with the all MacBooks, then remap to the right one. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=64401 Fixes: 8fe7b65ab465 ('ALSA: hda - Apply GPIO setup for MacBooks with CS4208') Cc: [v3.12+] Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 1ce1f40..072755c 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -597,6 +597,7 @@ static int patch_cs420x(struct hda_codec *codec) * Its layout is no longer compatible with CS4206/CS4207 */ enum { + CS4208_MAC_AUTO, CS4208_MBA6, CS4208_GPIO0, }; @@ -608,10 +609,14 @@ static const struct hda_model_fixup cs4208_models[] = { }; static const struct snd_pci_quirk cs4208_fixup_tbl[] = { - /* codec SSID */ + SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS4208_MAC_AUTO), + {} /* terminator */ +}; + +/* codec SSID matching */ +static const struct snd_pci_quirk cs4208_mac_fixup_tbl[] = { SND_PCI_QUIRK(0x106b, 0x7100, "MacBookAir 6,1", CS4208_MBA6), SND_PCI_QUIRK(0x106b, 0x7200, "MacBookAir 6,2", CS4208_MBA6), - SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS4208_GPIO0), {} /* terminator */ }; @@ -627,6 +632,20 @@ static void cs4208_fixup_gpio0(struct hda_codec *codec, } } +static const struct hda_fixup cs4208_fixups[]; + +/* remap the fixup from codec SSID and apply it */ +static void cs4208_fixup_mac(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + snd_hda_pick_fixup(codec, NULL, cs4208_mac_fixup_tbl, cs4208_fixups); + if (codec->fixup_id < 0 || codec->fixup_id == CS4208_MAC_AUTO) + codec->fixup_id = CS4208_GPIO0; /* default fixup */ + snd_hda_apply_fixup(codec, action); +} + static const struct hda_fixup cs4208_fixups[] = { [CS4208_MBA6] = { .type = HDA_FIXUP_PINS, @@ -638,6 +657,10 @@ static const struct hda_fixup cs4208_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = cs4208_fixup_gpio0, }, + [CS4208_MAC_AUTO] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs4208_fixup_mac, + }, }; /* correct the 0dB offset of input pins */ -- cgit v0.10.2 From 9a22a8f558d09a83965d2bbe168294eb8ffb70e9 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Fri, 8 Nov 2013 15:54:49 +0800 Subject: ALSA: hda/realtek - Add new codec ALC255/ALC3234 UAJ supported New codec ALC255/ALC3234 support multifunction jacks. It used for menual select the input device. Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 6a21466..24d924d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -577,26 +577,35 @@ static void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports) /* * COEF access helper functions */ -static int alc_read_coef_idx(struct hda_codec *codec, - unsigned int coef_idx) + +static int alc_read_coefex_idx(struct hda_codec *codec, + hda_nid_t nid, + unsigned int coef_idx) { unsigned int val; - snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, coef_idx); - val = snd_hda_codec_read(codec, 0x20, 0, + val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF, 0); return val; } -static void alc_write_coef_idx(struct hda_codec *codec, unsigned int coef_idx, +#define alc_read_coef_idx(codec, coef_idx) \ + alc_read_coefex_idx(codec, 0x20, coef_idx) + +static void alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid, + unsigned int coef_idx, unsigned int coef_val) { - snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, coef_idx); - snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PROC_COEF, coef_val); } +#define alc_write_coef_idx(codec, coef_idx, coef_val) \ + alc_write_coefex_idx(codec, 0x20, coef_idx, coef_val) + /* a special bypass for COEF 0; read the cached value at the second time */ static unsigned int alc_get_coef0(struct hda_codec *codec) { @@ -3109,6 +3118,19 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec) int val; switch (codec->vendor_id) { + case 0x10ec0255: + /* LDO and MISC control */ + alc_write_coef_idx(codec, 0x1b, 0x0c0b); + /* UAJ function set to menual mode */ + alc_write_coef_idx(codec, 0x45, 0xd089); + /* Direct Drive HP Amp control(Set to verb control)*/ + val = alc_read_coefex_idx(codec, 0x57, 0x05); + alc_write_coefex_idx(codec, 0x57, 0x05, val & ~(1<<14)); + /* Set MIC2 Vref gate with HP */ + alc_write_coef_idx(codec, 0x06, 0x6104); + /* Direct Drive HP Amp control */ + alc_write_coefex_idx(codec, 0x57, 0x03, 0x8aa6); + break; case 0x10ec0283: alc_write_coef_idx(codec, 0x1b, 0x0c0b); alc_write_coef_idx(codec, 0x45, 0xc429); @@ -3140,6 +3162,14 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, int val; switch (codec->vendor_id) { + case 0x10ec0255: + alc_write_coef_idx(codec, 0x45, 0xc489); + snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); + alc_write_coefex_idx(codec, 0x57, 0x03, 0x8aa6); + /* Set MIC2 Vref gate to normal */ + alc_write_coef_idx(codec, 0x06, 0x6100); + snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); + break; case 0x10ec0283: alc_write_coef_idx(codec, 0x45, 0xc429); snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); @@ -3171,6 +3201,12 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, static void alc_headset_mode_default(struct hda_codec *codec) { switch (codec->vendor_id) { + case 0x10ec0255: + alc_write_coef_idx(codec, 0x45, 0xc089); + alc_write_coef_idx(codec, 0x45, 0xc489); + alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6); + alc_write_coef_idx(codec, 0x49, 0x0049); + break; case 0x10ec0283: alc_write_coef_idx(codec, 0x06, 0x2100); alc_write_coef_idx(codec, 0x32, 0x4ea3); @@ -3194,6 +3230,12 @@ static void alc_headset_mode_default(struct hda_codec *codec) static void alc_headset_mode_ctia(struct hda_codec *codec) { switch (codec->vendor_id) { + case 0x10ec0255: + /* Set to CTIA type */ + alc_write_coef_idx(codec, 0x45, 0xd489); + alc_write_coef_idx(codec, 0x1b, 0x0c2b); + alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6); + break; case 0x10ec0283: alc_write_coef_idx(codec, 0x45, 0xd429); alc_write_coef_idx(codec, 0x1b, 0x0c2b); @@ -3216,6 +3258,12 @@ static void alc_headset_mode_ctia(struct hda_codec *codec) static void alc_headset_mode_omtp(struct hda_codec *codec) { switch (codec->vendor_id) { + case 0x10ec0255: + /* Set to OMTP Type */ + alc_write_coef_idx(codec, 0x45, 0xe489); + alc_write_coef_idx(codec, 0x1b, 0x0c2b); + alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6); + break; case 0x10ec0283: alc_write_coef_idx(codec, 0x45, 0xe429); alc_write_coef_idx(codec, 0x1b, 0x0c2b); @@ -3241,6 +3289,15 @@ static void alc_determine_headset_type(struct hda_codec *codec) struct alc_spec *spec = codec->spec; switch (codec->vendor_id) { + case 0x10ec0255: + /* combo jack auto switch control(Check type)*/ + alc_write_coef_idx(codec, 0x45, 0xd089); + /* combo jack auto switch control(Vref conteol) */ + alc_write_coef_idx(codec, 0x49, 0x0149); + msleep(300); + val = alc_read_coef_idx(codec, 0x46); + is_ctia = (val & 0x0070) == 0x0070; + break; case 0x10ec0283: alc_write_coef_idx(codec, 0x45, 0xd029); msleep(300); @@ -3387,6 +3444,21 @@ static void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec, alc_fixup_headset_mode(codec, fix, action); } +static void alc_fixup_headset_mode_alc255(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + /* Set to iphone type */ + alc_write_coef_idx(codec, 0x1b, 0x880b); + alc_write_coef_idx(codec, 0x45, 0xd089); + alc_write_coef_idx(codec, 0x1b, 0x080b); + alc_write_coef_idx(codec, 0x46, 0x0004); + alc_write_coef_idx(codec, 0x1b, 0x0c0b); + msleep(30); + } + alc_fixup_headset_mode(codec, fix, action); +} + static void alc_fixup_headset_mode_alc668(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -3688,6 +3760,8 @@ enum { ALC283_FIXUP_INT_MIC, ALC290_FIXUP_MONO_SPEAKERS, ALC269_FIXUP_THINKPAD_ACPI, + ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC255_FIXUP_HEADSET_MODE, }; static const struct hda_fixup alc269_fixups[] = { @@ -3997,6 +4071,20 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST }, + [ALC255_FIXUP_DELL1_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC255_FIXUP_HEADSET_MODE + }, + [ALC255_FIXUP_HEADSET_MODE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_mode_alc255, + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -4039,6 +4127,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x0609, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0613, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_MONO_SPEAKERS), + SND_PCI_QUIRK(0x1028, 0x061f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x063f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x15cc, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x15cd, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), -- cgit v0.10.2 From db2a7df0343a0fb166d369e58bcfe605162dc857 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Tue, 20 Aug 2013 16:45:36 +0200 Subject: microblaze: Remove deprecated IRQF_DISABLED Removed IRQF_DISABLED as it's no-op and should be removed. Signed-off-by: Michal Simek diff --git a/arch/microblaze/kernel/timer.c b/arch/microblaze/kernel/timer.c index e4b3f33..d7abb71 100644 --- a/arch/microblaze/kernel/timer.c +++ b/arch/microblaze/kernel/timer.c @@ -148,7 +148,7 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id) static struct irqaction timer_irqaction = { .handler = timer_interrupt, - .flags = IRQF_DISABLED | IRQF_TIMER, + .flags = IRQF_TIMER, .name = "timer", .dev_id = &clockevent_xilinx_timer, }; -- cgit v0.10.2 From 176195e757df0044623b2086527ad5b5cfde9488 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 16 Sep 2013 07:45:29 +0200 Subject: microblaze: Use predefined SYSCALL_DEFINE macro Use standard syscall definition. Signed-off-by: Michal Simek diff --git a/arch/microblaze/kernel/sys_microblaze.c b/arch/microblaze/kernel/sys_microblaze.c index f905b3a..e80a6c0 100644 --- a/arch/microblaze/kernel/sys_microblaze.c +++ b/arch/microblaze/kernel/sys_microblaze.c @@ -33,9 +33,9 @@ #include #include -asmlinkage long sys_mmap(unsigned long addr, unsigned long len, - unsigned long prot, unsigned long flags, - unsigned long fd, off_t pgoff) +SYSCALL_DEFINE6(mmap, unsigned long, addr, unsigned long, len, + unsigned long, prot, unsigned long, flags, unsigned long, fd, + off_t, pgoff) { if (pgoff & ~PAGE_MASK) return -EINVAL; -- cgit v0.10.2 From 99399545d62533b4ae742190b5c6b11f7a5826d9 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 16 Sep 2013 07:46:23 +0200 Subject: microblaze: Fix bug with mmap2 syscall MB implementation Fix mmap2 behaviour which incorrectly works with pgoff not in 4k units. Reported-by: Rich Felker Signed-off-by: Michal Simek diff --git a/arch/microblaze/kernel/sys_microblaze.c b/arch/microblaze/kernel/sys_microblaze.c index e80a6c0..f1e1f66 100644 --- a/arch/microblaze/kernel/sys_microblaze.c +++ b/arch/microblaze/kernel/sys_microblaze.c @@ -42,3 +42,14 @@ SYSCALL_DEFINE6(mmap, unsigned long, addr, unsigned long, len, return sys_mmap_pgoff(addr, len, prot, flags, fd, pgoff >> PAGE_SHIFT); } + +SYSCALL_DEFINE6(mmap2, unsigned long, addr, unsigned long, len, + unsigned long, prot, unsigned long, flags, unsigned long, fd, + unsigned long, pgoff) +{ + if (pgoff & (~PAGE_MASK >> 12)) + return -EINVAL; + + return sys_mmap_pgoff(addr, len, prot, flags, fd, + pgoff >> (PAGE_SHIFT - 12)); +} diff --git a/arch/microblaze/kernel/syscall_table.S b/arch/microblaze/kernel/syscall_table.S index 4fca56c..b882ad5 100644 --- a/arch/microblaze/kernel/syscall_table.S +++ b/arch/microblaze/kernel/syscall_table.S @@ -192,7 +192,7 @@ ENTRY(sys_call_table) .long sys_ni_syscall /* reserved for streams2 */ .long sys_vfork /* 190 */ .long sys_getrlimit - .long sys_mmap_pgoff /* mmap2 */ + .long sys_mmap2 .long sys_truncate64 .long sys_ftruncate64 .long sys_stat64 /* 195 */ -- cgit v0.10.2 From 63d7bd1b171d5e7190c50dd3e48b1c561d7803d8 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Fri, 14 Jun 2013 09:53:53 +0200 Subject: microblaze: Remove incorrect file path Trivial. Signed-off-by: Michal Simek diff --git a/arch/microblaze/boot/dts/Makefile b/arch/microblaze/boot/dts/Makefile index c3b3a5d..c4982d1 100644 --- a/arch/microblaze/boot/dts/Makefile +++ b/arch/microblaze/boot/dts/Makefile @@ -1,6 +1,4 @@ # -# arch/microblaze/boot/Makefile -# obj-y += linked_dtb.o -- cgit v0.10.2 From f5ae18ece391c3bbf03bb1edc5fdcf23dc8b7e54 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 7 Nov 2013 11:54:27 -0600 Subject: dt: disable self-tests for !OF_IRQ Fix OF selftest compile on sparc which does not enable OF_IRQ. drivers/of/selftest.c:177: undefined reference to `of_irq_parse_one' drivers/of/selftest.c:197: undefined reference to `of_irq_parse_one' drivers/of/selftest.c:248: undefined reference to `of_irq_parse_one' Signed-off-by: Rob Herring Acked-by: Grant Likely diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index 9d2009a..9b28f8b 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -17,6 +17,7 @@ config PROC_DEVICETREE config OF_SELFTEST bool "Device Tree Runtime self tests" + depends on OF_IRQ help This option builds in test cases for the device tree infrastructure that are executed one at boot time, and the results dumped to the -- cgit v0.10.2 From a8d3f362f52b65207cacbfb4c50f75e9d4751ef6 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 7 Nov 2013 12:13:27 -0600 Subject: dt/irq: add empty of_irq_count for !OF_IRQ Add an empty version of of_irq_count for !OF_IRQ. This fixes build error on sparc in linux-next: drivers/gpio/gpio-bcm-kona.c:542: undefined reference to `of_irq_count' Signed-off-by: Rob Herring diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h index c0d6dfe..3f23b44 100644 --- a/include/linux/of_irq.h +++ b/include/linux/of_irq.h @@ -37,12 +37,20 @@ extern int of_irq_parse_one(struct device_node *device, int index, extern unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data); extern int of_irq_to_resource(struct device_node *dev, int index, struct resource *r); -extern int of_irq_count(struct device_node *dev); extern int of_irq_to_resource_table(struct device_node *dev, struct resource *res, int nr_irqs); extern void of_irq_init(const struct of_device_id *matches); +#ifdef CONFIG_OF_IRQ +extern int of_irq_count(struct device_node *dev); +#else +static inline int of_irq_count(struct device_node *dev) +{ + return 0; +} +#endif + #if defined(CONFIG_OF) /* * irq_of_parse_and_map() is used by all OF enabled platforms; but SPARC -- cgit v0.10.2 From 70d7f98722a7a1df1a55d6a92d0ce959c7aba9fd Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 8 Nov 2013 16:35:55 +0100 Subject: uprobes: Fix the wrong usage of current->utask in uprobe_copy_process() Commit aa59c53fd459 "uprobes: Change uprobe_copy_process() to dup xol_area" has a stupid typo, we need to setup t->utask->vaddr but the code wrongly uses current->utask. Even with this bug dup_xol_work() works "in practice", but only because get_unmapped_area(NULL, TASK_SIZE - PAGE_SIZE) likely returns the same address every time. Signed-off-by: Oleg Nesterov diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 0ac346a..5e56950 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -1447,7 +1447,7 @@ void uprobe_copy_process(struct task_struct *t, unsigned long flags) if (!work) return uprobe_warn(t, "dup xol area"); - utask->vaddr = area->vaddr; + t->utask->vaddr = area->vaddr; init_task_work(work, dup_xol_work); task_work_add(t, work, true); } -- cgit v0.10.2 From 2ded0980a6e4ae96bdd84bda66c7240967d86f3c Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Thu, 7 Nov 2013 19:41:57 +0100 Subject: uprobes: Fix the memory out of bound overwrite in copy_insn() 1. copy_insn() doesn't look very nice, all calculations are confusing and it is not immediately clear why do we read the 2nd page first. 2. The usage of inode->i_size is wrong on 32-bit machines. 3. "Instruction at end of binary" logic is simply wrong, it doesn't handle the case when uprobe->offset > inode->i_size. In this case "bytes" overflows, and __copy_insn() writes to the memory outside of uprobe->arch.insn. Yes, uprobe_register() checks i_size_read(), but this file can be truncated after that. All i_size checks are racy, we do this only to catch the obvious mistakes. Change copy_insn() to call __copy_insn() in a loop, simplify and fix the bytes/nbytes calculations. Note: we do not care if we read extra bytes after inode->i_size if we got the valid page. This is fine because the task gets the same page after page-fault, and arch_uprobe_analyze_insn() can't know how many bytes were actually read anyway. Signed-off-by: Oleg Nesterov diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 5e56950..24b7d6c 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -504,9 +504,8 @@ static bool consumer_del(struct uprobe *uprobe, struct uprobe_consumer *uc) return ret; } -static int -__copy_insn(struct address_space *mapping, struct file *filp, char *insn, - unsigned long nbytes, loff_t offset) +static int __copy_insn(struct address_space *mapping, struct file *filp, + void *insn, int nbytes, loff_t offset) { struct page *page; @@ -528,28 +527,28 @@ __copy_insn(struct address_space *mapping, struct file *filp, char *insn, static int copy_insn(struct uprobe *uprobe, struct file *filp) { - struct address_space *mapping; - unsigned long nbytes; - int bytes; - - nbytes = PAGE_SIZE - (uprobe->offset & ~PAGE_MASK); - mapping = uprobe->inode->i_mapping; + struct address_space *mapping = uprobe->inode->i_mapping; + loff_t offs = uprobe->offset; + void *insn = uprobe->arch.insn; + int size = MAX_UINSN_BYTES; + int len, err = -EIO; - /* Instruction at end of binary; copy only available bytes */ - if (uprobe->offset + MAX_UINSN_BYTES > uprobe->inode->i_size) - bytes = uprobe->inode->i_size - uprobe->offset; - else - bytes = MAX_UINSN_BYTES; + /* Copy only available bytes, -EIO if nothing was read */ + do { + if (offs >= i_size_read(uprobe->inode)) + break; - /* Instruction at the page-boundary; copy bytes in second page */ - if (nbytes < bytes) { - int err = __copy_insn(mapping, filp, uprobe->arch.insn + nbytes, - bytes - nbytes, uprobe->offset + nbytes); + len = min_t(int, size, PAGE_SIZE - (offs & ~PAGE_MASK)); + err = __copy_insn(mapping, filp, insn, len, offs); if (err) - return err; - bytes = nbytes; - } - return __copy_insn(mapping, filp, uprobe->arch.insn, bytes, uprobe->offset); + break; + + insn += len; + offs += len; + size -= len; + } while (size); + + return err; } static int prepare_uprobe(struct uprobe *uprobe, struct file *file, -- cgit v0.10.2 From 9ebddac7ea2a1f4b4ce3335a78312a58dfaadb4d Mon Sep 17 00:00:00 2001 From: "Luck, Tony" Date: Fri, 8 Nov 2013 14:03:33 -0800 Subject: ACPI, x86: Fix extended error log driver to depend on CONFIG_X86_LOCAL_APIC Randconfig build by Fengguang's robot army reported: drivers/built-in.o: In function `extlog_print': >> acpi_extlog.c:(.text+0xcc719): undefined reference to `boot_cpu_physical_apicid' The config had CONFIG_SMP=n so we picked up this definition from: : #define cpu_physical_id(cpu) boot_cpu_physical_apicid But boot_cpu_physical_apicid is defined in arch/x86/kernel/apic/apic.c which is only built if CONFIG_X86_LOCAL_APIC=y. Reported-by: Fengguang Wu Signed-off-by: Tony Luck Cc: Chen Gong Cc: Rafael J. Wysocki Link: http://lkml.kernel.org/r/6be3afdcad7968f7fb7c0b681e547b3e872e44dd.1383947368.git.tony.luck@intel.com Signed-off-by: Ingo Molnar diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 08eadb4..e11faae 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -374,7 +374,7 @@ source "drivers/acpi/apei/Kconfig" config ACPI_EXTLOG tristate "Extended Error Log support" - depends on X86_MCE + depends on X86_MCE && X86_LOCAL_APIC select EFI select UEFI_CPER default n -- cgit v0.10.2 From 7053ea1a34fa8567cb5e3c39e04ace4c5d0fbeaa Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Fri, 1 Nov 2013 10:41:46 -0400 Subject: stop_machine: Fix race between stop_two_cpus() and stop_cpus() There is a race between stop_two_cpus, and the global stop_cpus. It is possible for two CPUs to get their stopper functions queued "backwards" from one another, resulting in the stopper threads getting stuck, and the system hanging. This can happen because queuing up stoppers is not synchronized. This patch adds synchronization between stop_cpus (a rare operation), and stop_two_cpus. Reported-and-Tested-by: Prarit Bhargava Signed-off-by: Rik van Riel Signed-off-by: Peter Zijlstra Acked-by: Mel Gorman Link: http://lkml.kernel.org/r/20131101104146.03d1e043@annuminas.surriel.com Signed-off-by: Ingo Molnar diff --git a/kernel/stop_machine.c b/kernel/stop_machine.c index c530bc5..84571e0 100644 --- a/kernel/stop_machine.c +++ b/kernel/stop_machine.c @@ -20,6 +20,7 @@ #include #include #include +#include /* * Structure to determine completion condition and record errors. May @@ -43,6 +44,14 @@ static DEFINE_PER_CPU(struct cpu_stopper, cpu_stopper); static DEFINE_PER_CPU(struct task_struct *, cpu_stopper_task); static bool stop_machine_initialized = false; +/* + * Avoids a race between stop_two_cpus and global stop_cpus, where + * the stoppers could get queued up in reverse order, leading to + * system deadlock. Using an lglock means stop_two_cpus remains + * relatively cheap. + */ +DEFINE_STATIC_LGLOCK(stop_cpus_lock); + static void cpu_stop_init_done(struct cpu_stop_done *done, unsigned int nr_todo) { memset(done, 0, sizeof(*done)); @@ -276,6 +285,7 @@ int stop_two_cpus(unsigned int cpu1, unsigned int cpu2, cpu_stop_fn_t fn, void * return -ENOENT; } + lg_local_lock(&stop_cpus_lock); /* * Queuing needs to be done by the lowest numbered CPU, to ensure * that works are always queued in the same order on every CPU. @@ -284,6 +294,7 @@ int stop_two_cpus(unsigned int cpu1, unsigned int cpu2, cpu_stop_fn_t fn, void * smp_call_function_single(min(cpu1, cpu2), &irq_cpu_stop_queue_work, &call_args, 0); + lg_local_unlock(&stop_cpus_lock); preempt_enable(); wait_for_completion(&done.completion); @@ -335,10 +346,10 @@ static void queue_stop_cpus_work(const struct cpumask *cpumask, * preempted by a stopper which might wait for other stoppers * to enter @fn which can lead to deadlock. */ - preempt_disable(); + lg_global_lock(&stop_cpus_lock); for_each_cpu(cpu, cpumask) cpu_stop_queue_work(cpu, &per_cpu(stop_cpus_work, cpu)); - preempt_enable(); + lg_global_unlock(&stop_cpus_lock); } static int __stop_cpus(const struct cpumask *cpumask, -- cgit v0.10.2 From e5137b50a0640009fd63a3e65c14bc6e1be8796a Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 4 Oct 2013 17:28:26 +0200 Subject: ftrace, sched: Add TRACE_FLAG_PREEMPT_RESCHED Since the introduction of PREEMPT_NEED_RESCHED in: f27dde8deef3 ("sched: Add NEED_RESCHED to the preempt_count") we need to be able to look at both TIF_NEED_RESCHED and PREEMPT_NEED_RESCHED to understand the full preemption behaviour. Add it to the trace output. Signed-off-by: Peter Zijlstra Acked-by: Steven Rostedt Cc: Fengguang Wu Cc: Huang Ying Cc: Yuanhan Liu Link: http://lkml.kernel.org/r/20131004152826.GP3081@twins.programming.kicks-ass.net Signed-off-by: Ingo Molnar diff --git a/Documentation/trace/ftrace.txt b/Documentation/trace/ftrace.txt index ea2d35d..bd36598 100644 --- a/Documentation/trace/ftrace.txt +++ b/Documentation/trace/ftrace.txt @@ -655,7 +655,11 @@ explains which is which. read the irq flags variable, an 'X' will always be printed here. - need-resched: 'N' task need_resched is set, '.' otherwise. + need-resched: + 'N' both TIF_NEED_RESCHED and PREEMPT_NEED_RESCHED is set, + 'n' only TIF_NEED_RESCHED is set, + 'p' only PREEMPT_NEED_RESCHED is set, + '.' otherwise. hardirq/softirq: 'H' - hard irq occurred inside a softirq. diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 7974ba2..d9fea7d 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -1509,7 +1509,8 @@ tracing_generic_entry_update(struct trace_entry *entry, unsigned long flags, #endif ((pc & HARDIRQ_MASK) ? TRACE_FLAG_HARDIRQ : 0) | ((pc & SOFTIRQ_MASK) ? TRACE_FLAG_SOFTIRQ : 0) | - (need_resched() ? TRACE_FLAG_NEED_RESCHED : 0); + (tif_need_resched() ? TRACE_FLAG_NEED_RESCHED : 0) | + (test_preempt_need_resched() ? TRACE_FLAG_PREEMPT_RESCHED : 0); } EXPORT_SYMBOL_GPL(tracing_generic_entry_update); diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 10c86fb..73d08aa 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -124,6 +124,7 @@ enum trace_flag_type { TRACE_FLAG_NEED_RESCHED = 0x04, TRACE_FLAG_HARDIRQ = 0x08, TRACE_FLAG_SOFTIRQ = 0x10, + TRACE_FLAG_PREEMPT_RESCHED = 0x20, }; #define TRACE_BUF_SIZE 1024 diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c index 34e7cba..ed32284 100644 --- a/kernel/trace/trace_output.c +++ b/kernel/trace/trace_output.c @@ -618,8 +618,23 @@ int trace_print_lat_fmt(struct trace_seq *s, struct trace_entry *entry) (entry->flags & TRACE_FLAG_IRQS_OFF) ? 'd' : (entry->flags & TRACE_FLAG_IRQS_NOSUPPORT) ? 'X' : '.'; - need_resched = - (entry->flags & TRACE_FLAG_NEED_RESCHED) ? 'N' : '.'; + + switch (entry->flags & (TRACE_FLAG_NEED_RESCHED | + TRACE_FLAG_PREEMPT_RESCHED)) { + case TRACE_FLAG_NEED_RESCHED | TRACE_FLAG_PREEMPT_RESCHED: + need_resched = 'N'; + break; + case TRACE_FLAG_NEED_RESCHED: + need_resched = 'n'; + break; + case TRACE_FLAG_PREEMPT_RESCHED: + need_resched = 'p'; + break; + default: + need_resched = '.'; + break; + } + hardsoft_irq = (hardirq && softirq) ? 'H' : hardirq ? 'h' : -- cgit v0.10.2 From c11eede69b6ad0ac44ebc1e021a8d2699c5f1f8f Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Sun, 10 Nov 2013 23:19:08 -0600 Subject: powerpc: add missing explicit OF includes for ppc Commit b5b4bb3f6a11f9 (of: only include prom.h on sparc) removed implicit includes of of_*.h headers by powerpc's prom.h. Some components were missed in initial clean-up patch, so add the necessary includes to fix powerpc builds. Signed-off-by: Rob Herring Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Tejun Heo Cc: Matt Mackall Cc: Herbert Xu Cc: "David S. Miller" Cc: Vinod Koul Cc: Dan Williams Cc: linuxppc-dev@lists.ozlabs.org Cc: linux-ide@vger.kernel.org Cc: linux-crypto@vger.kernel.org diff --git a/arch/powerpc/platforms/85xx/ppa8548.c b/arch/powerpc/platforms/85xx/ppa8548.c index 6a7704b..3daff7c 100644 --- a/arch/powerpc/platforms/85xx/ppa8548.c +++ b/arch/powerpc/platforms/85xx/ppa8548.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include diff --git a/arch/powerpc/platforms/85xx/sgy_cts1000.c b/arch/powerpc/platforms/85xx/sgy_cts1000.c index 7179726..b9197ce 100644 --- a/arch/powerpc/platforms/85xx/sgy_cts1000.c +++ b/arch/powerpc/platforms/85xx/sgy_cts1000.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/powerpc/platforms/85xx/smp.c b/arch/powerpc/platforms/85xx/smp.c index 281b7f0..393f975 100644 --- a/arch/powerpc/platforms/85xx/smp.c +++ b/arch/powerpc/platforms/85xx/smp.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/powerpc/sysdev/ehv_pic.c b/arch/powerpc/sysdev/ehv_pic.c index 9cd0e60..b74085c 100644 --- a/arch/powerpc/sysdev/ehv_pic.c +++ b/arch/powerpc/sysdev/ehv_pic.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include diff --git a/arch/powerpc/sysdev/ppc4xx_ocm.c b/arch/powerpc/sysdev/ppc4xx_ocm.c index 1b15f93..b7c4345 100644 --- a/arch/powerpc/sysdev/ppc4xx_ocm.c +++ b/arch/powerpc/sysdev/ppc4xx_ocm.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/powerpc/sysdev/xilinx_intc.c b/arch/powerpc/sysdev/xilinx_intc.c index f4fdc94..83f943a 100644 --- a/arch/powerpc/sysdev/xilinx_intc.c +++ b/arch/powerpc/sysdev/xilinx_intc.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index 2e39173..523524b 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/drivers/char/hw_random/ppc4xx-rng.c b/drivers/char/hw_random/ppc4xx-rng.c index 732c330..521f76b 100644 --- a/drivers/char/hw_random/ppc4xx-rng.c +++ b/drivers/char/hw_random/ppc4xx-rng.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include diff --git a/drivers/clk/clk-ppc-corenet.c b/drivers/clk/clk-ppc-corenet.c index e958707..c4f76ed 100644 --- a/drivers/clk/clk-ppc-corenet.c +++ b/drivers/clk/clk-ppc-corenet.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/crypto/amcc/crypto4xx_core.c b/drivers/crypto/amcc/crypto4xx_core.c index f88e3d8..efaf630 100644 --- a/drivers/crypto/amcc/crypto4xx_core.c +++ b/drivers/crypto/amcc/crypto4xx_core.c @@ -27,6 +27,9 @@ #include #include #include +#include +#include +#include #include #include #include diff --git a/drivers/dma/ppc4xx/adma.c b/drivers/dma/ppc4xx/adma.c index 370ff82..e24b5ef 100644 --- a/drivers/dma/ppc4xx/adma.c +++ b/drivers/dma/ppc4xx/adma.c @@ -42,6 +42,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/socrates_nand.c index 09dde7d..49bd9155 100644 --- a/drivers/mtd/nand/socrates_nand.c +++ b/drivers/mtd/nand/socrates_nand.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include diff --git a/drivers/pcmcia/m8xx_pcmcia.c b/drivers/pcmcia/m8xx_pcmcia.c index 18c0d8d..182034d 100644 --- a/drivers/pcmcia/m8xx_pcmcia.c +++ b/drivers/pcmcia/m8xx_pcmcia.c @@ -48,7 +48,9 @@ #include #include #include +#include #include +#include #include #include diff --git a/drivers/spi/spi-ppc4xx.c b/drivers/spi/spi-ppc4xx.c index 0ee53c2..19d615b 100644 --- a/drivers/spi/spi-ppc4xx.c +++ b/drivers/spi/spi-ppc4xx.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/drivers/watchdog/pika_wdt.c b/drivers/watchdog/pika_wdt.c index 7d3d471..329bc60 100644 --- a/drivers/watchdog/pika_wdt.c +++ b/drivers/watchdog/pika_wdt.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #define DRV_NAME "PIKA-WDT" -- cgit v0.10.2 From b5dfcb09debc38582c3cb72a3c1a88b919b07f2d Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 11 Nov 2013 19:53:42 +0100 Subject: Revert "x86/UV: Add uvtrace support" This reverts commit 8eba18428ac926f436064ac281e76d36d51bd631. uv_trace() is not used by anything, nor is uv_trace_nmi_func, nor uv_trace_func. That's not how we do instrumentation code in the kernel: we add tracepoints, printk()s, etc. so that everyone not just those with magic kernel modules can debug a system. So remove this unused (and misguied) piece of code. Signed-off-by: Ingo Molnar Cc: Mike Travis Cc: Dimitri Sivanich Cc: Hedi Berriche Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo Cc: Jason Wessel Link: http://lkml.kernel.org/n/tip-tumfBffmr4jmnt8Gyxanoblg@git.kernel.org diff --git a/arch/x86/include/asm/uv/uv.h b/arch/x86/include/asm/uv/uv.h index 8b1283d..6b964a0 100644 --- a/arch/x86/include/asm/uv/uv.h +++ b/arch/x86/include/asm/uv/uv.h @@ -14,13 +14,6 @@ extern void uv_cpu_init(void); extern void uv_nmi_init(void); extern void uv_register_nmi_notifier(void); extern void uv_system_init(void); -extern void (*uv_trace_nmi_func)(unsigned int reason, struct pt_regs *regs); -extern void (*uv_trace_func)(const char *f, const int l, const char *fmt, ...); -#define uv_trace(fmt, ...) \ -do { \ - if (unlikely(uv_trace_func)) \ - (uv_trace_func)(__func__, __LINE__, fmt, ##__VA_ARGS__);\ -} while (0) extern const struct cpumask *uv_flush_tlb_others(const struct cpumask *cpumask, struct mm_struct *mm, unsigned long start, @@ -33,7 +26,6 @@ static inline enum uv_system_type get_uv_system_type(void) { return UV_NONE; } static inline int is_uv_system(void) { return 0; } static inline void uv_cpu_init(void) { } static inline void uv_system_init(void) { } -static inline void uv_trace(void *fmt, ...) { } static inline void uv_register_nmi_notifier(void) { } static inline const struct cpumask * uv_flush_tlb_others(const struct cpumask *cpumask, struct mm_struct *mm, diff --git a/arch/x86/platform/uv/uv_nmi.c b/arch/x86/platform/uv/uv_nmi.c index 9b8ac60..2e863ad 100644 --- a/arch/x86/platform/uv/uv_nmi.c +++ b/arch/x86/platform/uv/uv_nmi.c @@ -1,5 +1,5 @@ /* - * SGI NMI/TRACE support routines + * SGI NMI support routines * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -39,13 +39,6 @@ #include #include -void (*uv_trace_func)(const char *f, const int l, const char *fmt, ...); -EXPORT_SYMBOL(uv_trace_func); - -void (*uv_trace_nmi_func)(unsigned int reason, struct pt_regs *regs); -EXPORT_SYMBOL(uv_trace_nmi_func); - - /* * UV handler for NMI * @@ -599,10 +592,6 @@ int uv_handle_nmi(unsigned int reason, struct pt_regs *regs) return NMI_DONE; } - /* Call possible NMI trace function */ - if (unlikely(uv_trace_nmi_func)) - (uv_trace_nmi_func)(reason, regs); - /* Indicate we are the first CPU into the NMI handler */ master = (atomic_read(&uv_nmi_cpu) == cpu); -- cgit v0.10.2 From 30e3488cbfe6e8af0832be715081c49b08299b73 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 25 Oct 2013 17:31:51 +0800 Subject: ARM: OMAP2+: smsc911x: fix return value check in gpmc_smsc911x_init() In case of error, the function platform_device_register_resndata() returns ERR_PTR() and never returns NULL. The NULL test in the return value check should be replaced with IS_ERR(). Signed-off-by: Wei Yongjun Acked-by: Igor Grinberg Signed-off-by: Tony Lindgren diff --git a/arch/arm/mach-omap2/gpmc-smsc911x.c b/arch/arm/mach-omap2/gpmc-smsc911x.c index ef99011..2757504 100644 --- a/arch/arm/mach-omap2/gpmc-smsc911x.c +++ b/arch/arm/mach-omap2/gpmc-smsc911x.c @@ -83,7 +83,7 @@ void __init gpmc_smsc911x_init(struct omap_smsc911x_platform_data *gpmc_cfg) pdev = platform_device_register_resndata(NULL, "smsc911x", gpmc_cfg->id, gpmc_smsc911x_resources, ARRAY_SIZE(gpmc_smsc911x_resources), &gpmc_smsc911x_config, sizeof(gpmc_smsc911x_config)); - if (!pdev) { + if (IS_ERR(pdev)) { pr_err("Unable to register platform device\n"); gpio_free(gpmc_cfg->gpio_reset); goto free2; -- cgit v0.10.2 From c9b3a7d227068ccf25f435b1b0720ccc73ee5178 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 11 Oct 2013 19:13:16 +0300 Subject: pinctrl: single: call pcs_soc->rearm() whenever IRQ mask is changed On OMAPs the IO ring must be rearmed each time the pad wakeup configuration is changed. So call pcs_soc->rearm() from pcs_irq_set(). As pinctrl-single is now an interrupt controller in some cases, we should follow the standards and keep the interrupts enabled constantly, and not just for wake-up events. The tracking of runtime vs wake-up interrupts can be handled separately for the automated runtime PM solution when we have it in the future. Signed-off-by: Roger Quadros Acked-by: Linus Walleij [tony@atomide.com: removed wrong comment, updated description] Signed-off-by: Tony Lindgren diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index 0846922..829b98c 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -1604,6 +1604,9 @@ static inline void pcs_irq_set(struct pcs_soc_data *pcs_soc, pcs->write(mask, pcswi->reg); raw_spin_unlock(&pcs->lock); } + + if (pcs_soc->rearm) + pcs_soc->rearm(); } /** @@ -1626,8 +1629,6 @@ static void pcs_irq_unmask(struct irq_data *d) struct pcs_soc_data *pcs_soc = irq_data_get_irq_chip_data(d); pcs_irq_set(pcs_soc, d->irq, true); - if (pcs_soc->rearm) - pcs_soc->rearm(); } /** @@ -1678,11 +1679,6 @@ static int pcs_irq_handle(struct pcs_soc_data *pcs_soc) } } - /* - * For debugging on omaps, you may want to call pcs_soc->rearm() - * here to see wake-up interrupts during runtime also. - */ - return count; } -- cgit v0.10.2 From 7b0d0cce3b441597d3a66a8951c2f3e70071c6cc Mon Sep 17 00:00:00 2001 From: Eric Witcher Date: Fri, 18 Oct 2013 06:01:08 -0400 Subject: ARM: OMAP: devicetree: fix SPI node compatible property syntax items Correct the SPI node compatible property items to match example code and match current DTS usage. Signed-off-by: Eric Witcher Acked-by: Sourav Poddar Signed-off-by: Tony Lindgren diff --git a/Documentation/devicetree/bindings/spi/omap-spi.txt b/Documentation/devicetree/bindings/spi/omap-spi.txt index 4c85c4c..2ba5f9c 100644 --- a/Documentation/devicetree/bindings/spi/omap-spi.txt +++ b/Documentation/devicetree/bindings/spi/omap-spi.txt @@ -2,8 +2,8 @@ OMAP2+ McSPI device Required properties: - compatible : - - "ti,omap2-spi" for OMAP2 & OMAP3. - - "ti,omap4-spi" for OMAP4+. + - "ti,omap2-mcspi" for OMAP2 & OMAP3. + - "ti,omap4-mcspi" for OMAP4+. - ti,spi-num-cs : Number of chipselect supported by the instance. - ti,hwmods: Name of the hwmod associated to the McSPI - ti,pindir-d0-out-d1-in: Select the D0 pin as output and D1 as -- cgit v0.10.2 From c27f2de754a1412ce4a6c4af723605f776ec4ac2 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 30 Oct 2013 13:23:21 +0800 Subject: ARM: OMAP3: Beagle: fix return value check in beagle_opp_init() In case of error, the function get_cpu_device() returns NULL pointer not ERR_PTR(). The IS_ERR() test in the return value check should be replaced with NULL test. Signed-off-by: Wei Yongjun Signed-off-by: Tony Lindgren diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c index 8b9cd06..09f6149 100644 --- a/arch/arm/mach-omap2/board-omap3beagle.c +++ b/arch/arm/mach-omap2/board-omap3beagle.c @@ -510,7 +510,7 @@ static int __init beagle_opp_init(void) mpu_dev = get_cpu_device(0); iva_dev = omap_device_get_by_hwmod_name("iva"); - if (IS_ERR(mpu_dev) || IS_ERR(iva_dev)) { + if (!mpu_dev || IS_ERR(iva_dev)) { pr_err("%s: Aiee.. no mpu/dsp devices? %p %p\n", __func__, mpu_dev, iva_dev); return -ENODEV; -- cgit v0.10.2 From cd6d364f474491160bfd8cb4bad0637858023161 Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Fri, 8 Nov 2013 11:07:36 +0530 Subject: ARM: dts: doc: Document missing compatible property for omap-sham driver A new compatible property "ti,omap5-sham" is added to the omap-sham driver recently to support SHA/MD5 for OMAP5,DRA7 and AM43XX. Documenting the same. Signed-off-by: Lokesh Vutla Signed-off-by: Tony Lindgren diff --git a/Documentation/devicetree/bindings/crypto/omap-sham.txt b/Documentation/devicetree/bindings/crypto/omap-sham.txt index f839acd..ad91155 100644 --- a/Documentation/devicetree/bindings/crypto/omap-sham.txt +++ b/Documentation/devicetree/bindings/crypto/omap-sham.txt @@ -6,7 +6,7 @@ Required properties: SHAM versions: - "ti,omap2-sham" for OMAP2 & OMAP3. - "ti,omap4-sham" for OMAP4 and AM33XX. - Note that these two versions are incompatible. + - "ti,omap5-sham" for OMAP5, DRA7 and AM43XX. - ti,hwmods: Name of the hwmod associated with the SHAM module - reg : Offset and length of the register set for the module - interrupts : the interrupt-specifier for the SHAM module. -- cgit v0.10.2 From 31844434239e30cc7419510921929194be41e637 Mon Sep 17 00:00:00 2001 From: Joel Fernandes Date: Mon, 11 Nov 2013 13:49:41 -0600 Subject: doc: devicetree: Add bindings documentation for omap-des driver Add documentation for the generic OMAP DES crypto modul describing the device tree bindings. Reviewed-by: Mark Rutland Acked-by: Santosh Shilimkar Signed-off-by: Joel Fernandes Signed-off-by: Tony Lindgren diff --git a/Documentation/devicetree/bindings/crypto/omap-des.txt b/Documentation/devicetree/bindings/crypto/omap-des.txt new file mode 100644 index 0000000..e8c63bf --- /dev/null +++ b/Documentation/devicetree/bindings/crypto/omap-des.txt @@ -0,0 +1,30 @@ +OMAP SoC DES crypto Module + +Required properties: + +- compatible : Should contain "ti,omap4-des" +- ti,hwmods: Name of the hwmod associated with the DES module +- reg : Offset and length of the register set for the module +- interrupts : the interrupt-specifier for the DES module +- clocks : A phandle to the functional clock node of the DES module + corresponding to each entry in clock-names +- clock-names : Name of the functional clock, should be "fck" + +Optional properties: +- dmas: DMA specifiers for tx and rx dma. See the DMA client binding, + Documentation/devicetree/bindings/dma/dma.txt + Each entry corresponds to an entry in dma-names +- dma-names: DMA request names should include "tx" and "rx" if present + +Example: + /* DRA7xx SoC */ + des: des@480a5000 { + compatible = "ti,omap4-des"; + ti,hwmods = "des"; + reg = <0x480a5000 0xa0>; + interrupts = ; + dmas = <&sdma 117>, <&sdma 116>; + dma-names = "tx", "rx"; + clocks = <&l3_iclk_div>; + clock-names = "fck"; + }; -- cgit v0.10.2 From 3522bf7bfa248b99eafa2f4872190699a808c7d9 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 14 Nov 2013 11:05:16 -0600 Subject: ARM: OMAP2+: omap_device: maintain sane runtime pm status around suspend/resume OMAP device hooks around suspend|resume_noirq ensures that hwmod devices are forced to idle using omap_device_idle/enable as part of the last stage of suspend activity. For a device such as i2c who uses autosuspend, it is possible to enter the suspend path with dev->power.runtime_status = RPM_ACTIVE. As part of the suspend flow, the generic runtime logic would increment it's dev->power.disable_depth to 1. This should prevent further pm_runtime_get_sync from succeeding once the runtime_status has been set to RPM_SUSPENDED. Now, as part of the suspend_noirq handler in omap_device, we force the following: if the device status is !suspended, we force the device to idle using omap_device_idle (clocks are cut etc..). This ensures that from a hardware perspective, the device is "suspended". However, runtime_status is left to be active. *if* an operation is attempted after this point to pm_runtime_get_sync, runtime framework depends on runtime_status to indicate accurately the device status, and since it sees it to be ACTIVE, it assumes the module is functional and returns a non-error value. As a result the user will see pm_runtime_get succeed, however a register access will crash due to the lack of clocks. To prevent this from happening, we should ensure that runtime_status exactly indicates the device status. As a result of this change any further calls to pm_runtime_get* would return -EACCES (since disable_depth is 1). On resume, we restore the clocks and runtime status exactly as we suspended with. These operations are not expected to fail as we update the states after the core runtime framework has suspended itself and restore before the core runtime framework has resumed. Cc: stable@vger.kernel.org # v3.4+ Reported-by: J Keerthy Signed-off-by: Nishanth Menon Acked-by: Rajendra Nayak Acked-by: Kevin Hilman Reviewed-by: Felipe Balbi Signed-off-by: Tony Lindgren diff --git a/arch/arm/mach-omap2/omap_device.c b/arch/arm/mach-omap2/omap_device.c index b69dd9a..53f0735 100644 --- a/arch/arm/mach-omap2/omap_device.c +++ b/arch/arm/mach-omap2/omap_device.c @@ -621,6 +621,7 @@ static int _od_suspend_noirq(struct device *dev) if (!ret && !pm_runtime_status_suspended(dev)) { if (pm_generic_runtime_suspend(dev) == 0) { + pm_runtime_set_suspended(dev); omap_device_idle(pdev); od->flags |= OMAP_DEVICE_SUSPENDED; } @@ -634,10 +635,18 @@ static int _od_resume_noirq(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct omap_device *od = to_omap_device(pdev); - if ((od->flags & OMAP_DEVICE_SUSPENDED) && - !pm_runtime_status_suspended(dev)) { + if (od->flags & OMAP_DEVICE_SUSPENDED) { od->flags &= ~OMAP_DEVICE_SUSPENDED; omap_device_enable(pdev); + /* + * XXX: we run before core runtime pm has resumed itself. At + * this point in time, we just restore the runtime pm state and + * considering symmetric operations in resume, we donot expect + * to fail. If we failed, something changed in core runtime_pm + * framework OR some device driver messed things up, hence, WARN + */ + WARN(pm_runtime_set_active(dev), + "Could not set %s runtime state active\n", dev_name(dev)); pm_generic_runtime_resume(dev); } -- cgit v0.10.2 From 26273e02a0cf18eb72416559310d3294390a9024 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Fri, 15 Nov 2013 08:27:29 -0800 Subject: ARM: OMAP2+: Fix build for dra7xx without omap4 and 5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise we can get errors like: arch/arm/mach-omap2/prm44xx.c:274: error: redefinition of ‘omap44xx_prm_reconfigure_io_chain’ arch/arm/mach-omap2/built-in.o: In function `default_finish_suspend': arch/arm/mach-omap2/omap-mpuss-lowpower.c:95: undefined reference to `omap_do_wfi' Signed-off-by: Tony Lindgren diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index e15ac00..1f25f3e 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile @@ -40,7 +40,7 @@ omap-4-5-common = omap4-common.o omap-wakeupgen.o obj-$(CONFIG_ARCH_OMAP4) += $(omap-4-5-common) $(smp-y) sleep44xx.o obj-$(CONFIG_SOC_OMAP5) += $(omap-4-5-common) $(smp-y) sleep44xx.o obj-$(CONFIG_SOC_AM43XX) += $(omap-4-5-common) -obj-$(CONFIG_SOC_DRA7XX) += $(omap-4-5-common) $(smp-y) +obj-$(CONFIG_SOC_DRA7XX) += $(omap-4-5-common) $(smp-y) sleep44xx.o plus_sec := $(call as-instr,.arch_extension sec,+sec) AFLAGS_omap-headsmp.o :=-Wa,-march=armv7-a$(plus_sec) diff --git a/arch/arm/mach-omap2/prm44xx_54xx.h b/arch/arm/mach-omap2/prm44xx_54xx.h index a085d9c..7a97606 100644 --- a/arch/arm/mach-omap2/prm44xx_54xx.h +++ b/arch/arm/mach-omap2/prm44xx_54xx.h @@ -42,7 +42,8 @@ extern u32 omap4_prm_vcvp_read(u8 offset); extern void omap4_prm_vcvp_write(u32 val, u8 offset); extern u32 omap4_prm_vcvp_rmw(u32 mask, u32 bits, u8 offset); -#if defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_SOC_OMAP5) +#if defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_SOC_OMAP5) || \ + defined(CONFIG_SOC_DRA7XX) void omap44xx_prm_reconfigure_io_chain(void); #else static inline void omap44xx_prm_reconfigure_io_chain(void) -- cgit v0.10.2